'load/save multiple images from/in a directory - opencv c++

i want to load a lot of images (not sequential names though) from a directory. edit them and then save them in a different directory with their original names if possible. I load them like that:

glob("/photos/field_new/*.jpg", fn, false);
size_t count = fn.size(); //number of jpg files in images folder
for (size_t i=0; i<count; i++)
    images.push_back(imread(fn[i]));

any ideas how i can save them in the directory /photos/results/ ? and if possible with their original names?



Solution 1:[1]

If you have C++17 it will be easy as there is filesystem library in standard (std::filesystem). Other wise I would recomend you to get boost::filesystem which is very similar (you should be good with replacing all of std::filesystem to boost::filesystem).

To load all images from certain folder there are 2 helper functions:

#include <filesystem> //for boost change this to #include <boost/filesystem> and all std:: to boost::
#include <opencv2/opencv.hpp> 

bool isSupportedFileType(const std::filesystem::path& pathToFile, 
                         const std::vector<std::string>& extensions)
{
    auto extension = pathToFile.extension().string();
    std::transform(extension.begin(), extension.end(), extension.begin(), [](char c)
                   {
                       return static_cast<char>(std::tolower(c));
                   });

    return std::find(extensions.begin(), extensions.end(), extension) != extensions.end();
}

std::tuple<std::vector<cv::Mat>, std::vector<std::filesystem::path>> loadImages(const std::filesystem::path& path,
                                                                                const std::vector<std::string>& extensions)
{
    std::vector<cv::Mat> images;
    std::vector<std::filesystem::path> names;
    for (const auto& dirIt : filesystem::DirectoryIterator(path))
    {
        if (std::filesystem::is_regular_file(dirIt.path()) && isSupportedFileType(dirIt.path(), extensions))
        {
            auto mask = cv::imread(dirIt.path().string(), cv::IMREAD_UNCHANGED);
            if (mask.data != nullptr) //there can be problem and image is not loaded
            {
                images.emplace_back(std::move(mask));
                names.emplace_back(dirIt.path().stem());
            }
        }
    }
    return {images, names};
}

You can use it like this (assuming C++17):

auto [images, names] = loadImages("/photos/field_new/", {".jpg", ".jpeg"});

Or (C++11)

auto tupleImageName = loadImages("/photos/field_new/", {".jpg", ".jpeg"});
auto images = std::get<0>(tupleImageName);
auto names = std::get<1>(tupleImageName);

To save you can use this function:

void saveImages(const std::filesystem::path& path,
                const std::vector<cv::Mat>& images, 
                const std::vector<std::filesystem::path>& names)
{
    for(auto i = 0u; i < images.size(); ++i)
    {
        cv::imwrite((path / names[i]).string(), images[i]);
    }
}

Like this:

saveImages("pathToResults",images,names);

In this save function it would be good to perform some validation if the number of images is the same as names, otherwise there might be a problem with stepping outside vector boundary.

Solution 2:[2]

Since C++ is compatible with C, and if you are using a Unix system, you could use dirent.h (https://pubs.opengroup.org/onlinepubs/7908799/xsh/dirent.h.html). It should be something like this:

#include <dirent.h>
#include <opencv2/opencv.hpp>
#include <string.h>
using namespace std;
using namespace cv;

int main(){
    DIR *d;
    struct dirent *dir;
    string dirname_input="dirname/input";
    string dirname_output="dirname/output";
    d=opendir(dirname_input.c_str());
    if (d){
        while ((dir=readdir(d))!=NULL){
            string filename=dir->d_name;
            if (filename.substr(filename.size() - 4)==".jpg"){
                Mat image;
                string fullpath_input=dirname_input+'/'+filename;
                string fullpath_output=dirname_output+'/'+filename;
 
                image=imread(fullpath_input,1);
                // process image 
                imwrite(fullpath_output,image);
            }
        }
    }
    return 0;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 mlbj