阅读提示:
《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++图像处理 -- 文章索引》。
原文地址:http://blog.csdn.net/maozefa/article/details/43971063