简体   繁体   English

如何获取和设置 EmguCV Mat 图像的像素值?

[英]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.我将 EmguCV 3.0.0 包装器用于 OpenCV 3.0 库。 I'm using the Mat class in a few places.我在几个地方使用Mat class。 Here's an example of a single channel, 8x8 image made of double values:下面是一个由double值组成的单通道 8x8 图像示例:

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: Image<> class 提供了获取和设置像素值的合理方法Matrix<> class 的方法相同,但Mat class 的方法似乎并不明显。我想出的唯一方法是设置单个像素使用遮罩:

// 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.Mat不止一个通道时,事情会变得更加复杂,因为Matrix<>只是二维的,所以必须使用遮罩来设置每个通道上的像素。

I cannot afford the time or memory to set pixels this way.我没有时间或 memory 以这种方式设置像素。 How can I set pixels with a single method call?如何使用单个方法调用设置像素?

You can get elements from Mat by copying unmanaged memory blocks using DataPointer and converting managed to unmanaged types.您可以通过使用 DataPointer 复制非托管内存块并将托管类型转换为非托管类型来从 Mat 获取元素。 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.对 200000000 次操作的测试表明,动态类型版本可能比静态版本慢约 2.5 倍。

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);
}

Based on the Bartosz Rachwal's great answer, I've tried to write it for OpenCvSharp:基于 Bartosz Rachwal 的精彩回答,我尝试为 OpenCvSharp 编写它:

    public static dynamic GetValue(this Mat mat, int row, int col)
    {
        var value = CreateElement(mat.Type());
        Marshal.Copy(mat.Data + (row * mat.Cols + col) * mat.ElemSize(), value, 0, 1);
        return value[0];
    }
    public static void SetValue(this Mat mat, int row, int col, dynamic value)
    {
        var target = CreateElement(mat.Type(), value);
        Marshal.Copy(target, 0, mat.Data + (row * mat.Cols + col) * mat.ElemSize(), 1);
    }
    private static dynamic CreateElement(MatType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }
    private static dynamic CreateElement(MatType depthType)
    {
        switch (depthType)
        {
            case MatType.CV_8S:
                return new sbyte[1];
            case MatType.CV_8U:
                return new byte[1];
            case MatType.CV_16S:
                return new short[1];
            case MatType.CV_16U:
                return new ushort[1];
            case MatType.CV_32S:
                return new int[1];
            case MatType.CV_32F:
                return new float[1];
            case MatType.CV_64F:
                return new double[1];
            default:
                throw new NotImplementedException();
        }
    }

A better one.一个更好的。

  • Tick "Allow unsafe code" in the project's Debug and Release configurations.在项目的调试和发布配置中勾选“允许不安全代码”。
  • Code:代码:
public static class MatExtension
{
        public static T Get<T>(this Mat mat, int row, int col)
        {
            unsafe
            {
                var span = new ReadOnlySpan<T>(mat.DataPointer.ToPointer(), mat.Rows * mat.Cols * mat.ElementSize);
                return span[row * mat.Cols + col];
            }
        }

        public static ReadOnlySpan<T> Get<T>(this Mat mat, int row, Range cols)
        {
            unsafe
            {
                var span = new ReadOnlySpan<T>(mat.DataPointer.ToPointer(), mat.Rows * mat.Cols * mat.ElementSize);
                var colOffsets = cols.GetOffsetAndLength(span.Length);
                return span.Slice(row * mat.Cols + colOffsets.Offset, colOffsets.Length);
            }
        }
}

Usage:用法:

using var stats = new Mat();
using var centroids = new Mat();
//...
var x = stats.Get<int>(i,(int)ConnectedComponentsTypes.Left);
var cxy = centroids.Get<double>(i, 0..1);
double cxy0 = cxy[0];
//...

