在 Windows 8.1 上使用 OpenCV 在 C++ 中加载图像需要很长时间

Posted

技术标签:

【中文标题】在 Windows 8.1 上使用 OpenCV 在 C++ 中加载图像需要很长时间【英文标题】:Loading images takes very long in C++ using OpenCV on Windows 8.1 【发布时间】:2016-02-24 12:59:49 【问题描述】:

我目前正在使用 C++ 开发一个数据驱动的学习应用程序。我有大量数据,超过 300.000 张图像总共需要大约 3 GB。

关于我的工作环境

Windows 8.1,64 位 Visual Studio 2013 OpenCV OpenMP

我的硬件

i7-3770 8GB 内存 SSD(系统和 Visual Studio) 硬盘

我的简而言之问题是,仅加载图像,因此 3 GB,需要 3 多个小时,我想改进。

实现如下:首先,我从一个文件中加载一些关于图像(不是图像本身)的信息。在内部,我使用一个标准向量,它包含 300.000 个指向我的类 Item 的指针。项目包含从文件和图像(OpenCV Mat)加载的信息,尚未加载。接下来是一些独立的中间步骤。之后,我将遍历我的向量 - 使用 OpenMP 并行化 - 并使用以下方法为每个项目加载图像:

imread(PATH_TO_FILE, CV_LOAD_IMAGE_UNCHANGED);

对我来说真正奇怪的是,增加图片的数量并不是图片加载时间的线性增加。使用 22000 张图片大约需要 22 秒,使用 44000 张图片大约需要 1 分 43 秒,66000 张图片大约需要 4 分钟,依此类推。

我不确定这个问题是由于硬件瓶颈(我最初假设的)还是由于我这边的实现缺陷造成的。我已经考虑过很多,比如:

将图像位深度减半并因此将内存大小减半并不会减少所用时间 我的应用程序的虚拟内存最大约为 4GB,因此不应该涉及太多交换 从我的系统 SSD 加载数据与从我的 HDD 加载数据没有区别 为进程赋予更高的优先级(我选择了最高的,即实时的)稍微改进了运行时间,但是,上面给出的运行时间确实已经使用了这种改进 即使我使用的是 OpenMP,RessourceManager 指出在加载我的图像期间,我的应用程序只使用了 15% 的 CPU。通过打印,我可以看出所有 8 名工人都在分担负载。 使用vector.shrink_to_fit() 缩小了向量大小

在我看来,这些事实不是硬件问题,而是存在实施缺陷。使用包含超过 300.000 个指针的巨大向量是否效率低下?或者我没有考虑过关于 OpenCV Mat 的任何事情?关于如何进一步查明问题的任何提示? 对于导致这种行为的原因以及我可能如何解决它的任何建议,我很感激。

提前致谢!

编辑:我如何将图像加载到矢量中。请注意,我重命名了一些内容,因此可能存在拼写错误。

void LoadAllImages()

    for (int i = 0; i < data->size(); i++)
    
        Item* cur_item = data->at(i);
        cur_item->setImage(cur_item->loadImage());
    



Mat Item::loadImage()

    return imread(IMAGES_PATH + image_name_, CV_LOAD_IMAGE_UNCHANGED);



void Item::setImage(Mat img)

    img_ = img;

EDIT2:我如何设置没有图像的矢量。请注意,我为此部分使用了增强多线程。另请注意,这部分执行时间随数据线性增加。

void foo(vector<Item*>* data, const string file_path, const string file_name)

    //open file

    string image_name;
    boost::mutex data_mutex;
    boost::thread_group thread_group;
    while (file >> image_name)
    
        //reading other data regarding the current image

        thread_group.add_thread(new boost::thread(addDataToVectorThread, data, image_name, other_data_read, &data_mutex));
    
    thread_group.join_all();



void FileHandler::addDataToVectorThread(vector<Item*>* data, string image_name, vector<float> other_data, boost::mutex* data_mutex)

    Item* item = new Item(other_data, image_name);
    data_mutex->lock();
    data->push_back(item);
    data_mutex->unlock();

EDIT3:我尝试了 SSteve 提供的代码,并且能够缩小我的问题范围。此代码生成与我的大小相同的随机图像,因此为 96x96,颜色深度为 8 位。请注意,我将他的代码更改为只生成我的图像的灰度图像。在我的笔记本电脑上加载 300.000 张图像大约需要 10 分钟,这很好。

我尽可能简化了我的代码并删除了所有多线程。我已经更改了我的代码,将图像直接加载到项目中,因此在矢量创建期间。

查看资源监视器我注意到我的图像占用了大量内存。加载 10.000 张图像已经占用了 1 GB。使用 CV_LOAD_IMAGE_GRAYSCALE 而不是 CV_LOAD_IMAGE_UNCHANGED 将内存消耗减半。我不明白,我的图像肯定是 96x96x8 位,而且还是太多了。

使用我的完整代码但仅使用一个颜色通道加载由 SSteve 的代码创建的随机图像需要 100 MB 的内存来存储 10.000 个图像和一些额外的东西。单独的图像应该需要大约 90 MB,所以应该没问题。与我的图片相比,这只是一小部分。

简而言之:我的图片似乎导致了问题,但我不明白为什么。

我如何获得这些图像:我用于算法问题部分的图像是由我预处理的。这个预处理步骤是独立的,基本上按比例缩小图像。所以我要工作的图像确实有 240x320 的大小和 16 位深度。然后我将这些图像缩放为 96x96 和 8 位深度。

有没有可能由于某种原因,我缩小的图像以正确的尺寸存储,并且这个尺寸在 Windows 的图像属性中正确显示,但图像仍然包含一些应该“删除”的信息?所以他们占用了比他们应该更多的内存?这对我来说没有任何意义。

