//程序功能:利用最小二乘法拟合直线 //输入:二维点 //输出:拟合直线 line calLine(LinkedList <myPair> input) { matrix A = new matrix(input.Count, 2); matrix AT = new matrix(2, input.Count); matrix B = new matrix(input.Count, 1); for (int i = 0; i < input.Count; ++i) { myPair cur = input.ElementAt(i); A.a[i, 0] = cur.x; A.a[i, 1] = cur.y; AT.a[0, i] = cur.x; AT.a[1, i] = cur.y; B.a[i, 0] = 100; } matrix coef = AT.doMul(A); matrix b = AT.doMul(B); matrix mat = new matrix(2, 3); for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { mat.a[i, j] = coef.a[i, j]; } } mat.a[0, 2] = b.a[0, 0]; mat.a[1, 2] = b.a[1, 0]; double BB = (mat.a[0, 0] * mat.a[1, 2] - mat.a[1, 0] * mat.a[0, 2]) / (mat.a[1, 1] * mat.a[0, 0] - mat.a[0, 1] * mat.a[1, 0]); double AA = (mat.a[0, 2] - mat.a[0, 1] * BB) / mat.a[0, 0]; return(new line(AA, BB, -100)); }
//输出找到的最小刻度线的点 public myPair FindLeft() { myPair ret = new myPair(-1, -1); int max1 = 0; for (int i = height - 1; i * 3 >= 2 *height; i--) { int sum = 0; for (int j = 0; j < 100; j++) { if (gray[j, i] == 255) { if (gray[j + 1, i - 1] == 255 && gray[j + 2, i - 2] == 255 && gray[j + 3, i - 3] == 255 && gray[j + 4, i - 4] == 255) { //return new node(circle_x - (int)Math.Sqrt(radius * radius - (circle_y - i) * (circle_y - i)),i); sum = (j - circle_x) * (j - circle_x) + (i - circle_y) * (i - circle_y); if (Math.Sqrt(max1) + 10 < Math.Sqrt(sum)) { ret.x = j; ret.y = i; max1 = sum; } break; } else if (gray[j + 1, i] == 255 && gray[j + 2, i - 1] == 255 && gray[j + 3, i - 2] == 255 && gray[j + 4, i - 3] == 255) { sum = (j - circle_x) * (j - circle_x) + (i - circle_y) * (i - circle_y); if (Math.Sqrt(max1) + 10 < Math.Sqrt(sum)) { ret.x = j; ret.y = i; max1 = sum; } break; } } } } return(ret); }
//根据直线的两个端点P,Q进行初始化 public line(myPair P, myPair Q) { if (P.x == Q.x) { A = 1; B = 0; C = -P.x; return; } if (P.y == Q.y) { A = 0; B = 1; C = -P.y; return; } B = 1; A = 1.0 * (Q.y - P.y) / (P.x - Q.x); C = -(A * P.x + B * P.y); }
//输出找到的最大刻度线的点 public myPair FindRight() { myPair ret = new myPair(0, 0); int max1 = 0; for (int i = height - 1; i * 3 >= 2 *height; i--) { int sum = 0; for (int j = width - 1, k = 0; k < 100; k++, j--) { if (gray[j, i] == 255) { if (gray[j - 1, i - 1] == 255 && gray[j - 2, i - 2] == 255 && gray[j - 3, i - 3] == 255 && gray[j - 4, i - 4] == 255) { sum = (j - circle_x) * (j - circle_x) + (i - circle_y) * (i - circle_y); if (Math.Sqrt(max1) + 10 < Math.Sqrt(sum)) { ret.x = j; ret.y = i; max1 = sum; } break; } else if (gray[j - 1, i] == 255 && gray[j - 2, i - 1] == 255 && gray[j - 3, i - 2] == 255 && gray[j - 4, i - 3] == 255) { sum = (j - circle_x) * (j - circle_x) + (i - circle_y) * (i - circle_y); if (Math.Sqrt(max1) + 10 < Math.Sqrt(sum)) { ret.x = j; ret.y = i; max1 = sum; } break; } } } } return(ret); }
//函数功能:利用RANSAC算法从图像中提取直线,RANSAC对噪声敏感度小 //输入:二值化图像input,目标直线的一个端点center //输出:目标直线方程 public line doRANSAC(myPicture input, myPair center) { LinkedList <myPair> arr = new LinkedList <myPair>(); for (int i = 0; i < input.width; ++i) { for (int j = 0; j < input.height; ++j) { if (input.grayDegree[i, j] == 255) { arr.AddLast(new myPair(i, j)); } } } Random rd = new Random(13); line ret = new line(0, 0, 0); double besterror = 1000000000000; int bound = (int)(0.07 * arr.Count); for (int iter = 0; iter < 100; ++iter) { int sampleIndex = rd.Next() % arr.Count; myPair sample = arr.ElementAt(sampleIndex); line cur = new line(center, sample); LinkedList <myPair> alsoinliers = new LinkedList <myPair>(); foreach (myPair p in arr) { double d = cur.distance(p); if (d <= 4) { alsoinliers.AddLast(p); } } if (alsoinliers.Count >= bound) { alsoinliers.AddLast(sample); line better = calLine(alsoinliers); double thiserror = 0; foreach (myPair p in alsoinliers) { double dis = better.distance(p); thiserror += dis * dis; } thiserror /= alsoinliers.Count; if (thiserror < besterror) { besterror = thiserror; ret = better; } } } return(ret); /* * for (int i = 0; i < input.width; ++i) * { * for (int j = 0; j < input.height; ++j) * { * double val = Math.Abs(ret.A * i + ret.B * j + ret.C); * if (val <= 1) * { * input.picture.SetPixel(i, j, Color.FromArgb(255, 0, 0)); * } * } * } */ //return input; }
//计算该直线到点P的距离 public double distance(myPair P) { return(Math.Abs(A * P.x + B * P.y + C) / Math.Sqrt(A * A + B * B)); }
//函数功能:对main.cs传入的图像进行处理,并将结果传递到ans.cs显示 public void domain() { //处理输入 double low = 0.0; double high = 1.0; try { low = Convert.ToDouble(smallInput.Text); high = Convert.ToDouble(largeInput.Text); } catch { return; } OTSU B = new OTSU(); //锐化、二值化 myPicture ret = B.doOTSU(sharp4.doSharp(Pic)); myPicture res = ret; //找圆心之前的预处理 findCircleInit circle = new findCircleInit(); ret = circle.init(ret); ret = sharp4.doSharp(ret); ret = circle.doHough(ret); //找圆心 circle o = new findCircleCenter().doFind(ret); //找指针 ret = new findLineInit().doFind(Pic, o); myPicture tmp = sharp1.doSharp(ret); tmp = smooth2.doSmooth(tmp); tmp = B.doOTSU(tmp); RANSAC R = new RANSAC(); myPair L = R.doFind(smooth2.doSmooth(B.doOTSU(sharp4.doSharp(Pic))), R.doRANSAC(tmp, new myPair((int)o.x, (int)o.y)), o); //找刻度线的预处理 eraseCircleElement ECE = new eraseCircleElement(res, (int)o.r, (int)o.x, (int)o.y); int r = ECE.solve(); res = ECE.erase(r - 1); //找到左右两端的刻度线 findTick ticks = new findTick(res, r - 1, (int)o.x, (int)o.y); myPair left = ticks.FindLeft(), right = ticks.FindRight(); //求角度 double thta = 2 * Math.PI - angle(left, right, o); double alpha; if (L.x <= o.x) { alpha = angle(left, L, o); } else { alpha = thta - angle(L, right, o); } double ans = low + (high - low) * (alpha + Math.PI / 180) / thta; Form newForm = new ans(ans); newForm.ShowDialog(); newForm.Dispose(); }
private double angle(myPair left, myPair right, circle o) { return(Math.Acos(((left.x * right.x - o.x * (left.x + right.x) + o.x * o.x) + (left.y * right.y - o.y * (left.y + right.y) + o.y * o.y)) / (Math.Sqrt((left.x - o.x) * (left.x - o.x) + (left.y - o.y) * (left.y - o.y)) * Math.Sqrt((right.x - o.x) * (right.x - o.x) + (right.y - o.y) * (right.y - o.y))))); }