我写了这个示例代码来解释我的问题。我在VS 2013中有一个解决方案,包含一个C#项目和一个C ++项目。我尝试用C ++(x86)中的OpenCV读取图像。并希望将C#x86项目(使用CLR模式)传递给Bitmap Object和BitmapImage对象以用作WPF ImageSource。
我的C ++代码:
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对象的只是一个灰色矩形,其中包含该图像的高度和宽度。
问题在哪里?
我如何将Image传递给我的C#项目?
答案 0 :(得分: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);
}
也许它会帮助将来偶然发现它的人。
答案 1 :(得分:0)
似乎问题在于共享内存
Mat
和Bitmap
分享C++
中的内存。因此,当Mat
对象销毁时,Bitmap
对象无法访问数据。这就是为什么它的数据在C ++方面是正确的,但在C#方面没有任何内容
为了解决这个问题,我使用了static Mat
。它永远不会释放,但可以解决我的问题。