Nourepide / ComfyUI-Allor

ComfyUI plugin for image processing and work with alpha chanel.
MIT License
186 stars 22 forks source link

Can't install on M1 Sonoma #8

Closed berrymoor closed 8 months ago

berrymoor commented 8 months ago

Always get "Import Failed" error while starting ComfyUI and no nodes showing up Terminal logs look like this:

  File "/Volumes/SSD/ComfyUI/custom_nodes/comfyui_allor/__init__.py", line 10, in <module>
    NODE_CLASS_MAPPINGS = loader.get_modules()
  File "/Volumes/SSD/ComfyUI/custom_nodes/comfyui_allor/Loader.py", line 84, in get_modules
    from .modules import ImageContainer
  File "/Volumes/SSD/ComfyUI/custom_nodes/comfyui_allor/modules/ImageContainer.py", line 3, in <module>
    from .Utils import create_rgba_image
  File "/Volumes/SSD/ComfyUI/custom_nodes/comfyui_allor/modules/Utils.py", line 123, in <module>
    size: tuple[int] | tuple[int, int],
TypeError: unsupported operand type(s) for |: 'types.GenericAlias' and 'types.GenericAlias'
Nourepide commented 8 months ago

I will try to investigate this problem, but unfortunately I can't promise anything since I don't have a computer running macOS.

What Python version do you use?

berrymoor commented 8 months ago

I will try to investigate this problem, but unfortunately I can't promise anything since I don't have a computer running macOS.

Feel free if you need test something

What Python version do you use?

3.9.6

Nourepide commented 8 months ago

I found out what the problem was, and as unexpected as it may sound, it was indeed in the Python version.

The error you see is related to the use of the ‘|’ operator in Python type annotations. This operator is used for type unions and was added in Python 3.10. It allows you to specify that a variable can be one of several types.

berrymoor commented 8 months ago

Python 3.10. It allows you to specify that a variable can be one of several types.

First of all, thank you for taking the time. I will try to update the python, and I will write the results later

Nourepide commented 8 months ago

You can update your Python version through brew or Anaconda if you’re using it for ComfyUI. If you don’t have the ability to update your Python version, you can replace all the code in the ‘modules/Utils.py’ file with this one as temporally solution.

from typing import Literal

import numpy as np
import torch
import torchvision.transforms as t
from PIL import Image as ImageF
from PIL.Image import Image as ImageB
from torch import Tensor

def tensor_to_image(self):
    return t.ToPILImage()(self.permute(2, 0, 1))

def image_to_tensor(self):
    return t.ToTensor()(self).permute(1, 2, 0)

def create_rgba_image(width: int, height: int, color=(0, 0, 0, 0)) -> ImageB:
    return ImageF.new("RGBA", (width, height), color)

def get_sampler_by_name(method) -> Literal[0, 1, 2, 3, 4, 5]:
    if method == "lanczos":
        return ImageF.LANCZOS
    elif method == "bicubic":
        return ImageF.BICUBIC
    elif method == "hamming":
        return ImageF.HAMMING
    elif method == "bilinear":
        return ImageF.BILINEAR
    elif method == "box":
        return ImageF.BOX
    elif method == "nearest":
        return ImageF.NEAREST
    else:
        raise ValueError("Sampler not found.")

def cv2_layer(tensor: Tensor, function) -> Tensor:
    shape_size = tensor.shape.__len__()

    def produce(image):
        channels = image[0, 0, :].shape[0]

        rgb = image[:, :, 0:3].numpy()
        result_rgb = function(rgb)

        if channels <= 3:
            return torch.from_numpy(result_rgb)
        elif channels == 4:
            alpha = image[:, :, 3:4].numpy()
            result_alpha = function(alpha)[..., np.newaxis]
            result_rgba = np.concatenate((result_rgb, result_alpha), axis=2)

            return torch.from_numpy(result_rgba)

    if shape_size == 3:
        return torch.from_numpy(produce(tensor))
    elif shape_size == 4:
        return torch.stack([
            produce(tensor[i]) for i in range(len(tensor))
        ])
    else:
        raise ValueError("Incompatible tensor dimension.")

def radialspace_1D(size, curvy=1.0, scale=1.0, min_val=0.0, max_val=1.0, normalize=True, dtype=torch.float32):
    tensor = radialspace_2D((1, size), curvy, scale, "square", min_val, max_val, (0.0, 0.5), None, normalize, dtype).squeeze()

    if min_val < 0:
        tensor[:len(tensor) // 2] = -(tensor[:len(tensor) // 2])

    if normalize:
        tensor = ((tensor - tensor.min()) / (tensor.max() - tensor.min())) * (max_val - min_val) + min_val

    return tensor

def radialspace_2D(size, curvy=1.0, scale=1.0, mode="square", min_val=0.0, max_val=1.0, center=(0.5, 0.5), function=None, normalize=True, dtype=torch.float32) -> Tensor:
    if isinstance(size, tuple):
        if len(size) == 1:
            width = height = size[0]
        elif len(size) == 2:
            width, height = size
        else:
            raise ValueError("Invalid size argument")
    else:
        raise TypeError("Size must be a tuple")

    x = torch.linspace(0, 1, width)
    y = torch.linspace(0, 1, height)

    xx, yy = torch.meshgrid(x, y, indexing='ij')

    if function is not None:
        d = function(xx, yy)
    elif mode == "square":
        xx = (torch.abs(xx - center[0]) ** curvy)
        yy = (torch.abs(yy - center[1]) ** curvy)
        d = torch.max(xx, yy)
    elif mode == "circle":
        d = torch.sqrt((xx - center[0]) ** 2 + (yy - center[1]) ** 2)
        d = (d ** curvy)
    elif mode == "rectangle":
        xx = (torch.abs(xx - center[0]) ** curvy)
        yy = (torch.abs(yy - center[1]) ** curvy)
        d = xx + yy
    elif mode == "corners":
        xx = (torch.abs(xx - center[0]) ** curvy)
        yy = (torch.abs(yy - center[1]) ** curvy)
        d = torch.min(xx, yy)
    else:
        raise ValueError("Not supported mode.")

    if normalize:
        d = d / d.max() * scale
        d = torch.clamp(d, min_val, max_val)

    return d.to(dtype)

Tensor.tensor_to_image = tensor_to_image
ImageB.image_to_tensor = image_to_tensor

It should help with this problem. In this case, it would be advisable to add the line ‘modules/Utils.py’ to .gitignore. Otherwise, with the next update, everything will revert back to how it was before.

Nourepide commented 8 months ago

Well, let's assume that the problem was only in the Python version.