将位图从 c# 传递到 c++

本文关键字:c++ 位图 | 更新日期: 2023-09-27 18:34:06

我有一个基于opencv用C++编写的图像处理函数。在我的 wpf 应用程序中,我使用 AForge 库访问网络摄像头并在 UI 上更新它。这是处理新帧的函数。

void UI_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        try
        {
            System.Drawing.Bitmap bitmapFrame = (Bitmap)eventArgs.Frame.Clone();
            MemoryStream ms = new MemoryStream();
            bitmapFrame.Save(ms, ImageFormat.Bmp);
            ms.Seek(0, SeekOrigin.Begin);
            BitmapImage bitmapImageFrame = new BitmapImage();
            bitmapImageFrame.BeginInit();
            bitmapImageFrame.StreamSource = ms;
            bitmapImageFrame.EndInit();
            bitmapImageFrame.Freeze();
            CurrentFrame = bitmapImageFrame;
        }
        catch (Exception ex)
        {
            Debug.WriteLine("fatal::" + ex.Message);
        }
    }

这是我的 c++ 代码:

void FaceTracker(unsigned char* imageBuffer, int width, int height){    
   Mat frame(Size(width, height), CV_8UC4, imageBuffer, Mat::AUTO_STEP);
   Mat gray;
   cvtColor(frame, gray, COLOR_BGR2GRAY);
   //do more operations
}

我有两个选择。我已经找到了使用 CopyPixels 将数据从BitmapImage复制到IntPtr缓冲区的代码。我测试了它,它工作正常。但我也读到我可以简单地使用 GetHBitmap 将句柄传递给位图。但是这对我不起作用。我读了这篇文章如何将 .NET 位图传递给本机 DLL?但我不明白关于GetDIBits的部分.我还尝试锁定位图的像素并像这样传递:

bitmapFrame.LockBits(new Rectangle(0, 0, bitmapFrame.Width, bitmapFrame.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, 
            System.Drawing.Imaging.PixelFormat.Format24bppRgb);
NativeFunctions.FaceTracker(bitmapFrame.GetHbitmap(), bitmapFrame.Width, bitmapFrame.Height);

简单地传递HBitMap,两者都不起作用。我总是收到一个异常,指出内存访问冲突。

将位图从 c# 传递到 c++

取决于范围。如果可以保证位图未在其他位置使用,则可以锁定图像缓冲区,然后将指针向下传递到C++代码,然后解锁它。

LockBits 命令返回一个 BitmapData 类,该类在其 Scan0 属性中具有指向图像缓冲区的指针:

BitmapData bmpData = bitmapFrame.LockBits(new Rectangle(0, 0, bitmapFrame.Width, bitmapFrame.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, 
            System.Drawing.Imaging.PixelFormat.Format24bppRgb);
NativeFunctions.FaceTracker(bmpData.Scan0 , bitmapFrame.Width, bitmapFrame.Height);
bitmapFrame.UnlockBits(bmpData); //Remember to unlock!!!
<</div> div class="answers">

HBITMAP是位图的不透明句柄。它不是像素缓冲区。所以你的两段代码不匹配。

如果将HBITMAP传递给本机代码,则需要本机代码使用 GDI 函数获取像素缓冲区并对其进行操作。或者,您可以在托管代码中获取像素缓冲区,并将其传递给本机代码。无论您采用哪种方式,您都需要匹配互操作的两侧。

您可以使用

Bitmap.Save()方法将其转换为内存流,然后将其发送到C++ dll

public Image ConvertImage(Image image)
{
   MemoryStream convertedImageMemoryStream;
   using (MemoryStream sourceImageStream = new MemoryStream())
   {
       image.Save(sourceImageStream, System.Drawing.Imaging.ImageFormat.Png);
       byte[] sourceImageData = sourceImageStream.ToArray();
       // Send it to dll and get the IntPtr byte array from dll
       byte[] imageData = new byte[imInfo.size];
       Marshal.Copy(imInfo.data, imageData, 0, imInfo.size);
       if (imInfo.data != IntPtr.Zero)
           AlgorithmCpp.ReleaseMemoryFromC(imInfo.data);
       convertedImageMemoryStream = new MemoryStream(imageData);
    }
    Image processed = new Bitmap(convertedImageMemoryStream);
    return processed;
}

然后,在C++ dll使用解码等方法获取图像cv::imdecode()

DllExport void convertToGray(unsigned char* data, int dataLen)
{
    vector<unsigned char> inputImageBytes(data, data + dataLen);
    Mat image = imdecode(inputImageBytes, CV_LOAD_IMAGE_COLOR);
    Mat processed;
    cvtColor(image, processed, CV_BGR2GRAY);
    vector<unsigned char> bytes;
    imencode(".png", processed, bytes);
    // ....

这种方式有一些优点,例如, 1. fewer data to transfer 2. minimum implementation effort from both C# and C++ end. 3. Does not depend on the source image type eg. color or grayscale or any other color palate type. So it's very safe to use.

唯一的问题是它是CPU密集型的,因为有编码和解码。

详情请点击此处。

我可以建议你使用EmguCV。它是 C# 中 openCV 的包装器。您不必担心dll,lib等。我可以看到您想从网络摄像头跟踪人脸。EmguCV使您可以捕获网络摄像头图像,然后以非常简单的方式跟踪面部: http://www.emgu.com/wiki/index.php/Face_detection 此链接也应该对您有所帮助。享受。