Search code examples
c#c++opencvemgucv

Converting a C++ implementation of Error Level Analysis to C#


I am trying to implement Error Level Analysis in C#. I have found examples in C++ using OpenCV and Python using PIL, but cannot find any in C#, so I am trying to convert C++ code using the OpenCvSharp library.

Here is the example I have used:

// Control
int scale = 15,
quality = 75;

// Image containers
cv::Mat input_image,
compressed_image;

void processImage(int, void*)
{
    // Setting up parameters and JPEG compression
    std::vector<int> parameters;
    parameters.push_back(CV_IMWRITE_JPEG_QUALITY);
    parameters.push_back(quality);
    cv::imwrite("temp.jpg", input_image, parameters);

    // Reading temp image from the disk
    compressed_image = cv::imread("temp.jpg");
 
    if (compressed_image.empty())
    {
        std::cout << "> Error loading temp image" << std::endl;
        exit(EXIT_FAILURE);
    }

    cv::Mat output_image = cv::Mat::zeros(input_image.size(), CV_8UC3);

    // Compare values through matrices
    for (int row = 0; row < input_image.rows; ++row)
    {
        const uchar* ptr_input = input_image.ptr<uchar>(row);
        const uchar* ptr_compressed = compressed_image.ptr<uchar>(row);
        uchar* ptr_out = output_image.ptr<uchar>(row);
   
        for (int column = 0; column < input_image.cols; column++)
        {
            // Calc abs diff for each color channel multiplying by a scale factor
            ptr_out[0] = abs(ptr_input[0] - ptr_compressed[0]) * scale;
            ptr_out[1] = abs(ptr_input[1] - ptr_compressed[1]) * scale;
            ptr_out[2] = abs(ptr_input[2] - ptr_compressed[2]) * scale;

            ptr_input += 3;
            ptr_compressed += 3;
            ptr_out += 3;
        }
    }

    // Shows processed image
    cv::imshow("Error Level Analysis", output_image);
}

Here is my attempt so far:

[STAThread]
static void Main(string[] args)
{
    // Control
    int scale = 15;
    int quality = 75;

    string test_img_path = "";
    // Image container
    OpenFileDialog fdlg = new OpenFileDialog();
    fdlg.Title = "C# Corner Open File Dialog";
    fdlg.InitialDirectory = @"c:\Documents\Pictures";
    fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
    fdlg.FilterIndex = 2;
    fdlg.RestoreDirectory = true;
    if (fdlg.ShowDialog() == DialogResult.OK)
    {
        test_img_path = fdlg.FileName;
    }

    Mat input_image = Cv2.ImRead(test_img_path);
    Mat compressed_image;
    // Setting up parameters and JPEG compression

    int[] Params = new int[2];
    Params.Append(95);
    Params.Append(50);
    Cv2.ImWrite("temp.jpg", input_image, Params);

    // Reading temp image from the disk
    compressed_image = Cv2.ImRead("temp.jpg");

    if (compressed_image.Empty())
    {
        Trace.WriteLine("File empty");
    }

    Mat output_image = Mat.Zeros(input_image.Size(), MatType.CV_8UC3);

    // Compare values through matrices
    for (int row = 0; row < input_image.Rows; ++row)
    {
        unsafe
        {
            char* ptr_input = (char*)input_image.Ptr(row);
            char* ptr_compressed = (char*)compressed_image.Ptr(row);
            char* ptr_out = (char*)output_image.Ptr(row);

            for (int column = 0; column < input_image.Cols; column++)
            {
                // Calc abs diff for each color channel multiplying by a scale factor
                ptr_out[0] = (char)((ptr_input[0] - ptr_compressed[0]) * scale);
                ptr_out[1] = (char)(Math.Abs(ptr_input[1] - ptr_compressed[1]) * scale);
                ptr_out[2] = (char)(Math.Abs(ptr_input[2] - ptr_compressed[2]) * scale);

                ptr_input += 3;
                ptr_compressed += 3;
                ptr_out += 3;
            }
        }
    }

    // Shows processed image
    Cv2.ImShow("Error Level Analysis", output_image);
}

The above will not compile, and leads to the error:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Any help in conversion, or a methodology to do ELA using C#, would be appreciated.


Solution

  • You can use EmguCV instead, which you can install from here. Then you can transform the code to C# like this.

    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using Emgu.CV;
    using Emgu.CV.CvEnum;
    
    class Program
    {
        // Control
        readonly int scale = 15, quality = 75;
    
        // Image containers
        Mat input_image, compressed_image;
    
        void ProcessImage()
        {
            // Setting up parameters and JPEG compression
            KeyValuePair<ImwriteFlags, int> parameters = new KeyValuePair<ImwriteFlags, int>(ImwriteFlags.JpegQuality, quality);
            CvInvoke.Imwrite("temp.jpg", input_image, parameters);
    
            // Reading temp image from the disk
            compressed_image = CvInvoke.Imread("temp.jpg");
    
            if (compressed_image.IsEmpty)
            {
                Console.WriteLine("> Error loading temp image");
                Environment.Exit(1);
            }
    
            Mat output_image = Mat.Zeros(input_image.Rows, input_image.Cols, DepthType.Cv8U, 3);
    
            byte[] input, compressed, output;
            input = new byte[input_image.Rows* input_image.Cols* 3];
            compressed = new byte[input_image.Rows * input_image.Cols * 3];
            output = new byte[input_image.Rows * input_image.Cols * 3];
            input_image.CopyTo(input);
            compressed_image.CopyTo(compressed);
    
            // Compare values through matrices
            for (int row = 0; row < input_image.Rows; ++row)
                for (int column = 0; column < input_image.Cols; column++)
                    for (int channel = 0; channel < 3; channel++)
                        // Calc abs diff for each color channel multiplying by a scale factor
                        output[(row * input_image.Cols + column) * 3 + channel] = (byte)(Math.Abs(input[(row * input_image.Cols + column) * 3 + channel] - compressed[(row * input_image.Cols + column) * 3 + channel]) * scale);
    
            byte[] t = new byte[1];  
            for (int i = 0; i < output.Length; i++)
            {
                t[0] = output[i];
                Marshal.Copy(t, 0, output_image.DataPointer + i, 1);
            }
    
            // Shows processed image
            CvInvoke.Imshow("Error Level Analysis", output_image); 
        }
    }