2026-01-26 14:51:28 +09:00

580 lines
20 KiB
C#

/*
* Copyright (c) 2025 LG Electronics Inc.
* SPDX-License-Identifier: LicenseRef-LGE-Proprietary
*/
using Aurora;
using Aurora.Api;
using Aurora.Api.DataType;
using CogCommon;
using VisionCommon;
using Hermes;
using Cognex.VisionPro;
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 CogCamera {
/// <summary>
/// Cognex 시뮬 카메라 디바이스
/// </summary>
public class CogCameraMock : CameraBase {
private List<string> _DummyImgPaths = new List<string>();
private List<Bitmap> _DummyImgs = new List<Bitmap>();
private ICogCameraDisplay _CogCameraDisplay; // Fov별로 가지는 CogCameraDisplay를 호출시마다 받아서 사용
private CogImgFlipAndRotationTool _CogImgFlipAndRotationTool;
private ICogImage _OriginalImage = null; // 원본 이미지 버퍼
private ICogImage _FilteredImage = null; // Flip + Rotation 이미지 버퍼
private ICogImage _FilteredLiveImage = null; // Flip + Rotation 라이브 이미지 버퍼
private ICogImage _CroppedImage = null; // Flip + Rotation + Crop 이미지 버퍼
private ICogImage _CroppedLiveImage = null; // Flip + Rotation + Crop 라이브 이미지 버퍼
private int _DummyImgCounter = 0;
/// <summary>
/// 생성자
/// </summary>
/// <param name="deviceId"></param>
public CogCameraMock(string deviceId) : base(deviceId) {
LOG = new Logger(string.Format("CogCameraMock[{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);
_CogImgFlipAndRotationTool = new CogImgFlipAndRotationTool(camId, CamConfig);
var res = Init();
if (res == E1.OK) {
CheckImageBufferRotationChanged();
}
return res;
}
#region Init
private int Init() {
if (!CogEnvironment.Instance.HasValidLicense()) {
LOG.FuncE("No Cognex 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;
}
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;
}
protected int AllocImages() {
try {
var results = new List<int>();
results.Add(AllocOriginalImage(ref _OriginalImage));
results.Add(AllocFilteredImage(ref _FilteredImage));
results.Add(AllocFilteredImage(ref _FilteredLiveImage));
results.Add(AllocCroppedImage(ref _CroppedImage));
results.Add(AllocCroppedImage(ref _CroppedLiveImage));
foreach (int result in results) {
if (result != E1.OK) {
return E1.GENERIC;
}
}
_CroppedImage = new CogImage8Grey(CamConfig.ImageSize.Width, CamConfig.ImageSize.Height);
_FilteredImage = new CogImage8Grey(CamConfig.ImageSize.Width, CamConfig.ImageSize.Height);
_FilteredLiveImage = new CogImage8Grey(CamConfig.ImageSize.Width, CamConfig.ImageSize.Height);
}
catch (Exception e) {
LOG.E(e.Message);
return E1.GENERIC;
}
return E1.OK;
}
private int AllocOriginalImage(ref ICogImage image) {
return CogEnvironment.Instance.AllocOriginalImage(CamConfig, ref image);
}
private int AllocFilteredImage(ref ICogImage image) {
if (CamConfig.ImageSize.Channel == 3) {
return CogEnvironment.Instance.AllocColorImage(CamConfig, false, ref image);
}
else {
return CogEnvironment.Instance.AllocGrayImage(CamConfig, false, ref image);
}
}
private int AllocCroppedImage(ref ICogImage image) {
if (CamConfig.ImageSize.Channel == 3) {
return CogEnvironment.Instance.AllocColorImage(CamConfig, true, ref image);
}
else {
return CogEnvironment.Instance.AllocGrayImage(CamConfig, true, ref image);
}
}
#endregion
protected override int DoDeactivate() {
FreeImages();
CogEnvironment.Instance.Dispose();
return E1.OK;
}
private void FreeImages() {
try {
CogEnvironment.FreeImage(_FilteredImage);
CogEnvironment.FreeImage(_FilteredLiveImage);
}
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) => throw new NotImplementedException();
/// <summary>
/// 이미지 반전 & 회전 Config 갱신
/// </summary>
/// <param name="flipAndRotation">이미지 반전 & 회전 Config</param>
/// <returns>성공 유무</returns>
public override int ResetFlipAndRotation(FlipAndRotation flipAndRotation) {
_CogImgFlipAndRotationTool.ResetFlipAndRotation(flipAndRotation);
return E1.OK;
}
/// <summary>
/// 이미지 버퍼 복제
/// </summary>
/// <returns>복제 버퍼</returns>
public override object CloneImageBuffer() {
return CogEnvironment.CloneImage(_CroppedImage);
}
/// <summary>
/// 디스플레이 가져오기
/// </summary>
/// <returns>디스플레이</returns>
public override ICameraDisplay GetDisplay() {
return _CogCameraDisplay;
}
/// <summary>
/// 디스플레이 지정하기
/// </summary>
/// <param name="cameraDisplay">디스플레이</param>
public override void SetDisplay(ICameraDisplay cameraDisplay) {
_CogCameraDisplay = cameraDisplay as ICogCameraDisplay;
}
/// <summary>
/// 이미지 사이즈 가져오기
/// </summary>
/// <returns>이미지 사이즈</returns>
public override ImageSize GetImageSize() {
return CogEnvironment.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 ICogCameraDisplay);
CogEnvironment.ClearImage(image);
CogEnvironment.ClearImage(_OriginalImage);
if (_DummyImgPaths.Count > 0) {
try {
if (File.Exists(_DummyImgPaths[_DummyImgCounter])) {
lock (LockThis) {
Bitmap bmpImg = GetDummyImage();
AddImageIndex(bmpImg, _DummyImgCounter + 1);
_OriginalImage = new CogImage8Grey(bmpImg);
FlipAndRotateImage();
CropImage();
image = IsLiveOn() ? _CroppedLiveImage : _CroppedImage;
CogEnvironment.FreeImage(_OriginalImage);
_OriginalImage = null;
_DummyImgCounter++;
if (_DummyImgCounter >= _DummyImgPaths.Count) {
_DummyImgCounter = 0;
}
}
SetDisplayImage();
return E1.OK;
}
}
catch (Exception e) {
LOG.E(e.Message);
return E1.GENERIC;
}
}
return E1.GENERIC;
}
private Bitmap GetDummyImage() {
try {
return (Bitmap)Image.FromFile(_DummyImgPaths[_DummyImgCounter]);
}
catch (Exception e) {
LOG.FuncE(e.Message);
}
return null;
}
private void AddImageIndex(Bitmap bitmap, int index) {
if (bitmap == null) {
return;
}
using (var graphics = Graphics.FromImage(bitmap)) {
var imageCenterX = bitmap.Width / 2;
var drawStr = index.ToString();
// String - Image Index
var fontSize = 60;
var strPosX = imageCenterX - fontSize;
var strPosY = bitmap.Height - fontSize * (float)1.5;
var strBrush = new SolidBrush(Color.FromKnownColor(KnownColor.White));
var font = new Font("Consolas", fontSize, System.Drawing.FontStyle.Bold);
// Rectangle - Black background
var strSize = graphics.MeasureString(drawStr, font);
var rect = new RectangleF(
strPosX,
strPosY,
strSize.Width,
strSize.Height);
graphics.FillRectangle(Brushes.Black, rect);
graphics.DrawString(drawStr, font, strBrush, strPosX, strPosY);
}
}
#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 {
return E1.OK;
}
catch (Exception e) {
LOG.FuncE(e.Message);
}
return E1.GENERIC;
}
#endregion
private void SetDisplayImage() {
if (_CogCameraDisplay == null) {
return;
}
if (IsLiveOn()) {
if (_FilteredLiveImage != null) {
_CogCameraDisplay.SetImage(_CroppedLiveImage);
}
}
else {
if (_FilteredImage != null) {
_CogCameraDisplay.SetImage(_CroppedImage);
}
}
}
private void FlipAndRotateImage() {
if (_OriginalImage == null) {
LOG.FuncE("_OriginalImage is null.");
return;
}
if (IsLiveOn()) {
_CogImgFlipAndRotationTool.FlipAndRotation(_OriginalImage, ref _FilteredLiveImage);
}
else {
_CogImgFlipAndRotationTool.FlipAndRotation(_OriginalImage, ref _FilteredImage);
}
}
private void CropImage() {
var cropRoi = GetCropRoi();
if (IsLiveOn()) {
if (_FilteredLiveImage != null) {
_CroppedLiveImage = CogEnvironment.CropImage(_FilteredLiveImage, cropRoi);
}
}
else {
if (_FilteredImage != null) {
_CroppedImage = CogEnvironment.CropImage(_FilteredImage, cropRoi);
}
}
}
/// <summary>
/// 라이브 시작
/// </summary>
/// <param name="cameraDisplay">디스플레이</param>
public override void StartLive(ICameraDisplay cameraDisplay) {
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(_CogCameraDisplay, 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 cogCameraDisplay = cameraDisplay as ICogCameraDisplay;
// 버퍼 그랩 이후, 디스플레이로부터 이미지를 꺼내오는 부분
// 이미지를 꺼내오기 전에 cogCameraDisplay와 동일한 _CogCameraDisplay가 사전에 Set되어 있어야 함.
if (cogCameraDisplay.IsSetImage()) {
return cogCameraDisplay.GetImage();
}
LOG.FuncE("Image of cogCameraDisplay is null.");
return null;
}
//public override MIL_ID GetGrabImageFromQueue(eGrabTrigSrcType grabTrigSrcType) {
// // TO-DO
// return MIL.M_NULL;
//}
}
}