kdbanman kdbanman - 1 month ago 131
C# Question

How can I get and set pixel values of an EmguCV Mat image?

I'm using the EmguCV 3.0.0 wrapper to the OpenCV 3.0 library. I'm using the

Mat
class in a few places. Here's an example of a single channel, 8x8 image made of
double
values:

Mat image = new Mat(8, 8, DepthType.Cv64F, 1);


The
Image<>
class provides reasonable means for getting and setting pixel values, and the method is identical for the
Matrix<>
class, but it doesn't seem as obvious for the
Mat
class. The only way I've figured out how to set individual pixel is using a mask:

// set two pixel values, (0,0) to 9.0, (2, 3) to 42.0

Matrix<byte> mask = new Matrix<byte>(8,8);
mask.Data[0, 0] = 1;
image.SetTo(new MCvScalar(9.0), mask);

mask = new Matrix<byte>(8,8);
mask.Data[2, 3] = 1;
image.SetTo(new MCvScalar(42.0), mask);


This is feels like it should be two lines, not six, so I feel like I'm missing something. Things get even more complicated when the
Mat
is more than one channel, because
Matrix<>
is only 2D, so the mask must be used to set the pixel on each channel.

I cannot afford the time or memory to set pixels this way. How can I set pixels with a single method call?

Answer

You can get elements from Mat by copying unmanaged memory blocks using DataPointer and converting managed to unmanaged types. Setting values is marshaling in the opposite direction.

For an example you can use such an extension class

public static class MatExtension
{
    public static dynamic GetValue(this Mat mat, int row, int col)
    {
        var value = CreateElement(mat.Depth);
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
        return value[0];
    }

    public static void SetValue(this Mat mat, int row, int col, dynamic value)
    {
        var target = CreateElement(mat.Depth, value);
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
    }
    private static dynamic CreateElement(DepthType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }

    private static dynamic CreateElement(DepthType depthType)
    {
        if (depthType == DepthType.Cv8S)
        {
            return new sbyte[1];
        }
        if (depthType == DepthType.Cv8U)
        {
            return new byte[1];
        }
        if (depthType == DepthType.Cv16S)
        {
            return new short[1];
        }
        if (depthType == DepthType.Cv16U)
        {
            return new ushort[1];
        }
        if (depthType == DepthType.Cv32S)
        {
            return new int[1];
        }
        if (depthType == DepthType.Cv32F)
        {
            return new float[1];
        }
        if (depthType == DepthType.Cv64F)
        {
            return new double[1];
        }
        return new float[1];
    }
}

Then getting and setting value is possible by single method call

var row = 2;
var col = 1;
var mat = new Mat(3, 3, DepthType.Cv64F, 3);
mat.SetValue(row, col, 3.14);
var value = mat.GetValue(row, col);

Tests with 200000000 operations shows that dynamic type version can be up to ~2.5x slower than static.

    public static double GetDoubleValue(this Mat mat, int row, int col)
    {
        var value = new double[1];
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 1);
        return value[0];
    }

    public static void SetDoubleValue(this Mat mat, int row, int col, double value)
    {
        var target = new[] { value };
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 1);
    }