一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

 

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

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

  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 }

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

 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 }

第一张效果图绿色通道色阶调整,第二张效果图是RGB输出色阶调整到完全颠倒时的负片图。
     

 

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

posted on 2020-10-13 11:21  一杯清酒邀明月  阅读(907)  评论(0编辑  收藏  举报