网上查了好多资料,还有查看opencv的文档终于解决了C#传递给C++编写的Opencv库图像传递不正确的问题。 基本思路是C#读取图片转成byte[]数组,然后传递给Opencv,通过Mat (int rows, int cols, int type, void *data, size_t step=AUTO_STEP)
构造函数接收。由于读取的图片格式可能有ARGB和RGB格式,统一转成RGB格式。
讲讲我遇到的各个坑
Bitmap bmpInitial = (Bitmap)System.Drawing.Image.FromFile("1.jpg");
Bitmap bmp = null;
//将图像转换为24位rgb图像
if (bmpInitial.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb)
{
bmp = new Bitmap(bmpInitial.Width, bmpInitial.Height,System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(bmpInitial, new Rectangle(0, 0, bmpInitial.Width, bmpInitial.Height));
}
}
else
{
bmp = bmpInitial;
}
//设置dpi=96
bmp.SetResolution(96, 96);
DrawImage
必须指定图像的宽高,不然图像显示的不全,原因和图像的分辨率和系统分辨率有关。
2. 图像转Byte[]数组
System.Drawing.Rectangle rect = new System.Drawing.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 bytesLength = Math.Abs(bmpData.Stride) * bmp.Height;
int imageWeidth = bmp.Width;
int imageHeight = bmp.Height;
//图像的Stride
int imageStride = bmpData.Stride;
byte[] buffer = new byte[bytesLength];
// Copy the RGB values into the array.
Marshal.Copy(ptr, buffer, 0, bytesLength);
bmp.UnlockBits(bmpData);
这个是msdn里LockBits
的示例,试过网上其他通过MemoryStream
,FileStream
转换,传递的数据都不正确。
3. Mat构造函数Mat (int rows, int cols, int type, void *data, size_t step=AUTO_STEP)
这里需要理解Stride。参数step的含义是Number of bytes each matrix row occupies.
,就我理解就是图像的Stride。涉及到位数对齐的问题。
(1) Stride = 每像素占用的字节数(也就是像素位数/8) * Width;
(2) 如果 Stride 不是 4 的倍数, 那么 Stride = Stride + (4 - Stride mod 4);
step
的默认值是不包含padding位,也就是默认按(1)来处理,这样得到的图像会发生错位,显示不全。
4. Opencv中接收图像的代码Byte[]->Mat
Mat ByteToMat(BYTE* pImg, int nH, int nW,int stride)
{
Mat image = Mat(nH, nW, CV_8UC3, pImg,stride).clone();
return image;
}
关于dll的编写调用,这里提供一下我用的函数原型。
C#中:
[DllImport("VesselExportCp.dll", EntryPoint = "GetResult", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern void GetResult(byte[] bimage, int nH, int nW, int stride, [In, Out] ref StructResult result);
C++中:
extern "C" VES_API void __stdcall GetResult(BYTE* bimage, int nH, int nW, int stride, StructResult* result);