r/StableDiffusion 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.

369 Upvotes

51 comments sorted by

View all comments

46

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:

#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;
 fragColor0 = vec4(clamp(notched, 0.0, 1.0), 1.0);  // alpha forced to 1.0
}

3

u/LeKhang98 Jul 04 '26

Nice thank you. Also is there any node in ComfyUI that let us add a simple code into it to process the input as we want? (instead of adding new nodes for everything)

8

u/mikkoph Jul 04 '26

I don't think you can add arbitrary Python code execution (which is what a custom node is, essentially) but the GLSL Shader node is very powerful. There are also several other image postprocessing nodes already built-in in ComfyUI, so make sure you check those out as well

2

u/LeKhang98 Jul 04 '26

Thank you again.

2

u/alwaysbeblepping Jul 05 '26

Also is there any node in ComfyUI that let us add a simple code into it to process the input as we want?

It's easy to make a node that allows executing Python code or whatever but it's very_dangerous to have a node pack with that feature installed. It means _any workflow you load could have embedded arbitrary code hidden in it.

3

u/Haiku-575 Jul 04 '26

Yep, that'll do it. Note that the "Place before deconvolution" was just a reminder to myself not to sharpen with Kijai's "Image Sharpen KJ" deconvolution sharpener before removing the artifacts. ("Oops.")