閱讀提示:
《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。
《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。
儘可能保持二者內容一致,可相互對照。
本文代碼必須包括文章《Delphi影像處理 -- 資料類型及公用過程》中的ImageData.pas單元。
本人已經寫過幾篇關於亮度調整的文章,但是關於映像的對比調整的過程和文章卻一直沒有寫,其原因是一直沒找到一個好的演算法。可能有人會說,映像的亮度,對比調整是最簡單的圖形操作,其演算法網上可說是一搜一大把,確實如此,可就是這最簡單的操作,網上的文章卻五花八門,我揀幾個試了一下,好像都不太理想,關鍵是演算法太簡單,實際操作效果不好,於是想,Photoshop的對比還是較好的,而且也通用,但偏偏網上沒有介紹它的演算法,用了大半天時間研究了一下,再花了1個來小時寫了個Delphi過程,試了一下,居然和Photoshop的對比調整完全一樣的效果!於是認真寫了個測試程式,把亮度和對比放在一起進行調整(亮度和對比處理過程為各自獨立的,其中亮度過程基本是本BLOG文章《GDI+ 在Delphi程式的應用 -- 調整映像亮度》的代碼),可是效果卻和Photoshop大不一樣了,是什麼原因呢,Photoshop的亮度調整演算法是最簡單的那種,與我的亮度過程做出來的是一樣的(效果比較圖參見《GDI+ 在Delphi程式的應用 -- 線性調整映像亮度》),而前面說了,對比過程演算法也是和Photoshop一樣的,可放在一起調整就不行了,無論是先調整亮度,還是先調整對比都這樣。後來仔細分析了一下,Photoshop是用一個函數處理亮度/對比,而且亮度調整是按對比的正負分別對待的,下面是實現代碼(包括一個簡單的例子):
function _CheckRgb(Rgb: Integer): Integer;asm test eax, eax jge @@1 xor eax, eax ret@@1: cmp eax, 255 jle @@2 mov eax, 255@@2:end;procedure ImageBrightContrast(var Data: TImageData; Bright, Contrast, Threshold: Integer);var vs: TGrayTable; cv: Single; i, v: Integer; height, dataOffset: Integer;begin if (Bright = 0) and (Contrast = 0) then Exit; if Contrast <= -255 then cv := -1 else cv := Contrast / 255; if (Contrast > 0) and (Contrast < 255) then cv := 1 / (1 - cv) - 1; for i := 0 to 255 do begin if Contrast > 0 then v := _CheckRgb(i + bright) else v := i; if Contrast >= 255 then begin if v >= Threshold then v := 255 else v := 0; end else v := _CheckRgb(v + Round((v - Threshold) * cv)); if Contrast <= 0 then vs[i] := _CheckRgb(v + bright) else vs[i] := v; end; asm mov eax, Data call _SetDataRegs mov height, edx mov dataOffset, ebx lea esi, vs;@@yLoop: push ecx@@xLoop: movzx eax, [edi].TARGBQuad.Blue movzx ebx, [edi].TARGBQuad.Green movzx edx, [edi].TARGBQuad.Red mov al, [esi+eax] mov bl, [esi+ebx] mov dl, [esi+edx] mov [edi].TARGBQuad.Blue, al mov [edi].TARGBQuad.Green, bl mov [edi].TARGBQuad.Red, dl add edi, 4 loop @@xLoop pop ecx add edi, dataOffset dec height jnz @@yLoop@@Exit: end;end;procedure TForm1.Button3Click(Sender: TObject);var bmp: TGpBitmap; g: TGpGraphics; data: TImageData;begin bmp := TGpBitmap.Create('..\media\source1.jpg'); g := TGpGraphics.Create(Canvas.Handle); g.DrawImage(bmp, 0, 0); data := LockGpBitmap(bmp); ImageBrightContrast(data, 50, 0, 121); UnlockGpBitmap(bmp, data); g.DrawImage(bmp, data.Width, 0); g.Free; bmp.Free;end;
下面對亮度/對比的原理簡單介紹一下。
一、Photoshop對比演算法。可以用下面的公式來表示:
(1)、nRGB = RGB + (RGB - Threshold) * Contrast / 255
公式中,nRGB表示映像像素新的R、G、B分量,RGB表示映像像素R、G、B分量,Threshold為給定的閾值,Contrast為處理過的對比增量。
Photoshop對於對比增量,是按給定值的正負分別處理的:
當增量等於-255時,是映像對比的下端極限,此時,映像RGB各分量都等於閾值,映像呈全灰色,灰階圖上只有1條線,即閾值灰階;
當增量大於-255且小於0時,直接用上面的公式計算映像像素各分量;
當增量等於 255時,是映像對比的上端極限,實際等於設定映像閾值,映像由最多八種顏色組成,灰階圖上最多8條線,即紅、黃、綠、青、藍、紫及黑與白;
當增量大於0且小於255時,則先按下面公式(2)處理增量,然後再按上面公式(1)計算對比:
(2)、nContrast = 255 * 255 / (255 - Contrast) - 255
公式中的nContrast為處理後的對比增量,Contrast為給定的對比增量。
二、映像亮度調整。本文採用的是最常用的非線性亮度調整(Phoposhop CS3以下版本也是這種亮度調整方式,CS3及以上版本也保留了該亮度調整方式的選項)。
三、映像亮度/對比綜合調整演算法。這個很簡單,當亮度、對比同時調整時,如果對比大於0,現調整亮度,再調整對比;當對比小於0時,則相反,先調整對比,再調整亮度。
在亮度/對比調整函數ImageBrightContrast中,首先按前面介紹的原理製造了一個256個元素大小的尋找表,然後對映像資料逐像素按R、G、B分量值在尋找表中取得調整後的資料,因此處理速度相當快。
下面是對比255時的運行:
《Delphi影像處理》系列使用GDI+單元和說明見文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》。
因水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:maozefa@hotmail.com
這裡可訪問《Delphi影像處理 -- 文章索引》。