六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 40|回复: 0

Delphi图像处理 -

[复制链接]

升级  3.95%

345

主题

345

主题

345

主题

探花

Rank: 6Rank: 6

积分
1079
 楼主| 发表于 2013-1-28 12:11:17 | 显示全部楼层 |阅读模式
本文在《GDI+ ColorMatrix的完全揭秘》的ColorMatrix原理揭秘的基础上,用Delphi代码来完整实现GDI+的ColorMatrix功能。
GDI+中设置ColorMatrix时有2个枚举选项,在实际运用中极少使用,所以代码中以GDI+设置ColorMatrix的缺省方式实现。
先给一个简易的浮点版本,因为该过程没有考虑子图处理,所以称之为简易版本,主要方便阅读者理解ColorMatrix实现原理:
procedure SetColorMatrixF(Data: TImageData; Matrix: TColorMatrix);var  I, J, Count: Integer;  P: PRGBQuad;  MainValue: Boolean;  v: Integer;  procedure SetPixel;  var    Pixel: array[0..3] 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[J] := Round(Matrix[I, I] * ps[J])      // 否则,处理所有颜色变换       else        Pixel[J] := Max(0, Min(255, Round(Matrix[0, I] * ps[2] +                                          Matrix[1, I] * ps[1] +                                          Matrix[2, I] * ps[0] +                                          Matrix[3, I] * ps[3] +                                          Matrix[4, I] * 255)));    end;    for I := 0 to 3 do      ps[I] := Pixel[I];  end;begin  // 处理矩阵中大与255的值(取模),并判断主对角线外是否存在数据   MainValue := True;  for I := 0 to 4 do    for J := 0 to 4 do    begin      v := Round(Matrix[I, J]) div 256;      if v > 0 then        Matrix[I, J] := Matrix[I, J] - 256.0 * v;      if (I <> J) and (Matrix[I, J] <> 0) then        MainValue := False;    end;  Count := Data.Width * Data.Height;  P := Data.Scan0;  for I := 1 to Count do  begin    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 [edi+eax*4]    fstsw       ax    sahf    je          @@1    fstp        st(0)           //     if (i != j && matrix[i, j] != 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 [esp], 256    fild        dword ptr [esp]    fld         st(0)    fmul        dword ptr [edi+(2*5+2)*4]    fistp       dword ptr [esp]     // matrixI[0, 0] = matrix[2, 2] * 256    fld         st(0)    fmul        dword ptr [edi+(1*5+1)*4]    fistp       dword ptr [esp+2]   // matrixI[0, 1] = matrix[1, 1] * 256    fld         st(0)                   fmul        dword ptr [edi+(0*5+0)*4]    fistp       dword ptr [esp+4]   // matrixI[0, 2] = matrix[0, 0] * 256    fmul        dword ptr [edi+(3*5+3)*4]    fistp       dword ptr [esp+6]   // matrixI[0, 3] = matrix[3, 3] * 256    mov         eax, ebx    call        SetCopyRegs32    pxor        mm7, mm7    movq        mm1, [esp]      // mm1 = m44  m11  m22  m33@@yLoop:    push        ecx@@xLoop:    movd        mm0, [esi]    punpcklbw   mm0, mm7        // mm0 = 00  A 00  R 00  G 00  B    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        [edi], 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 [esp], 128    fild        dword ptr [esp]    mov         esi, esp        // esi = matrixI    mov         eax, edi    mov         ecx, 4          // for (i = 0; i < 4; i ++)@@cvtLoop:                      // {    fld         st(0)    fmul        dword ptr [edi]    fistp       dword ptr [esi]     // matrixI[i, 0] = matrix[0, i] * 128    fld         st(0)    fmul        dword ptr [edi+1*5*4]    fistp       dword ptr [esi+2]   // matrixI[i, 1] = matrix[1, i] * 128    fld         st(0)    fmul        dword ptr [edi+2*5*4]    fistp       dword ptr [esi+4]   // matrixI[i, 2] = matrix[2, i] * 128    fld         st(0)    fmul        dword ptr [edi+3*5*4]    fistp       dword ptr [esi+6]   // matrixI[i, 3] = matrix[3, i] * 128    add         esi, 8    add         edi, 4    loop        @@cvtLoop       // }    fstp        st(0)    add         eax, 4*5*4      // 浮点数平移量转换为255倍整数    mov         dword ptr [esi], 255    fild        dword ptr [esi]    mov         ecx, 4          // for (j = 0; j < 4; j ++)@@tLoop:    fld         st(0)    fmul        dword ptr [eax]    fistp       dword ptr [esi]     // matrixI[4, j] = matrix[4, j] * 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[i, 0] <--> matrixI[i, 2]    mov         ax, [esi].TARGBQuadW.wBlue     xchg        ax, [esi].TARGBQuadW.wRed    mov         [esi].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, [esi]    punpcklbw   mm0, mm7        // mm0 = 00  A 00  R 00  G 00  B    movq        mm1, mm0    movq        mm2, mm0    movq        mm3, mm0    // esp+4: ecx push stack    pmaddwd     mm0, [esp+16+4] // mm0 = A*m43+R*m13  G*m23+B*m33  蓝色行    pmaddwd     mm1, [esp+8+4]  // mm1 = A*m42+R*m12  G*m22+B*m32  绿色行    pmaddwd     mm2, [esp+4]    // mm2 = A*m41+R*m11  G*m21+B*m31  红色行    pmaddwd     mm3, [esp+24+4] // mm3 = A*m44+R*m14  G*m24+B*m34  Alpha行    psrad       mm0, 7          // mm0 = A*m43+R*m13/128  G*m23+B*m33/128    psrad       mm1, 7          // mm1 = A*m42+R*m12/128  G*m22+B*m32/128    psrad       mm2, 7          // mm2 = A*m41+R*m11/128  G*m21+B*m31/128    psrad       mm3, 7          // mm3 = A*m44+R*m14/128  G*m24+B*m34/128    packssdw    mm0, mm1        // mm0 = Ag+Rg  Gg+Bg  Ab+Rb  Gb+Bb    packssdw    mm2, mm3        // mm2 = Aa+Ra  Ga+Ba  Ar+Rr  Gr+Br    pmaddwd     mm0, mm4        // mm0 = Ag+Rg+Gg+Bg=Gn  Ab+Rb+Gb+Bb=Bn    pmaddwd     mm2, mm4        // mm2 = Aa+Ra+Ga+Ba=An  Ar+Rr+Gr+Br=Rn    packssdw    mm0, mm2        // mm0 = 00 An 00 Rn 00 Gn 00 Bn       paddd       mm0, [esp+32+4] // mm0 = An+At Rn+Rt Gn+Gt Bn+Bt   平移行    packuswb    mm0, mm0        // mm0 = 00 00 00 00 An Rn Gn Bn    movd        [edi], 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);begin  ImageSetColorMatrix(Data, Data, Matrix);end;
该过程中作了更详细的注释,其特点是处理速度较快。在我的机器上,不包括图像格式转换耗时,处理千万像素图片主对角线数据耗时不到50ms,而处理全部变换耗时350-400ms。
下面是一个测试程序代码,该程序代码中,没有使用GDI+,用TJPEGImage对象装入图像转换为TImageData类型后,有上面的ImageSetColorMatrix过程进行处理。该测试代码界面与《GDI+ for VCL基础 -- 颜色调整矩阵ColorMatrix详解》是一样的。有兴趣的朋友可以同里面的测试代码作一下比较。
unit Main;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, ExtCtrls, StdCtrls, Buttons, Grids, ImageUtils;type  TMainForm = 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;var  MainForm: TMainForm;implementationuses JPEG;{$R *.dfm}const  ColorMatrix: 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;var  i, j: Integer;begin  for i := 0 to 4 do    for j := 0 to 4 do      Matrix[i, j] := StrToFloat(StringGrid1.Cells[j, i]);end;procedure TMainForm.SetMatrix;var  i, j: Integer;begin  for i := 0 to 4 do    for j := 0 to 4 do      StringGrid1.Cells[j, i] := Format('%.2f', [Matrix[i, j]]);end;procedure TMainForm.FormCreate(Sender: TObject);var  jpg: TJPEGImage;begin  jpg := TJPEGImage.Create;  jpg.LoadFromFile('..\..\Media\100_0349.jpg');  Source := GetImageData(jpg);  jpg.Free;  BitBtn2.Click;end;procedure TMainForm.FormDestroy(Sender: TObject);begin  FreeImageData(Source);end;// 灰度化procedure TMainForm.SpeedButton1Click(Sender: TObject);var  I: Integer;begin  Move(ColorMatrix, Matrix, Sizeof(Matrix));  for I := 0 to 2 do  begin    Matrix[0, I] := 0.3;    Matrix[1, I] := 0.59;    Matrix[2, I] := 0.11;  end;  SetMatrix;  PaintBox1.Invalidate;end;// 调整亮度0.1(25.5)procedure TMainForm.SpeedButton2Click(Sender: TObject);var  I: Integer;begin  Move(ColorMatrix, Matrix, Sizeof(Matrix));  for I := 0 to 2 do    Matrix[4, I] := 0.1;  SetMatrix;  PaintBox1.Invalidate;end;// 反色procedure TMainForm.SpeedButton3Click(Sender: TObject);begin  Move(ColorMatrix, Matrix, Sizeof(Matrix));  Matrix[0, 0] := -1;  Matrix[1, 1] := -1;  Matrix[2, 2] := -1;  SetMatrix;  PaintBox1.Invalidate;end;// 半透明显示procedure TMainForm.SpeedButton4Click(Sender: TObject);begin  Move(ColorMatrix, Matrix, Sizeof(Matrix));  Matrix[3, 3] := 0.5;  SetMatrix;  PaintBox1.Invalidate;end;procedure TMainForm.StringGrid1DrawCell(Sender: TObject; ACol,  ARow: Integer; Rect: TRect; State: TGridDrawState);var  r: TRect;  Text: string;  x: Integer;begin  with StringGrid1 do  begin    Text := Cells[ACol, ARow];    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);var  v: Double;begin  if Value = '' then v := 0.0  else v := StrToFloat(Value);  Value := Format('%.2f', [v]);end;procedure TMainForm.PaintBox1Paint(Sender: TObject);var  Data: TImageData;begin  Data := 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);begin  GetMatrix;  SetMatrix;  PaintBox1.Invalidate;end;procedure TMainForm.BitBtn2Click(Sender: TObject);begin  Move(ColorMatrix, Matrix, Sizeof(Matrix));  SetMatrix;  PaintBox1.Invalidate;end;procedure TMainForm.BitBtn3Click(Sender: TObject);begin  Close();end;end.
<div class="highlighter"> 例子代码中的图像显示过程DrawImage见《Delphi图像处理 -- 图像显示》。
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表