/*
* 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);
}
}
}
}