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);
}
}
}
}
}
}
}