C# OpenCV 从字节数组创建 Mat

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13599395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:08:18  来源:igfitidea点击:

OpenCV create Mat from byte array

c#c++opencv

提问by user1858884

In my C++ dll I am creating Mat from byte array:

在我的 C++ dll 中,我从字节数组创建 Mat:

BYTE * ptrImageData;  //Image data is in this array passed to this function

Mat newImg = Mat(nImageHeight, nImageWidth, CV_8UC3, ptrImageData);

The image is created with some gray shade not the original one.

图像是用一些灰色阴影创建的,而不是原始阴影。

Is this the proper way of creating Mat from byte array?

这是从字节数组创建 Mat 的正确方法吗?

Please see code

请看代码

ptrImageData is passed to the C++ dll from C# code.

ptrImageData 从 C# 代码传递到 C++ dll。

C# code to pass the image data

传递图像数据的C#代码

System.Drawing.Image srcImage //Has the image
MemoryStream ms = new MemoryStream(); 
Marshal.FreeHGlobal(ptrImageData); 
srcImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); 
byte[] imgArray = ms.ToArray();
ms.Dispose();


int size1 = Marshal.SizeOf(imgArray[0]) * imgArray.Length;
IntPtr ptrImageData = Marshal.AllocHGlobal(size1);
Marshal.Copy(imgArray, 0, ptrImageData, imgArray.Length);

//Calling C++ dll function
ProcessImage(ptrImageData, srcImage.Width, srcImage.Height);

Marshal.FreeHGlobal(ptrImageData);

回答by M Miller

Yes, this is one way to create a Mat from a byte array. You just have to be careful that your array contains what you think it does.

是的,这是从字节数组创建 Mat 的一种方法。你只需要注意你的数组包含你认为它做什么。

The image is created with some gray shade not the original one.

图像是用一些灰色阴影创建的,而不是原始阴影。

So you are getting an image in newImg? What was the pixel format of the original data?

所以你在 newImg 中得到一个图像?原始数据的像素格式是什么?

Maybe you've switched the red and blue channels. The following line will swap the channels:

也许您已经切换了红色和蓝色通道。以下行将交换频道:

cv::cvtColor(newImg,swappedImg,CV_RGB2BGR);

回答by Vlad

Here is link to docs: http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-mat

这是文档的链接:http: //docs.opencv.org/modules/core/doc/basic_structures.html#mat-mat

In general you should take care about two things:

一般来说,你应该注意两件事:

  1. When you pass external data into matrix constructor, the external data is not automatically deallocated, so you should take care of it. If you want OpenCV matrix to care about memory, then you should copy matrix (you can do it in many ways, e.g. using Mat::cloneor Mat::copyTomethods.
  2. External data may not be continuous, i.e. size of row may be bigger than width multiplied by number of channels multiplied by size of data element. So you may want specify "step" as last argument of constructor. If you allocate external data manually and 100% sure that it is continuous, then you may not pass step and rely on automatic step calculation.
  1. 当您将外部数据传递给矩阵构造函数时,外部数据不会自动释放,因此您应该注意它。如果您希望 OpenCV 矩阵关心内存,那么您应该复制矩阵(您可以通过多种方式进行复制,例如使用Mat::cloneMat::copyTo方法。
  2. 外部数据可能不连续,即行的大小可能大于宽度乘以通道数乘以数据元素大小。因此,您可能希望将“step”指定为构造函数的最后一个参数。如果您手动分配外部数据并且 100% 确定它是连续的,那么您可能无法通过步骤并依赖自动步骤计算。

I am not familiar with C#, but it seems to me that you release data right after ProcessImage call. So if ProcessImage is asynchronous or somehow caches your matrix (i.e. lifetime of matrix is longer that ProcessImage call), then you should care about memory management.

我不熟悉 C#,但在我看来,您在 ProcessImage 调用后立即发布数据。因此,如果 ProcessImage 是异步的或以某种方式缓存您的矩阵(即矩阵的生命周期比 ProcessImage 调用更长),那么您应该关心内存管理。

回答by gavinb

The C++ code appears ok, in that this creates a matrix wrapperfor the supplied image data, assuming the buffer is in the conventional RGB8 format. Note that this constructor does notcopy the buffer, so the buffer must remain valid for the duration of this Matinstance (or be copied).

C++ 代码看起来没问题,因为它为提供的图像数据创建了一个矩阵包装器,假设缓冲区是传统的 RGB8 格式。请注意,此构造函数复制缓冲区,因此缓冲区必须在此Mat实例的持续时间内保持有效(或被复制)。

Mat newImg = Mat(nImageHeight, nImageWidth, CV_8UC3, ptrImageData);

It appears the problem lies in Your C# code. I am not a C# developer, but I will do my best to help. You are creating a memory stream and using the JPEG codec to write a compressed version of the image into the buffer as if it were a file. But that is notthe data format that cv::Matis expecting, so you will basically see garbage (compressed data interpreted as uncompressed).

看来问题出在您的 C# 代码中。我不是 C# 开发人员,但我会尽力提供帮助。您正在创建一个内存流并使用 JPEG 编解码器将图像的压缩版本写入缓冲区,就像它是一个文件一样。但这不是cv::Mat预期的数据格式,因此您基本上会看到垃圾(压缩数据解释为未压缩)。

Given a System.Image.Drawing.Imageinstance, you can create a wrapper Bitmapobject directly (or maybe use as, since it is a simple downcast). Then you can just use the Bitmap.LockBits()method tog obtain a pointer to the underlying image data.

给定一个System.Image.Drawing.Image实例,您可以Bitmap直接创建一个包装对象(或者可能使用as,因为它是一个简单的向下转换)。然后你可以使用Bitmap.LockBits()方法 tog 获取指向底层图像数据的指针。

Bitmap bmp = new Bitmap(sourceImage);

// Lock the bitmap's bits.  
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
    bmp.PixelFormat);

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap.
int bytes  = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbBuffer = new byte[bytes];

// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbBuffer, 0, bytes);

// Do your OpenCV processing...
// ...

// Unlock the bits.
bmp.UnlockBits(bmpData);

and then you can pass the rgbBufferto OpenCV.

然后你可以将它传递rgbBuffer给 OpenCV。

I'm not convinced that the memory management in the original code is entirely correct either, but anyway the above will work provided the scope of the buffer ownership is within the lock and unlock method calls. If the image data is to outlive this code block, you will have to copy the buffer.

我也不相信原始代码中的内存管理是完全正确的,但无论如何,只要缓冲区所有权的范围在 lock 和 unlock 方法调用内,上述内容就会起作用。如果图像数据比此代码块的寿命长,则必须复制缓冲区。

Be careful with your pixel formats too - you need to make sure the Image/Bitmapinstance really contains RGB8 data. OpenCV's cv::Mathas various flags so you can work with a variety of in-memory image formats. But note that these are notthe same as the on-disk (typically compressed) formats, such as PNG, TIFF, and so forth.

也要小心您的像素格式 - 您需要确保Image/Bitmap实例确实包含 RGB8 数据。OpenCVcv::Mat具有各种标志,因此您可以使用各种内存中的图像格式。但需要注意的是,这些都是一样的磁盘上(通常经过压缩)格式,如PNG,TIFF,等等。