2012-11-28 39 views
9

以我C++ DLL我从字节数组创建垫创建垫:OpenCV中从字节数组

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

我认为你的C++代码有一些错误,Mat newImg(...)或Mat * newImg = new Mat(..),你的写作不是C++风格。 – Healer

+0

@healer ..代码是正确的。在上面的代码中,'newImg'正在使用'Mat'类的'explicit'构造函数初始化。 – sgarizvi

+0

请提供更详细的代码,如你如何显示图像,“ptrImageData”的布局是什么。 – luhb

回答

0

是的,这是创建从一个字节数组的垫的一种方式。你只需要小心你的数组包含了你的想法。

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

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

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

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

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

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

  1. 当您通过外部数据导入矩阵构造,外部数据不会自动释放,所以您应该照顾它。如果你想OpenCV矩阵关心内存,那么你应该复制矩阵(你可以用很多方法来完成,例如使用Mat::cloneMat::copyTo方法。
  2. 外部数据可能不连续,即行的大小可能大于宽度乘以通道数乘以数据元素的大小,所以你可能需要指定“step”作为构造函数的最后一个参数,如果你手动分配外部数据并且100%确定它是连续的,那么你可能不会通过step并依靠自动步计算。

我不熟悉C#,但在我看来,你processImage来电话后立即释放的数据。所以,如果processImage来是异步的,或在某种程度上缓存你的矩阵(即矩阵的寿命更长ŧ帽子ProcessImage调用),那么你应该关心内存管理。

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()方法获取指向底层图像数据的指针。您可以将rgbBuffer传递给OpenCV。

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

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

+0

将'srcImage.Save(ms,System.Drawing.Imaging.ImageFormat.Jpeg)'调用为使用'ImageFormat.Bmp'而不是这里建议的复杂转换可能就足够了 – slawekwin