守望者--AIR技术交流

标题: 利用边界矩形算法追踪图片轮廓 [打印本页]

作者: 破晓    时间: 2015-1-10 22:29
标题: 利用边界矩形算法追踪图片轮廓
你曾经好奇过图形软件是如何追踪一个图像轮廓的吗?没有嘛?我实际上就没有好奇过,但是当我做一个复杂项目时候,我发现用边界矩形算法来追踪图形轮廓是多么的有魔力。
处理的方法是很简单的:
        1)找到一个图像边界上的像素(这跟边界矩形没关系,只是假设你找到了这个像素)。这个像素就是需要分析的。    
        2)假设有一个2x2的像素矩形,其中包括位于矩阵左上角或右下角的当前处理像素。
        3)这个时候,你有四个像素,每个都可以是透明或不透明。这样我们就有162x2的矩阵。 尽管当我们移到图像边界时,透明跟不透明像素都不会看到。    
        4)根据2x2矩阵中不透明像素的位置和个数,我们可以猜测轮廓的方向,将当前处理像素朝这个方向移动,从第二步起,直到你发现跟第一步中说的像素。
      现在,我们如何找到第一步中的第一个像素呢?这需要暴力算法,我们需要遍历图像像素,直到找到一个不透明像素。
      我们看下下面带注释的代码:
  1. package {
  2. import flash.display.Sprite;
  3. import flash.display.BitmapData;
  4. import flash.display.Bitmap;
  5. import flash.geom.Matrix;
  6. import flash.geom.Point;
  7. public class Main extends Sprite {
  8. private var bitmapData:BitmapData=new BitmapData(640,480,true,0x00000000);
  9. // tolerance 保存一个像素的Alpha值
  10. private var tolerance:Number=0x01;
  11. public function Main() {
  12. // 添加一个具有透明度的png图片
  13. bitmapData.draw(new Logo(278,429),new Matrix(1,0,0,1,100,40));
  14. var bitmap:Bitmap=new Bitmap(bitmapData);
  15. addChild(bitmap);
  16. // 函数结束后,marchingVector将包含追踪轮廓的点
  17. var marchingVector:Vector.<Point>=marchingSquares(bitmapData);
  18. }
  19. public function marchingSquares(bitmapData:BitmapData):Vector.<Point> {
  20. var contourVector:Vector.<Point> = new Vector.<Point>();
  21. //这是我们画轮廓线的canvas
  22. var canvas:Sprite=new Sprite();
  23. addChild(canvas);
  24. canvas.graphics.lineStyle(2,0x00ff00);
  25. // 获取起始像素
  26. var startPoint:Point=getStartingPixel(bitmapData);
  27. // 找到起始像素后我们就可以开始了
  28. if (startPoint!=null) {
  29. // 将画笔移到起始点
  30. canvas.graphics.moveTo(startPoint.x,startPoint.y);
  31. // pX 跟 pY是起始点的x,y坐标
  32. var pX:Number=startPoint.x;
  33. var pY:Number=startPoint.y;
  34. // stepX 和 stepY 可能是 -1, 0 或 1  代表到轮廓下一个点的查找像素步骤
  35. var stepX:Number;
  36. var stepY:Number;
  37. // 下面两个变量保存上一步步骤
  38. var prevX:Number;
  39. var prevY:Number;
  40. // 追踪整个轮廓时,closedLoop将成为true
  41. var closedLoop:Boolean=false;
  42. while (!closedLoop) {
  43. // 这段主要是获取每个像素的2x2矩阵
  44. var squareValue:Number=getSquareValue(pX,pY);
  45. switch (squareValue) {
  46. /* 往上用这些事例:
  47. +---+---+ +---+---+ +---+---+
  48. | 1 | | | 1 | | | 1 | |
  49. +---+---+ +---+---+ +---+---+
  50. | | | | 4 | | | 4 | 8 |
  51. +---+---+ +---+---+ +---+---+
  52. */
  53. case 1 :
  54. case 5 :
  55. case 13 :
  56. stepX=0;
  57. stepY=-1;
  58. break;
  59. /* 往下用这些事例
  60. +---+---+ +---+---+ +---+---+
  61. | | | | | 2 | | 1 | 2 |
  62. +---+---+ +---+---+ +---+---+
  63. | | 8 | | | 8 | | | 8 |
  64. +---+---+ +---+---+ +---+---+
  65. */
  66. case 8 :
  67. case 10 :
  68. case 11 :
  69. stepX=0;
  70. stepY=1;
  71. break;
  72. /* 往左用这些事例
  73. +---+---+ +---+---+ +---+---+
  74. | | | | | | | | 2 |
  75. +---+---+ +---+---+ +---+---+
  76. | 4 | | | 4 | 8 | | 4 | 8 |
  77. +---+---+ +---+---+ +---+---+
  78. */
  79. case 4 :
  80. case 12 :
  81. case 14 :
  82. stepX=-1;
  83. stepY=0;
  84. break;
  85. /* 往右用这些事例
  86. +---+---+ +---+---+ +---+---+
  87. | | 2 | | 1 | 2 | | 1 | 2 |
  88. +---+---+ +---+---+ +---+---+
  89. | | | | | | | 4 | |
  90. +---+---+ +---+---+ +---+---+
  91. */
  92. case 2 :
  93. case 3 :
  94. case 7 :
  95. stepX=1;
  96. stepY=0;
  97. break;
  98. case 6 :
  99. /* 特殊鞍点用case 1:
  100. +---+---+
  101. | | 2 |
  102. +---+---+
  103. | 4 | |
  104. +---+---+
  105. 如果来自上面,那就往左,否则往右
  106. */
  107. if (prevX==0&&prevY==-1) {
  108. stepX=-1;
  109. stepY=0;
  110. }
  111. else {
  112. stepX=1;
  113. stepY=0;
  114. }
  115. break;
  116. case 9 :
  117. /* 特殊鞍点 case 2:
  118. +---+---+
  119. | 1 | |
  120. +---+---+
  121. | | 8 |
  122. +---+---+
  123. 如果来自右边,就往上,否则往下
  124. */
  125. if (prevX==1&&prevY==0) {
  126. stepX=0;
  127. stepY=-1;
  128. }
  129. else {
  130. stepX=0;
  131. stepY=1;
  132. }
  133. break;
  134. }
  135. // 移到下一个点
  136. pX+=stepX;
  137. pY+=stepY;
  138. // 保存轮廓点
  139. contourVector.push(new Point(pX, pY));
  140. prevX=stepX;
  141. prevY=stepY;
  142. // 画线
  143. canvas.graphics.lineTo(pX,pY);
  144. //如果返回到第一个访问的点,循环结束
  145. if (pX==startPoint.x&&pY==startPoint.y) {
  146. closedLoop=true;
  147. }
  148. }
  149. }
  150. return contourVector;
  151. }
  152. private function getStartingPixel(bitmapData:BitmapData):Point {
  153. //扫描图像像素来蛮力找到非透明的像素作为起始像素
  154. var zeroPoint:Point=new Point(0,0);
  155. var offsetPoint:Point=new Point(0,0);
  156. for (var i:Number=0; i<bitmapData.height; i++) {
  157. for (var j:Number=0; j<bitmapData.width; j++) {
  158. offsetPoint.x=j;
  159. offsetPoint.y=i;
  160. if (bitmapData.hitTest(zeroPoint,tolerance,offsetPoint)) {
  161. return offsetPoint;
  162. }
  163. }
  164. }
  165. return null;
  166. }
  167. private function getSquareValue(pX:Number,pY:Number):Number {
  168. /*
  169. 检测2x2像素网格,如果不是透明就给每个像素赋值
  170. +---+---+
  171. | 1 | 2 |
  172. +---+---+
  173. | 4 | 8 | <- 当前像素 (pX,pY)
  174. +---+---+
  175. */
  176. var squareValue:Number=0;
  177. // 检测左上部像素
  178. if (getAlphaValue(bitmapData.getPixel32(pX-1,pY-1))>=tolerance) {
  179. squareValue+=1;
  180. }
  181. // 检测上面像素
  182. if (getAlphaValue(bitmapData.getPixel32(pX,pY-1))>tolerance) {
  183. squareValue+=2;
  184. }
  185. // 检测左边像素
  186. if (getAlphaValue(bitmapData.getPixel32(pX-1,pY))>tolerance) {
  187. squareValue+=4;
  188. }
  189. // 检测像素本身
  190. if (getAlphaValue(bitmapData.getPixel32(pX,pY))>tolerance) {
  191. squareValue+=8;
  192. }
  193. return squareValue;
  194. }
  195. private function getAlphaValue(n:Number):Number {
  196. // 给定ARGB值,得到alpha值
  197. return n >> 24 & 0xFF;
  198. }
  199. }
  200. }
复制代码
这是对应的结果:

[attach]351[/attach]

绿线就是通过算法追踪出来的图像轮廓。下次,我将展示如何用这个算法结合BoxD来创建有趣的东西。   
   下载源码:

http://www.emanueleferonato.com/wp-content/uploads/2013/03/marchingsquares.zip

[attach]352[/attach]
原文链接:http://www.emanueleferonato.com/2013/03/01/using-marching-squares-algorithm-to-trace-the-contour-of-an-image/






欢迎光临 守望者--AIR技术交流 (http://www.airmyth.com/)