Delphi图像处理 -- 灰色浮雕

阅读提示:

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元和《Delphi图像处理 -- 平面几何变换类TransformMatrix.pas单元

 

    实现图像浮雕效果的一般原理是,将图像上每个像素点与其对角线的像素点形成差值,使相似颜色值淡化,不同颜色值突出,从而产生纵深感,达到浮雕的效果,具体的做法是用处于对角线的2个像素值相减,再加上一个背景常数,一般为128而成。这种算法的特点是简单快捷,缺点是不能调节图像浮雕效果的角度和深度。

    用Photoshop实现的图像浮雕效果,可以任意调节浮雕角度和深度(2个像素点的距离),还可以调整浮雕像素差值的数量。其基本算法原理和一般浮雕效果相同,但是具体做法不一样:对每个要处理的像素点,首先按照浮雕角度和深度计算处2个相应点的位置,然后计算这2个位置的颜色值,并使之形成差值,再乘上浮雕差值数量百分比,最后加上128的背景色。注意,这里计算的2个相应点是逻辑点,而不是实际的像素点,比如实现一个45度角,深度为3的图像浮雕效果,对每个像素点P(x, y),其对应的2个逻辑点的位置分别是P0(x - 3 * 0.7071 / 2, y - 3 * 0.7071 / 2)和P1(x + 3 * 0.7071 / 2, y + 3 * 0.7071 / 2),显然,对于这样的2个逻辑点,是不能直接从图像中找到其对应的像素点的,如果简单地对其四舍五入处理,将会造成大量的,由不同角度和深度而形成的相同的浮雕效果,这可不是我们想要的结果,而且使浮雕角度和深度参数失去了它原本的意义。为此,必须对原始图像按浮雕角度和深度进行缩放后,再对每个像素点进行浮雕效果处理,完毕再缩放回原图的大小,从而完成整个浮雕效果过程。下面是我经过反复试验后,写的Photoshop浮雕效果实现过程代码:

 

procedure GetSrcColor;
asm
    push      esi           // esi = src.Scan0
    mov       eax, ecx
    sar       eax, 12
    imul      eax, ebx
    add       esi, eax      // esi += (y / 4096 * src.Stride)
    mov       eax, edx
    sar       eax, 12
    shl       eax, 2
    add       esi, eax      // esi +=  (x / 4096 * 4)
    call      _GetBilinearColor
    movd      eax, xmm0
    movd      mm0, eax
    punpcklbw mm0, mm7      // return mm0 = ARGB (word * 4)
    pop       esi
end;

procedure ImageGraySculpture(var Data: TImageData;
  Angle: Single; Size: LongWord; Num: LongWord = 100);
var
  x, y, Radius: Integer;
  xDelta, yDelta: Integer;
  width, height: Integer;
  dstOffset: Integer;
  src: TImageData;
begin
  Radius := (Size + 1) shr 1; // 图像边框扩展半径
  Angle := PI * Angle / 180;
  Size := Size shl 12;        // Size = Size * 256
  Num := (Num shl 9) div 100; // Num *= 5.12 转换为2的幂
  xDelta := Round(Cos(Angle) * Size);
  yDelta := Round(Sin(Angle) * Size);
  x := (Radius shl 12) - (xDelta div 2);
  y := (Radius shl 12) - (yDelta div 2);
  width := x + (Data.Width shl 12);
  height := y + (Data.Height shl 12);
  if Data.AlphaFlag then
    ArgbConvertPArgb(Data);
  src := _GetExpandData(Data, Radius);
  asm
    mov       eax, Data
    lea       edx, src
    call      _SetCopyRegs
    mov       dstOffset, ebx
    movq      mm5, qword ptr ArgbTab[128*8]// mm5 = 00 128 00 128 00 128 00 128
    movd      mm6, Num        // mm6 = 00 Num 00 Num 00 Num 00 Num
    punpcklwd mm6, mm6
    punpcklwd mm6, mm6
    pxor      mm7, mm7        // mm7 = 00 00 00 00 00 00 00 00
    pxor      xmm7, xmm7
    mov       esi, src.Scan0
    mov       ebx, src.Stride
    mov       ecx, y          // for (; y < Height; y += 4096)
@@yLoop:                      // {
		mov 	    edx, x          //   for (; x < Width; x += 4096)
@@xLoop:                      //   {
    add       ecx, yDelta     //     y1 = y + yDelta
    add       edx, xDelta     //     x1 = x + xDelta
    call      GetSrcColor     //
    movq      mm1, mm0        //     mm1 = 00 A1 00 R1 00 G1 00 B1
    sub       ecx, yDelta     //     y0 = y - yDelta
    sub       edx, xDelta     //     x0 = x - xDelta
    call      GetSrcColor     //     mm0 = 00 A0 00 R0 00 G0 00 B0
    psubw     mm0, mm1        //     mm0 = A0-A1 R0-R1 G0-G1 B0-B1
    psllw     mm0, 7          //     mm0 = mm0 * 128 * Num / 65536
    pmulhw    mm0, mm6
    paddw     mm0, mm5        //     mm0 = A+128 R+128 G+128 B+128
    packuswb  mm0, mm7        //     mm0 = 00 00 00 00 A R G B
    mov       al, [edi].TARGBQuad.Alpha
    movd      [edi], mm0      //     *edi = mm0
    mov       [edi].TARGBQuad.Alpha, al
    add       edi, 4          //     edi += 4
    add       edx, 1000h
    cmp       edx, width
    jl        @@xLoop         //   }
    add       ecx, 1000h
    add       edi, dstOffset
    cmp       ecx, height
    jl        @@yLoop         // }
    emms
  end;
  FreeImageData(src);
  if Data.AlphaFlag then
    PArgbConvertArgb(Data);
end;

    下面是个演示例子代码:

 

procedure TForm1.Button3Click(Sender: TObject);
var
  bmp: TGpBitmap;
  g: TGpGraphics;
  data: TImageData;
begin
  bmp := TGpBitmap.Create('..\media\20041001.jpg');
  g := TGpGraphics.Create(Canvas.Handle);
  g.DrawImage(bmp, 0, 0);
  data := LockGpBitmap(bmp);
  ImageGraySculpture(data, 45, 3, 100);
  UnlockGpBitmap(bmp, data);
  g.DrawImage(bmp, 0, data.Height);
  g.Free;
  bmp.Free;
end;

    运行效果图:

    和Photoshop浮雕效果对比,基本一致。

     补记:在文章评论中,有人说灰色浮雕不带彩,其实是不正确的,我测试过网上和书上的很多代码,也测试过Photoshop,都是有可能带彩的,在浮雕深度很小而且原始图像色彩较平淡时也有可能不带彩,可以把我上面的原图裁下来,用Photoshop一试就清楚了。当然,如果你要达到完全不带彩也很简单,在浮雕处理前做一个图像灰度化即可。如果灰度浮雕完全不带彩,程序代码可得到简化,速度至少可再提高60%以上。

 

    《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

    这里可访问《Delphi图像处理 -- 文章索引》。

 

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值