从文件中并行读取图像

Posted

技术标签:

【中文标题】从文件中并行读取图像【英文标题】:read images from file in parallel 【发布时间】:2018-05-29 18:22:02 【问题描述】:

我有这个函数从数据集中读取负片图像(大约 122000)

void load_images(const String & dirname, vector< Mat > & img_lst, bool showImages = false)

    vector< String > files;
    glob(dirname, files);

    for (size_t i = 0; i < files.size(); ++i)
    
        Mat img = imread(files[i]); // preia imagine
        if (img.empty())            // treci peste daca este imagine invalida
        
            cout << files[i] << " is invalid!" << endl;
            continue;
        

        if (showImages)
        
            imshow("image", img);
            waitKey(1);
        
        img_lst.push_back(img);
    

并且需要大量时间来处理,有时它会被阻止。 如何优化它并使其并行化?

【问题讨论】:

CUDA 不能做文件 I/O,所以不可以不使用 CUDA 很高兴知道谢谢 :) 图片的像素尺寸是多少?以 kB 为单位?你到底打算如何一次处理所有在内存中的 122,000 张图像?您的磁盘速度非常快并且文件分布在多个文件系统和驱动器上吗?您使用的是什么操作系统?安装了多少 RAM? 我正在尝试训练 Hog,20kb * 122000, 96X120 我猜你可以尝试并行启动 4 个线程看看是否有帮助... 【参考方案1】:

我稍微更改了您的代码以使用 OpenMP 来并行加载 - 实际更改很小 - 我只是在 for 循环之前放置了一个 OpenMP pragma 并序列化访问图像矢量,因为矢量不是线程安全。

#include <iostream>
#include <vector>
#include <mutex>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void load_images(int start,int end)
   vector<Mat>img_lst;
   mutex mtx;

#pragma omp parallel for
   for(size_t i=start;i<=end;i++)
      char filename[16];
      sprintf(filename,"%d.jpg",i);
      Mat img = imread(filename);
      if (img.empty())
         cerr << "ERROR: Failed to load " << filename << endl;
      
      mtx.lock();
      img_lst.push_back(img);
      mtx.unlock();
   
   mtx.lock();
   cout << "INFO: Loaded " << img_lst.size() << endl;
   mtx.unlock();


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

    load_images(1,122000);

您可以像这样控制线程数:

export OMP_NUM_THREADS=2
time ./main

加载 122,000 张图片的时间根据我使用的线程数不同,如下表所示:

Threads Time (s)
================
1       44
2       23
4       12.4
8       8.8

然后我决定,如果您经常这样做以致在意,您可能希望预先支付少量费用以进一步改善时代。因此,与其执行所有 CPU 密集型代码来解压缩 JPEG,您可能希望将图像一次转换为更简单的读取格式 - 例如PNM。因此,我使用 GNU Parallel 将所有 JPEG 图像转换为 PNM,然后改为加载 PNM 图像:

所以看起来像:

seq 122000 | parallel convert .jpg .pnm

代码是:

...
...
#pragma omp parallel for
   for(size_t i=start;i<=end;i++)
      char filename[16];
      sprintf(filename,"%d.pnm",i);        <--- ONLY LINE CHANGED
      Mat img = imread(filename);
...
...

你可以看到时间大大缩短了:

Nthreads Time(s)
================
1        7
2        4
4        2.5
8        3.2


要使用 OpenMP 编译,请使用:

g++ -fopenmp =O3 -march native ...

【讨论】:

请注意,Mark Setchell 和我的解决方案都不会以与文件数组中的名称不同的顺序将图像存储在 img_list 数组中。所以要注意这一点。 Mark的解决方案可以简单地适应这个问题。【参考方案2】:

你可以试试这个

class parReader

public:
    parReader(std::string dirname, std::vector< cv::Mat > & lst);
private:
    size_t filesIdx;
    HANDLE hFilesMux,hImgListMux;
    std::vector<cv::String> files;
    std::vector<cv::Mat> img_lst;
    static void readImgs(parReader *nm);
    const char *getNext();
    void push_back(cv::Mat &img);
;
parReader::parReader(std::string dirname, std::vector<cv::Mat> & lst) :img_lst(lst), filesIdx(0),hFilesMux(NULL),hImgListMux(NULL)

    hFilesMux   = CreateMutex(NULL, 0, NULL);
    hImgListMux = CreateMutex(NULL, 0, NULL);
    cv::glob(dirname, files);
    std::thread pr1(readImgs, this);
    std::thread pr2(readImgs, this);
    std::thread pr3(readImgs, this);
    std::thread pr4(readImgs, this);
    pr1.join();
    pr2.join();
    pr3.join();
    pr4.join();
    CloseHandle(hFilesMux);
    CloseHandle(hImgListMux);

const char *parReader::getNext()

    const char *res = NULL;
    WaitForSingleObject(hFilesMux, INFINITE);
    if (filesIdx < files.size())
        res = files[filesIdx++].c_str();
    ReleaseMutex(hFilesMux);
    return res;

void parReader::push_back(cv::Mat &img)

    WaitForSingleObject(hImgListMux, INFINITE);
    img_lst.push_back(img);
    ReleaseMutex(hImgListMux);


void parReader::readImgs(parReader *nm)

    while (true)
    
        const char *fn = nm->getNext();
        if (fn == NULL) break;
        cv::Mat img = cv::imread(fn); 
        if (img.empty())            // treci peste daca este imagine invalida
        
            std::cout << fn << " is invalid!" << std::endl;
            continue;
        
        nm->push_back(img);
    




int main()

    std::vector<cv::Mat> imgList;

    parReader mgr("*.png",imgList);

对其进行了简短的测试,但它应该可以使用 4 个线程读取图像。

【讨论】:

以上是关于从文件中并行读取图像的主要内容,如果未能解决你的问题,请参考以下文章

从 Python 并行批量读取文件

R foreach:并行读取和操作多个文件

为啥从队列中并行读取消息很慢?

从图像文件中提取感兴趣区域而不读取整个图像

使用Java 8 Parallel Stream在并行读取多个文件时排除某些文件

从文件夹中读取图像(OpenCV)