C++影像處理,影像處理
閱讀提示:
《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。
《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。
儘可能保持二者內容一致,可相互對照。
本文代碼必須包括《C++影像處理 -- 資料類型及公用函數》文章中的BmpData.h標頭檔。
在Photoshop中,映像色階調整應用很廣泛,本文介紹的映像色階調整過程與Photoshop處理效果基本一致。
Photoshop的色階調整分輸入色階調整和輸出色階調整,其中輸入色階調整有3個調整點,即通常所說的黑場、白場及灰場調整。
輸入色階調整的基本演算法並不複雜,首先計算出白場與黑場的離差Diff,然後計算出像素各份量值與黑場的離差rgbDiff,如果rgbDiff<=0,像素各份量值等於0,否則,計算以rgbDiff與Diff的比值為底的灰場倒數的冪。用公式表示:
Diff = Highlight -Shadow
rgbDiff = RGB - Shadow
clRGB = Power(rgbDiff / Diff, 1 / Midtones)
其中Shadow為輸入色階低端資料(黑場),Highlight為輸入色階高端資料(白場), Midtones為輸入色階中間資料(灰場),Diff為二者的離差(必須大於1),RGB為調整前的像素分量值,clRGB為調整輸入色階後的像素分量值。
輸出色階調整更簡單,首先計算輸出色階白場與黑場的離差與255的比值係數,然後用輸入色階調整後的像素分量值乘上這個係數,再加上輸出黑場值即可。用公式表示:
outClRGB = clRGB * (outHighlight - outShadow) / 255 + outShadow
其中,outShadow為輸出黑場,outHighlight為輸出白場,outClRGB為全部色階調整後的像素分量值。
前面已經提到輸入色階黑白場的離差必須大於1,而輸入色階並沒有這個限制,輸出黑白場的離差可以為負數,當輸出黑場與白場完全顛倒時,輸出色階調整後的圖片為原圖片的負片。
色階調整涉及四個通道,即R、G、B各分量通道及整體色彩通道,如果每個通道單獨調整,將是比較麻煩和耗時的,本文採用色階表替換法,可一次性完成所有四個通道的色階調整。
下面是映像色階調整的代碼:
// 色階項結構typedef struct{UINT Shadow;FLOAT Midtones;UINT Highlight;UINT OutShadow;UINT OutHighlight;}ColorLevelItem, *PColorLevelItem;typedef struct{ColorLevelItem Blue;ColorLevelItem Green;ColorLevelItem Red;ColorLevelItem RGB;}ColorLevelData, *PColorLevelData;VOID InitColorLevelData(PColorLevelData clData){PColorLevelItem item = &clData->Blue;for (INT i = 0; i < 4; i ++, item ++){item->Shadow = item->OutShadow = 0;item->Highlight = item->OutHighlight = 255;item->Midtones = 1.0;}}BOOL GetColorLevelTable(PColorLevelItem item, LPBYTE clTable){INT diff = (INT)(item->Highlight - item->Shadow);INT outDiff = (INT)(item->OutHighlight - item->OutShadow);if (!((item->Highlight <= 255 && diff < 255 && diff >= 2) ||(item->OutShadow <= 255 && item->OutHighlight <= 255 && outDiff < 255) ||(!(item->Midtones > 9.99 && item->Midtones > 0.1) && item->Midtones != 1.0)))return FALSE;DOUBLE coef = 255.0 / diff;DOUBLE outCoef = outDiff / 255.0;DOUBLE exponent = 1.0 / item->Midtones;for (INT i = 0; i < 256; i ++){INT v;// 計算輸入色階黑白場if (clTable[i] <= (BYTE)item->Shadow)v = 0;else{v = (INT)((clTable[i] - item->Shadow) * coef + 0.5);if (v > 255)v = 255;}// 計算輸入色階灰場v = (INT)(pow(v / 255.0, exponent) * 255.0 + 0.5);// 計算輸出色階clTable[i] = (BYTE)(v * outCoef + item->OutShadow + 0.5);}return TRUE;}BOOL CheckColorLevelData(PColorLevelData clData, BYTE clTables[][256]){BOOL result = FALSE;INT i, j;for (i = 0; i < 3; i ++){for (j = 0; j < 256; j ++)clTables[i][j] = (BYTE)j;}PColorLevelItem item = &clData->Blue;for (i = 0; i < 3; i ++, item ++){if (GetColorLevelTable(item, clTables[i]))result = TRUE;}for (i = 0; i < 3; i ++){if (!GetColorLevelTable(item, clTables[i]))break;result = TRUE;}return result;}// 映像資料色階調整VOID ImageColorLevel(BitmapData *dest, BitmapData *source, PColorLevelData clData){PARGBQuad pd, ps;UINT width, height;INT dstOffset, srcOffset;GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);BYTE clTables[3][256];if (CheckColorLevelData(clData, clTables)){for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset){for (UINT x = 0; x < width; x ++, ps ++, pd ++){pd->Blue = clTables[0][ps->Blue];pd->Green = clTables[1][ps->Green];pd->Red = clTables[2][ps->Red];pd->Alpha = ps->Alpha;}}}else if (dest != source){for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset){for (UINT x = 0; x < width; x ++, ps ++, pd ++){pd->Color = ps->Color;}} }}
下面給一個簡單的映像色階調整函數調用例子:
void __fastcall TForm1::Button1Click(TObject *Sender){BitmapData dest, source;Bitmap *sBmp = new Bitmap(L"..\\..\\media\\source1.jpg");LockBitmap(sBmp, &source);Bitmap *dBmp = new Bitmap(source.Width, source.Height, PixelFormat32bppARGB);LockBitmap(dBmp, &dest);ColorLevelData clData;InitColorLevelData(&clData);clData.RGB.Shadow = 10;clData.RGB.Midtones = 1.2;clData.RGB.Highlight = 240;clData.RGB.OutShadow = 50;clData.RGB.OutHighlight = 200;/*clData.RGB.OutShadow = 255;clData.RGB.OutHighlight = 0;*/ImageColorLevel(&dest, &source, &clData);UnlockBitmap(dBmp, &dest);UnlockBitmap(sBmp, &source);Gdiplus::Graphics g(Canvas->Handle);g.DrawImage(sBmp, 0, 0);g.DrawImage(dBmp, source.Width, 0);delete dBmp;delete sBmp;}
下面是文章《Delphi影像處理 -- 映像色階調整》例子運行介面,第一張綠色通道色階調整,第二張是RGB輸出色階調整到完全顛倒時的負片圖,詳細的映像色階調整介面例子請參考《Delphi影像處理 -- 映像色階調整》。
本文代碼系用BCB XE7編輯和編譯。
因水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:maozefa@hotmail.com
這裡可訪問《C++影像處理 -- 文章索引》。