注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

鑫淼梦园的博客

圆你的梦想 从这里开始

 
 
 

日志

 
 

Delphi图像处理 -- 填充浮雕  

2013-06-24 17:56:46|  分类: delphi xe4 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
分类: Delphi图像处理 Delphi2008-09-03 23:20 4472人阅读 评论(18) 收藏 举报

阅读提示:

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

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

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

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

 

    这个填充浮雕效果过程代码已经完成好几天了,但是一直没敢在BLOG上发表,因为这是我在研究Photoshop浮雕效果做实验时,无意中写的一段代码,当时就感觉这个效果虽然不是我想要的Photoshop浮雕效果,但比Photoshop浮雕效果的应用价值应该不会差,可以说是各有特色。只是当时感觉处理速度较慢,同时顾虑该浮雕效果是我妙想天开,发表后会不会有引起嘲笑。经过了几天的改进和原理论证,我觉得该浮雕效果从原理上是说得通的,而改进后的速度一般情况下比彩色浮雕处理快,在浮雕深度较小(< 8)的时候,比灰色浮雕处理还快,所以还是决定发表在这里,供大家讨论其实用性,或给出改进意见。至于这种浮雕效果的名称,刚开始时因其效果类似石雕,准备取名石雕效果,经改进后,可用任何颜色或者图案进行填充浮雕画面,所以取名填充浮雕效果。

