OpenCV从字节数组创建Mat
在我的C ++ dll中,我从字节数组创建Mat:
In my C++ dll I am creating Mat from byte array:
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.
这是从字节数组创建Mat的正确方法吗?
Is this the proper way of creating Mat from byte array?
请参见代码
ptrImageData从C#代码传递到C ++ dll.
ptrImageData is passed to the C++ dll from C# code.
用于传递图像数据的C#代码
C# code to pass the image data
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);
C ++代码似乎正常,因为假定缓冲区在常规格式中,这会为提供的图像数据创建一个矩阵 wrapper RGB8格式.请注意,此构造函数不会不复制缓冲区,因此该缓冲区必须在此Mat
实例的持续时间内保持有效(或被复制).
The C++ code appears ok, in that this creates a matrix wrapper for the supplied image data, assuming the buffer is in the conventional RGB8 format. Note that this constructor does not copy the buffer, so the buffer must remain valid for the duration of this Mat
instance (or be copied).
Mat newImg = Mat(nImageHeight, nImageWidth, CV_8UC3, ptrImageData);
问题似乎出在您的C#代码中.我不是C#开发人员,但我会尽力提供帮助.您正在创建一个内存流,并使用JPEG编解码器将图像的压缩版本写入缓冲区,就好像它是一个文件一样.但这不是cv::Mat
期望的数据格式,因此您基本上会看到垃圾(压缩数据被解释为未压缩).
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 not the data format that cv::Mat
is expecting, so you will basically see garbage (compressed data interpreted as uncompressed).
给出一个System.Image.Drawing.Image
实例,您可以直接创建一个包装器Bitmap
对象(或者可以使用as
,因为它是一个简单的向下转换).然后,您可以使用 Bitmap.LockBits()
方法tog获得指向基础图像数据的指针.
Given a System.Image.Drawing.Image
instance, you can create a wrapper Bitmap
object 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.
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.
and then you can pass the rgbBuffer
to OpenCV.
我也不相信原始代码中的内存管理也是完全正确的,但是无论如何,只要缓冲区所有权的范围在lock和unlock方法调用之内,上述方法就可以工作.如果图像数据要超出此代码块的寿命,则必须复制缓冲区.
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.
也要小心像素格式-您需要确保Image/Bitmap
实例确实包含RGB8数据. OpenCV的cv::Mat
具有各种标志,因此您可以使用多种内存图像格式.但是请注意,它们与磁盘上(通常是压缩的)格式(例如PNG,TIFF等)不同.
Be careful with your pixel formats too - you need to make sure the Image/Bitmap
instance really contains RGB8 data. OpenCV's cv::Mat
has various flags so you can work with a variety of in-memory image formats. But note that these are not the same as the on-disk (typically compressed) formats, such as PNG, TIFF, and so forth.