• 利用边界矩形算法追踪图片轮廓


    你曾经好奇过图形软件是如何追踪一个图像轮廓的吗?没有嘛?我实际上就没有好奇过,但是当我做一个复杂项目时候,我发现用边界矩形算法来追踪图形轮廓是多么的有魔力。
    处理的方法是很简单的:
            1)找到一个图像边界上的像素(这跟边界矩形没关系,只是假设你找到了这个像素)。这个像素就是需要分析的。    
            2)假设有一个2x2的像素矩形,其中包括位于矩阵左上角或右下角的当前处理像素。
            3)这个时候,你有四个像素,每个都可以是透明或不透明。这样我们就有16种2x2的矩阵。 尽管当我们移到图像边界时,透明跟不透明像素都不会看到。    
            4)根据2x2矩阵中不透明像素的位置和个数,我们可以猜测轮廓的方向,将当前处理像素朝这个方向移动,从第二步起,直到你发现跟第一步中说的像素。
          现在,我们如何找到第一步中的第一个像素呢?这需要暴力算法,我们需要遍历图像像素,直到找到一个不透明像素。

          我们看下下面带注释的代码:

     

    View Code
      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

    原文链接:利用边界矩形算法追踪图片轮廓

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

     

  • 相关阅读:
    十天学会php之第一天
    学习PHP的一些经验
    PHP中的数据类型(1)
    PHP中的常量
    赵凡导师并发知识第一次分享观后感
    面向对象之 __setitem__()、__getitem__()、__delitem__() 用法
    spider数据抓取(第二章)
    识别网站所用技术
    scrapy安装要求
    基于bs4的防止xss攻击,过滤script标签
  • 原文地址:https://www.cnblogs.com/atong/p/2946527.html
Copyright © 2020-2023  润新知