【发布时间】:2014-06-25 22:34:35
【问题描述】:
我正在考虑在性能关键型应用程序中使用 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 通道 PNG:
1.764 -
3 通道 PNG:
1.290 4 通道 PNG:
1.3361 通道 BMP:
1.384-
3 通道 BMP:
1.099 4 通道 BMP:
1.8093 通道 JPG:
2.816(示例图片)
这些测试是在发布模式下编译完成的,没有使用官方 OpenCV Windows 库附加调试器。
我最初的想法是内存分配的速度,但看不同通道图像之间的差异似乎暗示并非如此。
我尝试过的其他事情:
- 将循环移动到 C++ 函数中:没有变化
- 打开我在 VS 中可以找到的所有优化选项:没有变化
- 第二台机器:在 AMD Phenom II 机器上试用,彩色图像开始在 1.25 左右,灰度在 1.5 左右。因此,尽管有所不同,但仍然受到 .NET 的青睐。
其他细节:
- Windows 7 x64
- Visual Studio 2013
- OpenCV 2.4.9
- .NET 4.5
结果似乎有点反直觉,但在这种特殊情况下,OpenCV 确实似乎更慢。
编辑:
感谢@πάντα ῥεῖ 指出操作不等效,编辑以在两种情况下创建一个 Mat 以隔离加载方法。我希望这使它成为一个有效的测试。
编辑2:
修复了@B 指出的问题,修改了使用 CV_LOAD_IMAGE_UNCHANGED 加载时的数字。
【问题讨论】:
-
比较苹果和梨?
-
你能解释一下为什么吗?谢谢。
-
底层实现和处理可能(故意)非常不同,你为什么认为,它们应该具有大致相同的性能。
-
我想这是真的。如果我通过 .NET 加载并从位图内存初始化 Mat,我会看到它有什么不同。
-
据我所知,如果我通过 .NET 加载并创建一个带有指向位图内存的指针的 Mat,速度几乎与单独的 .NET 完全相同。我认为这表明 Mat 结构本身的创建不是问题。