I was looking into using OpenCV in a performance critical application, so I decided to start with the basics and test image loading speed. To my surprise, image loading (something we do a lot of) takes ~1.5 times longer with OpenCV when compared to .NET.
Here is my code:
CvDll.cpp
#include "stdafx.h"
#include <opencv2\opencv.hpp>
#define CVDLL_API __declspec(dllexport)
extern "C"
{
CVDLL_API void CvLoadImage(const char* imagePath);
CVDLL_API void CvCreateMat(int width, int height, int stride, int channels, void* pBuffer);
}
CVDLL_API void CvLoadImage(const char* imagePath)
{
cv::Mat image = cv::imread(imagePath, CV_LOAD_IMAGE_UNCHANGED);
}
CVDLL_API void CvCreateMat(int width, int height, int stride, int channels, void* pBuffer)
{
int type = CV_MAKETYPE(CV_8U, channels);
cv::Mat image(cv::Size(width, height), type, pBuffer, stride);
}
Program.cs
static class Cv
{
[DllImport("CvDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "CvLoadImage")]
public static extern void LoadImage(string imagePath);
[DllImport("CvDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "CvCreateMat")]
public static extern void CreateMat(int width, int height, int stride, int channels, IntPtr pBuffer);
}
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Usage: {0} (path to image)", Path.GetFileName(System.Reflection.Assembly.GetCallingAssembly().Location));
Console.Write("Press any key to continue...");
Console.ReadKey();
return;
}
string imagePath = args[0];
try
{
if (!File.Exists(imagePath)) throw new ApplicationException("Image file does not exist.");
// Time .NET
Console.Write(".NET Loading {0} Bitmaps: ", ITERATIONS);
TimeSpan timeDotNet = TimeIt(
() =>
{
using (Bitmap img = new Bitmap(imagePath))
{
int width = img.Width;
int height = img.Height;
int channels = Image.GetPixelFormatSize(img.PixelFormat) / 8; // Assumes 1 byte per channel
BitmapData bd = img.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, img.PixelFormat);
// Create a Mat from the bitmap data to make the operation equivalent
Cv.CreateMat(width, height, Math.Abs(bd.Stride), channels, bd.Scan0);
img.UnlockBits(bd);
}
}
, ITERATIONS
);
Console.WriteLine("{0}", timeDotNet);
// Time OpenCV
Console.Write("OpenCV Loading {0} Mats: ", ITERATIONS);
TimeSpan timeCv = TimeIt(
() => Cv.LoadImage(imagePath)
, ITERATIONS
);
Console.WriteLine("{0}", timeCv);
// Show ratio
Console.WriteLine("CV / .NET: {0:0.000}", timeCv.TotalMilliseconds / timeDotNet.TotalMilliseconds);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught: {0}{1}", ex.Message, Environment.NewLine);
}
// End
Console.Write("Press any key to continue...");
Console.ReadKey();
}
static TimeSpan TimeIt(Action action, int iterations)
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; ++i)
{
action();
}
return sw.Elapsed;
}
}
Here is a link to the flower image I've been using to test.
My results (CV time / .NET time):
1.764
1.290
4 Channel PNG: 1.336
1 Channel BMP: 1.384
1.099
4 Channel BMP: 1.809
3 Channel JPG: 2.816
(Example image)
These tests were done compiled in Release mode without the debugger attached using the official OpenCV Windows libs.
My initial thought was the speed of memory allocation, but looking at the difference between different channel images seems to imply this is not the case.
Other things I've tried:
Other details:
The results seem kind of counter intuitive, but it sure seems like in this particular scenario OpenCV is simply slower.
Thanks to @πάντα ῥεῖ for pointing out that the operations weren't equivalent, edited to create a Mat in both scenarios to isolate the loading method. I hope this makes it a valid test.
Fixed issue pointed out by @B, revised the numbers when loading with CV_LOAD_IMAGE_UNCHANGED.
Unless you specify otherwise, which you haven't, OpenCv returns a color image. Therefore for OpenCV you are paying for a color conversion which probably isn't occurring for .NET. For the one color image you need to specify CV_LOAD_IMAGE_GRAYSCALE, or set the flags to -1 to get whatever is in the file.
Looking at the source code, it looks like 3 channel images come out of the actual decoder (PNG and Jpeg, at least) in RGB channel order and these are swapped to the BGR order that OpenCV expects everywhere. If your .NET library is returning the images in RGB order, then you might need to do a conversion to BGR if you are going to pass the images to other OpenCV functions. ANd then you would probably lose the speed advantage.
To be fair, you need to add a RGB2BGR conversion to your .NET load code - see Converting a BGR bitmap to RGB
Also, for the 4 channel PNG, OpenCV will discard the alpha channel and return a 3 channel image unless you specify flags = -1.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With