374 lines
18 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|