From 9029655a545f8e0b37974dbe7c1c94306234ddd0 Mon Sep 17 00:00:00 2001 From: vmobilis <75476228+vmobilis@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:56:31 +0300 Subject: [PATCH] feat: support numbering for preview images (#1895) --- examples/cli/main.cpp | 55 ++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/examples/cli/main.cpp b/examples/cli/main.cpp index 43719abb..d77356b1 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,29 +377,6 @@ bool load_images_from_dir(const std::string dir, return true; } -void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy, void* data) { - (void)step; - (void)is_noisy; - SDCliParams* cli_params = (SDCliParams*)data; - // 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, - 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()); - } - } 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) { - LOG_ERROR("save preview video to '%s' failed", cli_params->preview_path.c_str()); - } - } -} - std::string format_frame_idx(std::string pattern, int frame_idx) { std::smatch match; std::string result = pattern; @@ -419,6 +396,36 @@ std::string format_frame_idx(std::string pattern, int frame_idx) { 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; + SDCliParams* cli_params = (SDCliParams*)data; + // 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) { + 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", 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) { + LOG_ERROR("save preview video to '%s' failed", cli_params->preview_path.c_str()); + } + } +} + 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;