Files

165 lines
5.6 KiB
Python

# 文件路径: ComfyUI/custom_nodes/ComfyUI-Snap_Processing/Snap_canvas.py
from PyQt5.QtCore import QEventLoop
from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtGui import QImage
import time
import os
import torch
from PIL import Image
import numpy as np
from io import BytesIO
from .ui.canvas_window import CanvasWindow
class PyQtCanvasNode:
@staticmethod
def INPUT_TYPES():
return {
"required": {
"image": ("IMAGE",), # 必需的图像输入
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
}
}
RETURN_TYPES = ("IMAGE", "INT", "STRING", "INT", "INT", "FLOAT") # IMAGE, seed, path, x, y, scale_factor
RETURN_NAMES = ("image", "seed", "string", "X", "Y", "factor")
FUNCTION = "activate_pyqt"
CATEGORY = "Snap Processing"
def activate_pyqt(self, image, seed):
try:
# 使用用户提供的种子值
print(f"Received seed: {seed}")
# 将张量转换为 QImage
qimage = self.tensor_to_qimage(image)
print("Converted tensor to QImage")
# 运行 PyQt GUI 并阻塞主线程,直到用户完成操作
modified_image, x, y, scale_factor = self.run_pyqt_gui(qimage)
print("PyQt GUI closed")
# 将修改后的 QImage 转换为 NumPy 数组
numpy_array = self.qimage_to_numpy(modified_image)
print("Converted QImage to NumPy array")
save_directory = os.path.join(os.getcwd(), "canvas")
# 如果目录不存在,则创建该目录
if not os.path.exists(save_directory):
os.makedirs(save_directory)
# 使用固定文件名 'output.png'
png_image_filename = "output.png"
png_image_path = os.path.join(save_directory, png_image_filename)
# 保存图像
self.numpy_to_png(numpy_array, png_image_path)
print(f"已将图像保存到 {png_image_path}")
# 返回修改后的图像和其他参数
return (self.qimage_to_tensor(modified_image), seed, png_image_path, x, y, scale_factor)
except Exception as e:
print(f"激活 PyQt 时出错: {e}")
raise e
def run_pyqt_gui(self, input_image):
try:
app = QApplication.instance()
if app is None:
app = QApplication([]) # 创建全局应用程序
# 始终创建一个新的 CanvasWindow 实例
dialog = CanvasWindow(input_image)
dialog.save_signal.connect(self.on_save)
dialog.close_signal.connect(self.on_close)
print("PyQt CanvasWindow shown")
dialog.exec_() # 阻塞,直到对话框关闭
print("Dialog closed")
modified_image = dialog.get_modified_image()
x = dialog.get_top_left_x()
y = dialog.get_top_left_y()
scale_factor = dialog.get_scale_factor()
dialog.deleteLater() # 确保窗口被正确销毁
print("CanvasWindow deleted")
return modified_image, x, y, scale_factor
except Exception as e:
print(f"Error in run_pyqt_gui: {e}")
raise e
def on_save(self):
print("Image saved successfully")
def on_close(self):
print("Window closed")
def tensor_to_qimage(self, tensor):
if tensor.ndim == 4:
tensor = tensor.squeeze(0)
if tensor.ndim != 3:
raise ValueError(f"Expected tensor with 3 dimensions, but got {tensor.ndim} dimensions.")
if tensor.shape[0] in [1, 3, 4]:
pass
elif tensor.shape[2] in [1, 3, 4]:
tensor = tensor.permute(2, 0, 1)
else:
raise ValueError("Tensor has unsupported shape.")
num_channels = tensor.shape[0]
if num_channels == 1:
tensor = tensor.repeat(3, 1, 1)
elif num_channels == 2:
tensor = torch.cat([tensor, tensor[0:1, :, :]], dim=0)
elif num_channels == 4:
tensor = tensor[:3, :, :]
elif num_channels != 3:
raise ValueError(f"Unsupported number of channels: {num_channels}")
image_data = tensor.mul(255).byte().cpu().numpy()
image_data = image_data.transpose(1, 2, 0)
pil_image = Image.fromarray(image_data, 'RGB')
qimage = self.pil_image_to_qimage(pil_image)
return qimage
def qimage_to_tensor(self, qimage):
qimage = qimage.convertToFormat(QImage.Format_RGB888)
width = qimage.width()
height = qimage.height()
ptr = qimage.bits()
ptr.setsize(qimage.byteCount())
image_np = np.array(ptr).reshape((height, width, 3))
tensor = torch.from_numpy(image_np).permute(2, 0, 1).float() / 255.0
return tensor
def qimage_to_numpy(self, qimage):
qimage = qimage.convertToFormat(QImage.Format_RGB888)
width = qimage.width()
height = qimage.height()
ptr = qimage.bits()
ptr.setsize(qimage.byteCount())
image_np = np.array(ptr).reshape((height, width, 3))
return image_np
def numpy_to_png(self, numpy_array, save_path):
if numpy_array.dtype != np.uint8:
numpy_array = (numpy_array * 255).astype(np.uint8)
image = Image.fromarray(numpy_array)
image.save(save_path, format='PNG')
def pil_image_to_qimage(self, pil_image):
buffer = BytesIO()
pil_image.save(buffer, format="PNG")
image_bytes = buffer.getvalue()
qimage = QImage.fromData(image_bytes, "PNG")
return qimage