How to convert OpenCV cv::mat to System Bitmap to System ImageSource
OpenCV is an open source computer vision library which can be used for a wide variety of things such as face recognition, people detection, motion tracking, video editing and image manipulation. For my undergraduate project, I made use of the OpenCV libraries to create a facial recognition system. It used a webcam to detect ID cards (in my project, I made use of ACU ID cards) and find the photograph on it, then used facial recognition to match the presented ID card with one in its database. More recently, I utilised the OpenCV libraries in a piece of research to build software to perform automatic data capture from hospital videos to save the human-effort required in data capture.
In my undergraduate project, the core output was the dissertation report and not the software, thus the software was created to perform its functionality but without a clean or user-friendly interface. As such, displaying information from the OpenCV libraries was done using the cv::imshow() method, rather than embedding the cv::mat into a GUI of my design. For my more recent software development, I opted to do this to produce a clean GUI that I could use to demonstrate my research to those outside my field.
To do this, I build a GUI using the WPF libraries in C#, and, as is common for most of the tools I create in the C languages, used a C++ engine to work with the OpenCV libraries. The GUI needed to display the frames of the video as it was playing so the user could see the video at the same time as it was being analysed. To do this, I had an image control on my WPF GUI which would be used to display the frames. However, this meant converting the cv::mat image to an image source to display in the image control.
This was done by converting the cv::mat to a System Bitmap first as a go-between the cv::mat and image source data types and then converting the bitmap to an image source.
Converting a cv::mat to a System Bitmap
The following code example is given in its CLI form as this was how I passed the cv::mat from the C++ engine into a Bitmap for the C# UI code.
Bitmap^ Engine::ConvertMatToBitmap(cv::Mat matToConvert) { return gcnew Bitmap(matToConvert.cols, matToConvert.rows, 4*matToConvert.rows, System::Drawing::Imaging::PixelFormat::Format24bppRgb, IntPtr(matToConvert.data)); }
Converting a Bitmap to an ImageSource
The following code example is given in its C# form and made use of the following libraries:
- System.IO
- System.Drawing
- System.Windows.Media
- System.Windows.Media.Imaging
private ImageSource ConvertBitmapToImageSource(Bitmap imToConvert) { Bitmap bmp = new Bitmap(imToConvert); MemoryStream ms = new MemoryStream(); bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); BitmapImage image = new BitmapImage(); image.BeginInit(); ms.Seek(0, SeekOrigin.Begin); image.StreamSource = ms; image.EndInit(); ImageSource sc = (ImageSource)image; return sc; }
The resulting image source is then able to be applied to the image control in WPF and displayed in the GUI. To display a video in a GUI, simply use a timer to continue calling frames from the engine as they change and display them in the GUI.
Hello!
Well after 2 years someone needs your help 😉
I have exactly the same code as you but for some reason my application gives me the following error:
AccessViolationException was unhandled.
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I don’t know what to do..
Hi Drekbende,
It sounds like the bitmap memory is corrupted somehow during run. What code do you have? I can’t promise to fix it – it’s been a while since I looked at OpenCV stuff so I’ll be a little rusty but happy to take a look and see if there’s any obvious issue with the conversion.
Best regards,
Fraser
Oh wow! I never thought you would answer me.. 🙂
Well now, I have a chunk of code but I don’t think it’s readable in this comment section but let’s have a try.
C++ code:
Bitmap^ OpenCvWrapper::ApplyFilter() {
Mat image = imread(“C:/Users/Drekbende/Pictures/colored_squares.png”);
returnImg = ConvertMatToBitmap(image);
return returnImg;
}
Bitmap^ OpenCvWrapper::ConvertMatToBitmap(cv::Mat matToConvert) {
Bitmap^ test = gcnew Bitmap(matToConvert.rows, matToConvert.cols, 4 * matToConvert.rows, System::Drawing::Imaging::PixelFormat::Format4bppIndexed, IntPtr(matToConvert.data));
return test;
}
C# code:
public partial class Form1 : Form
{
private Bitmap f;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
OpencvDotNet.OpenCvWrapper obj = new OpencvDotNet.OpenCvWrapper();
f = obj.ApplyFilter();
pictureBox1.Image = f;
}
}
Thanks,
Drekbende
I try to reply to everyone that comments, I’m just a bit lax at checking my emails to see that a comment has come in so apologies for the delayed response!
The only thing I can think of from looking at that code would be either the Bitmap does not like the PixelFormat you’ve chosen, or the pictureBox1.Image is in a different thread to the bitmap.
Have you tried converting the bitmap into an image source after getting the bitmap from the C++ engine? If that doesn’t work then it could be something screwy occurring with the memory when the bitmap comes back from the CLI code, which can be near impossible to figure out, but I think converting it to an ImageSource may solve part of the issue by putting the bitmap into the memory of the C#.
Another suggestion, depending on how much of your application you have written, would be to look at the EmguCV libraries available with Nuget (or manually installed). They’re OpenCV classes but in the .net framework for C#, which removes the need to use C++ or CLI entirely. Of course it then removes the chance to control the memory and other C++ advanced stuff, but it can be powerful for producing quick demonstrations/applications that don’t require the advanced stuff.
Hope this helps!
Thanks for your comment! The code that I sent was coming from a Windows Form. So I deleted the Form and created a WPF.
And still I’m getting the AccessViolationError error on this line:
Bitmap bmp = new Bitmap(imToConvert);
So what I tried was right after the conversion from Mat to Bitmap, to save the Bitmap into a file and check the output of it. This is the output:
https://gyazo.com/006c4d8bc539a2168267b943784bb277
(Sorry if I’m not allowed to post any links)
Obviously the picture has to show the front of an Audi, but instead it’s all weird.
So if you look on the bright side: I’m getting some output. But somehow I still get the AccessViolationException error. Can’t figure it out.
Change is usually good, it shows progression in a direction but sometimes the direction is not always great!
Usually that type of output in that image link is caused when the rows and columns of the converted image don’t match up with the rows and columns of the output image. You can see it’s sort of repeating itself 4 times. Perhaps try changing:
Bitmap^ test = gcnew Bitmap(matToConvert.rows, matToConvert.cols, 4 * matToConvert.rows, System::Drawing::Imaging::PixelFormat::Format4bppIndexed, IntPtr(matToConvert.data));
To just
Bitmap^ test = gcnew Bitmap(matToConvert.rows, matToConvert.cols, matToConvert.rows, System::Drawing::Imaging::PixelFormat::Format4bppIndexed, IntPtr(matToConvert.data));
And see if that prevents the repetition.
As for the access violation exception, I’m not quite sure what to suggest – evidently it thinks the memory is in use somewhere which could be just because the bitmap isn’t being returned to C# correctly. Perhaps if we can ascertain the solution to that the AVE will be fixed with it, but it is most peculiar.
Let me know if removing the 4* does anything to change the output!
Hi, thanks for your comment!
I changed the code you mentioned but I still get a weird output pic:
https://gyazo.com/883cb1cd4b8b5c0fcf3c6d7e3f52e430
This is my input image: https://gyazo.com/d2221a14edc32523f01744408765797b
As for the AccessViolationException, I think you’re right about the memory management.So I tried deep copying the Bitmap AND Mat as well, but I still get the error.
I can give you my Project if you like ’cause I am all out of options right now..
Hi Fraser,
Back at it again. I have some good news. My C++ Mat to Bitmap conversion is all good! But when I return my Bitmap^ to C# I don’t have any data in it so that’s why it’s getting the AccessViolationException error.
How did you call the C++ method in your C# code?
Hi Drekbende,
I’m glad the conversion is working, that’s half the battle won I feel.
For the C#->C++ bit, I create an object of my C++ type that is static to the C# code. This keeps the instance in memory throughout the application. Then when I want to get a Bitmap frame out I call my bitmap method as normal. So for example:
In my MainWindow file:
public static Engine gEngine = new Engine();
In my other C# files:
Bitmap imgToConvert = gEngine.getCurrentFrame(); //Current frame of the video to convert to image source for WPF image display
Ouch. You can remove the previous comments! I did it! It worked! 🙂
I declared my cv::Mat as a public variable so now the Mat doesn’t delete itself from the memory (which it does, I read.) So now everything works as expected. Many thanks Fraser!
Happy coding!