DeepSeek OCR Expand Subset BBox
Grow OCR boxes into the gaps without swallowing neighboring text
- image
- ocr_result
Imagine a product photo with a promotional banner: the OCR finds the banner's text lines, but each detected box hugs its glyphs tightly. To mask the whole banner - including the blank space between lines and around the graphic - you want each box to grow outward until it fills the region. The catch is you don't want it to grow into the neighboring product text you're trying to keep.
That's exactly the trade this node is built for. It takes two OCR results, A and B, where B is a subset of A (say, only the boxes you flagged as promotional). Every box in B expands outward as far as max_expand allows, but boxes in A - B - everything you didn't select - are treated as protected obstacles. Expansion stops before crossing them.
How it works
The algorithm is per-side and independent: left, right, top, and bottom each try to move outward up to max_expand pixels, and only protected boxes on the corresponding side limit that side. Boxes inside B don't block each other, so a run of selected lines fuses into one big region. Boxes are half-open ([x1, y1, x2, y2)), so touching a protected edge is allowed but overlapping isn't - which is a nice detail when boxes in real OCR output share edges.
The output keeps B's original format (<|ref|>/<|det|> tags and text) and only swaps each bbox for its expanded version, so you can feed the result straight back into the pack's mask or draw nodes.
Inputs that matter
ocr_result_a/ocr_result_b- both STRING sockets.Ais the full OCR result;Bis the subset you're expanding. Same coordinate base for both.max_expand- how far each side may grow, in pixels, default100. This is your "how greedy" dial.safety_margin- extra protected padding around everyA - Bbox. Bump this up if you keep kissing the text you wanted to save.coord_base-1000for DeepSeek's normalized output,0for pixels.ignore_empty_label- defaulttrue, and it's a quiet lifesaver: it drops blank/unlabeled detections (including whitespace-only invisible Unicode) from the protected set, so a blank region OCR found doesn't block expansion.output_coord_base--1(default) keeps whatever base the input used;0forces pixels.
image_width/image_height default to 0; if you also connect the optional image, it reads the size automatically (needed for clipping to image bounds and for coord_base=1000 conversion). Expanded boxes clip to the image edge, so they can touch the border but never cross it.
Installing
This is part of comfyui-dsocr-bbox. Manager (search "dsocr") or:
cd ComfyUI/custom_nodes
git clone https://github.com/maomaozi/comfyui-dsocr-bbox
restart, then:
pip install -r custom_nodes/comfyui-dsocr-bbox/requirements.txt
Where people get burned
The most common mistake is flipping A and B - the node expands B and protects A - B, so if you wire them backwards you expand everything you meant to protect. The other trap is a too-aggressive max_expand combined with safety_margin=0: neighboring text you wanted to keep will get absorbed if it wasn't detected in A at all (protected set is only as good as the detection list). If blank areas keep blocking your growth, leave ignore_empty_label on. And remember the output stays in the input's coordinate system unless you change output_coord_base - if downstream expects pixels and you're feeding normalized JSON, convert explicitly.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| ocr_result_a | STRING | — | |
| ocr_result_b | STRING | — | |
| image_width | INT | 00–100000 | — |
| image_height | INT | 00–100000 | — |
| coord_base | INT | 10000–100000 | — |
| max_expand | INT | 1000–10000 | — |
| safety_margin | INT | 00–10000 | — |
| ignore_empty_label | BOOLEAN | true | — |
| output_coord_base | INT | -1-1–100000 | — |
| imageopt | IMAGE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| ocr_result | STRING | — |