Skip to content

Commit df4efe2

Browse files
authored
feat: add png sequence output for vid_gen (#1117)
1 parent 860a78e commit df4efe2

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

examples/cli/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
usage: ./bin/sd-cli [options]
55
66
CLI Options:
7-
-o, --output <string> path to write result image to (default: ./output.png)
7+
-o, --output <string> path to write result image to. you can use printf-style %d format specifiers for image sequences (default: ./output.png) (eg. output_%03d.png)
8+
--output-begin-idx <int> starting index for output image sequence, must be non-negative (default 0 if specified %d in output path, 1 otherwise)
89
--preview-path <string> path to write preview image to (default: ./preview.png)
910
--preview-interval <int> interval in denoising steps between consecutive updates of the image preview file (default is 1, meaning updating at
1011
every step)

examples/cli/main.cpp

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ const char* previews_str[] = {
2626
"vae",
2727
};
2828

29+
std::regex format_specifier_regex("(?:[^%]|^)(?:%%)*(%\\d{0,3}d)");
30+
2931
struct SDCliParams {
3032
SDMode mode = IMG_GEN;
3133
std::string output_path = "output.png";
34+
int output_begin_idx = -1;
3235

3336
bool verbose = false;
3437
bool canny_preprocess = false;
@@ -50,7 +53,7 @@ struct SDCliParams {
5053
options.string_options = {
5154
{"-o",
5255
"--output",
53-
"path to write result image to (default: ./output.png)",
56+
"path to write result image to. you can use printf-style %d format specifiers for image sequences (default: ./output.png) (eg. output_%03d.png)",
5457
&output_path},
5558
{"",
5659
"--preview-path",
@@ -63,6 +66,10 @@ struct SDCliParams {
6366
"--preview-interval",
6467
"interval in denoising steps between consecutive updates of the image preview file (default is 1, meaning updating at every step)",
6568
&preview_interval},
69+
{"",
70+
"--output-begin-idx",
71+
"starting index for output image sequence, must be non-negative (default 0 if specified %d in output path, 1 otherwise)",
72+
&output_begin_idx},
6673
};
6774

6875
options.bool_options = {
@@ -344,6 +351,25 @@ void step_callback(int step, int frame_count, sd_image_t* image, bool is_noisy,
344351
}
345352
}
346353

354+
std::string format_frame_idx(std::string pattern, int frame_idx) {
355+
std::smatch match;
356+
std::string result = pattern;
357+
while (std::regex_search(result, match, format_specifier_regex)) {
358+
std::string specifier = match.str(1);
359+
char buffer[32];
360+
snprintf(buffer, sizeof(buffer), specifier.c_str(), frame_idx);
361+
result.replace(match.position(1), match.length(1), buffer);
362+
}
363+
364+
// Then replace all '%%' with '%'
365+
size_t pos = 0;
366+
while ((pos = result.find("%%", pos)) != std::string::npos) {
367+
result.replace(pos, 2, "%");
368+
pos += 1;
369+
}
370+
return result;
371+
}
372+
347373
int main(int argc, const char* argv[]) {
348374
if (argc > 1 && std::string(argv[1]) == "--version") {
349375
std::cout << version_string() << "\n";
@@ -719,25 +745,59 @@ int main(int argc, const char* argv[]) {
719745
is_jpg = false;
720746
}
721747

722-
if (cli_params.mode == VID_GEN && num_results > 1) {
723-
std::string vid_output_path = cli_params.output_path;
724-
if (file_ext_lower == ".png") {
725-
vid_output_path = base_path + ".avi";
748+
if (std::regex_search(cli_params.output_path, format_specifier_regex)) {
749+
std::string final_output_path = cli_params.output_path;
750+
if (cli_params.output_begin_idx == -1) {
751+
cli_params.output_begin_idx = 0;
752+
}
753+
// writing image sequence, default to PNG
754+
if (!is_jpg && file_ext_lower != ".png") {
755+
base_path += file_ext;
756+
file_ext = ".png";
726757
}
727-
create_mjpg_avi_from_sd_images(vid_output_path.c_str(), results, num_results, gen_params.fps);
728-
LOG_INFO("save result MJPG AVI video to '%s'\n", vid_output_path.c_str());
758+
final_output_path = base_path + file_ext;
759+
for (int i = 0; i < num_results; i++) {
760+
if (results[i].data == nullptr) {
761+
continue;
762+
}
763+
std::string final_image_path = format_frame_idx(final_output_path, cli_params.output_begin_idx + i);
764+
if (is_jpg) {
765+
int write_ok = stbi_write_jpg(final_image_path.c_str(), results[i].width, results[i].height, results[i].channel,
766+
results[i].data, 90, get_image_params(cli_params, ctx_params, gen_params, gen_params.seed + i).c_str());
767+
LOG_INFO("save result JPEG image %d to '%s' (%s)", i, final_image_path.c_str(), write_ok == 0 ? "failure" : "success");
768+
} else {
769+
int write_ok = stbi_write_png(final_image_path.c_str(), results[i].width, results[i].height, results[i].channel,
770+
results[i].data, 0, get_image_params(cli_params, ctx_params, gen_params, gen_params.seed + i).c_str());
771+
LOG_INFO("save result PNG image %d to '%s' (%s)", i, final_image_path.c_str(), write_ok == 0 ? "failure" : "success");
772+
}
773+
}
774+
} else if (cli_params.mode == VID_GEN && num_results > 1) {
775+
std::string final_output_path = cli_params.output_path;
776+
if (file_ext_lower != ".avi") {
777+
if (!is_jpg && file_ext_lower != ".png") {
778+
base_path += file_ext;
779+
}
780+
file_ext = ".avi";
781+
final_output_path = base_path + file_ext;
782+
}
783+
create_mjpg_avi_from_sd_images(final_output_path.c_str(), results, num_results, gen_params.fps);
784+
LOG_INFO("save result MJPG AVI video to '%s'\n", final_output_path.c_str());
729785
} else {
730786
// appending ".png" to absent or unknown extension
731787
if (!is_jpg && file_ext_lower != ".png") {
732788
base_path += file_ext;
733789
file_ext = ".png";
734790
}
791+
if (cli_params.output_begin_idx == -1) {
792+
cli_params.output_begin_idx = 1;
793+
}
735794
for (int i = 0; i < num_results; i++) {
736795
if (results[i].data == nullptr) {
737796
continue;
738797
}
739798
int write_ok;
740-
std::string final_image_path = i > 0 ? base_path + "_" + std::to_string(i + 1) + file_ext : base_path + file_ext;
799+
std::string final_image_path;
800+
final_image_path = i > 0 ? base_path + "_" + std::to_string(cli_params.output_begin_idx + i) + file_ext : base_path + file_ext;
741801
if (is_jpg) {
742802
write_ok = stbi_write_jpg(final_image_path.c_str(), results[i].width, results[i].height, results[i].channel,
743803
results[i].data, 90, get_image_params(cli_params, ctx_params, gen_params, gen_params.seed + i).c_str());

0 commit comments

Comments
 (0)