25
25
from invokeai .app .services .shared .invocation_context import InvocationContext
26
26
from invokeai .backend .image_util .invisible_watermark import InvisibleWatermark
27
27
from invokeai .backend .image_util .safety_checker import SafetyChecker
28
+ from invokeai .app .util .misc import SEED_MAX
28
29
29
30
30
31
@invocation ("show_image" , title = "Show Image" , tags = ["image" ], category = "image" , version = "1.0.1" )
@@ -1089,3 +1090,60 @@ def invoke(self, context: InvocationContext) -> ImageOutput:
1089
1090
image_dto = context .images .save (image = generated_image )
1090
1091
1091
1092
return ImageOutput .build (image_dto )
1093
+
1094
+
1095
+ @invocation (
1096
+ "image_noise" ,
1097
+ title = "Add Image Noise" ,
1098
+ tags = ["image" , "noise" ],
1099
+ category = "image" ,
1100
+ version = "1.0.2" ,
1101
+ )
1102
+ class ImageNoiseInvocation (BaseInvocation , WithMetadata , WithBoard ):
1103
+ """Add noise to an image"""
1104
+
1105
+ image : ImageField = InputField (description = "The image to add noise to" )
1106
+ seed : int = InputField (
1107
+ default = 0 ,
1108
+ ge = 0 ,
1109
+ le = SEED_MAX ,
1110
+ description = FieldDescriptions .seed ,
1111
+ )
1112
+ noise_type : Literal ["gaussian" , "salt_and_pepper" ] = InputField (
1113
+ default = "gaussian" ,
1114
+ description = "The type of noise to add" ,
1115
+ )
1116
+ amount : float = InputField (default = 0.1 , ge = 0 , le = 1 , description = "The amount of noise to add" )
1117
+ color : bool = InputField (default = False , description = "Whether to add color noise" )
1118
+
1119
+ def invoke (self , context : InvocationContext ) -> ImageOutput :
1120
+ image = context .images .get_pil (self .image .image_name , mode = "RGBA" )
1121
+
1122
+ # Save out the alpha channel
1123
+ alpha = image .getchannel ("A" )
1124
+
1125
+ # Set the seed for numpy random
1126
+ rs = numpy .random .RandomState (numpy .random .MT19937 (numpy .random .SeedSequence (self .seed )))
1127
+
1128
+ if self .noise_type == "gaussian" :
1129
+ if self .color :
1130
+ noise = rs .normal (0 , 1 , (image .height , image .width , 3 )) * 255
1131
+ else :
1132
+ noise = rs .normal (0 , 1 , (image .height , image .width )) * 255
1133
+ noise = numpy .stack ([noise ] * 3 , axis = - 1 )
1134
+ elif self .noise_type == "salt_and_pepper" :
1135
+ if self .color :
1136
+ noise = rs .choice ([0 , 255 ], (image .height , image .width , 3 ), p = [1 - self .amount , self .amount ])
1137
+ else :
1138
+ noise = rs .choice ([0 , 255 ], (image .height , image .width ), p = [1 - self .amount , self .amount ])
1139
+ noise = numpy .stack ([noise ] * 3 , axis = - 1 )
1140
+
1141
+ noise = Image .fromarray (noise .astype (numpy .uint8 ), mode = "RGB" )
1142
+ noisy_image = Image .blend (image .convert ("RGB" ), noise , self .amount ).convert ("RGBA" )
1143
+
1144
+ # Paste back the alpha channel
1145
+ noisy_image .putalpha (alpha )
1146
+
1147
+ image_dto = context .images .save (image = noisy_image )
1148
+
1149
+ return ImageOutput .build (image_dto )
0 commit comments