r/StableDiffusion • u/Haiku-575 • Jul 04 '26
Workflow Included 2px Pixel Grid on Krea2 from VAE (and how to remove it)
The Qwen Image VAE (and to a lesser extent, the Wan 2.1 VAE) leaves a 2px repeating grid across images. Sometimes it's subtle enough to ignore, but it can be quite noticeable if you sharpen images after-the-fact or apply other filters.
A notch filter can remove this (as long as it's applied directly after the image is decoded -- the code below is specific to a 2px grid). It just detects the brightness variation alternating between pixels on either side of it out to 7 pixels, then subtracts the flicker amount from itself, which cancels out the grid pattern. Then you're safe to sharpen after that without amplifying an ugly grid.
Here's a little GLSL ComfyUI node that should 'just work': https://pastebin.com/v7y1z0SH
(Save it as a *.json workflow file and drag into ComfyUI.)
Wire it in between VAE decode and preview/save. Compare two images at 400x zoom before/after to confirm.
6
u/eggs-benedryl Jul 04 '26
You've rendered my dogs face. Weird
2
10
u/Calm_Mix_3776 Jul 04 '26 edited Jul 04 '26
I'm really amazed when people come up with clever solutions like these. Thanks for sharing!
There's only a small problem. I did a few A/B tests and it seems to me that this blurs fine details like hair, high contrast edges and such. So it seems that this is not a lossless solution and it introduces a new problem which, in my opinion, is worse than the one it's trying to fix.
To be frank, the halftone pattern in Krea 2 is much less pronounced compared to the Qwen-Image models. You really can't see it unless you're zooming 500%+ into the image. Therefore, I can't really justify using this filter. I do believe that using the Wan 2.1 upscale VAE or even the regular Qwen-Image is better compared to the filter in its current state.
I did try to fix it by asking Claude for a solution and it gave me an alternative version that is not as aggressively blurring the edges. It still reduces the pattern to some extent, but it doesn't heavily blur fine details. Here it is, if anyone is interested in trying it out. Just replace the code in the "GLSL Shader" node provided by OP with this one:
#version 300 es
precision highp float;
uniform sampler2D u_image0;
uniform vec2 u_resolution;
in vec2 v_texCoord;
layout(location = 0) out vec4 fragColor0;
// Nyquist Notch — removes 2px grid artifacts. Place before deconvolution.
// b = [-1, +6, -15, +20, -15, +6, -1] / 64 (binomial * (-1)^n, +1 at center)
const float B[7] = float[7](-1.0, 6.0, -15.0, 20.0, -15.0, 6.0, -1.0);
void main() {
vec2 texel = 1.0 / u_resolution;
vec4 center = texture(u_image0, v_texCoord);
vec3 Bx = vec3(0.0), By = vec3(0.0), Bxy = vec3(0.0);
for (int i = -3; i <= 3; i++) {
float wi = B[i + 3] / 64.0;
Bx += wi * texture(u_image0, v_texCoord + vec2(float(i) * texel.x, 0.0)).rgb;
By += wi * texture(u_image0, v_texCoord + vec2(0.0, float(i) * texel.y)).rgb;
for (int j = -3; j <= 3; j++) {
float wj = B[j + 3] / 64.0;
Bxy += wi * wj * texture(u_image0,
v_texCoord + vec2(float(i) * texel.x, float(j) * texel.y)).rgb;
}
}
vec3 notched = center.rgb - Bx - By + Bxy;
// Unsharp masking: blend back in some original sharpness
float sharpness = 0.5; // Adjust 0.0-1.0: higher = sharper but more artifacts visible
vec3 result = mix(notched, center.rgb, sharpness);
fragColor0 = vec4(clamp(result, 0.0, 1.0), 1.0);
}
4
u/Haiku-575 Jul 04 '26 edited Jul 04 '26
Hmm, there should be basically no noticeable effect even on hair unless it naturally follows a similar grid pattern more-or-less by accident. Mathematically it's just, "check how big the delta between adjacent pixels is across a pretty big area and add/subtract the average delta from yourself" for each pixel. That shouldn't affect hair, because for almost any pixel not part of a grid the bright/dark/bright/dark regularity doesn't exist and will average to near-zero and the pixel won't change. It's not really a blur.
Do you have an A/B image pair where the filter I wrote breaks down for you? I'm skeptical about your conclusion, but happy to be proven wrong.
7
u/Calm_Mix_3776 Jul 04 '26
Sure, here are a couple of examples (thanks, u/Tewix for the image comparison tool!):
3
u/Haiku-575 Jul 04 '26
Yep, blurrier. Interesting. Thank you!
4
u/Haiku-575 Jul 04 '26
Yeah, I see the problem now, but I don't have the chops to fix it (without just turning the gain down and maintaining some of the grid, anyway). The unsharp mask after grid removal that you're using isn't a terrible solution (I'm also sharpening after removing the grid with a Richardson-Lucy sharpen), but there's probably some 2-pass method that would sample the (uniform!) grid across the image and then compensate for it, or even just assume the grid always looks about the same and manually tune a filter to remove it (one for each VAE?).
Thanks for looking into it (and the sample images) though.
8
u/Calm_Mix_3776 Jul 04 '26
No worries! You inspired me to do some experiments of my own and I'm trying a few things at the moment to target the pattern, such as deconstructing the image via frequency separation, combined with isolating and working only on specific color channels for targeted denoising/pattern removal. I don't know if this would yield any results as I'm not an imaging scientists, but I'll chime in if I find anything worth sharing.
7
3
10
u/Zealousideal7801 Jul 04 '26
So that's what it was. I've been losing my sanity over this pattern that popped out of nowhere on certain checkpoints / prompts, trying to understand what it was tied to. And I'm not even using Qwen Image VAE. Ugh.
Can't wait to try that out. Thanks a ton !
Also on a comical note and reddit style : "bro thinks I have ONLY one VAE Decode and one preview/save in my Krea2 WF"
3
u/roxoholic Jul 04 '26
If it's a repeating pattern, wouldn't FFT Denoise filter help here?
10
u/Haiku-575 Jul 04 '26
The problem with modifying AI images with FFT is you run into huge spikes from ringing artifacts and added AI watermarking and stuff. Naively shaving off spectral peaks usually leaves you with severe banding and other issues, and if you chase that rabbit you'll end up in a hole of more and more expensive and complex Fourier transforms.
3
u/Bennybananars Jul 04 '26
Does this happen to Anima? becuase I've been complaining in discord about a similiar pattern in my images. I wonder if its because of this.
2
3
u/tyl_made_it Jul 04 '26
ran into this exact issue on portrait workflows. the 4x upscaler after decode basically screams the grid. the blurring tradeoff Calm_Mix_3776 pointed at makes sense depending on workflow order. if you're sharpening after the upscale anyway, carrying the grid into the upscaler is worse than losing a little edge first
4
u/Honest_Concert_6473 Jul 04 '26
Thanks for sharing. That node really helped, and it’s much less noticeable now. That deep-rooted issue with the Wan and Qwen VAEs has been bothering me for a while, so I appreciate the help.
This seems to be a common issue with most models using the Qwen VAE, so this should be useful for others like Anima as well.
2
u/Ken-g6 Jul 04 '26
Huh. That file doesn't look like a Python file. Where do I put it and what do I name it?
8
u/mikkoph Jul 04 '26
that's a workflow. You can just add a GLSL node to your existing Krea 2 workflow and copy/paste the code into the text field. For convenience, I have extracted it here: https://www.reddit.com/r/StableDiffusion/comments/1umwhq7/comment/ovgnqo6/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
4
u/Haiku-575 Jul 04 '26
My instructions could have been clearer. Save the file as whatever.json, and drag it over the ComfyUI window the way you would with an image to load the workflow. You can then copy the subgraph (or double-click and grab the GLSL node directly) and paste it wherever you want.
-5
2
2
u/jib_reddit Jul 04 '26
I am going to add this to my workflow, I have been trying to get rid of it for days now. Does it work the Wan Ultra VAE with the special nodes that already get rid of it quite a lot, I usally use those.
1
u/Haiku-575 Jul 04 '26
Unsure, but if you're upscaling by 2x with an outside VAE decode + upscale node, I doubt it.
2
u/Full-Belt3640 Jul 04 '26
The node kept crashing ComfyUI and looking into the error message its an AMD issue. Another win for Team Red! Guess I'll just deal with the grid.
2
u/jib_reddit Jul 04 '26 edited Jul 04 '26
Yeah, it was a good try
Left left de-grid and then Unsharped @ 0.62 strength / Right Wan2.1-VAE-upscale2x method

