/* * Copyright (c) 2025 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ using Aurora.Api; using CogCommon; using VisionCommon; using Hermes; using Cognex.VisionPro; using System; using System.Collections.Generic; using System.Windows; using Cognex.VisionPro.PMAlign; using System.Diagnostics; using System.Runtime.Serialization.Formatters.Binary; namespace CogPattern { public class ModelFinderRunParam : IRunParam, ICloneable { public int FindNumber { get; set; } = 1; public double Acceptance { get; set; } = 60; public double Certainty { get; set; } = 90; public Angle AngleTolerance { get; set; } = Angle.CreateWithDegree(2); public eIgnorePolarity IgnorePolarity { get; set; } = eIgnorePolarity.ENABLED; public double Score { get; set; } = 60; // 미사용 public ROI FindROI { get; set; } = new ROI(); public object Clone() { return new ModelFinderRunParam() { FindNumber = FindNumber, Acceptance = Acceptance, Certainty = Certainty, AngleTolerance = AngleTolerance.Clone() as Angle, IgnorePolarity = IgnorePolarity, Score = Score, FindROI = FindROI.Clone() as ROI, }; } } internal static class MmfCommon { public const double DEFAULT_ANGLE_TOLERANCE_DEGREE = 2.0; public const double DEFAULT_SCALE_TOLERANCE = 0.1; public const double DEFAULT_ACCEPT_THRESHOLD = 0.7; public const double DEFAULT_TIME_OUT = 3000; // 3 sec public const bool DEFAULT_SCORE_USING_CLUTTER = false; private static Logger _Logger = new Logger(typeof(MmfCommon).Name); public static CogPMAlignTool CreateTool() { var cogPMAlignTool = new CogPMAlignTool(); SetAngleTolerance(cogPMAlignTool.RunParams); SetScaleTolerance(cogPMAlignTool.RunParams); cogPMAlignTool.RunParams.AcceptThreshold = DEFAULT_ACCEPT_THRESHOLD; cogPMAlignTool.RunParams.ScoreUsingClutter = DEFAULT_SCORE_USING_CLUTTER; cogPMAlignTool.RunParams.TimeoutEnabled = true; cogPMAlignTool.RunParams.Timeout = DEFAULT_TIME_OUT; return cogPMAlignTool; } public static CogPMAlignTool LoadTool(string filePath) { return (CogPMAlignTool)CogSerializer.LoadObjectFromFile( filePath, typeof(BinaryFormatter), CogSerializationOptionsConstants.Minimum); } public static int SaveTool(CogPMAlignTool cogPMAlignTool, string filePath) { if (cogPMAlignTool != null && filePath != null) { CogSerializer.SaveObjectToFile( cogPMAlignTool, filePath, typeof(BinaryFormatter), CogSerializationOptionsConstants.Minimum); return E1.OK; } return E1.GENERIC; } public static void FreeTool(CogPMAlignTool cogPMAlignTool) { if (cogPMAlignTool != null) { cogPMAlignTool = null; } } public static bool IsTrained(CogPMAlignTool cogPMAlignTool) { if (cogPMAlignTool == null || cogPMAlignTool.Pattern == null) { return false; } return cogPMAlignTool.Pattern.Trained; } public static ROI GetFindROI(CogPMAlignTool cogPMAlignTool) { if (cogPMAlignTool.SearchRegion == null) { if (IsTrained(cogPMAlignTool)) { var imageSize = CogEnvironment.GetImageSize(cogPMAlignTool.Pattern.TrainImage); return RoiUtil.GetDefaultFindRoi(imageSize); } return new ROI(); } return CogEnvironment.ConvertToRoi(cogPMAlignTool.SearchRegion as CogRectangleAffine); } public static int SetFindROI(CogPMAlignTool cogPMAlignTool, ROI findRoi) { if (findRoi.Width > 0 && findRoi.Height > 0) { if (cogPMAlignTool != null) { cogPMAlignTool.SearchRegion = CogEnvironment.ConvertToCogRectangleAffine(findRoi); } } else { //ClearFindRoi(cogPMAlignTool); } return E1.OK; } //public static int ClearFindRoi(CogPMAlignTool cogPMAlignTool) { // if (cogPMAlignTool != null) { // cogPMAlignTool.SearchRegion = new CogRectangleAffine(); // } // return E1.OK; //} public static ModelFinderRunParam GetRunParam(CogPMAlignTool cogPMAlignTool) { var modelFinderRunParam = new ModelFinderRunParam(); modelFinderRunParam.FindNumber = cogPMAlignTool.RunParams.ApproximateNumberToFind; modelFinderRunParam.Acceptance = CogEnvironment.ConvertToVisionScore(cogPMAlignTool.RunParams.AcceptThreshold); modelFinderRunParam.Certainty = CogEnvironment.ConvertToCertainty(GetScaleTolerance(cogPMAlignTool.RunParams)); modelFinderRunParam.AngleTolerance = GetAngleTolerance(cogPMAlignTool.RunParams); if (IsTrained(cogPMAlignTool)) { modelFinderRunParam.IgnorePolarity = cogPMAlignTool.Pattern.IgnorePolarity? eIgnorePolarity.ENABLED : eIgnorePolarity.DISABLED; modelFinderRunParam.FindROI = GetFindROI(cogPMAlignTool); } return modelFinderRunParam; } private static void SetAngleTolerance(CogPMAlignRunParams runParams, ModelFinderRunParam runParam = null) { runParams.ZoneAngle.Configuration = CogPMAlignZoneConstants.LowHigh; if (runParam != null) { runParams.ZoneAngle.High = runParam.AngleTolerance.Radian; runParams.ZoneAngle.Low = runParam.AngleTolerance.Radian * (-1); } else { runParams.ZoneAngle.High = CogMisc.DegToRad(DEFAULT_ANGLE_TOLERANCE_DEGREE); runParams.ZoneAngle.Low = CogMisc.DegToRad(DEFAULT_ANGLE_TOLERANCE_DEGREE) * (-1); } } private static Angle GetAngleTolerance(CogPMAlignRunParams runParams) { double angleToleranceMaxRad = Math.Max(Math.Abs(runParams.ZoneAngle.High), Math.Abs(runParams.ZoneAngle.Low)); double angleToleranceMaxDeg = CogMisc.RadToDeg(angleToleranceMaxRad); return new Angle() { Degree = angleToleranceMaxDeg, Radian = angleToleranceMaxRad }; } private static void SetScaleTolerance(CogPMAlignRunParams runParams, ModelFinderRunParam runParam = null) { runParams.ZoneScale.Configuration = CogPMAlignZoneConstants.LowHigh; if (runParam != null) { runParams.ZoneScale.High = 1.0 + (1.0 - CogEnvironment.ConvertToScale(runParam.Certainty)); runParams.ZoneScale.Low = 1.0 - (1.0 - CogEnvironment.ConvertToScale(runParam.Certainty)); } else { runParams.ZoneScale.High = 1.0 + DEFAULT_SCALE_TOLERANCE; runParams.ZoneScale.Low = 1.0 - DEFAULT_SCALE_TOLERANCE; } } private static double GetScaleTolerance(CogPMAlignRunParams runParams) { double scaleMax = Math.Max(Math.Abs(runParams.ZoneScale.High), Math.Abs(runParams.ZoneScale.Low)); double scaleRatio = Math.Round(1.0 - Math.Abs(1.0 - scaleMax), 3); // scaleRatio : 0 ~ 1 return scaleRatio; } public static int SetRunParam(CogPMAlignTool cogPMAlignTool, IRunParam runParam) { if (runParam is ModelFinderRunParam) { var param = runParam as ModelFinderRunParam; if (cogPMAlignTool != null) { SetAngleTolerance(cogPMAlignTool.RunParams, param); SetScaleTolerance(cogPMAlignTool.RunParams, param); SetFindROI(cogPMAlignTool, param.FindROI); cogPMAlignTool.RunParams.ApproximateNumberToFind = param.FindNumber; cogPMAlignTool.RunParams.AcceptThreshold = CogEnvironment.ConvertToCogScore(param.Acceptance); cogPMAlignTool.Pattern.IgnorePolarity = GetIgnorePolarity(param.IgnorePolarity); //ClearFindRoi(cogPMAlignTool); } return E1.OK; } return E1.GENERIC; } private static bool GetIgnorePolarity(eIgnorePolarity ignorePolarity) { if (ignorePolarity == eIgnorePolarity.ENABLED) { return true; } else { return false; } } public static PatternResult Find(CogPMAlignTool cogPMAlignTool, ICogImage image, ROI findRoi) { if (CogEnvironment.IsColorImage(image)) { var grayImage = CogEnvironment.ConvertToGray(image); var patternResult = FindInternal(cogPMAlignTool, grayImage, findRoi); CogEnvironment.FreeImage(grayImage); return patternResult; } return FindInternal(cogPMAlignTool, image, findRoi); } private static PatternResult FindInternal(CogPMAlignTool cogPMAlignTool, ICogImage image, ROI findRoi) { var timeFind = new Stopwatch(); timeFind.Start(); var finderResult = new PatternResult(); var oldFindRoi = GetFindROI(cogPMAlignTool); try { if (IsTrained(cogPMAlignTool)) { cogPMAlignTool.InputImage = image; SetFindROI(cogPMAlignTool, findRoi); cogPMAlignTool.Run(); finderResult = ExtractResults(cogPMAlignTool, CogEnvironment.GetImageCenter(image)); } else { finderResult = GetNotFoundPatternResult(); } } catch (Exception e) { _Logger.FuncE(e.Message); } finally { SetFindROI(cogPMAlignTool, oldFindRoi); } timeFind.Stop(); finderResult.TactTimeInMilliSec = timeFind.ElapsedMilliseconds; return finderResult; } public static PatternResult GetNotFoundPatternResult() { return new PatternResult() { Status = eFoundStatus.NotFound, Results = new List() }; } private static PatternResult ExtractResults(CogPMAlignTool cogPMAlignTool, ImagePoint imageCenter) { var results = new List(); eFoundStatus status = eFoundStatus.NotFound; foreach (CogPMAlignResult result in cogPMAlignTool.Results) { double posX = result.GetPose().TranslationX; double posY = result.GetPose().TranslationY; double score = Math.Truncate(CogEnvironment.ConvertToVisionScore(result.Score)); double angleDeg = CogMisc.RadToDeg(result.GetPose().Rotation); results.Add(new ModelFinderResult( position: new ImagePoint(posX, posY), offset: (new ImagePoint(posX, posY) - imageCenter) as ImagePoint, angle: Angle.NormalizeAngle(Angle.CreateWithDegree(angleDeg)), score: score)); if (results.Count == cogPMAlignTool.RunParams.ApproximateNumberToFind) { status = eFoundStatus.FoundOK; break; } } return new PatternResult() { Status = status, Results = results }; } public static void Draw( PatternResult patternResult, ICogCameraDisplay cogCameraDisplay, bool isFixturePattern) { if (patternResult == null || patternResult.Results == null) { return; } var crosses = new List(); foreach (BaseResult baseResult in patternResult.Results) { var result = baseResult as ModelFinderResult; if (result.Score > 0) { var color = GraphicColors.GetFindResultColor(patternResult.Status, isFixturePattern); var cross = new Cross(result.ImagePosition.Clone() as ImagePoint, result.Angle, 50); crosses.Add(cross); cogCameraDisplay.SetPatternTracking(cross); cogCameraDisplay.AddCross(cross, color); var msgs = new List(); msgs.Add(string.Format("I({0:0.0}, {1:0.0})", result.ImagePosition.X, result.ImagePosition.Y)); msgs.Add(result.ConvertMaterialPosition()); msgs.Add(string.Format("Angle : {0:0.00}", result.Angle.Degree)); msgs.Add(string.Format("Score : {0:0.00}", result.Score)); msgs.Add(string.Format("TactTime : {0} ms", Math.Round(patternResult.TactTimeInMilliSec))); msgs.Add(string.Format("ID : {0}_{1}", patternResult.PatternIndex.PatternId, patternResult.PatternIndex.PatternSubId)); cogCameraDisplay.AddTextList( color, new ImagePoint(result.ImagePosition.X, result.ImagePosition.Y), msgs); } } cogCameraDisplay.DrawPatternTrackings(crosses); } } }