/* * Copyright (c) 2025 LG Electronics Inc. * SPDX-License-Identifier: LicenseRef-LGE-Proprietary */ using Aurora.Api; using VisionCommon; using System.Collections.Generic; namespace VisionAligner { /// /// 얼라인 모드(알고리즘) /// public abstract class AlignMode { /// 로거 protected Logger _Logger; /// 얼라인 보정량 제한 체커 protected AlignLimitChecker _AlignLimitChecker; /// 얼라인 재시도 스펙 체커 protected AlignRetryChecker _AlignRetryChecker; /// 얼라인 재시도 횟수 체커 protected AlignCountChecker _AlignCountChecker; /// 얼라인 레시피 protected AlignRecipe _AlignRecipe; /// 서브 시스템 정보 protected SubSysInfo _SubSysInfo; /// /// 생성자(UnitTest 전용) /// protected AlignMode() { // For UnitTest } /// /// 생성자 /// /// 얼라인 정보 public AlignMode(AlignInfo alignInfo) { _Logger = new Logger($"AlignMode[{alignInfo.AlignName}]"); _AlignRecipe = VisionRecipeManager.GetAlignRecipe(alignInfo); _SubSysInfo = alignInfo?.ToSubSysInfo(); SetAlignCheckers(); } private void SetAlignCheckers() { _AlignLimitChecker = new AlignLimitChecker(_AlignRecipe); _AlignRetryChecker = new AlignRetryChecker(_AlignRecipe); _AlignCountChecker = new AlignCountChecker(_AlignRecipe); } #region N-Point Align /// /// N-포인트 얼라인 보정량 XYT를 계산한다. /// /// 패턴 검출 위치 /// 레퍼런스(기준) 위치 /// 좌표계의 Y축 반전 여부 /// 얼라인 결과 public AlignResult Align( List meaPoints, List refPoints, int flipY) { StaticPoint thetaOrigin = GetThetaOrigin(); eAlignAxis alignAxis = _AlignRecipe.ProcParams.AlignAxis; AlignTgtDist alignTgtDist = _AlignRecipe.AlignTgtDist; eAlignAlgorithm alignAlgorithm = _AlignRecipe.ProcParams.AlignAlgorithm; AlignDelta alignDelta = CalculateAlign( meaPoints, refPoints, flipY, thetaOrigin, alignAxis, alignTgtDist); _Logger.I($"Align delta : {alignDelta}"); AlignStatus status = GetAlignStatus(alignDelta); int alignCount = _AlignCountChecker.GetCurrentAlignCount(status); var alignResult = new AlignResult(alignCount, status, alignDelta, alignAlgorithm); return alignResult; } private AlignDelta CalculateAlign( List meaPoints, List refPoints, int flipY, DoublePoint thetaOrigin, eAlignAxis alignAxis, AlignTgtDist alignTgtDist) { if(_AlignRecipe.ProcParams.AlignAlgorithm == eAlignAlgorithm.AVG) { return CalculateXYT(meaPoints, refPoints, flipY, thetaOrigin, alignAxis, alignTgtDist); } else if(_AlignRecipe.ProcParams.AlignAlgorithm == eAlignAlgorithm.ICP) { return CalculateXYT_ICP(meaPoints, refPoints, flipY, thetaOrigin, alignAxis, alignTgtDist); } return new AlignDelta(); } private AlignDelta CalculateXYT( List meaPoints, List refPoints, int flipY, DoublePoint thetaOrigin, eAlignAxis alignAxis, AlignTgtDist alignTgtDist) { AlignDelta alignDeltaT = CalculateT(meaPoints, refPoints, flipY); alignDeltaT.T = ApplyAlignAxisAndTargetDistT(alignDeltaT.T, alignAxis, alignTgtDist); AlignDelta alignDeltaXY = CalculateXY(meaPoints, refPoints, flipY, thetaOrigin, alignDeltaT.T); alignDeltaXY.X = ApplyAlignAxisAndTargetDistX(alignDeltaXY.X, alignAxis, alignTgtDist); alignDeltaXY.Y = ApplyAlignAxisAndTargetDistY(alignDeltaXY.Y, alignAxis, alignTgtDist); return new AlignDelta( alignDeltaXY.Translation, alignDeltaT.T); } private AlignDelta CalculateXYT_ICP( List meaPoints, List refPoints, int flipY, DoublePoint thetaOrigin, eAlignAxis alignAxis, AlignTgtDist alignTgtDist) { AlignDelta alignDeltaT = CalculateT_ICP(meaPoints, refPoints, flipY); alignDeltaT.T = ApplyAlignAxisAndTargetDistT(alignDeltaT.T, alignAxis, alignTgtDist); AlignDelta alignDeltaXY = CalculateXY(meaPoints, refPoints, flipY, thetaOrigin, alignDeltaT.T); alignDeltaXY.X = ApplyAlignAxisAndTargetDistX(alignDeltaXY.X, alignAxis, alignTgtDist); alignDeltaXY.Y = ApplyAlignAxisAndTargetDistY(alignDeltaXY.Y, alignAxis, alignTgtDist); return new AlignDelta( alignDeltaXY.Translation, alignDeltaT.T); } /// /// N-포인트 얼라인 보정량 T를 계산한다. /// /// 패턴 검출 위치 /// 레퍼런스(기준) 위치 /// 좌표계의 Y축 반전 여부 /// 얼라인 보정량 protected abstract AlignDelta CalculateT( List meaPoints, List refPoints, int flipY); /// /// ICP 알고리즘을 이용하여 N-포인트 얼라인 보정량 T를 계산한다. /// /// 패턴 검출 위치 /// 레퍼런스(기준) 위치 /// 좌표계의 Y축 반전 여부 /// 얼라인 보정량 protected abstract AlignDelta CalculateT_ICP( List meaPoints, List refPoints, int flipY); /// /// N-포인트 얼라인 보정량 XY를 계산한다. /// /// 패턴 검출 위치 /// 레퍼런스(기준) 위치 /// 좌표계의 Y축 반전 여부 /// 회전 원점 /// 얼라인 보정량 T /// 얼라인 보정량 protected abstract AlignDelta CalculateXY( List meaPoints, List refPoints, int flipY, DoublePoint thetaOrigin, Angle alignT); #endregion #region 1-Point Align /// /// 1-포인트 얼라인 보정량 XYT를 계산한다. /// /// 패턴 검출 위치 /// 레퍼런스(기준) 위치 /// 얼라인 보정량 T /// 좌표계의 Y축 반전 여부 /// 얼라인 결과 public AlignResult Align( DoublePoint meaPoint, DoublePoint refPoint, Angle theta, int flipY) { StaticPoint thetaOrigin = GetThetaOrigin(); eAlignAxis alignAxis = _AlignRecipe.ProcParams.AlignAxis; AlignTgtDist alignTgtDist = _AlignRecipe.AlignTgtDist; eAlignAlgorithm alignAlgorithm = _AlignRecipe.ProcParams.AlignAlgorithm; AlignDelta alignDelta = CalculateXYT( meaPoint, refPoint, flipY, thetaOrigin, alignAxis, alignTgtDist, theta); _Logger.I($"Align delta : {alignDelta}"); AlignStatus status = GetAlignStatus(alignDelta); int alignCount = _AlignCountChecker.GetCurrentAlignCount(status); return new AlignResult(alignCount, status, alignDelta, alignAlgorithm); } private AlignDelta CalculateXYT( DoublePoint meaPoint, DoublePoint refPoint, int flipY, DoublePoint thetaOrigin, eAlignAxis alignAxis, AlignTgtDist alignTgtDist, Angle theta) { var alignT = ApplyAlignAxisAndTargetDistT(theta, alignAxis, alignTgtDist); var alignDelta = CalculateXYT( meaPoint, refPoint, flipY, thetaOrigin, alignT); alignDelta.X = ApplyAlignAxisAndTargetDistX(alignDelta.X, alignAxis, alignTgtDist); alignDelta.Y = ApplyAlignAxisAndTargetDistY(alignDelta.Y, alignAxis, alignTgtDist); return alignDelta; } /// /// 1-포인트 얼라인 보정량 XYT를 계산한다. /// /// 패턴 검출 위치 /// 레퍼런스(기준) 위치 /// 좌표계의 Y축 반전 여부 /// 회전 원점 /// 얼라인 보정량 T /// 얼라인 보정량 protected abstract AlignDelta CalculateXYT( DoublePoint meaPoint, DoublePoint refPoint, int flipY, DoublePoint thetaOrigin, Angle theta); #endregion /// /// 얼라인 상태를 체크한다. /// /// 얼라인 보정량 /// 얼라인 상태 코드 protected AlignStatus GetAlignStatus(AlignDelta alignDelta) { AlignStatus status = AlignStatus.OK; if (_AlignLimitChecker.IsEnabled()) { status = _AlignLimitChecker.GetAlignLimitStatus(alignDelta); if (status == AlignStatus.ALIGN_LIMIT_OVER) { ResetRetrySpecChecker(); return status; } } if (_AlignRetryChecker.IsEnabled()) { status = _AlignRetryChecker.GetRetryStatus(alignDelta); if (status == AlignStatus.RETRY_COUNT_OVER) { ResetRetrySpecChecker(); } return status; } return status; } /// /// 얼라인 재시도 스펙 체커를 초기화 한다. /// public void ResetRetrySpecChecker() { _AlignRetryChecker.Reset(); } /// /// 얼라인 재시도 횟수를 초기화 한다. /// public void ResetAlignCountChecker() { _AlignCountChecker.Reset(); } /// /// 얼라인 재시도 스펙 체커와 재시도 횟수를 초기화 한다. /// public void ResetAll() { ResetRetrySpecChecker(); ResetAlignCountChecker(); } /// /// 회전 원점을 얻어온다. /// /// 회전 원점 좌표 protected StaticPoint GetThetaOrigin() { var calRecipe = VisionRecipeManager.GetCalRecipe(_SubSysInfo); return (calRecipe.ThetaOrigin + _AlignRecipe.AlignThetaOrigin.Offset) as StaticPoint; } /// /// 얼라인 보정량 T에 얼라인 타겟 회전량를 적용한다. /// /// 얼라인 보정량 T /// 얼라인 사용 축 /// 얼라인 타겟 회전량 /// 최종 얼라인 보정량 T protected static Angle ApplyAlignAxisAndTargetDistT( Angle alignT, eAlignAxis alignAxis, AlignTgtDist alignTgtDist) { if (IsRotationEnabled(alignAxis)) { if (alignTgtDist.Enabled) { return Angle.CreateWithDegree(alignT.Degree - alignTgtDist.DistT); } return alignT; } return new Angle(); } /// /// 얼라인 보정량 X에 얼라인 타겟 거리 X를 적용한다. /// /// 얼라인 보정량 X /// 얼라인 사용 축 /// 얼라인 타겟 거리 X /// 최종 얼라인 보정량 X protected static double ApplyAlignAxisAndTargetDistX( double alignX, eAlignAxis alignAxis, AlignTgtDist alignTgtDist) { if (IsTranslationXEnabled(alignAxis)) { if (alignTgtDist.Enabled) { return alignX - alignTgtDist.DistX; } return alignX; } return 0; } /// /// 얼라인 보정량 Y에 얼라인 타겟 거리 Y를 적용한다. /// /// 얼라인 보정량 Y /// 얼라인 사용 축 /// 얼라인 타겟 거리 Y /// 최종 얼라인 보정량 Y protected static double ApplyAlignAxisAndTargetDistY( double alignY, eAlignAxis alignAxis, AlignTgtDist alignTgtDist) { if (IsTranslationYEnabled(alignAxis)) { if (alignTgtDist.Enabled) { return alignY - alignTgtDist.DistY; } return alignY; } return 0; } /// /// 얼라인 T축 사용여부를 체크한다. /// /// 얼라인 축 정보 /// 체크 결과 protected static bool IsRotationEnabled(eAlignAxis alignAxis) { return ((int)alignAxis & (int)eAlignAxis.T) != 0; } /// /// 얼라인 XY축 사용여부를 체크한다. /// /// 얼라인 축 정보 /// 체크 결과 protected static bool IsTranslationEnabled(eAlignAxis alignAxis) { return ((int)alignAxis & (int)eAlignAxis.XY) != 0; } /// /// 얼라인 X축 사용여부를 체크한다. /// /// 얼라인 축 정보 /// 체크 결과 protected static bool IsTranslationXEnabled(eAlignAxis alignAxis) { return ((int)alignAxis & (int)eAlignAxis.X) != 0; } /// /// 얼라인 Y축 사용여부를 체크한다. /// /// 얼라인 축 정보 /// 체크 결과 protected static bool IsTranslationYEnabled(eAlignAxis alignAxis) { return ((int)alignAxis & (int)eAlignAxis.Y) != 0; } } }