閱讀提示:
《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。
《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。
儘可能保持二者內容一致,可相互對照。
本文代碼必須包括文章《Delphi影像處理 -- 資料類型及公用過程》中的ImageData.pas單元。
對映像的翻轉處理,是產生一個與原映像在水平方向或者垂直方向相對稱的鏡像映像。
映像翻轉的原理很簡單,就是以映像的中間列像素(水平方向),或者中間行像素(垂直方向)為基列(行),將映像第一列(行)的像素與映像最後一列(行)的像素相交換,映像第二列(行)的像素與映像倒數第二列(行)的像素相交換......,直至基列(行)為止,如果映像的列(行)數是偶數,則頭尾對應的列(行),包括基列(行)在內兩兩交換,如果映像的列(行)數是偶數,則基列(行)不變,其它頭尾對應的列(行)兩兩交換。
下面是映像翻轉的實現代碼:
type<br /> // 映像翻轉方式: 水平,垂直<br /> TReversalMode = (rmHorizontal, rmVertical);</p><p>流程定義:</p><p> // 翻轉映像<br /> procedure ImageReversal(var Data: TImageData; Mode: TReversalMode);</p><p>實現代碼:</p><p>procedure ImageReversal(var Data: TImageData; Mode: TReversalMode);<br />var<br /> srcOffset, dstOffset: Integer;<br />asm<br /> push esi<br /> push edi<br /> push ebx<br /> xchg eax, edx<br /> mov esi, [edx].TImageData.Scan0<br /> mov ebx, [edx].TImageData.Stride<br /> mov ecx, [edx].TImageData.Width<br /> mov edx, [edx].TImageData.Height<br /> mov srcOffset, ebx // srcOffset = Data.Stride<br /> cmp al, rmHorizontal<br /> je @@1<br /> mov edi, edx // if (Mode == rmVertical)<br /> dec edi // {<br /> imul edi, ebx // esi = Data.Scan0<br /> add edi, esi // edi = Data.Scan0 +<br /> shr edx, 1 // (Data.Height - 1) * Data.Stride<br /> neg ebx // dstOffset = -Data.Stride<br /> mov eax, 4 // edx = Data.Height / 2; eax = 4<br /> jmp @@2 // }<br /> @@1:<br /> mov edi, esi // else<br /> mov esi, ecx // {<br /> dec esi // edi = Data.Scan0<br /> shl esi, 2 // esi = Data.Scan0 + (Data.Width - 1) * 4<br /> add esi, edi // dstOffset = Data.Stride<br /> shr ecx, 1 // ecx = Data.Width / 2; eax = -4<br /> mov eax, -4 // }<br /> @@2:<br /> mov dstOffset, ebx<br /> mov ebx, eax // ebx = eax // 4 or -4<br /> cld<br /> @yLoop: // for (y = edx; y > 0; y --)<br /> push ecx // {<br /> push esi // Save(esi)<br /> push edi // Save(edi)<br /> @xLoop: // for (x = ecx; x > 0; x --)<br /> mov eax, [edi] // {<br /> xchg eax, [esi] // xchg([esi], [edi])<br /> stosd // edi += 4<br /> add esi, ebx // esi += ebx<br /> loop @xLoop // }<br /> pop edi // Reset(edi)<br /> pop esi // Reset(esi)<br /> pop ecx<br /> add esi, srcOffset // esi += srcOffset<br /> add edi, dstOffset // edi += dstOffset<br /> dec edx<br /> jnz @yLoop // }<br /> pop ebx<br /> pop edi<br /> pop esi<br />end;<br />
下面給出一個完全等價的純PAS代碼映像翻轉過程代碼,供不太懂BASM代碼的朋友學習和使用:
procedure ImageReversal(Data: TImageData; Mode: TReversalMode);<br />var<br /> x, y: Integer;<br /> Width, Height: Integer;<br /> ps, pd, ps0, pd0: PLongWord;<br /> tmp: LongWord;<br /> Delta, dstOffset: Integer;<br />begin<br /> Width := Data.Width;<br /> Height := Data.Height;<br /> if Mode = rmHorizontal then<br /> begin<br /> pd := Data.Scan0;<br /> ps := PLongWord(Integer(pd) + (Data.Width shl 2) - 4);<br /> dstOffset := Data.Stride;<br /> Delta := -1;<br /> Width := Width shr 1;<br /> end<br /> else<br /> begin<br /> ps := Data.Scan0;<br /> pd := PLongWord(Integer(ps) + (Data.Height - 1) * Data.Stride);<br /> dstOffset := -Data.Stride;<br /> Delta := 1;<br /> Height := Height shr 1;<br /> end;<br /> for y := 1 to Height do<br /> begin<br /> pd0 := pd;<br /> ps0 := ps;<br /> for x := 1 to Width do<br /> begin<br /> tmp := pd0^;<br /> pd0^ := ps0^;<br /> ps0^ := tmp;<br /> Inc(pd0);<br /> Inc(ps0, Delta);<br /> end;<br /> Inc(Integer(ps), Data.Stride);<br /> Inc(Integer(pd), dstOffset);<br /> end;<br />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》。
因水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:maozefa@hotmail.com
這裡可訪問《Delphi影像處理 -- 文章索引》。