using Aurora; using Aurora.Api; using Aurora.Api.DataType; using VisionCommon; using Hermes; using Matrox.MatroxImagingLibrary; using System; using System.Collections.Generic; using System.IO; using System.Windows; using System.Linq; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Reactive.Linq; using VisionCommonUI; using VisionCommonUI.Resources; namespace MilCommon { /// /// MIL 환경 변수 및 메소드 관리 /// public class MilEnvironment : IDisposable { private static readonly Logger _Logger = new Logger("MilEnvironment"); private static object LockObj = new object(); private MIL_INT _LicenseModules = 0; private List DisplayList; public MIL_ID Application { get; private set; } // M_DEFAULT를 사용하는 경우, 시스템을 _DefaultSys 1개만 할당한다. private MIL_ID _DefaultSys; // M_DEFAULT가 아닐 경우, 시스템을 BoardType & Slot의 개수만큼 할당한다. private Dictionary> Systems { get; set; } = new Dictionary>(); private static MilEnvironment _instance = null; /// /// 싱글톤 객체 /// public static MilEnvironment Instance { get { lock (LockObj) { if (_instance == null) { _instance = new MilEnvironment(); } return _instance; } } } private MilEnvironment() { IpAdapter.Instance.IpEnvironment = new IpEnvironment(); DisplayList = new List(); CheckLangToken(); AllocApp(); SetMilErrPrintType(); VisionEnv.AuroraEnv.EventObservable.AsObservable(AuroraSysEvent.SYS_SETTING_CHANGED) .Where(msg => (string)msg.Param == VisionKeys.VisionSettingKey).Subscribe(x => UpdateVisionSettingActions()); } private void CheckLangToken() { string lastLanguage = (VisionEnv.AuroraEnv.SettingEntry[AuroraSettings.Equipment] as EquipmentInfo)?.Language ?? null; if (lastLanguage != null && lastLanguage.Length > 0) { System.Globalization.CultureInfo.CurrentCulture = new System.Globalization.CultureInfo(lastLanguage); StrTokens.Culture = System.Globalization.CultureInfo.CurrentCulture; } } private void AllocApp() { try { MIL_ID app = MIL.M_NULL; MIL.MappAlloc(MIL.M_NULL, MIL.M_DEFAULT, ref app); _Logger.FuncI(string.Format("MilApplication : {0}", app)); MIL.MappInquire(app, MIL.M_LICENSE_MODULES, ref _LicenseModules); CheckMilVersion(); Application = app; } catch (Exception e) { _Logger.E(e.Message); } } private void CheckMilVersion() { if (!VersionChecker.CheckMilVersionAndType()) { MessageBoxUtil.ShowMessageBoxWarning( string.Format(StrTokens.MSG_W_MilVersion, VersionChecker.GetMilVersion(), VersionChecker.GetMilType())); } } private void UpdateVisionSettingActions() { SetMilErrPrintType(); } private void SetMilErrPrintType() { var visionSetting = VisionEnv.AuroraEnv.SettingEntry[VisionKeys.VisionSettingKey] as VisionSetting; switch (visionSetting.MilErrPrint) { case eMilErrPrint.PRINT_ENABLE: MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_ENABLE); break; case eMilErrPrint.PRINT_DISABLE: MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_DISABLE); break; case eMilErrPrint.THROW_EXCEPTION: MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_THROW_EXCEPTION); break; } } /// /// MIL 시스템 가져오기 /// /// 보드 타입 /// 보드 슬롯 위치 /// MIL 시스템 public MIL_ID GetSystem(eBoardType boardType, eBoardSlotPos boardSlotPos) { AllocSys(boardType, boardSlotPos); if (Systems.ContainsKey(boardType)) { if (Systems[boardType].ContainsKey(boardSlotPos)) { return Systems[boardType][boardSlotPos]; } else { _Logger.FuncE($"MilSystems[{boardType}] do not contain boardSlotPos [{boardSlotPos}]"); } } else { _Logger.FuncE($"MilSystems do not contain boardType [{boardType}]"); } return MIL.M_NULL; } /// /// MIL 시스템 할당 여부 체크 /// /// 보드 타입 /// 보드 슬롯 위치 /// public bool IsSysAllocated(eBoardType boardType, eBoardSlotPos boardSlotPos) { if (Systems.ContainsKey(boardType)) { if (Systems[boardType].ContainsKey(boardSlotPos)) { return true; } } return false; } /// /// 이미지로 MIL 시스템 가져오기 /// /// 이미지 /// MIL 시스템 public static MIL_ID GetSystemOfBuffer(MIL_ID image) { return (MIL_ID)MIL.MbufInquire(image, MIL.M_OWNER_SYSTEM, MIL.M_NULL); } /// /// MIL 시스템 할당 /// /// 보드 타입 /// 보드 슬롯 위치 public void AllocSys(eBoardType boardType, eBoardSlotPos boardSlotPos) { try { lock (LockObj) { if (IsSysAllocated(boardType, boardSlotPos)) { return; } if (!Systems.ContainsKey(boardType)) { Systems.Add(boardType, new Dictionary()); } if (!Systems[boardType].ContainsKey(boardSlotPos)) { MIL_ID sys = MIL.M_NULL; string sysDescriptor = GetSystemDescriptor(boardType); if (sysDescriptor == MIL.M_SYSTEM_DEFAULT) { // M_SYSTEM_DEFAULT일 경우 MIL System이 1개만 있으면 된다. if (_DefaultSys == MIL.M_NULL) { MIL.MsysAlloc(MIL.M_DEFAULT, sysDescriptor, (int)boardSlotPos, MIL.M_DEFAULT, ref _DefaultSys); } sys = _DefaultSys; } else { // M_SYSTEM_DEFAULT이 아닐 경우 MIL System은 Slot 개수만큼 존재한다. MIL.MsysAlloc(MIL.M_DEFAULT, sysDescriptor, (int)boardSlotPos, MIL.M_DEFAULT, ref sys); } _Logger.FuncI(string.Format("BoardType : {0} / BoardSlotPos : {1} / MilSystem[{2}] : {3}", sysDescriptor, (int)boardSlotPos, (int)boardSlotPos, sys)); Systems[boardType].Add(boardSlotPos, sys); } } } catch (Exception e) { _Logger.E(e.Message); } } private string GetSystemDescriptor(eBoardType boardType) { if (EquipmentInfoManager.EquipmentInfo.SimulationMode) { // M_SYSTEM_DEFAULT일 경우, 시뮬에서 eBoardSlotPos이 달라도 eBoardType이 같으면 할당이 안됨. return MIL.M_SYSTEM_HOST; } switch (boardType) { case eBoardType.MIL_GIGE: return MIL.M_SYSTEM_GIGE_VISION; case eBoardType.MIL_CL_SOLIOS: return MIL.M_SYSTEM_SOLIOS; case eBoardType.MIL_CXP_RADIENT: return MIL.M_SYSTEM_RADIENTCXP; case eBoardType.MIL_CXP_RAPIXO: return MIL.M_SYSTEM_RAPIXOCXP; case eBoardType.EURESYS_GIGELINK: case eBoardType.EURESYS_COAXLINK: case eBoardType.EURESYS_GRABLINK: default: return MIL.M_SYSTEM_DEFAULT; } } /// /// MIL 라이센스 체크 /// /// 라이센스 여부 public bool HasValidLicense() { return (_LicenseModules & MIL.M_LICENSE_MOD) != 0; } /// /// 디스플레이 생성 /// /// 윈폼 핸들러(IntPtr) /// 이미지 버퍼 Mil_ID /// Display ID (MIL_ID) public MIL_ID CreateDisplay(IntPtr WinFormHandle, MIL_ID imageBuffer) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } lock (LockObj) { MIL_ID displayId = MIL.M_NULL; MIL.MdispAlloc(GetSystemOfBuffer(imageBuffer), MIL.M_DEFAULT, "MIL.M_DEFAULT", MIL.M_DEFAULT, ref displayId); MIL.MdispSelectWindow(displayId, imageBuffer, WinFormHandle); DisplayList.Add(displayId); return displayId; } } /// /// 해당 디스플레이 선택 /// /// 윈폼 핸들러(IntPtr) /// 이미지 버퍼 Mil_ID /// 선택할 Display Id (MIL_ID) public void SelectDisplay(IntPtr WinFormHandle, MIL_ID imageBuffer, MIL_ID displayId) { if (!Instance.HasValidLicense()) { return; } lock (LockObj) { MIL.MdispSelectWindow(displayId, imageBuffer, WinFormHandle); } } /// /// 디스플레이 해제/제거 /// /// 해제할 Display Id (MIL_ID) public void DestroyDisplay(MIL_ID displayId) { if (!Instance.HasValidLicense()) { return; } lock (LockObj) { if (displayId != MIL.M_NULL) { DisplayList.Remove(displayId); MIL.MdispFree(displayId); } } } [Obsolete] public void Dispose() { if (!Instance.HasValidLicense()) { return; } foreach (var itemPairs in Systems) { foreach (var systemPairs in itemPairs.Value) { MIL_ID sys = systemPairs.Value; if (sys != MIL.M_NULL) { MIL.MsysFree(sys); } } } Systems.Clear(); if (Application != MIL.M_NULL) { MIL.MappFree(Application); } } /// /// 원본 이미지 버퍼 할당 /// /// 카메라 컨피그 /// 원본 이미지 버퍼 /// 성공 유무 public int AllocOriginalImage(CamConfig camConfig, ref MIL_ID image) { switch (camConfig.ImageSize.Channel) { case 1: // 원본이미지는 항상 정사이즈 (W > H) MIL.MbufAlloc2d( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Width, camConfig.ImageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; case 3: default: // 원본이미지는 항상 정사이즈 (W > H) MIL.MbufAllocColor( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Channel, camConfig.ImageSize.Width, camConfig.ImageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } } /// /// Flip & Rotation 이미지 버퍼 할당 /// /// 카메라 컨피그 /// Flip & Rotation 이미지 버퍼 /// 성공 유무 public int AllocFilteredImage(CamConfig camConfig, ref MIL_ID image) { switch (camConfig.ImageSize.Channel) { case 1: if (IsHeightLongerThanWidth(camConfig)) { // 가로 < 세로 MIL.MbufAlloc2d( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Height, camConfig.ImageSize.Width, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } else { // 가로 > 세로 MIL.MbufAlloc2d( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Width, camConfig.ImageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } case 3: default: if (IsHeightLongerThanWidth(camConfig)) { // 가로 < 세로 MIL.MbufAllocColor( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Channel, camConfig.ImageSize.Height, camConfig.ImageSize.Width, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } else { // 가로 > 세로 MIL.MbufAllocColor( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Channel, camConfig.ImageSize.Width, camConfig.ImageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } } } /// /// Crop 이미지 버퍼 할당 /// /// 카메라 컨피그 /// Crop 이미지 버퍼 /// 성공 유무 public int AllocCroppedImage(CamConfig camConfig, ref MIL_ID image) { switch (camConfig.ImageSize.Channel) { case 1: if (IsHeightLongerThanWidth(camConfig)) { // 가로 < 세로 MIL.MbufAlloc2d( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), (int)(camConfig.ImageSize.Height * camConfig.ImageSizeRatioY), (int)(camConfig.ImageSize.Width * camConfig.ImageSizeRatioX), 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } else { // 가로 > 세로 MIL.MbufAlloc2d( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), (int)(camConfig.ImageSize.Width * camConfig.ImageSizeRatioX), (int)(camConfig.ImageSize.Height * camConfig.ImageSizeRatioY), 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } case 3: default: if (IsHeightLongerThanWidth(camConfig)) { // 가로 < 세로 MIL.MbufAllocColor( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Channel, (int)(camConfig.ImageSize.Height * camConfig.ImageSizeRatioY), (int)(camConfig.ImageSize.Width * camConfig.ImageSizeRatioX), 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } else { // 가로 > 세로 MIL.MbufAllocColor( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Channel, (int)(camConfig.ImageSize.Width * camConfig.ImageSizeRatioX), (int)(camConfig.ImageSize.Height * camConfig.ImageSizeRatioY), 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref image); ClearImage((MIL_ID)image); return (image == MIL.M_NULL || image == 0) ? E1.GENERIC : E1.OK; } } } /// /// Dig Process 이미지 배열 할당 /// /// 카메라 컨피그 /// 버퍼 사이즈 /// Proc 이미지 버퍼 배열 /// public int AllocProcImages(CamConfig camConfig, int bufferSize, ref object[] images) { switch (camConfig.ImageSize.Channel) { case 1: for (int i = 0; i < bufferSize; i++) { MIL_ID allocImage = MIL.M_NULL; MIL.MbufAlloc2d( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Width, camConfig.ImageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref allocImage); ClearImage(allocImage); images[i] = allocImage; } break; case 3: default: for (int i = 0; i < bufferSize; i++) { MIL_ID allocImage = MIL.M_NULL; MIL.MbufAllocColor( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Channel, camConfig.ImageSize.Width, camConfig.ImageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref allocImage); ClearImage(allocImage); images[i] = allocImage; } break; } return E1.OK; } /// /// Child 이미지 할당 /// /// 카메라 컨피그 /// 부모 이미지 버퍼 /// 자식 이미지 버퍼 배열 /// public int AllocChildImages(CamConfig camConfig, object parentImage, ref object[] childImages) { try { if (parentImage == null) { _Logger.FuncE($"parentImage is null."); return E1.GENERIC; } if (camConfig.ChildImage.ChildBufRowCols.Count == 0) { _Logger.FuncE($"ChildBufRowCols.Count is zero."); return E1.GENERIC; } childImages = new object[camConfig.ChildImage.ChildBufRowCols.Count]; for (int i = 0; i < childImages.Length; i++) { childImages[i] = MIL.M_NULL; } int childWidth = camConfig.ImageSize.Width; int childHeight = camConfig.ImageSize.Height; for (int i = 0; i < camConfig.ChildImage.ChildBufRowCols.Count; i++) { var childBufRowCol = camConfig.ChildImage.ChildBufRowCols[i]; //if (!childBufRowCol.Enabled) { // continue; //} int offsetX = childBufRowCol.ColIdx * childWidth; int offsetY = childBufRowCol.RowIdx * childHeight; MIL_ID childImage = MIL.M_NULL; MIL.MbufChild2d((MIL_ID)parentImage, offsetX, offsetY, childWidth, childHeight, ref childImage); if (childImages.Length > childBufRowCol.PosIdx) { childImages[childBufRowCol.PosIdx] = childImage; } else { _Logger.FuncE("[ERROR] childImages.Length <= childBufRowCol.PosIdx"); } } } catch (Exception e) { _Logger.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } /// /// Child 이미지 사용시, 부모 이미지 버퍼 할당 /// /// 카메라 컨피그 /// 부모 이미지 버퍼 /// 성공 유무 public int AllocParentImage(CamConfig camConfig, ref object image) { MIL_ID allocImage = MIL.M_NULL; switch (camConfig.ImageSize.Channel) { case 1: MIL.MbufAlloc2d( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Width * camConfig.ChildImage.ColCnt, camConfig.ImageSize.Height * camConfig.ChildImage.RowCnt, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref allocImage); break; case 3: default: MIL.MbufAllocColor( GetSystem(camConfig.CamConnConfig.BoardType, camConfig.CamConnConfig.BoardSlotPos), camConfig.ImageSize.Channel, camConfig.ImageSize.Width * camConfig.ChildImage.ColCnt, camConfig.ImageSize.Height * camConfig.ChildImage.RowCnt, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref allocImage); break; } ClearImage(allocImage); image = allocImage; return image == null ? E1.GENERIC : E1.OK; } /// /// 이미지 반전 유무 확인 /// /// 카메라 컨피그 /// 이미지 반전 유무 public bool IsFlipEnabled(CamConfig camConfig) { if (camConfig.FlipAndRotation.ImageFlip.Equals(eImageFlip.None)) return false; else return true; } /// /// 이미지 회전 유무 확인 /// /// 카메라 컨피그 /// 이미지 회전 유무 public bool IsRotationEnabled(CamConfig camConfig) { if (camConfig.FlipAndRotation.ImageRotation.Equals(eImageRotation.None) && camConfig.FlipAndRotation.ImageRotationOffset == 0) return false; else return true; } /// /// 이미지 세로 사이즈가 가로 사이즈보다 큰지 확인 /// /// 카메라 컨피그 /// 이미지 세로 사이즈가 가로 사이즈보다 크면 True public bool IsHeightLongerThanWidth(CamConfig camConfig) { switch (camConfig.FlipAndRotation.ImageRotation) { case eImageRotation.CW_DEG_90: case eImageRotation.CW_DEG_270: return true; } return false; } /// /// 이미지 저장 /// /// 파일경로 /// 이미지버퍼 MIL_ID /// 크롭(저장) 할 영역 /// 성공 유무 public static int SaveImage(string filePath, MIL_ID imageId, Rect roi) { if (!Instance.HasValidLicense()) { return E1.GENERIC; } try { MIL_ID childImage = CropImage(imageId, roi); SaveImage(filePath, childImage); } catch (Exception e) { _Logger.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } /// /// 이미지 저장 /// /// 파일경로 /// 이미지버퍼 MIL_ID /// 성공 유무 public static int SaveImage(string filePath, MIL_ID imageId) { if (!Instance.HasValidLicense()) { return E1.GENERIC; } try { var dirPath = Path.GetDirectoryName(filePath); if (!Directory.Exists(dirPath)) { new DirectoryInfo(dirPath).Create(); } if (filePath.ToLower().EndsWith("bmp")) { MIL.MbufExport(filePath, MIL.M_BMP, imageId); } else if (filePath.ToLower().EndsWith("png")) { MIL.MbufExport(filePath, MIL.M_PNG, imageId); } else if (filePath.ToLower().EndsWith("jpg")) { MIL.MbufExport(filePath, MIL.M_JPEG_LOSSY, imageId); } else { MIL.MbufExport(filePath, MIL.M_PNG, imageId); } } catch (Exception e) { _Logger.FuncE(e.Message); return E1.GENERIC; } return E1.OK; } /// /// 이미지 로드 /// /// 불러올 이미지 경로 /// 보드 타입 /// 보드 슬롯 위치 /// 이미지 버퍼 MIL_ID public static MIL_ID LoadImage(string filePath, eBoardType boardType, eBoardSlotPos boardSlotPos) { return LoadImage(filePath, Instance.GetSystem(boardType, boardSlotPos)); } /// /// 이미지 로드 /// /// 불러올 이미지 경로 /// Mil 시스템 Id /// 이미지 버퍼 MIL_ID public static MIL_ID LoadImage(string filePath, MIL_ID milSystemId) { MIL_ID loadImage = MIL.M_NULL; if (!Instance.HasValidLicense()) { return MIL.M_NULL; } try { if (!File.Exists(filePath)) { return E1.GENERIC; } MIL.MbufRestore(filePath, milSystemId, ref loadImage); } catch (Exception e) { _Logger.FuncE(e.Message); return E1.GENERIC; } return loadImage; } /// /// 이미지 복사 /// /// 소스 이미지 버퍼 /// 목적지 이미지 버퍼 /// 성공 유무 public static int CopyImage(MIL_ID srcImage, MIL_ID dstImage) { if (!Instance.HasValidLicense()) { return E1.GENERIC; } if (IsCopyableBuffer(srcImage, dstImage)) { MIL.MbufCopy(srcImage, dstImage); return E1.OK; } return E1.GENERIC; } /// /// 버퍼 카피 가능한지 여부 /// /// 소스 이미지 버퍼 /// 목적지 이미지 버퍼 /// public static bool IsCopyableBuffer(MIL_ID srcImage, MIL_ID dstImage) { if (!Instance.HasValidLicense()) { return false; } var srcImageSize = GetImageSize(srcImage); var dstImageSize = GetImageSize(dstImage); if (srcImageSize.Width == dstImageSize.Width && srcImageSize.Height == dstImageSize.Height) { return true; } return false; } /// /// 이미지 복제 /// /// 복제할 이미지 ID /// 복제된 이미지 MIL_ID public static MIL_ID CloneImage(MIL_ID imageId) { try { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } return CloneImage(imageId, GetSystemOfBuffer(imageId)); } catch (MILException ex) { _Logger.E($"Mil Exception Catch >> Message({ex.Message})"); return MIL.M_NULL; } } /// /// 이미지 복제 /// /// 복제할 이미지 ID /// MIL 시스템 ID /// 복제된 이미지 MIL_ID public static MIL_ID CloneImage(MIL_ID imageId, MIL_ID systemId) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } return MIL.MbufClone(imageId, systemId, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP, MIL.M_COPY_SOURCE_DATA, MIL.M_NULL); } /// /// 호환가능한 이미지 버퍼 가져오기 /// /// 이미지 버퍼 MIL_ID /// 호환가능한 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleImageBuffer(MIL_ID imageId) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } var imageSize = GetImageSize(imageId); return GetCompatibleImageBuffer(imageSize, GetSystemOfBuffer(imageId)); } /// /// 호환가능한 이미지 버퍼 가져오기 /// /// 이미지 사이즈 /// 보드 타입 /// 보드 슬롯 위치 /// 호환가능한 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleImageBuffer(ImageSize imageSize, eBoardType boardType, eBoardSlotPos boardSlotPos) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } return GetCompatibleImageBuffer(imageSize, Instance.GetSystem(boardType, boardSlotPos)); } /// /// 호환가능한 이미지 버퍼 가져오기 /// /// 이미지 사이즈 /// Mil 시스템 Id /// 호환가능한 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleImageBuffer(ImageSize imageSize, MIL_ID systemId) { if (imageSize.Channel == 1) { return GetCompatibleGrayImageBuffer(imageSize, systemId); } else { return GetCompatibleColorImageBuffer(imageSize, systemId); } } /// /// 호환가능한 Gray 이미지 버퍼 가져오기 /// /// 이미지 버퍼 MIL_ID /// 호환가능한 Gray 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleGrayImageBuffer(MIL_ID imageId) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } var imageSize = GetImageSize(imageId); return GetCompatibleGrayImageBuffer(imageSize, GetSystemOfBuffer(imageId)); } /// /// 호환가능한 Gray이미지 버퍼 가져오기 /// /// 이미지 사이즈 /// 보드 타입 /// 보드 슬롯 위치 /// 호환가능한 Gray 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleGrayImageBuffer(ImageSize imageSize, eBoardType boardType, eBoardSlotPos boardSlotPos) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } return GetCompatibleGrayImageBuffer(imageSize, Instance.GetSystem(boardType, boardSlotPos)); } /// /// 호환가능한 Gray 이미지 버퍼 가져오기 /// /// 이미지 사이즈 /// Mil 시스템 Id /// 호환가능한 Gray 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleGrayImageBuffer(ImageSize imageSize, MIL_ID systemId) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } MIL_ID grayImageId = MIL.M_NULL; MIL.MbufAlloc2d( systemId, imageSize.Width, imageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref grayImageId); MIL.MbufClear(grayImageId, 0); return grayImageId; } /// /// 호환가능한 Color 이미지 버퍼 가져오기 /// /// 이미지 버퍼 MIL_ID /// 호환가능한 Color 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleColorImageBuffer(MIL_ID imageId) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } var imageSize = GetImageSize(imageId); return GetCompatibleColorImageBuffer(imageSize, GetSystemOfBuffer(imageId)); } /// /// 호환가능한 Color 이미지 버퍼 가져오기 /// /// 이미지 사이즈 /// 보드 타입 /// 보드 슬롯 위치 /// 호환가능한 Color 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleColorImageBuffer(ImageSize imageSize, eBoardType boardType, eBoardSlotPos boardSlotPos) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } return GetCompatibleColorImageBuffer(imageSize, Instance.GetSystem(boardType, boardSlotPos)); } /// /// 호환가능한 Color 이미지 버퍼 가져오기 /// /// 이미지 사이즈 /// Mil 시스템 Id /// 호환가능한 Color 이미지 버퍼 MIL_ID public static MIL_ID GetCompatibleColorImageBuffer(ImageSize imageSize, MIL_ID systemId) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } MIL_ID imageBuffer = MIL.M_NULL; MIL.MbufAllocColor( systemId, 3, imageSize.Width, imageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref imageBuffer); MIL.MbufClear(imageBuffer, 0); return imageBuffer; } /// /// 이미지 크롭 /// /// 이미지 버퍼 MIL_ID /// /// public static MIL_ID CropImage(MIL_ID imageId, Rect roi) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } ImageSize imageSize = GetImageSize(imageId); if (imageSize.Width == roi.Width && imageSize.Height == roi.Height) { return imageId; } else { // roi의 Angle을 고려한 Crop API가 있으면 좋을 것 같음. 현재는 Crop시, Angle을 고려하지 않음. return MIL.MbufChild2d(imageId, (int)roi.Left, (int)roi.Top, (int)roi.Width, (int)roi.Height, MIL.M_NULL); } } /// /// 이미지 크롭 /// /// 이미지 버퍼 MIL_ID /// /// public static MIL_ID CropImage(MIL_ID imageId, ROI roi) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } var imageSize = GetImageSize(imageId); if (imageSize.Width > roi.Width && imageSize.Height > roi.Height) { // roi의 Angle을 고려한 Crop API가 있으면 좋을 것 같음. 현재는 Crop시, Angle을 고려하지 않음. return MIL.MbufChild2d(imageId, (int)roi.Left, (int)roi.Top, (int)roi.Width, (int)roi.Height, MIL.M_NULL); } else { return imageId; } } /// /// 이미지 리소스 회수 /// /// 이미지 ID /// 성공 유무 public static int FreeImage(MIL_ID imageId) { if (!Instance.HasValidLicense()) { return E1.GENERIC; } if (imageId != MIL.M_NULL) { MIL.MbufFree(imageId); } return E1.OK; } /// /// 이미지 리소스 회수 /// /// 이미지 ID /// 성공 유무 public static int FreeImage(object imageId) { if (!Instance.HasValidLicense()) { return E1.GENERIC; } if (imageId != null) { MIL.MbufFree((MIL_ID)imageId); } return E1.OK; } /// /// Proc 이미지 배열 리소스 회수 /// /// 버퍼 사이즈 /// Proc 이미지 배열 /// 성공 유무 public static int FreeProcImages(int bufferSize, object[] images) { if (images == null) { return E1.GENERIC; } for (int i = 0; i < bufferSize; i++) { FreeImage((MIL_ID)images[i]); } return E1.OK; } /// /// 사각형 중심 포인트 가져오기 /// /// /// public static ImagePoint GetRectCenter(Rect roi) { return new ImagePoint(roi.Left + roi.Width / 2, roi.Top + roi.Height / 2); } /// /// 이미지 중심 포인트 가져오기 /// /// 이미지 버퍼 MIL_ID /// public static ImagePoint GetImageCenter(MIL_ID imageId) { if (!Instance.HasValidLicense()) { return new ImagePoint(0, 0); } var imageSize = GetImageSize(imageId); return new ImagePoint( imageSize.Width * 0.5, imageSize.Height * 0.5); } /// /// 이미지 사이즈 가져오기 /// /// 이미지 버퍼 MIL_ID /// 이미지 사이즈 public static ImageSize GetImageSize(MIL_ID imageId) { if (!Instance.HasValidLicense()) { return new ImageSize(); } int width = -1; int height = -1; int band = -1; int bitDepth = -1; try { width = (int)MIL.MbufInquire(imageId, MIL.M_SIZE_X); height = (int)MIL.MbufInquire(imageId, MIL.M_SIZE_Y); band = (int)MIL.MbufInquire(imageId, MIL.M_SIZE_BAND); bitDepth = (int)MIL.MbufInquire(imageId, MIL.M_SIZE_BIT); } catch (Exception e) { } return new ImageSize() { Width = width, Height = height, Channel = band, BitDepth = bitDepth }; } /// /// 이미지 사이즈 가져오기 /// /// 카메라 컨피그 /// 성공 유무 public ImageSize GetImageSize(CamConfig camConfig) { var imageSize = new ImageSize(); if (IsHeightLongerThanWidth(camConfig)) { imageSize.Width = (int)(camConfig.ImageSize.Height * camConfig.ImageSizeRatioY); imageSize.Height = (int)(camConfig.ImageSize.Width * camConfig.ImageSizeRatioX); } else { imageSize.Width = (int)(camConfig.ImageSize.Width * camConfig.ImageSizeRatioX); imageSize.Height = (int)(camConfig.ImageSize.Height * camConfig.ImageSizeRatioY); } imageSize.Channel = camConfig.ImageSize.Channel; imageSize.BitDepth = camConfig.ImageSize.BitDepth; // TODO : 프로그램 로드 시 MilDisplay UI의 GetImageSize가 Config의 Set보다 순서상 빨라서 defualt를 Get하는 문제가 있음 // ImageSize를 D드라이브 Config 파일을 읽어오는 것으로 변경함 string configPath = Path.Combine(ApplicationExt.DataPath, "_config_device", eCamId.CAM1 + @".json"); imageSize = JsonManager.GetValue(configPath, "ImageSize"); return imageSize; } /// /// 이미지 사이즈 동일 여부 체크 /// /// 이미지1 /// 이미지2 /// 이미지 사이즈 동일 여부 public static bool ImageSizeEquals(MIL_ID image1, MIL_ID image2) { var imageSize1 = GetImageSize(image1); var imageSize2 = GetImageSize(image2); return ImageSizeEquals(imageSize1, imageSize2); } /// /// 이미지 사이즈 동일 여부 체크 /// /// 이미지 사이즈 1 /// 이미지 사이즈 2 /// 이미지 사이즈 동일 여부 public static bool ImageSizeEquals(ImageSize imageSize1, ImageSize imageSize2) { if (imageSize1.Width != imageSize2.Width || imageSize1.Height != imageSize2.Height || imageSize1.BitDepth != imageSize2.BitDepth || imageSize1.Channel != imageSize2.Channel) { return false; } return true; } /// /// 이미지 사이즈 Null인지 여부 /// /// 이미지 사이즈 /// True : Null public static bool IsImageSizeNull(ImageSize imageSize) { if (imageSize.Width == -1 && imageSize.Height == -1) { return true; } else { return false; } } /// /// 이미지가 Color인지 여부 /// /// 이미지 버퍼 MIL_ID /// True : ColorImage public static bool IsColorImage(MIL_ID imageId) { if (!Instance.HasValidLicense()) { return false; } var imageSize = GetImageSize(imageId); return (imageSize.Channel != 1); } /// /// Mil 이미지를 byte array로 변환 /// /// 이미지 버퍼 MIL_ID /// 변환된 이미지 byte array public static byte[] ConvertMilToByteArry(MIL_ID imageId) { ImageSize imageSize = GetImageSize(imageId); byte[] byteArry = new byte[imageSize.Width * imageSize.Height * imageSize.Channel]; MIL.MbufGet2d(imageId, 0, 0, imageSize.Width, imageSize.Height, byteArry); return byteArry; } /// /// byte array를 mil 이미지 로 변환 /// /// /// /// /// 이미지 버퍼 MIL_ID public static void ConvertByteArryToMil(byte[] byteArry, int width, int height, ref MIL_ID imageId) { var imageSize = GetImageSize(imageId); if (imageSize.Channel == 3) { MIL.MbufPutColor2d(imageId, MIL.M_PACKED + MIL.M_BGR24, MIL.M_ALL_BANDS, 0, 0, width, height, byteArry); } else { MIL.MbufPut2d(imageId, 0, 0, width, height, byteArry); } } /// /// color 이미지를 gray 이미지로 변환 /// /// /// public static MIL_ID ConvertToGray(MIL_ID colorImageId) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } MIL_ID grayImageId = GetCompatibleGrayImageBuffer(colorImageId); ConvertToGray(colorImageId, grayImageId); return grayImageId; } /// /// color 이미지를 gray 이미지로 변환 /// /// /// /// public static int ConvertToGray(MIL_ID colorImageId, MIL_ID grayImageId) { if (!Instance.HasValidLicense()) { return E1.GENERIC; } MIL.MimConvert(colorImageId, grayImageId, MIL.M_RGB_TO_L); return E1.OK; } /// /// gray 이미지를 rgb 이미지로 변환 /// /// 이미지 버퍼 MIL_ID /// 보드 타입 /// 보드 슬롯 위치 /// public static MIL_ID ConvertGrayToRGB(MIL_ID imageId, eBoardType boardType, eBoardSlotPos boardSlotPos) { if (!Instance.HasValidLicense()) { return MIL.M_NULL; } var imageSize = GetImageSize(imageId); if (IsColorImage(imageSize)) { return CloneImage(imageId); } else { MIL_ID rgbImageId = MIL.M_NULL; MIL.MbufAllocColor( Instance.GetSystem(boardType, boardSlotPos), 3, imageSize.Width, imageSize.Height, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_GRAB + MIL.M_PROC + MIL.M_DISP, ref rgbImageId); MIL.MbufCopyColor(imageId, rgbImageId, MIL.M_RED); MIL.MbufCopyColor(imageId, rgbImageId, MIL.M_GREEN); MIL.MbufCopyColor(imageId, rgbImageId, MIL.M_BLUE); return rgbImageId; } } /// /// mil 이미지를 bitmap 이미지로 변환 /// /// 이미지 /// public static Bitmap ConvertToBitmap(MIL_ID image) { if (image == MIL.M_NULL) { return ThumbnailUtil.GetDefaultImage(); } try { var imageSize = GetImageSize(image); byte[] byteData = new byte[imageSize.Width * imageSize.Height * imageSize.Channel]; Bitmap bitmap; if (imageSize.Channel == 3) { MIL.MbufGetColor2d( image, MIL.M_PACKED + MIL.M_RGB24, MIL.M_ALL_BANDS, 0, 0, imageSize.Width, imageSize.Height, byteData); bitmap = new Bitmap(imageSize.Width, imageSize.Height, PixelFormat.Format24bppRgb); } else { MIL.MbufGet2d(image, 0, 0, imageSize.Width, imageSize.Height, byteData); bitmap = new Bitmap(imageSize.Width, imageSize.Height, PixelFormat.Format8bppIndexed); } var rowLength = bitmap.Width * imageSize.Channel; var bitmapData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); for (int h = 0; h < bitmap.Height; h++) { Marshal.Copy( byteData, h * rowLength, bitmapData.Scan0 + (h * bitmapData.Stride), rowLength); } bitmap.UnlockBits(bitmapData); return bitmap; } catch (Exception e) { _Logger.FuncE(e.Message); } return ThumbnailUtil.GetDefaultImage(); } /// /// 컬러 이미지인지 여부 /// /// 이미지 사이즈 /// public static bool IsColorImage(ImageSize imageSize) { return imageSize.Channel != 1; } /// /// 이미지 픽셀 클리어 /// /// 이미지 public static void ClearImage(MIL_ID image) { if (!Instance.HasValidLicense()) { return; } if (image != MIL.M_NULL) { MIL.MbufClear(image, 0); } } /// /// 이미지 픽셀 클리어 /// /// 이미지 public static void ClearImage(object image) { if (!Instance.HasValidLicense()) { return; } if (image != null) { MIL.MbufClear((MIL_ID)image, 0); } } /// /// 이미지의 ROI 내에서 Pixel Sharpness 값 가져오기 /// /// 이미지 /// Rect /// Pixel Sharpness 값 public static int GetSharpnessWithCropRoi(MIL_ID image, Rect cropRoi) { MIL_ID cropImage = CropImage(image, cropRoi); return GetSharpness(cropImage); } /// /// 이미지의 ROI 내에서 Pixel Sharpness 값 가져오기 /// /// 이미지 /// ROI /// Pixel Sharpness 값 public static int GetSharpnessWithCropRoi(MIL_ID image, ROI cropRoi) { MIL_ID cropImage = CropImage(image, cropRoi); return GetSharpness(cropImage); } /// /// 이미지 내에서 Pixel Sharpness 값 가져오기 /// /// 이미지 /// Pixel Sharpness 값 public static int GetSharpness(MIL_ID image) { MIL_INT sharpness = 0; MIL.MdigFocus(MIL.M_NULL, image, MIL.M_DEFAULT, null, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_EVALUATE, ref sharpness); return (int)sharpness; } /// /// 이미지의 ROI 내에서 Pixel Brightness 값 가져오기 /// /// 이미지 /// Rect /// Pixel Brightness 값 public static BrightnessResult GetBrightnessWithCropRoi(MIL_ID image, Rect cropRoi) { MIL_ID cropImage = CropImage(image, cropRoi); return GetBrightness(cropImage, GetRectCenter(cropRoi)); } /// /// 이미지의 ROI 내에서 Pixel Brightness 값 가져오기 /// /// 이미지 /// ROI /// Pixel Brightness 값 public static BrightnessResult GetBrightnessWithCropRoi(MIL_ID image, ROI cropRoi) { MIL_ID cropImage = CropImage(image, cropRoi); return GetBrightness(cropImage, cropRoi.Center); } /// /// 이미지 Point에서 Pixel Brightness 값 가져오기 /// /// 이미지 /// 이미지 포인트 /// Pixel Brightness 값 public static BrightnessResult GetBrightness(MIL_ID image, ImagePoint imagePoint) { MIL_ID contextId = MIL.M_NULL; MIL_ID resultBufId = MIL.M_NULL; MIL_INT min = 0; MIL_INT max = 0; MIL_INT avg = 0; MIL.MimAlloc(GetSystemOfBuffer(image), MIL.M_STATISTICS_CONTEXT, MIL.M_DEFAULT, ref contextId); MIL.MimAllocResult(GetSystemOfBuffer(image), MIL.M_DEFAULT, MIL.M_STATISTICS_RESULT, ref resultBufId); MIL.MimControl(contextId, MIL.M_STAT_MIN, MIL.M_ENABLE); MIL.MimControl(contextId, MIL.M_STAT_MAX, MIL.M_ENABLE); MIL.MimControl(contextId, MIL.M_STAT_MEAN, MIL.M_ENABLE); MIL.MimStatCalculate(contextId, image, resultBufId, MIL.M_DEFAULT); MIL.MimGetResult(resultBufId, MIL.M_STAT_MIN + MIL.M_TYPE_MIL_INT, ref min); MIL.MimGetResult(resultBufId, MIL.M_STAT_MAX + MIL.M_TYPE_MIL_INT, ref max); MIL.MimGetResult(resultBufId, MIL.M_STAT_MEAN + MIL.M_TYPE_MIL_INT, ref avg); MIL.MimFree(resultBufId); MIL.MimFree(contextId); return new BrightnessResult( imagePoint, (int)min, (int)max, (int)avg); } /// /// Overlay 이미지 가져오기 /// /// mil 카메라 디스플레이 /// 그랩 이미지 /// Overlay 이미지 public static MIL_ID GetOverlayImage(IMilCameraDisplay milCameraDisplay, object grabImage = null) { try { MIL_ID image = MIL.M_NULL; if (grabImage == null) { image = milCameraDisplay.GetImage(); } else { image = (MIL_ID)grabImage; } var overlayImage = GetCompatibleColorImageBuffer(image); CopyImage(image, overlayImage); MIL.MgraDraw(milCameraDisplay.GetGraphicListId(), overlayImage, MIL.M_DEFAULT); return overlayImage; } catch (MILException ex) { _Logger.E($"Mil Exception Catch >> Message({ex.Message})"); return MIL.M_NULL; } } /// /// Overlay 이미지 저장 /// /// 파일 경로 /// 모든 FOV 정보 /// 디스플레이 타입 /// 이미지 default:Null /// 성공 유무 public static int SaveScreenShot(string filePath, List fovInfos, eDisplayType displayType, object image = null) { var overlayImages = new Dictionary(); MIL_ID screenShotImage = MIL.M_NULL; try { foreach (var fovInfo in fovInfos) { var milCameraDisplay = CameraDisplayManager.GetDisplay(fovInfo, displayType) as IMilCameraDisplay; var overlayImage = GetOverlayImage(milCameraDisplay, image); overlayImages[fovInfo] = overlayImage; } if (0 < overlayImages.Count) { var screenShotImageSize = GetScreenShotImageSize(overlayImages, displayType); if (screenShotImageSize == null) { return E1.GENERIC; } screenShotImage = GetCompatibleColorImageBuffer(screenShotImageSize, GetSystemOfBuffer(overlayImages.Values.ToList()[0])); var (ltFovInfo, ltFovUiPos) = ScreenShotUtil.GetLeftTopFovInfo(overlayImages.Keys.ToList(), displayType); var (ltFovUiPosRow, ltFovUiPosCol) = FovRecipe.GetFovUiPosRowCol(ltFovUiPos); foreach (var fovInfo in fovInfos) { var fovRecipe = VisionRecipeManager.GetFovRecipe(fovInfo); eFovUiPos fovUiPos = new eFovUiPos(); foreach (var mainUiRecipe in fovRecipe.MainUiRecipes) { if (mainUiRecipe.MainDisplayType == displayType) { fovUiPos = mainUiRecipe.FovUiPos; } } var (curFovUiPosRow, curFovUiPosCol) = FovRecipe.GetFovUiPosRowCol(fovUiPos); var imageSize = GetImageSize(overlayImages[fovInfo]); MIL.MbufCopyClip( overlayImages[fovInfo], screenShotImage, (curFovUiPosCol - ltFovUiPosCol) * imageSize.Width, (curFovUiPosRow - ltFovUiPosRow) * imageSize.Height); } SaveImage(filePath, screenShotImage); } } catch (Exception e) { _Logger.E(e.Message); } finally { FreeImage(screenShotImage); foreach (var overlayImage in overlayImages.Values) { FreeImage(overlayImage); } } return E1.OK; } private static ImageSize GetScreenShotImageSize(Dictionary overlayImages, eDisplayType displayType) { if (0 < overlayImages.Count && 0 < overlayImages.Count) { var (ltFovInfo, ltFovUiPos) = ScreenShotUtil.GetLeftTopFovInfo(overlayImages.Keys.ToList(), displayType); if (ltFovInfo == null) { return null; } var imageSize = GetImageSize(overlayImages[ltFovInfo]); imageSize.Channel = 3; if (ScreenShotUtil.HasRightFov(ltFovInfo, ltFovUiPos, overlayImages.Keys.ToList(), displayType)) { imageSize.Width *= 2; } if (ScreenShotUtil.HasBottomFov(ltFovInfo, ltFovUiPos, overlayImages.Keys.ToList(), displayType)) { imageSize.Height *= 2; } return imageSize; } return null; } } }