feat: add configurable image input preprocessing (#2028)

This commit is contained in:
leejet authored and GitHub committed 2026-09-23 02:12:49 +08:00
1 parent 2bb72947cb
commit 28b454bda1
19 files changed
+898 -227

No files matched your search

+6
View File
@@ -14,6 +14,12 @@ equivalent to `--log-level verbose`. If repeated, the last logging option wins.
For direct image repair or automatic post-generation YOLOv8 detection followed by cropped inpainting, see
[ADetailer](../../docs/adetailer.md).
Use repeatable `--image-preprocess` rules to select resizing, cropping, padding,
and resampling separately for each image input. Add `canny=true` to any input
rule for edge detection. See
[Image preprocessing](../../docs/image_preprocessing.md) for input selectors,
input defaults, downstream model processing, mask alignment, and examples.
Metadata mode inspects PNG/JPEG container metadata without loading any model:
```bash
+11 -51
View File
@@ -41,7 +41,6 @@ struct SDCliParams {
std::string metadata_format = "text";
sd_log_level_t log_level = SD_LOG_INFO;
bool canny_preprocess = false;
bool convert_name = false;
preview_t preview_method = PREVIEW_NONE;
@@ -107,10 +106,6 @@ struct SDCliParams {
};
options.bool_options = {
{"",
"--canny",
"apply canny preprocessor (edge detection)",
true, &canny_preprocess},
{"",
"--convert-name",
"convert tensor name (for convert mode)",
@@ -268,7 +263,6 @@ struct SDCliParams {
<< " metadata_format: \"" << metadata_format << "\",\n"
<< " log_level: " << log_level_name(log_level) << ",\n"
<< " color: " << (color ? "true" : "false") << ",\n"
<< " canny_preprocess: " << (canny_preprocess ? "true" : "false") << ",\n"
<< " convert_name: " << (convert_name ? "true" : "false") << ",\n"
<< " preview_method: " << previews_str[preview_method] << ",\n"
<< " preview_interval: " << preview_interval << ",\n"
@@ -328,9 +322,7 @@ void sd_log_cb(enum sd_log_level_t level, const char* log, void* data) {
bool load_images_from_dir(const std::string dir,
std::vector<SDImageOwner>& images,
int expected_width = 0,
int expected_height = 0,
int max_image_num = 0) {
int max_image_num = 0) {
if (!fs::exists(dir) || !fs::is_directory(dir)) {
LOG_ERROR("'%s' is not a valid directory\n", dir.c_str());
return false;
@@ -358,7 +350,7 @@ bool load_images_from_dir(const std::string dir,
int width = 0;
int height = 0;
int loaded_channel = 0;
uint8_t* image_buffer = load_image_from_file(path.c_str(), width, height, loaded_channel, expected_width, expected_height);
uint8_t* image_buffer = load_image_from_file(path.c_str(), width, height, loaded_channel, 0, 0);
if (image_buffer == nullptr) {
LOG_ERROR("load image from '%s' failed", path.c_str());
return false;
@@ -654,8 +646,8 @@ int main(int argc, const char* argv[]) {
SDContextParams ctx_params;
SDGenerationParams gen_params;
parse_args(argc, argv, cli_params, ctx_params, gen_params);
sd_set_log_callback(sd_log_cb, (void*)&cli_params);
parse_args(argc, argv, cli_params, ctx_params, gen_params);
if (cli_params.mode == METADATA) {
MetadataReadOptions options;
@@ -751,16 +743,8 @@ int main(int argc, const char* argv[]) {
auto load_image_and_update_size = [&](const std::string& path,
SDImageOwner& image,
bool resize_image = true,
int expected_channel = 3) -> bool {
int expected_width = 0;
int expected_height = 0;
if (resize_image && gen_params.width_and_height_are_set()) {
expected_width = gen_params.width;
expected_height = gen_params.height;
}
if (!load_sd_image_from_file(image.put(), path.c_str(), expected_width, expected_height, expected_channel)) {
if (!load_sd_image_from_file(image.put(), path.c_str(), 0, 0, expected_channel)) {
LOG_ERROR("load image from '%s' failed", path.c_str());
return false;
}
@@ -783,7 +767,7 @@ int main(int argc, const char* argv[]) {
if (gen_params.init_image_path.size() > 0) {
const bool native_init = cli_params.mode == IMG_GEN || cli_params.mode == ADETAILER;
if (!load_image_and_update_size(gen_params.init_image_path, gen_params.init_image, true, native_init ? 0 : 3)) {
if (!load_image_and_update_size(gen_params.init_image_path, gen_params.init_image, native_init ? 0 : 3)) {
return 1;
}
}
@@ -798,7 +782,7 @@ int main(int argc, const char* argv[]) {
gen_params.ref_images.clear();
for (auto& path : gen_params.ref_image_paths) {
SDImageOwner ref_image({0, 0, 0, nullptr});
if (!load_image_and_update_size(path, ref_image, false, 0)) {
if (!load_image_and_update_size(path, ref_image, 0)) {
return 1;
}
gen_params.ref_images.push_back(std::move(ref_image));
@@ -839,41 +823,22 @@ int main(int argc, const char* argv[]) {
if (gen_params.mask_image_path.size() > 0) {
if (!load_sd_image_from_file(gen_params.mask_image.put(),
gen_params.mask_image_path.c_str(),
gen_params.get_resolved_width(),
gen_params.get_resolved_height(),
0,
0,
1)) {
LOG_ERROR("load image from '%s' failed", gen_params.mask_image_path.c_str());
return 1;
}
} else {
sd_image_t generated_mask = {0, 0, 1, nullptr};
generated_mask.data = (uint8_t*)malloc(gen_params.get_resolved_width() * gen_params.get_resolved_height());
if (generated_mask.data == nullptr) {
LOG_ERROR("malloc mask image failed");
return 1;
}
generated_mask.width = gen_params.get_resolved_width();
generated_mask.height = gen_params.get_resolved_height();
memset(generated_mask.data, 255, gen_params.get_resolved_width() * gen_params.get_resolved_height());
gen_params.mask_image.reset(generated_mask);
}
if (gen_params.control_image_path.size() > 0) {
if (!load_sd_image_from_file(gen_params.control_image.put(),
gen_params.control_image_path.c_str(),
gen_params.get_resolved_width(),
gen_params.get_resolved_height())) {
0,
0)) {
LOG_ERROR("load image from '%s' failed", gen_params.control_image_path.c_str());
return 1;
}
if (cli_params.canny_preprocess) { // apply preprocessor
preprocess_canny(gen_params.control_image.get(),
0.08f,
0.08f,
0.8f,
1.0f,
false);
}
}
if (gen_params.ip_adapter_image_path.size() > 0) {
@@ -890,8 +855,6 @@ int main(int argc, const char* argv[]) {
gen_params.control_frames.clear();
if (!load_images_from_dir(gen_params.control_video_path,
gen_params.control_frames,
gen_params.get_resolved_width(),
gen_params.get_resolved_height(),
gen_params.video_frames)) {
return 1;
}
@@ -900,10 +863,7 @@ int main(int argc, const char* argv[]) {
if (!gen_params.pm_id_images_dir.empty()) {
gen_params.pm_id_images.clear();
if (!load_images_from_dir(gen_params.pm_id_images_dir,
gen_params.pm_id_images,
0,
0,
0)) {
gen_params.pm_id_images)) {
return 1;
}
}
+62 -56
View File
@@ -1128,6 +1128,9 @@ ArgOptions SDGenerationParams::get_options() {
"Key-value list to set up the way the reference images are processed (empty = auto-detect from model weigths)",
(int)',',
&ref_image_args},
{"", "--image-preprocess",
"Image preprocessing rule: target=init|end|mask|control|ref|ip-adapter|id|control-frame,index=N,mode=auto|none|stretch|crop|crop-resize|fit-pad,filter=auto|nearest|nearest-exact|bilinear|bicubic|lanczos,antialias=auto|true|false,width=W,height=H,anchor=center|top|bottom|left|right,pad_color=#RRGGBB[AA],canny=true|false. Repeat for multiple rules.",
(int)';', &image_preprocess},
};
options.int_options = {
@@ -1308,11 +1311,6 @@ ArgOptions SDGenerationParams::get_options() {
"automatically increase the indices of references images based on the order they are listed (starting with 1).",
true,
&increase_ref_index},
{"",
"--disable-auto-resize-ref-image",
"disable auto resize of ref images",
false,
&auto_resize_ref_image},
{"",
"--circular",
"enable circular padding on both axes for tileable output",
@@ -1870,8 +1868,6 @@ bool decode_base64_image(const std::string& encoded_input,
static bool parse_image_json_field(const json& parent,
const char* key,
int channels,
int expected_width,
int expected_height,
SDImageOwner& out_image) {
if (!parent.contains(key)) {
return true;
@@ -1883,14 +1879,12 @@ static bool parse_image_json_field(const json& parent,
if (!parent.at(key).is_string()) {
return false;
}
return decode_base64_image(parent.at(key).get<std::string>(), channels, expected_width, expected_height, out_image);
return decode_base64_image(parent.at(key).get<std::string>(), channels, 0, 0, out_image);
}
static bool parse_image_array_json_field(const json& parent,
const char* key,
int channels,
int expected_width,
int expected_height,
std::vector<SDImageOwner>& out_images) {
if (!parent.contains(key)) {
return true;
@@ -1909,7 +1903,7 @@ static bool parse_image_array_json_field(const json& parent,
return false;
}
SDImageOwner image;
if (!decode_base64_image(item.get<std::string>(), channels, expected_width, expected_height, image)) {
if (!decode_base64_image(item.get<std::string>(), channels, 0, 0, image)) {
return false;
}
out_images.push_back(std::move(image));
@@ -2008,6 +2002,29 @@ static bool resolve_model_file_from_dir(const std::string& model_name,
return false;
}
bool SDGenerationParams::parse_image_preprocess_json(const std::string& json_str) {
const auto value = json::parse(json_str, nullptr, false);
std::string rules;
if (value.is_string()) {
rules = value.get<std::string>();
} else if (value.is_array()) {
for (const auto& item : value) {
if (!item.is_string()) {
LOG_ERROR("image_preprocess must contain rule strings");
return false;
}
if (!rules.empty())
rules += ";";
rules += item.get<std::string>();
}
} else {
LOG_ERROR("image_preprocess must be a string or array of strings");
return false;
}
image_preprocess = std::move(rules);
return true;
}
bool SDGenerationParams::from_json_str(
const std::string& json_str,
const std::function<std::string(const std::string&)>& lora_path_resolver) {
@@ -2019,6 +2036,9 @@ bool SDGenerationParams::from_json_str(
return false;
}
if (j.contains("image_preprocess") && !parse_image_preprocess_json(j["image_preprocess"].dump()))
return false;
auto load_if_exists = [&](const char* key, auto& out) {
if (j.contains(key)) {
using T = std::decay_t<decltype(out)>;
@@ -2056,6 +2076,7 @@ bool SDGenerationParams::from_json_str(
load_if_exists("cache_mode", cache_mode);
load_if_exists("cache_option", cache_option);
load_if_exists("scm_mask", scm_mask);
load_if_exists("ref_image_args", ref_image_args);
load_if_exists("clip_skip", clip_skip);
load_if_exists("width", width);
@@ -2073,7 +2094,6 @@ bool SDGenerationParams::from_json_str(
load_if_exists("moe_boundary", moe_boundary);
load_if_exists("vace_strength", vace_strength);
load_if_exists("auto_resize_ref_image", auto_resize_ref_image);
load_if_exists("increase_ref_index", increase_ref_index);
load_if_exists("embed_image_metadata", embed_image_metadata);
@@ -2217,37 +2237,23 @@ bool SDGenerationParams::from_json_str(
LOG_ERROR("invalid lora");
return false;
}
if (!parse_image_json_field(j, "init_image", 0, width, height, init_image)) {
LOG_ERROR("invalid init_image");
auto load_image = [&](const char* key, int channels, SDImageOwner& image) {
if (!parse_image_json_field(j, key, channels, image)) {
LOG_ERROR("invalid %s", key);
return false;
}
return true;
};
if (!load_image("init_image", 0, init_image) ||
!load_image("end_image", 3, end_image) ||
!load_image("mask_image", 1, mask_image) ||
!load_image("control_image", 3, control_image) ||
!load_image("ip_adapter_image", 3, ip_adapter_image)) {
return false;
}
if (!parse_image_json_field(j, "end_image", 3, width, height, end_image)) {
LOG_ERROR("invalid end_image");
return false;
}
if (!parse_image_array_json_field(j,
"ref_images",
0,
auto_resize_ref_image ? width : 0,
auto_resize_ref_image ? height : 0,
ref_images)) {
LOG_ERROR("invalid ref_images");
return false;
}
if (!parse_image_array_json_field(j, "control_frames", 3, width, height, control_frames)) {
LOG_ERROR("invalid control_frames");
return false;
}
if (!parse_image_json_field(j, "mask_image", 1, width, height, mask_image)) {
LOG_ERROR("invalid mask_image");
return false;
}
if (!parse_image_json_field(j, "control_image", 3, width, height, control_image)) {
LOG_ERROR("invalid control_image");
return false;
}
if (!parse_image_json_field(j, "ip_adapter_image", 3, width, height, ip_adapter_image)) {
LOG_ERROR("invalid ip_adapter_image");
if (!parse_image_array_json_field(j, "ref_images", 0, ref_images) ||
!parse_image_array_json_field(j, "control_frames", 3, control_frames)) {
LOG_ERROR("invalid input image array");
return false;
}
@@ -2491,6 +2497,10 @@ bool SDGenerationParams::resolve(const std::string& lora_model_dir, const std::s
}
bool SDGenerationParams::validate(SDMode mode) {
if (!image_preprocess.empty() && mode != IMG_GEN && mode != VID_GEN) {
LOG_ERROR("--image-preprocess requires img_gen or vid_gen mode");
return false;
}
if (batch_count <= 0) {
LOG_ERROR("error: batch_count must be greater than 0");
return false;
@@ -2666,14 +2676,6 @@ sd_img_gen_params_t SDGenerationParams::to_sd_img_gen_params_t() {
pulid_id_weight,
};
if (!auto_resize_ref_image) {
if (!ref_image_args.empty()) {
ref_image_args += ",";
}
ref_image_args += "resize_before_vae=0";
LOG_WARN("Notice: --disable-auto-resize-ref-image is deprecated. Use --ref-image-args \"resize_before_vae=off\" instead.");
}
if (increase_ref_index) {
if (!ref_image_args.empty()) {
ref_image_args += ",";
@@ -2721,6 +2723,7 @@ sd_img_gen_params_t SDGenerationParams::to_sd_img_gen_params_t() {
params.hires.custom_sigmas_count = static_cast<int>(hires_custom_sigmas.size());
params.circular_x = circular || circular_x;
params.circular_y = circular || circular_y;
params.image_preprocess = {image_preprocess.c_str()};
return params;
}
@@ -2823,6 +2826,7 @@ sd_vid_gen_params_t SDGenerationParams::to_sd_vid_gen_params_t() {
params.hires.custom_sigmas_count = static_cast<int>(hires_custom_sigmas.size());
params.circular_x = circular || circular_x;
params.circular_y = circular || circular_y;
params.image_preprocess = {image_preprocess.c_str()};
return params;
}
@@ -2879,7 +2883,8 @@ std::string SDGenerationParams::to_string() const {
<< " ref_video_audio_paths: " << vec_str_to_string(ref_video_audio_paths) << ",\n"
<< " ref_audio_paths: " << vec_str_to_string(ref_audio_paths) << ",\n"
<< " control_video_path: \"" << control_video_path << "\",\n"
<< " auto_resize_ref_image: " << (auto_resize_ref_image ? "true" : "false") << ",\n"
<< " image_preprocess: " << image_preprocess << ",\n"
<< " ref_image_args: " << ref_image_args << ",\n"
<< " increase_ref_index: " << (increase_ref_index ? "true" : "false") << ",\n"
<< " pm_id_images_dir: \"" << pm_id_images_dir << "\",\n"
<< " pm_id_embed_path: \"" << pm_id_embed_path << "\",\n"
@@ -3030,12 +3035,13 @@ std::string build_sdcpp_image_metadata_json(const SDContextParams& ctx_params,
set_json_basename_if_not_empty(models, "control_net", ctx_params.control_net_path);
root["models"] = std::move(models);
root["clip_skip"] = gen_params.clip_skip;
root["strength"] = gen_params.strength;
root["control_strength"] = gen_params.control_strength;
root["ip_adapter_strength"] = gen_params.ip_adapter_strength;
root["auto_resize_ref_image"] = gen_params.auto_resize_ref_image;
root["increase_ref_index"] = gen_params.increase_ref_index;
root["clip_skip"] = gen_params.clip_skip;
root["strength"] = gen_params.strength;
root["control_strength"] = gen_params.control_strength;
root["ip_adapter_strength"] = gen_params.ip_adapter_strength;
root["ref_image_args"] = gen_params.ref_image_args;
root["image_preprocess"] = gen_params.image_preprocess;
root["increase_ref_index"] = gen_params.increase_ref_index;
if (mode == VID_GEN) {
root["video"] = {
{"frame_count", gen_params.video_frames},
+13 -12
View File
@@ -200,18 +200,17 @@ struct SDGenerationParams {
std::string ad_prompt;
std::string ad_negative_prompt;
std::string extra_ad_args;
int clip_skip = -1; // <= 0 represents unspecified
int width = -1;
int height = -1;
int batch_count = 1;
int qwen_image_layers = 3;
int64_t seed = 42;
float strength = 0.75f;
float control_strength = 0.9f;
float ip_adapter_strength = 1.0f;
bool auto_resize_ref_image = true;
bool increase_ref_index = false;
bool embed_image_metadata = true;
int clip_skip = -1; // <= 0 represents unspecified
int width = -1;
int height = -1;
int batch_count = 1;
int qwen_image_layers = 3;
int64_t seed = 42;
float strength = 0.75f;
float control_strength = 0.9f;
float ip_adapter_strength = 1.0f;
bool increase_ref_index = false;
bool embed_image_metadata = true;
std::string init_image_path;
std::string end_image_path;
@@ -247,6 +246,7 @@ struct SDGenerationParams {
std::string extra_tiling_args;
std::string ref_image_args;
std::string image_preprocess;
std::string pm_id_images_dir;
std::string pm_id_embed_path;
@@ -310,6 +310,7 @@ struct SDGenerationParams {
ArgOptions get_options();
bool from_json_str(const std::string& json_str,
const std::function<std::string(const std::string&)>& lora_path_resolver = {});
bool parse_image_preprocess_json(const std::string& json_str);
bool initialize_cache_params();
void extract_and_remove_lora(const std::string& lora_model_dir);
bool width_and_height_are_set() const;
+23 -11
View File
@@ -148,17 +148,17 @@ Native extension fields:
- any `sdcpp API` fields embedded through `sd_cpp_extra_args` inside `prompt`
Reference image sizing follows `auto_resize_ref_image`, as in the native and SDAPI APIs.
The server default is `true`; `--disable-auto-resize-ref-image` sets it to `false`.
When enabled, uploaded references are center-cropped and resized to the request dimensions.
If `size` is omitted, the first decoded image establishes those dimensions.
When disabled, each reference retains its original dimensions.
The init image and mask still use the request dimensions, independently of this option.
Uploaded images are decoded at their original dimensions. The first decoded
image establishes the generation dimensions if `size` is omitted. Input
geometry follows `image_preprocess`: references preserve their dimensions by
default, while init and mask use the generation canvas preset.
To override the server default for one request, include this in `prompt`:
Reference encoding then follows model presets and `ref_image_args`. To skip
input geometry for references and disable resizing before VAE encoding, include
this in `prompt`:
```text
edit this image <sd_cpp_extra_args>{"auto_resize_ref_image":false}</sd_cpp_extra_args>
edit this image <sd_cpp_extra_args>{"image_preprocess":"target=ref,mode=none","ref_image_args":"resize_before_vae=false"}</sd_cpp_extra_args>
```
Response fields:
@@ -539,7 +539,7 @@ LTX and Wan preserve causal state between temporal tiles. Hunyuan Video and TAEH
| Field | Type |
| --- | --- |
| `batch_count` | `integer` |
| `auto_resize_ref_image` | `boolean` |
| `ref_image_args` | `string` |
| `increase_ref_index` | `boolean` |
| `control_strength` | `number` |
| `ip_adapter_strength` | `number` |
@@ -666,7 +666,7 @@ Example:
"strength": 0.75,
"seed": -1,
"batch_count": 1,
"auto_resize_ref_image": true,
"ref_image_args": "",
"increase_ref_index": false,
"control_strength": 0.9,
"ip_adapter_strength": 1.0,
@@ -741,6 +741,17 @@ Example:
### Image Encoding Rules
Native image/video requests and SDAPI accept `image_preprocess` as a rule string
or array of rule strings. OpenAI-compatible requests can supply it in
`sd_cpp_extra_args`. See [Image preprocessing](../../docs/image_preprocessing.md)
for one-time input geometry, native-resolution decoding, mask alignment, and
`canny=true` for edge detection on any supported image input.
Image generation also accepts `ref_image_args` as a string (for example,
`"resize_before_vae=false"`) in native and SDAPI requests, or through
`sd_cpp_extra_args` in OpenAI-compatible requests. It controls downstream
reference encoding and is independent of input geometry rules.
Any image field accepts:
- a raw base64 string, or
@@ -776,7 +787,8 @@ Top-level scalar fields:
| `strength` | `number` |
| `seed` | `integer` |
| `batch_count` | `integer` |
| `auto_resize_ref_image` | `boolean` |
| `ref_image_args` | `string` |
| `image_preprocess` | `string \| array<string>` |
| `increase_ref_index` | `boolean` |
| `control_strength` | `number` |
| `ip_adapter_strength` | `number` |
+1 -1
View File
@@ -76,9 +76,9 @@ int main(int argc, const char** argv) {
SDSvrParams svr_params;
SDContextParams ctx_params;
SDGenerationParams default_gen_params;
parse_args(argc, argv, svr_params, ctx_params, default_gen_params);
sd_set_log_callback(sd_log_cb, (void*)&svr_params);
parse_args(argc, argv, svr_params, ctx_params, default_gen_params);
LOG_VERBOSE("version: %s", version_string().c_str());
LOG_VERBOSE("%s", sd_get_system_info());
+6 -42
View File
@@ -158,18 +158,6 @@ static bool build_openai_edit_request(const httplib::Request& req,
request.gen_params.batch_count = n;
std::string sd_cpp_extra_args_str = extract_and_remove_sd_cpp_extra_args(request.gen_params.prompt);
if (!sd_cpp_extra_args_str.empty()) {
const json extra_args = json::parse(sd_cpp_extra_args_str, nullptr, false);
if (extra_args.is_discarded()) {
error_message = "invalid sd_cpp_extra_args";
return false;
}
// Resolve resizing before decoding while keeping embedded image overrides last.
if (extra_args.contains("auto_resize_ref_image") && extra_args["auto_resize_ref_image"].is_boolean()) {
request.gen_params.auto_resize_ref_image = extra_args["auto_resize_ref_image"].get<bool>();
}
}
for (auto& bytes : images_bytes) {
int img_w = 0;
int img_h = 0;
@@ -178,12 +166,7 @@ static bool build_openai_edit_request(const httplib::Request& req,
reinterpret_cast<const char*>(bytes.data()),
static_cast<int>(bytes.size()),
img_w, img_h, resolved_channel,
request.gen_params.auto_resize_ref_image && request.gen_params.width_and_height_are_set()
? request.gen_params.width
: 0,
request.gen_params.auto_resize_ref_image && request.gen_params.width_and_height_are_set()
? request.gen_params.height
: 0,
0, 0,
0);
if (raw_pixels == nullptr) {
continue;
@@ -194,23 +177,10 @@ static bool build_openai_edit_request(const httplib::Request& req,
request.gen_params.set_width_and_height_if_unset(image_owner.get().width, image_owner.get().height);
if (is_first_ref_image) {
int init_w = 0;
int init_h = 0;
if (request.gen_params.width_and_height_are_set()) {
init_w = request.gen_params.width;
init_h = request.gen_params.height;
}
int init_img_w = 0;
int init_img_h = 0;
int init_resolved_channel = 0;
uint8_t* init_pixels = load_image_from_memory(
reinterpret_cast<const char*>(bytes.data()),
static_cast<int>(bytes.size()),
init_img_w, init_img_h, init_resolved_channel,
init_w, init_h, 0);
if (init_pixels != nullptr) {
request.gen_params.init_image.reset({(uint32_t)init_img_w, (uint32_t)init_img_h, (uint32_t)init_resolved_channel, init_pixels});
request.gen_params.init_image = image_owner;
if (request.gen_params.init_image.get().data == nullptr) {
error_message = "could not allocate init image";
return false;
}
}
@@ -218,12 +188,6 @@ static bool build_openai_edit_request(const httplib::Request& req,
}
if (!mask_bytes.empty()) {
int expected_width = 0;
int expected_height = 0;
if (request.gen_params.width_and_height_are_set()) {
expected_width = request.gen_params.width;
expected_height = request.gen_params.height;
}
int mask_w = 0;
int mask_h = 0;
int mask_channel = 0;
@@ -232,7 +196,7 @@ static bool build_openai_edit_request(const httplib::Request& req,
reinterpret_cast<const char*>(mask_bytes.data()),
static_cast<int>(mask_bytes.size()),
mask_w, mask_h, mask_channel,
expected_width, expected_height, 1);
0, 0, 1);
request.gen_params.mask_image.reset({(uint32_t)mask_w, (uint32_t)mask_h, 1, mask_raw});
const sd_image_t& mask_image = request.gen_params.mask_image.get();
request.gen_params.set_width_and_height_if_unset(mask_image.width, mask_image.height);
+19 -27
View File
@@ -80,17 +80,6 @@ static enum sample_method_t get_sdapi_sample_method(std::string name) {
return it != hardcoded.end() ? it->second : SAMPLE_METHOD_COUNT;
}
static void assign_solid_mask(SDImageOwner& mask_owner, int width, int height) {
const size_t pixel_count = static_cast<size_t>(width) * static_cast<size_t>(height);
uint8_t* raw_mask = static_cast<uint8_t*>(malloc(pixel_count));
if (raw_mask == nullptr) {
mask_owner.reset({0, 0, 1, nullptr});
return;
}
std::memset(raw_mask, 255, pixel_count);
mask_owner.reset({(uint32_t)width, (uint32_t)height, 1, raw_mask});
}
static bool build_sdapi_img_gen_request(const json& j,
ServerRuntime& runtime,
bool img2img,
@@ -193,15 +182,25 @@ static bool build_sdapi_img_gen_request(const json& j,
}
}
if (img2img) {
const int expected_width = request.gen_params.width_and_height_are_set() ? request.gen_params.width : 0;
const int expected_height = request.gen_params.width_and_height_are_set() ? request.gen_params.height : 0;
if (j.contains("ref_image_args")) {
if (!j["ref_image_args"].is_string()) {
error_message = "ref_image_args must be a string";
return false;
}
request.gen_params.ref_image_args = j["ref_image_args"].get<std::string>();
}
if (j.contains("image_preprocess") && !request.gen_params.parse_image_preprocess_json(j["image_preprocess"].dump())) {
error_message = "invalid image_preprocess";
return false;
}
if (img2img) {
if (j.contains("init_images") && j["init_images"].is_array() && !j["init_images"].empty()) {
if (decode_base64_image(j["init_images"][0].get<std::string>(),
0,
expected_width,
expected_height,
0,
0,
request.gen_params.init_image)) {
const sd_image_t& image = request.gen_params.init_image.get();
request.gen_params.set_width_and_height_if_unset(image.width, image.height);
@@ -211,8 +210,8 @@ static bool build_sdapi_img_gen_request(const json& j,
if (j.contains("mask") && j["mask"].is_string()) {
if (decode_base64_image(j["mask"].get<std::string>(),
1,
expected_width,
expected_height,
0,
0,
request.gen_params.mask_image)) {
const sd_image_t& image = request.gen_params.mask_image.get();
request.gen_params.set_width_and_height_if_unset(image.width, image.height);
@@ -225,9 +224,7 @@ static bool build_sdapi_img_gen_request(const json& j,
}
}
} else {
const int resolved_width = request.gen_params.get_resolved_width();
const int resolved_height = request.gen_params.get_resolved_height();
assign_solid_mask(request.gen_params.mask_image, resolved_width, resolved_height);
request.gen_params.mask_image.reset({0, 0, 1, nullptr});
}
float denoising_strength = j.value("denoising_strength", -1.f);
@@ -244,12 +241,7 @@ static bool build_sdapi_img_gen_request(const json& j,
SDImageOwner image_owner;
if (decode_base64_image(extra_image.get<std::string>(),
0,
request.gen_params.auto_resize_ref_image && request.gen_params.width_and_height_are_set()
? request.gen_params.width
: 0,
request.gen_params.auto_resize_ref_image && request.gen_params.width_and_height_are_set()
? request.gen_params.height
: 0,
0, 0,
image_owner)) {
const sd_image_t& image = image_owner.get();
request.gen_params.set_width_and_height_if_unset(image.width, image.height);
+3 -1
View File
@@ -127,7 +127,8 @@ static json make_img_gen_defaults_json(const SDGenerationParams& defaults, const
{"seed", defaults.seed},
{"batch_count", defaults.batch_count},
{"qwen_image_layers", defaults.qwen_image_layers},
{"auto_resize_ref_image", defaults.auto_resize_ref_image},
{"ref_image_args", defaults.ref_image_args},
{"image_preprocess", defaults.image_preprocess},
{"increase_ref_index", defaults.increase_ref_index},
{"control_strength", defaults.control_strength},
{"ip_adapter_strength", defaults.ip_adapter_strength},
@@ -153,6 +154,7 @@ static json make_vid_gen_defaults_json(const SDGenerationParams& defaults, const
{"strength", defaults.strength},
{"seed", defaults.seed},
{"video_frames", defaults.video_frames},
{"image_preprocess", defaults.image_preprocess},
{"fps", defaults.fps},
{"moe_boundary", defaults.moe_boundary},
{"vace_strength", defaults.vace_strength},