600 lines
21 KiB
C#
600 lines
21 KiB
C#
/*
|
|
* Copyright (c) 2025 LG Electronics Inc.
|
|
* SPDX-License-Identifier: LicenseRef-LGE-Proprietary
|
|
*/
|
|
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.Api.DataType;
|
|
using MilCommon;
|
|
using VisionCommon;
|
|
using Hermes;
|
|
using Matrox.MatroxImagingLibrary;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using VisionCommonUI;
|
|
using VisionCommonUI.Resources;
|
|
|
|
namespace MilCamera {
|
|
/// <summary>
|
|
/// MIL 시뮬 카메라 디바이스
|
|
/// </summary>
|
|
public class MilCameraMock : CameraBase {
|
|
private List<string> _DummyImgPaths = new List<string>();
|
|
private List<Bitmap> _DummyImgs = new List<Bitmap>();
|
|
|
|
private IMilCameraDisplay _MilCameraDisplay; // Fov별로 가지는 MilCameraDisplay를 호출시마다 받아서 사용
|
|
private MilImgFlipAndRotationTool _MilImgFlipAndRotationTool;
|
|
|
|
private MIL_ID _OriginalImage = MIL.M_NULL; // 원본 이미지 버퍼
|
|
private MIL_ID _FilteredImage = MIL.M_NULL; // Flip + Rotation 이미지 버퍼
|
|
private MIL_ID _FilteredLiveImage = MIL.M_NULL; // Flip + Rotation 라이브 이미지 버퍼
|
|
private MIL_ID _CroppedImage = MIL.M_NULL; // Flip + Rotation + Crop 이미지 버퍼
|
|
private MIL_ID _CroppedLiveImage = MIL.M_NULL; // Flip + Rotation + Crop 라이브 이미지 버퍼
|
|
|
|
private int _DummyImgCounter = 0;
|
|
|
|
/// <summary>
|
|
/// 생성자
|
|
/// </summary>
|
|
/// <param name="deviceId">디바이스 ID</param>
|
|
public MilCameraMock(string deviceId) : base(deviceId) {
|
|
LOG = new Logger(string.Format("MilCameraMock[{0}]", deviceId));
|
|
|
|
Task.Run(() => {
|
|
SetDummyImages();
|
|
});
|
|
}
|
|
|
|
private void SetDummyImages() {
|
|
try {
|
|
var dummyImgDestPath = Path.Combine(System.Environment.CurrentDirectory, "_dummyImages", DeviceId);
|
|
|
|
_DummyImgPaths.AddRange(
|
|
Directory.EnumerateFiles(dummyImgDestPath, "*.bmp", SearchOption.TopDirectoryOnly)
|
|
);
|
|
_DummyImgPaths.AddRange(
|
|
Directory.EnumerateFiles(dummyImgDestPath, "*.png", SearchOption.TopDirectoryOnly)
|
|
);
|
|
_DummyImgPaths.AddRange(
|
|
Directory.EnumerateFiles(dummyImgDestPath, "*.jpg", SearchOption.TopDirectoryOnly)
|
|
);
|
|
_DummyImgPaths.AddRange(
|
|
Directory.EnumerateFiles(dummyImgDestPath, "*.jpeg", SearchOption.TopDirectoryOnly)
|
|
);
|
|
|
|
foreach (var dummyImgPath in _DummyImgPaths) {
|
|
LOG.FuncI("Current Img File : {0} ", dummyImgPath);
|
|
_DummyImgs.Add((Bitmap)Image.FromFile(dummyImgPath));
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
LOG.FuncE(e.Message);
|
|
}
|
|
}
|
|
|
|
protected override int DoActivate(IDeviceConfig config) {
|
|
CamConfig = config as CamConfig;
|
|
eCamId camId = CamRecipe.GetCamIdFromDevId(DeviceId);
|
|
_MilImgFlipAndRotationTool = new MilImgFlipAndRotationTool(camId, CamConfig);
|
|
|
|
var res = Init();
|
|
if (res == E1.OK) {
|
|
CheckImageBufferRotationChanged();
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
#region Init
|
|
private int Init() {
|
|
MilEnvironment.Instance.AllocSys(CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos);
|
|
|
|
if (!MilEnvironment.Instance.HasValidLicense()) {
|
|
LOG.FuncE("No MIL License.");
|
|
return E1.GENERIC;
|
|
}
|
|
|
|
if (E1.OK != SetImageSize()) {
|
|
MessageBoxUtil.ShowMessageBoxError(StrTokens.MSG_E_ImageSizeSettingError);
|
|
return E1.GENERIC;
|
|
}
|
|
if (E1.OK != AllocImages()) {
|
|
LOG.FuncE("AllocImage Error.");
|
|
return E1.GENERIC;
|
|
}
|
|
|
|
return E1.OK;
|
|
}
|
|
|
|
protected int AllocImages() {
|
|
try {
|
|
var results = new List<int>();
|
|
//results.Add(AllocOriginalImage(ref _OriginalImage)); // 원본 이미지 불러오면서 버퍼가 자동 할당되므로 필요X
|
|
results.Add(AllocFilteredImage(ref _FilteredImage));
|
|
results.Add(AllocFilteredImage(ref _FilteredLiveImage));
|
|
results.Add(AllocCroppedImage(ref _CroppedImage));
|
|
results.Add(AllocCroppedImage(ref _CroppedLiveImage));
|
|
results.AddRange(AllocProcImages());
|
|
|
|
foreach (int result in results) {
|
|
if (result != E1.OK) {
|
|
return E1.GENERIC;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
LOG.E(e.Message);
|
|
return E1.GENERIC;
|
|
}
|
|
|
|
return E1.OK;
|
|
}
|
|
|
|
private int SetImageSize() {
|
|
try {
|
|
if (_DummyImgs.Count > 0) {
|
|
CamConfig.ImageSize.Width = _DummyImgs[0].Width;
|
|
CamConfig.ImageSize.Height = _DummyImgs[0].Height;
|
|
}
|
|
else {
|
|
CamConfig.ImageSize.Width = 2448; // Default SizeX
|
|
CamConfig.ImageSize.Height = 2048; // Default SizeY
|
|
}
|
|
|
|
CamConfig.ImageSize.Channel = 1;
|
|
CamConfig.ImageSize.BitDepth = 8;
|
|
}
|
|
catch (Exception e) {
|
|
LOG.E(e.Message);
|
|
return E1.GENERIC;
|
|
}
|
|
|
|
return E1.OK;
|
|
}
|
|
#endregion
|
|
|
|
protected override int DoDeactivate() {
|
|
return Dispose();
|
|
}
|
|
|
|
private int Dispose() {
|
|
try {
|
|
FreeImages();
|
|
|
|
return E1.OK;
|
|
}
|
|
catch (Exception e) {
|
|
LOG.FuncE(e.Message);
|
|
}
|
|
|
|
return E1.GENERIC;
|
|
}
|
|
|
|
private void FreeImages() {
|
|
try {
|
|
MilEnvironment.FreeImage(_OriginalImage);
|
|
MilEnvironment.FreeImage(_FilteredImage);
|
|
MilEnvironment.FreeImage(_FilteredLiveImage);
|
|
MilEnvironment.FreeImage(_CroppedImage);
|
|
MilEnvironment.FreeImage(_CroppedLiveImage);
|
|
MilEnvironment.FreeImage(_ParentImage);
|
|
|
|
int bufSize = CamConfig.ChildImage.Enabled ? CamConfig.ChildImage.ChildBufRowCols.Count : CamConfig.HwTrigConfig.ProcBufSize;
|
|
MilEnvironment.FreeProcImages(bufSize, _ProcImages);
|
|
}
|
|
catch (Exception e) {
|
|
LOG.E(e.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 카메라 연결 유무
|
|
/// </summary>
|
|
/// <returns>연결 유무</returns>
|
|
public override bool IsConnected() => IsActivated;
|
|
|
|
/// <summary>
|
|
/// 카메라 Gain 읽기
|
|
/// </summary>
|
|
/// <returns>Gain 값</returns>
|
|
public override int GetGain() => throw new NotImplementedException();
|
|
|
|
/// <summary>
|
|
/// 카메라 Gain 쓰기
|
|
/// </summary>
|
|
/// <param name="gain">Gain 값</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int SetGain(int gain) => throw new NotImplementedException();
|
|
|
|
/// <summary>
|
|
/// 카메라 노출시간 읽기
|
|
/// </summary>
|
|
/// <returns>노출시간</returns>
|
|
public override double GetExposure() => throw new NotImplementedException();
|
|
|
|
/// <summary>
|
|
/// 카메라 최소 노출시간 읽기
|
|
/// </summary>
|
|
/// <returns>최소 노출시간</returns>
|
|
public override double GetMinExposure() => throw new NotImplementedException();
|
|
|
|
/// <summary>
|
|
/// 카메라 최대 노출시간 읽기
|
|
/// </summary>
|
|
/// <returns>최대 노출시간</returns>
|
|
public override double GetMaxExposure() => throw new NotImplementedException();
|
|
|
|
/// <summary>
|
|
/// 카메라 노출시간 쓰기
|
|
/// </summary>
|
|
/// <param name="exposureTime_ms">노출시간[ms]</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int SetExposure(double exposureTime_ms) {
|
|
LOG.I($"Exposure Time Set : {exposureTime_ms} ms");
|
|
return E1.OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이미지 반전 & 회전 Config 갱신
|
|
/// </summary>
|
|
/// <param name="flipAndRotation">이미지 반전 & 회전 Config</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int ResetFlipAndRotation(FlipAndRotation flipAndRotation) {
|
|
_MilImgFlipAndRotationTool?.ResetFlipAndRotation(flipAndRotation);
|
|
return E1.OK;
|
|
}
|
|
|
|
private int AllocOriginalImage(ref MIL_ID image) {
|
|
return MilEnvironment.Instance.AllocOriginalImage(CamConfig, ref image);
|
|
}
|
|
|
|
private int AllocFilteredImage(ref MIL_ID image) {
|
|
return MilEnvironment.Instance.AllocFilteredImage(CamConfig, ref image);
|
|
}
|
|
|
|
private int AllocCroppedImage(ref MIL_ID image) {
|
|
return MilEnvironment.Instance.AllocCroppedImage(CamConfig, ref image);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이미지 버퍼 복제
|
|
/// </summary>
|
|
/// <returns>복제 버퍼</returns>
|
|
public override object CloneImageBuffer() {
|
|
return MilEnvironment.CloneImage(_CroppedImage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 디스플레이 가져오기
|
|
/// </summary>
|
|
/// <returns>디스플레이</returns>
|
|
public override ICameraDisplay GetDisplay() {
|
|
return _MilCameraDisplay;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 디스플레이 지정하기
|
|
/// </summary>
|
|
/// <param name="cameraDisplay">디스플레이</param>
|
|
public override void SetDisplay(ICameraDisplay cameraDisplay) {
|
|
_MilCameraDisplay = cameraDisplay as IMilCameraDisplay;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이미지 사이즈 가져오기
|
|
/// </summary>
|
|
/// <returns>이미지 사이즈</returns>
|
|
public override ImageSize GetImageSize() {
|
|
return MilEnvironment.Instance.GetImageSize(CamConfig);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 버퍼로부터 이미지를 꺼내온다. 시뮬모드이므로 신규 그랩으로 처리
|
|
/// </summary>
|
|
/// <param name="cameraDisplay">디스플레이</param>
|
|
/// <param name="image">이미지</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int GetGrabImageFromBuffer(ICameraDisplay cameraDisplay, ref object image) {
|
|
return GrabSync(cameraDisplay, ref image);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 동기식 그랩
|
|
/// </summary>
|
|
/// <param name="cameraDisplay">디스플레이</param>
|
|
/// <param name="image">그랩 이미지</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int GrabSync(ICameraDisplay cameraDisplay, ref object image) {
|
|
SetDisplay(cameraDisplay as IMilCameraDisplay);
|
|
MilEnvironment.ClearImage(_OriginalImage);
|
|
|
|
if (_DummyImgPaths.Count > 0) {
|
|
try {
|
|
if (File.Exists(_DummyImgPaths[_DummyImgCounter])) {
|
|
lock (LockThis) {
|
|
MIL.MbufRestore(_DummyImgPaths[_DummyImgCounter], MilEnvironment.Instance.GetSystem(
|
|
CamConfig.CamConnConfig.BoardType, CamConfig.CamConnConfig.BoardSlotPos), ref _OriginalImage);
|
|
|
|
FlipAndRotateImage();
|
|
CropImage();
|
|
image = IsLiveOn() ? _CroppedLiveImage : _CroppedImage;
|
|
|
|
AddImageIndex(_DummyImgCounter + 1, (MIL_ID)image);
|
|
|
|
MIL.MbufFree(_OriginalImage);
|
|
_OriginalImage = MIL.M_NULL;
|
|
|
|
_DummyImgCounter++;
|
|
if (_DummyImgCounter >= _DummyImgPaths.Count) {
|
|
_DummyImgCounter = 0;
|
|
}
|
|
}
|
|
|
|
SetFlyingImage();
|
|
SetDisplayImage();
|
|
|
|
return E1.OK;
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
LOG.E(e.Message);
|
|
return E1.GENERIC;
|
|
}
|
|
}
|
|
|
|
return E1.GENERIC;
|
|
}
|
|
|
|
#region For Hw Trigger
|
|
private List<int> AllocProcImages() {
|
|
var results = new List<int>();
|
|
|
|
if (!CamConfig.IsAlwaysSwTrigger()) {
|
|
if (_ProcImages?.Length > 0) {
|
|
MilEnvironment.FreeProcImages(_ProcImages.Length, _ProcImages);
|
|
}
|
|
|
|
if (CamConfig.ChildImage.Enabled) {
|
|
results.Add(MilEnvironment.Instance.AllocParentImage(CamConfig, ref _ParentImage));
|
|
results.Add(MilEnvironment.Instance.AllocChildImages(CamConfig, _ParentImage, ref _ProcImages));
|
|
}
|
|
else {
|
|
_ProcImages = new object[CamConfig.HwTrigConfig.ProcBufSize];
|
|
results.Add(MilEnvironment.Instance.AllocProcImages(CamConfig, CamConfig.HwTrigConfig.ProcBufSize, ref _ProcImages));
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
#endregion
|
|
|
|
#region For Flying
|
|
/// <summary>
|
|
/// 플라잉 시작
|
|
/// </summary>
|
|
/// <param name="fovInfo">FOV 정보</param>
|
|
/// <param name="param">추가 파라미터</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int StartFlying(FovInfo fovInfo, object param = null) {
|
|
if (IsFlyingRunning()) {
|
|
LOG.FuncW("Flying Already Running...");
|
|
return E1.GENERIC;
|
|
}
|
|
LOG.I("Flying Start.");
|
|
|
|
try {
|
|
var camDisplay = CameraDisplayManager.GetDisplay(fovInfo);
|
|
SetDisplay(camDisplay);
|
|
|
|
_FlyingIdx = 0;
|
|
_IsFlyingRunning = true;
|
|
|
|
//StartMdigProcess();
|
|
}
|
|
catch (Exception e) {
|
|
LOG.FuncE(e.Message);
|
|
return E1.GENERIC;
|
|
}
|
|
return E1.OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 플라잉 종료
|
|
/// </summary>
|
|
/// <param name="param">추가 파라미터</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int StopFlying(object param = null) {
|
|
LOG.I("Flying Stopped.");
|
|
|
|
try {
|
|
_IsFlyingRunning = false;
|
|
//StopMdigProcess();
|
|
}
|
|
catch (Exception e) {
|
|
LOG.FuncE(e.Message);
|
|
return E1.GENERIC;
|
|
}
|
|
return E1.OK;
|
|
}
|
|
|
|
private void SetFlyingImage() {
|
|
if (!CamConfig.TriggerMode.IsFlying) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
FlipAndRotateImage();
|
|
CropImage();
|
|
|
|
_FlyingIdx = (_FlyingIdx < _ProcImages.Length) ? _FlyingIdx : 0;
|
|
MilEnvironment.CopyImage(_CroppedImage, (MIL_ID)_ProcImages[_FlyingIdx++]);
|
|
|
|
SetDisplayImage();
|
|
}
|
|
catch (Exception e) {
|
|
LOG.FuncE(e.Message);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region For Child Image
|
|
/// <summary>
|
|
/// Child 이미지들로 Parent(원장) 이미지가 정상적으로 만들어지는지 검증
|
|
/// </summary>
|
|
/// <param name="cameraDisplay">디스플레이</param>
|
|
/// <param name="image">원장(부모) 이미지</param>
|
|
/// <returns>성공 유무</returns>
|
|
public override int VerifyChildImage(ICameraDisplay cameraDisplay, ref object image) {
|
|
try {
|
|
SetDisplay(cameraDisplay);
|
|
|
|
object childImage = CloneImageBuffer();
|
|
for (int i = 0; i < _ProcImages.Length; i++) {
|
|
SetExposure((i + 1) * 5);
|
|
GrabSync(_MilCameraDisplay, ref childImage);
|
|
MilEnvironment.CopyImage((MIL_ID)childImage, (MIL_ID)_ProcImages[i]);
|
|
}
|
|
image = GetParentImage();
|
|
MilEnvironment.FreeImage(childImage);
|
|
|
|
return E1.OK;
|
|
}
|
|
catch (Exception e) {
|
|
LOG.FuncE(e.Message);
|
|
}
|
|
return E1.GENERIC;
|
|
}
|
|
#endregion
|
|
|
|
private void AddImageIndex(int index, MIL_ID filteredImage) {
|
|
if (filteredImage == MIL.M_NULL) {
|
|
return;
|
|
}
|
|
|
|
var imageSize = MilEnvironment.GetImageSize(filteredImage);
|
|
var imageCenter = MilEnvironment.GetImageCenter(filteredImage);
|
|
|
|
// String - Image Index
|
|
var fontSize = 60;
|
|
var strPosX = imageCenter.X - fontSize;
|
|
var strPosY = imageSize.Height - fontSize * (float)1.5;
|
|
MIL.MgraFontScale(MIL.M_DEFAULT, 6, 6);
|
|
MIL.MgraText(MIL.M_DEFAULT, filteredImage, strPosX, strPosY, index.ToString());
|
|
MIL.MgraFontScale(MIL.M_DEFAULT, 1, 1);
|
|
}
|
|
|
|
private void SetDisplayImage() {
|
|
if (_MilCameraDisplay == null) {
|
|
return;
|
|
}
|
|
|
|
if (IsLiveOn()) {
|
|
if (_CroppedLiveImage != null) {
|
|
_MilCameraDisplay.SetImage(_CroppedLiveImage);
|
|
}
|
|
}
|
|
else {
|
|
if (_CroppedImage != null) {
|
|
_MilCameraDisplay.SetImage(_CroppedImage);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void FlipAndRotateImage() {
|
|
if (_OriginalImage == null) {
|
|
LOG.FuncE("_OriginalImage is null.");
|
|
return;
|
|
}
|
|
|
|
if (IsLiveOn()) {
|
|
_MilImgFlipAndRotationTool.FlipAndRotation(_OriginalImage, ref _FilteredLiveImage);
|
|
}
|
|
else {
|
|
_MilImgFlipAndRotationTool.FlipAndRotation(_OriginalImage, ref _FilteredImage);
|
|
}
|
|
}
|
|
|
|
private void CropImage() {
|
|
var cropRoi = GetCropRoi();
|
|
if (IsLiveOn()) {
|
|
if (_FilteredLiveImage != null) {
|
|
_CroppedLiveImage = MilEnvironment.CropImage(_FilteredLiveImage, cropRoi);
|
|
}
|
|
}
|
|
else {
|
|
if (_FilteredImage != null) {
|
|
_CroppedImage = MilEnvironment.CropImage(_FilteredImage, cropRoi);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 라이브 시작
|
|
/// </summary>
|
|
/// <param name="cameraDisplay">디스플레이</param>
|
|
public override void StartLive(ICameraDisplay cameraDisplay) {
|
|
if (_CroppedLiveImage == MIL.M_NULL) {
|
|
MessageBoxUtil.ShowMessageBoxError(string.Format(StrTokens.MSG_E_SomethingNotAllocated, "_CroppedLiveImage"));
|
|
return;
|
|
}
|
|
|
|
lock (_LiveLock) {
|
|
// 이미 라이브가 실행중이면, 디스플레이만 변경한다.
|
|
SetDisplay(cameraDisplay);
|
|
|
|
if (!IsLiveOn()) {
|
|
LiveOn();
|
|
Task.Run(() => { StartLive_Simul(); });
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 라이브 종료
|
|
/// </summary>
|
|
public override void StopLive() {
|
|
lock (_LiveLock) {
|
|
if (IsLiveOn()) {
|
|
LiveOff();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void StartLive_Simul() {
|
|
try {
|
|
object tmpImage = null; // 라이브때 리턴 이미지는 필요X
|
|
|
|
while (IsLiveOn()) {
|
|
GrabSync(_MilCameraDisplay, ref tmpImage);
|
|
Thread.Sleep(CamConfig.LiveDelay_Ms);
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
LOG.FuncE(e.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 디스플레이에서 이미지 꺼내오기
|
|
/// </summary>
|
|
/// <param name="cameraDisplay">디스플레이</param>
|
|
/// <returns>디스플레이 이미지</returns>
|
|
public override object GetImageFromDisplay(ICameraDisplay cameraDisplay) {
|
|
var milCameraDisplay = cameraDisplay as IMilCameraDisplay;
|
|
|
|
// 버퍼 그랩 이후, 디스플레이로부터 이미지를 꺼내오는 부분
|
|
// 이미지를 꺼내오기 전에 milCameraDisplay와 동일한 _MilCameraDisplay가 사전에 Set되어 있어야 함.
|
|
if (milCameraDisplay.IsSetImage()) {
|
|
return milCameraDisplay.GetImage();
|
|
}
|
|
|
|
LOG.FuncE("Image of milCameraDisplay is null.");
|
|
return MIL.M_NULL;
|
|
}
|
|
}
|
|
} |