mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-01-11 06:40:48 +08:00
Fix misalignment between EXIF tags and whatever pillow was actually doing
This commit is contained in:
parent
b9368317af
commit
c48163e78c
@ -83,6 +83,14 @@ class FsSpecComfyMetadata(TypedDict, total=True):
|
|||||||
batch_number_str: str
|
batch_number_str: str
|
||||||
|
|
||||||
|
|
||||||
|
# for keys that are missing
|
||||||
|
_PNGINFO_TO_EXIF_KEY_MAP = {
|
||||||
|
"CreationDate": "DateTimeOriginal",
|
||||||
|
"Title": "DocumentName",
|
||||||
|
"Description": "ImageDescription",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class SaveNodeResultWithName(SaveNodeResult):
|
class SaveNodeResultWithName(SaveNodeResult):
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
@ -121,11 +129,13 @@ def create_exif_from_pnginfo(metadata: Dict[str, Any]) -> Exif:
|
|||||||
if key.startswith('GPS'):
|
if key.startswith('GPS'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
exif_key = _PNGINFO_TO_EXIF_KEY_MAP.get(key, key)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tag = getattr(ExifTags.Base, key)
|
tag = getattr(ExifTags.Base, exif_key)
|
||||||
exif[tag] = value
|
exif[tag] = value
|
||||||
except AttributeError:
|
except (AttributeError, ValueError):
|
||||||
pass
|
continue
|
||||||
|
|
||||||
return exif
|
return exif
|
||||||
|
|
||||||
|
|||||||
@ -470,11 +470,12 @@ def test_basic_exif(format, bits, supports_16bit, use_temporary_output_directory
|
|||||||
node = SaveImagesResponse()
|
node = SaveImagesResponse()
|
||||||
filename = f"test_exif_{bits}bit.{format}"
|
filename = f"test_exif_{bits}bit.{format}"
|
||||||
|
|
||||||
# Create EXIF data with common tags
|
# Create EXIF data with common tags, including Title and Description to test mapping
|
||||||
exif = ExifContainer({
|
exif = ExifContainer({
|
||||||
"Artist": "Test Artist",
|
"Artist": "Test Artist",
|
||||||
"Copyright": "Test Copyright",
|
"Copyright": "Test Copyright",
|
||||||
"ImageDescription": "Test Description",
|
"Title": "Test Title",
|
||||||
|
"Description": "Test Description",
|
||||||
"Make": "Test Camera",
|
"Make": "Test Camera",
|
||||||
"Model": "Test Model",
|
"Model": "Test Model",
|
||||||
"Software": "Test Software",
|
"Software": "Test Software",
|
||||||
@ -509,7 +510,8 @@ def test_basic_exif(format, bits, supports_16bit, use_temporary_output_directory
|
|||||||
info = img.info
|
info = img.info
|
||||||
assert info.get("Artist") == "Test Artist"
|
assert info.get("Artist") == "Test Artist"
|
||||||
assert info.get("Copyright") == "Test Copyright"
|
assert info.get("Copyright") == "Test Copyright"
|
||||||
assert info.get("ImageDescription") == "Test Description"
|
assert info.get("Title") == "Test Title"
|
||||||
|
assert info.get("Description") == "Test Description"
|
||||||
assert info.get("Make") == "Test Camera"
|
assert info.get("Make") == "Test Camera"
|
||||||
assert info.get("Model") == "Test Model"
|
assert info.get("Model") == "Test Model"
|
||||||
assert info.get("Software") == "Test Software"
|
assert info.get("Software") == "Test Software"
|
||||||
@ -521,7 +523,10 @@ def test_basic_exif(format, bits, supports_16bit, use_temporary_output_directory
|
|||||||
checked_tags = {
|
checked_tags = {
|
||||||
"Artist": "Test Artist",
|
"Artist": "Test Artist",
|
||||||
"Copyright": "Test Copyright",
|
"Copyright": "Test Copyright",
|
||||||
"ImageDescription": "Test Description",
|
# For formats that use full EXIF (like 16-bit PNG, JPEG, TIFF),
|
||||||
|
# check that semantic names are mapped to standard EXIF tags.
|
||||||
|
"DocumentName": "Test Title", # Mapped from "Title"
|
||||||
|
"ImageDescription": "Test Description", # Mapped from "Description"
|
||||||
"Make": "Test Camera",
|
"Make": "Test Camera",
|
||||||
"Model": "Test Model",
|
"Model": "Test Model",
|
||||||
"Software": "Test Software",
|
"Software": "Test Software",
|
||||||
@ -576,11 +581,17 @@ def test_gps_exif(format, use_temporary_output_directory):
|
|||||||
assert float(gps_info.get(ExifTags.GPS.GPSAltitude, "0")) == pytest.approx(43.2, rel=0.1)
|
assert float(gps_info.get(ExifTags.GPS.GPSAltitude, "0")) == pytest.approx(43.2, rel=0.1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("format", ["png", "tiff", "jpeg", "webp"])
|
@pytest.mark.parametrize("format,bits", [
|
||||||
def test_datetime_exif(format, use_temporary_output_directory):
|
("png", 8),
|
||||||
|
("png", 16),
|
||||||
|
("tiff", 8),
|
||||||
|
("jpeg", 8),
|
||||||
|
("webp", 8),
|
||||||
|
])
|
||||||
|
def test_datetime_exif(format, bits, use_temporary_output_directory):
|
||||||
"""Test DateTime EXIF tags are correctly saved and loaded"""
|
"""Test DateTime EXIF tags are correctly saved and loaded"""
|
||||||
node = SaveImagesResponse()
|
node = SaveImagesResponse()
|
||||||
filename = f"test_datetime.{format}"
|
filename = f"test_datetime_{bits}bit.{format}"
|
||||||
|
|
||||||
# Fixed datetime string in EXIF format
|
# Fixed datetime string in EXIF format
|
||||||
now = "2024:01:14 12:34:56"
|
now = "2024:01:14 12:34:56"
|
||||||
@ -588,7 +599,7 @@ def test_datetime_exif(format, use_temporary_output_directory):
|
|||||||
# Create EXIF data with datetime tags
|
# Create EXIF data with datetime tags
|
||||||
exif = ExifContainer({
|
exif = ExifContainer({
|
||||||
"DateTime": now,
|
"DateTime": now,
|
||||||
"DateTimeOriginal": now,
|
"CreationDate": now, # This should be mapped to DateTimeOriginal for EXIF formats
|
||||||
"DateTimeDigitized": now,
|
"DateTimeDigitized": now,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -597,24 +608,30 @@ def test_datetime_exif(format, use_temporary_output_directory):
|
|||||||
images=_image_1x1,
|
images=_image_1x1,
|
||||||
uris=[filename],
|
uris=[filename],
|
||||||
exif=[exif],
|
exif=[exif],
|
||||||
pil_save_format=format
|
pil_save_format=format,
|
||||||
|
bits=bits
|
||||||
)
|
)
|
||||||
|
|
||||||
# Load and verify datetime EXIF data
|
# Load and verify datetime EXIF data
|
||||||
filepath = os.path.join(folder_paths.get_output_directory(), filename)
|
filepath = os.path.join(folder_paths.get_output_directory(), filename)
|
||||||
with Image.open(filepath) as img:
|
with Image.open(filepath) as img:
|
||||||
if format == "png":
|
if format == "png" and bits == 8:
|
||||||
|
# For 8-bit PNG, keys are saved as-is in text chunks
|
||||||
assert img.info["DateTime"] == now
|
assert img.info["DateTime"] == now
|
||||||
|
assert img.info["CreationDate"] == now
|
||||||
|
assert img.info["DateTimeDigitized"] == now
|
||||||
else:
|
else:
|
||||||
exif_data = img.getexif()
|
exif_data = img.getexif()
|
||||||
|
assert exif_data is not None, f"EXIF data is missing for {format} {bits}-bit."
|
||||||
|
# For EXIF formats (including 16-bit PNG), CreationDate is mapped to DateTimeOriginal
|
||||||
for tag_name in ["DateTime", "DateTimeOriginal", "DateTimeDigitized"]:
|
for tag_name in ["DateTime", "DateTimeOriginal", "DateTimeDigitized"]:
|
||||||
tag_id = None
|
tag_id = None
|
||||||
for key, name in ExifTags.TAGS.items():
|
for key, name in ExifTags.TAGS.items():
|
||||||
if name == tag_name:
|
if name == tag_name:
|
||||||
tag_id = key
|
tag_id = key
|
||||||
break
|
break
|
||||||
assert tag_id is not None
|
assert tag_id is not None, f"Tag name '{tag_name}' is not a valid EXIF tag."
|
||||||
if tag_id in exif_data:
|
assert tag_id in exif_data, f"Tag '{tag_name}' not found in EXIF for {format} {bits}-bit"
|
||||||
assert exif_data[tag_id] == now
|
assert exif_data[tag_id] == now
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user