Files
2026-04-14 17:12:31 +08:00

88 lines
3.8 KiB
C#

// ============================================================================
// 文件名: AngleMeasurementProcessor.cs
// 描述: 角度测量算子 — 共端点的两条直线夹角
// 功能:
// - 用户定义三个点:端点(顶点)、射线1终点、射线2终点
// - 计算两条射线之间的夹角(0°~180°)
// - 在图像上绘制两条射线、角度弧线和标注
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
using System.Drawing;
namespace XP.ImageProcessing.Processors;
public class AngleMeasurementProcessor : ImageProcessorBase
{
private static readonly ILogger _logger = Log.ForContext<AngleMeasurementProcessor>();
public AngleMeasurementProcessor()
{
Name = LocalizationHelper.GetString("AngleMeasurementProcessor_Name");
Description = LocalizationHelper.GetString("AngleMeasurementProcessor_Description");
}
protected override void InitializeParameters()
{
// 三个点坐标(由交互控件注入,使用 double 避免取整误差)
Parameters.Add("VX", new ProcessorParameter("VX", "VX", typeof(double), 250.0, null, null, "") { IsVisible = false });
Parameters.Add("VY", new ProcessorParameter("VY", "VY", typeof(double), 250.0, null, null, "") { IsVisible = false });
Parameters.Add("AX", new ProcessorParameter("AX", "AX", typeof(double), 100.0, null, null, "") { IsVisible = false });
Parameters.Add("AY", new ProcessorParameter("AY", "AY", typeof(double), 250.0, null, null, "") { IsVisible = false });
Parameters.Add("BX", new ProcessorParameter("BX", "BX", typeof(double), 250.0, null, null, "") { IsVisible = false });
Parameters.Add("BY", new ProcessorParameter("BY", "BY", typeof(double), 100.0, null, null, "") { IsVisible = false });
}
public override Image<Gray, byte> Process(Image<Gray, byte> inputImage)
{
double vx = GetParameter<double>("VX"), vy = GetParameter<double>("VY");
double ax = GetParameter<double>("AX"), ay = GetParameter<double>("AY");
double bx = GetParameter<double>("BX"), by = GetParameter<double>("BY");
OutputData.Clear();
// 向量 VA 和 VB
double vax = ax - vx, vay = ay - vy;
double vbx = bx - vx, vby = by - vy;
double lenA = Math.Sqrt(vax * vax + vay * vay);
double lenB = Math.Sqrt(vbx * vbx + vby * vby);
double angleDeg = 0;
if (lenA > 0.001 && lenB > 0.001)
{
double dot = vax * vbx + vay * vby;
double cosAngle = Math.Clamp(dot / (lenA * lenB), -1.0, 1.0);
angleDeg = Math.Acos(cosAngle) * 180.0 / Math.PI;
}
// 计算角度弧的起始角和扫过角(用于绘制弧线)
double angleA = Math.Atan2(vay, vax) * 180.0 / Math.PI;
double angleB = Math.Atan2(vby, vbx) * 180.0 / Math.PI;
// 确保从 angleA 到 angleB 的扫过方向是较小的夹角
double sweep = angleB - angleA;
if (sweep > 180) sweep -= 360;
if (sweep < -180) sweep += 360;
string angleText = $"{angleDeg:F2} deg";
OutputData["AngleMeasurementResult"] = true;
OutputData["Vertex"] = new Point((int)Math.Round(vx), (int)Math.Round(vy));
OutputData["PointA"] = new Point((int)Math.Round(ax), (int)Math.Round(ay));
OutputData["PointB"] = new Point((int)Math.Round(bx), (int)Math.Round(by));
OutputData["AngleDeg"] = angleDeg;
OutputData["ArcStartAngle"] = angleA;
OutputData["ArcSweepAngle"] = sweep;
OutputData["AngleText"] = angleText;
_logger.Information("AngleMeasurement: Angle={Angle}, V=({VX},{VY}), A=({AX},{AY}), B=({BX},{BY})",
angleText, vx, vy, ax, ay, bx, by);
return inputImage.Clone();
}
}