This solution https://stackoverflow.com/a/32559496/15221325 for three color channels as requested by the user Quergo:此解决方案https://stackoverflow.com/a/32559496/15221325应用户 Quergo 的要求用于三个颜色通道:

At first, nice answer, helped me a lot.起初,很好的答案,对我帮助很大。 But I do not understand how this method deals with multiple channels.但我不明白这种方法如何处理多个渠道。 In your example you wrote var mat = new Mat(3, 3, DepthType.Cv64F, 3);在您的示例中,您编写了 var mat = new Mat(3, 3, DepthType.Cv64F, 3); shouldn't it return a double[] of size 3 containing a value for each channel?它不应该返回一个大小为 3 的 double[] ,其中包含每个通道的值吗? – Quergo Nov 18 '19 at 22:30 – Quergo 19 年 11 月 18 日,22:30

public static class MatExtension
{
    public static dynamic GetValues(this Mat mat, int row, int col)
    {
        var value = CreateElement3Channels(mat.Depth);
        Marshal.Copy(mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, value, 0, 3);
        return value;
    }

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

    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 SetValues(this Mat mat, int row, int col, dynamic value)
    {
        Marshal.Copy(value, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 3);
    }

    public static void SetValue(this Mat mat, int channel, int row, int col, dynamic value)
    {
        var element = GetValues(mat, row, col);
        var target = CreateElement(element, value, channel);
        Marshal.Copy(target, 0, mat.DataPointer + (row * mat.Cols + col) * mat.ElementSize, 3);
    }

    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(dynamic element, dynamic value, int channel)
    {
        element[channel] = value;
        return element;
    }

    private static dynamic CreateElement(DepthType depthType, dynamic value)
    {
        var element = CreateElement(depthType);
        element[0] = value;
        return element;
    }

    private static dynamic CreateElement3Channels(DepthType depthType)
    {
        if (depthType == DepthType.Cv8S)
        {
            return new sbyte[3];
        }

        if (depthType == DepthType.Cv8U)
        {
            return new byte[3];
        }

        if (depthType == DepthType.Cv16S)
        {
            return new short[3];
        }

        if (depthType == DepthType.Cv16U)
        {
            return new ushort[3];
        }

        if (depthType == DepthType.Cv32S)
        {
            return new int[3];
        }

        if (depthType == DepthType.Cv32F)
        {
            return new float[3];
        }

        if (depthType == DepthType.Cv64F)
        {
            return new double[3];
        }

        return new float[3];
    }

    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];
    }
}

you can easily get and set the pixel value of the Mat image in two ways:您可以通过两种方式轻松获取和设置 Mat 图像的像素值:

1- convert Mat image to Image<ColorType, DDepth> 1- 将 Mat 图像转换为 Image<ColorType, DDepth>
2- use SetValue and GetValue directly from the Mat class. 2- 直接从 Mat class 使用 SetValue 和 GetValue。

1- convert Mat image to Image<> Format: 1- 将 Mat 图像转换为 Image<> 格式:

int row = 0;
int col = 1;
int channel = 6;
Mat Image = CvInvoke.Imread("path");
Image<Gray, Byte> ImageFormat = Image.ToImage<Gray, Byte>();
int pixVal = ImageFormat.Data[row, col, channel];

// for set value 

int numValue = 165;
ImageFormat.Data[row, col, channel] = numValue;

you can access to Mat Format of Image<> with:您可以通过以下方式访问 Image<> 的 Mat 格式:

ImageFormat.Mat;

2- you can set or get pixel values directly with SetValue and GetValue methods (these methods return an object, you have to convert the object to a number): 2- 您可以直接使用 SetValue 和 GetValue 方法设置或获取像素值(这些方法返回 object,您必须将 object 转换为数字):

object pixVal = Image.Data.GetValue(row, col, channel);
float pixValue = Convert.ToInt32(pixVal);

// for set value:

float setPixVal = 159;
Image.Data.SetValue(setPixVal, row, col, channel);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM