Delphi图像处理 -
本文在《GDI+ ColorMatrix的完全揭秘》的ColorMatrix原理揭秘的基础上,用Delphi代码来完整实现GDI+的ColorMatrix功能。GDI+中设置ColorMatrix时有2个枚举选项,在实际运用中极少使用,所以代码中以GDI+设置ColorMatrix的缺省方式实现。
先给一个简易的浮点版本,因为该过程没有考虑子图处理,所以称之为简易版本,主要方便阅读者理解ColorMatrix实现原理:
procedure SetColorMatrixF(Data: TImageData; Matrix: TColorMatrix);varI, J, Count: Integer;P: PRGBQuad;MainValue: Boolean;v: Integer;procedure SetPixel;var Pixel: array of Byte; I, J: Integer; ps: PByteArray;begin ps := Pointer(P); // 注意:为使矩阵与ARGB排列顺序一致,以下运算中调整了行列的顺序 for I := 0 to 3 do begin if I < 3 then J := 2 - I else J := I; // 如果只存在主对角线数据,只处理颜色缩放 if MainValue then Pixel := Round(Matrix * ps) // 否则,处理所有颜色变换 else Pixel := Max(0, Min(255, Round(Matrix * ps + Matrix * ps + Matrix * ps + Matrix * ps + Matrix * 255))); end; for I := 0 to 3 do ps := Pixel;end;begin// 处理矩阵中大与255的值(取模),并判断主对角线外是否存在数据 MainValue := True;for I := 0 to 4 do for J := 0 to 4 do begin v := Round(Matrix) div 256; if v > 0 then Matrix := Matrix - 256.0 * v; if (I <> J) and (Matrix <> 0) then MainValue := False; end;Count := Data.Width * Data.Height;P := Data.Scan0;for I := 1 to Count dobegin SetPixel; Inc(P);end;end;
因代码已经有了注释,而实现原理、公式已经在《GDI+ ColorMatrix的完全揭秘》中进行了详尽的介绍,所以本文不再累述。
该过程代码的特点是简单易读,缺点是效率较低,在我的P4 2.8G计算机上,处理一张千万像素的照片,耗时为1000ms左右(不包括GDI+图像格式转换耗时。千万像素的24位格式图像转换为32位格式,耗时就达650ms)。
下面是一个MMX BASM代码的整数ColorMatrix实现过程:
过程定义:// 设置图像颜色矩阵。参数:// Dest输出图,Source原图,Data自身操作图像// Matrix颜色矩阵procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix); overload; {$IF RTLVersion >= 17.00}inline;{$IFEND}procedure ImageSetColorMatrix(var Dest: TImageData; const Source: TImageData; Matrix: TColorMatrix); overload;实现代码:procedure ImageSetColorMatrix(var Dest: TImageData;const Source: TImageData; Matrix: TColorMatrix);asm push esi push edi push ebx call IsValid32 jc @@Exit mov ebx, eax mov eax, edx call IsValid32 jc @@Exit mov edi, ecx // edi = matrix mov esi, 4 // for (i = 4; i >= 0; i --) fldz // {@@iLoop: mov ecx, 4 // for (j = 4; j >= 0; j --)@@jLoop: // { cmp ecx, esi je @@1 mov eax, esi imul eax, 5 add eax, ecx fcom dword ptr fstsw ax sahf je @@1 fstp st(0) // if (i != j && matrix != 0) jmp @@TransformAll// goto TransformAll@@1: dec ecx jns @@jLoop // } dec esi jns @@iLoop // } fstp st(0) fwait // 处理颜色缩放(主对角线的数据) sub esp, 8+2 mov dword ptr , 256 fild dword ptr fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 256 fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 256 fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 256 fmul dword ptr fistp dword ptr // matrixI = matrix * 256 mov eax, ebx call SetCopyRegs32 pxor mm7, mm7 movq mm1, // mm1 = m44m11m22m33@@yLoop: push ecx@@xLoop: movd mm0, punpcklbw mm0, mm7 // mm0 = 00A 00R 00G 00B pmullw mm0, mm1 // mm0 = A*m44 R*m11 G*m22 B*m33 psrlw mm0, 8 // mm0 = A*m44/256 R*m11/256 G*m22/256 B*m33/256 packuswb mm0, mm0 // mm0 = 00 00 00 00 An Rn Gn Bn movd , mm0 add esi, 4 add edi, 4 loop @@xLoop add esi, eax add edi, ebx pop ecx dec edx jnz @@yLoop add esp, 8+2 jmp @@end // 处理全部颜色变换@@TransformAll: sub esp, 5*8+2 // 浮点颜色矩阵行列交换转换为128倍整数 mov dword ptr , 128 fild dword ptr mov esi, esp // esi = matrixI mov eax, edi mov ecx, 4 // for (i = 0; i < 4; i ++)@@cvtLoop: // { fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 128 fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 128 fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 128 fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 128 add esi, 8 add edi, 4 loop @@cvtLoop // } fstp st(0) add eax, 4*5*4 // 浮点数平移量转换为255倍整数 mov dword ptr , 255 fild dword ptr mov ecx, 4 // for (j = 0; j < 4; j ++)@@tLoop: fld st(0) fmul dword ptr fistp dword ptr // matrixI = matrix * 255 add esi, 2 add eax, 4 loop @@tLoop fstp st(0) mov esi, esp // 红蓝(0、2列)交换 mov ecx, 5 // for (i = 0; i < 5; i ++)@@swapLoop: // matrixI <--> matrixI mov ax, .TARGBQuadW.wBlue xchg ax, .TARGBQuadW.wRed mov .TARGBQuadW.wBlue, ax add esi, 8 loop @@swapLoop mov eax, ebx call SetCopyRegs32 pxor mm7, mm7 pcmpeqb mm4, mm4 // mm4 = FF FF FF FF FF FF FF FF psrlw mm4, 15 // mm4 = 00 01 00 01 00 01 00 01@@yLoopA: push ecx@@xLoopA: movd mm0, punpcklbw mm0, mm7 // mm0 = 00A 00R 00G 00B movq mm1, mm0 movq mm2, mm0 movq mm3, mm0 // esp+4: ecx push stack pmaddwd mm0, // mm0 = A*m43+R*m13G*m23+B*m33蓝色行 pmaddwd mm1, // mm1 = A*m42+R*m12G*m22+B*m32绿色行 pmaddwd mm2, // mm2 = A*m41+R*m11G*m21+B*m31红色行 pmaddwd mm3, // mm3 = A*m44+R*m14G*m24+B*m34Alpha行 psrad mm0, 7 // mm0 = A*m43+R*m13/128G*m23+B*m33/128 psrad mm1, 7 // mm1 = A*m42+R*m12/128G*m22+B*m32/128 psrad mm2, 7 // mm2 = A*m41+R*m11/128G*m21+B*m31/128 psrad mm3, 7 // mm3 = A*m44+R*m14/128G*m24+B*m34/128 packssdw mm0, mm1 // mm0 = Ag+RgGg+BgAb+RbGb+Bb packssdw mm2, mm3 // mm2 = Aa+RaGa+BaAr+RrGr+Br pmaddwd mm0, mm4 // mm0 = Ag+Rg+Gg+Bg=GnAb+Rb+Gb+Bb=Bn pmaddwd mm2, mm4 // mm2 = Aa+Ra+Ga+Ba=AnAr+Rr+Gr+Br=Rn packssdw mm0, mm2 // mm0 = 00 An 00 Rn 00 Gn 00 Bn paddd mm0, // mm0 = An+At Rn+Rt Gn+Gt Bn+Bt 平移行 packuswb mm0, mm0 // mm0 = 00 00 00 00 An Rn Gn Bn movd , mm0 add esi, 4 add edi, 4 loop @@xLoopA add esi, eax add edi, ebx pop ecx dec edx jnz @@yLoopA add esp, 5*8+2@@end: emms@@Exit: pop ebx pop edi pop esiend;procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix);beginImageSetColorMatrix(Data, Data, Matrix);end;
该过程中作了更详细的注释,其特点是处理速度较快。在我的机器上,不包括图像格式转换耗时,处理千万像素图片主对角线数据耗时不到50ms,而处理全部变换耗时350-400ms。
下面是一个测试程序代码,该程序代码中,没有使用GDI+,用TJPEGImage对象装入图像转换为TImageData类型后,有上面的ImageSetColorMatrix过程进行处理。该测试代码界面与《GDI+ for VCL基础 -- 颜色调整矩阵ColorMatrix详解》是一样的。有兴趣的朋友可以同里面的测试代码作一下比较。
unit Main;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, ExtCtrls, StdCtrls, Buttons, Grids, ImageUtils;typeTMainForm = class(TForm) Label1: TLabel; PaintBox1: TPaintBox; SpeedButton1: TSpeedButton; SpeedButton2: TSpeedButton; SpeedButton3: TSpeedButton; SpeedButton4: TSpeedButton; StringGrid1: TStringGrid; BitBtn1: TBitBtn; BitBtn3: TBitBtn; BitBtn2: TBitBtn; procedure FormCreate(Sender: TObject); procedure BitBtn1Click(Sender: TObject); procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); procedure StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer; var Value: String); procedure PaintBox1Paint(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure BitBtn2Click(Sender: TObject); procedure SpeedButton2Click(Sender: TObject); procedure SpeedButton1Click(Sender: TObject); procedure SpeedButton4Click(Sender: TObject); procedure SpeedButton3Click(Sender: TObject); procedure BitBtn3Click(Sender: TObject);private { Private declarations } Matrix: TColorMatrix; Source: TImageData; procedure SetMatrix; procedure GetMatrix;public { Public declarations }end;varMainForm: TMainForm;implementationuses JPEG;{$R *.dfm}constColorMatrix: TColorMatrix =( (1.0, 0.0, 0.0, 0.0, 0.0), (0.0, 1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0, 0.0), (0.0, 0.0, 0.0, 0.0, 1.0));procedure TMainForm.GetMatrix;vari, j: Integer;beginfor i := 0 to 4 do for j := 0 to 4 do Matrix := StrToFloat(StringGrid1.Cells);end;procedure TMainForm.SetMatrix;vari, j: Integer;beginfor i := 0 to 4 do for j := 0 to 4 do StringGrid1.Cells := Format('%.2f', ]);end;procedure TMainForm.FormCreate(Sender: TObject);varjpg: TJPEGImage;beginjpg := TJPEGImage.Create;jpg.LoadFromFile('..\..\Media\100_0349.jpg');Source := GetImageData(jpg);jpg.Free;BitBtn2.Click;end;procedure TMainForm.FormDestroy(Sender: TObject);beginFreeImageData(Source);end;// 灰度化procedure TMainForm.SpeedButton1Click(Sender: TObject);varI: Integer;beginMove(ColorMatrix, Matrix, Sizeof(Matrix));for I := 0 to 2 dobegin Matrix := 0.3; Matrix := 0.59; Matrix := 0.11;end;SetMatrix;PaintBox1.Invalidate;end;// 调整亮度0.1(25.5)procedure TMainForm.SpeedButton2Click(Sender: TObject);varI: Integer;beginMove(ColorMatrix, Matrix, Sizeof(Matrix));for I := 0 to 2 do Matrix := 0.1;SetMatrix;PaintBox1.Invalidate;end;// 反色procedure TMainForm.SpeedButton3Click(Sender: TObject);beginMove(ColorMatrix, Matrix, Sizeof(Matrix));Matrix := -1;Matrix := -1;Matrix := -1;SetMatrix;PaintBox1.Invalidate;end;// 半透明显示procedure TMainForm.SpeedButton4Click(Sender: TObject);beginMove(ColorMatrix, Matrix, Sizeof(Matrix));Matrix := 0.5;SetMatrix;PaintBox1.Invalidate;end;procedure TMainForm.StringGrid1DrawCell(Sender: TObject; ACol,ARow: Integer; Rect: TRect; State: TGridDrawState);varr: TRect;Text: string;x: Integer;beginwith StringGrid1 dobegin Text := Cells; if Text = '' then Text := '0.00' else begin x := Pos('.', Text); if x < 1 then Text := Text + '.00' else if x = 1 then Text := '0' + Text; end; r := TRect(Rect); Canvas.FillRect(r); Canvas.Pen.Color := clBtnShadow; Canvas.Rectangle(r); InflateRect(r, -2, -2); DrawText(Canvas.Handle, PChar(Text), Length(Text), r, DT_RIGHT);end;end;procedure TMainForm.StringGrid1GetEditText(Sender: TObject; ACol,ARow: Integer; var Value: String);varv: Double;beginif Value = '' then v := 0.0else v := StrToFloat(Value);Value := Format('%.2f', );end;procedure TMainForm.PaintBox1Paint(Sender: TObject);varData: TImageData;beginData := NewImageData(Source.Width, Source.Height, 0);try ImageSetColorMatrix(Data, Source, Matrix); DrawImage(PaintBox1.Canvas, 10, 10, Source); DrawImage(PaintBox1.Canvas, Data.Width + 20, 10, Data);finally FreeImageData(Data);end;end;procedure TMainForm.BitBtn1Click(Sender: TObject);beginGetMatrix;SetMatrix;PaintBox1.Invalidate;end;procedure TMainForm.BitBtn2Click(Sender: TObject);beginMove(ColorMatrix, Matrix, Sizeof(Matrix));SetMatrix;PaintBox1.Invalidate;end;procedure TMainForm.BitBtn3Click(Sender: TObject);beginClose();end;end.
<div class="highlighter"> 例子代码中的图像显示过程DrawImage见《Delphi图像处理 -- 图像显示》。
页:
[1]