ColorPalette / src /skin_utils.py
HardikUppal's picture
clean up changes
953a2bf
from PIL import Image
import numpy as np
from sklearn.cluster import KMeans
from collections import Counter
from src.color_utils import calculate_color_distance_lab, rgb_to_lab
import cv2
class SkinTonePalette:
def __init__(self):
self.palette = {
"Ebony": ((55, 48, 40), "#373028"),
"Deep Mocha": ((66, 40, 17), "#422811"),
"Dark Chocolate": ((81, 59, 46), "#513b2e"),
"Warm Almond": ((111, 80, 60), "#6f503c"),
"Golden Bronze": ((129, 101, 79), "#81654f"),
"Honey": ((157, 122, 84), "#9d7a54"),
"Caramel": ((190, 160, 126), "#bea07e"),
"Light Tan": ((229, 200, 166), "#e5c8a6"),
"Peach": ((231, 193, 184), "#e7c1b8"),
"Fair": ((243, 218, 214), "#f3dad6"),
"Ivory": ((251, 242, 243), "#fbf2f3"),
}
def get_dominant_colors(self, image_np, n_colors):
pixels = image_np.reshape((-1, 3))
kmeans = KMeans(n_clusters=n_colors)
kmeans.fit(pixels)
dominant_colors = kmeans.cluster_centers_
counts = Counter(kmeans.labels_)
dominant_colors = [dominant_colors[i] for i in counts.keys()]
return dominant_colors
def get_closest_color(self, image, mask, n_colors=3):
image_np = np.array(image)
mask_np = np.array(mask)
if image_np.shape[:2] != mask_np.shape[:2]:
raise ValueError("Image and mask must have the same dimensions")
skin_pixels = image_np[mask_np > 0]
dominant_colors = self.get_dominant_colors(skin_pixels, n_colors)
closest_color = None
closest_hex = None
min_distance = float("inf")
for dom_color in dominant_colors:
for color_name, (color_value, color_hex) in self.palette.items():
distance = calculate_color_distance_lab(dom_color, color_value)
if distance < min_distance:
min_distance = distance
closest_color = color_name
closest_hex = color_hex
return closest_color, closest_hex
def calculate_ita(self, rgb_color):
lab_color = rgb_to_lab(rgb_color)
L = lab_color.lab_l
b = lab_color.lab_b
ita = np.arctan2(L - 50, b) * (180 / np.pi)
return ita
def is_within_vectorscope_skin_tone_line(self, rgb_color):
ycbcr_color = cv2.cvtColor(np.uint8([[rgb_color]]), cv2.COLOR_RGB2YCrCb)[0][0]
cb, cr = ycbcr_color[1], ycbcr_color[2]
return 80 <= cb <= 120 and 133 <= cr <= 173