Nodes/comfyui-fvmtools/Sign Detailer
ComfyUI Node

Sign Detailer

Typeset it yourself, then let the sampler restyle

By ping1979ping·Created 6 months ago·Updated about a month ago· 0
Sign Detailer
  • images
  • sign_data
  • model
  • clip
  • vae
  • positive_base
  • negative
  • sign_options
  • dd_options
  • images
  • refined_crops
  • glyph_preview
  • report
seed0
steps8
denoise0.85
sampler_nameer_sde
schedulersimple
auto_resolutiontrue
target_width1024
target_height1024
max_upscale8.0
glyph_guidanceinit
glyph_font<auto>
glyph_denoise0.55
glyph_strength1.00
glyph_fitauto
glyph_cylinder0.00
glyph_match_source_sizetrue
glyph_preserve_surfacetrue
glyph_surface_restyle0.35
glyph_autocolortrue
glyph_plate_color
glyph_ink_color
min_legible_px0
too_small_policysoften
cluster_modeshared_seed
verify_afteroff
verify_similarity0.60
max_attempts2
mask_expand_pixels4
mask_blend_pixels16
detail_daemon_enabledfalse
detail_amount0.00

Everything before this node decides what the sign should say. Sign Detailer is the part that makes the words actually legible, and it does it with a trick so obvious you wonder why every text-repair tool doesn't do it: it typesets the target text itself, warps it onto the sign's real quadrilateral, composites it into the crop, and only then runs the sampler. The model never has to invent letterforms - it just restyles material, lighting and perspective. It's the AnyText/GlyphControl idea implemented with PIL and a quad, no extra model, which is why it works even with checkpoints that can't render text on their own.

The settings that matter

  • glyph_guidance - init (default), init_strong (0.15 lower denoise, maximum letter fidelity, less freedom to match the surface), or off (prompt-only, needs a genuinely text-capable model). Leave it on; it's the whole point.
  • glyph_denoise - the measured sweet spot is 0.55. At 0.35 the text is clean but the surface is flat and characterless; at 0.65 a ghost copy of the word appears behind the first; above 0.70 the sign gets reinvented - warped, invented hardware, faded text. Stay at or below 0.60, and the node warns above it.
  • denoise - 0.85 default, and it's high on purpose: with glyph guidance off, the old garbled strokes survive anything below ~0.7. When glyph guidance is on, glyph_denoise takes over and can be much lower.
  • glyph_fit - auto measures how badly a four-corner fit misses the mask and switches to a column-wise contour fit when it does. perspective for flat signs, contour for curved labels and folded fabric. Add glyph_cylinder around 0.4–0.6 for the foreshortening of lettering wrapped around a bottle.
  • glyph_preserve_surface - on, only the lettering is replaced, not the whole plate. Off was the old behavior, and it erases frames, rims and painted borders, then makes the sampler invent them back from a flat fill - measured on real photos as turning a bordered enamel oval into a plain white disc.

Why repeat passes are safe

glyph_surface_restyle (default 0.35) decides how much of the denoise the surface around the lettering gets, as a fraction; the lettering itself always gets all of it. At 1.0 the whole plate re-renders every pass, so three passes means three generations of drift - measured on a navy enamel sign that walked to dark grey. At 0.35 a flat synthetic plate picks up enough material to look real without drifting. Set it to 0.0 for the most faithful repeat edits.

The trap in too_small_policy

Regions the selector flagged below its size gate get one of four fates: soften, skip, render, or erase. The trap is that soften - the default - is NOT a safe fallback, and the author measured it: at low denoise the pass doesn't erase the old lettering, so the model repaints it as sharp, transcribable pseudo-text. A census can read TOKL CUBA ANY RODA off the wine shelf, which means it's legible fantasy writing - the exact thing you were trying to kill. For a surface too small to carry a readable word, erase is the honest choice: it empties the text band, rebuilds the surface from its own edge, and writes nothing back.

Verification, model choice, steps

