Files
ComfyUI-Dataset-Gates/docs/plans/2026-06-21-folder-image-loader-design.md
Ethanfel 08df5c8840 Add Folder Image Loader design + implementation plan
Dataset-oriented loader: folder path, control_after_generate index
(fixed/increment/decrement), depth control, sidecar .txt text output,
alpha->mask, stem filename, resolved index. TDD plan with a pure stdlib
scan layer; self-contained except a merge-aware root __init__ registration
(pool node is being built concurrently).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:15:05 +02:00

3.9 KiB
Raw Permalink Blame History

Folder Image Loader — Design

Date: 2026-06-21 Status: Approved (brainstorming complete, ready for implementation plan)

1. Purpose

A dataset-oriented image loader node: point it at a folder, pick an index (fixed or auto-advancing), and it outputs the image, its sidecar caption text, an alpha mask, the file stem, and the resolved index. Designed for sequential one-image-per-run dataset processing (inpaint/sort pipelines) where you want to walk a folder and stop cleanly when exhausted.

Second node in the ComfyUI-Datasete-Gates package (alongside Image Pool (Grid)).

2. IO

dir name type notes
widget folder STRING any absolute path
widget index INT (control_after_generate) fixed / increment / decrement after each run; min 0
widget depth INT, default 0 0 = top-level only; N = recurse up to N levels; -1 = unlimited
out image IMAGE [1,H,W,3] float 0..1
out text STRING sidecar <stem>.txt content (UTF-8, trailing newline stripped); "" if absent
out mask MASK from alpha channel (1 - alpha, the LoadImage convention); zeros sized to image if no alpha
out filename STRING the file stem (no extension, no dir)
out index INT the resolved index actually loaded

3. Behavior

  • Scan: walk folder depth-limited, keep files whose suffix is in {.png, .jpg, .jpeg, .webp, .bmp, .tif, .tiff}, natural-sort by path relative to the folder (so img2.png < img10.png) → a deterministic list.
  • Index control: native control_after_generate gives fixed/increment/decrement. Increment past the last image walks off the end → error (the intended end-of-batch stop signal). min=0 means decrement floors at the first image.
  • Out of range / empty / bad path → raise a clear error:
    • index N out of range: M images in <folder>
    • No images found in <folder> / Not a folder: <folder>
  • Sidecar: <same-stem>.txt next to the image; UTF-8, rstrip("\n"); missing → "".
  • IS_CHANGED: hash (folder, depth, resolved index, image mtime, sidecar mtime) so fixed-mode file edits re-trigger. (Increment mode re-runs anyway — the widget value changes each run.)

4. Code shape

Kept self-contained so it can be built independently of the in-flight pool work.

  • gates/scan.py — pure, stdlib-only, unit-testable: natural_key, list_images, resolve_index, sidecar_path, read_sidecar, stem.
  • gates/loader.py — the FolderImageLoader node (torch/PIL); contains its own load_image_and_mask(path) (RGB + alpha→mask). ~10 lines overlap with the pool's imaging.py; deliberate, to decouple the two workstreams. Optional post-merge dedupe.
  • Shared file: root __init__.py — the only place both nodes meet. The plan extends the existing if __package__: block to also import + merge the loader's mappings (does not overwrite).

5. Edge cases

  • Non-existent / non-dir path → NotADirectoryError with the path.
  • Folder with no matching images → FileNotFoundError.
  • Image without alpha → zero mask sized to the image (not 64×64).
  • Symlinks/hidden files: included if extension matches (no special handling v1).
  • Huge folders: os.walk + one sort per run is fine for thousands of files.

6. Testing

  • pytest (tests/test_scan.py): natural sort, depth limiting (0 / N / -1), extension filter, resolve_index raises on OOB and empty, sidecar present/missing, stem.
  • pytest (tests/test_loader.py): run() against a tmp folder of real PNGs (with and without alpha + sidecars) — output tensor shapes, text, mask polarity, stem, resolved index; OOB raises; IS_CHANGED differs across index and sidecar mtime.
  • Manual: drop the node in ComfyUI, point at a real dataset folder, increment through it, confirm caption text + mask, and confirm it errors at the end.