我有一些代码可以并行运行一堆线程。每个线程执行一个功能,以便在从目录加载的图像上复制“水印”,并将修改后的图像保存在用户指定的另一个目录中。为了获得结果,我必须使用CImg库。我不知道为什么有时CImg会像CImgIOException
或CImg<unsigned char>::save_other(): Failed to save file
一样抛出CImg<unsigned char>::save_jpeg(): Failed to save file
。我已经安装了imagemagick,因此使用jpeg / jpg格式保存和打开图像应该没有问题。
这是我的代码:
#include <atomic>
#include <chrono>
#include "CImg.h"
#include <cstdlib>
#include <filesystem>
#include <functional>
#include <iostream>
#include <mutex>
#include <optional>
#include <queue>
#include <string>
#include <thread>
#include <typeinfo>
using namespace cimg_library;
std::mutex IMAGES_MUTEX, IO_MUTEX;
std::atomic_int PROCESSED_IMAGES = 0;
void apply_watermark(std::queue<std::string>& images, CImg<unsigned char>& watermark, int workload, \
std::string output_dir, int id) {
std::queue<std::string> to_process;
int counter = 0;
bool stop = false;
CImg<unsigned char> img;
while (!stop) {
IMAGES_MUTEX.lock();
while(counter < workload) {
if (images.empty()) {
std::cout << "Thread " << id << ": founded empty queue" << std::endl;
counter = workload;
stop = true;
} else {
std::cout << "Thread " << id << ": aquiring image" << images.front() << std::endl;
to_process.push(images.front());
images.pop();
counter += 1;
}
}
IMAGES_MUTEX.unlock();
counter = 0;
while(!(to_process.empty())) {
img.assign(to_process.front().c_str());
if (!(img.width() != 1024 || img.height() != 768)) {
cimg_forXY(watermark, x, y) {
int R = (int)watermark(x, y, 0, 0);
if (R != 255) {
img(x, y, 0, 0) = 0;
img(x, y, 0, 1) = 0;
img(x, y, 0, 2) = 0;
}
}
std::string fname = to_process.front().substr(to_process.front().find_last_of('/') + 1);
IO_MUTEX.lock();
std::cout << "Thread " << id << ": saving image " << fname << std::endl;
IO_MUTEX.unlock();
img.save_jpeg(((std::string)output_dir + (std::string)"/" + fname).c_str());
to_process.pop();
PROCESSED_IMAGES += 1;
} else {
to_process.pop();
}
img.clear();
}
}
return;
}
int main(int argc, char const *argv[]) {
auto completion_time_start = std::chrono::high_resolution_clock::now();
if (argc != 5) {
std::cout << "MISSING PARAMETERS!" << std::endl;
return -1;
}
int par_degree = std::atoi(argv[3]);
if (!(std::filesystem::exists(argv[2]))) {
std::cout << "WATERMARK NOT FOUND!" << std::endl;
return -1;
}
CImg<unsigned char> wtrk(argv[2]);
std::queue<std::string> images;
if (!(std::filesystem::exists(argv[1]))) {
std::cout << "IMAGES' DIRECTORY NOT FOUND!" << std::endl;
return -1;
}
for (auto& path : std::filesystem::directory_iterator(argv[1])) {
std::string fname = path.path().string().substr(path.path().string().find_last_of('/') + 1);
if (fname != ".DS_Store") {
images.push(path.path().string());
}
}
if (!(std::filesystem::exists((std::string)argv[4]))) {
std::filesystem::create_directory((std::string)argv[4]);
}
if (par_degree == 1) {
while(!(images.empty())) {
CImg<unsigned char> img(images.front().c_str());
if (!(img.width() != 1024 || img.height() != 768)) {
cimg_forXY(wtrk, x, y) {
int R = (int)wtrk(x, y, 0, 0);
if (R != 255) {
img(x, y, 0, 0) = 0;
img(x, y, 0, 1) = 0;
img(x, y, 0, 2) = 0;
}
}
std::string fname = images.front().substr(images.front().find_last_of('/') + 1);
img.save_jpeg(((std::string)argv[4] + (std::string)"/" + fname).c_str());
images.pop();
PROCESSED_IMAGES += 1;
} else {
images.pop();
}
}
} else {
int workload = (int)images.size() / par_degree;
std::thread workers[par_degree];
for (int i = 0; i < par_degree; i++) {
workers[i] = std::thread(apply_watermark, std::ref(images), std::ref(wtrk), workload, \
(std::string)argv[4], i);
}
for (int i = 0; i < par_degree; i++) {
workers[i].join();
}
}
auto completion_time_end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::ratio<1>> completion_time = completion_time_end - \
completion_time_start;
std::cout << "\nPARALLELISM DEGREE: " << par_degree << std::endl;
std::cout << "COMPLETION TIME: " << completion_time.count() << " SECONDS" << std::endl;
std::cout << "PROCESSED IMAGES: " << PROCESSED_IMAGES << std::endl;
return 0;
}
所有异常都在保存图像时抛出,对于C ++语言我还是很陌生,我真的不知道。仅使用一个线程(主要线程)运行程序不会造成任何问题。
这是启动命令的示例:
./test ../imgs ../watermark.jpg 4 ../output_dir
任何帮助将不胜感激。
答案 0 :(得分:0)
我怀疑write命令不是线程安全的。例如,大多数jpeg编码器/解码器使用的结构需要为每个线程制作,在这里它们可能是共享的。尽管cimg网站上有声明,但是否/如何考虑仍不是很明显。
我将通过序列化.save_jpeg
命令的执行来检验假设,尽管解决方案将来自不使用cimg,而是直接调用libjpeg-turbo的情况:
//somewhere in global
std::mutex protect_save_image;
//At your section
{
std::unique_lock lk(protect_save_image);
img.save_jpeg(((std::string)argv[4] + (std::string)"/" + fname).c_str());`
}
如果线程数确实很高(又名par_degree
> 1000),则可能超出了导致IO故障的可用文件描述的最大数量。