verify_after: ocr reads the result back and retries with a fresh seed if the text didn't land, up to max_attempts; it silently stays off without an OCR backend. Use a text-capable model - Qwen-Image, Ideogram, or Krea 2. Turbo and SDXL-class models can't render legible words and will just produce different gibberish; the node detects known text-weak families and warns in the report. Steps: 8 for Krea 2 Turbo (it's distilled, more steps over-cook it), 15–25 for Raw or Qwen-Image. And note the sampler default is er_sde, not euler - the author verified it against Krea 2, and these settings were measured with it.

Outputs

images (pasted back onto the originals), refined_crops, glyph_preview - check this to see exactly what the typeset layer looked like before sampling; it's your first debugging move - and a report.

Installing it

The pack clone into custom_nodes, restart, done. Glyph guidance finds fonts in the pack's fonts/ folder plus your system font directories. The optional OCR backend comes from python scripts/fetch_ocr_models.py.

CategoryFVM Tools/Text

Inputs (40)

NameTypeDefaultDescription
imagesIMAGEThe image batch the selector scanned
sign_dataSIGN_DATARegions with proposals, from Sign Text Proposer
modelMODELUse a text-capable model — Qwen-Image, Ideogram, Krea 2
clipCLIP
vaeVAE
seedINT00–18446744073709550000
stepsINT81–1008 is the verified value for Krea 2 Turbo, which is distilled — more steps over-cook it. Non-distilled models (Krea 2 Raw, Qwen-Image) want 15-25.
denoiseFLOAT0.850–1Used when glyph guidance is off. High on purpose: the old garbled strokes survive anything below ~0.7.
sampler_nameCOMBOer_sdeer_sde is the verified pairing for Krea 2. Without an explicit default ComfyUI would pick euler, which is not what these settings were measured against.
schedulerCOMBOsimple9 options: simple, sgm_uniform, karras, exponential, ddim_uniform, beta, +3
auto_resolutionBOOLEANtrueShape the sampling resolution like the region instead of using target_width x target_height literally. Those two then only set the pixel BUDGET, so VRAM use stays the same. A fixed square wastes most of that budget on a wide sign: the crop is expanded to 1:1 first, leaving the lettering in a thin band. Measured on a 550x95 sign — square 1024 gives the text 1.9x upscale, the same budget at the region's own aspect gives 4.5x.
target_widthINT102464–4096With auto_resolution on this is half of the pixel budget, not a literal width.
target_heightINT102464–4096With auto_resolution on this is half of the pixel budget, not a literal height.
max_upscaleFLOAT8.01–32Caps how far a small region is blown up before sampling. Beyond this the model hallucinates detail that breaks on stitch-back.
glyph_guidanceCOMBOinitinit: typeset text composited into the crop, sampled at glyph_denoise init_strong: same, but 0.15 lower denoise — maximum letter fidelity, less freedom to match the sign's material and lighting off: prompt-only, needs a genuinely text-capable model
glyph_fontCOMBO<auto>Typeface for the rendered text. <auto> follows the model's font hint.
glyph_denoiseFLOAT0.550–1Denoise when glyph guidance is on. Lower keeps the letterforms exact, higher blends better into the surface. Measured on Krea 2 Turbo (8 steps, cfg 1, er_sde/simple): 0.35 clean text, but the surface stays flat and characterless 0.55 best - sharp text AND real material (enamel, screws, wear) 0.65 a second ghost copy of the word appears behind the first 0.70+ the sign is reinvented: warped, extra hardware, text faded Stay at or below 0.60.
glyph_strengthFLOAT1.000–1Opacity of the typeset layer before sampling. Keep at 1.0: measured, even 0.95 leaves the ORIGINAL garbled lettering clearly readable underneath, and the sampler then has it in the init latent. Lower this only to deliberately carry over the old surface shading.
glyph_fitCOMBOautoHow the typeset text is fitted onto the region. auto: measures how badly a four-corner fit misses the outline and switches to the column-wise fit when it does perspective: four corners — correct for flat signs, gives the text a vanishing line when the sign is angled away contour: follows the mask's top and bottom edge column by column — for curved labels, folded fabric, torn paper. A flat plane cannot describe those, so the text would otherwise sit dead straight on a bowed surface.
glyph_cylinderFLOAT0.000–1Extra horizontal compression towards the sides, the way lettering wrapped around a bottle or can foreshortens away from the viewer. Only applies to the contour fit. Try 0.4-0.6 for bottle labels, 0 for anything flat.
glyph_match_source_sizeBOOLEANtrueSet the replacement text at the size the surface already used, instead of scaling it to fill the region. Filling the box is right for a headline sign and wrong for everything else. A wine label carries one large number over three lines of small print — scaled to the box it becomes a poster, and long words no longer fit and get cut off at the edge. The size is measured from the existing strokes, so it adapts per region. It is a cap, not a fixed size: longer replacement text still shrinks to fit.
glyph_preserve_surfaceBOOLEANtrueReplace only the lettering, not the whole region. Painting the full masked area with the plate colour erases everything the surface had, and the sampler must then invent it back from a flat fill. On real photographs that turned a bordered enamel oval into a plain white disc, and a curved shop-window inscription into a pasted-on banner covering the glass. On: only the old strokes and the new ones are covered. Parts that run into the region's edge — frames, rims, painted borders — are recognised as structure and left alone. Off: the old full-area behaviour.
glyph_surface_restyleFLOAT0.350–1How much of the denoise the surface AROUND the lettering gets, as a fraction. The lettering itself always gets all of it. This is what makes a second pass over the same sign safe. At 1.0 the whole plate is re-rendered every time, so three passes means three generations of drift — measured on a navy enamel sign, the plate walked to dark grey and the border broke up. 0.0 the surface is untouched — most faithful for repeat edits 0.35 default — enough for a flat synthetic plate to gain material 1.0 the old behaviour Only applies with glyph_preserve_surface on.
glyph_autocolorBOOLEANtrueSample ink and plate colour from the original sign so the replacement keeps its scheme.
glyph_plate_colorSTRINGForce the SURFACE colour of the typeset layer. Empty = use the colour sampled from the original. Accepts '#ffe680', 'ffe680', '#fe8' or '255,230,128'. Use this when you want a different surface than the one in the picture — a yellow post-it, a white plate, a green road sign — and describe that surface in the Sign Options prompt_suffix too.
glyph_ink_colorSTRINGForce the LETTER colour of the typeset layer. Empty = sampled. Same formats as the plate colour.
min_legible_pxINT00–128Smallest capital height, in pixels, the replacement lettering is typeset at. A floor under glyph_match_source_size, not a gate: a region that cannot carry the word that large still gets it, only smaller. 0 = off.\n\nWithout it the new text is bound to the size of the OLD text, and on a defocused label the old text measures 4-6px - so a word was set at 4-5px on a surface that would carry three times that.\n\nOff by default. It demonstrably sharpens single words (RIESLING came back clean where it had read IESLIN/SLIN), but a full suite run could not show a gain: larger type in pass A shifts the region layout the selector finds in pass B, which moves words onto other surfaces. Opt in per job, do not switch on blind.
too_small_policyCOMBOsoftenRegions the selector flagged below its size gate.\nsoften: re-render as out-of-focus text - but measured, this invents fresh sharp pseudo-lettering instead of blurring what is there\nskip: leave untouched\nrender: try anyway\nerase: empty the text band and rebuild the surface from its own edge, writing nothing back - no sampler pass runs over the region at all, so there is nothing left that could invent lettering. Take it for a surface too small to carry a readable word: a blank surface is the only outcome a census cannot read words into.\n\nsoften is NOT a safe fallback, and this is measured. At denoise 0.35 the pass does not erase the lettering already there, so the model repaints it as its own invention - sharp, transcribable pseudo-text rather than blur. Caught on the wine shelf as TOKL CUBA ANY RODA, TEGLING AVYAT, BUTORT DOATRKR. A census counts that as gibberish, and it is right to: anything legible enough to transcribe is legible fantasy writing.
cluster_modeCOMBOshared_seedshared_seed renders every member of a cluster with the same seed and prompt, so a shelf of identical bottles stays identical.
verify_afterCOMBOoffocr: read the result back and retry with a new seed if the text did not land. Needs an OCR backend; without one it silently stays off.
verify_similarityFLOAT0.600–1Minimum similarity between the read-back text and the target.
max_attemptsINT21–5Render attempts per region when verification fails.
mask_expand_pixelsINT40–64Small expansion helps cover anti-aliased edges of the old lettering.
mask_blend_pixelsINT160–128Lower than for faces — a hard-edged sign should not fade into its surroundings.
detail_daemon_enabledBOOLEANfalseUsually off for text: extra detail turns clean strokes noisy.
detail_amountFLOAT0.00-5–5
positive_baseoptCONDITIONINGFallback conditioning when a region has no text
negativeoptCONDITIONINGOverrides the negative prompt from Sign Options
sign_optionsoptSIGN_OPTIONSPer-class overrides and advanced settings
dd_optionsoptDD_OPTIONS

Outputs (4)

NameTypeDescription
imagesIMAGE
refined_cropsIMAGE
glyph_previewIMAGE
reportSTRING