感谢到目前为止的所有帮助。

【问题讨论】:

可能是硬件问题。尝试使用其他 API 加载图像以进行比较。让测试应用程序变得简单:只加载图像,仅此而已。 您的图像是 3GB 硬盘还是内存?如果它们是硬 JPEG,它们将作为位图加载到内存中。这意味着它们的内存远远超过 3GB 我不知道 OpenCV,但我们是在谈论 30,000 个压缩图像,在文件系统上占用大约 3GB 空间吗?也就是说,也许它们正在被加载和未压缩占用更多的空间导致内存交换? 文件 I/O 不是很可并行化。 15% 的 CPU 大约是一个线程在工作,一个在做 I/O,剩下的六个在等待一个在做 I/O。 我对您将图像加载到矢量中的方式有​​一点疑问。你能给我们看看那部分吗?或者只是确保没有发生重新分配(您在推送之前保留了正确数量的项目) 【参考方案1】:

我不认为 OpenCV 是您的瓶颈。我在运行 OS X 10.11.3 的 8 GB RAM 的 2009 年老式 2.8 GHz Core 2 Duo MacBook Pro 上进行了测试。我能够在 3.3 分钟内加载 300,000 张图像。 150,000 张图片耗时 1.5 分钟。

这是我用来创建 300,000 张图像的程序。它们在我的硬盘上占用了大约 8.6 GB 的空间。

#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"

using namespace cv;

class Item;

int main(int argc, char *argv[]) 

    Mat image(Size(96,96), CV_8UC3);
    RNG rng;
    char fname[256];
    for (int i = 0; i < 300000; i++) 
        rng.fill(image, RNG::UNIFORM, 0, 256);
        sprintf(fname, "img%06d.png", i);
        imwrite(fname, image);
        if (0 == i % 500) 
            printf("%d\n", i);
        
    
    return 0;

这是我用来创建Items 的向量并加载图像的程序。我认为它与您问题中的代码 sn-ps 足以重复该问题。

#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

#define CV_LOAD_IMAGE_UNCHANGED -1

String IMAGES_PATH = "/Users/steve/Development/tests/so35602911/images/";

class Item 
public:
    String image_name;
    Mat img_;
    Mat loadImage();
    void setImage(Mat img);
;

Mat Item::loadImage() 
    return imread(IMAGES_PATH + image_name, CV_LOAD_IMAGE_UNCHANGED);


void Item::setImage(Mat img) 
    img_ = img;


int main(int argc, char *argv[]) 
    int imagesToProcess = 300000;

    vector<Item*> items;
    char filename[256];
    for (int i = 0; i < imagesToProcess; i++) 
        Item *theItem = new Item;
        sprintf(filename, "img%06d.png", i);
        theItem->image_name = filename;
        items.push_back(theItem);
    

    printf("Set up %lu items.\n", items.size());

    time_t startTime = time(0);
    for (int i = 0; i < items.size(); i++) 
        Item* cur_item = items[i];
        cur_item->setImage(cur_item->loadImage());
    
    time_t endTime = time(0);

    printf("%lu images. Finished in %.1f minutes.\n", items.size(), (endTime - startTime) / 60.0);

    //Show the last image just to prove they got loaded
    //imshow("last", items[items.size() - 1]->img_);
    //waitKey(0);

    return 0;

我建议删除代码以并行加载图像。正如 cmets 中所指出的,文件 I/O 不能很好地并行化。

如果这没有帮助,你应该尝试在 Unix 或 OS X 上运行你的程序(或让别人为你运行它),看看 Windows 是否是罪魁祸首。

【讨论】:

您好,感谢您的意见。我已经测试了您提供的简化代码。是的,它几乎捕捉到了我的完整应用程序的有问题的功能。我唯一改变的是颜色通道——我的图像是灰度的,所以我改用 CV_8UC1。在我的笔记本电脑(也是 Windows 8.1)上,加载 300.000 张图像大约需要 10 分钟,这对我来说完全没问题。我的应用程序使用了大约 2.7GB 的 RAM,其中 2.3GB 存在于物理 RAM 中。 与我的完整应用程序仅有的两个区别是:项目每个实例使用额外的 440 字节,对于所有 300.000,这只有 125MB。我的图像以几千个为一组位于不同的文件夹中。我不认为其中一个会导致瓶颈......:/我将尝试进一步调试它以最终找到解决方案。您还有什么建议吗? 你是说在 i7 机器上仍然需要几个小时,而现在在笔记本电脑上只需要 10 分钟? 没有。您的代码在我的笔记本电脑上花了 10 分钟,我相信它在我的塔上更快(现在无法测试)。我的代码肯定会在我的笔记本电脑和我的塔上花费数小时。我能够缩小我的问题范围,并将我的新见解作为编辑 3 添加到问题中。

以上是关于在 Windows 8.1 上使用 OpenCV 在 C++ 中加载图像需要很长时间的主要内容,如果未能解决你的问题,请参考以下文章

Windows 8.1 中 Visual Studio 12 中的 OpenCV 错误。这是调试时显示一些错误的简单代码。我怎样才能解决这个问题?

在 Windows 8.1 频道上使用相同的推送通知频道

Win32 应用程序是不是也可以在 Windows 7 和 Windows 8/8.1 上运行?

使用 Windows 8.1 在 localhost 上测试 PHP 的 mail() 函数 [关闭]

使用Windows 8.1在localhost上测试PHP的mail()函数[关闭]

MySQL (Xampp) 不会在 Windows 8.1 上启动