Ribot commited on
Commit
a8624ef
·
verified ·
1 Parent(s): 51039fb

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +161 -0
app.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import numpy as np
3
+ import math
4
+ import gradio as gr
5
+ import io
6
+ from collections import Counter
7
+ from sklearn.cluster import KMeans
8
+ import colorsys
9
+
10
+ def rgb_to_hsv(rgb):
11
+ """Convertit RGB vers HSV"""
12
+ r, g, b = rgb / 255.0
13
+ return np.array(colorsys.rgb_to_hsv(r, g, b))
14
+
15
+ def extract_dominant_colors(image, max_colors=100):
16
+ """Extrait les couleurs dominantes en regroupant par teintes"""
17
+ if image.mode != 'RGB':
18
+ image = image.convert('RGB')
19
+
20
+ # Convertit en tableau numpy
21
+ np_image = np.array(image)
22
+ pixels = np_image.reshape(-1, 3)
23
+
24
+ if len(pixels) == 0:
25
+ return np.array([])
26
+
27
+ # Si on a moins de couleurs que la limite, on retourne toutes les couleurs uniques
28
+ unique_pixels = np.unique(pixels, axis=0)
29
+ if len(unique_pixels) <= max_colors:
30
+ return unique_pixels
31
+
32
+ # Sinon, on utilise K-means pour regrouper les couleurs similaires
33
+ kmeans = KMeans(n_clusters=max_colors, random_state=42, n_init=10)
34
+ kmeans.fit(pixels)
35
+
36
+ # Retourne les centres des clusters (couleurs dominantes)
37
+ return kmeans.cluster_centers_.astype(int)
38
+
39
+ def sort_colors_by_hue(colors):
40
+ """Trie les couleurs par teinte (HSL)"""
41
+ def get_hue(rgb):
42
+ r, g, b = rgb / 255.0
43
+ h, s, v = colorsys.rgb_to_hsv(r, g, b)
44
+ return h
45
+
46
+ def get_brightness(rgb):
47
+ return np.mean(rgb)
48
+
49
+ # Trie d'abord par teinte, puis par luminosité
50
+ sorted_colors = sorted(colors, key=lambda rgb: (get_hue(rgb), get_brightness(rgb)))
51
+ return np.array(sorted_colors)
52
+
53
+ def create_color_palette(colors, square_size=50):
54
+ if len(colors) == 0:
55
+ return None
56
+
57
+ num_colors = len(colors)
58
+ # Calcul du nombre de colonnes et lignes pour faire un carré
59
+ grid_size = math.ceil(math.sqrt(num_colors))
60
+ palette_size = grid_size * square_size
61
+
62
+ # Crée une image vide
63
+ palette = Image.new("RGB", (palette_size, palette_size), (255, 255, 255))
64
+
65
+ for i, color in enumerate(colors):
66
+ r, g, b = color
67
+ # Crée un carré de la couleur
68
+ color_square = Image.new("RGB", (square_size, square_size), (int(r), int(g), int(b)))
69
+ x = (i % grid_size) * square_size
70
+ y = (i // grid_size) * square_size
71
+ palette.paste(color_square, (x, y))
72
+
73
+ return palette
74
+
75
+ def process_image(input_image, max_colors, sort_by_hue):
76
+ if input_image is None:
77
+ return None, "Veuillez uploader une image"
78
+
79
+ try:
80
+ # Extraction des couleurs dominantes
81
+ colors = extract_dominant_colors(input_image, max_colors)
82
+
83
+ # Tri par teinte si demandé
84
+ if sort_by_hue and len(colors) > 0:
85
+ colors = sort_colors_by_hue(colors)
86
+
87
+ # Création de la palette
88
+ palette = create_color_palette(colors)
89
+
90
+ if palette is not None:
91
+ message = f"Palette créée avec {len(colors)} couleurs dominantes"
92
+ return palette, message
93
+ else:
94
+ return None, "Aucune couleur trouvée dans l'image"
95
+
96
+ except Exception as e:
97
+ return None, f"Erreur lors du traitement : {str(e)}"
98
+
99
+ def download_palette(input_image, max_colors, sort_by_hue):
100
+ if input_image is None:
101
+ return None
102
+
103
+ try:
104
+ colors = extract_dominant_colors(input_image, max_colors)
105
+ if sort_by_hue and len(colors) > 0:
106
+ colors = sort_colors_by_hue(colors)
107
+ palette = create_color_palette(colors)
108
+ if palette:
109
+ # Sauvegarde dans un buffer
110
+ buffer = io.BytesIO()
111
+ palette.save(buffer, format="PNG")
112
+ buffer.seek(0)
113
+ return buffer
114
+ except:
115
+ return None
116
+
117
+ # Création de l'interface Gradio
118
+ with gr.Blocks(title="Extracteur de Palette de Couleurs") as demo:
119
+ gr.Markdown("# 🎨 Extracteur de Palette de Couleurs")
120
+ gr.Markdown("Upload une image pour extraire les couleurs dominantes et créer une palette visuelle")
121
+
122
+ with gr.Row():
123
+ with gr.Column():
124
+ input_image = gr.Image(type="pil", label="Image d'entrée")
125
+ max_colors = gr.Slider(minimum=5, maximum=200, value=50, step=5, label="Nombre maximum de couleurs")
126
+ sort_by_hue = gr.Checkbox(value=True, label="Trier par teintes")
127
+ with gr.Row():
128
+ submit_btn = gr.Button("🎨 Extraire les couleurs", variant="primary")
129
+ download_btn = gr.DownloadButton("💾 Télécharger la palette", variant="secondary")
130
+
131
+ with gr.Column():
132
+ output_image = gr.Image(label="Palette de couleurs", interactive=False)
133
+ status_text = gr.Textbox(label="Statut", interactive=False)
134
+
135
+ # Traitement principal
136
+ submit_btn.click(
137
+ fn=process_image,
138
+ inputs=[input_image, max_colors, sort_by_hue],
139
+ outputs=[output_image, status_text]
140
+ )
141
+
142
+ # Téléchargement
143
+ download_btn.click(
144
+ fn=download_palette,
145
+ inputs=[input_image, max_colors, sort_by_hue],
146
+ outputs=[download_btn]
147
+ )
148
+
149
+ # Exemples
150
+ gr.Examples(
151
+ examples=[
152
+ ["https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/1200px-React-icon.svg.png", 50, True],
153
+ ["https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/1200px-HTML5_logo_and_wordmark.svg.png", 30, True]
154
+ ],
155
+ inputs=[input_image, max_colors, sort_by_hue],
156
+ outputs=[output_image, status_text],
157
+ fn=process_image
158
+ )
159
+
160
+ if __name__ == "__main__":
161
+ demo.launch()