2015-07-20 2422 views
1

我编写了这个示例代码来解释我的问题。我在VS 2013中有一个解决方案,包含一个C#项目和一个C++项目。我尝试用C++(x86)中的OpenCV读取图像。并希望将一个C#x86项目(使用CLR模式)传递给位图对象,然后将BitmapImage对象用作WPF图像源。
我的C++代码:将opencv图像cv :: Mat格式转换为C#BitmapImage

Bitmap^ SomeClass::Test(System::String^ imgFileName) 
{ 
    auto fileName = msclr::interop::marshal_as<string>(imgFileName); 
    Mat img = imread(fileName); 
    //Do something 
    auto bmp = gcnew Bitmap(img.cols, img.rows, img.step, Imaging::PixelFormat::Format24bppRgb, (IntPtr)img.data); 
    bmp->Save("InC++Side.png");  
    return bmp; 
} 

我的C#代码:

private void ImageTester(object sender, RoutedEventArgs e) 
{ 
    var image = testClass.Test("test.png"); 
    image.Save("InC#Side.png"); 
    bg.Source = ConvertToBitmapImageFromBitmap(image); 
} 
public static BitmapImage ConvertToBitmapImageFromBitmap(Bitmap image) 
{ 
    using(var ms = new MemoryStream()) 
    { 
     image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); 
     BitmapImage bImg = new BitmapImage(); 
     bImg.BeginInit(); 
     bImg.StreamSource = new MemoryStream(ms.ToArray()); 
     bImg.EndInit(); 
     return bImg; 
    } 
} 

问题是,C++(INC++ Side.png)保存的文件是完美的;但在C#中呈现Bitmap对象的另一个只是带有该图像高度和宽度的灰色矩形。
问题在哪里?
如何将图像传递给我的C#项目?

+0

“将图像传递给C#项目”是什么意思?你是否创建了一个单一的应用程序(即C#项目)并使用OpenCV C++ API来从文件加载图像? – enzom83

+0

@ enzom83:是的。我有一个解决方案,它有一个C#项目在UI上显示图像,使用OpenCV的C++项目。我不使用OpenCV来从原始项目中的文件加载图像;但在这个例子中,是的。我只是加载图像。 – aisa

+0

您可以将'Mat'转换为'byte'数组(参见[本答案](http://answers.opencv.org/question/33596/convert-mat-to-byte-in-c/?answer=33603# post-id-33603)),然后尝试使用[本答案](http://stackoverflow.com/a/10214278)中描述的过程。此外,你是否尝试将'(IntPtr)img.data'更改为'img.data.ptr'? – enzom83

回答

0

看来问题是关于共享内存。
MatBitmap共享内存在C++。所以,当Mat对象销毁时,Bitmap对象无法访问数据。这就是为什么它的数据在C++端是正确的,但在C#端没有任何内容。
为了解决这个问题,我使用了static Mat。它永远不会释放,但可以解决我的问题。

2

我看到这是一个相当古老的问题,但我刚刚遇到了类似的任务。我相信最好将数据复制到托管堆并让CLR处理它,而不是让非托管资源保持活动状态并将其用于托管代码。这是我的方法:

System::Windows::Media::ImageSource^ ManagedCppClass::GetLatestImage() 
{ 
    cv::Mat frame; 
    if (!videoSrc->read(frame)) 
     return nullptr; 

    int totSize = frame.total() * frame.elemSize(); 
    auto managedArray = gcnew array<System::Byte>(totSize); 
    System::Runtime::InteropServices::Marshal::Copy(System::IntPtr((void*)frame.data), 
                managedArray, 0, totSize); //assume isContinuous is always true at this point (?) 

    return System::Windows::Media::Imaging::BitmapImage::Create(
       frame.cols, frame.rows, 96, 96, System::Windows::Media::PixelFormats::Bgr24, //Rgb24, 
       System::Windows::Media::Imaging::BitmapPalettes::WebPalette, managedArray, frame.step); 
} 

也许它会帮助别人在未来绊倒它。