为啥 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 通道 PNG1.764 3 通道 PNG1.290

4 通道 PNG1.336

1 通道 BMP1.384

3 通道 BMP1.099

4 通道 BMP1.809

3 通道 JPG2.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 慢很多?

C++/openCV - 为啥显示图像会产生 linux 分段错误? (openCV 错误?)

为啥一根长字符串比许多小字符串占用更多空间?

为啥字符串-字符串连接比字符串长连接更快? [关闭]