Files
2024-03-07 18:59:10 -05:00

130 lines
4.0 KiB
Python

import torch
from PIL import Image
import numpy as np
from typing import List, Union
def tensor2np(tensor: torch.Tensor) -> List[np.ndarray]:
batch_count = tensor.size(0) if len(tensor.shape) > 3 else 1
if batch_count > 1:
out = []
for i in range(batch_count):
out.extend(tensor2np(tensor[i]))
return out
return [np.clip(255.0 * tensor.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)]
def pil2tensor(image: Union[Image.Image, List[Image.Image]]) -> torch.Tensor:
if isinstance(image, list):
return torch.cat([pil2tensor(img) for img in image], dim=0)
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
class ColorListMaskToImage:
"""Converts a mask (alpha) to an RGB image with a color and background"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"mask": ("MASK",),
"colorlist": ("STR",),
"background": ("COLOR", {"default": "#000000"}),
}
}
CATEGORY = "meshmesh"
RETURN_TYPES = ("IMAGE",)
FUNCTION = "render_mask"
OUTPUT_NODE = True
idx = 0
def select_next_color(self, colorlist):
print('colorlist', colorlist)
colors = colorlist["string"].split(",")
print('colors', colors)
color = colors[self.idx]
if self.idx == len(colors) - 1:
self.idx = 0
else:
self.idx += 1
return color
def render_mask(self, mask, colorlist, background):
masks = tensor2np(mask)
images = []
print('colorlist', colorlist)
idx = 0
for m in masks:
_mask = Image.fromarray(m).convert("L")
color = self.select_next_color(colorlist)
print('selected color', color)
image = Image.new("RGBA", _mask.size, color=color)
# apply the mask
image = Image.composite(
image, Image.new("RGBA", _mask.size, color=background), _mask
)
images.append(image.convert("RGB"))
return (pil2tensor(images),)
class FlattenAndCombineMaskImages:
"""Flattens/combines mask images from ColorListMaskToImage"""
def __init__(self):
self.images = []
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"count": ("INT", {"default": 0, "min": 0, "max": 100, "step": 1})
}
}
CATEGORY = "meshmesh"
RETURN_TYPES = ("IMAGE",)
FUNCTION = "run"
OUTPUT_NODE = True
def alpha_composite(self,bottom, top):
# Assumes both tensors are of shape [C, H, W] with C=4 (RGBA)
alpha_top = top[3:4]
alpha_bottom = bottom[3:4]
composite = (alpha_top * top[:3] + alpha_bottom * (1 - alpha_top) * bottom[:3]) / (alpha_top + alpha_bottom * (1 - alpha_top))
composite_alpha = alpha_top + alpha_bottom * (1 - alpha_top)
return torch.cat((composite, composite_alpha), dim=0)
def run(self, image, count):
self.images.append(image)
# Check if we've received all images
if len(self.images) == count: # All images received
composite = self.images[0].squeeze(0) # Remove the batch dimension
for _image in self.images[1:]:
_image_squeezed = _image.squeeze(0) # Remove the batch dimension
composite = self.alpha_composite(composite, _image_squeezed)
# Reset images for the next batch
self.images = []
# Return the composite image
return (composite,)
else:
# Return None or appropriate signal to indicate waiting for more images
pass
NODE_CLASS_MAPPINGS = {
"ColorListMaskToImage": ColorListMaskToImage,
"FlattenAndCombineMaskImages": FlattenAndCombineMaskImages
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ColorListMaskToImage": "ColorListMaskToImage",
"FlattenAndCombineMaskImages": "FlattenAndCombineMaskImages"
}