using System; using System.Collections.Generic; using System.Drawing; using System.Linq; namespace SmartCoalApplication.Base.CommTool { /// /// 基础计算帮助类 /// public class BasicCalculationHelper { /// /// 计算两点之间的距离 /// /// 起点 /// 终点 /// public static double GetDistance(Point startPoint, Point endPoint, int digit) { try { long x = System.Math.Abs(endPoint.X - startPoint.X); long y = System.Math.Abs(endPoint.Y - startPoint.Y); return Math.Round(Math.Sqrt(x * x + y * y), digit); } catch (Exception) { } return 0.0; } /// /// 计算两点之间的距离 /// /// 起点 /// 终点 /// public static double GetDistance(PointF startPoint, PointF endPoint) { try { float x = System.Math.Abs(endPoint.X - startPoint.X); float y = System.Math.Abs(endPoint.Y - startPoint.Y); return Math.Sqrt(x * x + y * y); } catch (Exception) { } return 0.0; } public static double GetDistance(PointF startPoint, PointF endPoint, int digit) { try { long x = System.Math.Abs((int)(endPoint.X - startPoint.X)); long y = System.Math.Abs((int)(endPoint.Y - startPoint.Y)); return Math.Round(Math.Sqrt(x * x + y * y), digit); } catch (Exception) { } return 0.0; } /// /// opencv的仿射变换 /// /// /// /// /// public static OpenCvSharp.Point GetPointAffinedPos(OpenCvSharp.Point src, OpenCvSharp.Point center, double angle) { OpenCvSharp.Point dst; int x = src.X - center.X; int y = src.Y - center.Y; dst.X = (int)Math.Round(x * Math.Cos(angle) + y * Math.Sin(angle) + center.X); dst.Y = (int)Math.Round(-x * Math.Sin(angle) + y * Math.Cos(angle) + center.Y); return dst; } /// /// 计算轮廓内最左和最右点的距离 /// /// public static int GetLeftAndRightDistance(OpenCvSharp.Point[] points) { OpenCvSharp.Point left = points[0]; OpenCvSharp.Point right = points[0]; foreach (OpenCvSharp.Point p in points) { if (left.X > p.X) { left = p; } if (right.X < p.X) { right = p; } } return right.X - left.X; } public static List GetCaliperDiameter(OpenCvSharp.Point[] contours, OpenCvSharp.HierarchyIndex hierarchyIndex) { //计算结果 List vs = new List(); //每次旋转的角度 double angle = 360 / 64; //计算中心点 OpenCvSharp.Moments m = OpenCvSharp.Cv2.Moments(contours); double cx = m.M10 / m.M00; double cy = m.M01 / m.M00; for (int i = 0; i < 64; i++) { if (i == 0) { vs.Add(GetLeftAndRightDistance(contours)); } else { if (contours.Length >= 5 && hierarchyIndex.Parent == -1 && !Double.IsNaN(cx)) { try { OpenCvSharp.Point[] temps = new OpenCvSharp.Point[contours.Count()]; for (int j = 0; j < contours.Count(); j++) { temps[j] = BasicCalculationHelper.GetPointAffinedPos(contours[j], new OpenCvSharp.Point(cx, cy), angle * i); } vs.Add(GetLeftAndRightDistance(temps)); } catch (Exception) { } } /*else { vs.Add(0); }*/ } } return vs; } /// /// 求第三个点到直线上的垂足的点 /// /// 构成直线的点1 /// 构成直线的点2 /// 线外的一点 /// public static PointF GetDropFeet(PointF start, PointF end, PointF three) { PointF footPoint = new PointF(); // 求直线斜率 double k = (double)(end.Y - start.Y) / (end.X - start.X); //pointArray[0].X == pointArray[1].X || pointArray[0].Y == pointArray[1].Y if (double.IsInfinity(k) || Math.Abs(k) < 0.001) //垂线斜率不存在或斜率为0 { if (k > 0) { footPoint.X = start.X; footPoint.Y = three.Y; } else if (k < 0) { footPoint.X = start.X; footPoint.Y = three.Y; } else { footPoint.Y = start.Y; footPoint.X = three.X; } } else { footPoint.X = (float)((k * start.X + three.X / k + three.Y - start.Y) / (1 / k + k)); footPoint.Y = (float)(-1 / k * (footPoint.X - three.X) + three.Y); } return footPoint; } /// /// 已知直线上一点、斜率、线外一点,求垂足 /// /// 线上一点 /// 斜率 /// 线外一点 /// public static PointF GetDropFeet(PointF start, double k, PointF three) { PointF footPoint = new PointF(); if (double.IsInfinity(k) || Math.Abs(k) < 0.001) //垂线斜率不存在或斜率为0 { if (k > 0) { footPoint.X = start.X; footPoint.Y = three.Y; } else if (k < 0) { footPoint.X = start.X; footPoint.Y = three.Y; } else { footPoint.Y = start.Y; footPoint.X = three.X; } } else { footPoint.X = (float)((k * start.X + three.X / k + three.Y - start.Y) / (1 / k + k)); footPoint.Y = (float)(-1 / k * (footPoint.X - three.X) + three.Y); } return footPoint; } /// /// 三点计算角度,可能不对 /// /// /// /// /// public static double Angle(Point cen, Point first, Point second) { //判断象限 int x = first.X - cen.X; int y = first.Y - cen.Y; int i = 0; if (x > 0 && y > 0) //第4象限 { i = 4; } else if (x > 0 && y < 0) //第1象限 { i = 1; } else if (x < 0 && y < 0) //第2象限 { i = 2; } else if (x < 0 && y > 0) //第3象限 { i = 3; } double ma_x = first.X - cen.X; double ma_y = first.Y - cen.Y; double mb_x = second.X - cen.X; double mb_y = second.Y - cen.Y; double v1 = (ma_x * mb_x) + (ma_y * mb_y); double ma_val = Math.Sqrt(ma_x * ma_x + ma_y * ma_y); double mb_val = Math.Sqrt(mb_x * mb_x + mb_y * mb_y); double cosM = v1 / (ma_val * mb_val); double angleAMB = Math.Acos(cosM) * 180 / Math.PI; if (i == 1) angleAMB = angleAMB - 90; if (i == 4) angleAMB = 90 - angleAMB; if (i == 3) angleAMB = angleAMB + 90; if (i == 2) angleAMB = 270 - angleAMB; return double.IsNaN(angleAMB) ? 0 : angleAMB; } public static double Angle(PointF cen, PointF first, PointF second) { //判断象限 int x = (int)(first.X - cen.X); int y = (int)(first.Y - cen.Y); int i = 0; if (x > 0 && y > 0) //第4象限 { i = 4; } else if (x > 0 && y < 0) //第1象限 { i = 1; } else if (x < 0 && y < 0) //第2象限 { i = 2; } else if (x < 0 && y > 0) //第3象限 { i = 3; } double ma_x = first.X - cen.X; double ma_y = first.Y - cen.Y; double mb_x = second.X - cen.X; double mb_y = second.Y - cen.Y; double v1 = (ma_x * mb_x) + (ma_y * mb_y); double ma_val = Math.Sqrt(ma_x * ma_x + ma_y * ma_y); double mb_val = Math.Sqrt(mb_x * mb_x + mb_y * mb_y); double cosM = v1 / (ma_val * mb_val); double angleAMB = Math.Acos(cosM) * 180 / Math.PI; if (i == 1) angleAMB = angleAMB - 90; if (i == 4) angleAMB = 90 - angleAMB; if (i == 3) angleAMB = angleAMB + 90; if (i == 2) angleAMB = 270 - angleAMB; return double.IsNaN(angleAMB) ? 0 : angleAMB; } /// /// 三点计算角度,第二个写法 /// /// /// /// /// /// /// /// public static double CalculateAngle(double P1X, double P1Y, double P2X, double P2Y, double P3X, double P3Y) { double numerator = P2Y * (P1X - P3X) + P1Y * (P3X - P2X) + P3Y * (P2X - P1X); double denominator = (P2X - P1X) * (P1X - P3X) + (P2Y - P1Y) * (P1Y - P3Y); double ratio = numerator / denominator; double angleRad = Math.Atan(ratio); double angleDeg = (angleRad * 180) / Math.PI; /*if (angleDeg < 0) { angleDeg = 180 + angleDeg; }*/ /*if(angleDeg>360) { angleDeg = angleDeg - 360 * (angleDeg / 360); }*/ //angleDeg = 180 - angleDeg; return -angleDeg; } /// /// 根据圆心点旋转另一个点 /// /// 圆上的点 /// 圆心点 /// 旋转角度 /// 返回旋转后的点的坐标 public static Point GetAnglePoint(Point point, Point center, double angle) { Point p = new Point(); angle = angle * Math.PI / 180; p.X = Convert.ToInt32((point.X - center.X) * Math.Cos(angle) - (point.Y - center.Y) * Math.Sin(angle) + center.X); p.Y = Convert.ToInt32((point.X - center.X) * Math.Sin(angle) + (point.Y - center.Y) * Math.Cos(angle) + center.Y); return p; } /// /// 计算夹角 /// /// 固定点 /// 圆心点 /// 浮点 /// 返回旋转后的点的坐标 public static double CalculateAngle(PointF cen, PointF first, PointF second) { double ma_x = first.X - cen.X; double ma_y = first.Y - cen.Y; double mb_x = second.X - cen.X; double mb_y = second.Y - cen.Y; double v1 = (ma_x * mb_x) + (ma_y * mb_y); double ma_val = Math.Sqrt(ma_x * ma_x + ma_y * ma_y); double mb_val = Math.Sqrt(mb_x * mb_x + mb_y * mb_y); double cosM = v1 / (ma_val * mb_val); double angleAMB = Math.Acos(cosM) * 180 / Math.PI; if (second.Y < first.Y) angleAMB = -angleAMB; return angleAMB; } public static PointF GetAnglePoint(PointF point, PointF center, double angle) { PointF p = new PointF(); angle = angle * Math.PI / 180; p.X = (float)((point.X - center.X) * Math.Cos(angle) - (point.Y - center.Y) * Math.Sin(angle) + center.X); p.Y = (float)((point.X - center.X) * Math.Sin(angle) + (point.Y - center.Y) * Math.Cos(angle) + center.Y); return p; } /// /// 计算宽高比 /// /// /// public static double CalcAspectRatio(OpenCvSharp.Point[] points) { List ps = points.ToList(); //最左侧的点 int left = ps.Min(a => a.X); //最右侧的点 int right = ps.Max(a => a.X); //最顶部的点 int top = ps.Min(a => a.Y); //最底部的点 int bottom = ps.Max(a => a.Y); int width = right - left; int height = bottom - top; if (width == 0 || height == 0) return 0; else return width * 1d / height; } /// /// 计算长径,找轮廓的最小外接圆 /// /// /// public static double CalcLongTrail(OpenCvSharp.Point[] points) { if (points.Length < 5) return 0; OpenCvSharp.Point2f center; float radius = 0f; OpenCvSharp.Cv2.MinEnclosingCircle(points, out center, out radius); return radius; //OpenCvSharp.RotatedRect rect = OpenCvSharp.Cv2.FitEllipse(points); //return rect.Size.Height > rect.Size.Width ? rect.Size.Height : rect.Size.Width; } /// /// 保留两位小数进位 /// /// double类型的值 /// public static double DecimalCeiling(double value) { double values = Math.Round(value, 2); if (values < value) { values += Math.Pow(0.1, 2); } return values; } /// /// 保留两位小数不进位 /// /// double类型的值 /// public static double DecimalFloor(double value) { return double.Parse(value.ToString("F2")); } /// /// 计算长径,找轮廓的外接椭圆,计算椭圆的长径 /// /// /// public static double CalcLongTrailEllipse(OpenCvSharp.Point[] points) { if (points.Length < 5) return 0; OpenCvSharp.RotatedRect rect = OpenCvSharp.Cv2.FitEllipse(points); return rect.Size.Height > rect.Size.Width ? rect.Size.Height : rect.Size.Width; } /// /// 计算曲线长度 /// /// /// /// /// private static double A = 4f / 14175f; private static int[] B = { 989, 5888, -928, 10496, -4540, 10496, -928, 5888, 989 }; public static double curveLength(PointF[] mLeafPt) { if (mLeafPt == null || mLeafPt.Length == 1) { return 0f; } int n = mLeafPt.Length; PointF p0 = mLeafPt[0]; PointF Pn = mLeafPt[n - 1]; PointF[] tempArray = new PointF[n + 2]; tempArray[0] = p0; tempArray[tempArray.Length - 1] = Pn; for (int i = 1; i < tempArray.Length - 1; i++) { tempArray[i] = mLeafPt[i - 1]; } double a = 0f, b = 0.5f; double h = (b - a) / 8f; double length = 0f; for (int i = 0; i < n - 1; i++) { double integral = 0f; for (int j = 0; j <= 8; j++) { double t = a + j * h; double px = Px(tempArray[i].X, tempArray[i + 1].X, tempArray[i + 2].X, tempArray[i + 1].X, t); double py = Px(tempArray[i].Y, tempArray[i + 1].Y, tempArray[i + 2].Y, tempArray[i + 1].Y, t); double f = (double)Math.Sqrt(px * px + py * py); integral += B[j] * f; } integral *= h * A; length += integral; } return length; } private static double Px(float p1, float p2, float p3, float p4, double t) { return (-12f * t * t + 8 * t - 1) * p1 + (36 * t * t - 20 * t) * p2 + (-36 * t * t + 16 * t + 1) * p3 + (12 * t * t - 4 * t) * p4; } /// /// 三点角计算 /// /// /// /// /// public static double AngleText(PointF cen, PointF first, PointF second) { const double M_PI = 3.1415926535897; double ma_x = first.X - cen.X; double ma_y = first.Y - cen.Y; double mb_x = second.X - cen.X; double mb_y = second.Y - cen.Y; double v1 = (ma_x * mb_x) + (ma_y * mb_y); double ma_val = Math.Sqrt(ma_x * ma_x + ma_y * ma_y); double mb_val = Math.Sqrt(mb_x * mb_x + mb_y * mb_y); double cosM = v1 / (ma_val * mb_val); double angleAMB = Math.Acos(cosM) * 180 / M_PI; return angleAMB; } /// ///点到直线距离 /// /// 线上的1点 /// 线上的2点 /// 线外的点 public static double pointToLine(Point point1, Point point2, Point point3) { double space = 0; double a, b, c; a = GetDistance(point1, point2, 7);// 线段的长度 b = GetDistance(point1, point3, 7);// (x1,y1)到点的距离 c = GetDistance(point2, point3, 7);// (x2,y2)到点的距离 if (c <= 0.000001 || b <= 0.000001) { space = 0; return space; } if (a <= 0.000001) { space = b; return space; } if (c * c >= a * a + b * b) { space = b; return space; } if (b * b >= a * a + c * c) { space = c; return space; } double p = (a + b + c) / 2;// 半周长 double s = Math.Sqrt(p * (p - a) * (p - b) * (p - c));// 海伦公式求面积 space = 2 * s / a;// 返回点到线的距离(利用三角形面积公式求高) return space; } /// /// 计算多边形周长 /// /// 多边形顶点 /// 小数位数 /// public static double GetPolygonPerimeter(PointF[] Points, int digit) { float x; float y; int j = 0; double length = 0.0; for (int i = 0; i < Points.Length; i++) { if (i == Points.Length - 1) j = -1; else j = i; x = System.Math.Abs(Points[j + 1].X - Points[i].X); y = System.Math.Abs(Points[j + 1].Y - Points[i].Y); length = length + Math.Round(Math.Sqrt(x * x + y * y), digit); } return length; } /// /// 计算多边形面积 /// /// /// /// public static double GetPolygonArea(List points) { var count = points.Count; double area0 = 0; double area1 = 0; for (int i = 0; i < count; i++) { double x = points[i].X; double y = i + 1 < count ? points[i + 1].Y : points[0].Y; area0 += x * y; double lat = points[i].Y; double lon = i + 1 < count ? points[i + 1].X : points[0].X; area1 += lat * lon; } return Math.Round(Math.Abs(0.5 * (area0 - area1)), 10); } /// /// 计算两条直线的交点 /// /// L1的点1坐标 /// L1的点2坐标 /// L2的点1坐标 /// L2的点2坐标 /// public static PointF GetIntersection(PointF lineFirstStar, PointF lineFirstEnd, PointF lineSecondStar, PointF lineSecondEnd) { /* * L1,L2都存在斜率的情况: * 直线方程L1: ( y - y1 ) / ( y2 - y1 ) = ( x - x1 ) / ( x2 - x1 ) * => y = [ ( y2 - y1 ) / ( x2 - x1 ) ]( x - x1 ) + y1 * 令 a = ( y2 - y1 ) / ( x2 - x1 ) * 有 y = a * x - a * x1 + y1 .........1 * 直线方程L2: ( y - y3 ) / ( y4 - y3 ) = ( x - x3 ) / ( x4 - x3 ) * 令 b = ( y4 - y3 ) / ( x4 - x3 ) * 有 y = b * x - b * x3 + y3 ..........2 * * 如果 a = b,则两直线平等,否则, 联解方程 1,2,得: * x = ( a * x1 - b * x3 - y1 + y3 ) / ( a - b ) * y = a * x - a * x1 + y1 * * L1存在斜率, L2平行Y轴的情况: * x = x3 * y = a * x3 - a * x1 + y1 * * L1 平行Y轴,L2存在斜率的情况: * x = x1 * y = b * x - b * x3 + y3 * * L1与L2都平行Y轴的情况: * 如果 x1 = x3,那么L1与L2重合,否则平等 * */ float a = 0, b = 0; int state = 0; if (lineFirstStar.X != lineFirstEnd.X) { a = (lineFirstEnd.Y - lineFirstStar.Y) / (lineFirstEnd.X - lineFirstStar.X); state |= 1; } if (lineSecondStar.X != lineSecondEnd.X) { b = (lineSecondEnd.Y - lineSecondStar.Y) / (lineSecondEnd.X - lineSecondStar.X); state |= 2; } switch (state) { case 0: //L1与L2都平行Y轴 { if (lineFirstStar.X == lineSecondStar.X) { //throw new Exception("两条直线互相重合,且平行于Y轴,无法计算交点。"); return new PointF(0, 0); } else { //throw new Exception("两条直线互相平行,且平行于Y轴,无法计算交点。"); return new PointF(0, 0); } } case 1: //L1存在斜率, L2平行Y轴 { float x = lineSecondStar.X; float y = (lineFirstStar.X - x) * (-a) + lineFirstStar.Y; return new PointF(x, y); } case 2: //L1 平行Y轴,L2存在斜率 { float x = lineFirstStar.X; //网上有相似代码的,这一处是错误的。你可以对比case 1 的逻辑 进行分析 //源code:lineSecondStar * x + lineSecondStar * lineSecondStar.X + p3.Y; float y = (lineSecondStar.X - x) * (-b) + lineSecondStar.Y; return new PointF(x, y); } case 3: //L1,L2都存在斜率 { if (a == b) { // throw new Exception("两条直线平行或重合,无法计算交点。"); return new PointF(0, 0); } float x = (a * lineFirstStar.X - b * lineSecondStar.X - lineFirstStar.Y + lineSecondStar.Y) / (a - b); float y = a * x - a * lineFirstStar.X + lineFirstStar.Y; return new PointF(x, y); } } // throw new Exception("不可能发生的情况"); return new PointF(0, 0); } public static List> GetWholeContoursTree(OpenCvSharp.Point[][] contours, OpenCvSharp.HierarchyIndex[] hierarchyIndex, int k) { List> wholeContours = new List>(); wholeContours.Add(contours[k].ToList()); if (hierarchyIndex[k].Child > 0) { RecursionGetWholeContoursTree(wholeContours, contours, hierarchyIndex, k); } return wholeContours; } public static void RecursionGetWholeContoursTree(List> wholeContours, OpenCvSharp.Point[][] contours, OpenCvSharp.HierarchyIndex[] hierarchyIndex, int k) { if (hierarchyIndex[k].Child > 0) { for (int i = 0; i < hierarchyIndex.Length; i++) { if (hierarchyIndex[i].Parent == k) { wholeContours.Add(contours[i].ToList()); if (hierarchyIndex[i].Child > 0) { RecursionGetWholeContoursTree(wholeContours, contours, hierarchyIndex, i); } } } } } } }