// ============================================================================ // 文件? AngleMeasurementProcessor.cs // 描述: 角度测量算子 ?共端点的两条直线夹角 // 功能: // - 用户定义三个点:端点(顶点)、射?终点、射?终点 // - 计算两条射线之间的夹角(0°~180°? // - 在图像上绘制两条射线、角度弧线和标注 // ============================================================================ using Emgu.CV; using Emgu.CV.Structure; using Serilog; using System.Drawing; using XP.ImageProcessing.Core; namespace XP.ImageProcessing.Processors; public class AngleMeasurementProcessor : ImageProcessorBase { private static readonly ILogger _logger = Log.ForContext(); 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 Process(Image inputImage) { double vx = GetParameter("VX"), vy = GetParameter("VY"); double ax = GetParameter("AX"), ay = GetParameter("AY"); double bx = GetParameter("BX"), by = GetParameter("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(); } }