将位图从 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++代码,然后解锁它。
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 此链接也应该对您有所帮助。享受。