ai-pp3
Version:
CLI tool combining multimodal AI analysis with RawTherapee's engine to generate optimized PP3 profiles for RAW photography
102 lines • 4.83 kB
JavaScript
import { Command } from "commander";
import { convertDngToImageWithPP3 } from "./raw-therapee-wrap.js";
import { generatePP3FromRawImage } from "./agent.js";
import fs from "node:fs";
import packageJson from "../package.json" with { type: "json" };
export async function processImage(inputPath, options = {}) {
if (!inputPath) {
throw new Error("Input path cannot be empty");
}
// Validate input file exists and is readable
try {
await fs.promises.access(inputPath, fs.constants.R_OK);
}
catch (error) {
if (error instanceof Error && "code" in error) {
if (error.code === "ENOENT") {
throw new Error(`Input file not found: ${inputPath}`);
}
else if (error.code === "EACCES") {
throw new Error(`Permission denied reading input file: ${inputPath}`);
}
throw error;
}
}
// Generate PP3 content
const pp3Content = await generatePP3FromRawImage({
inputPath,
basePP3Path: options.base,
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
providerName: options.provider || "openai",
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
visionModel: options.model || "gpt-4-vision-preview",
verbose: options.verbose,
keepPreview: options.keepPreview,
prompt: options.prompt,
sections: options.sections?.split(",").filter((s) => s.trim() !== ""),
previewQuality: options.previewQuality,
});
if (!pp3Content) {
throw new Error("Failed to generate PP3 content");
}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const pp3Path = options.output || inputPath.replace(/\.[^.]+$/, ".pp3");
await fs.promises.writeFile(pp3Path, pp3Content);
// Handle PP3-only mode
if (options.pp3Only) {
return;
}
// Process image with PP3
// eslint-disable-next-line unicorn/no-nested-ternary
const format = options.png ? "png" : options.tiff ? "tiff" : "jpeg";
const outputPath = options.output ?? inputPath.replace(/\.[^.]+$/, `_processed.${format}`);
await convertDngToImageWithPP3({
input: inputPath,
output: outputPath,
pp3Path,
format,
tiffCompression: options.compression,
bitDepth: Number(options.bitDepth),
});
}
// Create the CLI program
const program = new Command();
program
.name("ai-pp3")
.description("AI-Powered PP3 Profile Generator for RawTherapee\nSpecializes in bulk generation and customization of PP3 development profiles\nKey features:\n- AI-driven analysis of RAW files (DNG/NEF/CR2/ARW)\n- Batch PP3 creation with consistent processing parameters\n- Customizable development settings through natural language prompts\n- Seamless integration with existing PP3 workflows\n- Multi-model support for different processing styles\n- Interactive preview generation with quality controls\nDocumentation available in README for advanced customization")
.version(packageJson.version)
.argument("<input>", "Input RAW file path")
.option("-o, --output <path>", "Output file path (defaults to input.pp3 or input_processed.jpg)")
.option("--pp3-only", "Only generate PP3 file without processing the image")
.option("-p, --prompt <text>", "Prompt text for AI analysis")
.option("--provider <n>", "AI provider to use", "openai")
.option("--model <n>", "Model name to use", "gpt-4-vision-preview")
.option("-v, --verbose", "Enable verbose logging")
.option("-k, --keep-preview", "Keep the preview.jpg file after processing")
.option("-q, --quality <n>", "Quality of the output image (JPEG only)")
.option("--preview-quality <n>", "JPEG quality for preview generation (1-100)", (value) => {
const quality = Number.parseInt(value, 10);
if (Number.isNaN(quality) || quality < 1 || quality > 100) {
throw new Error("Preview quality must be between 1 and 100");
}
return quality;
})
.option("--tiff", "Output as TIFF format")
.option("--png", "Output as PNG format")
.option("--compression <type>", "TIFF compression type (z/none)")
.option("--bit-depth <n>", "Bit depth (8 or 16)", "16")
.option("--sections <names>", "Comma-separated list of PP3 sections to process", (value) => value.split(","))
.option("--base <path>", "Base PP3 file to improve upon")
.action(async (input, options) => {
try {
await processImage(input, options);
}
catch (error_) {
const error = error_ instanceof Error ? error_ : new Error("Unknown error occurred");
console.error("Error:", error.message);
process.exit(1);
}
});
program.parse();
//# sourceMappingURL=bin.js.map