最近遇到需要求图形轮廓中距离最远的点,即长轴。
网上查了一些资料,思路如下。
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