import gradio as gr import tensorflow as tf from sklearn.cluster import KMeans import numpy as np import cv2 from webcolors import hex_to_rgb, rgb_to_hex from scipy.spatial import KDTree from collections import Counter model = tf.keras.models.load_model("model.h5") classes = [ "background", "skin", "left eyebrow", "right eyebrow", "left eye", "right eye", "nose", "upper lip", "inner mouth", "lower lip", "hair" ] def face_skin_extract(pred, image_x): output = np.zeros_like(image_x, dtype=np.uint8) mask = (pred == 1) output[mask] = image_x[mask] return output def extract_dom_color_kmeans(img): mask = ~np.all(img == [0, 0, 0], axis=-1) # Mask to exclude black pixels non_black_pixels = img[mask] # Extract non-black pixels k_cluster = KMeans(n_clusters=3, n_init="auto") # Apply KMeans clustering on non-black pixels k_cluster.fit(non_black_pixels) width = 300 palette = np.zeros((50, width, 3), dtype=np.uint8) n_pixels = len(k_cluster.labels_) counter = Counter(k_cluster.labels_) perc = {i: np.round(counter[i] / n_pixels, 2) for i in counter} perc = dict(sorted(perc.items())) cluster_centers = k_cluster.cluster_centers_ # print("Cluster Percentages:", perc) # print("Cluster Centers (RGB):", cluster_centers) val = list(perc.values()) val.sort() res = val[-1] print(res) sec_high_val = list(perc.keys())[list(perc.values()).index(res)] rgb_list = cluster_centers[sec_high_val] step = 0 for idx, center in enumerate(k_cluster.cluster_centers_): width_step = int(perc[idx] * width + 1) palette[:, step:step + width_step, :] = center step += width_step return rgb_list def closest_tone_match(rgb_tuple): skin_tones = {'Monk 10': '#292420', 'Monk 9': '#3a312a', 'Monk 8':'#604134', 'Monk 7':'#825c43', 'Monk 6':'#a07e56', 'Monk 5':'#d7bd96', 'Monk 4':'#eadaba', 'Monk 3':'#f7ead0', 'Monk 2':'#f3e7db', 'Monk 1':'#f6ede4'} rgb_values = [] names = [] for monk in skin_tones: names.append(monk) rgb_values.append(hex_to_rgb(skin_tones[monk])) kdt_db = KDTree(rgb_values) distance, index = kdt_db.query(rgb_tuple) monk_hex = skin_tones[names[index]] derived_hex = rgb_to_hex((int(rgb_tuple[0]), int(rgb_tuple[1]), int(rgb_tuple[2]))) return names[index],monk_hex,derived_hex def process_image(image_path): image = cv2.imread(image_path) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image_x = cv2.resize(image, (512, 512)) image_norm = image_x / 255.0 image_norm = np.expand_dims(image_norm, axis=0).astype(np.float32) pred = model.predict(image_norm)[0] pred = np.argmax(pred, axis=-1).astype(np.int32) face_skin = face_skin_extract(pred, image_x) # face_skin_vis = cv2.imread(face_skin) dominant_color_rgb = extract_dom_color_kmeans(face_skin) monk_tone, monk_hex, derived_hex = closest_tone_match( (dominant_color_rgb[0], dominant_color_rgb[1], dominant_color_rgb[2]) ) return [monk_tone,derived_hex,monk_hex] inputs = gr.Image(type="filepath", label="Upload Face Image") outputs = [ gr.Label(label="Monk Skin Tone"), gr.ColorPicker(label="Derived Color"), gr.ColorPicker(label="Closest Monk Color"), # gr.JSON(label="Dominant RGB Values"), ] interface = gr.Interface( fn=process_image, inputs=inputs, outputs=outputs, title="Skin Tone Analysis", description="Upload a face image to analyse skin tone and find closest Monk skin tone match." ) if __name__ == "__main__": interface.launch()