Skip to content

Commit afec505

Browse files
ursgleejet
andauthored
feat: write generation parameter exif data into output png (leejet#57)
* Write generation parameter exif data into output pngs. This adds prompt, negative prompt (if nonempty) and other generation parameters to the output file as a tEXt PNG block, in the same format as AUTOMATIC1111 webui does. In order to keep everything free of external library dependencies, I have somewhat dirtily hacked this into the stb_image_write implementation. * Mention png text data in README.md, include "karras" in sampler text * add Steps/Model/RNG to parameter string --------- Co-authored-by: leejet <leejet714@gmail.com>
1 parent bd62138 commit afec505

File tree

3 files changed

+55
-8
lines changed

3 files changed

+55
-8
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Inference of [Stable Diffusion](https://github.com/CompVis/stable-diffusion) in
2727
- [`DPM++ 2M v2`](https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/8457)
2828
- `DPM++ 2S a`
2929
- Cross-platform reproducibility (`--rng cuda`, consistent with the `stable-diffusion-webui GPU RNG`)
30+
- Embedds generation parameters into png output as webui-compatible text string
3031
- Supported platforms
3132
- Linux
3233
- Mac OS

examples/main.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,18 @@ void parse_args(int argc, const char* argv[], Option* opt) {
365365
}
366366
}
367367

368+
std::string basename(const std::string& path) {
369+
size_t pos = path.find_last_of('/');
370+
if (pos != std::string::npos) {
371+
return path.substr(pos + 1);
372+
}
373+
pos = path.find_last_of('\\');
374+
if (pos != std::string::npos) {
375+
return path.substr(pos + 1);
376+
}
377+
return path;
378+
}
379+
368380
int main(int argc, const char* argv[]) {
369381
Option opt;
370382
parse_args(argc, argv, &opt);
@@ -437,7 +449,24 @@ int main(int argc, const char* argv[]) {
437449
return 1;
438450
}
439451

440-
stbi_write_png(opt.output_path.c_str(), opt.w, opt.h, 3, img.data(), 0);
452+
std::string parameter_string = opt.prompt + "\n";
453+
if (opt.negative_prompt.size() != 0) {
454+
parameter_string += "Negative prompt: " + opt.negative_prompt + "\n";
455+
}
456+
parameter_string += "Steps: " + std::to_string(opt.sample_steps) + ", ";
457+
parameter_string += "CFG scale: " + std::to_string(opt.cfg_scale) + ", ";
458+
parameter_string += "Seed: " + std::to_string(opt.seed) + ", ";
459+
parameter_string += "Size: " + std::to_string(opt.w) + "x" + std::to_string(opt.h) + ", ";
460+
parameter_string += "Model: " + basename(opt.model_path) + ", ";
461+
parameter_string += "RNG: " + std::string(rng_type_to_str[opt.rng_type]) + ", ";
462+
parameter_string += "Sampler: " + std::string(sample_method_str[opt.sample_method]);
463+
if (opt.schedule == KARRAS) {
464+
parameter_string += " karras";
465+
}
466+
parameter_string += ", ";
467+
parameter_string += "Version: stable-diffusion.cpp";
468+
469+
stbi_write_png(opt.output_path.c_str(), opt.w, opt.h, 3, img.data(), 0, parameter_string.c_str());
441470
printf("save result image to '%s'\n", opt.output_path.c_str());
442471

443472
return 0;

examples/stb_image_write.h

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ STBIWDEF int stbi_write_force_png_filter;
173173
#endif
174174

175175
#ifndef STBI_WRITE_NO_STDIO
176-
STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);
176+
STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes, const char* parameters = NULL);
177177
STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);
178178
STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);
179179
STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);
@@ -1125,9 +1125,10 @@ static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int
11251125
}
11261126
}
11271127

1128-
STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)
1128+
STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len, const char* parameters)
11291129
{
11301130
int force_filter = stbi_write_force_png_filter;
1131+
int param_length = 0;
11311132
int ctype[5] = { -1, 0, 4, 2, 6 };
11321133
unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };
11331134
unsigned char *out,*o, *filt, *zlib;
@@ -1177,10 +1178,15 @@ STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int s
11771178
STBIW_FREE(filt);
11781179
if (!zlib) return 0;
11791180

1181+
if(parameters != NULL) {
1182+
param_length = strlen(parameters);
1183+
param_length += strlen("parameters") + 1; // For the name and the null-byte
1184+
}
1185+
11801186
// each tag requires 12 bytes of overhead
1181-
out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);
1187+
out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12 + ((parameters)?(param_length+12):0));
11821188
if (!out) return 0;
1183-
*out_len = 8 + 12+13 + 12+zlen + 12;
1189+
*out_len = 8 + 12+13 + 12+zlen + 12 + ((parameters)?(param_length+12):0);
11841190

11851191
o=out;
11861192
STBIW_MEMMOVE(o,sig,8); o+= 8;
@@ -1195,6 +1201,17 @@ STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int s
11951201
*o++ = 0;
11961202
stbiw__wpcrc(&o,13);
11971203

1204+
if(parameters != NULL) {
1205+
stbiw__wp32(o, param_length);
1206+
stbiw__wptag(o, "tEXt");
1207+
STBIW_MEMMOVE(o, "parameters", strlen("parameters"));
1208+
o+=strlen("parameters");
1209+
*o++ = 0; // Null pyte separator
1210+
STBIW_MEMMOVE(o, parameters, strlen(parameters));
1211+
o+=strlen(parameters);
1212+
stbiw__wpcrc(&o, param_length);
1213+
}
1214+
11981215
stbiw__wp32(o, zlen);
11991216
stbiw__wptag(o, "IDAT");
12001217
STBIW_MEMMOVE(o, zlib, zlen);
@@ -1212,11 +1229,11 @@ STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int s
12121229
}
12131230

12141231
#ifndef STBI_WRITE_NO_STDIO
1215-
STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
1232+
STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes, const char* parameters)
12161233
{
12171234
FILE *f;
12181235
int len;
1219-
unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1236+
unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len, parameters);
12201237
if (png == NULL) return 0;
12211238

12221239
f = stbiw__fopen(filename, "wb");
@@ -1231,7 +1248,7 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const
12311248
STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
12321249
{
12331250
int len;
1234-
unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len);
1251+
unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len, NULL);
12351252
if (png == NULL) return 0;
12361253
func(context, png, len);
12371254
STBIW_FREE(png);

0 commit comments

Comments
 (0)