feat: add latent2rgba preview for Qwen-Image 2.1 (#2032)

This commit is contained in:
stduhpf 2026-09-23 16:16:55 +02:00 committed by GitHub
parent c92d73c408
commit 241518b35d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 127 additions and 18 deletions

View File

@ -1974,6 +1974,8 @@ void StableDiffusionGGML::preview_image(int step,
int patch_sz = 1; int patch_sz = 1;
const float(*latent_rgb_proj)[3] = nullptr; const float(*latent_rgb_proj)[3] = nullptr;
float* latent_rgb_bias = nullptr; float* latent_rgb_bias = nullptr;
const float* latent_alpha_proj = nullptr;
float latent_alpha_bias = 1.f;
if (channels == 128) { if (channels == 128) {
if (sd_version_uses_flux2_vae(version)) { if (sd_version_uses_flux2_vae(version)) {
@ -1987,6 +1989,16 @@ void StableDiffusionGGML::preview_image(int step,
LOG_WARN("No latent to RGB projection known for this model"); LOG_WARN("No latent to RGB projection known for this model");
return; return;
} }
} else if (channels == 64) {
if (version == VERSION_QWEN_IMAGE_2_1) {
latent_rgb_proj = qwen21_latent_rgb_proj;
latent_rgb_bias = qwen21_latent_rgb_bias;
latent_alpha_proj = qwen21_latent_alpha_proj;
latent_alpha_bias = qwen21_latent_alpha_bias;
} else {
LOG_WARN("No latent to RGB projection known for this model");
return;
}
} else if (channels == 48) { } else if (channels == 48) {
if (sd_version_is_wan(version)) { if (sd_version_is_wan(version)) {
latent_rgb_proj = wan_22_latent_rgb_proj; latent_rgb_proj = wan_22_latent_rgb_proj;
@ -2037,13 +2049,14 @@ void StableDiffusionGGML::preview_image(int step,
uint32_t img_width = static_cast<uint32_t>(_latents.shape()[0]) * patch_sz; uint32_t img_width = static_cast<uint32_t>(_latents.shape()[0]) * patch_sz;
uint32_t img_height = static_cast<uint32_t>(_latents.shape()[1]) * patch_sz; uint32_t img_height = static_cast<uint32_t>(_latents.shape()[1]) * patch_sz;
uint8_t* data = (uint8_t*)malloc(frames * img_width * img_height * 3 * sizeof(uint8_t)); uint32_t img_channels = latent_alpha_proj != nullptr ? 4 : 3;
uint8_t* data = (uint8_t*)malloc(frames * img_width * img_height * img_channels * sizeof(uint8_t));
GGML_ASSERT(data != nullptr); GGML_ASSERT(data != nullptr);
preview_latent_video(data, _latents, latent_rgb_proj, latent_rgb_bias, patch_sz); preview_latent_video(data, _latents, latent_rgb_proj, latent_rgb_bias, patch_sz, latent_alpha_proj, latent_alpha_bias);
sd_image_t* images = (sd_image_t*)malloc(frames * sizeof(sd_image_t)); sd_image_t* images = (sd_image_t*)malloc(frames * sizeof(sd_image_t));
GGML_ASSERT(images != nullptr); GGML_ASSERT(images != nullptr);
for (uint32_t i = 0; i < frames; i++) { for (uint32_t i = 0; i < frames; i++) {
images[i] = {img_width, img_height, 3, data + i * img_width * img_height * 3}; images[i] = {img_width, img_height, img_channels, data + i * img_width * img_height * img_channels};
} }
step_callback(step, frames, images, is_noisy, step_callback_data); step_callback(step, frames, images, is_noisy, step_callback_data);
free(data); free(data);

View File

@ -179,7 +179,7 @@ public:
std::unique_ptr<ModelConfig> config_; std::unique_ptr<ModelConfig> config_;
RunnerState runner_state_; RunnerState runner_state_;
bool conditioning_cache_allowed_ = false; bool conditioning_cache_allowed_ = false;
bool executing_ = false; bool executing_ = false;
std::shared_ptr<Denoiser> denoiser; std::shared_ptr<Denoiser> denoiser;
std::vector<float> file_alphas_cumprod; std::vector<float> file_alphas_cumprod;

View File

@ -4,6 +4,86 @@
#include "core/tensor.hpp" #include "core/tensor.hpp"
#include "ggml.h" #include "ggml.h"
// RGB is projected to [-1, 1]; alpha is projected directly to [0, 1].
const float qwen21_latent_rgb_proj[64][3] = {
{0.00860495522f, 0.01219501462f, -0.00321337196f},
{0.01889233090f, 0.01246581216f, 0.01074959482f},
{0.1255941446f, 0.1176879344f, -0.0332212352f},
{0.0418238528f, 0.1043427754f, 0.0121666316f},
{0.02025338f, 0.01453670296f, -0.000224336044f},
{-0.01896720702f, -0.0206099030f, -0.0322728584f},
{0.00438984796f, -0.01374969766f, 0.02849196f},
{-0.0374495856f, -0.0286777126f, -0.0693192810f},
{0.01511914734f, 0.0242979386f, 0.0553878870f},
{-0.1138629518f, -0.020391466f, 0.001550520522f},
{-0.0233650696f, -0.0417292018f, -0.0362361182f},
{-0.0351603342f, -0.0243595924f, -0.00216261038f},
{0.01093355288f, -0.0373466924f, 0.00241315350f},
{0.01778704744f, -0.00401984678f, -0.0343259192f},
{0.0486059334f, 0.0253144f, 0.0672564966f},
{0.0309463558f, 0.0277963166f, 0.0520869622f},
{0.0374485008f, 0.0551753676f, 0.0225853902f},
{-0.0090809962f, -0.004756176f, 0.00636443612f},
{-0.0270455652f, -0.0384966954f, -0.00905908082f},
{-0.00553493756f, 0.01484553684f, -0.0211502468f},
{0.01319502562f, 0.00948005666f, 0.0483789212f},
{-0.00931847104f, -0.00276452734f, -0.01011985302f},
{0.0180478258f, 0.01614954356f, -0.0209424690f},
{-0.0214530434f, -0.00272961176f, 0.0217887476f},
{-0.0636772304f, -0.0208893548f, 0.0479167742f},
{-0.0250321236f, -0.0286715676f, 0.0530110146f},
{-0.01853078078f, 0.01647272818f, -0.00207747588f},
{0.0023101082f, 0.01228800748f, 0.01303505006f},
{-0.01243671408f, -0.0258638728f, -0.0379116264f},
{0.00598934710f, 0.00642563550f, -0.01234514304f},
{-0.0296733996f, -0.0234698050f, 0.00060018212f},
{-0.0322019498f, -0.0529200462f, -0.00344987414f},
{-0.00205026458f, -0.00846599446f, 0.00455971038f},
{-0.01082227064f, 0.0315661948f, -0.0677753362f},
{0.0645553474f, 0.1109666998f, 0.0674744864f},
{0.01036801108f, -0.00484841210f, -0.001529168474f},
{0.01264353566f, 0.01548126338f, -0.00966374324f},
{-0.0223892408f, -0.00871751526f, -0.000306421670f},
{0.0271322742f, 0.03496524f, -0.0089692858f},
{0.0512178672f, 0.0173080034f, 0.00804227746f},
{0.01210987192f, 0.00758025926f, -0.00281712586f},
{0.1897278390f, 0.1210261828f, 0.062603892f},
{0.0208058822f, 0.00547548182f, 0.01262955638f},
{0.00813332858f, 0.01015930914f, 0.01301771290f},
{-0.000927236014f, -0.00152540594f, -0.00599213302f},
{0.01663314616f, -0.00582789626f, 0.0163958132f},
{-0.0252546342f, -0.0604193732f, -0.1606919922f},
{-0.091722686f, -0.0409201224f, -0.0959576198f},
{0.0282963112f, -0.01387223872f, -0.01648814464f},
{0.0552316818f, 0.0967547788f, 0.0413586632f},
{0.00922849292f, 0.00451467542f, -0.0529172378f},
{0.0558600768f, 0.0122988308f, -0.01445942422f},
{0.000210660902f, -0.01295958782f, -0.01804761764f},
{0.0358136250f, -0.0472505970f, -0.1156405142f},
{-0.0506390696f, -0.0471914842f, 0.0349791468f},
{-0.0480143168f, 0.00628389868f, -0.0545163826f},
{0.0315499582f, 0.0564846606f, -0.0430850488f},
{-0.0362330316f, -0.01267788554f, 0.0061024772f},
{0.0038627542f, 0.00911055916f, -0.00758526008f},
{-0.0447103298f, -0.00835411408f, 0.01545872328f},
{-0.015006738f, 0.00270612302f, -0.00784361356f},
{-0.0221755048f, -0.0513344748f, -0.0475317424f},
{-0.01036656294f, -0.00422146068f, -0.0213499052f},
{0.01788952706f, 0.01191944190f, 0.0397205238f},
};
float qwen21_latent_rgb_bias[3] = {-0.043293118f, -0.02695978f, -0.11986706f};
const float qwen21_latent_alpha_proj[64] = {
-0.0416241114f, -0.00678954612f, -0.0169095515f, -0.0230551401f, 0.0100882595f, 0.00655586802f, 0.0401166874f, -0.0055510216f,
0.0224234441f, -0.0389640963f, -0.0114492163f, -0.00721128977f, -0.0029064082f, 0.0150300547f, -0.00321615308f, -0.0498856338f,
-0.0215251401f, 0.0240220482f, 0.0117338008f, -0.0460420624f, 0.0387872889f, 0.0131517207f, 0.0147100836f, 0.0266985286f,
0.0153097324f, -0.0418119757f, 0.0421013917f, 0.0401724499f, 0.00972515915f, 0.011718495f, 0.0117622291f, 0.0136505134f,
-0.0350017363f, -0.0100692606f, -0.0131582529f, -0.00660639315f, 0.00253611396f, -0.0195736368f, -0.04240184f, 0.0321299262f,
0.0106089414f, -0.0179845306f, -0.00806212708f, 0.0135889057f, -0.0157393098f, -0.0267791344f, 0.0109068534f, 0.0283931966f,
-0.0435370078f, 0.00187883536f, -0.0108995378f, -0.0450757676f, -0.0699481501f, 0.0123562106f, -0.0222592249f, 0.0216155907f,
0.0563755424f, -0.0073379912f, 0.0160012921f, 0.0411637742f, 0.0189607258f, -0.024025029f, -0.0161487905f, -0.016913203f};
const float qwen21_latent_alpha_bias = 0.871268134f;
const float minimax_latent_rgb_proj[24][3] = { const float minimax_latent_rgb_proj[24][3] = {
{0.19819857f, 0.11584999f, 0.07929777f}, {0.19819857f, 0.11584999f, 0.07929777f},
{-0.16047224f, -0.10601170f, -0.15996324f}, {-0.16047224f, -0.10601170f, -0.15996324f},
@ -324,7 +404,7 @@ const float sd_latent_rgb_proj[4][3] = {
{-0.178022f, -0.200862f, -0.678514f}}; {-0.178022f, -0.200862f, -0.678514f}};
float sd_latent_rgb_bias[3] = {-0.017478f, -0.055834f, -0.105825f}; float sd_latent_rgb_bias[3] = {-0.017478f, -0.055834f, -0.105825f};
void preview_latent_video(uint8_t* buffer, ggml_tensor* latents, const float (*latent_rgb_proj)[3], const float latent_rgb_bias[3], int patch_size) { void preview_latent_video(uint8_t* buffer, ggml_tensor* latents, const float (*latent_rgb_proj)[3], const float latent_rgb_bias[3], int patch_size, const float* latent_alpha_proj = nullptr, float latent_alpha_bias = 1.f) {
size_t buffer_head = 0; size_t buffer_head = 0;
uint32_t latent_width = static_cast<uint32_t>(latents->ne[0]); uint32_t latent_width = static_cast<uint32_t>(latents->ne[0]);
@ -338,7 +418,8 @@ void preview_latent_video(uint8_t* buffer, ggml_tensor* latents, const float (*l
uint32_t rgb_width = latent_width * patch_size; uint32_t rgb_width = latent_width * patch_size;
uint32_t rgb_height = latent_height * patch_size; uint32_t rgb_height = latent_height * patch_size;
uint32_t unpatched_dim = dim / (patch_size * patch_size); uint32_t unpatched_dim = dim / (patch_size * patch_size);
const uint32_t output_channels = latent_alpha_proj != nullptr ? 4 : 3;
for (uint32_t k = 0; k < frames; k++) { for (uint32_t k = 0; k < frames; k++) {
for (uint32_t rgb_x = 0; rgb_x < rgb_width; rgb_x++) { for (uint32_t rgb_x = 0; rgb_x < rgb_width; rgb_x++) {
@ -356,13 +437,16 @@ void preview_latent_video(uint8_t* buffer, ggml_tensor* latents, const float (*l
// should be incremented by 1 for each pixel // should be incremented by 1 for each pixel
size_t pixel_id = k * rgb_width * rgb_height + rgb_y * rgb_width + rgb_x; size_t pixel_id = k * rgb_width * rgb_height + rgb_y * rgb_width + rgb_x;
float r = 0, g = 0, b = 0; float r = 0, g = 0, b = 0, a = 0;
if (latent_rgb_proj != nullptr) { if (latent_rgb_proj != nullptr) {
for (uint32_t d = 0; d < unpatched_dim; d++) { for (uint32_t d = 0; d < unpatched_dim; d++) {
float value = *(float*)((char*)latents->data + latent_id + (d * patch_size * patch_size + channel_offset) * latents->nb[ggml_n_dims(latents) - 1]); float value = *(float*)((char*)latents->data + latent_id + (d * patch_size * patch_size + channel_offset) * latents->nb[ggml_n_dims(latents) - 1]);
r += value * latent_rgb_proj[d][0]; r += value * latent_rgb_proj[d][0];
g += value * latent_rgb_proj[d][1]; g += value * latent_rgb_proj[d][1];
b += value * latent_rgb_proj[d][2]; b += value * latent_rgb_proj[d][2];
if (latent_alpha_proj != nullptr) {
a += value * latent_alpha_proj[d];
}
} }
} else { } else {
// interpret first 3 channels as RGB // interpret first 3 channels as RGB
@ -386,9 +470,13 @@ void preview_latent_video(uint8_t* buffer, ggml_tensor* latents, const float (*l
g = g >= 0 ? g <= 1 ? g : 1 : 0; g = g >= 0 ? g <= 1 ? g : 1 : 0;
b = b >= 0 ? b <= 1 ? b : 1 : 0; b = b >= 0 ? b <= 1 ? b : 1 : 0;
buffer[pixel_id * 3 + 0] = (uint8_t)(r * 255); buffer[pixel_id * output_channels + 0] = (uint8_t)(r * 255);
buffer[pixel_id * 3 + 1] = (uint8_t)(g * 255); buffer[pixel_id * output_channels + 1] = (uint8_t)(g * 255);
buffer[pixel_id * 3 + 2] = (uint8_t)(b * 255); buffer[pixel_id * output_channels + 2] = (uint8_t)(b * 255);
if (latent_alpha_proj != nullptr) {
a = std::min(1.0f, std::max(0.0f, a + latent_alpha_bias));
buffer[pixel_id * output_channels + 3] = (uint8_t)(a * 255);
}
} }
} }
} }
@ -398,16 +486,17 @@ static inline bool preview_latent_tensor_is_video(const sd::Tensor<float>& laten
return latents.dim() == 5; return latents.dim() == 5;
} }
void preview_latent_video(uint8_t* buffer, const sd::Tensor<float>& latents, const float (*latent_rgb_proj)[3], const float latent_rgb_bias[3], int patch_size) { void preview_latent_video(uint8_t* buffer, const sd::Tensor<float>& latents, const float (*latent_rgb_proj)[3], const float latent_rgb_bias[3], int patch_size, const float* latent_alpha_proj = nullptr, float latent_alpha_bias = 1.f) {
uint32_t latent_width = static_cast<uint32_t>(latents.shape()[0]); uint32_t latent_width = static_cast<uint32_t>(latents.shape()[0]);
uint32_t latent_height = static_cast<uint32_t>(latents.shape()[1]); uint32_t latent_height = static_cast<uint32_t>(latents.shape()[1]);
bool is_video = preview_latent_tensor_is_video(latents); bool is_video = preview_latent_tensor_is_video(latents);
uint32_t frames = is_video ? static_cast<uint32_t>(latents.shape()[2]) : 1; uint32_t frames = is_video ? static_cast<uint32_t>(latents.shape()[2]) : 1;
uint32_t dim = is_video ? static_cast<uint32_t>(latents.shape()[3]) : static_cast<uint32_t>(latents.shape()[2]); uint32_t dim = is_video ? static_cast<uint32_t>(latents.shape()[3]) : static_cast<uint32_t>(latents.shape()[2]);
uint32_t rgb_width = latent_width * patch_size; uint32_t rgb_width = latent_width * patch_size;
uint32_t rgb_height = latent_height * patch_size; uint32_t rgb_height = latent_height * patch_size;
uint32_t unpatched_dim = dim / (patch_size * patch_size); uint32_t unpatched_dim = dim / (patch_size * patch_size);
const uint32_t output_channels = latent_alpha_proj != nullptr ? 4 : 3;
for (uint32_t k = 0; k < frames; k++) { for (uint32_t k = 0; k < frames; k++) {
for (uint32_t rgb_x = 0; rgb_x < rgb_width; rgb_x++) { for (uint32_t rgb_x = 0; rgb_x < rgb_width; rgb_x++) {
@ -427,7 +516,7 @@ void preview_latent_video(uint8_t* buffer, const sd::Tensor<float>& latents, con
: latents.values()[latent_x + latent_width * (latent_y + latent_height * latent_channel)]; : latents.values()[latent_x + latent_width * (latent_y + latent_height * latent_channel)];
}; };
float r = 0.f, g = 0.f, b = 0.f; float r = 0.f, g = 0.f, b = 0.f, a = 0.f;
if (latent_rgb_proj != nullptr) { if (latent_rgb_proj != nullptr) {
for (uint32_t d = 0; d < unpatched_dim; d++) { for (uint32_t d = 0; d < unpatched_dim; d++) {
uint32_t latent_channel = d * patch_size * patch_size + channel_offset; uint32_t latent_channel = d * patch_size * patch_size + channel_offset;
@ -435,6 +524,9 @@ void preview_latent_video(uint8_t* buffer, const sd::Tensor<float>& latents, con
r += value * latent_rgb_proj[d][0]; r += value * latent_rgb_proj[d][0];
g += value * latent_rgb_proj[d][1]; g += value * latent_rgb_proj[d][1];
b += value * latent_rgb_proj[d][2]; b += value * latent_rgb_proj[d][2];
if (latent_alpha_proj != nullptr) {
a += value * latent_alpha_proj[d];
}
} }
} else { } else {
r = latent_value(0); r = latent_value(0);
@ -450,9 +542,13 @@ void preview_latent_video(uint8_t* buffer, const sd::Tensor<float>& latents, con
g = std::min(1.0f, std::max(0.0f, g * .5f + .5f)); g = std::min(1.0f, std::max(0.0f, g * .5f + .5f));
b = std::min(1.0f, std::max(0.0f, b * .5f + .5f)); b = std::min(1.0f, std::max(0.0f, b * .5f + .5f));
buffer[pixel_id * 3 + 0] = (uint8_t)(r * 255); buffer[pixel_id * output_channels + 0] = (uint8_t)(r * 255);
buffer[pixel_id * 3 + 1] = (uint8_t)(g * 255); buffer[pixel_id * output_channels + 1] = (uint8_t)(g * 255);
buffer[pixel_id * 3 + 2] = (uint8_t)(b * 255); buffer[pixel_id * output_channels + 2] = (uint8_t)(b * 255);
if (latent_alpha_proj != nullptr) {
a = std::min(1.0f, std::max(0.0f, a + latent_alpha_bias));
buffer[pixel_id * output_channels + 3] = (uint8_t)(a * 255);
}
} }
} }
} }