代码之家  ›  专栏  ›  技术社区  ›  Alexandra Tupu

并行读取文件中的图像

  •  0
  • Alexandra Tupu  · 技术社区  · 7 年前

    我有这个函数从一个数据集读取负图像(大约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);
        }
    }
    

    并且需要花费大量的时间来处理,有时会被阻塞。 如何优化它并使之并行?

    2 回复  |  直到 7 年前
        1
  •  2
  •   Mark Setchell    7 年前

    我稍微修改了你的代码以使用OpenMP来并行加载-实际的更改是最小的-我只放置了一个OpenMP pragma for

    #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
    

    加载122000个图像的时间根据我使用的线程数而变化,如下表所示:

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

    然后我决定,如果你经常这样做,足够关心,你可能会想预先支付一个小的价格,以进一步改善时间。因此,您可能希望将图像转换一次,转换为更简单的读取格式,而不是执行所有CPU密集型代码来解压缩JPEG,例如 PNM . 所以,我用 GNU并联 然后加载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
    

    enter image description here


    要使用OpenMP进行编译,请使用:

    g++ -fopenmp =O3 -march native ...
    
        2
  •  1
  •   Benno Geels    7 年前

    你可以试试这个

    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个线程读取图像。