Nodes/comfyui-fvmtools/Signs to Ideogram Caption
ComfyUI Node

Signs to Ideogram Caption

The JSON bridge you're not allowed to skip

By ping1979ping·Created 7 months ago·Updated about 10 hours ago· 3
Signs to Ideogram Caption
  • sign_data
  • image
  • caption_json
  • box_prompts
  • mask
  • bboxes
  • width
  • height
  • report
◄high_level_description►
◄background►
◄lookphotographic►
◄text_inset0.07►
◄mask_pad_px4►
◄empty_too_smalltrue►
◄min_word_px0►
◄fallback_text►
◄auto_scenetrue►
◄lm_base_urlhttp://localhost:1234/v1►
◄lm_model_idqwen3-8b-vl-instruct-abliterated►
◄surface_hint►

Ideogram 4 shipped its weights in June 2026 - 9.3B, non-commercial licence, quantized checkpoints only, and a safety filter so eager it emits a dedicated refusal image rather than your prompt. It was the best text-in-image model open weights had ever seen, and it talks in JSON, not prompts. That's the whole reason this node exists: it converts the pack's SIGN_DATA into the structured caption the Ideogram node actually consumes, so a chain that starts with "find the lettering and decide what it should say" can end in an Ideogram render. (It's also why the community's LoRA production moved to Krea 2 three weeks later - better renderer, worse licence terms, same old story.)

The one thing you must get right

Wire caption_json into Ideogram4PromptBuilderKJ's import_json input and set import_mode to always. That isn't a preference, it's the only route that works. The builder's elements_data widget is re-serialised from a live array in the browser every time the graph is queued, so anything written into it from outside gets overwritten the instant you press run - silently. If your caption seems to be ignored, this is why, and it's the first thing to check.

Inputs

  • sign_data - from Sign Text Proposer, or from the selector if you're supplying the words yourself.
  • image - only its size is used, to convert boxes into Ideogram's 0–1000 grid. Feed the same image the selector scanned.
  • high_level_description - one line about the whole picture. Blank is allowed, but blank is worse.
  • background - the scene around the lettering: light, weather, materials. This is what keeps the replacement inside the same photograph as the rest of the frame.
  • look (photographic / flat graphic) - Ideogram's untended pull is poster art: ask for a sign and it answers with flat vector lettering on a clean plate. The caption therefore states the camera, not the design. Only switch to flat graphic when the source genuinely is one.
  • text_inset (0.07) - how far the text box sits inside the region. The mask stays full size; the box shrinks. A box that reaches the edge of a panel puts letters past that edge.
  • mask_pad_px (4) - extra mask around each region. Err high: a mask that misses a stroke leaves the old scribble standing bit-identical, which is the single most visible failure in this pipeline.
  • empty_too_small (on) - regions the selector judged too small for a readable word are described as blank rather than written on. Turn it off and you get letter-like shapes where words should be, and Ideogram counts those as invented text.
  • min_word_px (0) - the last safety net before the caption. Box heights below this get no word; the surface is described as blank instead. Leave it on if you're splitting multi-line signs, because the selector measures regions before the split.
  • fallback_text - used for a region the proposer left wordless. Blank means that region gets emptied.
  • Optional auto_scene (on) with lm_base_url / lm_model_id - if the two text fields above are empty, the node writes the scene description itself with the local vision model, which is what lets the graph run from nothing but a chosen image. surface_hint overrides the built-in wording per text class, one class: description line each.

Outputs

caption_json into the builder's import_json; box_prompts for reading what was decided; mask for whatever inpaints or re-renders the regions; bboxes, width, height for debug and for grid math; report for the log.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/ping1979ping/comfyui-FVMtools

Restart ComfyUI, or install from ComfyUI Manager with comfyui-fvmtools. No extra Python packages for this node - it's numpy, torch and json. Ideogram 4 itself is a separate download, you'll want Kijai's prompt-builder node alongside it, and remember that Ideogram's weights are non-commercial.

Where people get burned

import_mode left on its default is the number one cause of "the caption did nothing". Check that first, before you touch a single parameter.

Leftover scribble around the new text means the mask is too tight - raise mask_pad_px. Text spilling off the edge of a sign means the opposite problem, the text box is too big - raise text_inset.

And resist the urge to describe the design here. This node's caption fields are about the photograph; the lettering itself comes from the proposer. Fill the design in by hand and you'll fight the model, because Ideogram's guidance - confirmed by the author's own testing - is that describing the scene as photographic is the counterweight to its pull toward flat poster lettering.

CategoryFVMtools/signs

Inputs (14)

NameTypeDefaultDescription
sign_dataSIGN_DATAFrom Sign Text Proposer (or the selector, if you supply the words yourself).
imageIMAGEThe image the selector scanned. Only its size is used, to convert boxes into Ideogram's 0-1000 grid.
high_level_descriptionSTRINGOne line about the whole picture. Blank is allowed.
backgroundSTRINGThe scene around the lettering: light, weather, materials. This is what keeps the replacement in the same photograph as the rest of the frame.
lookCOMBOphotographicIdeogram drifts towards poster art unless told otherwise. Use 'flat graphic' only when the source genuinely is one.
text_insetFLOAT0.070–0.45How far the TEXT box sits inside the region. The mask stays full size. A box that reaches the edge of a surface puts letters past that edge.
mask_pad_pxINT40–64Extra mask around each region. Err high: what the mask misses is restored unchanged and stands there as leftover scribble.
empty_too_smallBOOLEANtrueRegions the selector judged too small for a readable word are described as blank instead of being written on. Off: every region gets a word, which produces letter-like shapes that count as invented text.
min_word_pxINT00–200Box heights below this get no word — the surface is described as blank instead. 0 turns the test off. The selector has its own height floor, but it measures the region BEFORE the line splitter cuts it into bands, so a 90px sign split into four passes the selector and arrives here as four 20px strips that the splitter only checks against its own 8px 'is this a line at all' bar. This is the last station before the caption and the only one that sees the geometry that will actually be rendered.
fallback_textSTRINGUsed for a region the proposer left without a word. Blank means that region is emptied.
auto_sceneoptBOOLEANtrueLook at the picture and write the scene description itself when the two text fields above are empty. That is what lets the graph run from nothing but a chosen image.
lm_base_urloptSTRINGhttp://localhost:1234/v1LM Studio endpoint for the scene description.
lm_model_idoptSTRINGqwen3-8b-vl-instruct-abliteratedName it. 'Whatever is loaded' is not a default, it is a silent failure: LM Studio lists models it has not loaded, the call returns nothing, and the run continues with an empty description. Must be a VISION model.
surface_hintoptSTRINGPer class, one per line, 'class: description'. Overrides the built-in wording for that class.

Outputs (7)

NameTypeDescription
caption_jsonSTRING—
box_promptsSTRING—
maskMASK—
bboxesBOUNDING_BOX—
widthINT—
heightINT—
reportSTRING—