我正在考虑在性能关键应用程序中使用OpenCV,所以我决定从基础知识开始并测试图像加载速度。令我惊讶的是,与.NET相比,OpenCV的图像加载(我们做了很多事情)需要大约1.5倍。
这是我的代码:
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是我用来测试的花卉图像的链接。
我的结果(CV时间/ .NET时间):
1.764
1.290
4频道PNG :1.336
1频道BMP :1.384
1.099
4频道BMP :1.809
3频道JPG :2.816
(示例图片)
这些测试是在发布模式下编译完成的,没有使用官方OpenCV Windows库附加调试器。
我最初的想法是内存分配的速度,但是看看不同频道图像之间的差异似乎暗示情况并非如此。
我尝试过的其他事情:
其他细节:
结果似乎有点反直觉,但在这种特殊情况下,OpenCV看起来确实比较慢。
感谢@πάνταῥεῖ指出操作不相同,编辑在两种情况下创建Mat以隔离加载方法。我希望这能使它成为一个有效的测试。
修正了@B指出的问题,在使用CV_LOAD_IMAGE_UNCHANGED加载时修改了数字。
答案 0 :(得分:3)
除非您另有指定,否则OpenCv将返回彩色图像。因此,对于OpenCV,您需要支付可能不会出现在.NET中的颜色转换。对于单色图像,您需要指定CV_LOAD_IMAGE_GRAYSCALE,或将标志设置为-1以获取文件中的任何内容。
查看源代码,看起来3个通道图像以RGB通道顺序从实际解码器(至少是PNG和Jpeg)中出来,并且这些图像被交换到OpenCV期望的BGR顺序。 如果您的.NET库以RGB顺序返回图像,那么如果要将图像传递给其他OpenCV函数,则可能需要转换为BGR。那么你可能会失去速度优势。
公平地说,您需要在.NET加载代码中添加RGB2BGR转换 - 请参阅Converting a BGR bitmap to RGB
此外,对于4通道PNG,除非指定flags = -1,否则OpenCV将丢弃Alpha通道并返回3通道图像。