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

时间:2014-06-25 22:34:36

标签: 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);
}

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.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加载时修改了数字。

1 个答案:

答案 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通道图像。