UNPKG

ai-pp3

Version:

CLI tool combining multimodal AI analysis with RawTherapee's engine to generate optimized PP3 profiles for RAW photography

184 lines 8.23 kB
// Generation helper functions extracted from agent.ts import path from "node:path"; import fs from "node:fs"; import { convertDngToImageWithPP3 } from "../raw-therapee-wrap.js"; import { processAIGeneration, evaluateGenerations } from "./ai-processor.js"; /** * Helper function to log generation progress */ export function logGenerationProgress(verbose, visionModel, models, generations, index, currentModel) { if (!verbose) return; if (index === undefined) { // Initial log if (Array.isArray(visionModel)) { console.log(`Generating ${String(models.length)} PP3 profiles using different models...`); } else { console.log(`Generating ${String(generations)} different PP3 profiles...`); } return; } // Per-generation log if (Array.isArray(visionModel) && currentModel) { console.log(`Generating PP3 profile ${String(index + 1)}/${String(models.length)} using model: ${currentModel}...`); } else { console.log(`Generating PP3 profile ${String(index + 1)}/${String(generations)}...`); } } /** * Helper function to generate a single PP3 profile */ export async function generateSinglePP3Profile(inputPath, basePP3Path, sections, providerName, currentModel, prompt, preset, maxRetries, verbose, index, baseName, directoryName, previewPath, previewFormat, previewQuality, previewExtension, isMultiModel) { try { const pp3Content = await processAIGeneration(previewPath, basePP3Path, sections, providerName, currentModel, prompt, preset, maxRetries, verbose); // Create file names with model info if using multiple models const modelSuffix = isMultiModel ? `_${currentModel.replaceAll(/[^a-zA-Z0-9-]/g, "_")}` : ""; const pp3Path = path.join(directoryName, `${baseName}_gen${String(index + 1)}${modelSuffix}.pp3`); // Ensure the directory exists before writing files await fs.promises.mkdir(path.dirname(pp3Path), { recursive: true }); await fs.promises.writeFile(pp3Path, pp3Content); // Create evaluation image with same format/quality as preview for consistency const evaluationImagePath = path.join(directoryName, `${baseName}_gen${String(index + 1)}${modelSuffix}_eval.${previewExtension}`); // Ensure the directory exists before creating the evaluation image await fs.promises.mkdir(path.dirname(evaluationImagePath), { recursive: true, }); await convertDngToImageWithPP3({ input: inputPath, output: evaluationImagePath, pp3Path, format: previewFormat, quality: previewQuality, }); return { pp3Content, pp3Path, processedImagePath: "", evaluationImagePath, generationIndex: index, success: true, }; } catch (error) { if (verbose) { console.warn(`Failed to generate PP3 profile ${String(index + 1)}:`, error); } // Return a failed generation entry to track it return { pp3Content: "", pp3Path: "", processedImagePath: "", evaluationImagePath: "", generationIndex: index, success: false, }; } } /** * Helper function to log multi-generation analysis */ export function logMultiGenerationAnalysis(verbose, inputPath, providerName, visionModel, models, generations) { if (!verbose) return; if (Array.isArray(visionModel)) { console.log(`Analyzing image ${inputPath} with ${providerName} using ${String(models.length)} different models: ${models.join(", ")}`); } else { console.log(`Analyzing image ${inputPath} with ${providerName} model ${String(visionModel)} for ${String(generations)} generations`); } } /** * Helper function to log single generation analysis */ export function logSingleGenerationAnalysis(verbose, inputPath, providerName, visionModel) { if (!verbose) return; if (Array.isArray(visionModel)) { console.log(`Analyzing image ${inputPath} with ${providerName} models: ${visionModel.join(", ")}`); } else { console.log(`Analyzing image ${inputPath} with ${providerName} model ${String(visionModel)}`); } } /** * Generates multiple PP3 profiles and evaluates them */ export async function generateMultiplePP3Profiles(inputPath, basePP3Path, sections, providerName, visionModel, prompt, preset, maxRetries, verbose, generations, previewPath, previewFormat, previewQuality, outputFormat, outputQuality, tiffCompression, bitDepth) { const generationResults = []; const extension = inputPath.slice(inputPath.lastIndexOf(".")); const baseName = path.basename(inputPath, extension); const directoryName = path.dirname(inputPath); const previewExtension = previewFormat === "png" ? "png" : "jpg"; // Handle multiple models const models = Array.isArray(visionModel) ? visionModel : [visionModel]; const actualGenerations = Array.isArray(visionModel) ? models.length : generations; // Log initial progress logGenerationProgress(verbose, visionModel, models, generations); // Generate multiple PP3 profiles for (let index = 0; index < actualGenerations; index++) { // If using multiple models, select the appropriate model for this generation const currentModel = Array.isArray(visionModel) ? models[index] : visionModel; // Log per-generation progress logGenerationProgress(verbose, visionModel, models, generations, index, currentModel); // Generate a single PP3 profile const result = await generateSinglePP3Profile(inputPath, basePP3Path, sections, providerName, currentModel, prompt, preset, maxRetries, verbose, index, baseName, directoryName, previewPath, previewFormat, previewQuality, previewExtension, Array.isArray(visionModel)); if (result) { generationResults.push(result); } } const successfulGenerations = generationResults.filter((result) => result.success); if (successfulGenerations.length === 0) { throw new Error("Failed to generate any successful PP3 profiles"); } // Use AI to evaluate and select the best result const { bestIndex, evaluationReason } = await evaluateGenerations(generationResults, providerName, visionModel, maxRetries, verbose); // Get the winning generation const winningGeneration = successfulGenerations[bestIndex]; // Extract model name from the pp3Path if it was generated with multiple models let modelSuffix = ""; if (Array.isArray(visionModel)) { // Extract model name from the pp3Path const pp3FileName = path.basename(winningGeneration.pp3Path); const modelMatch = /_gen\d+_(.+?)\.pp3$/.exec(pp3FileName); if (modelMatch?.[1]) { modelSuffix = `_${modelMatch[1]}`; } } // Generate final output image with the winning PP3 const finalOutputPath = path.join(directoryName, `${baseName}_final${modelSuffix}.${outputFormat}`); if (verbose) { console.log(`Generating final output image with winning PP3...`); console.log(`Final output path: ${finalOutputPath}`); } // Ensure the output directory exists await fs.promises.mkdir(path.dirname(finalOutputPath), { recursive: true }); await convertDngToImageWithPP3({ input: inputPath, output: finalOutputPath, pp3Path: generationResults[bestIndex].pp3Path, format: outputFormat, quality: outputQuality, tiffCompression, bitDepth, }); if (verbose) { console.log(`Final output image created at ${finalOutputPath}`); } // Update the winning generation's processedImagePath to point to the final output generationResults[bestIndex].processedImagePath = finalOutputPath; return { bestResult: generationResults[bestIndex], allResults: generationResults, evaluationReason, finalOutputPath, }; } //# sourceMappingURL=generation-helpers.js.map