为啥 OpenCV 加载图像的时间比 .NET 长得多?
Posted
技术标签:
【中文标题】为啥 OpenCV 加载图像的时间比 .NET 长得多?【英文标题】:Why does OpenCV take much longer to load an image than .NET?为什么 OpenCV 加载图像的时间比 .NET 长得多? 【发布时间】: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: 01", 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.336
1 通道 BMP:1.384
1.099
4 通道 BMP:1.809
3 通道 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 结构本身的创建不是问题。 【参考方案1】:除非您另外指定,否则 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,OpenCV 将丢弃 alpha 通道并返回 3 通道图像,除非您指定 flags = -1。
【讨论】:
感谢您的回答!你是对的,我认为 CV_LOAD_IMAGE_ANYDEPTH 与 CV_LOAD_IMAGE_UNCHANGED 相同,只允许 >8 位深度,但事实并非如此,我实际上在所有情况下都转换为灰度。不幸的是,在将其更改为 CV_LOAD_IMAGE_UNCHANGED (-1) 后,数字现在变得更糟(花 JPG 为 2.8)。关于 BGR/RGB:.NET 以 BGR 形式加载它,所以我不需要交换通道来匹配 OpenCV。以上是关于为啥 OpenCV 加载图像的时间比 .NET 长得多?的主要内容,如果未能解决你的问题,请参考以下文章
Python and OpenCV - 为啥用 OpenCV 处理的裁剪图像仍然可以影响原始图像?
为啥 OpenCV Gpu 模块的性能比 VisionWorks 快?
为啥 OpenCV GPU CUDA 模板匹配比 CPU 慢很多?