33import cv2
44import numpy as np
55from PIL import Image , ImageEnhance
6+ from PIL import Image
67
78
89class ArithmeticBlend :
@@ -149,20 +150,14 @@ def INPUT_TYPES(s):
149150
150151 CATEGORY = "postprocessing"
151152
152- def gaussian_kernel (self , kernel_size : int , sigma : float ):
153- x , y = torch .meshgrid (torch .linspace (- 1 , 1 , kernel_size ), torch .linspace (- 1 , 1 , kernel_size ), indexing = "ij" )
154- d = torch .sqrt (x * x + y * y )
155- g = torch .exp (- (d * d ) / (2.0 * sigma * sigma ))
156- return g / g .sum ()
157-
158153 def blur (self , image : torch .Tensor , blur_radius : int , sigma : float ):
159154 if blur_radius == 0 :
160155 return (image ,)
161156
162157 batch_size , height , width , channels = image .shape
163158
164159 kernel_size = blur_radius * 2 + 1
165- kernel = self . gaussian_kernel (kernel_size , sigma ).repeat (channels , 1 , 1 ).unsqueeze (1 )
160+ kernel = gaussian_kernel (kernel_size , sigma ).repeat (channels , 1 , 1 ).unsqueeze (1 )
166161
167162 image = image .permute (0 , 3 , 1 , 2 ) # Torch wants (B, C, H, W) we use (B, H, W, C)
168163 blurred = F .conv2d (image , kernel , padding = kernel_size // 2 , groups = channels )
@@ -593,17 +588,11 @@ def apply_glow(self, image: torch.Tensor, intensity: float, blur_radius: int):
593588 glowing_image = torch .clamp (glowing_image , 0 , 1 )
594589 return (glowing_image ,)
595590
596- def gaussian_kernel (self , kernel_size : int ):
597- sigma = (kernel_size - 1 ) / 6
598- x , y = torch .meshgrid (torch .linspace (- 1 , 1 , kernel_size ), torch .linspace (- 1 , 1 , kernel_size ))
599- d = torch .sqrt (x * x + y * y )
600- g = torch .exp (- (d * d ) / (2.0 * sigma * sigma ))
601- return g / g .sum ()
602-
603591 def gaussian_blur (self , image : torch .Tensor , kernel_size : int ):
604592 batch_size , height , width , channels = image .shape
605593
606- kernel = self .gaussian_kernel (kernel_size ).repeat (channels , 1 , 1 ).unsqueeze (1 )
594+ sigma = (kernel_size - 1 ) / 6
595+ kernel = gaussian_kernel (kernel_size , sigma ).repeat (channels , 1 , 1 ).unsqueeze (1 )
607596
608597 image = image .permute (0 , 3 , 1 , 2 ) # Torch wants (B, C, H, W) we use (B, H, W, C)
609598 blurred = F .conv2d (image , kernel , padding = kernel_size // 2 , groups = channels )
@@ -614,6 +603,91 @@ def gaussian_blur(self, image: torch.Tensor, kernel_size: int):
614603 def add_glow (self , img , blurred_img , intensity ):
615604 return img + blurred_img * intensity
616605
606+ class PencilSketch :
607+ def __init__ (self ):
608+ pass
609+
610+ @classmethod
611+ def INPUT_TYPES (cls ):
612+ return {
613+ "required" : {
614+ "image" : ("IMAGE" ,),
615+ "blur_radius" : ("INT" , {
616+ "default" : 5 ,
617+ "min" : 1 ,
618+ "max" : 31 ,
619+ "step" : 1
620+ }),
621+ "sharpen_alpha" : ("FLOAT" , {
622+ "default" : 1.0 ,
623+ "min" : 0.0 ,
624+ "max" : 10.0 ,
625+ "step" : 0.1
626+ }),
627+ },
628+ }
629+
630+ RETURN_TYPES = ("IMAGE" ,)
631+ FUNCTION = "apply_sketch"
632+
633+ CATEGORY = "postprocessing"
634+
635+ def apply_sketch (self , image : torch .Tensor , blur_radius : int = 5 , sharpen_alpha : float = 1 ):
636+ image = image .permute (0 , 3 , 1 , 2 ) # Torch wants (B, C, H, W) we use (B, H, W, C)
637+
638+ grayscale = image .mean (dim = 1 , keepdim = True )
639+ grayscale = grayscale .repeat (1 , 3 , 1 , 1 )
640+ inverted = 1 - grayscale
641+
642+ blur_sigma = blur_radius / 3
643+ blurred = self .gaussian_blur (inverted , blur_radius , blur_sigma )
644+
645+ final_image = self .dodge (blurred , grayscale )
646+
647+ if sharpen_alpha != 0.0 :
648+ final_image = self .sharpen (final_image , 1 , sharpen_alpha )
649+
650+ final_image = final_image .permute (0 , 2 , 3 , 1 ) # Back to (B, H, W, C)
651+
652+ return (final_image ,)
653+
654+ def dodge (self , front : torch .Tensor , back : torch .Tensor ) -> torch .Tensor :
655+ result = back / (1 - front + 1e-7 )
656+ result = torch .clamp (result , 0 , 1 )
657+ return result
658+
659+ def gaussian_blur (self , image : torch .Tensor , blur_radius : int , sigma : float ):
660+ if blur_radius == 0 :
661+ return image
662+
663+ batch_size , channels , height , width = image .shape
664+
665+ kernel_size = blur_radius * 2 + 1
666+ kernel = gaussian_kernel (kernel_size , sigma ).repeat (channels , 1 , 1 ).unsqueeze (1 )
667+
668+ blurred = F .conv2d (image , kernel , padding = kernel_size // 2 , groups = channels )
669+
670+ return blurred
671+
672+ def sharpen (self , image : torch .Tensor , blur_radius : int , alpha : float ):
673+ if blur_radius == 0 :
674+ return image
675+
676+ batch_size , channels , height , width = image .shape
677+
678+ kernel_size = blur_radius * 2 + 1
679+ kernel = torch .ones ((kernel_size , kernel_size ), dtype = torch .float32 ) * - 1
680+ center = kernel_size // 2
681+ kernel [center , center ] = kernel_size ** 2
682+ kernel *= alpha
683+ kernel = kernel .repeat (channels , 1 , 1 ).unsqueeze (1 )
684+
685+ sharpened = F .conv2d (image , kernel , padding = center , groups = channels )
686+
687+ result = torch .clamp (sharpened , 0 , 1 )
688+
689+ return result
690+
617691class PixelSort :
618692 def __init__ (self ):
619693 pass
@@ -819,6 +893,12 @@ def solarize_image(self, image: torch.Tensor, threshold: float):
819893 solarized_image = torch .clamp (solarized_image , 0 , 1 )
820894 return (solarized_image ,)
821895
896+ def gaussian_kernel (kernel_size : int , sigma : float ):
897+ x , y = torch .meshgrid (torch .linspace (- 1 , 1 , kernel_size ), torch .linspace (- 1 , 1 , kernel_size ), indexing = "ij" )
898+ d = torch .sqrt (x * x + y * y )
899+ g = torch .exp (- (d * d ) / (2.0 * sigma * sigma ))
900+ return g / g .sum ()
901+
822902def sort_span (span , sort_by , reverse_sorting ):
823903 if sort_by == 'H' :
824904 key = lambda x : x [1 ][0 ]
@@ -920,6 +1000,7 @@ def pixel_sort(img, mask, horizontal_sort=False, span_limit=None, sort_by='H', r
9201000 "DodgeAndBurn" : DodgeAndBurn ,
9211001 "FilmGrain" : FilmGrain ,
9221002 "Glow" : Glow ,
1003+ "PencilSketch" : PencilSketch ,
9231004 "PixelSort" : PixelSort ,
9241005 "Pixelize" : Pixelize ,
9251006 "Quantize" : Quantize ,
0 commit comments