轮廓中对踵点/旋转卡克

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了轮廓中对踵点/旋转卡克相关的知识,希望对你有一定的参考价值。

最近遇到需要求图形轮廓中距离最远的点,即长轴。

网上查了一些资料,思路如下。

1. 利用OpenCV提取图形的轮廓点。

2. 把提取到的所有轮廓点当做一个集合,再次提取最外面的轮廓。

3. 用凸包旋转卡克方法求解。

 

  1         private async void Calculate()
  2         {
  3           
  4    
  5             var a = new Image<Bgr, byte>((Bitmap)pictureBox1.Image);
  6             var b = new Image<Gray, byte>(a.Width, a.Height);
  7             var c = new Image<Gray, byte>(a.Width, a.Height);
  8             var d = new Image<Bgr, byte>(a.Width, a.Height);
  9 
 10             var t1 = trackBar1.Value;
 11             var t2 = trackBar2.Value;
 12 
 13             CvInvoke.Canny(a, b, t1, t2);
 14             var con = new VectorOfVectorOfPoint();
 15             CvInvoke.FindContours(b, con, c, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple);
 16 
 17 
 18             var con1 = con.ToArrayOfArray();
 19             var con2 = Array.ConvertAll<Point[], PointF[]>(con1, new Converter<Point[], PointF[]>(PointToPointF));
 20 
 21             var points = new List<PointF>(con2.Length);
 22             foreach (PointF[] t in con2)
 23             {
 24                 for (int j = 0; j < t.Length; j++)
 25                 {
 26                     points.Add(t[j]);
 27                 }
 28             }
 29 
 30             //找出凸包
 31             var hull = CvInvoke.ConvexHull(points.ToArray(), true);
 32 
 33             var color = new MCvScalar(34, 177, 76);
 34             //画出轮廓
 35             for (int j = 0; j < hull.Length; j++)
 36             {
 37                 var p1 = new Point((int)(hull[j].X + 0.5), (int)(hull[j].Y + 0.5));
 38                 Point p2;
 39                 if (j == hull.Length - 1)
 40                     p2 = new Point((int)(hull[0].X + 0.5), (int)(hull[0].Y + 0.5));
 41                 else
 42                     p2 = new Point((int)(hull[j + 1].X + 0.5), (int)(hull[j + 1].Y + 0.5));
 43                 //CvInvoke.Circle(d, p1, 3, color);
 44                 CvInvoke.Line(a, p1, p2, color);
 45             }
 46 
 47             int pp1 = 0, pp2 = 0, pp3 = 0;
 48             Find(hull, ref pp1, ref pp2, ref pp3, ref a);
 49 
 50             Console.WriteLine("Find1 p1={0}, p2={1}", pp1, pp3);
 51 
 52             double k = 0, bb = 0d;
 53             CalculateLineFormula(hull[pp1], hull[pp2], ref k, ref bb);
 54 
 55             var tmp1 = new Point((int)(hull[pp1].X + 0.5), (int)(hull[pp1].Y + 0.5));
 56 
 57             //var tmp2 = new Point((int)(hull[pp2].X + 0.5), (int)(hull[pp2].Y + 0.5));
 58             var tmp3 = new Point((int)(hull[pp3].X + 0.5), (int)(hull[pp3].Y + 0.5));
 59             CvInvoke.Line(a, tmp1, tmp3, color);
 60             imageBox1.Image = a;
 61        
 62         }
 63 
 64 
 65             
 66          /// <param name="p2Index"></param>
 67         public void Find2(PointF[] points, ref int p1Index,  ref int p3Index)
 68         {
 69             var max = double.MinValue;
 70 
 71             for (int i = 0; i < points.Length; i++)
 72              {
 73                 for (int j = 0; i < points.Length; i++)
 74                 {
 75                     var dis = CalculateDisOfPoints(points[i], points[j]);
 76                     if (!(dis > max)) continue;
 77                     max = dis;
 78                     p1Index = i;
 79                     p3Index = j;
 80 
 81                     Console.WriteLine("Find2 max={0}", max);
 82                 }
 83              }
 84         }
 85 
 86 
 87         private Point ConvetT(PointF p)
 88         {
 89             return new Point((int)p.X, (int)p.Y);
 90         }
 91 
 92 
 93 
 94         /// <summary>
 95         /// 计算两个距离最长的对点
 96         /// </summary>
 97         /// <param name="points"></param>
 98         /// <param name="p1Index"></param>
 99         /// <param name="p2Index"></param>
100         public void Find(PointF[] points, ref int p1Index, ref int p2Index, ref int p3Index, ref Image<Bgr, byte> image)
101         {
102 
103             var maxYPos = -1;
104             var minYPos = -1;
105             var maxDis = 0d;
106 
107             var len = points.Length;
108             //先寻找最大的Y
109             FindMax(points, ref maxYPos, ref minYPos);
110             p1Index = maxYPos;
111             p2Index = (maxYPos + 1) % len;
112             p3Index = minYPos;
113             //对重点
114             maxDis = CalculateDisOfPoints(points[maxYPos], points[minYPos]);
115             Console.WriteLine("max={0}", maxDis);
116             var cnt = 0;
117       
118             var n1 =  (maxYPos )%len;
119             var n2 =(minYPos) % len ;
120 
121             //四个向量
122             PointF vec1, vec2, vec3, vec4;
123             vec1 = new PointF(1, 0); //指向右边
124             vec3= new PointF(-1,0); //指向左边
125             var offset1 = 0;
126             var offset2 = 0;
127             //顺时针旋转一周
128             var color = new MCvScalar(34, 177, 76);
129             while(offset1<len && offset2<len)
130             {
131                 var tmpImg = image.Copy();
132                 var p1 = points[ (n1+offset1)%len];
133                 var p2 = points[ (n1+offset1+1)%len];
134                 vec2 = new PointF(p2.X - p1.X, p2.Y - p1.Y);
135 
136                 
137                 var p3 = points[ (n2+offset2)%len];
138                 var p4 = points[ (n2 + offset2+1) % len];
139                 vec4= new PointF(p4.X - p3.X, p4.Y - p3.Y);;
140 
141                 //计算向量夹角
142                 var ang12 = CalculateAngle(vec1, vec2);
143                 var ang34 = CalculateAngle(vec3, vec4);
144 
145                 var dis = 0d;
146                 if(ang12<ang34)
147                 {
148                     //12夹角比较小, 计算p2与p3的距离
149                     dis = CalculateDisOfPoints(p2, p3);
150                     if (dis  -maxDis>0.000001)
151                     {
152                         maxDis = dis;
153                         p1Index = (n1 + offset1 + 1) % len;
154                         p3Index = (n2 + offset2) % len;
155 
156                         //CvInvoke.Line(image, ConvetT(points[p1Index]), ConvetT(points[p3Index]), color);
157                         //imageBox1.Image = image;
158                     }
159 
160                     //经过p3点的与vec1反方向的平行线
161                     vec3 = new PointF(-vec1.X, -vec1.Y);
162                     //更新向量
163                     vec1 = vec2;
164                   
165                     //转动
166                     offset1++;
167 
168                     Console.WriteLine("Find1 max={0}", maxDis);
169                 }
170                 else
171                 {
172                     //12夹角比较小, 计算p2与p3的距离
173                     dis = CalculateDisOfPoints(p1, p4);
174                     if (dis - maxDis > 0.000001)
175                     {
176                         maxDis = dis;
177                         p1Index = (n1 + offset1) % len;
178                         p3Index = (n2 + offset2 + 1) % len;
179                     }
180       
181                     //转动
182                     offset2++;
183                     //经过p3点的与vec1反方向的平行线
184                     vec1 = new PointF(-vec3.X, -vec3.Y);
185                     vec3 = vec4;
186                     Console.WriteLine("Find1 max={0}", maxDis);
187                 }              
188             }
189         }
190 
191 
192         /// <summary>
193         /// 计算经过两个点p1,p2的直线
194         /// </summary>
195         /// <param name="p1">点1</param>
196         /// <param name="p2">点2</param>
197         /// <param name="k">斜率</param>
198         /// <param name="b"></param>
199         private void CalculateLineFormula(PointF p1, PointF p2, ref double k,ref double b)
200         {
201             var dX = p1.X - p2.X;
202             var dY = p2.Y - p2.Y;
203 
204             if(dX==0)
205             {
206                 //X=C
207                 k = 1;
208                 b = 0;
209   
210             }
211             else if(dY==0)
212             {                
213                 k = 0;
214                 b = 0;
215             }
216             else
217             {
218                 k = (double)dY / dX;
219                 b = p1.Y - p1.X * k;
220             }
221         }
222 
223         /// <summary>
224         /// 计算平行线的距离
225         /// </summary>
226         /// <param name="k"></param>
227         /// <param name="b1"></param>
228         /// <param name="b2"></param>
229         /// <returns></returns>
230         private double CalculateLinesDis(double k, double b1, PointF p1, PointF p2)
231         {
232             var toll=0.000001d;
233             if (Math.Abs(k) < toll)  // x=常熟
234                 return Math.Abs(p1.X-p2.X);
235             else    if (Math.Abs(k)-1 < toll)   //y=常数
236                 return Math.Abs(p1.Y-p2.Y);
237             else
238             {
239                 //line 1, ax+by=c 
240                 var a = -k;
241                 var b = 1;
242                 var c = b1;
243                 //计算p2到 line1的距离
244                 return Math.Abs(a * p1.X + b * p1.Y + c) / Math.Sqrt(a * a + b * b);
245             }
246 
247         }
248 
249 
250 
251         /// <summary>
252         /// 先寻找一对对踵点
253         /// </summary>
254         /// <param name="points"></param>
255         /// <param name="maxPos"></param>
256         /// <param name="minPos"></param>
257         private void FindMax(PointF[] points, ref int maxPos, ref int minPos)
258         {
259             var max = double.MinValue;
260             var min = double.MaxValue;
261             
262             for (int i = 0; i < points.Length; i++)
263             {
264                 if(points[i].Y>max)
265                 {
266                     max = points[i].Y;
267                     maxPos = i;
268                 }
269                 if (points[i].Y < min)
270                 {
271                     min = points[i].Y;
272                     minPos = i;
273                 }
274             }
275 
276         }
277 
278 
279 
280         private double CalculateDisOfPoints(PointF p1, PointF p2)
281         {
282             return Math.Sqrt( (p1.Y-p2.Y)*(p1.Y-p2.Y)+(p1.X-p2.X)*(p1.X-p2.X));
283         }
284 
285 
286         /// <summary>
287         /// 计算两个向量的夹角,0-180度
288         /// </summary>
289         /// <param name="vec1">向量1</param>
290         /// <param name="vec2">向量2</param>
291         /// <returns></returns>
292         private double CalculateAngle(PointF  vec1, PointF vec2)
293         {
294             //var ang=Math.Atan(k2-k1)
295 
296             var toll=0.0000001;
297 
298             if (vec1.X * vec2.X + vec1.Y * vec2.Y < toll)
299                 return 90;
300 
301 
302             if(Math.Abs(vec1.X)<toll  )
303             {
304                 
305             }
306 
307 
308             var sum1 = Math.Sqrt(vec1.X*vec1.X+vec1.Y*vec1.Y);
309             var sum2 = Math.Sqrt(vec2.X * vec2.X + vec2.Y * vec2.Y);
310 
311             if (sum1 + sum2 < toll)
312                 return 0;
313 
314             var sum3 = vec1.X * vec2.X + vec1.Y * vec2.Y;
315             var ang = Math.Acos(sum3 / (sum1 * sum2))*180/Math.PI;
316             return ang;
317         }

 

 

 

 效果如下:

 技术分享

 技术分享

 

参考资料:

http://www.cnblogs.com/Booble/archive/2011/04/03/2004865.html

以上是关于轮廓中对踵点/旋转卡克的主要内容,如果未能解决你的问题,请参考以下文章

UVALive 4728 Squares (平面最远点对)

[luogu1452]Beauty Contest凸包+旋转卡壳

旋转卡壳模板

旋转卡壳-求凸包最大直径

BZOJ 1185 [HNOI2007]最小矩形覆盖:凸包 + 旋转卡壳

POJ2079:Triangle