阅读提示:
《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。
《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。
尽可能保持二者内容一致,可相互对照。
本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元。
对图像的翻转处理,是产生一个与原图像在水平方向或者垂直方向相对称的镜像图像。
图像翻转的原理很简单,就是以图像的中间列像素(水平方向),或者中间行像素(垂直方向)为基列(行),将图像第一列(行)的像素与图像最后一列(行)的像素相交换,图像第二列(行)的像素与图像倒数第二列(行)的像素相交换......,直至基列(行)为止,如果图像的列(行)数是偶数,则头尾对应的列(行),包括基列(行)在内两两交换,如果图像的列(行)数是偶数,则基列(行)不变,其它头尾对应的列(行)两两交换。
下面是图像翻转的实现代码:
type // 图像翻转方式: 水平,垂直 TReversalMode = (rmHorizontal, rmVertical); 过程定义: // 翻转图像 procedure ImageReversal(var Data: TImageData; Mode: TReversalMode); 实现代码: procedure ImageReversal(var Data: TImageData; Mode: TReversalMode); var srcOffset, dstOffset: Integer; asm push esi push edi push ebx xchg eax, edx mov esi, [edx].TImageData.Scan0 mov ebx, [edx].TImageData.Stride mov ecx, [edx].TImageData.Width mov edx, [edx].TImageData.Height mov srcOffset, ebx // srcOffset = Data.Stride cmp al, rmHorizontal je @@1 mov edi, edx // if (Mode == rmVertical) dec edi // { imul edi, ebx // esi = Data.Scan0 add edi, esi // edi = Data.Scan0 + shr edx, 1 // (Data.Height - 1) * Data.Stride neg ebx // dstOffset = -Data.Stride mov eax, 4 // edx = Data.Height / 2; eax = 4 jmp @@2 // } @@1: mov edi, esi // else mov esi, ecx // { dec esi // edi = Data.Scan0 shl esi, 2 // esi = Data.Scan0 + (Data.Width - 1) * 4 add esi, edi // dstOffset = Data.Stride shr ecx, 1 // ecx = Data.Width / 2; eax = -4 mov eax, -4 // } @@2: mov dstOffset, ebx mov ebx, eax // ebx = eax // 4 or -4 cld @yLoop: // for (y = edx; y > 0; y --) push ecx // { push esi // Save(esi) push edi // Save(edi) @xLoop: // for (x = ecx; x > 0; x --) mov eax, [edi] // { xchg eax, [esi] // xchg([esi], [edi]) stosd // edi += 4 add esi, ebx // esi += ebx loop @xLoop // } pop edi // Reset(edi) pop esi // Reset(esi) pop ecx add esi, srcOffset // esi += srcOffset add edi, dstOffset // edi += dstOffset dec edx jnz @yLoop // } pop ebx pop edi pop esi end;
下面给出一个完全等价的纯PAS代码图像翻转过程代码,供不太懂BASM代码的朋友学习和使用:
procedure ImageReversal(Data: TImageData; Mode: TReversalMode); var x, y: Integer; Width, Height: Integer; ps, pd, ps0, pd0: PLongWord; tmp: LongWord; Delta, dstOffset: Integer; begin Width := Data.Width; Height := Data.Height; if Mode = rmHorizontal then begin pd := Data.Scan0; ps := PLongWord(Integer(pd) + (Data.Width shl 2) - 4); dstOffset := Data.Stride; Delta := -1; Width := Width shr 1; end else begin ps := Data.Scan0; pd := PLongWord(Integer(ps) + (Data.Height - 1) * Data.Stride); dstOffset := -Data.Stride; Delta := 1; Height := Height shr 1; end; for y := 1 to Height do begin pd0 := pd; ps0 := ps; for x := 1 to Width do begin tmp := pd0^; pd0^ := ps0^; ps0^ := tmp; Inc(pd0); Inc(ps0, Delta); end; Inc(Integer(ps), Data.Stride); Inc(Integer(pd), dstOffset); end; end;
下面是个图像水平翻转的例子代码:
procedure TForm1.Button2Click(Sender: TObject);
var
bmp: TGpBitmap;
g: TGpGraphics;
data: TImageData;
begin
bmp := TGpBitmap.Create('..\..\media\001-1.jpg');
g := TGpGraphics.Create(Canvas.Handle);
g.DrawImage(bmp, 0, 0);
data := LockGpBitmap(bmp);
ImageReversal(data, rmHorizontal);
UnlockGpBitmap(bmp, data);
g.DrawImage(bmp, data.Width, 0);
g.Free;
bmp.Free;
end;
例子运行后的镜像效果图如下,左边是原图,右边是水平镜像图:
《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]
这里可访问《Delphi图像处理 -- 文章索引》。