SaveImageOpenEXR¶
Documentation¶
- Class name:
SaveImageOpenEXR
- Category:
Marigold
- Output node:
True
This node specializes in saving images in the OpenEXR format, catering to the need for high dynamic range (HDR) imaging. It ensures that images are saved with enhanced luminance levels, accommodating a broader spectrum of light and color details. The node is designed to adapt to the system's available resources, ensuring that images are preserved in the desired format with high fidelity.
Input types¶
Required¶
images
- The images to be saved, expected to be in a NumPy array format. This input is crucial as it directly represents the data that will be processed and saved in the EXR format.
- Comfy dtype:
IMAGE
- Python dtype:
numpy.ndarray
filename_prefix
- A prefix for the filename under which the image will be saved. This allows for customizable naming of output files, facilitating better organization and retrieval of saved images.
- Comfy dtype:
STRING
- Python dtype:
str
Output types¶
file_url
- Comfy dtype:
STRING
- The URL or path to the saved EXR file. This output provides a direct link to the saved image, enabling easy access and integration into further processing or storage solutions.
- Python dtype:
str
- Comfy dtype:
Usage tips¶
- Infra type:
CPU
- Common nodes: unknown
Source code¶
class SaveImageOpenEXR:
def __init__(self):
try:
import OpenEXR
import Imath
self.OpenEXR = OpenEXR
self.Imath = Imath
self.use_openexr = True
except ImportError:
print("No OpenEXR module found, trying OpenCV...")
self.use_openexr = False
try:
os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1"
import cv2
self.cv2 = cv2
except ImportError:
raise ImportError("No OpenEXR or OpenCV module found, can't save EXR")
self.output_dir = folder_paths.get_output_directory()
self.type = "output"
self.prefix_append = ""
@classmethod
def INPUT_TYPES(s):
return {"required": {
"images": ("IMAGE", ),
"filename_prefix": ("STRING", {"default": "ComfyUI_EXR"})
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES =("file_url",)
FUNCTION = "saveexr"
OUTPUT_NODE = True
CATEGORY = "Marigold"
def saveexr(self, images, filename_prefix):
import re
filename_prefix += self.prefix_append
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])
results = list()
def file_counter():
max_counter = 0
# Loop through the existing files
for existing_file in os.listdir(full_output_folder):
# Check if the file matches the expected format
match = re.fullmatch(f"{filename}_(\d+)_?\.[a-zA-Z0-9]+", existing_file)
if match:
# Extract the numeric portion of the filename
file_counter = int(match.group(1))
# Update the maximum counter value if necessary
if file_counter > max_counter:
max_counter = file_counter
return max_counter
for image in images:
# Ensure the tensor is on the CPU and convert it to a numpy array
image_np = image.cpu().numpy()
image_np = image_np.astype(np.float32)
if self.use_openexr:
# Assuming the image is in the format of floating point 32 bit (change PIXEL_TYPE if not)
PIXEL_TYPE = self.Imath.PixelType(self.Imath.PixelType.FLOAT)
height, width, channels = image_np.shape
# Prepare the EXR header
header = self.OpenEXR.Header(width, height)
half_chan = self.Imath.Channel(PIXEL_TYPE)
header['channels'] = dict([(c, half_chan) for c in "RGB"])
# Split the channels for OpenEXR
R = image_np[:, :, 0].tostring()
G = image_np[:, :, 1].tostring()
B = image_np[:, :, 2].tostring()
# Increment the counter by 1 to get the next available value
counter = file_counter() + 1
file = f"{filename}_{counter:05}.exr"
# Write the EXR file
exr_file = self.OpenEXR.OutputFile(os.path.join(full_output_folder, file), header)
exr_file.writePixels({'R': R, 'G': G, 'B': B})
exr_file.close()
else:
counter = file_counter() + 1
file = f"{filename}_{counter:05}.exr"
exr_file = os.path.join(full_output_folder, file)
self.cv2.imwrite(exr_file, image_np)
return (f"/view?filename={file}&subfolder=&type=output",)