DNF 像素艺术重构(三色阶/结构线/净色块)
When 'close enough' pixel art has to become a game asset
- pixel_image
- reference
- structure_map
- final_original_background
- green_edge_check
- subject_mask
This is the pack's heavyweight and the closest thing to "one button to game asset" in this corner of ComfyUI. Where the other two nodes do color work across the whole frame, DNFPixelPerfectFinalize spends every bit of effort on one thing: the character. It restores the original painting's color and shading onto just the subject, compresses each "intrinsic color" into a strict dark/mid/light three-tone set, scrubs out stray specks and holes, redraws a clean continuous outline - in each area's own shadow color, never pure black - and can rebuild one-pixel internal lines from a Canny structure map instead of letting the quantizer guess them. If you've ever squinted at an AI pixel portrait and thought "the outline is mush and the face has 40 colors," this is aimed at you.
How it works, in order: it finds the subject by diffing your pixel candidate against the spatially aligned reference within mask_tolerance (a per-row distance threshold), then cleans that binary mask's edges. Inside the mask it restores reference chroma at reference_color_strength and reference tone at reference_tone_strength - and the head region is handled separately at a much higher tone strength, because faces need the original's shading or they flatten into plastic. Saturation gets a boost (saturation_gain), then chroma is clustered into "intrinsic color families" that deliberately ignore luminance (color_block_cleanliness decides how many), and each family's pixels snap to exactly three brightness steps. The outline is rebuilt from the eroded mask boundary using each family's darkest step, and optional internal lines come from your structure_map gated by internal_line_strength, again drawn in the family shadow color.
The inputs worth your attention:
pixel_imageandreference- your downscaled pixel candidate (feed it the result of the palette step) and the original art.referenceis auto-resized to match.structure_map(optional) - wire a Canny edge map here so internal lines come from real armor/feather/clothing boundaries in the source rather than post-processing guesses. The pack's own workflow feeds the same slim Canny used by ControlNet into both places.color_block_cleanliness- the README's guidance is concrete: ~0.65 keeps more decorative colors for complex large art, ~0.82 gives 8–9 families (this is the "recommended default" feel even though the code default is 0.78), ~0.92 pushes hard to 6–7 families for small final game art.foreground_colors- a total safety ceiling on color count; ~36 for an ordinary character, ~48 for armored/multi-material ones. The green background doesn't count against it.mask_tolerance(0.065),outline_width(1),outline_strength(0.64),cleanup_passes(1) - tune these if the subject mask bleeds onto the background or the edge looks soft.
Three outputs, each with a job: final_original_background is the finished subject sitting on its original background; green_edge_check is the same result with the background forced to pure green - this is your edge-quality QA view and your chroma-key source; subject_mask is the mask so you can composite onto an actual game background that isn't the original painting.
Install is the same for the whole pack - Manager search DNF Pixel Perfect Color Lock (registry dnf-pixel-perfect), or:
cd ComfyUI/custom_nodes
git clone https://github.com/yifu23333/ComfyUI-DNF-ColorLock
Restart, no models to fetch, no dependencies beyond the NumPy/Pillow/torch ComfyUI already ships. It lives under DNF/Pixel Perfect. One practical note from v2.3.1: the new color_block_cleanliness and internal_line_strength sliders were moved behind the older integer widgets specifically so old cached node definitions don't mis-map floats onto outline_width - if you upgraded, clear the cache/reload the workflow rather than fighting a weird outline width.
For a final reality check: none of this works if your reference and candidate don't roughly match framing, because the mask is a pixel diff, not a semantic segmenter. Match your crops upstream and this node will do the part diffusion can't - the part that makes it pixel art.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| pixel_image | IMAGE | — | |
| reference | IMAGE | — | |
| mask_tolerance | FLOAT | 0.0650.01–0.3 | — |
| reference_color_strength | FLOAT | 1.000–1 | — |
| reference_tone_strength | FLOAT | 0.480–1 | — |
| saturation_gain | FLOAT | 1.240.5–2 | — |
| foreground_colors | INT | 3618–256 | — |
| cleanup_passes | INT | 10–3 | — |
| outline_width | INT | 11–2 | — |
| outline_strength | FLOAT | 0.640–1 | — |
| color_block_cleanliness | FLOAT | 0.780–1 | — |
| internal_line_strength | FLOAT | 0.720–1 | — |
| structure_mapopt | IMAGE | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| final_original_background | IMAGE | — |
| green_edge_check | IMAGE | — |
| subject_mask | MASK | — |