OpenCV从字节数组创建Mat

时间:2012-11-28 06:50:03

标签: c# c++ opencv

在我的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);

3 个答案:

答案 0 :(得分:1)

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。

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

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

答案 1 :(得分:0)

是的,这是从字节数组创建Mat的一种方法。你必须要小心你的数组包含你的想法。

  

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

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

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

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

答案 2 :(得分:0)

以下是docs:http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-mat

的链接

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

  1. 当您将外部数据传递到矩阵构造函数时,外部数据不会自动释放,因此您应该处理它。如果您希望OpenCV矩阵关注内存,那么您应该复制矩阵(您可以通过多种方式执行此操作,例如使用Mat::cloneMat::copyTo方法。
  2. 外部数据可能不是连续的,即行的大小可能大于宽度乘以通道数乘以数据元素的大小。因此,您可能需要将“step”指定为构造函数的最后一个参数。如果您手动分配外部数据并且100%确定它是连续的,那么您可能无法通过步骤并依赖于自动步骤计算。
  3. 我不熟悉C#,但在我看来,您在ProcessImage调用后立即发布数据。因此,如果ProcessImage是异步的或以某种方式缓存您的矩阵(即矩阵的生命周期比ProcessImage调用的更长),那么您应该关心内存管理。