170 lines
6.1 KiB
Python
170 lines
6.1 KiB
Python
import os
|
||
from PIL import Image
|
||
import torch
|
||
import numpy as np
|
||
|
||
|
||
def get_first_image(img):
|
||
# 递归取到最内层的非list对象,遇到空list返回None
|
||
while isinstance(img, list):
|
||
if not img: # 空list
|
||
return None
|
||
img = img[0]
|
||
return img
|
||
|
||
class SaveImageWithName:
|
||
@classmethod
|
||
def INPUT_TYPES(s):
|
||
return {
|
||
"required": {
|
||
"image": ("IMAGE",),
|
||
"filename": ("STRING",),
|
||
"filename_suffix": ("STRING", {
|
||
"default": "",
|
||
"multiline": False
|
||
}),
|
||
"save_path": ("STRING", {
|
||
"default": "",
|
||
"multiline": False
|
||
}),
|
||
}
|
||
}
|
||
|
||
RETURN_TYPES = ()
|
||
RETURN_NAMES = ()
|
||
FUNCTION = "save_image"
|
||
CATEGORY = "Image/Save"
|
||
OUTPUT_NODE = True
|
||
|
||
def tensor_to_pil(self, image_tensor):
|
||
if isinstance(image_tensor, torch.Tensor):
|
||
# 确保张量在CPU上
|
||
image = image_tensor.cpu().numpy()
|
||
# 如果是批处理格式,取第一张图片
|
||
if len(image.shape) == 4:
|
||
image = image[0]
|
||
# 调整通道顺序
|
||
if image.shape[0] == 3:
|
||
image = np.transpose(image, (1, 2, 0))
|
||
# 转换为uint8格式
|
||
image = (image * 255).clip(0, 255).astype(np.uint8)
|
||
pil_image = Image.fromarray(image)
|
||
|
||
# 处理EXIF方向信息,防止图片旋转
|
||
try:
|
||
# 获取EXIF信息
|
||
exif = pil_image.getexif()
|
||
if exif:
|
||
# 获取方向信息
|
||
orientation = exif.get(274) # 274是方向标签的ID
|
||
if orientation:
|
||
# 根据方向信息旋转图片
|
||
rotation_values = {
|
||
3: 180,
|
||
6: 270,
|
||
8: 90
|
||
}
|
||
if orientation in rotation_values:
|
||
pil_image = pil_image.rotate(rotation_values[orientation], expand=True)
|
||
# 清除EXIF方向信息,防止重复旋转
|
||
exif[274] = 1 # 设置为正常方向
|
||
pil_image.info['exif'] = exif.tobytes()
|
||
except Exception as e:
|
||
print(f"[DaShuai] EXIF处理警告: {str(e)}")
|
||
|
||
return pil_image
|
||
return image_tensor
|
||
|
||
def get_unique_filename(self, base_path, filename):
|
||
"""生成唯一的文件名,避免覆盖"""
|
||
name, ext = os.path.splitext(filename)
|
||
counter = 1
|
||
new_filename = filename
|
||
|
||
while os.path.exists(os.path.join(base_path, new_filename)):
|
||
new_filename = f"{name}_{counter}{ext}"
|
||
counter += 1
|
||
|
||
return new_filename
|
||
|
||
def process_filename(self, filename, suffix):
|
||
"""处理文件名,将后缀拼接到合适的位置"""
|
||
if not suffix:
|
||
return filename
|
||
|
||
# 检查文件名中是否有'.'符号
|
||
if '.' in filename:
|
||
# 有'.'符号,拼接到'.'前面
|
||
name, ext = os.path.splitext(filename)
|
||
return f"{name}{suffix}{ext}"
|
||
else:
|
||
# 没有'.'符号,直接拼接到文件名后面
|
||
return f"{filename}{suffix}"
|
||
|
||
def save_image(self, image, filename, save_path, filename_suffix):
|
||
try:
|
||
os.makedirs(save_path, exist_ok=True)
|
||
|
||
# 强制转换为list处理
|
||
if isinstance(image, list):
|
||
if isinstance(filename, str):
|
||
# 自动扩展文件名
|
||
name, ext = os.path.splitext(filename)
|
||
ext = ext if ext else ".png"
|
||
filename = [f"{name}({i+1}){ext}" for i in range(len(image))]
|
||
|
||
# 统一list对list处理
|
||
for img, name in zip(image, filename):
|
||
img = get_first_image(img)
|
||
if img is None:
|
||
continue
|
||
if not isinstance(name, str):
|
||
name = str(name)
|
||
if not name.lower().endswith((".png", ".jpg", ".jpeg", ".bmp")):
|
||
name += ".png"
|
||
|
||
# 处理拼接文件名
|
||
processed_name = self.process_filename(name, filename_suffix)
|
||
|
||
# 检查重名并生成唯一文件名
|
||
unique_name = self.get_unique_filename(save_path, processed_name)
|
||
full_path = os.path.join(save_path, unique_name)
|
||
|
||
img_pil = self.tensor_to_pil(img)
|
||
img_pil.save(full_path)
|
||
print(f"[DaShuai] 图片已保存: {full_path}")
|
||
else:
|
||
# 单张图片处理
|
||
img = get_first_image(image)
|
||
if img is None:
|
||
print("[DaShuai] 跳过空图片")
|
||
return ()
|
||
if not isinstance(filename, str):
|
||
filename = str(filename)
|
||
if not filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp')):
|
||
filename += '.png'
|
||
|
||
# 处理拼接文件名
|
||
processed_name = self.process_filename(filename, filename_suffix)
|
||
|
||
# 检查重名并生成唯一文件名
|
||
unique_name = self.get_unique_filename(save_path, processed_name)
|
||
full_path = os.path.join(save_path, unique_name)
|
||
|
||
img_pil = self.tensor_to_pil(img)
|
||
img_pil.save(full_path)
|
||
print(f"[DaShuai] 图片已保存: {full_path}")
|
||
return ()
|
||
except Exception as e:
|
||
print(f"[DaShuai] 保存图片失败: {str(e)}")
|
||
raise e
|
||
|
||
# 节点映射
|
||
NODE_CLASS_MAPPINGS = {
|
||
"SaveImageWithName": SaveImageWithName
|
||
}
|
||
|
||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||
"SaveImageWithName": "SaveImageWithName☀"
|
||
}
|