diff --git a/Loader.py b/Loader.py index aeb9d7b..141c997 100644 --- a/Loader.py +++ b/Loader.py @@ -2,6 +2,7 @@ import json import os import folder_paths +import nodes class Loader: @@ -31,6 +32,15 @@ class Loader: if not os.path.exists(fonts_folder_path): os.mkdir(fonts_folder_path) + def setup_override(self): + if self.config()["override"]["postprocessing"]: + nodes.NODE_CLASS_MAPPINGS = dict( + filter( + lambda item: not item[1].CATEGORY.startswith("image/postprocessing"), + nodes.NODE_CLASS_MAPPINGS.items() + ) + ) + def get_modules(self): modules = dict() @@ -54,6 +64,10 @@ class Loader: from .modules import ImageDraw modules.update(ImageDraw.NODE_CLASS_MAPPINGS) + if self.config()["modules"]["ImageFilter"]: + from .modules import ImageFilter + modules.update(ImageFilter.NODE_CLASS_MAPPINGS) + if self.config()["modules"]["ImageSegmentation"]: from .modules import ImageSegmentation modules.update(ImageSegmentation.NODE_CLASS_MAPPINGS) diff --git a/README.MD b/README.MD index 825f915..36821f2 100644 --- a/README.MD +++ b/README.MD @@ -479,6 +479,136 @@ You also can change the fonts folder in config. +--- +### Image Filter +![image_filter](images/image_filter.png) +> Apply filter on images. + +
+ Nodes: + +`Warning` Some changes are difficult to notice without detailed consideration.You can load this [JSON](images/image_filter.json) and look at the changes in detail. + +### Smooth +![image_draw_polygon](images/image_filter_smooth.png) +> Anti-Aliasing of image artifacts. + +### SmoothMore +![image_filter_smooth_more](images/image_filter_smooth_more.png) +> Improved Anti-Aliasing of image artifacts. + +### Blur +![image_filter_blur](images/image_filter_blur.png) +> Blurring the image. + +### BoxBlur +![image_filter_box_blur](images/image_filter_box_blur.png) +> Blurring the image by box algorithm. + +
+ Params: + +* radius `[0 - *]` - blurring radius. + +
+ +### GaussianBlur +![image_filter_gaussian_blur](images/image_filter_gaussian_blur.png) +> Blurring the image by gaussian algorithm. + +
+ Params: + +* radius `[0 - *]` - blurring radius. + +
+ +### Contour +![image_filter_contour](images/image_filter_contour.png) +> Find the contours of the image. + +### Detail +![image_filter_detail](images/image_filter_detail.png) +> Image detailing. + +### EdgeEnhance +![image_filter_edge_enhance](images/image_filter_edge_enhance.png) +> Clarity of the image boundaries. + +### EdgeEnhanceMore +![image_filter_edge_enhance_more](images/image_filter_edge_enhance_more.png) +> Improved clarity of image boundaries. + +### Emboss +![image_filter_emboss](images/image_filter_emboss.png) +> Embossed image. + +### FindEdges +![image_filter_find_edges](images/image_filter_find_edges.png) +> Identifies the boundaries of the image. + +### Sharpen +![image_filter_sharpen](images/image_filter_sharpen.png) +> Sharpness improvement. + +### Rank +![image_filter_rank](images/image_filter_rank.png) +> Rank filter sorts all pixels in a window of a given size. + +
+ Params: + +* size `[0 - *]` - The size of the core in PX. +* rank `[0 - *]` - Which pixel value to choose. + +
+ +### Median +![image_filter_median](images/image_filter_median.png) +> Selects the average pixel value in a window of the specified size. + +
+ Params: + +* size `[0 - *]` - The size of the core in PX. + +
+ +### Min +![image_filter_min](images/image_filter_min.png) +> Selects the smallest pixel value in the window at the specified size. + +
+ Params: + +* size `[0 - *]` - The size of the core in PX. + +
+ +### Max +![image_filter_max](images/image_filter_max.png) +> Selects the largest pixel value in the window at the specified size. + +
+ Params: + +* size `[0 - *]` - The size of the core in PX. + +
+ +### Mode +![image_filter_mode](images/image_filter_mode.png) +> Selects the most common pixel value in a field of the specified size. + +
+ Params: + +* size `[0 - *]` - The size of the core in PX. + +
+ +
+ --- ### Clamp ![clamp](images/clamp.png) @@ -507,8 +637,9 @@ You also can change the fonts folder in config. ## Configuration: You can configurate Allor with `config.json`. -* `fonts_folder_path` - Array with a relative path to fonts folder, by default is `["comfy_extras", "fonts"]`. (Converted to `ComfyUI/comfy_extras/fonts` for Unix and `ComfyUI\comfy_extras\fonts` for Windows). -* `modules` - You can disable (or enable) modules at will. Nodes from disabled modules will not be loaded during the start of ComfyUI. +* fonts_folder_path - Array with a relative path to fonts folder, by default is `["comfy_extras", "fonts"]`. (Converted to `ComfyUI/comfy_extras/fonts` for Unix and `ComfyUI\comfy_extras\fonts` for Windows). +* modules - You can disable (or enable) modules at will. Nodes from disabled modules will not be loaded during the start of ComfyUI. +* override - If the nested value is set to `true`, similar functionality from the original nodes is disabled. ## Examples: ![example](images/example.png) diff --git a/__init__.py b/__init__.py index 368bdb6..1f4378e 100644 --- a/__init__.py +++ b/__init__.py @@ -5,5 +5,6 @@ loader = Loader() loader.setup_rembg() loader.setup_paths() +loader.setup_override() NODE_CLASS_MAPPINGS = loader.get_modules() diff --git a/config.json b/config.json index 8e92e94..6f319d7 100644 --- a/config.json +++ b/config.json @@ -6,7 +6,11 @@ "ImageComposite": true, "ImageContainer": true, "ImageDraw": true, + "ImageFilter": true, "ImageSegmentation": true, "ImageText": true + }, + "override" : { + "postprocessing": true } } diff --git a/modules/ImageFilter.py b/modules/ImageFilter.py new file mode 100644 index 0000000..49a3446 --- /dev/null +++ b/modules/ImageFilter.py @@ -0,0 +1,406 @@ +import torch +from PIL import ImageFilter + + +def applyImageFilter(images, image_filter): + return (torch.stack([ + images[i].tensor_to_image().filter(image_filter).image_to_tensor() for i in range(len(images)) + ]),) + + +class ImageFilterSmooth: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_smooth" + CATEGORY = "image/filter" + + def image_filter_smooth(self, images): + return applyImageFilter(images, ImageFilter.SMOOTH) + + +class ImageFilterSmoothMore: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_smooth_more" + CATEGORY = "image/filter" + + def image_filter_smooth_more(self, images): + return applyImageFilter(images, ImageFilter.SMOOTH_MORE) + + +class ImageFilterBlur: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_blur" + CATEGORY = "image/filter" + + def image_filter_blur(self, images): + return applyImageFilter(images, ImageFilter.BLUR) + + +class ImageFilterBoxBlur: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "radius": ("INT", { + "default": 1, + "step": 1 + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_box_blur" + CATEGORY = "image/filter" + + def image_filter_box_blur(self, images, radius): + return applyImageFilter(images, ImageFilter.BoxBlur(radius)) + + +class ImageFilterGaussianBlur: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "radius": ("INT", { + "default": 1, + "step": 1 + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_gaussian_blur" + CATEGORY = "image/filter" + + def image_filter_gaussian_blur(self, images, radius): + return applyImageFilter(images, ImageFilter.GaussianBlur(radius)) + + +class ImageFilterContour: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_contour" + CATEGORY = "image/filter" + + def image_filter_contour(self, images): + return applyImageFilter(images, ImageFilter.CONTOUR) + + +class ImageFilterDetail: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_detail" + CATEGORY = "image/filter" + + def image_filter_detail(self, images): + return applyImageFilter(images, ImageFilter.DETAIL) + + +class ImageFilterEdgeEnhance: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_edge_enhance" + CATEGORY = "image/filter" + + def image_filter_edge_enhance(self, images): + return applyImageFilter(images, ImageFilter.EDGE_ENHANCE) + + +class ImageFilterEdgeEnhanceMore: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_edge_enhance_more" + CATEGORY = "image/filter" + + def image_filter_edge_enhance_more(self, images): + return applyImageFilter(images, ImageFilter.EDGE_ENHANCE_MORE) + + +class ImageFilterEmboss: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_emboss" + CATEGORY = "image/filter" + + def image_filter_emboss(self, images): + return applyImageFilter(images, ImageFilter.EMBOSS) + + +class ImageFilterFindEdges: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_find_edges" + CATEGORY = "image/filter" + + def image_filter_find_edges(self, images): + return applyImageFilter(images, ImageFilter.FIND_EDGES) + + +class ImageFilterSharpen: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_sharpen" + CATEGORY = "image/filter" + + def image_filter_sharpen(self, images): + return applyImageFilter(images, ImageFilter.SHARPEN) + + +class ImageFilterRank: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "size": ("INT", { + "default": 2, + "min": 0, + "step": 2 + }), + "rank": ("INT", { + "default": 1, + "step": 1 + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_rank" + CATEGORY = "image/filter" + + def image_filter_rank(self, images, size, rank): + return applyImageFilter(images, ImageFilter.RankFilter(int(size) + 1, rank)) + + +class ImageFilterMedian: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "size": ("INT", { + "default": 2, + "min": 0, + "step": 2 + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_median" + CATEGORY = "image/filter" + + def image_filter_median(self, images, size): + return applyImageFilter(images, ImageFilter.MedianFilter(int(size) + 1)) + + +class ImageFilterMin: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "size": ("INT", { + "default": 2, + "min": 0, + "step": 2 + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_min" + CATEGORY = "image/filter" + + def image_filter_min(self, images, size): + return applyImageFilter(images, ImageFilter.MinFilter(int(size) + 1)) + + +class ImageFilterMax: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "size": ("INT", { + "default": 2, + "min": 0, + "step": 2 + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_max" + CATEGORY = "image/filter" + + def image_filter_max(self, images, size): + return applyImageFilter(images, ImageFilter.MaxFilter(int(size) + 1)) + + +class ImageFilterMode: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "size": ("INT", { + "default": 2, + "min": 0, + "step": 2 + }), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "image_filter_mode" + CATEGORY = "image/filter" + + def image_filter_mode(self, images, size): + return applyImageFilter(images, ImageFilter.ModeFilter(int(size) + 1)) + + +NODE_CLASS_MAPPINGS = { + "ImageFilterSmooth": ImageFilterSmooth, + "ImageFilterSmoothMore": ImageFilterSmoothMore, + "ImageFilterBlur": ImageFilterBlur, + "ImageFilterBoxBlur": ImageFilterBoxBlur, + "ImageFilterGaussianBlur": ImageFilterGaussianBlur, + "ImageFilterContour": ImageFilterContour, + "ImageFilterDetail": ImageFilterDetail, + "ImageFilterEdgeEnhance": ImageFilterEdgeEnhance, + "ImageFilterEdgeEnhanceMore": ImageFilterEdgeEnhanceMore, + "ImageFilterEmboss": ImageFilterEmboss, + "ImageFilterFindEdges": ImageFilterFindEdges, + "ImageFilterSharpen": ImageFilterSharpen, + "ImageFilterRank": ImageFilterRank, + "ImageFilterMedian": ImageFilterMedian, + "ImageFilterMin": ImageFilterMin, + "ImageFilterMax": ImageFilterMax, + "ImageFilterMode": ImageFilterMode +}