From a250d5b742ee0eaadf87339cce55993a30a059fd Mon Sep 17 00:00:00 2001 From: &LED-M& <105789115+xledx@users.noreply.github.com> Date: Tue, 15 Sep 2026 21:27:39 +0900 Subject: [PATCH 1/2] fix: match MiniMax H3 follower crop geometry --- src/pipeline/video.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/pipeline/video.cpp b/src/pipeline/video.cpp index 503afa530..deddd5fff 100644 --- a/src/pipeline/video.cpp +++ b/src/pipeline/video.cpp @@ -460,6 +460,57 @@ namespace sd::pipeline { return window; } + static int64_t minimax_h3_round_half_to_even(double value) { + GGML_ASSERT(value >= 0.0); + double base_d = std::floor(value); + double frac = value - base_d; + int64_t base = static_cast(base_d); + + if (frac < 0.5) { + return base; + } + if (frac > 0.5) { + return base + 1; + } + return (base % 2 == 0) ? base : base + 1; + } + + static sd::Tensor minimax_h3_cover_crop(const sd::Tensor& image, + int target_width, + int target_height) { + GGML_ASSERT(!image.empty()); + GGML_ASSERT(image.dim() == 4); + GGML_ASSERT(target_width > 0 && target_height > 0); + + if (image.shape()[0] == target_width && image.shape()[1] == target_height) { + return image; + } + + double scale = std::max(static_cast(target_width) / image.shape()[0], + static_cast(target_height) / image.shape()[1]); + + int64_t resized_width = std::max( + target_width, + minimax_h3_round_half_to_even(static_cast(image.shape()[0]) * scale)); + int64_t resized_height = std::max( + target_height, + minimax_h3_round_half_to_even(static_cast(image.shape()[1]) * scale)); + + auto resized = sd::ops::interpolate( + image, + {resized_width, resized_height, image.shape()[2], image.shape()[3]}, + sd::ops::InterpolateMode::Lanczos, + false, + true); + + int64_t left = std::max((resized_width - target_width) / 2, 0); + int64_t top = std::max((resized_height - target_height) / 2, 0); + + resized = sd::ops::slice(resized, 0, left, left + target_width); + resized = sd::ops::slice(resized, 1, top, top + target_height); + return resized; + } + static std::optional prepare_video_generation_latents(StableDiffusionGGML* sd, const sd_vid_gen_params_t* sd_vid_gen_params, GenerationRequest* request) { @@ -708,6 +759,11 @@ namespace sd::pipeline { } } + if (!has_references && !start_image.empty() && !end_image.empty()) { + auto follower_image = sd_image_to_tensor(sd_vid_gen_params->end_image); + end_image = minimax_h3_cover_crop(follower_image, request->width, request->height); + } + if (!has_references && (!start_image.empty() || !end_image.empty())) { LOG_INFO(!start_image.empty() && !end_image.empty() ? "MiniMax-H3 FL2VA" : !start_image.empty() ? "MiniMax-H3 I2VA" : "MiniMax-H3 end-frame conditioning"); From efa32517b62bbb956e3195dcd82f2bf9ca8248c9 Mon Sep 17 00:00:00 2001 From: &LED-M& <105789115+xledx@users.noreply.github.com> Date: Tue, 15 Sep 2026 22:33:15 +0900 Subject: [PATCH 2/2] fix: preserve MiniMax H3 keyframe inputs in CLI --- examples/cli/main.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/cli/main.cpp b/examples/cli/main.cpp index eb96d9b5f..d96872762 100644 --- a/examples/cli/main.cpp +++ b/examples/cli/main.cpp @@ -923,6 +923,18 @@ int main(int argc, const char* argv[]) { return 1; } + if (cli_params.mode == VID_GEN && + std::string(sd_get_model_version_name(sd_ctx.get())) == "MiniMax-H3") { + if (!gen_params.init_image_path.empty() && + !load_image_and_update_size(gen_params.init_image_path, gen_params.init_image, false)) { + return 1; + } + if (!gen_params.end_image_path.empty() && + !load_image_and_update_size(gen_params.end_image_path, gen_params.end_image, false)) { + return 1; + } + } + if (gen_params.sample_params.sample_method == SAMPLE_METHOD_COUNT) { gen_params.sample_params.sample_method = sd_get_default_sample_method(sd_ctx.get()); }