'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 |