- 积分
- 136189
- 注册时间
- 2014-12-27
- 最后登录
- 2024-4-10
- 在线时间
- 603 小时
- 威望
- 562
- 贡献
- 29
- 金币
- 52692
- 钢镚
- 1422
- 交易凭证
- 1
- 分享
- 0
- 精华
- 33
- 帖子
- 2094
- 主题
- 1742
TA的每日心情 | 擦汗 2018-4-10 15:18 |
---|
签到天数: 447 天 [LV.9]以坛为家II
超级版主
- 威望
- 562
- 贡献
- 29
- 金币
- 52692
- 钢镚
- 1422
|
你曾经好奇过图形软件是如何追踪一个图像轮廓的吗?没有嘛?我实际上就没有好奇过,但是当我做一个复杂项目时候,我发现用边界矩形算法来追踪图形轮廓是多么的有魔力。
处理的方法是很简单的:
1)找到一个图像边界上的像素(这跟边界矩形没关系,只是假设你找到了这个像素)。这个像素就是需要分析的。
2)假设有一个2x2的像素矩形,其中包括位于矩阵左上角或右下角的当前处理像素。
3)这个时候,你有四个像素,每个都可以是透明或不透明。这样我们就有16种2x2的矩阵。 尽管当我们移到图像边界时,透明跟不透明像素都不会看到。
4)根据2x2矩阵中不透明像素的位置和个数,我们可以猜测轮廓的方向,将当前处理像素朝这个方向移动,从第二步起,直到你发现跟第一步中说的像素。
现在,我们如何找到第一步中的第一个像素呢?这需要暴力算法,我们需要遍历图像像素,直到找到一个不透明像素。
我们看下下面带注释的代码: - package {
- import flash.display.Sprite;
- import flash.display.BitmapData;
- import flash.display.Bitmap;
- import flash.geom.Matrix;
- import flash.geom.Point;
- public class Main extends Sprite {
- private var bitmapData:BitmapData=new BitmapData(640,480,true,0x00000000);
- // tolerance 保存一个像素的Alpha值
- private var tolerance:Number=0x01;
- public function Main() {
- // 添加一个具有透明度的png图片
- bitmapData.draw(new Logo(278,429),new Matrix(1,0,0,1,100,40));
- var bitmap:Bitmap=new Bitmap(bitmapData);
- addChild(bitmap);
- // 函数结束后,marchingVector将包含追踪轮廓的点
- var marchingVector:Vector.<Point>=marchingSquares(bitmapData);
- }
- public function marchingSquares(bitmapData:BitmapData):Vector.<Point> {
- var contourVector:Vector.<Point> = new Vector.<Point>();
- //这是我们画轮廓线的canvas
- var canvas:Sprite=new Sprite();
- addChild(canvas);
- canvas.graphics.lineStyle(2,0x00ff00);
- // 获取起始像素
- var startPoint:Point=getStartingPixel(bitmapData);
- // 找到起始像素后我们就可以开始了
- if (startPoint!=null) {
- // 将画笔移到起始点
- canvas.graphics.moveTo(startPoint.x,startPoint.y);
- // pX 跟 pY是起始点的x,y坐标
- var pX:Number=startPoint.x;
- var pY:Number=startPoint.y;
- // stepX 和 stepY 可能是 -1, 0 或 1 代表到轮廓下一个点的查找像素步骤
- var stepX:Number;
- var stepY:Number;
- // 下面两个变量保存上一步步骤
- var prevX:Number;
- var prevY:Number;
- // 追踪整个轮廓时,closedLoop将成为true
- var closedLoop:Boolean=false;
- while (!closedLoop) {
- // 这段主要是获取每个像素的2x2矩阵
- var squareValue:Number=getSquareValue(pX,pY);
- switch (squareValue) {
- /* 往上用这些事例:
- +---+---+ +---+---+ +---+---+
- | 1 | | | 1 | | | 1 | |
- +---+---+ +---+---+ +---+---+
- | | | | 4 | | | 4 | 8 |
- +---+---+ +---+---+ +---+---+
- */
- case 1 :
- case 5 :
- case 13 :
- stepX=0;
- stepY=-1;
- break;
- /* 往下用这些事例
- +---+---+ +---+---+ +---+---+
- | | | | | 2 | | 1 | 2 |
- +---+---+ +---+---+ +---+---+
- | | 8 | | | 8 | | | 8 |
- +---+---+ +---+---+ +---+---+
- */
- case 8 :
- case 10 :
- case 11 :
- stepX=0;
- stepY=1;
- break;
- /* 往左用这些事例
- +---+---+ +---+---+ +---+---+
- | | | | | | | | 2 |
- +---+---+ +---+---+ +---+---+
- | 4 | | | 4 | 8 | | 4 | 8 |
- +---+---+ +---+---+ +---+---+
- */
- case 4 :
- case 12 :
- case 14 :
- stepX=-1;
- stepY=0;
- break;
- /* 往右用这些事例
- +---+---+ +---+---+ +---+---+
- | | 2 | | 1 | 2 | | 1 | 2 |
- +---+---+ +---+---+ +---+---+
- | | | | | | | 4 | |
- +---+---+ +---+---+ +---+---+
- */
- case 2 :
- case 3 :
- case 7 :
- stepX=1;
- stepY=0;
- break;
- case 6 :
- /* 特殊鞍点用case 1:
- +---+---+
- | | 2 |
- +---+---+
- | 4 | |
- +---+---+
- 如果来自上面,那就往左,否则往右
- */
- if (prevX==0&&prevY==-1) {
- stepX=-1;
- stepY=0;
- }
- else {
- stepX=1;
- stepY=0;
- }
- break;
- case 9 :
- /* 特殊鞍点 case 2:
- +---+---+
- | 1 | |
- +---+---+
- | | 8 |
- +---+---+
- 如果来自右边,就往上,否则往下
- */
- if (prevX==1&&prevY==0) {
- stepX=0;
- stepY=-1;
- }
- else {
- stepX=0;
- stepY=1;
- }
- break;
- }
- // 移到下一个点
- pX+=stepX;
- pY+=stepY;
- // 保存轮廓点
- contourVector.push(new Point(pX, pY));
- prevX=stepX;
- prevY=stepY;
- // 画线
- canvas.graphics.lineTo(pX,pY);
- //如果返回到第一个访问的点,循环结束
- if (pX==startPoint.x&&pY==startPoint.y) {
- closedLoop=true;
- }
- }
- }
- return contourVector;
- }
- private function getStartingPixel(bitmapData:BitmapData):Point {
- //扫描图像像素来蛮力找到非透明的像素作为起始像素
- var zeroPoint:Point=new Point(0,0);
- var offsetPoint:Point=new Point(0,0);
- for (var i:Number=0; i<bitmapData.height; i++) {
- for (var j:Number=0; j<bitmapData.width; j++) {
- offsetPoint.x=j;
- offsetPoint.y=i;
- if (bitmapData.hitTest(zeroPoint,tolerance,offsetPoint)) {
- return offsetPoint;
- }
- }
- }
- return null;
- }
- private function getSquareValue(pX:Number,pY:Number):Number {
- /*
- 检测2x2像素网格,如果不是透明就给每个像素赋值
- +---+---+
- | 1 | 2 |
- +---+---+
- | 4 | 8 | <- 当前像素 (pX,pY)
- +---+---+
- */
- var squareValue:Number=0;
- // 检测左上部像素
- if (getAlphaValue(bitmapData.getPixel32(pX-1,pY-1))>=tolerance) {
- squareValue+=1;
- }
- // 检测上面像素
- if (getAlphaValue(bitmapData.getPixel32(pX,pY-1))>tolerance) {
- squareValue+=2;
- }
- // 检测左边像素
- if (getAlphaValue(bitmapData.getPixel32(pX-1,pY))>tolerance) {
- squareValue+=4;
- }
- // 检测像素本身
- if (getAlphaValue(bitmapData.getPixel32(pX,pY))>tolerance) {
- squareValue+=8;
- }
- return squareValue;
- }
- private function getAlphaValue(n:Number):Number {
- // 给定ARGB值,得到alpha值
- return n >> 24 & 0xFF;
- }
- }
- }
复制代码这是对应的结果:
绿线就是通过算法追踪出来的图像轮廓。下次,我将展示如何用这个算法结合BoxD来创建有趣的东西。
下载源码:
http://www.emanueleferonato.com/wp-content/uploads/2013/03/marchingsquares.zip
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|