Crop Prompt from Config
The one-wire adapter for your segmentation node
- crop_config
- subject_mask
What it's for
Your subject mask prompt is sitting inside an Input & Crop Config bundle. Your segmenter - SAM 3, Grounded SAM, whatever you're using for promptable masking - wants a plain STRING. Typed bundles don't connect to primitive sockets, and that's deliberate: the whole point of the bundle is that configuration travels as one object, not as fifteen wires.
So this node is the adapter. One bundle in, one subject_mask string out. Nothing to configure, no widgets, no state. It's about as close to a no-op as a node gets, and it's the difference between "this config system works" and "I'm maintaining two copies of every prompt."
How it works
Required input: crop_config (PORTRAIT_CROP_CONFIG_V2). Output: subject_mask (STRING).
It reads the field and returns it. It does not rerun preparation, recrop, or validate that your prompt is a good one - the suite's own framing is that readers "expose existing values; they do not rerun preparation." If you change the prompt in the config node, the next execution picks it up.
Where it fits
The reason this exists is the mask stage, and specifically the fact that promptable segmentation lets you name a region instead of training a detector for it. SAM 3 takes a text phrase; GroundingDINO + SAM takes a phrase and returns a precise mask. Both want a string, and an editable prompt in your config is exactly the thing you want to change per project without redoing the graph.
Input & Crop Config ── crop_config ──> Crop Prompt from Config ── STRING ──> segmenter (subject mask)
│
v
Smart Photo Prepare (mask)
That wiring is the pack's documented shape: the subject prompt drives the segmenter, the segmenter produces a mask, and Smart Photo Prepare uses that mask to place its crop. Change the prompt from person to the dog and the framing follows.
Two practical notes. Put the reader next to the segmenter, not next to the config - the whole reason for a reader instead of a direct wire is that a short local hop is easier to read than a wire crossing the canvas. And remember this emits exactly one string: the subject_mask field. If your workflow also needs a second, different prompt - the pack's own example uses a separate SAM 3 node prompted with head for the protected-region mask - that prompt lives in its own text source, because the crop config only carries the one. That second prompt doesn't come out of the crop config - there's only the one field to read.
Related readers
The same pattern covers the rest of the suite, and it's worth knowing the family so you grab the right one instead of a generic converter:
- Edit Prompt from Config - pulls the inpaint prompt or one of the three stitch prompts out of the edit bundle.
- Outpaint Prompt from Config - the outpainting prompt as a
STRING. - Seed from Config - the run seed as an
INT, for a sampler. - Scale from Config -
initial_upscaleorfinal_upscaleas aFLOAT, for a third-party upscaler. - Save Info from Source - filename and output directory out of the loader's source bundle.
Everything on that list is one input, one output, zero decisions.
Where people get burned
The only real failure mode is trying to skip it. Converting the segmenter's prompt widget to an input and wiring the config bundle into it won't work - the socket is STRING, the bundle is PORTRAIT_CROP_CONFIG_V2, and ComfyUI will refuse the connection. Same story if you try to feed the bundle into a PrimitiveString; the primitives are for primitives.
And if the reader errors with a type complaint, you've got a version mismatch, not a wiring mistake: an old 1.x config object (or a saved workflow carrying a retired AutoCrop Config node) is not a PORTRAIT_CROP_CONFIG_V2. Rebuild the config node and reconnect.
Install
Part of the PortraitUtils pack. ComfyUI Manager → search PortraitUtils (publisher heyburns, GPLv3), or:
cd ComfyUI/custom_nodes
git clone https://github.com/heyburns/PortraitUtils
/path/to/ComfyUI/venv/bin/python -m pip install -r PortraitUtils/requirements.txt
Install into ComfyUI's Python environment - numpy, Pillow, opencv-python-headless and tifffile, with only one OpenCV variant present - then restart the ComfyUI server. A browser refresh alone won't load new node classes, which is why "I installed it and there's no such node" is usually a restart problem.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| crop_config | PORTRAIT_CROP_CONFIG_V2 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| subject_mask | STRING | — |