守望者--AIR技术交流

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: ANE FlasCC 炼金术
查看: 1299|回复: 0

[图像分析] 利用边界矩形算法追踪图片轮廓

[复制链接]
  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    51691
    钢镚
    1422

    开源英雄守望者

    发表于 2015-1-10 22:29:19 | 显示全部楼层 |阅读模式
    你曾经好奇过图形软件是如何追踪一个图像轮廓的吗?没有嘛?我实际上就没有好奇过,但是当我做一个复杂项目时候,我发现用边界矩形算法来追踪图形轮廓是多么的有魔力。
    处理的方法是很简单的:
            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. }
    复制代码
    这是对应的结果:


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

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


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    守望者AIR技术交流社区(www.airmyth.com)
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    
    关闭

    站长推荐上一条 /4 下一条

    QQ|手机版|Archiver|网站地图|小黑屋|守望者 ( 京ICP备14061876号

    GMT+8, 2019-8-18 13:23 , Processed in 0.043111 second(s), 39 queries .

    守望者AIR

    守望者AIR技术交流社区

    本站成立于 2014年12月31日

    快速回复 返回顶部 返回列表