Nodes/toobusy · 너무바쁜베짱이/2BZ Face-Size Scale Plan
ComfyUI Node

2BZ Face-Size Scale Plan

Your retouch looks plasticky because the face was 98 pixels wide

By nicekriss·Created about a year ago·Updated about 11 hours ago· 21
2BZ Face-Size Scale Plan
  • image
  • bboxes
  • detect_image
  • vosr2_upscale
  • width
  • height
  • scale
  • info
  • needs_upscale
◄target_face_px200►
◄picklargest►
◄no_face_megapixels4.0►
◄max_scale4.00►
◄shrink_large_facesfalse►
◄multiple_of16►

A face that occupies 98 pixels of a 1MP photo comes back from a generative retouch looking like a wax museum. Give that same face about 200 pixels to live in first and the same pass looks like it came off a better camera. That's the entire idea behind 2BZ Face-Size Scale Plan (ToobusyFaceScalePlan) - a node that outputs no pixels at all. It measures the faces SAM3 found and hands you the working resolution that puts a face near that sweet spot.

What it is, in the wider workflow

Upscaling is three jobs wearing one word, and this belongs to the second: the generative restoration rung, where SeedVR2 has been the incumbent since late 2025 and the output is new micro-texture (pores, lashes, fabric weave) rather than a bigger canvas.

Such a model can only add detail where there's pixel budget to spend. A 70px face in a 1024px frame is a smear because the latent has nothing to work with, and the standard fix is to give that region its own pass - the detect → crop → re-render loop detailers automate. This node decides how much to upscale, from a measured face instead of a number you made up. The author's numbers are baked into the tooltip: 1MP full-body at ×2 (face ≈ 201px) looked dramatically better, the same image at ×4 (face ≈ 397px) looked over-sharpened, and a close-up with a 500px+ face needs no pass at all.

How it works

Arithmetic over bounding boxes, short enough to read in one sitting:

  1. SAM3's face boxes arrive as bboxes. Widths are mapped back into image's coordinate space, sorted, and one is picked - largest by default.
  2. scale = target_face_px / face_width, capped by max_scale. If the face is already wider than the target and shrink_large_faces is off, scale snaps to exactly 1.0 and the original size passes through untouched.
  3. Scale becomes width/height, rounded to multiple_of (16). At scale exactly 1.0 you get the original numbers back, no rounding.
  4. Separately it computes vosr2_upscale: a whole number, minimum 2, capped at 4, chosen as ceil(scale / 1.1). That deliberate 10% undershoot is the neat part - a 2.04× plan runs VOSR 2.0 at ×2 and lets a plain Image Scale finish the last 2%, instead of burning a ×3 pass only to shrink back down.

If SAM3 finds nothing, it falls back to no_face_megapixels (default 4MP) rather than quietly doing nothing.

The inputs and outputs that matter

  • target_face_px (default 200) - the knob. Higher is crunchier; the tooltip admits too high goes unnatural.
  • pick - largest, median, smallest. On a group shot or a character sheet, largest is what stops the biggest face from getting over-processed.
  • detect_image (optional) - wire the image SAM3 actually saw. The example scales to 2MP before detection to keep it fast; skip this input and those box widths get read as original-resolution pixels, so your scale lands roughly 2× too big.
  • max_scale (default 4) is a per-side cap, so it's a 16× area ceiling. shrink_large_faces stays off unless you genuinely want a big face shrunk to the target. no_face_megapixels at 0 means "leave it alone".

Six outputs, fanning out three ways:

bboxes    <- SAM3 Detect ("face:8")
image     <- Load Image
             2BZ Face-Size Scale Plan (target_face_px 200, pick largest)
   vosr2_upscale -> VOSR 2.0 Upscale   (upscale input)
   width/height  -> Image Scale        (width, height inputs, lanczos)
   needs_upscale -> 2BZ Use Upscaled If Needed (use_upscaled)
   info          -> Show Text / Note

scale is the raw float multiplier. info is the plan's log line - input size, face count, why it picked that scale, whether VOSR2 is being skipped - and it prints in Korean, like the rest of the author's docs.

One caveat about ordering

The usual community rule is detail at native resolution first, then upscale, because a diffusion detailer bakes a mangled face into more pixels. That rule is about detailers that re-render with a prompt and rewrite identity. SeedVR2 and VOSR 2.0 add texture without re-inventing the subject, which is why upscaling before the pass is fine here - same pipeline, different job.

Install

Both nodes in this pair are new in toobusy 0.5.6, so version matters more than usual:

cd ComfyUI/custom_nodes
git clone https://github.com/nicekriss/toobusy.git toobusy

ComfyUI Manager finds the pack by title, but it installs whatever is on the Comfy Registry, and older registry builds have been pulled - you can land on 0.4.8, where this node doesn't exist. The install succeeds and the rest of the pack works, so there's no error to chase. Check custom_nodes/toobusy/pyproject.toml for the version line; if it's behind, use the clone above or Manager's Install via Git URL.

Beyond the pack it wants VOSR 2.0 (comfyui-vosr2, searchable in Manager; its ~7GB model downloads on first run) and SAM3 for detection, with sam3.1_multiplex_fp16.safetensors dropped in ComfyUI/models/checkpoints/. SAM3 Detect is core. None of this pack's requirements_*.txt files apply here - those cover the rembg, face-mask, FlashVSR and DreamID nodes.

Common issues

It scaled my 1MP portrait to 4MP for no reason. SAM3 found no faces, so the fallback fired. Check the detect prompt uses the target:count grammar (face:8); without the :N suffix SAM3 returns one box per category, which looks a lot like detection failing.

The scale is way bigger than it should be. You downscaled before detection and didn't connect detect_image.

It did nothing on my close-up. By design - the face is already past 200px, needs_upscale came back false, and the pass downstream got skipped. Raise target_face_px in small steps if you want crunch anyway; 397px is where the author measured it going over.

SAM3 won't initialize. Core SAM3 is what the example uses, but some community SAM3 packs need Triton, a known Windows headache.

The graph is slow for a node that only outputs numbers. It sits in front of a VOSR 2.0 pass and the model load behind it. Cleaning that up is the entire job of its companion, 2BZ Use Upscaled If Needed.

Categorytoobusy/retouch

Inputs (9)

NameTypeDefaultDescription
imageIMAGE원본 이미지(이 크기 기준으로 배율 계산)
bboxesBOUNDING_BOXSAM3 Detect 'face' 박스
target_face_pxINT20032–2048처리할 때 얼굴(SAM3 face 박스) 폭. 200이 실측 적정선 — 올리면 더 쨍하고, 너무 크면 부자연스러움.
pickCOMBOlargest얼굴이 여러 개일 때 기준. largest = 가장 큰 얼굴이 과해지지 않게(권장).
no_face_megapixelsFLOAT4.00–32얼굴이 없을 때 이 MP 근처로 키움(이미 크면 그대로). 0 = 그대로.
max_scaleFLOAT4.001–8한 변 최대 배율(4 = 면적 16배)
shrink_large_facesBOOLEANfalse얼굴이 목표보다 크면 줄여서 처리(기본 끔 = 크기 그대로)
multiple_ofINT168–64—
detect_imageoptIMAGESAM3가 본 이미지(줄여서 검출했을 때). 박스 좌표를 원본 크기로 환산.

Outputs (6)

NameTypeDescription
vosr2_upscaleINT—
widthINT—
heightINT—
scaleFLOAT—
infoSTRING—
needs_upscaleBOOLEAN—