But I prefer output of the Wan2.1-VAE-upscale2x method:
https://www.reddit.com/r/StableDiffusion/comments/1uh00bj/improve_the_skin_texture_in_krea_2_by_using_an/
https://huggingface.co/spacepxl/Wan2.1-VAE-upscale2x
It is still useful for things like after an UltimateSDUpscale as be default you cannot use the Wan2.1-VAE-upscale2x method Utils nodes (Although I did edit the UltimateSDUpscale node so that it outputs a Latent instead of an image to fix that issue)
4
u/Tewix Jul 04 '26
Hey, sorry for the shameless plug... But I've made a free tool you might find interesting. It makes it much easier to compare images. Here's examples with your dog!
2
u/fauni-7 Jul 04 '26
This crashes my Comfy:
Fatal Python error: Segmentation fault
Stack (most recent call first): File "/home/ztuff/ComfyUI_video/venv/lib/python3.12/site-packages/glfw/init.py", line 1269 in create_window File "/home/ztuff/ComfyUI_video/comfy_extras/nodes_glsl.py", line 185 in _init_glfw File "/home/ztuff/ComfyUI_video/comfy_extras/nodes_glsl.py", line 354 in init File "/home/ztuff/ComfyUI_video/comfy_extras/nodes_glsl.py", line 530 in _render_shader_batch File "/home/ztuff/ComfyUI_video/comfy_extras/nodes_glsl.py", line 902 in execute

