轮廓中对踵点/旋转卡克

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

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

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

时间: 2024-10-22 10:01:19

轮廓中对踵点/旋转卡克的相关文章

Eclipse中查看Android模拟器SD卡目录

有时候用到Android模拟器来模拟SD卡相关操作,在Eclipse中可以直接查看SD卡目录: 首先,新建模拟器的时候要创建SD卡,存储的大小根据需要创建: 启动模拟器,在Eclipse中打开视图窗口:Window--Show View--File Explorer: 可以看到下面有mnt目录,mnt--sdcard 就是SD卡的目录, 也就是代码中 Environment.getExternalStorageDirectory()  的目录: 这样就可以很直观的看到代码对sd卡的操作,比如新建

Android中使用SQLiteOpenHelper管理SD卡中的数据库

使用Android中自带的SQLiteOpenHelper可以完成数据库的创建与管理,但有两点局限: (1)数据库创建在内存卡中,大小受限,创建位置位于/data/data/应用程序名/databases中(可使用Eclispe的DDMS查看). (2)如果无法获取Root权限,则无法直接查看创建的数据库. 鉴于上述限制及实际需要,打算使用SQLiteOpenHelper管理SD卡上的数据库,通过研究SQLiteOpenHelper的源码,发现其创建或打开数据库的代码位于getWritableD

iOS中的屏幕的旋转(UIViewController)横屏竖屏

RootViewController //视图控制器(UIViewController):它不是视图,用来管理视图,所以屏幕上看不到,但是自身携带一个视图(根视图) #import "RootViewController.h" #import "LoginView.h" //视图控制器的延展 @interface RootViewController () @end //视图控制器的实现部分 @implementation RootViewController //

ArcGis for Android中如何获取自定义图层中的图片并旋转

============问题描述============ RT,在GraphicsLayer自定义图层中,有一个图片,现在可以获取到手机的旋转角度,然后根据角度去旋转那个图片.现在只能remove掉GraphicsLayer后旋转图片重新添加到map中,这样的话会非常不连贯,达不到百度地图的旋转效果,请教大神们如何解决? ============解决方案1============ BitmapDrawable bd = new BitmapDrawable(context.getResource

教你ZBrush&#174;中Local模式的旋转

刚接触ZBrush的小伙伴可能对Local(局部)有了简单的了解,但是大多数人对它的认识还是比较模糊的,那么在本文中小编将对local命令做详细说明.此工具可以控制视图的旋转轴心点的位置,默认情况下,我们选旋转视图对模型进行多视角观察,是以所选物体的几何中心为轴心的,当应用此命令后,视图的旋转将以上一次鼠标设置的位置为轴心点来进行旋转. 下面我们通过一个简单的案例操作来了解Local(局部)按钮时如何使用的.在顶部工具架上单击LightBox按钮,旋转ZBrush®软件中默认的怪兽模型,双击,在

poj2187 旋转卡(qia)壳(ke)

题意:求凸包的直径 关于对踵点对.旋转卡壳算法的介绍可以参考这里: http://www.cnblogs.com/Booble/archive/2011/04/03/2004865.html http://www.cppblog.com/staryjy/archive/2009/11/19/101412.html http://blog.csdn.net/ACMaker 这里使用了lrj的complex<double>大法来表示复数. 注意别忘了复数乘法的定义:(a+bi)*(c+di)=(a

Path2.0中绚丽的的旋转菜单

我们看一下实现的效果图: 在上图中,我将菜单弹出的效果设置成直线型,最终的弹出或汇总点在下面的红色按钮中. 它的实现原理是设置动画的同时并利用动画中的插入器(interpolator)来实现弹力.主要用到了OvershootInterpolator和AnticipateOvershootInterpolator,简单介绍下这两个插入器. OvershootInterpolator:表示向前甩一定值后再回到原来位置. AnticipateOvershootInterpolator:表示开始的时候向

Android 实现Path2.0中绚丽的的旋转菜单

上图先: 那么下面开始吧~ 首先,将整个菜单动画分解开来. 1.       一级菜单按钮的旋转动画2个,十字和叉叉状态的转换. 2.       二级菜单按钮的平移动画2个,弹簧效果的in和out 3.       二级菜单按钮的点击效果,放大消失,其他未点击按钮缩小消失. 4.       一级菜单按钮的恢复效果,放大出现 好的 逐一去实现: 首先是一级菜单按钮的旋转动画,这2个动画可以直接在xml中定义,然后load到代码中来,具体代码如下: rotate_story_add_button

opengl学习-利用模板测试勾画物体轮廓中出现的一个问题

我在学习OpenGL模板测试勾画物体轮廓的时候,出现了这个问题: 这个出现的原因就是,改变摄像机的时候,每次绘制,上次绘制中模板缓冲区的数据没有清除的原因.也就是在while循环开始的时候,glClear(GL_STENCIL_BUFFER_BIT);没有起作用,原来 我在while的末尾忘记了修改回来模板缓冲区为可写.glStencilMask(0xff); 原文地址:https://www.cnblogs.com/pixs-union/p/8184366.html