为什么OpenCV加载图像需要比.NET长得多?

Mil*_*lls 5 c# c++ opencv

我正在考虑在性能关键应用程序中使用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);
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

是我用来测试的花卉图像的链接.

我的结果(CV时间/ .NET时间):

  • 1频道PNG:1.764
  • 3频道PNG:1.290
  • 4频道PNG:1.336

  • 1频道BMP:1.384

  • 3频道BMP: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以隔离加载方法.我希望这能使它成为一个有效的测试.

EDIT2:

修复了@B指出的问题,在使用CV_LOAD_IMAGE_UNCHANGED加载时修改了数字.

Bul*_*ull 4

除非您另外指定(您没有指定),否则 OpenCv 将返回彩色图像。因此,对于 OpenCV,您需要支付颜色转换费用,而 .NET 可能不会发生这种情况。对于单色图像,您需要指定 CV_LOAD_IMAGE_GRAYSCALE,或将标志设置为 -1 以获取文件中的任何内容。

查看源代码,看起来 3 通道图像以 RGB 通道顺序从实际解码器(至少是 PNG 和 Jpeg)中出来,并且这些图像被交换为 OpenCV 在任何地方都期望的 BGR 顺序。如果您的 .NET 库按 RGB 顺序返回图像,那么如果要将图像传递给其他 OpenCV 函数,您可能需要转换为 BGR。然后你可能会失去速度优势。

公平地说,您需要将 RGB2BGR 转换添加到 .NET 加载代码中 - 请参阅将 BGR 位图转换为 RGB

另外,对于 4 通道 PNG,OpenCV 将丢弃 alpha 通道并返回 3 通道图像,除非您指定 flags = -1。