Image Format Converter (cpp, written by Codex)
envgap__codex__cpp-t1-18
Written by a coding agent; not on GitHubWritten 2026-03-03
01 / FAILURE SIGNATURE
As the study recorded it
OpenCV headers not found - missing target_include_directories in CMakeLists
Not a benchmark task.
- In a clean container the reported failure did not reproduce, or the known fix did not make the project run.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
CMakeLists.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
codex/cpp-t1 #18 · read the task the agent was given
Codex wrote this cpp project from the task below. It does not run on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: Image Format Converter Write a program that converts images between common formats (PNG, JPEG, BMP, TIFF, WebP) with configurable quality settings, preserving metadata where possible. FUNCTIONAL REQUIREMENTS: - Accept an image file path and target format as command-line arguments - Support conversions between: PNG, JPEG, BMP, TIFF, and WebP formats - Support a --quality flag for lossy formats (JPEG, WebP) with a value from 1-100 (default: 85) - Support a --resize flag to resize during conversion using WxH syntax (e.g., --resize 800x600), preserving aspect ratio by default - Support --maintain-aspect flag (default true) to control whether resizing preserves the original aspect ratio by fitting within the specified dimensions - Preserve EXIF metadata during conversion where the target format supports it, with a --strip-metadata flag to optionally remove it - Support alpha channel handling: when converting from formats with transparency (PNG, WebP) to formats without (JPEG, BMP), use a configurable background color via --background flag (default: white) - Support batch conversion of all images in a directory via --batch flag, optionally filtering by input format with --filter flag - Print conversion details to console: input format, output format, original dimensions, new dimensions, file size before and after, compression ratio - Save the converted image to a path specified by --output flag (default: same name with new extension) - If no input is given, generate a sample 800x600 PNG image with gradients, text, and transparency, then convert it to all other supported formats and display a comparison table - Handle errors: unsupported formats, corrupted images, insufficient disk space, invalid resize dimensions Create a complete C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include: - Source code - CMakeLists.txt with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
CMakeLists.txt
cmake_minimum_required(VERSION 3.22) project(image_format_converter_cpp VERSION 1.0.0 LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) include(FetchContent) # Pinned OpenCV source FetchContent_Declare( opencv URL https://github.com/opencv/opencv/archive/refs/tags/4.10.0.zip ) set(BUILD_LIST "core,imgproc,imgcodecs" CACHE STRING "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(BUILD_TESTS OFF CACHE BOOL "" FORCE) set(BUILD_PERF_TESTS OFF CACHE BOOL "" FORCE) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(opencv) add_executable(image_converter src/main.cpp) target_link_libraries(image_converter PRIVATE opencv_core opencv_imgproc opencv_imgcodecs)
README.md
# Image Format Converter (C++) Converts images among PNG/JPEG/BMP/TIFF/WebP using OpenCV with quality, resize, alpha handling, batch mode, and sample demo generation. ## Requirements - Ubuntu 22.04 - G++ 12+ - CMake 3.22+ ## Dependencies (Pinned) - OpenCV `4.10.0` (fetched and built via `FetchContent`) ## Build ```bash cmake -S . -B build cmake --build build --config Release ``` ## Run ```bash ./build/image_converter input.png webp --quality 90 ./build/image_converter input.png jpeg --background "#ffffff" --resize 800x600 --maintain-aspect true ./build/image_converter input.webp png --strip-metadata ./build/image_converter ./images jpeg --batch --filter png --quality 80 ./build/image_converter ``` ## Features - PNG/JPEG/BMP/TIFF/WebP conversion - `--quality` for lossy output (JPEG/WebP) - `--resize WxH` and `--maintain-aspect` support - Alpha flattening with `--background` - Batch conversion with optional input-format filter - Conversion details per output - `--output` path override - No-args demo sample + format comparison table
src/main.cpp
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
namespace {
const std::vector<std::string> SUPPORTED = {"png", "jpeg", "jpg", "bmp", "tiff", "webp"};
struct ParsedArgs {
std::map<std::string, std::string> options;
std::vector<std::string> positional;
};
struct Details {
std::filesystem::path input;
std::filesystem::path output;
std::string inputFmt;
std::string outputFmt;
std::string origDim;
std::string newDim;
std::uintmax_t before;
std::uintmax_t after;
double ratio;
};
ParsedArgs parseArgs(int argc, char** argv) {
ParsedArgs out;
for (int i = 1; i < argc; i++) {
std::string t = argv[i];
if (t.rfind("--", 0) == 0) {
std::string k = t.substr(2);
if (i + 1 < argc && std::string(argv[i + 1]).rfind("--", 0) != 0) out.options[k] = argv[++i];
else out.options[k] = "true";
} else out.positional.push_back(t);
}
return out;
}
std::string normalizeFormat(std::string f) {
std::transform(f.begin(), f.end(), f.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
if (f == "jpg") f = "jpeg";
if (std::find(SUPPORTED.begin(), SUPPORTED.end(), f) == SUPPORTED.end()) throw std::runtime_error("Unsupported format: " + f);
return f;
}
std::pair<int, int> parseResize(const std::string& s) {
auto pos = s.find('x');
if (pos == std::string::npos) throw std::runtime_error("Invalid --resize. Use WxH.");
return {std::stoi(s.substr(0, pos)), std::stoi(s.substr(pos + 1))};
}
bool boolOpt(const ParsedArgs& args, const std::string& key, bool def) {
auto it = args.options.find(key);
if (it == args.options.end()) return def;
std::string v = it->second;
std::transform(v.begin(), v.end(), v.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return !(v == "false" || v == "0" || v == "no");
}
cv::Scalar parseColor(const std::string& raw) {
if (raw.empty() || raw == "white") return cv::Scalar(255, 255, 255, 255);
if (raw[0] == '#' && raw.size() == 7) {
int r = std::stoi(raw.substr(1, 2), nullptr, 16);
int g = std::stoi(raw.substr(3, 2), nullptr, 16);
int b = std::stoi(raw.substr(5, 2), nullptr, 16);
return cv::Scalar(b, g, r, 255);
}
return cv::Scalar(255, 255, 255, 255);
}
std::filesystem::path outputPath(const std::filesystem::path& input, const std::string& target, const ParsedArgs& args) {
if (args.options.count("output")) return std::filesystem::absolute(args.options.at("output"));
return input.parent_path() / (input.stem().string() + "." + target);
}
cv::Mat flattenAlpha(const cv::Mat& src, const cv::Scalar& bg) {
if (src.channels() < 4) return src.clone();
cv::Mat bgr(src.rows, src.cols, CV_8UC3);
for (int y = 0; y < src.rows; y++) {
for (int x = 0; x < src.cols; x++) {
cv::Vec4b p = src.at<cv::Vec4b>(y, x);
double a = p[3] / 255.0;
cv::Vec3b o;
o[0] = static_cast<unsigned char>(p[0] * a + bg[0] * (1.0 - a));
o[1] = static_cast<unsigned char>(p[1] * a + bg[1] * (1.0 - a));
o[2] = static_cast<unsigned char>(p[2] * a + bg[2] * (1.0 - a));
bgr.at<cv::Vec3b>(y, x) = o;
}
}
return bgr;
}
Details convertOne(const std::filesystem::path& in, const std::string& target, const ParsedArgs& args) {
const int quality = std::stoi(args.options.count("quality") ? args.options.at("quality") : "85");
if (quality < 1 || quality > 100) throw std::runtime_error("Quality must be in range 1..100.");
cv::Mat img = cv::imread(in.string(), cv::IMREAD_UNCHANGED);
if (img.empty()) throw std::runtime_error("Unsupported/corrupted image: " + in.string());
const std::string inputFmt = normalizeFormat(in.extension().string().substr(1));
const std::string outputFmt = normalizeFormat(target);
const auto before = std::filesystem::file_size(in);
const std::string origDim = std::to_string(img.cols) + "x" + std::to_string(img.rows);
cv::Mat working = img;
if (args.options.count("resize")) {
auto [w, h] = parseResize(args.options.at("resize"));
bool maintain = boolOpt(args, "maintain-aspect", true);
cv::Size sz(w, h);
if (maintain) {
double sx = static_cast<double>(w) / working.cols;
double sy = static_cast<double>(h) / working.rows;
double s = std::min(sx, sy);
sz = cv::Size(std::max(1, static_cast<int>(working.cols * s)), std::max(1, static_cast<int>(working.rows * s)));
}
cv::resize(working, working, sz, 0, 0, cv::INTER_CUBIC);
}
if ((outputFmt == "jpeg" || outputFmt == "bmp") && working.channels() == 4) {
working = flattenAlpha(working, parseColor(args.options.count("background") ? args.options.at("background") : "white"));
}
if ((outputFmt == "jpeg" || outputFmt == "bmp") && working.channels() == 1) cv::cvtColor(working, working, cv::COLOR_GRAY2BGR);
std::vector<int> params;
if (outputFmt == "jpeg") params = {cv::IMWRITE_JPEG_QUALITY, quality};
else if (outputFmt == "webp") params = {cv::IMWRITE_WEBP_QUALITY, quality};
else if (outputFmt == "tiff") params = {cv::IMWRITE_TIFF_COMPRESSION, 1};
auto out = outputPath(in, outputFmt, args);
std::filesystem::create_directories(out.parent_path());
if (!cv::imwrite(out.string(), working, params)) throw std::runtime_error("Failed writing output image.");
auto after = std::filesystem::file_size(out);
return {
in, out, inputFmt, outputFmt,
origDim, std::to_string(working.cols) + "x" + std::to_string(working.rows),
before, after, before == 0 ? 1.0 : static_cast<double>(after) / static_cast<double>(before)
};
}
void printDetails(const Details& d) {
std::cout << "Input: " << d.input << "\n";
std::cout << "Output: " << d.output << "\n";
std::cout << "Format: " << d.inputFmt << " -> " << d.outputFmt << "\n";
std::cout << "Dimensions: " << d.origDim << " -> " << d.newDim << "\n";
std::cout << "Size: " << d.before << " -> " << d.after << " bytes\n";
std::cout << "Compression ratio: " << std::fixed << std::setprecision(4) << d.ratio << "\n\n";
}
void batchConvert(const ParsedArgs& args, const std::filesystem::path& dir, const std::string& target) {
std::string filter;
if (args.options.count("filter")) filter = normalizeFormat(args.options.at("filter"));
for (const auto& e : std::filesystem::directory_iterator(dir)) {
if (!e.is_regular_file()) continue;
std::string ext = e.path().extension().string();
if (!ext.empty() && ext[0] == '.') ext.erase(0, 1);
std::string fmt = normalizeFormat(ext);
if (!filter.empty() && fmt != filter) continue;
ParsedArgs per = args;
per.options.erase("output");
printDetails(convertOne(e.path(), target, per));
}
}
void runDemo() {
cv::Mat demo(600, 800, CV_8UC4);
for (int y = 0; y < demo.rows; y++) {
for (int x = 0; x < demo.cols; x++) {
unsigned char r = static_cast<unsigned char>(255.0 * y / demo.rows);
unsigned char b = static_cast<unsigned char>(255 - r);
demo.at<cv::Vec4b>(y, x) = cv::Vec4b(b, 120, r, 255);
}
}
cv::circle(demo, {250, 240}, 170, cv::Scalar(255, 255, 255, 100), -1);
cv::rectangle(demo, {430, 140}, {650, 360}, cv::Scalar(0, 0, 0, 100), -1);
cv::putText(demo, "Image Converter Demo", {180, 560}, cv::FONT_HERSHEY_SIMPLEX, 1.2, cv::Scalar(255, 255, 255, 255), 2);
auto sample = std::filesystem::absolute("sample_800x600.png");
cv::imwrite(sample.string(), demo);
std::cout << "format,orig_dim,new_dim,size_before,size_after,ratio\n";
ParsedArgs args;
args.options["quality"] = "85";
args.options["background"] = "white";
args.options["maintain-aspect"] = "true";
for (const std::string& f : {"jpeg", "bmp", "tiff", "webp"}) {
auto d = convertOne(sample, f, args);
std::cout << d.outputFmt << "," << d.origDim << "," << d.newDim << "," << d.before << "," << d.after << "," << std::fixed << std::setprecision(4) << d.ratio << "\n";
}
}
} // namespace
int main(int argc, char** argv) {
ParsedArgs args = parseArgs(argc, argv);
try {
if (args.positional.empty() && !args.options.count("batch")) {
runDemo();
return 0;
}
if (args.options.count("batch")) {
if (args.positional.size() < 2) throw std::runtime_error("Batch usage: --batch <directory> <target-format>");
batchConvert(args, std::filesystem::absolute(args.positional[0]), normalizeFormat(args.positional[1]));
return 0;
}
if (args.positional.size() < 2) throw std::runtime_error("Usage: <input-image> <target-format> [flags]");
printDetails(convertOne(std::filesystem::absolute(args.positional[0]), normalizeFormat(args.positional[1]), args));
return 0;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
}