using NLog.Filters; using OpenCvSharp; namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye; public class OpenCVBayerProcessor { private SplineInterpolator GreenInterpolation { get; set; } private SplineInterpolator RedInterpolation { get; set; } private SplineInterpolator BlueInterpolation { get; set; } // Cached lookup tables private byte[] redLut; private byte[] greenLut; private byte[] blueLut; private Mat _lookupTable; public OpenCVBayerProcessor() { // Initialize with default linear interpolation (same as your defaults) GreenInterpolation = new SplineInterpolator(new Dictionary() {{0,0}, { 100, 100 }, { 255,255}}); RedInterpolation = new SplineInterpolator(new Dictionary() {{0,0}, { 100, 100 }, { 255,255}}); BlueInterpolation = new SplineInterpolator(new Dictionary() {{0,0}, { 100, 100 }, { 255,255}}); UpdateLookupTables(); } public void SetInterpolators(SplineInterpolator red, SplineInterpolator green, SplineInterpolator blue) { RedInterpolation = red; GreenInterpolation = green; BlueInterpolation = blue; UpdateLookupTables(); } public void SetCalibration(List> calibration) { RedInterpolation = new SplineInterpolator(calibration[0]); GreenInterpolation = new SplineInterpolator(calibration[1]); BlueInterpolation = new SplineInterpolator(calibration[2]); UpdateLookupTables(); } private void UpdateLookupTables() { redLut = Enumerable.Range(0, 256).Select(x => (byte)RedInterpolation.GetValue(x)).ToArray(); greenLut = Enumerable.Range(0, 256).Select(x => (byte)GreenInterpolation.GetValue(x)).ToArray(); blueLut = Enumerable.Range(0, 256).Select(x => (byte)BlueInterpolation.GetValue(x)).ToArray(); // Create lookup table for all three channels _lookupTable = new Mat(1, 256, MatType.CV_8UC3); // Use the Mat indexer for safe access var indexer = _lookupTable.GetGenericIndexer(); for (int i = 0; i < 256; i++) { indexer[0, i] = new Vec3b(blueLut[i], greenLut[i], redLut[i]); } } public Mat ProcessBayerImage(Mat grayImage) { // Step 1: Apply Bayer demosaicing with RG pattern (matching your pattern) Mat colorImage = new Mat(); Cv2.CvtColor(grayImage, colorImage, ColorConversionCodes.BayerBG2BGR); // Step 2: Apply color interpolation/correction using lookup tables Mat correctedImage = ApplyColorCorrection(colorImage); return correctedImage; } private Mat ApplyColorCorrection(Mat colorImage) { // Apply the lookup table Mat result = new Mat(); Cv2.LUT(colorImage, _lookupTable, result); return result; } // Alternative: Apply correction per channel if you need more control public Mat ProcessBayerImageWithChannelControl(Mat grayImage) { // Step 1: Demosaic Mat colorImage = new Mat(); Cv2.CvtColor(grayImage, colorImage, ColorConversionCodes.BayerBG2BGR); // Step 2: Split channels Mat[] channels = Cv2.Split(colorImage); // Step 3: Apply individual LUTs to each channel Mat blueCorrected = new Mat(); Mat greenCorrected = new Mat(); Mat redCorrected = new Mat(); Mat blueLutMat = Mat.FromPixelData(1, 256, MatType.CV_8U, blueLut); Mat greenLutMat = Mat.FromPixelData(1, 256, MatType.CV_8U, greenLut); Mat redLutMat = Mat.FromPixelData(1, 256, MatType.CV_8U, redLut); Cv2.LUT(channels[0], blueLutMat, blueCorrected); Cv2.LUT(channels[1], greenLutMat, greenCorrected); Cv2.LUT(channels[2], redLutMat, redCorrected); // Step 4: Merge channels back Mat result = new Mat(); Cv2.Merge(new Mat[] { blueCorrected, greenCorrected, redCorrected }, result); // Cleanup foreach (var channel in channels) channel.Dispose(); blueCorrected.Dispose(); greenCorrected.Dispose(); redCorrected.Dispose(); colorImage.Dispose(); return result; } }