2026-01-26 18:56:53 +09:00

374 lines
18 KiB
C#

/*
* Copyright (c) 2025 LG Electronics Inc.
* SPDX-License-Identifier: LicenseRef-LGE-Proprietary
*/
using Aurora.Api;
using MilCommon;
using VisionCommon;
using Hermes;
using Matrox.MatroxImagingLibrary;
using System;
using System.Collections.Generic;
using Aurora.View;
using Newtonsoft.Json;
using System.IO;
namespace MilPattern {
public class LineFinderRunParam : IRunParam, ICloneable {
public ePolarity Polarity { get; set; } = ePolarity.Any;
public int SubRegionNumber { get; set; } = 8;
public double MinEdgeValue { get; set; } = 2.0;
public Angle AngleTolerance { get; set; } = Angle.CreateWithDegree(360); // Default Angle 값은 CreateContext시에도 최초에 360도가 적용됨.
public MrkParamExt MrkParamExt { get; set; } = new MrkParamExt();
public object Clone() {
return new LineFinderRunParam() {
Polarity = Polarity,
SubRegionNumber = SubRegionNumber,
MinEdgeValue = MinEdgeValue,
AngleTolerance = AngleTolerance.Clone() as Angle,
MrkParamExt = MrkParamExt.Clone() as MrkParamExt,
};
}
}
/// <summary>
/// 마커의 가장자리가 설정된 강도 전환의 시작과 끝 사이의 회색조 차이를 기준으로 점수를 백분율로 계산
/// 대조의 경우 8비트 이미지의 경우 최대 가능한 대비는 255이며, 이는 에지 값 100에 해당
/// </summary>
public class ContrastParam : NotifyPropertyChangedBase, ICloneable {
private double _Min = 0.0;
private double _Max = 255;//MIL.M_MAX_POSSIBLE_VALUE;
private double _Low = 0.0;
private double _High = 255; //MIL.M_MAX_POSSIBLE_VALUE;
public bool ContrastEnable { get; set; } = false;
public double ScoreOffset { get; set; } = 0.0;
public double Min { get => _Min; set { if (value <= Max) { SetProperty(ref _Min, value); } } }//if (Low < Min) { _Low = Min; } if (High < Min) { _High = Min; } } }
public double Max { get => _Max; set { if (Min <= value) { SetProperty(ref _Max, value); } } }//if (High > Max) { _High = Max; } if (Low > Max) { _Low = Max; } } }
public double Low => Min;//{ get => _Low; set { if (value<= High && value>= Min) { SetProperty(ref _Low, value); } } }
public double High => Max;// { get => _High; set { if (Low <= value&& value <= Max) { SetProperty(ref _High, value); } } }
public object Clone() {
return new ContrastParam() {
ContrastEnable = this.ContrastEnable,
Min = this.Min,
Max = this.Max,
//Low = this.Low,
//High = this.High,
ScoreOffset = this.ScoreOffset,
};
}
}
/// <summary>
/// 마커의 가장자리가 설정된 강도 전환의 시작과 끝 사이의 회색조 차이를 기준으로 점수를 백분율로 계산
/// 강도의 경우 최대 가능한 값은 100이며, 이는 에지 값 100에 해당
/// </summary>
public class StrengthParam : NotifyPropertyChangedBase, ICloneable {
private double _Min = 0.0;
private double _Max = MIL.M_MAX_POSSIBLE_VALUE;
private double _Low = 0.0;
private double _High = MIL.M_MAX_POSSIBLE_VALUE;
public bool StrengthEnable { get; set; } = false;
public double ScoreOffset { get; set; } = 0.0;
public double Min { get => _Min; set { if (Min <= Max) { SetProperty(ref _Min, value); } if (Low < Min) { Low = Min; } } }
public double Max { get => _Max; set { if (Min <= Max) { SetProperty(ref _Max, value); } if (High > Max) { High = Max; } } }
public double Low { get => _Low; set { if (Low <= High && Low >= Min) { SetProperty(ref _Low, value); } } }
public double High { get => _High; set { if (Low <= High && High <= Max) { SetProperty(ref _High, value); } } }
public object Clone() {
return new StrengthParam() {
StrengthEnable = this.StrengthEnable,
Min = this.Min,
Max = this.Max,
Low = this.Low,
High = this.High,
ScoreOffset = this.ScoreOffset,
};
}
}
public class MrkParamExt : ICloneable {
private const string FILE_NAME = "MrkParamExt.json";
public bool IsStripe { get; set; } = false;
public ROI FindROI { get; set; } = new ROI();
public ContrastParam ContrastParam { get; set; } = new ContrastParam();
public StrengthParam StrengthParam { get; set; } = new StrengthParam();
public MrkParamExt() {
}
public MrkParamExt(ContrastParam contrastParam, StrengthParam strengthParam) {
ContrastParam = contrastParam.Clone() as ContrastParam;
StrengthParam = strengthParam.Clone() as StrengthParam;
}
public object Clone() {
return new MrkParamExt() {
IsStripe = this.IsStripe,
FindROI = FindROI.Clone() as ROI,
ContrastParam = this.ContrastParam.Clone() as ContrastParam,
StrengthParam = this.StrengthParam.Clone() as StrengthParam,
};
}
public static bool Exists(string dirPath) {
string filePath = Path.Combine(dirPath, FILE_NAME);
return File.Exists(filePath);
}
internal static MrkParamExt Load(string dirPath) {
string filePath = Path.Combine(dirPath, FILE_NAME);
if (File.Exists(filePath)) {
return JsonConvert.DeserializeObject(
File.ReadAllText(filePath), typeof(MrkParamExt)) as MrkParamExt;
}
return new MrkParamExt();
}
public static int Save(string dirPath, MrkParamExt mrkParamExt) {
string filePath = Path.Combine(dirPath, FILE_NAME);
if (!Directory.Exists(dirPath)) {
Directory.CreateDirectory(dirPath);
}
using (var fs = File.Create(filePath))
using (var sw = new StreamWriter(fs))
using (var jtw = new JsonTextWriter(sw) {
Formatting = Formatting.Indented,
Indentation = 2,
}) {
(new JsonSerializer()).Serialize(jtw, mrkParamExt);
}
return E1.OK;
}
}
internal static class MilMeasureLine {
private static Logger _Logger = new Logger(typeof(MilMeasureLine).Name);
public static MIL_ID CreateContext(MIL_ID milSystem) {
return MilMeasureCommon.CreateContext(milSystem, MIL.M_EDGE);
}
public static MIL_ID CreateStripeContext(MIL_ID milSystem) {
return MilMeasureCommon.CreateContext(milSystem, MIL.M_STRIPE);
}
public static IRunParam GetRunParam(MIL_ID contextId) {
double angleTolerance = 0;
MIL.MmeasInquire(contextId, MIL.M_BOX_ANGLE_TOLERANCE, ref angleTolerance, MIL.M_NULL);
LineFinderRunParam runParam = new LineFinderRunParam() {
SubRegionNumber =
(int)MIL.MmeasInquire(contextId, MIL.M_SUB_REGIONS_NUMBER, MIL.M_NULL, MIL.M_NULL),
Polarity =
(ePolarity)(int)MIL.MmeasInquire(contextId, MIL.M_POLARITY, MIL.M_NULL, MIL.M_NULL),
MinEdgeValue =
(int)MIL.MmeasInquire(contextId, MIL.M_EDGEVALUE_MIN, MIL.M_NULL, MIL.M_NULL),
AngleTolerance = Angle.CreateWithDegree(angleTolerance),
//FindROI = MilMeasureCommon.GetFindROI(contextId),
};
return runParam;
}
public static int SetRunParam(MIL_ID contextId, IRunParam runParam) {
if (runParam is LineFinderRunParam) {
var param = runParam as LineFinderRunParam;
if (param.AngleTolerance.Degree == 0) {
param.AngleTolerance = Angle.CreateWithDegree(0.1);
}
MIL.MmeasSetMarker(contextId, MIL.M_POLARITY, (double)param.Polarity, MIL.M_NULL);
MIL.MmeasSetMarker(contextId, MIL.M_SUB_REGIONS_NUMBER, param.SubRegionNumber, MIL.M_NULL);
MIL.MmeasSetMarker(contextId, MIL.M_EDGEVALUE_MIN, param.MinEdgeValue, MIL.M_NULL);
MIL.MmeasSetMarker(contextId, MIL.M_BOX_ANGLE_TOLERANCE, param.AngleTolerance.Degree, MIL.M_NULL);
MilMeasureCommon.SetFindROI(contextId, param.MrkParamExt.FindROI);
// Internal Param
MIL.MmeasSetMarker(contextId, MIL.M_NUMBER, MIL.M_DEFAULT, MIL.M_NULL);
MIL.MmeasSetMarker(contextId, MIL.M_FILTER_TYPE, MIL.M_SHEN, MIL.M_NULL);
MIL.MmeasSetMarker(contextId, MIL.M_SEARCH_REGION_INPUT_UNITS, MIL.M_PIXEL, MIL.M_NULL);
// SetScore
if (param.MrkParamExt.ContrastParam.ContrastEnable) {
MIL.MmeasSetScore(contextId, MIL.M_EDGE_CONTRAST_SCORE, param.MrkParamExt.ContrastParam.Min, param.MrkParamExt.ContrastParam.Low,
param.MrkParamExt.ContrastParam.High, param.MrkParamExt.ContrastParam.Max, param.MrkParamExt.ContrastParam.ScoreOffset, MIL.M_DEFAULT, MIL.M_DEFAULT);
}
else {
MIL.MmeasSetScore(contextId, MIL.M_EDGE_CONTRAST_SCORE, 0.0, 0.0, 255, 255, 0.0, MIL.M_DEFAULT, MIL.M_DEFAULT);
// 사용하지 않을 때에는 Default 값을 넣어준다.
}
if (param.MrkParamExt.StrengthParam.StrengthEnable) {
MIL.MmeasSetScore(contextId, MIL.M_STRENGTH_SCORE, param.MrkParamExt.StrengthParam.Min, param.MrkParamExt.StrengthParam.Low,
param.MrkParamExt.StrengthParam.High, param.MrkParamExt.StrengthParam.Max, param.MrkParamExt.StrengthParam.ScoreOffset, MIL.M_DEFAULT, MIL.M_DEFAULT);
}
else {
MIL.MmeasSetScore(contextId, MIL.M_STRENGTH_SCORE, 0.0, 0.0, MIL.M_MAX_POSSIBLE_VALUE, MIL.M_MAX_POSSIBLE_VALUE, 0.0, MIL.M_DEFAULT, MIL.M_DEFAULT);
// 사용하지 않을 때에는 Default 값을 넣어준다.
}
return E1.OK;
}
return E1.GENERIC;
}
public static PatternResult Find(MIL_ID contextId, MIL_ID image, IRunParam runParam) {
SetRunParam(contextId, runParam);
return Find(contextId, image);
}
public static PatternResult Find(MIL_ID contextId, MIL_ID image, ROI findRoi) {
return MilMeasureCommon.Find(contextId, image, ExtractResults, findRoi);
}
public static PatternResult Find(MIL_ID contextId, MIL_ID image) {
return MilMeasureCommon.Find(contextId, image, ExtractResults);
}
private static PatternResult ExtractResults(MIL_ID contextId, double tactTime) {
var results = new List<LineFinderResult>();
MIL_INT numOfResult = 0;
MIL.MmeasGetResult(contextId, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, ref numOfResult);
if (0 < numOfResult) {
double positionX = 0, positionY = 0;
MIL.MmeasGetResult(contextId, MIL.M_POSITION, ref positionX, ref positionY);
double orininalAngle = 0;
MIL.MmeasGetResult(contextId, MIL.M_ANGLE, ref orininalAngle);
var runParam = GetRunParam(contextId) as LineFinderRunParam;
var angleTol = runParam.AngleTolerance.Degree;
var roiAngle = runParam.MrkParamExt.FindROI.Angle.Degree;
var expectedLineAngle = (roiAngle + 90) % 180;
var measuredLineAngle = orininalAngle % 180;
var diff = expectedLineAngle - measuredLineAngle;
if (diff > 90) {
diff = Math.Abs(measuredLineAngle + 180 - expectedLineAngle);
}
else if (diff < -90) {
diff = Math.Abs(expectedLineAngle + 180 - measuredLineAngle);
}
else {
diff = Math.Abs(diff);
}
if (diff <= angleTol) {
double contrast = 0;
double strength = 0;
MIL.MmeasGetResult(contextId, MIL.M_EDGE_CONTRAST, ref contrast);
MIL.MmeasGetResult(contextId, MIL.M_EDGE_STRENGTH, ref strength);
var item = new LineFinderResult(
new ImagePoint(positionX, positionY),
Angle.CreateWithDegree(orininalAngle % 180),
GetLineEquation(contextId),
MilMeasureCommon.GetFindROI(contextId),
contrast, strength,
contextId);
item.Status = eFoundStatus.FoundOK;
results.Add(item);
}
}
return new PatternResult() {
PatternType = ePatternType.LineFinder,
Status = (results.Count > 0) ? eFoundStatus.FoundOK : eFoundStatus.NotFound,
Results = results,
FindRoi = MilMeasureCommon.GetFindROI(contextId),
TactTimeInMilliSec = MathUtil.ConvertUnitSecToMilli(tactTime)
};
}
private static LineEquation GetLineEquation(MIL_ID contextId) {
double lineA = 0, lineB = 0, lineC = 0;
MIL.MmeasGetResult(contextId, MIL.M_LINE_A, ref lineA);
MIL.MmeasGetResult(contextId, MIL.M_LINE_B, ref lineB);
MIL.MmeasGetResult(contextId, MIL.M_LINE_C, ref lineC);
return new LineEquation(lineA, lineB, lineC);
}
public static PatternResult GetNotFoundPatternResult() {
return new PatternResult() {
Status = eFoundStatus.NotFound,
Results = new List<LineFinderResult>()
};
}
public static void Draw(MIL_ID contextId, PatternResult patternResult, IMilCameraDisplay milCameraDisplay) {
MilMeasureCommon.DrawSubPositions(contextId, milCameraDisplay);
DrawLine(contextId, milCameraDisplay);
DrawText(patternResult, milCameraDisplay);
MilMeasureCommon.DrawPosition(contextId, milCameraDisplay);
}
public static void Draw(MIL_ID contextId, IMilCameraDisplay milCameraDisplay) {
MilMeasureCommon.DrawSubPositions(contextId, milCameraDisplay);
DrawLine(contextId, milCameraDisplay);
MilMeasureCommon.DrawPosition(contextId, milCameraDisplay);
}
public static void DrawTextForMulti(LineFinderResult lineFinderResult, IMilCameraDisplay milCameraDisplay) {
DrawText(lineFinderResult, milCameraDisplay);
}
[Obsolete]
public static void Draw(MIL_ID contextId, LineFinderResult lineFinderResult, IMilCameraDisplay milCameraDisplay) {
MilMeasureCommon.DrawSubPositions(contextId, milCameraDisplay);
DrawLine(contextId, milCameraDisplay);
DrawText(lineFinderResult, milCameraDisplay);
MilMeasureCommon.DrawPosition(contextId, milCameraDisplay);
}
private static void DrawLine(MIL_ID contextId, IMilCameraDisplay milCameraDisplay) {
MIL_INT numOfResult = 0;
MIL.MmeasGetResult(contextId, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, ref numOfResult);
if (0 < numOfResult) {
var imageSize = MilEnvironment.GetImageSize(milCameraDisplay.GetImage());
var lineEquation = GetLineEquation(contextId);
var line = MilMeasureCommon.GetMinLengthLine(lineEquation, imageSize);
milCameraDisplay.AddLine(GraphicColors.PatternFoundOK, line, false);
}
}
private static void DrawText(PatternResult patternResult, IMilCameraDisplay milCameraDisplay) {
if (patternResult.Status == eFoundStatus.FoundOK || patternResult.Status == eFoundStatus.ForceOK) {
foreach (BaseResult baseResult in patternResult.Results) {
var lineFinderResult = baseResult as LineFinderResult;
DrawText(patternResult, lineFinderResult, milCameraDisplay);
}
}
}
private static void DrawText(LineFinderResult lineFinderResult, IMilCameraDisplay milCameraDisplay) {
if (lineFinderResult != null) {
var msgs = new List<string>();
msgs.Add(string.Format("X : {0:0.00}", lineFinderResult.ImagePosition.X));
msgs.Add(string.Format("Y : {0:0.00}", lineFinderResult.ImagePosition.Y));
msgs.Add(string.Format("Angle : {0:0.00}", lineFinderResult.Angle.Degree));
milCameraDisplay.AddTextList(
GraphicColors.PatternFoundOK, new ImagePoint(lineFinderResult.ImagePosition.X, lineFinderResult.ImagePosition.Y), msgs);
}
}
private static void DrawText(PatternResult patternResult, LineFinderResult lineFinderResult, IMilCameraDisplay milCameraDisplay) {
if (lineFinderResult != null) {
var msgs = new List<string>();
msgs.Add(string.Format("X : {0:0.00}", lineFinderResult.ImagePosition.X));
msgs.Add(string.Format("Y : {0:0.00}", lineFinderResult.ImagePosition.Y));
msgs.Add(string.Format("Angle : {0:0.00}", lineFinderResult.Angle.Degree));
msgs.Add(string.Format("Contrast : {0:0.000}", lineFinderResult.Contrast));
msgs.Add(string.Format("Strength : {0:0.000}", lineFinderResult.Strength));
msgs.Add(string.Format("TactTime : {0:F3} ms", patternResult.TactTimeInMilliSec));
msgs.Add(string.Format("ID : {0}_{1}", patternResult.PatternIndex?.PatternId, patternResult.PatternIndex?.PatternSubId));
milCameraDisplay.AddTextList(
GraphicColors.PatternFoundOK, new ImagePoint(lineFinderResult.ImagePosition.X, lineFinderResult.ImagePosition.Y), msgs);
}
}
}
}