/* * 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 System.Text; using Aurora.Api.DataType; namespace MilPattern { public enum eMilCodeType { DataMatrix = MIL.M_DATAMATRIX, // 코드 타입 : 2D DataMatrix QRCode = MIL.M_QRCODE, // 코드 타입 : 2D QR Barcode_Any = MIL.M_ANY, // 코드 타입 : 1D Any (자동 탐지 후 검출. 직접 지정 방식보다 속도는 느림.) Barcode_BC412 = MIL.M_BC412, // 코드 타입 : 1D BC412 Barcode_CODABAR = MIL.M_CODABAR, // 코드 타입 : 1D CODABAR Barcode_CODE39 = MIL.M_CODE39, // 코드 타입 : 1D CODE39 Barcode_CODE93 = MIL.M_CODE93, // 코드 타입 : 1D CODE93 Barcode_CODE128 = MIL.M_CODE128, // 코드 타입 : 1D CODE128 Barcode_EAN8 = MIL.M_EAN8, // 코드 타입 : 1D EAN8 Barcode_EAN13 = MIL.M_EAN13, // 코드 타입 : 1D EAN13 Barcode_EAN14 = MIL.M_EAN14, // 코드 타입 : 1D EAN14 Barcode_GS1_128 = MIL.M_GS1_128, // 코드 타입 : 1D GS1_128 Barcode_INDUSTRIAL25 = MIL.M_INDUSTRIAL25, // 코드 타입 : 1D INDUSTRIAL25 Barcode_INTERLEAVED25 = MIL.M_INTERLEAVED25, // 코드 타입 : 1D INTERLEAVED25 Barcode_UPC_A = MIL.M_UPC_A, // 코드 타입 : 1D UPC_A Barcode_UPC_E = MIL.M_UPC_E // 코드 타입 : 1D UPC_E } public enum eMilThresholdMode { Adaptive = MIL.M_ADAPTIVE, GlobalSeg = MIL.M_GLOBAL_SEGMENTATION, GlobalWithLocalReSeg = MIL.M_GLOBAL_WITH_LOCAL_RESEGMENTATION, } public class CodeRunParam : IRunParam, ICloneable { public eMilSpeed Speed { get; set; } = eMilSpeed.Default; public eMilThresholdMode ThresholdMode { get; set; } = eMilThresholdMode.Adaptive; public bool AutoThreshold { get; set; } = true; public int Threshold { get; set; } = 10; public CodeParamExt CodeParamExt { get; set; } = new CodeParamExt(); public bool IsValidFindRoi(ImageSize imageSize) { return (0 < CodeParamExt.FindROI.Width && CodeParamExt.FindROI.Width < imageSize.Width) && (0 < CodeParamExt.FindROI.Height && CodeParamExt.FindROI.Height < imageSize.Height); } public ROI GetFindRoi() { return CodeParamExt.FindROI.Clone() as ROI; } public void SetFindROI(ROI findRoi) { CodeParamExt.FindROI = findRoi; } public object Clone() { return new CodeRunParam() { Speed = Speed, ThresholdMode = ThresholdMode, AutoThreshold = AutoThreshold, Threshold = Threshold, CodeParamExt = CodeParamExt.Clone() as CodeParamExt }; } } /// /// // Barcode, QRCode, DataMatrix 검출 /// internal static class MilCodeRead { private static Logger _logger = new Logger(typeof(MilCodeRead).Name); public static MIL_ID CreateContext(MIL_ID milSystem) { MIL_ID contextId = MIL.M_NULL; MIL.McodeAlloc(milSystem, MIL.M_DEFAULT, MIL.M_DEFAULT, ref contextId); return contextId; } public static MIL_ID LoadContext(MIL_ID milSystem, string filePath) { MIL_ID contextId = MIL.M_NULL; MIL.McodeRestore(filePath, milSystem, MIL.M_DEFAULT, ref contextId); return contextId; } public static int SaveContext(MIL_ID contextId, string filePath) { if (contextId != MIL.M_NULL && filePath != null) { MIL.McodeSave(filePath, contextId, MIL.M_DEFAULT); return E1.OK; } return E1.GENERIC; } public static void FreeContext(MIL_ID contextId) { if (contextId != MIL.M_NULL) { MIL.McodeFree(contextId); } } public static MIL_ID CreateResultContext(MIL_ID contextId) { var milSystem = GetSystemOfContext(contextId); return MIL.McodeAllocResult(milSystem, MIL.M_DEFAULT, MIL.M_NULL); } private static MIL_ID GetSystemOfContext(MIL_ID contextId) { return (MIL_ID)MIL.McodeInquire(contextId, MIL.M_OWNER_SYSTEM, MIL.M_NULL); } public static CodeRunParam GetRunParam(MIL_ID contextId) { var codeRunParam = new CodeRunParam(); try { double codeType = MIL.McodeInquire(contextId, MIL.M_CODE_TYPE, MIL.M_NULL); if (codeType != MIL.M_NULL && codeType != MIL.M_DEFAULT) { codeRunParam.CodeParamExt.CodeType = (eMilCodeType)codeType; } double thresholdMode = MIL.McodeInquire(contextId, MIL.M_THRESHOLD_MODE, MIL.M_NULL); if (thresholdMode != MIL.M_DEFAULT) { codeRunParam.ThresholdMode = (eMilThresholdMode)thresholdMode; } double threshold = MIL.McodeInquire(contextId, MIL.M_THRESHOLD_VALUE, MIL.M_NULL); codeRunParam.AutoThreshold = (threshold == MIL.M_AUTO_COMPUTE); codeRunParam.Threshold = (int)threshold; codeRunParam.Speed = (eMilSpeed)Convert.ToInt32(MIL.McodeInquire(contextId, MIL.M_SPEED, MIL.M_NULL)); } catch (Exception e) { _logger.FuncE(e.Message); } return codeRunParam; } public static int SetRunParam(MIL_ID contextId, IRunParam runParam) { if (runParam is CodeRunParam) { var param = runParam as CodeRunParam; MIL.McodeControl(contextId, MIL.M_INITIALIZATION_MODE, MIL.M_IMPROVED_RECOGNITION); MIL.McodeControl(contextId, MIL.M_THRESHOLD_MODE, (double)param.ThresholdMode); MIL.McodeControl(contextId, MIL.M_SPEED, (double)param.Speed); if (param.AutoThreshold) { MIL.McodeControl(contextId, MIL.M_THRESHOLD_VALUE, MIL.M_AUTO_COMPUTE); } else { MIL.McodeControl(contextId, MIL.M_THRESHOLD_VALUE, param.Threshold); } return E1.OK; } return E1.GENERIC; } public static bool HasModel(MIL_ID contextId) { return MIL.McodeInquire(contextId, MIL.M_NUMBER_OF_CODE_MODELS, MIL.M_NULL) > 0; } public static PatternResult Find(MIL_ID contextId, MIL_ID resultContextId, MIL_ID image, CodeParamExt codeParamExt) { MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_RESET + MIL.M_SYNCHRONOUS, MIL.M_NULL); DetectCodeModels(contextId, image, codeParamExt); MIL.McodeRead(contextId, image, resultContextId); double tactTime = 0; MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, ref tactTime); var finderResult = ExtractResults(resultContextId, MilEnvironment.GetImageCenter(image), codeParamExt); finderResult.TactTimeInMilliSec = MathUtil.ConvertUnitSecToMilli(tactTime); return finderResult; } private static void DetectCodeModels(MIL_ID contextId, MIL_ID image, CodeParamExt codeParamExt) { var milSystem = GetSystemOfContext(contextId); MIL_ID milCodeResult = MIL.McodeAllocResult(milSystem, MIL.M_CODE_DETECT_RESULT, MIL.M_NULL); if (codeParamExt.CodeType != eMilCodeType.Barcode_Any) { const int STRING_LENGTH_MAX = 64; MIL_INT codeType = (int)codeParamExt.CodeType; // 지정된 코드 타입 감지 MIL.McodeDetect(image, STRING_LENGTH_MAX, codeType, codeParamExt.FindNumber, MIL.M_DEFAULT, MIL.M_DEFAULT, milCodeResult); MIL.McodeModel(contextId, MIL.M_ADD, codeType, MIL.M_NULL, milCodeResult, MIL.M_NULL); } else { // 자동으로 코드 타입 감지 MIL.McodeDetect(image, 0, MIL.M_NULL, codeParamExt.FindNumber, MIL.M_DEFAULT, MIL.M_DEFAULT, milCodeResult); MIL.McodeModel(contextId, MIL.M_RESET_FROM_DETECTED_RESULTS, MIL.M_NULL, MIL.M_ALL, milCodeResult, MIL.M_NULL); #region 디버깅용 코드 //MIL_INT codeDetectedNum = 0; //MIL.McodeGetResult(milCodeResult, MIL.M_GENERAL, MIL.M_GENERAL, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, ref codeDetectedNum); //for (MIL_INT i = 0; i < codeDetectedNum; i++) { // MIL_INT[] codeTypes = new MIL_INT[codeDetectedNum]; // MIL.McodeGetResult(milCodeResult, MIL.M_ALL, MIL.M_GENERAL, MIL.M_CODE_TYPE + MIL.M_TYPE_MIL_INT, ref codeTypes[i]); // MIL_INT codeTypeStringSize = 0; // MIL.McodeGetResult(milCodeResult, i, MIL.M_GENERAL, MIL.M_CODE_TYPE_NAME + MIL.M_STRING_SIZE + MIL.M_TYPE_MIL_INT, ref codeTypeStringSize); // StringBuilder codeTypeString = new StringBuilder((int)codeTypeStringSize); // MIL.McodeGetResult(milCodeResult, i, MIL.M_GENERAL, MIL.M_CODE_TYPE_NAME + MIL.M_TYPE_STRING, codeTypeString); //} #endregion } MIL.McodeControl(contextId, MIL.M_NUMBER, MIL.M_ALL); MIL.McodeFree(milCodeResult); } private static PatternResult ExtractResults(MIL_ID contextResult, ImagePoint imageCenter, CodeParamExt codeParamExt) { List results = new List(); MIL_INT resultStatus = 0; MIL.McodeGetResult(contextResult, MIL.M_GENERAL, MIL.M_GENERAL, MIL.M_STATUS + MIL.M_TYPE_MIL_INT, ref resultStatus); if (resultStatus == MIL.M_STATUS_READ_OK) { MIL_INT codeReadNum = 0; MIL.McodeGetResult(contextResult, MIL.M_GENERAL, MIL.M_GENERAL, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, ref codeReadNum); for (MIL_INT i = 0; i < codeReadNum; i++) { MIL_INT codeStringSize = 0; MIL.McodeGetResult(contextResult, i, MIL.M_GENERAL, MIL.M_STRING + MIL.M_STRING_SIZE + MIL.M_TYPE_MIL_INT, ref codeStringSize); StringBuilder codeString = new StringBuilder((int)codeStringSize); MIL.McodeGetResult(contextResult, i, MIL.M_GENERAL, MIL.M_STRING, codeString); double posX = 0, posY = 0; double score = 0; double angleDeg = 0; MIL.McodeGetResult(contextResult, i, MIL.M_GENERAL, MIL.M_POSITION_X, ref posX); MIL.McodeGetResult(contextResult, i, MIL.M_GENERAL, MIL.M_POSITION_Y, ref posY); MIL.McodeGetResult(contextResult, i, MIL.M_GENERAL, MIL.M_SCORE, ref score); MIL.McodeGetResult(contextResult, i, MIL.M_GENERAL, MIL.M_ANGLE, ref angleDeg); // Warning : Counter Clockwise results.Add(new CodeResult( codeType: codeParamExt.CodeType.ToString(), position: new ImagePoint(posX, posY), offset: (new ImagePoint(posX, posY) - imageCenter) as ImagePoint, angle: Angle.NormalizeAngle(Angle.CreateWithDegree(angleDeg)), score: score * 100, code: codeString.ToString())); } } return new PatternResult() { PatternType = ePatternType.Code, Status = (results.Count > 0) ? eFoundStatus.FoundOK : eFoundStatus.NotFound, Results = results, FindRoi = codeParamExt.FindROI.Clone() as ROI, }; } public static PatternResult GetNotFoundPatternResult() { return new PatternResult() { Status = eFoundStatus.NotFound, Results = new List() }; } public static void Draw(PatternResult patternResult, IMilCameraDisplay milCameraDisplay) { MIL_ID staticGraphicList = 0; MIL.MdispInquire(milCameraDisplay.GetMilDisplayId(), MIL.M_ASSOCIATED_GRAPHIC_LIST_ID, ref staticGraphicList); if (patternResult.Status == eFoundStatus.FoundOK) { var results = patternResult.Results as List; var crosses = new List(); foreach (var result in results) { var color = GraphicColors.PatternFoundOK; var cross = new Cross(result.ImagePosition.Clone() as ImagePoint, 50); crosses.Add(cross); milCameraDisplay.SetPatternTracking(cross); milCameraDisplay.AddCross(cross, color); var msgs = new List(); msgs.Add(string.Format("CodeType : {0}", result.CodeType)); msgs.Add(string.Format("X : {0:0.00}", result.ImagePosition.X)); msgs.Add(string.Format("Y : {0:0.00}", result.ImagePosition.Y)); 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("Code : {0}", result.Code)); milCameraDisplay.AddTextList( GraphicColors.PatternFoundOK, new ImagePoint(result.ImagePosition.X, result.ImagePosition.Y), msgs); } milCameraDisplay.DrawPatternTrackings(crosses); } } } }