下面先给出浮雕过程处理代码

  1. procedure GetSrcColor;  
  2. asm  
  3.     push      esi           // esi = src.Scan0  
  4.     mov       eax, ecx  
  5.     sar       eax, 12  
  6.     imul      eax, ebx  
  7.     add       esi, eax      // esi += (y / 4096 * src.Stride)  
  8.     mov       eax, edx  
  9.     sar       eax, 12  
  10.     shl       eax, 2  
  11.     add       esi, eax      // esi +=  (x / 4096 * 4)  
  12.     call      _GetBilinearColor  
  13.     movd      eax, xmm0  
  14.     movd      mm0, eax  
  15.     punpcklbw mm0, mm7      // return mm0 = ARGB (word * 4)  
  16.     pop       esi  
  17. end;  
  18.   
  19. // 实填充浮雕。Data 图像数据结构, Angle 角度, Size 长度, Color 填充色  
  20. procedure ImageSolidSculpture(var Data: TImageData; Angle: Single;  
  21.   Size: LongWord; Color: TARGB);  
  22. var  
  23.   x, y, Radius: Integer;  
  24.   xDelta, yDelta: Integer;  
  25.   width, height: Integer;  
  26.   dstOffset, divSize: Integer;  
  27.   src: TImageData;  
  28. begin  
  29.   if Size > 128 then Size := 128;  
  30.   divSize := DivTab[Size];  
  31.   Radius := (Size + 1shr 1;        // 图像边框扩展半径  
  32.   Angle := PI * Angle / 180;  
  33.   xDelta := Round(Cos(Angle) * 4096);  
  34.   yDelta := Round(Sin(Angle) * 4096);  
  35.   x := (Radius shl 12) - xDelta * Size div 2;  
  36.   y := (Radius shl 12) - yDelta * Size div 2;  
  37.   width := x + (Data.Width shl 12);  
  38.   height := Data.Height;  
  39.   if Data.AlphaFlag then  
  40.     ArgbConvertPArgb(Data);  
  41.   src := _GetExpandData(Data, Radius);  
  42.   asm  
  43.     push      esi  
  44.     push      edi  
  45.     push      ebx  
  46.     mov       eax, Data  
  47.     lea       edx, src  
  48.     call      _SetCopyRegs  
  49.     mov       dstOffset, ebx  
  50.     mov       eax, Size       // mm3 = word * 4 = Size div to mul  
  51.     movq      mm3, qword ptr MMDivTab[eax*8]  
  52.     pxor      xmm7, xmm7  
  53.     pxor      mm7, mm7        // xmm7 = mm7 = 0  
  54.     movd      mm2, Color      // mm2 = word Color  
  55.     punpcklbw mm2, mm7          
  56.     mov       ebx, src.Stride  
  57.     mov       ecx, y          // for (; y < Height; y += 4096)  
  58. @@yLoop:                      // {  
  59.         mov       edx, x          //   for (; x < Width; x += 4096)  
  60. @@xLoop:                      //   {  
  61.     push      edx  
  62.     push      ecx  
  63.     push      edi             //     x1 = x, y1 = y  
  64.     pxor      mm1, mm1  
  65.     mov       edi, Size       //     for (i = Size - 1; Size > 0; i --)  
  66. @@addLoop:                    //     {  
  67.     call      GetSrcColor     //       mm1 += GetSrcColor(x1, y1)  
  68.     paddw     mm1, mm0  
  69.     add       ecx, yDelta     //       y1 += yDelta  
  70.     add       edx, xDelta     //       x1 += xDelta  
  71.     dec       edi  
  72.     jnz       @@addLoop       //     }  
  73.     call      GetSrcColor     //     mm0 = GetSrcColor(x1, y1)  
  74.     pmulhuw   mm1, mm3        //     mm1 /= Size  
  75.     psubw     mm1, mm0        //     mm1 -= mm0  
  76.     paddw     mm1, mm2        //     mm1 += Color  
  77.     packuswb  mm1, mm7  
  78.     pop       edi  
  79.     pop       ecx  
  80.     pop       edx  
  81.     mov       al, [edi].TARGBQuad.Alpha  
  82.     movd      [edi], mm1      //     *edi = mm1  
  83.     mov       [edi].TARGBQuad.Alpha, al  
  84.     add       edi, 4          //     edi ++  
  85.     add       edx, 1000h  
  86.     cmp       edx, width  
  87.     jl        @@xLoop         //   }  
  88.     add       edi, dstOffset  
  89.     add       ecx, 1000h  
  90.     dec       height  
  91.     jnz       @@yLoop  
  92.     emms  
  93.     pop       ebx  
  94.     pop       edi  
  95.     pop       esi  
  96.   end;  
  97.   FreeImageData(src);  
  98.   if Data.AlphaFlag then  
  99.     PArgbConvertArgb(Data);  
  100. end;  
  101.   
  102. // 图案填充浮雕。Data 图像数据结构, Angle 角度, Size 长度, FillData 填充图像数据结构  
  103. procedure ImageTextureSculpt(var Data: TImageData; Angle: Single;  
  104.   Size: LongWord; const FillData: TImageData);  
  105.   
  106.   // 调整fillData宽度和高度为其最大2的幂,并拷贝到Result  
  107.   function AdjustFillData: TImageData;  
  108.   asm  
  109.     push    esi  
  110.     push    edi  
  111.     push    ebx  
  112.     push    eax  
  113.     push    eax  
  114.     push    eax           // NewImageData Result param  
  115.     mov     edi, fillData  
  116.     bsr     ecx, [edi].TImageData.Width  
  117.     xor     eax, eax      // mov eax, 1  
  118.     bts     eax, ecx      // shl eax, cl (width max bit)  
  119.     bsr     ecx, [edi].TImageData.Height  
  120.     xor     edx, edx      // mov edx, 1  
  121.     bts     edx, ecx      // shl edx, cl (height max bit)  
  122.     mov     ecx, pf32bit  
  123.     call    NewImageData  // Result = NewImageData(width, height, pf32bit)  
  124.     pop     eax  
  125.     mov     edx, edi  
  126.     call    _SetCopyRegs  
  127.   @@cpyLoop:  
  128.     push    ecx  
  129.     rep     movsd  
  130.     pop     ecx  
  131.     add     esi, eax  
  132.     dec     edx  
  133.     jnz     @@cpyLoop  
  134.     pop     eax  
  135.     // (Result.Width - 1) * 4: 宽度的最大余数*4,作为x*4的掩码  
  136.     mov     edx, [eax].TImageData.Width  
  137.     dec     edx  
  138.     shl     edx, 2  
  139.     mov     [eax].TImageData.Width, edx  
  140.     // Result.Height - 1: 高度的最大余数作为y的掩码  
  141.     dec     [eax].TImageData.Height  
  142.     pop     ebx  
  143.     pop     edi  
  144.     pop     esi  
  145.   end;  
  146.   
  147. var  
  148.   x, y, Radius: Integer;  
  149.   xDelta, yDelta: Integer;  
  150.   width, height: Integer;  
  151.   dstOffset: Integer;  
  152.   src, back: TImageData;  
  153. begin  
  154.   if Size > 128 then Size := 128;  
  155.   Radius := (Size + 1shr 1;        // 图像边框扩展半径  
  156.   Angle := PI * Angle / 180;  
  157.   xDelta := Round(Cos(Angle) * 4096);  
  158.   yDelta := Round(Sin(Angle) * 4096);  
  159.   x := (Radius shl 12) - xDelta * Size div 2;  
  160.   y := (Radius shl 12) - yDelta * Size div 2;  
  161.   width := x + (Data.Width shl 12);  
  162.   height := Data.Height;  
  163.   if Data.AlphaFlag then  
  164.     ArgbConvertPArgb(Data);  
  165.   src := _GetExpandData(Data, Radius);  
  166.   back := AdjustFillData;  
  167.   asm  
  168.     push      esi  
  169.     push      edi  
  170.     push      ebx  
  171.     mov       eax, Data  
  172.     lea       edx, src  
  173.     call      _SetCopyRegs  
  174.     mov       dstOffset, ebx  
  175.     mov       eax, Size       // mm3 = word * 4 = Size div to mul  
  176.     movq      mm3, qword ptr MMDivTab[eax*8]  
  177.     pxor      xmm7, xmm7      // xmm7 = mm7 = 0  
  178.     pxor      mm7, mm7          
  179.     mov       ebx, src.Stride  
  180.     mov       ecx, y          // for (; y < Height; y += 4096)  
  181. @@yLoop:                      // {  
  182.         mov       edx, x          //   for (; x < Width; x += 4096)  
  183. @@xLoop:                      //   {  
  184.     push      edx  
  185.     push      ecx               
  186.     push      edi  
  187.     push      edx  
  188.     push      ecx             //     x1 = x, y1 = y  
  189.     mov       edi, Size       //     for (i = Size - 1; Size > 0; i --)  
  190.     pxor      mm1, mm1        //     {  
  191. @@addLoop:  
  192.     call      GetSrcColor     //       mm1 += GetSrcColor(x1, y1)  
  193.     paddw     mm1, mm0  
  194.     add       ecx, yDelta     //       y1 += yDelta  
  195.     add       edx, xDelta     //       x1 += xDelta  
  196.     dec       edi  
  197.     jnz       @@addLoop       //     }  
  198. @@1:  
  199.     call      GetSrcColor     //     mm0 = GetSrcColor(x1, y1)  
  200.     pmulhuw   mm1, mm3        //     mm1 /= Size  
  201.     psubw     mm1, mm0        //     mm1 -= mm0  
  202.     pop       eax  
  203.     pop       edx  
  204.     shr       eax, 12         //     fy = (y / 4096) & back.Height  
  205. //    shr       edx, 12         //     fx = (x / 4096) & back.Width  
  206.     shr       edx, 10  
  207.     and       eax, back.TImageData.Height // height is y mask  
  208.     and       edx, back.TImageData.Width  // width is x*4 mask  
  209.     bsr       ecx, back.TImageData.Stride  
  210.     shl       eax, cl  
  211. //    shl       edx, 2  
  212.     add       eax, edx  
  213.     add       eax, back.TImageData.Scan0  
  214.     movd      mm0, [eax]      //     mm0 = FillData.Scan0[fy * back.Stride + fx * 4]  
  215.     punpcklbw mm0, mm7  
  216.     paddw     mm0, mm1        //     mm0 += mm1  
  217.     packuswb  mm0, mm7  
  218.     pop       edi  
  219.     pop       ecx  
  220.     pop       edx  
  221.     mov       al, [edi].TARGBQuad.Alpha  
  222.     movd      [edi], mm0      //     *edi = mm0  
  223.     mov       [edi].TARGBQuad.Alpha, al  
  224.     add       edi, 4          //     edi ++  
  225.     add       edx, 1000h  
  226.     cmp       edx, width  
  227.     jl        @@xLoop         //   }  
  228.     add       edi, dstOffset  
  229.     add       ecx, 1000h  
  230.     dec       height  
  231.     jnz       @@yLoop  
  232.     emms  
  233.     pop       ebx  
  234.     pop       edi  
  235.     pop       esi  
  236.   end;  
  237.   FreeImageData(back);  
  238.   FreeImageData(src);  
  239.   if Data.AlphaFlag then  
  240.     PArgbConvertArgb(Data);  
  241. end;  

 

从处理流程看,填充浮雕和彩色浮雕、灰色浮雕是一样的,但在像素处理上是不相同的。下面是用一个用45度角,2像素深度浮雕差值计算矩阵图来说明几种浮雕效果像素处理的差异 

各矩阵中,中间的点可以看作为要处理的像素。

灰度浮雕固定地取左上角点和右下角的差值加上128背景值 (无论深度多大,都是如此,只是矩阵中间的0多少的问题),如此一来,差异小的像素趋向于背景色,差异大的像素形成“黑白分明”的阴线和阳线,这就成了灰色的浮雕效果,浮雕深度越大,“黑白分明”的效果就越明显,当深度达到一个比较大的值时,画面上会形成加强了的正、负片以及图像原色彩3层画面的共存状态;

彩色浮雕也是固定的取邻近3个点的值减去2倍右下角点的值,由于被减的点数大于减的点数,在雕刻阴影形成时保留了很大的亮度,这就相当于给各像素加了一个不固定的背景值,所以形成彩色浮雕效果;

而填充浮雕的则是取主对角线除右下角外的各点之和的平均值,减去右下角点的值,再加上填充背景色,在背景色固定的前提下(假定128),会形成类似灰度浮雕的效果,但是由于是取主对角线除右下角外的各点之和的平均值,就相当于先对像素点做了一定的表面模糊后再减去右下角点的值,所以,当雕刻深度增大时,不会形成明显的“黑白分明”效应,而是有一定的模糊过渡带,即类似阴影的半影调。

下面是以45度角,深度为10,背景色128的灰度浮雕和填充浮雕效果比较图:

 

左上角是原图,左下角是灰度浮雕效果图,右上角是填充浮雕效果图,因为填充浮雕在浮雕效果处理前对原图作了灰度处理,为便于比较,所以右下角是灰度化后的灰色浮雕效果图。

从比较图上可以看出,当浮雕深度较大时,无论是否去色,灰度浮雕效果的阳线带和阴线带都很明显,而填充浮雕则存在过渡半影调,所以,看起来有石雕的效果。另外,从灰色背景上看,填充浮雕效果明显的平坦于灰色浮雕效果,这是因为填充浮雕处理有一定的表面模糊作用的缘故。

填充浮雕可以以任意颜色和图案作为背景填充,这就使得填充浮雕过程能产生各种效果的浮雕图,为了便于使用,我把填充浮雕过程写成了2个过程,分别用来处理实色填充和图案填充。

下面先对实色填充作几个图片处理测试:

TGpBitmap测试代码(仿玉石浮雕效果):

 

  1. procedure TForm1.Button3Click(Sender: TObject);  
  2. var  
  3.   bmp: TGpBitmap;  
  4.   g: TGpGraphics;  
  5.   data: TImageData;  
  6. begin  
  7.   bmp := TGpBitmap.Create('..\media\graysource.bmp');  
  8.   g := TGpGraphics.Create(Canvas.Handle);  
  9.   g.DrawImage(bmp, 00);  
  10.   data := LockGpBitmap(bmp);  
  11.   ImageSolidSculpture(data, 4510$AEC0C8);  
  12.   UnlockGpBitmap(bmp, data);  
  13.   g.DrawImage(bmp, 0, data.Height);  
  14.   g.Free;  
  15.   bmp.Free;  
  16. end;  

 

效果图如下,上面是原图,下面左边是45度,深度为5的浮雕效果,下面右边是45度,深度为10的浮雕效果。

 

 

原图:

 

你是否觉得象玉石浮雕?特别是右边图中那些重叠着的叶片, 真有着一种晶莹剔透的感觉!

 

下面是以角度30,深度8,填充颜色为$A5140A的仿玛瑙色浮雕效果图,测试代码就不贴了:

 

TBitmap填充图案测试代码:

 

  1. procedure TForm1.Button4Click(Sender: TObject);  
  2. var  
  3.   bmp, fbmp: TBitmap;  
  4.   data, fdata: TImageData;  
  5. begin  
  6.   bmp := TBitmap.Create;  
  7.   fbmp := TBitmap.Create;  
  8.   bmp.LoadFromFile('..\media\graysource.bmp');  
  9.   fbmp.LoadFromFile('..\media\back-2.bmp');  
  10.   data := GetBitmapData(bmp);  
  11.   fData := GetBitmapData(fbmp);  
  12.   ImageTextureSculpt(data, 455, fData);  
  13.   Canvas.Draw(00, bmp);  
  14.   fbmp.Free;  
  15.   bmp.free;  
  16. end;  


下面是使用4种不同填充图案调用ImageTextureSculpture过程分别形成的浮雕效果图(原图在文章前面),角度50,浮雕深度从左上角开始,依次是8657

 

填充浮雕前也可以不进行灰度处理,有时还能产生意想不到的效果,下面是使用图案填充的2幅效果:

填充背景图案:

通过几天的研究和测试,发现浮雕角度、深度和填充色(图案)的选择,应根据图片的实际情况确定,如上图中,左上角的填充图案颜色较深、较杂,这就需要把浮雕深度用大一点,反之,如左下角就要相应小一点;图像较平淡的,不宜用很深的浮雕;浮雕角度与图像光照方向基本对应;填充颜色(图案)也应按图像具体选用,如前面的玉雕效果图,如果换成其它图片,效果不一定那么好,反过来也可以说,那张图用玛瑙色效果就差多了。

关于填充效果图的介绍就到此为止,欢迎朋友们提出改进意见。

最后重申一下,该效果确是本人无意中搞出来的,如果已经有类似的方法,纯属巧合,本人不和你争“专利权”了。

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

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

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

  评论这张
 
阅读(392)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017