Converting c++ to c# code for image color matching script (OpenCV and EmguCV)

  c++, code-conversion, emgucv, opencv

I’m trying to create a script to match the colours between two images using EmguCV.

I’ve managed to find code that does exactly what I want to, here, however it’s written in C++ which I’m not very familiar with.

I’m sure a lot of these things are basic C++ -> C# issues even if you’re not familiar with EmguCV / OpenCV…

So far I’m stumped on the following (see code below).

  1. ‘mask(p)’ – mask is of type Mat, and this produces an error in C#: ‘a method name is expected’. I presume that the code is trying to index the mask but not sure how to do this. There are quite a few of these instances in the code.
  2. ‘chns[i]’ – probably similar to the above, chns is again of type Mat, and this produces the error "Cannot apply indexing with [] to an expression of type Mat".
  3. With ‘mask(p)’ above, and the other various instances, once the issue above is corrected I suspect that there will be another issue in comparing the Mat with an integer to iterate through – perhaps this is looking at the columns or the rows, I’m not sure. (I’m referring to, for example ‘if (mask(p) > 0)’ )
  4. With ‘CvInvoke.Split(src, chns)’ creates an error ‘Cannot convert from ‘System.Collections.Generic.List<Emgu.CV.Mat> to Emgu.CV.IOutputArray’. I assume I need to define chns and chns1 to an IOutputArray – although I’m not sure how to do this – declaring an IOutputArray using new OutputArray requires an IntPtr reference (possibly ‘new Mat()’? and a parent – not quite sure what’s required here. lease see below.

I’ve run the original C++ code through a C++ to C# converter to get rid of the obvious issues, and have made as many changes as I can to convert OpenCV calls to EmguCV, and I’m left with the below. Any help in deciphering the remaining parts would be most gratefully received.

Further assumptions that I’ve applied:

  1. In referencing ‘Mat’ in the method names, I don’t think C# requires you to specify the depth as you do in C++, so I’ve removed the and references, but instead updated the DepthTypes when the mats are declared, e.g. ‘new Mat(1, 256, DepthType.Cv64F, 1);’ (in the case of double)
  2. Updated variable types, e.g. ‘uchar’ -> ‘byte’
  3. Other conversions have been annoted, apart from obvious OpenCV -> EmguCV conversions that are clearly correct.

Code I’ve got to so far:

public static class EmguCVColourMatchingHelper
{

    // Compute histogram and CDF for an image with mask
    // C++: void do1ChnHist(const Mat_<uchar> &img, const Mat_<uchar> &mask, Mat_<double> &h, Mat_<double> &cdf)
    public static void do1ChnHist(Mat img, Mat mask, Mat h, Mat cdf)
    {
        // C++: for (size_t p = 0; p<img.total(); p++)
        for (var p = 0; p < (Int32)img.Total; p++)
        {
            if (mask(p) > 0)        // ERROR (Issue 1): 'Mat mask - Method name expected' - happens with all Mat types followed by ().
            {
                byte c = img(p);    // ERROR (Issue 1)
                h(c) += 1.0;        // ERROR (Issue 1)
            }
        }

        CvInvoke.Normalize(h, h, 1, 0, NormType.MinMax);

        cdf(0) = h(0);                      // ERROR (Issue 1)
        for (int j = 1; j < 256; j++)
        {
            cdf(j) = cdf(j - 1) + h(j);     // ERROR (Issue 1)
        }

        CvInvoke.Normalize(cdf, cdf, 1, 0, NormType.MinMax);
    }

    public static void histMatchRGB(Mat src, Mat src_mask, Mat dst, Mat dst_mask)
    {
        double histmatch_epsilon = 0.000001;

        // C++: vector<Mat_<uchar>> chns, chns1;
        //  List<Mat> chns = new List<Mat>(); - this is the main way to convert vector<Mat> chns, chns1 - however itn's not compatible with CvInvoke.Split below
        //  I think I need to declare an IOutputArray below, but not exactly sure how to do this.
        IOutputArray chns = new OutputArray(new Mat(), something??);    // Issue 4.

        CvInvoke.Split(src, chns);
        CvInvoke.Split(dst, chns1);

        for (int i = 0; i < 3; i++)
        {
            // C++: Mat_<double> src_hist = Mat_<double>::zeros(1, 256); etc...
            //     NOTE: here I've assumed 1 channel (last '1' reference in new statements below), as I think we're iterating through RGB
            Mat src_hist = new Mat(1, 256, DepthType.Cv64F, 1);
            Mat dst_hist = new Mat(1, 256, DepthType.Cv64F, 1);
            Mat src_cdf = new Mat(1, 256, DepthType.Cv64F, 1);
            Mat dst_cdf = new Mat(1, 256, DepthType.Cv64F, 1);

            do1ChnHist(chns[i], src_mask, src_hist, src_cdf);   // ERROR (Issue 2): Cannot apply indexing with [] to an expression of type 'Mat'
            do1ChnHist(chns1[i], dst_mask, dst_hist, dst_cdf);  // ERROR(Issue 2)

            byte last = 0;

            Mat lut = new Mat(1, 256, DepthType.Cv8U, 1);
            for (int j = 0; j < src_cdf.Cols; j++)
            {
                double F1j = src_cdf(j);                    // ERROR (Issue 1)

                for (byte k = last; k < dst_cdf.Cols; k++)
                {
                    double F2k = dst_cdf(k);                // ERROR (Issue 1)

                    if (Math.Abs(F2k - F1j) < histmatch_epsilon || F2k > F1j)
                    {
                        lut(j) = k;                         // ERROR (Issue 1)
                        last = k;                   
                        break;
                    }
                }
            }
            
            CvInvoke.LUT(chns[i], lut, chns[i]);            //ERROR(Issue 2)
        }

        Mat res = new Mat();
        CvInvoke.Merge(chns, res);

        res.CopyTo(src);
    }

    internal static void Main(string[] args)
    {
        Mat src = CvInvoke.Imread("e:/test/iO6S1m.png");
        Mat dst = CvInvoke.Imread("e:/test/kfku3m.png");
        Mat mask = new Mat(src.Size, DepthType.Cv8U, 255);

        histMatchRGB(dst, mask, src, mask);
    }
}

Thanks for any help!

Source: Windows Questions C++

LEAVE A COMMENT