C++图像处理 -- 图像色阶调整

原网址:http://blog.****.net/maozefa/article/details/43971063

阅读提示

    《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各分量通道及整体颜色通道,如果每个通道单独调整,将是比较麻烦和耗时的,本文采用色阶表替换法,可一次性完成所有四个通道的色阶调整。

    下面是图像色阶调整的代码:

[cpp] view plain copy
  1. // 色阶项结构  
  2. typedef struct  
  3. {  
  4.     UINT Shadow;  
  5.     FLOAT Midtones;  
  6.     UINT Highlight;  
  7.     UINT OutShadow;  
  8.     UINT OutHighlight;  
  9. }ColorLevelItem, *PColorLevelItem;  
  10.   
  11. typedef struct  
  12. {  
  13.     ColorLevelItem Blue;  
  14.     ColorLevelItem Green;  
  15.     ColorLevelItem Red;  
  16.     ColorLevelItem RGB;  
  17. }ColorLevelData, *PColorLevelData;  
  18.   
  19. VOID InitColorLevelData(PColorLevelData clData)  
  20. {  
  21.     PColorLevelItem item = &clData->Blue;  
  22.     for (INT i = 0; i < 4; i ++, item ++)  
  23.     {  
  24.         item->Shadow = item->OutShadow = 0;  
  25.         item->Highlight = item->OutHighlight = 255;  
  26.         item->Midtones = 1.0;  
  27.     }  
  28. }  
  29.   
  30. BOOL GetColorLevelTable(PColorLevelItem item, LPBYTE clTable)  
  31. {  
  32.     INT diff = (INT)(item->Highlight - item->Shadow);  
  33.     INT outDiff = (INT)(item->OutHighlight - item->OutShadow);  
  34.   
  35.     if (!((item->Highlight <= 255 && diff < 255 && diff >= 2) ||  
  36.         (item->OutShadow <= 255 && item->OutHighlight <= 255 && outDiff < 255) ||  
  37.         (!(item->Midtones > 9.99 && item->Midtones > 0.1) && item->Midtones != 1.0)))  
  38.         return FALSE;  
  39.   
  40.     DOUBLE coef = 255.0 / diff;  
  41.     DOUBLE outCoef = outDiff / 255.0;  
  42.     DOUBLE exponent = 1.0 / item->Midtones;  
  43.   
  44.     for (INT i = 0; i < 256; i ++)  
  45.     {  
  46.         INT v;  
  47.         // 计算输入色阶黑白场  
  48.         if (clTable[i] <= (BYTE)item->Shadow)  
  49.             v = 0;  
  50.         else  
  51.         {  
  52.             v = (INT)((clTable[i] - item->Shadow) * coef + 0.5);  
  53.             if (v > 255)  
  54.                 v = 255;  
  55.         }  
  56.         // 计算输入色阶灰场  
  57.         v = (INT)(pow(v / 255.0, exponent) * 255.0 + 0.5);  
  58.         // 计算输出色阶  
  59.         clTable[i] = (BYTE)(v * outCoef + item->OutShadow + 0.5);  
  60.     }  
  61.     return TRUE;  
  62. }  
  63.   
  64. BOOL CheckColorLevelData(PColorLevelData clData, BYTE clTables[][256])  
  65. {  
  66.     BOOL result = FALSE;  
  67.     INT i, j;  
  68.     for (i = 0; i < 3; i ++)  
  69.     {  
  70.         for (j = 0; j < 256; j ++)  
  71.             clTables[i][j] = (BYTE)j;  
  72.     }  
  73.     PColorLevelItem item = &clData->Blue;  
  74.     for (i = 0; i < 3; i ++, item ++)  
  75.     {  
  76.         if (GetColorLevelTable(item, clTables[i]))  
  77.             result = TRUE;  
  78.     }  
  79.     for (i = 0; i < 3; i ++)  
  80.     {  
  81.         if (!GetColorLevelTable(item, clTables[i]))  
  82.             break;  
  83.         result = TRUE;  
  84.     }  
  85.     return result;  
  86. }  
  87.   
  88. // 图像数据色阶调整  
  89. VOID ImageColorLevel(BitmapData *dest, BitmapData *source, PColorLevelData clData)  
  90. {  
  91.     PARGBQuad pd, ps;  
  92.     UINT width, height;  
  93.     INT dstOffset, srcOffset;  
  94.     GetDataCopyParams(dest, source, width, height, pd, ps, dstOffset, srcOffset);  
  95.   
  96.     BYTE clTables[3][256];  
  97.     if (CheckColorLevelData(clData, clTables))  
  98.     {  
  99.         for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset)  
  100.         {  
  101.             for (UINT x = 0; x < width; x ++, ps ++, pd ++)  
  102.             {  
  103.                 pd->Blue = clTables[0][ps->Blue];  
  104.                 pd->Green = clTables[1][ps->Green];  
  105.                 pd->Red = clTables[2][ps->Red];  
  106.                 pd->Alpha = ps->Alpha;  
  107.             }  
  108.         }  
  109.     }  
  110.     else if (dest != source)  
  111.     {  
  112.         for (UINT y = 0; y < height; y ++, ps += srcOffset, pd += dstOffset)  
  113.         {  
  114.             for (UINT x = 0; x < width; x ++, ps ++, pd ++)  
  115.             {  
  116.                 pd->Color = ps->Color;  
  117.             }  
  118.         }  
  119.     }  
  120. }  

    下面给一个简单的图像色阶调整函数调用例子:

[cpp] view plain copy
  1. void __fastcall TForm1::Button1Click(TObject *Sender)  
  2. {  
  3.     BitmapData dest, source;  
  4.   
  5.     Bitmap *sBmp = new Bitmap(L"..\\..\\media\\source1.jpg");  
  6.     LockBitmap(sBmp, &source);  
  7.   
  8.     Bitmap *dBmp = new Bitmap(source.Width, source.Height, PixelFormat32bppARGB);  
  9.     LockBitmap(dBmp, &dest);  
  10.   
  11.     ColorLevelData clData;  
  12.     InitColorLevelData(&clData);  
  13.   
  14.     clData.RGB.Shadow = 10;  
  15.     clData.RGB.Midtones = 1.2;  
  16.     clData.RGB.Highlight = 240;  
  17.     clData.RGB.OutShadow = 50;  
  18.     clData.RGB.OutHighlight = 200;  
  19.   
  20. /* 
  21.     clData.RGB.OutShadow = 255; 
  22.     clData.RGB.OutHighlight = 0; 
  23. */  
  24.     ImageColorLevel(&dest, &source, &clData);  
  25.   
  26.     UnlockBitmap(dBmp, &dest);  
  27.     UnlockBitmap(sBmp, &source);  
  28.   
  29.     Gdiplus::Graphics g(Canvas->Handle);  
  30.     g.DrawImage(sBmp, 0, 0);  
  31.     g.DrawImage(dBmp, source.Width, 0);  
  32.   
  33.     delete dBmp;  
  34.     delete sBmp;  
  35. }  

    下面是文章Delphi图像处理 -- 图像色阶调整例子运行界面效果图,第一张效果图绿色通道色阶调整,第二张效果图是RGB输出色阶调整到完全颠倒时的负片图,详细的图像色阶调整界面例子请参考Delphi图像处理 -- 图像色阶调整》。
C++图像处理 -- 图像色阶调整     

C++图像处理 -- 图像色阶调整


    本文代码系用BCB XE7编辑和编译。


    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]

    这里可访问《C++图像处理 -- 文章索引