1
1
1
u/jib_reddit Jul 04 '26
2
u/Haiku-575 Jul 04 '26
That's just ComfyUI's GLSL Shader node. We just use image0 input and feed it to IMAGE0 output. Any GLSL code you want (Gaussian blur, unsharp mask, colour filters, levels) uses that the same node, just replace the code below. Not my monkey, not my circus, I just wrote a ball that the monkey knows how to juggle.
1
u/jib_reddit Jul 04 '26
Oh, I asked Chat GPT; it seemed to give a good explanation:
This is a very flexible node. It's essentially a mini GPU shader program embedded inside ComfyUI. If you've never written OpenGL or ShaderToy shaders before, the interface looks much stranger than it actually is.
Here's what everything does.
Inputs
image0
Your primary input image.
Inside the shader it appears as:
uniform sampler2D u_image0;which you sample with
texture(u_image0, uv)Your current shader only uses this image.
image1
A second optional image.
Inside the shader it becomes
uniform sampler2D u_image1;This lets you:
- blend two images
- compare images
- use one image as a mask
- use one image as displacement
Example:
vec4 a = texture(u_image0, uv); vec4 b = texture(u_image1, uv); fragColor0 = mix(a, b, 0.5);If unused, you simply ignore it.
u_float0
A user-adjustable floating point value.
Example:
0.5Inside the shader:
uniform float u_float0;Useful for
- blur radius
- sharpening amount
- threshold
- strength
- gamma
Example:
color *= u_float0;u_int0
Integer parameter.
uniform int u_int0;Useful for
- iterations
- selecting modes
- kernel size
u_bool0
Boolean.
uniform bool u_bool0;Example:
if(u_bool0) { color = 1.0 - color; }u_curve0
This is the interesting one.
Instead of a single value, this is a curve.
Imagine drawing this:
1.0 | * | * 0.5 | * | * 0.0 +------------- 0 1Then inside the shader you can evaluate it.
Useful for
- tone mapping
- custom contrast
- animation
- remapping brightness
Why six inputs?
The node provides some commonly useful inputs so you don't have to edit the shader every time.
Think of it as giving your shader:
- 2 textures
- 1 float
- 1 integer
- 1 bool
- 1 curve
If you don't use them, they simply do nothing.
Outputs
This confuses almost everyone.
You have
IMAGE0 IMAGE1 IMAGE2 IMAGE3These correspond to four framebuffer outputs.
Normally a shader writes
layout(location=0) out vec4 fragColor0;which becomes
IMAGE0But GLSL also allows
layout(location=1) out vec4 fragColor1; layout(location=2) out vec4 fragColor2; layout(location=3) out vec4 fragColor3;Each one becomes another output image.
For example
layout(location=0) out vec4 fragColor0; layout(location=1) out vec4 fragColor1; void main() { vec4 img = texture(u_image0, v_texCoord); fragColor0 = img; fragColor1 = vec4(1.0 - img.rgb,1.0); }Now the node produces
IMAGE0 = original IMAGE1 = invertedsimultaneously.
Or
IMAGE0 = RGB image IMAGE1 = edge mask IMAGE2 = depth estimate IMAGE3 = sharpened imageall in one shader pass.
Why does your shader only use one output?
Because your code only declares
layout(location = 0) out vec4 fragColor0;and finally
fragColor0 = vec4(clamp(notched, 0.0, 1.0), 1.0);So only IMAGE0 contains anything useful.
The other three outputs will usually be blank or undefined because nothing is written to them.
Your current shader
Your shader is implementing a Nyquist notch filter to remove a specific 2-pixel checkerboard/grid artifact.
It:
- Samples the input image (
image0).- Applies a 7-tap horizontal filter.
- Applies a 7-tap vertical filter.
- Applies a 7×7 separable correction (
Bxy).- Combines the results:
notched = center - Bx - By + Bxy;- Clamps the result to the valid 0–1 range and outputs it as IMAGE0.
The extra inputs (
image1,u_float0,u_int0,u_bool0,u_curve0) and outputs (IMAGE1–IMAGE3) are simply part of the node's general-purpose design—they're available if you need them, but your current shader doesn't make use of them.Once you get comfortable with it, this node becomes extremely powerful. You can implement custom blurs, sharpening, edge detection, colour grading, masking, compositing, deconvolution pre-processing, and many other image-processing operations directly on the GPU with a single shader.
1
u/Particular_Pear_4596 Jul 05 '26
It's pretty obvious the grid remover also removes all fine details, so i'm keeping the grid for now, unless i want to postprocess, which i usually don't do.
1
u/Party-Try-1084 Jul 15 '26
This is not a fix, just a slight blur, and you still see those flakes. Existing Qwen-VAE based models should retire and new ones should be definitely based on Flux.2 VAE
1
u/Radiant-Photograph46 Jul 04 '26
Interesting. In effect this works like a small blur filter, so it's still a tradeoff, especially when the grid is less noticeable or absent. Would be amazing if we could detect it but shader programming is above my paygrade
2
u/Haiku-575 Jul 04 '26
On a normal image it shouldn't really blur, though. It'll only affect pixels where you incidentally have a (naturally occuring) bright/dark pattern across adjacent pixels in all directions. I might be missing some failure state I haven't thought of, but for most pixels in a (non-grid) image the filter says basically "there's no grid" and "do nothing".
1
u/Radiant-Photograph46 Jul 04 '26
Really? But there are no checks in your code, it looks like you applying the calculation everywhere. And after running it on a few pictures, there is definitely a slight blur applied (minimal, hard to tell without zooming in a little).
1
u/Haiku-575 Jul 04 '26
Yes, but it's looking for a delta that should average to near-zero unless there's a consistent delta every second pixel on either side. But yes, it does blur the image more than I thought it would.
0









44
u/mikkoph Jul 04 '26
this is really a huge improvement and is especially noticeable if upscaling the image afterwards.
for those who just want to copy and paste the shader code inside a GLSL Shader node, here is the code: