Perfect Pixel
Upscale pixel art without the blur
- images
- scaled_image
- refined_w
- refined_h
Upscaling pixel art with a normal upscaler is how you get mush. Neural upscalers are trained on photos and want to add texture; pixel art wants exactly the opposite - each pixel stays a clean, chunky square. Perfect Pixel is the node that gets that right: it detects the pixel grid spacing in your image, then upscales it to a refined size with exact nearest-neighbor multiples, so the result is crisp and blocky instead of smeared.
The KB's post-processing doc makes the point worth repeating here: real pixel art isn't made by the model, it's made by a deterministic pass after generation - grid detection, then integer scaling. That's exactly the job Perfect Pixel automates, on the output side. It wraps the perfect-pixel Python package (from github.com/theamusing/perfectPixel) - which is precisely why the pack's requirements list contains perfect-pixel[opencv]>=0.1.4. That's the one dependency that isn't installed by default in ComfyUI, and it's the rare FairLab node that genuinely needs its package. If the node errors on import, that's what's missing.
Inputs and outputs
images(IMAGE) - the sprite(s) to upscale.sample_method-majority,center, ormedian(defaultcenter) - how a grid cell is sampled when the grid isn't perfectly aligned.min_size(INT) - minimum detected pixel-pattern size, default 1.peak_width(INT) - minimum peak width for grid detection, default 1.refine_intensity(FLOAT) - how far the grid-line refinement may search around its initial estimate; recommended 0–0.5, default 0.25.fix_square(BOOLEAN) - force a near-square output to be exactly square, default true.debug(BOOLEAN) - print debug plots during detection.scaled_image(IMAGE) out - the upscaled result.refined_w,refined_h(INT) out - the detected/refined output dimensions.
How it works
The core is grid detection: it figures out the pixel pattern's periodicity in the image (that's what min_size and peak_width tune), refines those grid lines within the search window set by refine_intensity, then resamples each cell with your chosen sample_method at exact multiples. Because it detects the grid rather than assuming a scale, it handles sprites that were already resized imperfectly better than a blind nearest-neighbor upscale.
Where it fits
Pixel-art and sprite upscaling, period. The KB's upscaling doc separates "pixel upscalers (non-generative)" from the photo-focused ones for exactly this reason - different jobs, different tools. If your source is a photo, Perfect Pixel will hunt for a pixel grid that isn't there and do odd things; it's for art with an actual pixel grid.
Install
FairLab, one pack: ComfyUI Manager (search ComfyUI-FairLab) or:
cd ComfyUI/custom_nodes
git clone https://github.com/yanhuifair/ComfyUI-FairLab.git
cd ComfyUI-FairLab
pip install -r requirements.txt
The requirements install perfect-pixel[opencv]>=0.1.4 and opencv-python, which this node actually uses - unlike most of the pack. If it fails to load, verify with pip list | grep -E "opencv|perfect-pixel". Restart, then find it under Fair/image.
Gotchas
- It's for pixel art, not photos. Wrong input type is the #1 way people get bad results.
- Detection parameters (
min_size,peak_width,refine_intensity) exist because grid detection is heuristic. If your sprite comes out at an unexpected size or with seams, tune those rather than fighting the output -debugmode shows you what the detector found. - The heavy dependencies (opencv + perfect-pixel) are why this node is the one that breaks on a bare install; everything else in the pack runs without them.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| sample_method | COMBO | center | 3 options: majority, center, median |
| min_size | INT | 1 | Minimum size of the detected pixel pattern |
| peak_width | INT | 1 | Minimum peak width for peak detection. |
| refine_intensity | FLOAT | 0.250–0.5 | Intensity for grid line refinement. Recommended range is [0, 0.5]. Given original estimated grid line at x, the refinement will search in [x * (1 - refine_intensity), x * (1 + refine_intensity)]. |
| fix_square | BOOLEAN | true | Whether to enforce output to be square when detected image is almost square. |
| debug | BOOLEAN | false | Whether to show debug plots.s |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| scaled_image | IMAGE | — |
| refined_w | INT | — |
| refined_h | INT | — |