diff --git a/examples/cli/main.cpp b/examples/cli/main.cpp index 953a44e9d..654877f00 100644 --- a/examples/cli/main.cpp +++ b/examples/cli/main.cpp @@ -81,7 +81,7 @@ struct SDCliParams { &metadata_format}, {"", "--preview-path", - "path to write preview image to (default: ./preview.png). Multi-frame previews support .avi, .webm, and animated .webp", + "path to write preview image to (default: ./preview.png). For image generation, the filename can have %03d placeholder for sequential numbering. Multi-frame previews support .avi, .webm, and animated .webp", 0, &preview_path}, {"", @@ -377,6 +377,27 @@ bool load_images_from_dir(const std::string dir, return true; } +std::string format_frame_idx(std::string pattern, int frame_idx) { + std::smatch match; + std::string result = pattern; + while (std::regex_search(result, match, format_specifier_regex)) { + std::string specifier = match.str(1); + char buffer[32]; + snprintf(buffer, sizeof(buffer), specifier.c_str(), frame_idx); + result.replace(match.position(1), match.length(1), buffer); + } + + // Then replace all '%%' with '%' + size_t pos = 0; + while ((pos = result.find("%%", pos)) != std::string::npos) { + result.replace(pos, 2, "%"); + pos += 1; + } + return result; +} + +int continuous_preview_counter = 0; + void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy, void* data) { (void)step; (void)is_noisy; @@ -384,14 +405,19 @@ void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy, // is_noisy is set to true if the preview corresponds to noisy latents, false if it's denoised latents // unused in this app, it will either be always noisy or always denoised here if (frame_count == 1) { - if (!write_image_to_file(cli_params->preview_path, + fs::path path = cli_params->preview_path; + if (encoded_image_format_from_path(path.string()) == EncodedImageFormat::UNKNOWN) + path += ".png"; + if (std::regex_search(path.string(), format_specifier_regex)) + path = fs::path(format_frame_idx(path.string(), continuous_preview_counter++)); + if (!write_image_to_file(path.string(), image->data, image->width, image->height, image->channel, "", cli_params->compression_quality)) { - LOG_ERROR("save preview image to '%s' failed", cli_params->preview_path.c_str()); + LOG_ERROR("save preview image to '%s' failed", path.string().c_str()); } } else { if (create_video_from_sd_images(cli_params->preview_path.c_str(), image, frame_count, cli_params->preview_fps, cli_params->compression_quality) != 0) { @@ -400,25 +426,6 @@ void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy, } } -std::string format_frame_idx(std::string pattern, int frame_idx) { - std::smatch match; - std::string result = pattern; - while (std::regex_search(result, match, format_specifier_regex)) { - std::string specifier = match.str(1); - char buffer[32]; - snprintf(buffer, sizeof(buffer), specifier.c_str(), frame_idx); - result.replace(match.position(1), match.length(1), buffer); - } - - // Then replace all '%%' with '%' - size_t pos = 0; - while ((pos = result.find("%%", pos)) != std::string::npos) { - result.replace(pos, 2, "%"); - pos += 1; - } - return result; -} - static fs::path get_video_audio_sidecar_path(const SDCliParams& cli_params) { fs::path out_path = cli_params.output_path; fs::path base_path = out_path;