Spaces:
Sleeping
Sleeping
from PIL import Image | |
import numpy as np | |
import math | |
import gradio as gr | |
import io | |
from collections import Counter | |
from sklearn.cluster import KMeans | |
import colorsys | |
def rgb_to_hsv(rgb): | |
"""Convertit RGB vers HSV""" | |
r, g, b = rgb / 255.0 | |
return np.array(colorsys.rgb_to_hsv(r, g, b)) | |
def extract_dominant_colors(image, max_colors=100): | |
"""Extrait les couleurs dominantes en regroupant par teintes""" | |
if image.mode != 'RGB': | |
image = image.convert('RGB') | |
# Convertit en tableau numpy | |
np_image = np.array(image) | |
pixels = np_image.reshape(-1, 3) | |
if len(pixels) == 0: | |
return np.array([]) | |
# Si on a moins de couleurs que la limite, on retourne toutes les couleurs uniques | |
unique_pixels = np.unique(pixels, axis=0) | |
if len(unique_pixels) <= max_colors: | |
return unique_pixels | |
# Sinon, on utilise K-means pour regrouper les couleurs similaires | |
kmeans = KMeans(n_clusters=max_colors, random_state=42, n_init=10) | |
kmeans.fit(pixels) | |
# Retourne les centres des clusters (couleurs dominantes) | |
return kmeans.cluster_centers_.astype(int) | |
def sort_colors_by_hue(colors): | |
"""Trie les couleurs par teinte (HSL)""" | |
def get_hue(rgb): | |
r, g, b = rgb / 255.0 | |
h, s, v = colorsys.rgb_to_hsv(r, g, b) | |
return h | |
def get_brightness(rgb): | |
return np.mean(rgb) | |
# Trie d'abord par teinte, puis par luminosité | |
sorted_colors = sorted(colors, key=lambda rgb: (get_hue(rgb), get_brightness(rgb))) | |
return np.array(sorted_colors) | |
def create_color_palette(colors, square_size=50): | |
if len(colors) == 0: | |
return None | |
num_colors = len(colors) | |
# Calcul du nombre de colonnes et lignes pour faire un carré | |
grid_size = math.ceil(math.sqrt(num_colors)) | |
palette_size = grid_size * square_size | |
# Crée une image vide | |
palette = Image.new("RGB", (palette_size, palette_size), (255, 255, 255)) | |
for i, color in enumerate(colors): | |
r, g, b = color | |
# Crée un carré de la couleur | |
color_square = Image.new("RGB", (square_size, square_size), (int(r), int(g), int(b))) | |
x = (i % grid_size) * square_size | |
y = (i // grid_size) * square_size | |
palette.paste(color_square, (x, y)) | |
return palette | |
def process_image(input_image, max_colors, sort_by_hue): | |
if input_image is None: | |
return None, "Veuillez uploader une image" | |
try: | |
# Extraction des couleurs dominantes | |
colors = extract_dominant_colors(input_image, max_colors) | |
# Tri par teinte si demandé | |
if sort_by_hue and len(colors) > 0: | |
colors = sort_colors_by_hue(colors) | |
# Création de la palette | |
palette = create_color_palette(colors) | |
if palette is not None: | |
message = f"Palette créée avec {len(colors)} couleurs dominantes" | |
return palette, message | |
else: | |
return None, "Aucune couleur trouvée dans l'image" | |
except Exception as e: | |
return None, f"Erreur lors du traitement : {str(e)}" | |
def download_palette(input_image, max_colors, sort_by_hue): | |
if input_image is None: | |
return None | |
try: | |
colors = extract_dominant_colors(input_image, max_colors) | |
if sort_by_hue and len(colors) > 0: | |
colors = sort_colors_by_hue(colors) | |
palette = create_color_palette(colors) | |
if palette: | |
# Sauvegarde dans un buffer | |
buffer = io.BytesIO() | |
palette.save(buffer, format="PNG") | |
buffer.seek(0) | |
return buffer | |
except: | |
return None | |
# Création de l'interface Gradio | |
with gr.Blocks(title="Extracteur de Palette de Couleurs") as demo: | |
gr.Markdown("# 🎨 Extracteur de Palette de Couleurs") | |
gr.Markdown("Upload une image pour extraire les couleurs dominantes et créer une palette visuelle") | |
with gr.Row(): | |
with gr.Column(): | |
input_image = gr.Image(type="pil", label="Image d'entrée") | |
max_colors = gr.Slider(minimum=5, maximum=200, value=50, step=5, label="Nombre maximum de couleurs") | |
sort_by_hue = gr.Checkbox(value=True, label="Trier par teintes") | |
with gr.Row(): | |
submit_btn = gr.Button("🎨 Extraire les couleurs", variant="primary") | |
download_btn = gr.DownloadButton("💾 Télécharger la palette", variant="secondary") | |
with gr.Column(): | |
output_image = gr.Image(label="Palette de couleurs", interactive=False) | |
status_text = gr.Textbox(label="Statut", interactive=False) | |
# Traitement principal | |
submit_btn.click( | |
fn=process_image, | |
inputs=[input_image, max_colors, sort_by_hue], | |
outputs=[output_image, status_text] | |
) | |
# Téléchargement | |
download_btn.click( | |
fn=download_palette, | |
inputs=[input_image, max_colors, sort_by_hue], | |
outputs=[download_btn] | |
) | |
# Exemples | |
gr.Examples( | |
examples=[ | |
["https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1200px-React-icon.svg.png", 50, True], | |
["https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/1200px-HTML5_logo_and_wordmark.svg.png", 30, True] | |
], | |
inputs=[input_image, max_colors, sort_by_hue], | |
outputs=[output_image, status_text], | |
fn=process_image | |
) | |
if __name__ == "__main__": | |
demo.launch() |