轮廓中对踵点/旋转卡壳
最近遇到需要求图形轮廓中距离最远的点,即长轴。
网上查了一些资料,思路如下。
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
转载于:https://www.cnblogs.com/fantasy-zhtr/p/6200724.html
轮廓中对踵点/旋转卡壳相关推荐
- 最远对踵点 旋转卡壳
original link - http://poj.org/problem?id=2187 题意: 求最远点对 推荐 https://blog.csdn.net/wang_heng199/artic ...
- 旋转卡壳凸包(不用一下子就学完所有)
目录 前言 参考博客 前置知识 1.极角排序 2.凸包(默认逆时针) 3.对踵点 旋转卡壳能解决的各类问题 1.计算距离 1.1凸多边形直径 1.2凸多边形宽 1.3凸多边形间最大距离 1.4凸多边形 ...
- LA 4728 (旋转卡壳) Squares
题意: 求平面上的最远点对距离的平方. 分析: 对于这个数据量枚举肯定是要超时的. 首先这两个点一定是在凸包上的,所以可以枚举凸包上的点,因为凸包上的点要比原来的点会少很多,可最坏情况下的时间复杂度也 ...
- 模板:旋转卡壳(计算几何)
所谓旋转卡壳,就是旋转起来的卡壳 (逃) 前言 前置知识:凸包 个人感觉很像 two-pointers 算法. 能够在优秀的线性时间复杂度内完成总多求最值(周长.面积-)的神奇操作. 解析 给出情境: ...
- 旋转卡壳算法(转载)
https://www.cnblogs.com/little-w/p/3579603.html 转自:http://blog.csdn.net/acmaker/article/details/3188 ...
- 计算几何之旋转卡壳算法
一.目录 一些历史: 1978年, M.I. Shamos's Ph.D. 的论文"Computational Geometry"标志着计算机科学的这一领域的诞生. 当时他发表成果 ...
- 凸包问题--旋转卡壳
前情提要: 1978年,M.I.Shamos在论文<Computational Ceometry>中介绍了一种寻找凸多边形直径的线性算法. Shamos的算法就像绕着多边形旋转一对卡壳,因 ...
- 计算几何之 旋转卡壳 代码模板与证明
旋转卡壳 旋转卡壳这个算法很形象,一般用来在O(nlogn)O(nlogn)O(nlogn)的时间复杂度下求最远点对问题,就是求平面中任意两点的最远距离. 一般求最远点对问题得枚举两个点,所以复杂度是 ...
- [POJ2187]Beauty Contest(计算几何-旋转卡壳-最远点对)
题目: 我是超链接 题解: 值得一提的是,这是一个"不定向"算法,为什么呢,因为ta的名字不定哈哈哈,旋转卡壳一共有2*3*2*2=24种不同的读音哦 旋转卡壳可以解决:凸多边形最 ...
最新文章
- Eclipse 快捷键(转载)
- 为程序员量身定做的目标
- python3之MongoDB
- Leetcode--239. 滑动窗口最大值
- Word 相关的快捷键
- .net EventHandler 事件处理
- C语言宏的特殊用法和几个坑 (转)
- HTML+CSS网页设计视频教程
- 怎么设置电脑防火墙ping启用_如何通过windows防火墙启用和禁用ping命令
- 面试现场说要轮岗,实际岗位是地推
- write()与 read() 函数用法(C语言)
- SpringBoot + Sharding JDBC 读写分离、分库分表
- iframe 接班人-微前端框架 qiankun 在中后台系统实践
- 【虚幻引擎】UE4/UE5 后期处理盒子(PostProcessVolume)
- BZOJ2215 : [Poi2011]Conspiracy
- 捷信总经理Ondrej:愿为中欧企业间的长远发展与互惠共赢而努力
- 如何使用API接口查询物流信息?
- 互联网时代下的十大商业模式
- 微信昵称表情符号前端显示问题
- stm32f103c8t6 TIM2定时器1ms中断
热门文章
- 【实测有效】“解决国内访问s3.amazonaws.com下载文件非常缓慢的问题”
- jsTree 组件官方文档学习
- REMIND Your Neural Network to Prevent Catastrophic Forgetting 翻译
- 基于React的图片预览组件
- 装箱和拆箱,自动装箱和自动拆箱
- Matlab RoboticToolBox(一)Link参数、三自由度/四自由度逆运动学
- c语言for循环如何打印菱形
- Go汇编入门资料【强!!!】
- 表格内容居中css样式,css表格中的内容如何居中?css表格中的文本样式介绍
- 软件开发——软技能(一、IDE)