pumpkinbbyx commited on
Commit
c9d8571
·
verified ·
1 Parent(s): 7d6b767

Upload 3 files

Browse files
ComfyUI-Metadata-Saver/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .metadata_saver import SaveImageCustomMetadata
2
+
3
+ NODE_CLASS_MAPPINGS = {
4
+ "SaveImageCustomMetadata": SaveImageCustomMetadata
5
+ }
6
+
7
+ NODE_DISPLAY_NAME_MAPPINGS = {
8
+ "SaveImageCustomMetadata": "Save Image (Custom Metadata)"
9
+ }
10
+
11
+ __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
ComfyUI-Metadata-Saver/metadata_saver.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import numpy as np
4
+ from PIL import Image, ExifTags
5
+ from PIL.PngImagePlugin import PngInfo
6
+ import folder_paths
7
+
8
+ class SaveImageCustomMetadata:
9
+ def __init__(self):
10
+ self.output_dir = folder_paths.get_output_directory()
11
+ self.type = "output"
12
+ self.prefix_append = ""
13
+ self.compress_level = 4
14
+
15
+ @classmethod
16
+ def INPUT_TYPES(s):
17
+ return {
18
+ "required": {
19
+ "images": ("IMAGE", ),
20
+ "filename_prefix": ("STRING", {"default": "ComfyUI"}),
21
+ "strip_workflow": ("BOOLEAN", {"default": True, "label": "Remove Original Workflow"}),
22
+
23
+ # PNG Text Info
24
+ "custom_author": ("STRING", {"default": "Me", "multiline": False}),
25
+ "copyright_info": ("STRING", {"default": "Copyright 2024", "multiline": False}),
26
+
27
+ # EXIF Data (Camera & GPS)
28
+ "camera_make": ("STRING", {"default": "Apple"}),
29
+ "camera_model": ("STRING", {"default": "iPhone 16 Pro Max"}),
30
+
31
+ # Default: Miami, FL (25.7617, -80.1918)
32
+ "latitude": ("FLOAT", {"default": 25.7617, "min": -90.0, "max": 90.0, "step": 0.0001}),
33
+ "longitude": ("FLOAT", {"default": -80.1918, "min": -180.0, "max": 180.0, "step": 0.0001}),
34
+ },
35
+ "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
36
+ }
37
+
38
+ RETURN_TYPES = ()
39
+ FUNCTION = "save_images"
40
+ OUTPUT_NODE = True
41
+ CATEGORY = "image"
42
+
43
+ def _convert_to_degrees(self, value):
44
+ """
45
+ Helper to convert decimal coordinates to EXIF (Degrees, Minutes, Seconds) format.
46
+ EXIF expects rational numbers (numerator, denominator).
47
+ """
48
+ d = int(value)
49
+ m = int((value - d) * 60)
50
+ s = (value - d - m/60) * 3600.00
51
+ # precision of 100 for seconds
52
+ return ((d, 1), (m, 1), (int(s * 100), 100))
53
+
54
+ def save_images(self, images, filename_prefix="ComfyUI", strip_workflow=True,
55
+ custom_author="", copyright_info="",
56
+ camera_make="", camera_model="", latitude=0.0, longitude=0.0,
57
+ prompt=None, extra_pnginfo=None):
58
+
59
+ filename_prefix += self.prefix_append
60
+ full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
61
+
62
+ results = list()
63
+
64
+ # EXIF Standard Tag IDs
65
+ TAG_MAKE = 271
66
+ TAG_MODEL = 272
67
+ TAG_SOFTWARE = 305
68
+ TAG_ARTIST = 315
69
+ TAG_COPYRIGHT = 33432
70
+ TAG_GPS_INFO = 34853
71
+
72
+ # GPS Sub-IFD Tags
73
+ GPS_LAT_REF = 1
74
+ GPS_LAT = 2
75
+ GPS_LONG_REF = 3
76
+ GPS_LONG = 4
77
+
78
+ for image in images:
79
+ i = 255. * image.cpu().numpy()
80
+ img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
81
+
82
+ # --- 1. Handle PNG Text Metadata (Workflow, Author) ---
83
+ metadata = PngInfo()
84
+ if not strip_workflow:
85
+ if prompt is not None:
86
+ metadata.add_text("prompt", json.dumps(prompt))
87
+ if extra_pnginfo is not None:
88
+ for x in extra_pnginfo:
89
+ metadata.add_text(x, json.dumps(extra_pnginfo[x]))
90
+
91
+ if custom_author:
92
+ metadata.add_text("Author", custom_author)
93
+ if copyright_info:
94
+ metadata.add_text("Copyright", copyright_info)
95
+
96
+ # --- 2. Handle EXIF Data (Camera & GPS) ---
97
+ exif_data = img.getexif()
98
+
99
+ # Set Camera Details
100
+ if camera_make:
101
+ exif_data[TAG_MAKE] = camera_make
102
+ if camera_model:
103
+ exif_data[TAG_MODEL] = camera_model
104
+
105
+ exif_data[TAG_SOFTWARE] = "ComfyUI Custom Node"
106
+ if custom_author:
107
+ exif_data[TAG_ARTIST] = custom_author
108
+ if copyright_info:
109
+ exif_data[TAG_COPYRIGHT] = copyright_info
110
+
111
+ # Set GPS Data
112
+ # We must access the GPS IFD (Sub-dictionary) specifically
113
+ gps_ifd = exif_data.get_ifd(TAG_GPS_INFO)
114
+
115
+ # Latitude
116
+ gps_ifd[GPS_LAT_REF] = 'N' if latitude >= 0 else 'S'
117
+ gps_ifd[GPS_LAT] = self._convert_to_degrees(abs(latitude))
118
+
119
+ # Longitude
120
+ gps_ifd[GPS_LONG_REF] = 'E' if longitude >= 0 else 'W'
121
+ gps_ifd[GPS_LONG] = self._convert_to_degrees(abs(longitude))
122
+
123
+ # Save the image with both PNG Info and EXIF binary data
124
+ file = f"{filename}_{counter:05}_.png"
125
+ img.save(
126
+ os.path.join(full_output_folder, file),
127
+ pnginfo=metadata,
128
+ exif=exif_data,
129
+ compress_level=self.compress_level
130
+ )
131
+
132
+ results.append({
133
+ "filename": file,
134
+ "subfolder": subfolder,
135
+ "type": self.type
136
+ })
137
+ counter += 1
138
+
139
+ return { "ui": { "images": results } }
ComfyUI-Metadata-Saver/📸 ComfyUI Custom Metadata Saver.txt ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 📸 ComfyUI Custom Metadata Saver
2
+ This custom node allows you to save images while stripping the internal ComfyUI workflow data and injecting custom EXIF (Camera & GPS) and Copyright information.
3
+ 📥 Installation
4
+ Navigate to your ComfyUI/custom_nodes/ folder.
5
+ Create a new folder named ComfyUI-Metadata-Saver.
6
+ Create two files inside: __init__.py and metadata_saver.py (paste the provided code).
7
+ Restart ComfyUI.
8
+ ⚙️ How to Use
9
+ Add Node: Double-click the canvas and search for "Save Image (Custom Metadata)".
10
+ Connect: Link your final image output to this node (replace the standard "Save Image" node).
11
+ Configure Settings:
12
+ Setting Description
13
+ strip_workflow True: Removes the ComfyUI node graph/prompt (privacy mode).<br>False: Keeps the workflow hidden in the file.
14
+ filename_prefix Standard file naming (e.g., "MyRender").
15
+ custom_author Sets the "Artist/Author" tag.
16
+ camera_make e.g., Apple, Sony, Canon.
17
+ camera_model e.g., iPhone 16 Pro Max.
18
+ latitude / longitude Sets GPS location.
19
+ 📍 Example Presets (Miami / iPhone)
20
+ To make your render look like a photo taken in Miami on a new iPhone:
21
+ Camera Make: Apple
22
+ Camera Model: iPhone 16 Pro Max
23
+ Latitude: 25.7617
24
+ Longitude: -80.1918
25
+ ⚠️ Note: If you set strip_workflow to True, you will not be able to drag and drop the resulting image back into ComfyUI to load the workflow. The image will be "clean."