利用Ramer-Douglas-Peucker算法减少多边形轮廓点数

部分童鞋应该可以从《使用方形游移匹配算法来勾勒图像轮廓》一文看到它的潜在用途了,只要获得图像的轮廓,转换成Box2D形状就相当容易。

但有个问题,所生成的形状会有太多点,范例中处理的logo就多达2200点,太多。

点的数量真心需要减少。为了解决这个问题,Ramer-Douglas-Peucker算法闪亮登场。

该算法的思想为,给定一条由若干线段组成的曲线,找出包含的点数少且外观相近的曲线。

该算法基于原始曲线与简化曲线直接最大间距定义了“dissimilar”。简化曲线由定义原始曲线的点的一个子集组成。

Marius Karthaus为RDP算法写了一个非常不错的javascript范例,我在此基础上添加了部分内容将其移植到AS3,请参见19~75行:
介于排版问题,代码贴于沙发
RDP函数的第二个参数,即ε,用于约束全局,,范围介于0.2到0.8。

看最后看一个例子,舞台右侧勾勒的轮廓仅仅用了343个点,使用的ε值为0.50:

(wtf!背景白色看不出了,移驾原始页面看效果吧http://www.emanueleferonato.com/ ... archingsquares1.swf
无需下载任何东西,只要将这段代码制粘贴到原始范例的Main类即可。

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 is the amount of alpha for a pixel to be considered solid
 10                 private var tolerance:Number=0x01;
 11                 public function Main() {
 12                         // adding a png image with transparency
 13                         bitmapData.draw(new Logo(278,429),new Matrix(1,0,0,1,20,40));
 14                         var bitmap:Bitmap=new Bitmap(bitmapData);
 15                         addChild(bitmap);
 16                         bitmap.alpha=0.5
 17                         // at the end of this function, marchingVector will contain the points tracing the contour
 18                         var marchingVector:Vector.<Point>=marchingSquares(bitmapData);
 19                         marchingVector=RDP(marchingVector,0.50);
 20                         var canvas:Sprite=new Sprite();
 21                         addChild(canvas);
 22                         canvas.graphics.moveTo(marchingVector[0].x+320,marchingVector[0].y);
 23                         for (var i:Number=0; i<marchingVector.length; i++) {
 24                                 canvas.graphics.lineStyle(2,0xffffff);
 25                                 canvas.graphics.lineTo(marchingVector[i].x+320,marchingVector[i].y);
 26                                 canvas.graphics.lineStyle(1,0xff0000);
 27                                 canvas.graphics.drawCircle(marchingVector[i].x+320,marchingVector[i].y, 2);
 28                         }
 29                         canvas.graphics.lineStyle(2,0xffffff);
 30                         canvas.graphics.lineTo(marchingVector[0].x+320,marchingVector[0].y);
 31                 }
 32 
 33                 public function RDP(v:Vector.<Point>,epsilon:Number):Vector.<Point> {
 34                         var firstPoint:Point=v[0];
 35                         var lastPoint:Point=v[v.length-1];
 36                         if (v.length<3) {
 37                                 return v;
 38                         }
 39                         var index:Number=-1;
 40                         var dist:Number=0;
 41                         for (var i:Number=1; i<v.length-1; i++) {
 42                                 var cDist:Number=findPerpendicularDistance(v[i],firstPoint,lastPoint);
 43                                 if (cDist>dist) {
 44                                         dist=cDist;
 45                                         index=i;
 46                                 }
 47                         }
 48                         if (dist>epsilon) {
 49                                 var l1:Vector.<Point>=v.slice(0,index+1);
 50                                 var l2:Vector.<Point>=v.slice(index);
 51                                 var r1=RDP(l1,epsilon);
 52                                 var r2=RDP(l2,epsilon);
 53                                 var rs:Vector.<Point>=r1.slice(0,r1.length-1).concat(r2);
 54                                 return rs;
 55                         }
 56                         else {
 57                                 return new Vector.<Point>(firstPoint,lastPoint);
 58                         }
 59                         return null;
 60                 }
 61 
 62                 private function findPerpendicularDistance(p:Point, p1:Point,p2:Point) {
 63                         var result;
 64                         var slope;
 65                         var intercept;
 66                         if (p1.x==p2.x) {
 67                                 result=Math.abs(p.x-p1.x);
 68                         }
 69                         else {
 70                                 slope = (p2.y - p1.y) / (p2.x - p1.x);
 71                                 intercept=p1.y-(slope*p1.x);
 72                                 result = Math.abs(slope * p.x - p.y + intercept) / Math.sqrt(Math.pow(slope, 2) + 1);
 73                         }
 74                         return result;
 75                 }
 76 
 77                 public function marchingSquares(bitmapData:BitmapData):Vector.<Point> {
 78                         var contourVector:Vector.<Point> = new Vector.<Point>();
 79                         // this is the canvas we'll use to draw the contour
 80                         var canvas:Sprite=new Sprite();
 81                         addChild(canvas);
 82                         canvas.graphics.lineStyle(2,0x00ff00);
 83                         // getting the starting pixel;
 84                         var startPoint:Point=getStartingPixel(bitmapData);
 85                         // if we found a starting pixel we can begin
 86                         if (startPoint!=null) {
 87                                 // moving the graphic pen to the starting pixel
 88                                 canvas.graphics.moveTo(startPoint.x,startPoint.y);
 89                                 // pX and pY are the coordinates of the starting point;
 90                                 var pX:Number=startPoint.x;
 91                                 var pY:Number=startPoint.y;
 92                                 // stepX and stepY can be -1, 0 or 1 and represent the step in pixels to reach
 93                                 // next contour point
 94                                 var stepX:Number;
 95                                 var stepY:Number;
 96                                 // we also need to save the previous step, that's why we use prevX and prevY
 97                                 var prevX:Number;
 98                                 var prevY:Number;
 99                                 // closedLoop will be true once we traced the full contour
100                                 var closedLoop:Boolean=false;
101                                 while (!closedLoop) {
102                                         // the core of the script is getting the 2x2 square value of each pixel
103                                         var squareValue:Number=getSquareValue(pX,pY);
104                                         switch (squareValue) {
105                                                         /* going UP with these cases:
106                                                         
107                                                         +---+---+   +---+---+   +---+---+
108                                                         | 1 |   |   | 1 |   |   | 1 |   |
109                                                         +---+---+   +---+---+   +---+---+
110                                                         |   |   |   | 4 |   |   | 4 | 8 |
111                                                         +---+---+   +---+---+  +---+---+
112                                                         
113                                                         */
114                                                 case 1 :
115                                                 case 5 :
116                                                 case 13 :
117                                                         stepX=0;
118                                                         stepY=-1;
119                                                         break;
120                                                         /* going DOWN with these cases:
121                                                         
122                                                         +---+---+   +---+---+   +---+---+
123                                                         |   |   |   |   | 2 |   | 1 | 2 |
124                                                         +---+---+   +---+---+   +---+---+
125                                                         |   | 8 |   |   | 8 |   |   | 8 |
126                                                         +---+---+   +---+---+  +---+---+
127                                                         
128                                                         */
129                                                 case 8 :
130                                                 case 10 :
131                                                 case 11 :
132                                                         stepX=0;
133                                                         stepY=1;
134                                                         break;
135                                                         /* going LEFT with these cases:
136                                                         
137                                                         +---+---+   +---+---+   +---+---+
138                                                         |   |   |   |   |   |   |   | 2 |
139                                                         +---+---+   +---+---+   +---+---+
140                                                         | 4 |   |   | 4 | 8 |   | 4 | 8 |
141                                                         +---+---+   +---+---+  +---+---+
142                                                         
143                                                         */
144                                                 case 4 :
145                                                 case 12 :
146                                                 case 14 :
147                                                         stepX=-1;
148                                                         stepY=0;
149                                                         break;
150                                                         /* going RIGHT with these cases:
151                                                         
152                                                         +---+---+   +---+---+   +---+---+
153                                                         |   | 2 |   | 1 | 2 |   | 1 | 2 |
154                                                         +---+---+   +---+---+   +---+---+
155                                                         |   |   |   |   |   |   | 4 |   |
156                                                         +---+---+   +---+---+  +---+---+
157                                                         
158                                                         */
159                                                 case 2 :
160                                                 case 3 :
161                                                 case 7 :
162                                                         stepX=1;
163                                                         stepY=0;
164                                                         break;
165                                                 case 6 :
166                                                         /* special saddle point case 1:
167                                                         
168                                                         +---+---+ 
169                                                         |   | 2 | 
170                                                         +---+---+
171                                                         | 4 |   |
172                                                         +---+---+
173                                                         
174                                                         going LEFT if coming from UP
175                                                         else going RIGHT 
176                                                         
177                                                         */
178                                                         if (prevX==0&&prevY==-1) {
179                                                                 stepX=-1;
180                                                                 stepY=0;
181                                                         }
182                                                         else {
183                                                                 stepX=1;
184                                                                 stepY=0;
185                                                         }
186                                                         break;
187                                                 case 9 :
188                                                         /* special saddle point case 2:
189                                                         
190                                                         +---+---+ 
191                                                         | 1 |   | 
192                                                         +---+---+
193                                                         |   | 8 |
194                                                         +---+---+
195                                                         
196                                                         going UP if coming from RIGHT
197                                                         else going DOWN 
198                                                         
199                                                         */
200                                                         if (prevX==1&&prevY==0) {
201                                                                 stepX=0;
202                                                                 stepY=-1;
203                                                         }
204                                                         else {
205                                                                 stepX=0;
206                                                                 stepY=1;
207                                                         }
208                                                         break;
209                                         }
210                                         // moving onto next point
211                                         pX+=stepX;
212                                         pY+=stepY;
213                                         // saving contour point
214                                         contourVector.push(new Point(pX, pY));
215                                         prevX=stepX;
216                                         prevY=stepY;
217                                         //  drawing the line
218                                         canvas.graphics.lineTo(pX,pY);
219                                         // if we returned to the first point visited, the loop has finished;
220                                         if (pX==startPoint.x&&pY==startPoint.y) {
221                                                 closedLoop=true;
222                                         }
223                                 }
224                         }
225                         return contourVector;
226                 }
227 
228                 private function getStartingPixel(bitmapData:BitmapData):Point {
229                         // finding the starting pixel is a matter of brute force, we need to scan
230                         // the image pixel by pixel until we find a non-transparent pixel
231                         var zeroPoint:Point=new Point(0,0);
232                         var offsetPoint:Point=new Point(0,0);
233                         for (var i:Number=0; i<bitmapData.height; i++) {
234                                 for (var j:Number=0; j<bitmapData.width; j++) {
235                                         offsetPoint.x=j;
236                                         offsetPoint.y=i;
237                                         if (bitmapData.hitTest(zeroPoint,tolerance,offsetPoint)) {
238                                                 return offsetPoint;
239                                         }
240                                 }
241                         }
242                         return null;
243                 }
244 
245                 private function getSquareValue(pX:Number,pY:Number):Number {
246                         /*
247                         
248                         checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent
249                         
250                         +---+---+
251                         | 1 | 2 |
252                         +---+---+
253                         | 4 | 8 | <- current pixel (pX,pY)
254                         +---+---+
255                         
256                         */
257                         var squareValue:Number=0;
258                         // checking upper left pixel
259                         if (getAlphaValue(bitmapData.getPixel32(pX-1,pY-1))>=tolerance) {
260                                 squareValue+=1;
261                         }
262                         // checking upper pixel
263                         if (getAlphaValue(bitmapData.getPixel32(pX,pY-1))>tolerance) {
264                                 squareValue+=2;
265                         }
266                         // checking left pixel
267                         if (getAlphaValue(bitmapData.getPixel32(pX-1,pY))>tolerance) {
268                                 squareValue+=4;
269                         }
270                         // checking the pixel itself
271                         if (getAlphaValue(bitmapData.getPixel32(pX,pY))>tolerance) {
272                                 squareValue+=8;
273                         }
274                         return squareValue;
275                 }
276 
277                 private function getAlphaValue(n:Number):Number {
278                         // given an ARGB color value, returns the alpha 0 -> 255
279                         return n >> 24 & 0xFF;
280                 }
281         }
282 }

 

原文链接:算法减少多边形轮廓点数

英文链接:http://www.emanueleferonato.com/2013/03/04/reduce-the-number-of-points-in-a-polygon-with-the-ramer-douglas-peucker-algorithm/

posted @ 2013-03-06 17:55  【Winco】  阅读(4380)  评论(0编辑  收藏  举报