Ask Your Question

Revision history [back]

click to hide/show revision 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