【问题标题】:Passing bitmap from c# to c++将位图从 c# 传递到 c++
【发布时间】:2015-02-12 08:36:15
【问题描述】:

我有一个基于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 简单地将句柄传递给位图。但是,这对我不起作用。我读了这个How can I pass a .NET Bitmap to a native 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,两者都不起作用。我总是收到一个异常说明内存访问冲突。

【问题讨论】:

  • 只需使用LockBits,然后传递Scan0GetHbitmapScan0 完全不同。

标签: c# opencv bitmap c++-cli pinvoke


【解决方案1】:

取决于范围。如果您可以保证位图不在其他地方使用,则可以锁定图像缓冲区,然后将指针向下传递给 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!!!

【讨论】:

  • 正是我想要的。正如我所希望的,传递 Bitmap 而不是 BitmapImage 让我的 CPU 使用率显着下降。谢谢你:)
  • 不要忘记传递bmpData.Stride。如果你没有在 C++ 部分指定步长,你会得到像这个问题这样的扭曲图像(在处理彩色图像时):stackoverflow.com/questions/45738718/… 另外你应该使用bitmapFrame..PixelFormat 而不是硬编码。
  • 我找了好几天才找到这个答案,其他人需要支持这个答案!!!它没有给我最终的解决方案,但数据传输是正确的:)
【解决方案2】:

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

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

【讨论】:

  • 我假设GetDIBitsCreateCompatibeDC 是 GDI 函数?我更愿意将我的位图转换为像素缓冲区并传递给本机代码。我该怎么做?
  • LockBits 然后 Scan0
【解决方案3】:

您可以使用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 密集型的,因为有编码和解码。

详情为here

【讨论】:

  • 很高兴它有帮助。 :)
【解决方案4】:

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

【讨论】:

  • 是的,我阅读了有关 EmguCV 的信息。但是我编写自己的代码的原因是因为我是一名刚从大学毕业的开发人员,而且我的职位要求我在未来经常使用互操作。所以我必须学习它所有的复杂性:)
猜你喜欢
  • 1970-01-01
  • 2016-02-22
  • 1970-01-01
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多