sogotobj 发表于 2013-1-28 12:11:17

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]
查看完整版本: Delphi图像处理 -