// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: LineMeasurementProcessor.cs
// 描述: 直线测量算子,用于测量图像中两点之间的距离
// 功能:
// - 用户指定两个点坐标(像素坐标)
// - 计算两点之间的欧氏距离(像素单位)
// - 支持像素尺寸标定,输出实际物理距离
// - 在图像上绘制测量线和标注
// - 输出测量结果供后续处理使用
// 算法: 欧氏距离计算
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
using System.Drawing;
namespace XP.ImageProcessing.Processors;
///
/// 直线测量算子 - 测量两点之间的距离
///
public class LineMeasurementProcessor : ImageProcessorBase
{
private static readonly ILogger _logger = Log.ForContext();
public LineMeasurementProcessor()
{
Name = LocalizationHelper.GetString("LineMeasurementProcessor_Name");
Description = LocalizationHelper.GetString("LineMeasurementProcessor_Description");
}
protected override void InitializeParameters()
{
Parameters.Add("X1", new ProcessorParameter(
"X1",
LocalizationHelper.GetString("LineMeasurementProcessor_X1"),
typeof(int), 100, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_X1_Desc"))
{ IsVisible = false });
Parameters.Add("Y1", new ProcessorParameter(
"Y1",
LocalizationHelper.GetString("LineMeasurementProcessor_Y1"),
typeof(int), 100, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_Y1_Desc"))
{ IsVisible = false });
Parameters.Add("X2", new ProcessorParameter(
"X2",
LocalizationHelper.GetString("LineMeasurementProcessor_X2"),
typeof(int), 400, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_X2_Desc"))
{ IsVisible = false });
Parameters.Add("Y2", new ProcessorParameter(
"Y2",
LocalizationHelper.GetString("LineMeasurementProcessor_Y2"),
typeof(int), 400, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_Y2_Desc"))
{ IsVisible = false });
Parameters.Add("PixelSize", new ProcessorParameter(
"PixelSize",
LocalizationHelper.GetString("LineMeasurementProcessor_PixelSize"),
typeof(double), 1.0, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_PixelSize_Desc")));
Parameters.Add("Unit", new ProcessorParameter(
"Unit",
LocalizationHelper.GetString("LineMeasurementProcessor_Unit"),
typeof(string), "px", null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_Unit_Desc"),
new string[] { "px", "mm", "μm", "cm" }));
Parameters.Add("Thickness", new ProcessorParameter(
"Thickness",
LocalizationHelper.GetString("LineMeasurementProcessor_Thickness"),
typeof(int), 2, 1, 10,
LocalizationHelper.GetString("LineMeasurementProcessor_Thickness_Desc")));
Parameters.Add("ShowLabel", new ProcessorParameter(
"ShowLabel",
LocalizationHelper.GetString("LineMeasurementProcessor_ShowLabel"),
typeof(bool), true, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_ShowLabel_Desc")));
}
public override Image Process(Image inputImage)
{
int x1 = GetParameter("X1");
int y1 = GetParameter("Y1");
int x2 = GetParameter("X2");
int y2 = GetParameter("Y2");
double pixelSize = GetParameter("PixelSize");
string unit = GetParameter("Unit");
int thickness = GetParameter("Thickness");
bool showLabel = GetParameter("ShowLabel");
_logger.Debug("LineMeasurement: ({X1},{Y1}) -> ({X2},{Y2}), PixelSize={PixelSize}, Unit={Unit}",
x1, y1, x2, y2, pixelSize, unit);
OutputData.Clear();
// 限制坐标在图像范围内
x1 = Math.Clamp(x1, 0, inputImage.Width - 1);
y1 = Math.Clamp(y1, 0, inputImage.Height - 1);
x2 = Math.Clamp(x2, 0, inputImage.Width - 1);
y2 = Math.Clamp(y2, 0, inputImage.Height - 1);
// 计算像素距离
double dx = x2 - x1;
double dy = y2 - y1;
double pixelDistance = Math.Sqrt(dx * dx + dy * dy);
// 计算实际距离
double actualDistance = pixelDistance * pixelSize;
// 计算角度(相对于水平方向)
double angleRad = Math.Atan2(dy, dx);
double angleDeg = angleRad * 180.0 / Math.PI;
// 存储测量结果
OutputData["MeasurementType"] = "Line";
OutputData["Point1"] = new Point(x1, y1);
OutputData["Point2"] = new Point(x2, y2);
OutputData["PixelDistance"] = pixelDistance;
OutputData["ActualDistance"] = actualDistance;
OutputData["Unit"] = unit;
OutputData["Angle"] = angleDeg;
OutputData["Thickness"] = thickness;
OutputData["ShowLabel"] = showLabel;
// 构建测量信息文本
string distanceText = unit == "px"
? $"{pixelDistance:F2} px"
: $"{actualDistance:F4} {unit} ({pixelDistance:F2} px)";
OutputData["MeasurementText"] = distanceText;
_logger.Information("LineMeasurement completed: Distance={Distance}, Angle={Angle:F2}°",
distanceText, angleDeg);
return inputImage.Clone();
}
}