1 | initial version |
I have tested two ways that solve C# (Bitmap) and native C++ (cv::Mat) communication problem. All communication is based by C++ Dll which is used in C# by DllImport-ing (PInvoke). For image information storing I use two similar structures. C++ structure consists char* member for image data and C# structure consists InPtr for same data. C# structure is defined as [StructLayout(LayoutKind.Sequential)]. This "ImgBridge" is main condition for successful communication.
There are two functions for C# that convert C# (Bitmap) to C++ (cv::Mat) and vice versa via mentioned "ImgBridge".:
public static unsafe Bitmap ImgBridge2Bitmap(ImgBridge imgBridge)
{
if ( (0 < imgBridge.mImgWidth)
&& (0 < imgBridge.mImgHeight))
{
PixelFormat pixelFormat = GetPixelFormat(imgBridge.mBitsPerPixel);
if (pixelFormat != PixelFormat.Undefined)
{
Bitmap bitmap = new Bitmap(imgBridge.mImgWidth, imgBridge.mImgHeight, pixelFormat);
if (pixelFormat == PixelFormat.Format8bppIndexed)
{
SetGrayscalePalette(bitmap);
}
BitmapData bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.WriteOnly,
pixelFormat);
CopyImgData(
imgBridge.mBytesPerPixel,
imgBridge.mImgWidth,
imgBridge.mImgHeight,
imgBridge.mBytesPerRow,
imgBridge.mImgData,
bitmapData.Stride,
bitmapData.Scan0);
bitmap.UnlockBits(bitmapData);
return bitmap;
}
}
return null;
}
_
public static unsafe ImgBridge Bitmap2ImgBridge(Bitmap bitmap)
{
ImgBridge imgBridge = new ImgBridge();
if (bitmap != null)
{
int BitsPerPixel = GetBitsPerPixel(bitmap.PixelFormat);
if (BitsPerPixel != -1)
{
BitmapData bitmapData = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly,
bitmap.PixelFormat);
imgBridge.mImgWidth = bitmap.Width;
imgBridge.mImgHeight = bitmap.Height;
imgBridge.mChannelsCount = (BitsPerPixel / 8);
imgBridge.mPixelsCount = (imgBridge.mImgWidth * imgBridge.mImgHeight);
imgBridge.mBytesPerPixel = (BitsPerPixel / 8);
imgBridge.mBitsPerPixel = BitsPerPixel;
imgBridge.mBytesPerRow = (imgBridge.mBytesPerPixel * imgBridge.mImgWidth);
imgBridge.mImgSize = (imgBridge.mPixelsCount * imgBridge.mBytesPerPixel);
imgBridge.mImgData = Marshal.AllocHGlobal(imgBridge.mImgSize);
CopyImgData(
imgBridge.mBytesPerPixel,
bitmap.Width,
bitmap.Height,
bitmapData.Stride,
bitmapData.Scan0,
imgBridge.mBytesPerRow,
imgBridge.mImgData);
bitmap.UnlockBits(bitmapData);
}
}
return imgBridge;
}
Both these function use CopyImgData function.:
public static unsafe void CopyImgData(int PixelSize, int ImgWidth, int ImgHeight,
int SrcImgBytesPerRow, IntPtr SrcImgData,
int DstImgBytesPerRow, IntPtr DstImgData)
{
if ((0 < PixelSize) && (0 < ImgWidth) && (0 < ImgHeight))
{
for (int h = 0; h < ImgHeight; h++)
{
byte* pSrcRow = ((byte*) SrcImgData + (h * SrcImgBytesPerRow));
byte* pDstRow = ((byte*) DstImgData + (h * DstImgBytesPerRow));
for (int w = 0; w < ImgWidth; w++)
{
for (int p = 0; p < PixelSize; p++)
{
pDstRow[w * PixelSize + p] = pSrcRow[w * PixelSize + p];
}
}
}
}
}
At the beginning I had written that I have tested two ways. The second one inspired by GitHub/shimat/opencvsharp project https://github.com/shimat/opencvsharp/blob/1cc16b53d727a0ef6a2f674ea90172fdbf008299/src/OpenCvSharp.Extensions/BitmapConverter_Mat.cs If you would like to try this one you will need to extract CopyMemory function https://github.com/shimat/opencvsharp/blob/1cc16b53d727a0ef6a2f674ea90172fdbf008299/src/OpenCvSharp/Src/Utilities/Util.cs and implement it in your C# code. After this process you have to swap original CopyImgData function by
byte* pSrcImgData = (byte*) imgBridge.mImgData.ToPointer();
byte* pDstImgData = (byte*) bitmapData.Scan0.ToPointer();
int SrcStep = imgBridge.mBytesPerRow;
int DstStep = (((imgBridge.mImgWidth * imgBridge.mChannelsCount) + 3) / 4 * 4);
switch (pixelFormat)
{
case PixelFormat.Format1bppIndexed:
{
// TODO
break;
}
case PixelFormat.Format8bppIndexed:
case PixelFormat.Format24bppRgb:
{
for (int h = 0; h < imgBridge.mImgHeight; h++)
{
long SrcOffset = (h * SrcStep);
long DstOffset = (h * DstStep);
CopyMemory((pDstImgData + DstOffset), (pSrcImgData + SrcOffset), (uint) (imgBridge.mImgWidth * imgBridge.mChannelsCount));
}
break;
}
default:
{
break;
}
}
for ImgBridge2Bitmap and by this
int SrcStep = (((imgBridge.mImgWidth * imgBridge.mBytesPerPixel) + 3) / 4 * 4);
int DstStep = imgBridge.mBytesPerRow;
switch (bitmap.PixelFormat)
{
case PixelFormat.Format1bppIndexed:
{
// TODO
break;
}
case PixelFormat.Format8bppIndexed:
case PixelFormat.Format24bppRgb:
{
byte* pSrcImgData = (byte*) bitmapData.Scan0.ToPointer();
byte* pDstImgData = (byte*) imgBridge.mImgData.ToPointer();
for (int h = 0; h < imgBridge.mImgHeight; h++)
{
CopyMemory(pDstImgData, pSrcImgData, (uint) DstStep);
pSrcImgData += SrcStep;
pDstImgData += DstStep;
}
break;
}
default:
{
break;
}
for Bitmap2ImgBridge