diff --git a/color_output.txt b/color_output.txt new file mode 100644 index 000000000..5a5e4bcec --- /dev/null +++ b/color_output.txt @@ -0,0 +1,5 @@ +MC1: #d1a443 - peru, MC2: #16b3d8 - darkturquoise, MC3: #c0e5ed - powderblue, MC4: #5f870f - olivedrab +AN1: #b7d143 - yellowgreen, AN2: #d15d43 - indianred +T1: #43d1a4 - mediumaquamarine, T2: #a443d1 - darkorchid +C1: #4370d1 - royalblue, C2: #d83b16 - orangered +MO1: #d1c4a6 - tan, MO2: #9ecdd8 - lightsteelblue, MO3: #e0ebed - lavender, MO4: #7b8763 - gray diff --git a/comfy_runpod.py b/comfy_runpod.py index 6a29b4c88..26a021f5a 100644 --- a/comfy_runpod.py +++ b/comfy_runpod.py @@ -10,7 +10,7 @@ from PIL import Image import base64 import io - +from custom_scripts_for_nodes.Rainbow import extract_rainbow import runpod @@ -64,22 +64,40 @@ def get_images(ws, prompt): def run_prompt(job): - + + data = {'images':[],'rainbow':"None"} + + #Inferring from the Rainbow Script + image_string = job['input']['image_string'] prompt_text = job["input"]["prompt"] - prompt = prompt_text - ws = websocket.WebSocket() - ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id)) - images = get_images(ws, prompt) - data = {'images':[]} - for node_id in images: - for image_data in images[node_id]: - image = Image.open(io.BytesIO(image_data)) - im_file = io.BytesIO() - image.save(im_file, format="JPEG") - im_bytes = im_file.getvalue() # im_bytes: image in binary format. - im_b64 = base64.b64encode(im_bytes) - im_b64 = str(im_b64) - data['images'].append(im_b64) + + if image_string != 'None': + # Decode the base64 string into bytes + decoded_bytes = base64.b64decode(image_string) + # Convert the bytes to an in-memory file-like object using io.BytesIO + image_data = io.BytesIO(decoded_bytes) + image = Image.open(image_data) + rnbw = extract_rainbow() + rnbw_values = rnbw.main(image) + + data['rainbow'] = rnbw_values + + if prompt_text != "None": + prompt = prompt_text + ws = websocket.WebSocket() + ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id)) + images = get_images(ws, prompt) + + + for node_id in images: + for image_data in images[node_id]: + image = Image.open(io.BytesIO(image_data)) + im_file = io.BytesIO() + image.save(im_file, format="JPEG") + im_bytes = im_file.getvalue() # im_bytes: image in binary format. + im_b64 = base64.b64encode(im_bytes) + im_b64 = str(im_b64) + data['images'].append(im_b64) return data diff --git a/custom_scripts_for_nodes/Rainbow.py b/custom_scripts_for_nodes/Rainbow.py new file mode 100644 index 000000000..b2721e8c8 --- /dev/null +++ b/custom_scripts_for_nodes/Rainbow.py @@ -0,0 +1,135 @@ +from PIL import Image +from sklearn.cluster import KMeans +import numpy as np +from scipy.spatial import KDTree +import colorsys +import webcolors + + + +class extract_rainbow(): + + def __init__(self): + pass + + # Utility Functions + def rgb_to_hsv(self,rgb): + return colorsys.rgb_to_hsv(rgb[0]/255, rgb[1]/255, rgb[2]/255) + + def hsv_to_rgb(self,hsv): + return tuple(round(i * 255) for i in colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2])) + + def rgb_to_hex(self,rgb): + return '#{:02x}{:02x}{:02x}'.format(int(rgb[0]), int(rgb[1]), int(rgb[2])) + + def hex_to_rgb(self,hex_color): + return webcolors.hex_to_rgb(hex_color) + + def get_most_common_colors(self,image, n_colors=4): + image = image.resize((50, 50)) # Reduce the size + ar = np.asarray(image) + ar = ar.reshape(-1, 3) + + kmeans = KMeans(n_clusters=n_colors) + kmeans.fit(ar) + + return [self.rgb_to_hex(tuple(map(int, center))) for center in kmeans.cluster_centers_] + + def closest_color_name(self,requested_color): + min_colors = {} + for key, name in webcolors.CSS3_HEX_TO_NAMES.items(): + r_c, g_c, b_c = webcolors.hex_to_rgb(key) + rd = (r_c - requested_color[0]) ** 2 + gd = (g_c - requested_color[1]) ** 2 + bd = (b_c - requested_color[2]) ** 2 + min_colors[(rd + gd + bd)] = name + return min_colors[min(min_colors.keys())] + + def get_color_name(self,hex_color): + try: + closest_name = actual_name = webcolors.hex_to_name(hex_color) + except ValueError: + closest_name = self.closest_color_name(self.hex_to_rgb(hex_color)) + actual_name = None + return closest_name + + def get_analogous_colors(self,hsv, angle=30): + analogous1_hue = (hsv[0] + angle/360) % 1 + analogous2_hue = (hsv[0] - angle/360) % 1 + analogous1 = self.rgb_to_hex(self.hsv_to_rgb((analogous1_hue, hsv[1], hsv[2]))) + analogous2 = self.rgb_to_hex(self.hsv_to_rgb((analogous2_hue, hsv[1], hsv[2]))) + return analogous1, analogous2 + + def get_triadic_colors(self,hsv): + triadic1_hue = (hsv[0] + 1/3) % 1 + triadic2_hue = (hsv[0] + 2/3) % 1 + triadic1 = self.rgb_to_hex(self.hsv_to_rgb((triadic1_hue, hsv[1], hsv[2]))) + triadic2 = self.rgb_to_hex(self.hsv_to_rgb((triadic2_hue, hsv[1], hsv[2]))) + return triadic1, triadic2 + + def get_complementary_color(self,hsv): + complementary_hue = (hsv[0] + 0.5) % 1 + complementary = self.rgb_to_hex(self.hsv_to_rgb((complementary_hue, hsv[1], hsv[2]))) + return complementary + + def get_monochromatic_color(self,hsv): + # Here we are taking 3 saturation levels for a given color + monochromatic1 = self.rgb_to_hex(self.hsv_to_rgb((hsv[0], hsv[1]*0.3, hsv[2]))) + monochromatic2 = self.rgb_to_hex(self.hsv_to_rgb((hsv[0], hsv[1]*0.5, hsv[2]))) + monochromatic3 = self.rgb_to_hex(self.hsv_to_rgb((hsv[0], hsv[1]*0.7, hsv[2]))) + return monochromatic1, monochromatic2, monochromatic3 + + def main(self,tensor): + # Load image and get most common colors + hex_colors = self.get_most_common_colors(image) + + # Get color names and HSV + MC_hex = [] + MC_names = [] + MC_hsv = [] + + for hex_color in hex_colors: + MC_hex.append(hex_color) + MC_names.append(self.get_color_name(hex_color)) + MC_hsv.append(self.rgb_to_hsv(self.hex_to_rgb(hex_color))) + + # If MC1 color is black or very dark, use MC2 for calculations + dark_colors = ['black', 'darkslategrey', 'indigo', 'midnightblue', 'darkred'] + if MC_names[0].lower() in dark_colors: + MC = MC_hex[1] + MC_name = MC_names[1] + MC_hsv = MC_hsv[1] + else: + MC = MC_hex[0] + MC_name = MC_names[0] + MC_hsv = MC_hsv[0] + + # Calculate color schemes + AN1, AN2 = self.get_analogous_colors(MC_hsv) + T1, T2 = self.get_triadic_colors(MC_hsv) + C1 = self.get_complementary_color(MC_hsv) + C2 = self.get_complementary_color(self.rgb_to_hsv(self.hex_to_rgb(MC_hex[1]))) # Use MC2 for second complementary color + + # Get monochromatic colors for each MC + MO = [] + for color in MC_hex: + hsv = self.rgb_to_hsv(self.hex_to_rgb(color)) + MO.append(self.get_monochromatic_color(hsv)) + + # Generate Output + output = f"MC1: {MC_hex[0]} - {MC_names[0]}, MC2: {MC_hex[1]} - {MC_names[1]}, MC3: {MC_hex[2]} - {MC_names[2]}, MC4: {MC_hex[3]} - {MC_names[3]}\n" + output += f"AN1: {AN1} - {self.get_color_name(AN1)}, AN2: {AN2} - {self.get_color_name(AN2)}\n" + output += f"T1: {T1} - {self.get_color_name(T1)}, T2: {T2} - {self.get_color_name(T2)}\n" + output += f"C1: {C1} - {self.get_color_name(C1)}, C2: {C2} - {self.get_color_name(C2)}\n" + output += f"MO1: {MO[0][0]} - {self.get_color_name(MO[0][0])}, MO2: {MO[1][0]} - {self.get_color_name(MO[1][0])}, MO3: {MO[2][0]} - {self.get_color_name(MO[2][0])}, MO4: {MO[3][0]} - {self.get_color_name(MO[3][0])}\n" + + # Save to a text file + with open('color_output.txt', 'w') as f: + f.write(output) + + return output + + +if __name__ == '__main__': + rnbw = extract_rainbow() + rnbw.main() \ No newline at end of file diff --git a/custom_scripts_for_nodes/color_output.txt b/custom_scripts_for_nodes/color_output.txt new file mode 100644 index 000000000..9c5ff9899 --- /dev/null +++ b/custom_scripts_for_nodes/color_output.txt @@ -0,0 +1,5 @@ +MC1: #9a5938 - sienna, MC2: #656771 - dimgray, MC3: #1e1413 - black, MC4: #d1ad91 - tan +AN1: #9a8a38 - olivedrab, AN2: #9a3848 - brown +T1: #389a59 - seagreen, T2: #59389a - darkslateblue +C1: #38799a - steelblue, C2: #716f65 - dimgray +MO1: #9a867d - gray, MO2: #6d6e71 - dimgray, MO3: #1e1b1b - black, MO4: #d1c6be - silver diff --git a/custom_scripts_for_nodes/color_output_class.txt b/custom_scripts_for_nodes/color_output_class.txt new file mode 100644 index 000000000..40b94ec04 --- /dev/null +++ b/custom_scripts_for_nodes/color_output_class.txt @@ -0,0 +1,5 @@ +MC1: #1d1413 - black, MC2: #656771 - dimgray, MC3: #995838 - sienna, MC4: #d0ad90 - tan +AN1: #696571 - dimgray, AN2: #656d71 - dimgray +T1: #716567 - dimgray, T2: #677165 - dimgray +C1: #716f65 - dimgray, C2: #716f65 - dimgray +MO1: #1d1a1a - black, MO2: #6d6e71 - dimgray, MO3: #99867c - gray, MO4: #d0c5bd - silver diff --git a/custom_scripts_for_nodes/color_output_orig.txt b/custom_scripts_for_nodes/color_output_orig.txt new file mode 100644 index 000000000..119530fd8 --- /dev/null +++ b/custom_scripts_for_nodes/color_output_orig.txt @@ -0,0 +1,5 @@ +MC1: #d1ad91 - tan, MC2: #9a5938 - sienna, MC3: #1e1413 - black, MC4: #656771 - dimgray +AN1: #d1cd91 - tan, AN2: #d19195 - rosybrown +T1: #91d1ad - darkseagreen, T2: #ad91d1 - mediumpurple +C1: #91b5d1 - lightsteelblue, C2: #38799a - steelblue +MO1: #d1c6be - silver, MO2: #9a867d - gray, MO3: #1e1b1b - black, MO4: #6d6e71 - dimgray diff --git a/custom_scripts_for_nodes/input.png b/custom_scripts_for_nodes/input.png new file mode 100644 index 000000000..6e3a21b8d Binary files /dev/null and b/custom_scripts_for_nodes/input.png differ diff --git a/requirement_our.txt b/requirement_our.txt new file mode 100644 index 000000000..4e6b86de9 --- /dev/null +++ b/requirement_our.txt @@ -0,0 +1 @@ +webcolors==1.13 \ No newline at end of file diff --git a/test_input.json b/test_input.json new file mode 100644 index 000000000..f2ddf8a22 --- /dev/null +++ b/test_input.json @@ -0,0 +1,6 @@ +{ + "input": { + "prompt": {"3": {"inputs": {"seed": 156680208700286, "steps": 20, "cfg": 8.0, "sampler_name": "euler", "scheduler": "normal", "denoise": 1.0, "model": ["4", 0], "positive": ["6", 0], "negative": ["7", 0], "latent_image": ["5", 0]}, "class_type": "KSampler"}, "4": {"inputs": {"ckpt_name": "sd-v1-4.ckpt"}, "class_type": "CheckpointLoaderSimple"}, "5": {"inputs": {"width": 512, "height": 512, "batch_size": 1}, "class_type": "EmptyLatentImage"}, "6": {"inputs": {"text": "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,", "clip": ["4", 1]}, "class_type": "CLIPTextEncode"}, "7": {"inputs": {"text": "text, watermark", "clip": ["4", 1]}, "class_type": "CLIPTextEncode"}, "8": {"inputs": {"samples": ["3", 0], "vae": ["4", 2]}, "class_type": "VAEDecode"}, "9": {"inputs": {"filename_prefix": "ComfyUI", "images": ["8", 0]}, "class_type": "SaveImage"}}, + "image_string" : "None" + } +} \ No newline at end of file