13

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

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

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

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

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

请看代码

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

传递图像数据的 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);
4

3 回答 3

3

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

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

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

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

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

然后您可以将 传递rgbBuffer给 OpenCV。

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

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

于 2016-09-28T11:39:31.213 回答
0

是的,这是从字节数组创建 Mat 的一种方法。您只需要注意您的数组包含您认为的内容。

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

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

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

cv::cvtColor(newImg,swappedImg,CV_RGB2BGR);
于 2013-03-22T18:23:22.767 回答
0

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

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

  1. 当您将外部数据传递给矩阵构造函数时,外部数据不会自动释放,因此您应该注意它。如果您希望 OpenCV 矩阵关心内存,那么您应该复制矩阵(您可以通过多种方式进行,例如使用Mat::cloneMat::copyTo方法。
  2. 外部数据可能不是连续的,即行的大小可能大于宽度乘以通道数乘以数据元素的大小。因此,您可能希望将“step”指定为构造函数的最后一个参数。如果您手动分配外部数据并且100%确定它是连续的,那么您可能无法通过步骤并依赖自动步骤计算。

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

于 2015-07-24T09:15:39.460 回答