# Production replacements for retired IDs The former replacement previews are now the production nodes. Their historical Python IDs still contain `Preview` from the testing period, but ComfyUI displays the proper names below. Superseded IDs are no longer registered. No workflow JSON is bundled in this repository. The historical local v5.3 workflow uses the production replacements but still needs current editor target labels and gate socket wiring; v5.2 additionally uses removed node IDs. See the [current editor wiring](SmartPhotoPrepare.md). ## Migrating a saved workflow Restart the ComfyUI server and refresh the browser. Reconnect matching sockets and copy old widget settings when replacing a node manually; changing a node type does not migrate its widget values automatically. Keep a copy of the old workflow for comparison. | Existing node | Replacement display name | Replacement ID | | --- | --- | --- | | Stitch by Mask (`StitchByMaskV2`) | Stitch by Mask | `PortraitStitchPreview` | | Simple Image Saver | Simple Image Saver | `PortraitImageSaverPreview` | | Process Scanned Photo | Process Scanned Photo | `PortraitScannedPhotoPreview` | | Paired Image Loader | Paired Image Loader | `PortraitPairedLoaderPreview` | | Crop by Margins (Image) | Crop by Margins (Image) | `PortraitCropImageMarginsPreview` | | Crop by Margins (Mask) | Crop by Margins (Mask) | `PortraitCropMaskMarginsPreview` | Existing required/optional/hidden socket names, existing widget order/types, defaults, and output types/order are mostly retained. The saver and paired loader add optional widgets. Reconnect old graphs by socket name and copy the intended settings; similarly named controls need not have identical behavior. ## Stitch by Mask Same inputs as `StitchByMaskV2`: `image_a`, `image_b`, the existing `PORTRAIT_EDIT_CONFIG_V2` bundle, `opacity_source`, inversion, bypass, feather radius, and optional force-size dimensions; optional `mask`. Same outputs: `stitched` IMAGE, `processed_mask` MASK. Changes: - Feathering operates on coverage first. Configured opacity is applied **once, last**, so the effective mask cannot exceed the selected blend/stitch opacity. Earlier versions could turn a 75% blend into nearly 100% after feathering. - Feathering is an outward Gaussian halo, bounded by `feather_radius`, with original soft coverage retained. There is no binary threshold or disk-dilation pass. Retune the radius if comparing with results saved by earlier versions. - Constant masks retain their coverage at image edges. CPU feathering uses OpenCV's efficient separable blur; non-CPU tensors use separable PyTorch filtering on their current device. No shaders/models are involved. - `processed_mask` is standard ComfyUI **BHW**. HW and BHW1 inputs are accepted too; any 3D mask is interpreted as BHW, including one-pixel-wide masks. Wrap HWC1 as BHW1 explicitly. - The source A batch is authoritative. B and mask may each have one member or match A's count. A single member broadcasts; other counts are rejected. - RGBA is retained and blended using premultiplied color weights, preventing transparent hidden RGB from tinting visible pixels. RGB/grayscale can be promoted when paired with an RGBA image. Alpha-aware image resizing avoids hidden-color bleed. RGB-only blends still use the legacy sRGB interpolation. - Source dimensions, dtype, and device are retained unless force-size is on. Half inputs are processed in float32; double inputs remain double. Images and masks must be finite, normalized 0–1. Mismatches produce actionable errors. With force-size on, output actually changes to the target dimensions; this is intentional compositing alignment, not the analysis-only option in white balance. Images use antialiased bilinear resizing; masks use nearest-neighbor resizing before feathering. With force-size off, all spatial dimensions must match. ## Simple Image Saver Same existing controls: `images`, `output_path`, `filename`, `suffix`, `file_format` (PNG/JPG), `jpeg_quality`, `include_metadata`, and `unique_filenames`. This remains an output node with **no graph output sockets**; its result is the usual ComfyUI saved-image UI preview. New optional controls: - `png_bit_depth`: **8-bit** (default, same output precision as before) or **16-bit**, preserving more of the working float image's precision. PNG16 supports grayscale, RGB, and RGBA, including workflow metadata. It cannot restore detail already lost earlier in the pipeline. - `jpeg_alpha_background`: **White** (default) or **Black**. PNG retains alpha; JPEG composites RGBA onto this matte rather than silently discarding alpha. Changes: - Encode a temporary file in the destination directory and publish only a complete, synced image. Encoding/publication failures clean up the temporary file. Existing destination files remain intact when encoding fails. - Unique mode atomically publishes through a no-overwrite hard link and retries numbered collisions. This requires a destination filesystem supporting hard links. Failure is explicit; it never silently switches to overwriting. - `unique_filenames=false` is an explicit overwrite choice and uses atomic replacement after successful encoding. Batch filenames keep their index. - Relative output paths stay under ComfyUI/output, including symlink checks. Explicit absolute paths remain supported. Necessary directories are created. - Filename/suffix components cannot create subdirectories, contain control characters, or use reserved device names. Excessively long names are rejected. - Grayscale works; float16/bfloat16 and other float inputs are safely converted for encoding. NaN/infinite inputs are rejected. Finite out-of-range pixels are clamped for display-format export, as in the old saver. - PNG workflow text supports Unicode. Non-finite numbers in workflow metadata are written as JSON `null` with a warning; the live workflow is not changed. Other invalid workflow JSON produces an error before image writes. Disabling metadata bypasses it; the global ComfyUI setting is respected. Oversized JPEG comments are omitted with a warning; use PNG for a large workflow. - External-directory UI previews copy already encoded bytes, preserving JPEG quality and PNG16 precision instead of re-encoding. If creating this preview fails after a successful save, the error reports the real saved path. Atomicity applies to each file, not an entire batch. Successfully published earlier batch files remain if a later file fails. The saver does not infer or convert a tensor's color profile; use a color-managed loader for your working photos. It does not copy original EXIF or invent a source ICC profile. PNG16 uses [OpenCV's documented unsigned-16-bit PNG encoder](https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html), with workflow text written as standard [PNG text chunks](https://www.w3.org/TR/png-3/). JPEG workflow comments use [Pillow's JPEG comment support](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#jpeg). ## Process Scanned Photo The original five controls remain: `image`, `straighten`, `crop_mode`, `padding`, and `threshold`. One IMAGE output. This is still a narrow scanned-print utility, not a subject cropper or perspective correction node. - **Scanner Bed Only** detects a dominant rectangular print against a bright scanner background and crops around its outer physical boundary. A distinct print border is retained, including a Polaroid's thicker lower border. - **Inner Photo Frame** starts from that print boundary, then seeks a broadly supported rectangular image/paper seam to remove the actual print frame too. It can fall back to conservative uniform-border detection for bright photos that do not qualify for the threshold-based inner detector. - **None** skips cropping; straightening can still operate if enabled. Straightening uses the dominant rectangular print, not the convex hull of all dark marks on the scanner bed. Isolated dust does not extend that rectangle. Confidence checks require bright matching corner background, a substantial rectangular region, and room around the print. Uncertain angle/boundary estimates are not forced. Outer detection uses contrast with the scanner bed rather than assuming the print's own white paper is the same thing as the scanner background. `threshold` is the minimum scanner-background brightness for outer detection and the bright-paper/darker-image divider for the inner threshold detector. It also influences the fallback uniform-border tolerance. It is no longer a single blind threshold for every stage. Adjust it to your scanner/print. `padding` is applied once around the final crop: positive retains extra context, negative crops further inward. Negative padding that removes the detected image raises an explanatory error. Scanner Bed Only can conservatively retain a thin scanner-bed fringe to avoid clipping physical print edges. Analysis is bounded to a 2048-pixel longest side, but **the actual pixels are never converted to 8-bit**. Crop-only processing is an exact slice of the original floating-point data. Rotation necessarily resamples the original full-resolution image once; dimensions expand to avoid clipping. Grayscale/RGBA, source dtype, and source device are preserved; geometry analysis/rotation are CPU/OpenCV work. RGBA rotation uses premultiplied colors. Differently sized batch results are padded white at bottom/right, not resized to each other. Limitations: one dominant print per scan; no perspective correction, semantic paper model, or guaranteed recovery of white-on-identical-white boundaries. Bright/textured frames, extreme dust, multiple prints, very thin low-contrast paper edges, or prints touching canvas edges can remain ambiguous. Inspect real scans before trusting a large batch. No settings mean “remove the subject.” ## Paired Image Loader The original inputs remain: `source_dir`, `output_dir`, `reverse`, and `strip_trailing_numbers`. Outputs remain **output_image, source_image, filename** in that order. Relative directory paths retain the old behavior: relative to the current process directory. Absolute paths are least ambiguous. Pairs match case-insensitive basenames, not file extensions. Natural sorting puts photo2 before photo10. A trailing `(n)` copy marker is ignored for matching only when requested; ordinary filename digits are never stripped. `filename` remains the actual source stem, matching the old output contract. Changes: - Each node instance has independent, bounded cursor state. Separate nodes do not affect one another even when using the same folders or hidden node ID. - Positions track the last successfully loaded basename, not an unstable numeric index. Adding an earlier file does not shift the cursor. Switching folder pairs preserves each folder pair's position. Removing the previous pair restarts from the first (or last in reverse). - Both files decode before the cursor advances. A broken second file retries the same pair until fixed; it does not silently skip or misalign images. - Every queued execution reloads/advances; cycling back to an earlier pair does not reuse a cached old image. Reverse starts from the last pair and wraps. - Ambiguous name collisions are errors unless there is exactly one common- extension pairing. Unmatched basenames are reported once per changed list. - Uses the tested Photo Loader preview decoder: EXIF orientation once, preserved 16-bit samples, optional 8-bit ICC conversion, and explicit alpha compositing. New optional controls: `color_management` (Convert to sRGB / Keep encoded values) and `alpha_background` (White / Black), with the same defaults and limitations as [Photo Loader](PhotoLoaderPreview.md), including explicit errors for 16-bit ICC conversions that would otherwise lose precision. Animated/multipage files use their first image. No resize aligns the two images automatically. ## Crop by Margins: Image and Mask Same controls: `left_px`, `top_px`, `right_px`, `bottom_px`, and `snap_multiple`. The image preview has `image` input/output; the mask preview has `mask` input/output. Use identical margin values for both. The two previews share one crop-box calculation. They preserve source pixels, precision, channels, and device without clamping, resampling, or color changes. MASK accepts HW/BHW/BHW1 and returns standard BHW, including multi-image batches. Every 3D MASK is BHW; use a batch dimension for HWC1 masks. Nonnegative margins trim inward. `snap_multiple` rounds the remaining width and height **down**, keeping the top-left fixed; any extra trim comes from right and bottom. There is no protection-mask or semantic framing logic in these manual nodes. Margins that remove the entire image, or a requested multiple larger than the remaining crop, raise an explanation instead of producing a hidden one-pixel result that does not honor the settings. No negative-margin padding, fill-color, or enforce-even widgets exist.