using Inspectron.HawkEye.Protocol; using OpenCvSharp; using Serilog; using System.Net; using System.Runtime.InteropServices; using VisionBuilder.UI.Common; using VisionBuilder.UI.Common.RecipeProcessing; using CameraSettings = Inspectron.HawkEye.Protocol.CameraSettings; namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye { public class HawkeyeCameraImageSource : IImageSource, IVisionBuilderModule { private readonly HawkeyeSettings _settings; private ImageClientTCP _client; private CameraSettings _cameraSettings; public HawkeyeCameraImageSource(HawkeyeSettings settings) { _settings = settings; } public void Open() { _client = new ImageClientTCP(new IPEndPoint(IPAddress.Parse("192.168.3.15"), 27001), IPAddress.Parse(_settings.Adapter)); _client.ImageReceived += _client_ImageReceived; _client.SettingsReceived += _client_SettingsReceived; _client.Connect(); Log.Information("Connected to Hawkeye camera at {Adapter}", _settings.Adapter, 27001); } private void _client_SettingsReceived(CameraSettings obj) { ApplySettings(obj); } public void ApplySettings(CameraSettings obj) { _cameraSettings = UICameraSettings.LoadSettingsLocal(_settings.SettingsFile).ToCameraSettings(); _client.ApplySettings(_cameraSettings); } public Task GetImage(CancellationToken token) { _lastImage = null; if (_imageAquisitionTaskSource != null && !_imageAquisitionTaskSource.Task.IsCanceled) { _imageAquisitionTaskSource.SetCanceled(CancellationToken.None); _imageAquisitionTaskSource = null; } _imageAquisitionTaskSource= new TaskCompletionSource(); _client.Trigger(); return _imageAquisitionTaskSource.Task; } private TaskCompletionSource? _imageAquisitionTaskSource; private Mat _lastImage; byte[] _flipBuffer = new byte[2000 * 2000]; private void _client_ImageReceived(byte[] obj) { Log.Debug("Got image on {adapter}", _settings.Adapter); FlipLines(obj, _flipBuffer); obj = _flipBuffer; var pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned); var pointer = pinnedArray.AddrOfPinnedObject(); Mat image = new Mat(_cameraSettings.ImageSettings.Lines, _cameraSettings.ImageSettings.SensorWidth, MatType.CV_8UC1, pointer); var xCrop = _cameraSettings.ImageSettings.SensorWidth - _cameraSettings.ImageWidth - _cameraSettings.OffsetX; // crop the image with opencv _lastImage = image[0, _cameraSettings.ImageSettings.Lines, xCrop, xCrop + _cameraSettings.ImageWidth].Clone(); if (_cameraSettings.BayerFilter) { _lastImage = BayerFilter(_lastImage); } _imageAquisitionTaskSource!.SetResult(_lastImage); pinnedArray.Free(); } Mat BayerFilter(Mat image) { // Assumes input is a single-channel Bayer pattern image (CV_8UC1) // Output is a 3-channel BGR image (CV_8UC3) if (image == null || image.Empty()) throw new ArgumentException("Input image is null or empty.", nameof(image)); Mat bgrImage = new Mat(); // Use OpenCV's demosaicing function for Bayer BG pattern Cv2.CvtColor(image, bgrImage, ColorConversionCodes.BayerBG2BGR); return bgrImage; } protected void FlipLines(byte[] src, byte[] dst) { int srcStride = _cameraSettings.ImageSettings.SensorWidth; int dstStride = _cameraSettings.ImageSettings.SensorWidth; int pixelSize = 1; int copySize = srcStride * pixelSize; var lines = _cameraSettings.ImageSettings.Lines; for (int line = 0; line < lines; line += 4) { for (int i = 0; i < 4; i++) { int srcPos = (line + i) * srcStride; int dstPos = (line + (4 - i)) * dstStride; for (int j = 0; j < copySize; j++) { dst[dstPos + j] = src[srcPos + j]; } } } } public void InitializeModule() { } } }