Files
SS-snap-ComfyUI-Snap_Proces…/Snap_canvas.py
T

159 lines
5.4 KiB
Python

from PyQt5.QtCore import QEventLoop
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QImage
import time
import os
import random
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",), # Required image input
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
}
}
RETURN_TYPES = ("IMAGE", "INT", "STRING") # Added "STRING" to the return types
FUNCTION = "activate_pyqt"
CATEGORY = "Snap Processing"
def activate_pyqt(self, image, seed):
try:
# Use the user-provided seed value
print(f"Received seed: {seed}")
# Convert tensor to QImage
qimage = self.tensor_to_qimage(image)
print("Converted tensor to QImage")
# Run PyQt GUI and block the main thread until the user finishes
modified_image = self.run_pyqt_gui(qimage)
print("PyQt GUI closed")
# Convert the modified QImage to a NumPy array
numpy_array = self.qimage_to_numpy(modified_image)
print("Converted QImage to NumPy array")
# Define the save directory
save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "ComfyUI-Snap_Processing", "save")
# Create a unique file name using the seed and current timestamp
timestamp = int(time.time())
png_image_filename = f"output_image_seed{seed}_{timestamp}.png"
png_image_path = os.path.join(save_directory, png_image_filename)
self.numpy_to_png(numpy_array, png_image_path)
print(f"Saved image to {png_image_path}")
# Get the absolute path for consistency
absolute_png_image_path = os.path.abspath(png_image_path)
# Return the modified image, seed, and the file path
return (self.qimage_to_tensor(modified_image), seed, absolute_png_image_path)
except Exception as e:
print(f"Error in activate_pyqt: {e}")
raise e
def run_pyqt_gui(self, input_image):
try:
app = QApplication.instance()
if app is None:
app = QApplication([]) # Create the global application
# Always create a new CanvasWindow instance
dialog = CanvasWindow(input_image)
dialog.save_signal.connect(self.on_save)
dialog.close_signal.connect(self.on_close)
print("PyQt CanvasWindow shown")
dialog.exec_() # Block until the dialog is closed
print("Dialog closed")
modified_image = dialog.get_modified_image()
dialog.deleteLater() # Ensure the window is properly destroyed
print("CanvasWindow deleted")
return modified_image
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