mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-28 01:47:32 +08:00
Image assets now carry width/height under the existing `metadata` field on
asset responses, shaped as `{"kind": "image", "width": W, "height": H}`.
This lets consumers get original dimensions (e.g. for clients that render
server-side thumbnails and can't recover them from naturalWidth/Height)
without an extra round-trip.
Dimensions are written to AssetReference.system_metadata across three
ingest paths:
- Direct file ingest (upload, in-place registration): Pillow reads the
image header right after hashing, while the file is still in OS page
cache. Non-image MIME types are skipped without touching the file.
- From-hash registration: this path never reads the file bytes, so
dimensions are best-effort copied from any prior sibling reference of
the same asset that already carries kind=image metadata. Missing
siblings, non-image siblings, or absent dimension keys leave the new
reference's metadata unchanged.
- Scanner enrichment: extends the existing system_metadata write in
enrich_asset so scanner-registered images get the same treatment as
uploaded ones.
Existing system_metadata keys (e.g. safetensors fields written by the
enricher, download provenance) are preserved through merge. Existing
assets ingested before this change retain their current metadata — no
automatic backfill in this PR.
Tests cover image emission, non-image no-op, merge preservation, and the
from-hash sibling back-fill (including the no-sibling and non-image-sibling
cases).
64 lines
2.0 KiB
Python
64 lines
2.0 KiB
Python
"""Image dimension extraction for asset ingest.
|
|
|
|
Reads only the image header via Pillow to capture width/height cheaply,
|
|
without a full pixel decode. Returns a metadata dict suitable for merging
|
|
into ``AssetReference.system_metadata``.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def extract_image_dimensions(
|
|
file_path: str, mime_type: str | None = None
|
|
) -> dict[str, Any] | None:
|
|
"""Extract image dimensions for the file at ``file_path``.
|
|
|
|
Args:
|
|
file_path: Absolute path to a file on disk.
|
|
mime_type: Optional MIME type hint. When provided and not prefixed
|
|
with ``image/``, extraction is skipped without touching the file.
|
|
|
|
Returns:
|
|
``{"kind": "image", "width": W, "height": H}`` when the file is a
|
|
recognizable image with positive dimensions, otherwise ``None``.
|
|
|
|
The dict shape is intended to be merged into ``system_metadata`` so the
|
|
asset response surfaces ``metadata.kind`` plus dimension fields for image
|
|
assets. Forward-compatible: future media kinds (e.g. ``"video"`` with
|
|
duration/fps) can extend this shape without schema changes.
|
|
"""
|
|
if mime_type is not None and not mime_type.startswith("image/"):
|
|
return None
|
|
|
|
try:
|
|
from PIL import Image, UnidentifiedImageError
|
|
except ImportError:
|
|
logger.debug(
|
|
"Pillow not available; skipping image dimension extraction for %s",
|
|
file_path,
|
|
)
|
|
return None
|
|
|
|
try:
|
|
with Image.open(file_path) as img:
|
|
width, height = img.size
|
|
except (OSError, UnidentifiedImageError, ValueError) as exc:
|
|
logger.debug(
|
|
"Failed to read image dimensions from %s: %s", file_path, exc
|
|
)
|
|
return None
|
|
|
|
if (
|
|
not isinstance(width, int)
|
|
or not isinstance(height, int)
|
|
or width <= 0
|
|
or height <= 0
|
|
):
|
|
return None
|
|
|
|
return {"kind": "image", "width": width, "height": height}
|