bebechien commited on
Commit
76ca028
·
verified ·
1 Parent(s): f20e980

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. README.md +4 -6
  3. app.py +455 -0
  4. requirements.txt +2 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ transformers-4.57.0.dev0-py3-none-any.whl filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,14 +1,12 @@
1
  ---
2
- title: Mood Palette
3
- emoji:
4
- colorFrom: indigo
5
- colorTo: gray
6
  sdk: gradio
7
  sdk_version: 5.44.1
8
  app_file: app.py
9
  pinned: false
10
- license: gemma
11
  short_description: Mood Palette Generator
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Mood Palette Generator
3
+ emoji: 🎨
4
+ colorFrom: purple
5
+ colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 5.44.1
8
  app_file: app.py
9
  pinned: false
 
10
  short_description: Mood Palette Generator
11
  ---
12
 
 
app.py ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import os
4
+ from huggingface_hub import login
5
+ from sentence_transformers import SentenceTransformer, util
6
+
7
+ # --- 1. CONFIGURATION ---
8
+ # Centralized place for all settings and constants.
9
+
10
+ class Config:
11
+ """Configuration settings for the application."""
12
+ EMBEDDING_MODEL_ID = "google/embeddinggemma-300M"
13
+ PROMPT_NAME = "STS"
14
+ TOP_K = 5
15
+ HF_TOKEN = os.getenv('HF_TOKEN')
16
+
17
+
18
+ # --- 2. COLOR DATA ---
19
+ # The color palette data is kept separate for clarity and easy modification.
20
+
21
+ COLOR_DATA = [
22
+ {
23
+ "name": "Crimson",
24
+ "hex": "#DC143C",
25
+ "description": "A deep, rich red color, leaning slightly towards purple."
26
+ },
27
+ {
28
+ "name": "Scarlet",
29
+ "hex": "#FF2400",
30
+ "description": "A brilliant, vivid red with a hint of orange."
31
+ },
32
+ {
33
+ "name": "Coral",
34
+ "hex": "#FF7F50",
35
+ "description": "A vibrant pinkish-orange reminiscent of marine invertebrates."
36
+ },
37
+ {
38
+ "name": "Tangerine",
39
+ "hex": "#F28500",
40
+ "description": "A saturated, zesty orange, like the ripe citrus fruit."
41
+ },
42
+ {
43
+ "name": "Gold",
44
+ "hex": "#FFD700",
45
+ "description": "A bright, metallic yellow associated with wealth and luxury."
46
+ },
47
+ {
48
+ "name": "Lemon Chiffon",
49
+ "hex": "#FFFACD",
50
+ "description": "A pale, light yellow, as soft and airy as the dessert."
51
+ },
52
+ {
53
+ "name": "Lime Green",
54
+ "hex": "#32CD32",
55
+ "description": "A bright green color, evoking freshness and zesty energy."
56
+ },
57
+ {
58
+ "name": "Forest Green",
59
+ "hex": "#228B22",
60
+ "description": "A dark, shaded green, like the canopy of a dense forest."
61
+ },
62
+ {
63
+ "name": "Teal",
64
+ "hex": "#008080",
65
+ "description": "A medium blue-green color, often seen as sophisticated and calming."
66
+ },
67
+ {
68
+ "name": "Cyan",
69
+ "hex": "#00FFFF",
70
+ "description": "A vibrant greenish-blue, one of the primary subtractive colors."
71
+ },
72
+ {
73
+ "name": "Sky Blue",
74
+ "hex": "#87CEEB",
75
+ "description": "A light, pale blue, like the color of a clear daytime sky."
76
+ },
77
+ {
78
+ "name": "Royal Blue",
79
+ "hex": "#4169E1",
80
+ "description": "A deep, vivid blue that is both rich and bright."
81
+ },
82
+ {
83
+ "name": "Indigo",
84
+ "hex": "#4B0082",
85
+ "description": "A deep, rich color between blue and violet in the spectrum."
86
+ },
87
+ {
88
+ "name": "Lavender",
89
+ "hex": "#E6E6FA",
90
+ "description": "A light, pale purple with a bluish hue, named after the flower."
91
+ },
92
+ {
93
+ "name": "Plum",
94
+ "hex": "#DDA0DD",
95
+ "description": "A reddish-purple color, like the ripe fruit it's named after."
96
+ },
97
+ {
98
+ "name": "Magenta",
99
+ "hex": "#FF00FF",
100
+ "description": "A purplish-red color that lies between red and violet."
101
+ },
102
+ {
103
+ "name": "Hot Pink",
104
+ "hex": "#FF69B4",
105
+ "description": "A bright, vivid pink that is both bold and energetic."
106
+ },
107
+ {
108
+ "name": "Ivory",
109
+ "hex": "#FFFFF0",
110
+ "description": "An off-white color that resembles the material from tusks and teeth."
111
+ },
112
+ {
113
+ "name": "Beige",
114
+ "hex": "#F5F5DC",
115
+ "description": "A pale sandy fawn color, often used as a warm, neutral tone."
116
+ },
117
+ {
118
+ "name": "Taupe",
119
+ "hex": "#483C32",
120
+ "description": "A dark grayish-brown or brownish-gray color."
121
+ },
122
+ {
123
+ "name": "Slate Gray",
124
+ "hex": "#708090",
125
+ "description": "A medium gray with a slight blue tinge, like the metamorphic rock."
126
+ },
127
+ {
128
+ "name": "Charcoal",
129
+ "hex": "#36454F",
130
+ "description": "A dark, almost black gray, like burnt wood."
131
+ },
132
+ {
133
+ "name": "Onyx",
134
+ "hex": "#353839",
135
+ "description": "A deep, rich black, often with a subtle hint of dark blue."
136
+ },
137
+ {
138
+ "name": "Emerald",
139
+ "hex": "#50C878",
140
+ "description": "A brilliant green, named after the precious gemstone."
141
+ },
142
+ {
143
+ "name": "Sapphire",
144
+ "hex": "#0F52BA",
145
+ "description": "A deep, lustrous blue, reminiscent of the valuable gemstone."
146
+ },
147
+ {
148
+ "name": "Ruby",
149
+ "hex": "#E0115F",
150
+ "description": "A deep red color, inspired by the gemstone of the same name."
151
+ },
152
+ {
153
+ "name": "Amethyst",
154
+ "hex": "#9966CC",
155
+ "description": "A moderate, violet-purple color, like the quartz gemstone."
156
+ },
157
+ {
158
+ "name": "Peridot",
159
+ "hex": "#E6E200",
160
+ "description": "A light olive-green or yellowish-green, named for the gem."
161
+ },
162
+ {
163
+ "name": "Turquoise",
164
+ "hex": "#40E0D0",
165
+ "description": "A greenish-blue color, often associated with tropical waters."
166
+ },
167
+ {
168
+ "name": "Silver",
169
+ "hex": "#C0C0C0",
170
+ "description": "A metallic gray color that resembles polished silver."
171
+ },
172
+ {
173
+ "name": "Bronze",
174
+ "hex": "#CD7F32",
175
+ "description": "A metallic brown color that resembles the alloy of copper and tin."
176
+ },
177
+ {
178
+ "name": "Obsidian",
179
+ "hex": "#000000",
180
+ "description": "A pure, deep black, like the volcanic glass formed from cooled lava."
181
+ }
182
+ ]
183
+
184
+
185
+ # --- 3. CORE LOGIC ---
186
+ # Encapsulated in a class to manage state (model, embeddings) cleanly.
187
+
188
+ class MoodPaletteGenerator:
189
+ """Handles model loading, embedding generation, and palette creation."""
190
+
191
+ def __init__(self, config: Config, color_data: list[dict[str, any]]):
192
+ """Initializes the generator, logs in, and loads necessary assets."""
193
+ self.config = config
194
+ self.color_data = color_data
195
+ self._login_to_hf()
196
+ self.embedding_model = self._load_model()
197
+ self.color_embeddings = self._precompute_color_embeddings()
198
+
199
+ def _login_to_hf(self):
200
+ """Logs into Hugging Face Hub if a token is provided."""
201
+ if self.config.HF_TOKEN:
202
+ print("Logging into Hugging Face Hub...")
203
+ login(token=self.config.HF_TOKEN)
204
+ else:
205
+ print("HF_TOKEN not found. Proceeding without login.")
206
+ print("Note: This may fail if the model is gated.")
207
+
208
+ def _load_model(self) -> SentenceTransformer:
209
+ """Loads the Sentence Transformer model."""
210
+ print(f"Initializing embedding model: {self.config.EMBEDDING_MODEL_ID}...")
211
+ try:
212
+ return SentenceTransformer(self.config.EMBEDDING_MODEL_ID)
213
+ except Exception as e:
214
+ print(f"Error loading model: {e}")
215
+ raise
216
+
217
+ def _precompute_color_embeddings(self) -> np.ndarray:
218
+ """Generates and stores embeddings for the color descriptions."""
219
+ print("Pre-computing embeddings for color palette...")
220
+ color_texts = [
221
+ f"{color['name']}, {color['description']}"
222
+ for color in self.color_data
223
+ ]
224
+ embeddings = self.embedding_model.encode(
225
+ color_texts,
226
+ prompt_name=self.config.PROMPT_NAME
227
+ )
228
+ print("Embeddings computed successfully.")
229
+ return embeddings
230
+
231
+ def _get_text_color_for_bg(self, hex_color: str) -> str:
232
+ """
233
+ Calculates the luminance of a hex color and returns black ('#000000')
234
+ or white ('#FFFFFF') for the best text contrast.
235
+ """
236
+ hex_color = hex_color.lstrip('#')
237
+ try:
238
+ r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
239
+ luminance = (0.299 * r + 0.587 * g + 0.114 * b)
240
+ return '#000000' if luminance > 150 else '#FFFFFF'
241
+ except (ValueError, IndexError):
242
+ return '#000000' # Default to black on invalid hex
243
+
244
+ def _format_palette_as_html(self, top_hits: list[dict[str, any]]) -> str:
245
+ """Formats the top color hits into a displayable HTML string."""
246
+ if not top_hits:
247
+ return "<p>Could not generate a palette. Please try another mood.</p>"
248
+
249
+ cards_html = ""
250
+ for hit in top_hits:
251
+ color_info = self.color_data[hit['corpus_id']]
252
+ hex_code = color_info['hex']
253
+ name = color_info['name']
254
+ score = hit['score']
255
+ text_color = self._get_text_color_for_bg(hex_code)
256
+
257
+ cards_html += f"""
258
+ <div class="color-card" style="background-color: {hex_code}; color: {text_color};">
259
+ {name} | {hex_code} | Score: {score:.2f}
260
+ </div>
261
+ """
262
+ return f"<div class='palette-container'>{cards_html}</div>"
263
+
264
+ def _create_dynamic_theme_css(self, top_hits: list[dict[str, any]]) -> str:
265
+ """Generates a <style> block to override Gradio theme variables."""
266
+ theme_colors = []
267
+
268
+ if top_hits:
269
+ theme_colors = [
270
+ {
271
+ "bg": self.color_data[hit['corpus_id']]['hex'],
272
+ "txt": self._get_text_color_for_bg(self.color_data[hit['corpus_id']]['hex'])
273
+ }
274
+ for hit in top_hits
275
+ ]
276
+
277
+ css_map = {
278
+ "button-primary-background-fill": (0, 'bg'),
279
+ "button-primary-text-color": (0, 'txt'),
280
+ "button-secondary-background-fill": (1, 'bg'),
281
+ "button-secondary-text-color": (1, 'txt'),
282
+ "block-background-fill": (2, 'bg'),
283
+ "block-info-text-color": (2, 'txt'),
284
+ "block-title-background-fill": (3, 'bg'),
285
+ "button-primary-background-fill-hover": (3, 'bg'),
286
+ "block-title-text-color": (3, 'txt'),
287
+ "button-primary-text-color-hover": (3, 'txt'),
288
+ "button-secondary-background-fill-hover": (4, 'bg'),
289
+ "button-secondary-text-color-hover": (4, 'txt'),
290
+ }
291
+
292
+ css_rules = []
293
+ num_available_colors = len(theme_colors)
294
+
295
+ for var_suffix, (index, key) in css_map.items():
296
+ if index < num_available_colors:
297
+ color_value = theme_colors[index][key]
298
+ css_rules.append(f"--{var_suffix}: {color_value};")
299
+
300
+ css_rules_str = "\n".join(css_rules)
301
+
302
+ # Create CSS variables to inject
303
+ css = f"""
304
+ <style>
305
+ :root {{
306
+ {css_rules_str}
307
+ }}
308
+
309
+ :root .dark {{
310
+ {css_rules_str}
311
+ }}
312
+
313
+ .gallery-item .gallery {{
314
+ background: {theme_colors[4]['bg']};
315
+ color: {theme_colors[4]['txt']};
316
+ }}
317
+ </style>
318
+ """
319
+
320
+ return css
321
+
322
+ def generate_palette_and_theme(self, mood_text: str) -> tuple[str, str]:
323
+ """
324
+ Generates a color palette HTML and a dynamic theme CSS string.
325
+ """
326
+ if not mood_text or not mood_text.strip():
327
+ return "<p>Please enter a mood or a description.</p>", ""
328
+
329
+ mood_embedding = self.embedding_model.encode(
330
+ mood_text,
331
+ prompt_name=self.config.PROMPT_NAME
332
+ )
333
+ top_hits = util.semantic_search(
334
+ mood_embedding, self.color_embeddings, top_k=self.config.TOP_K
335
+ )[0]
336
+
337
+ palette_html = self._format_palette_as_html(top_hits)
338
+ theme_css = self._create_dynamic_theme_css(top_hits)
339
+
340
+ return palette_html, theme_css
341
+
342
+ def clear_theme(self) -> tuple[str, str]:
343
+ return "", ""
344
+
345
+
346
+ # --- 4. GRADIO UI ---
347
+ # Defines and launches the web interface.
348
+
349
+ def create_ui(generator: MoodPaletteGenerator):
350
+ """Creates the Gradio web interface."""
351
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
352
+ # This invisible component will hold our dynamic CSS
353
+ dynamic_css_output = gr.HTML()
354
+
355
+ gr.Markdown("""
356
+ # 🎨 Mood Palette Generator
357
+ Describe a mood, a scene, or a feeling, and get a matching color palette.<br>
358
+ **The UI theme will update to match your mood!**
359
+ """)
360
+
361
+ with gr.Row():
362
+ with gr.Column(scale=4):
363
+ mood_input = gr.Textbox(
364
+ value="Strawberry ice cream",
365
+ label="Enter Your Mood or Scene",
366
+ info="Be as descriptive as you like!"
367
+ )
368
+ with gr.Column(scale=1, min_width=150):
369
+ submit_button = gr.Button("Generate Palette", variant="primary")
370
+ clear_button = gr.Button("Clear", variant="secondary")
371
+
372
+ palette_output = gr.HTML(label="Your Generated Palette")
373
+
374
+ # Define CSS for palette cards here once, instead of in every update
375
+ gr.HTML("""
376
+ <style>
377
+ .palette-container {
378
+ display: flex; flex-direction: column; gap: 10px;
379
+ align-items: center; width: 100%;
380
+ q}
381
+ .color-card {
382
+ border-radius: 10px; text-align: center; padding: 15px 10px;
383
+ width: 90%; max-width: 400px;
384
+ box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
385
+ font-family: sans-serif; font-size: 16px; font-weight: bold;
386
+ transition: transform 0.2s;
387
+ }
388
+ .color-card:hover { transform: scale(1.05); }
389
+ </style>
390
+ """)
391
+
392
+ # Define the function to be called by events
393
+ event_handler = generator.generate_palette_and_theme
394
+ outputs_list = [palette_output, dynamic_css_output]
395
+
396
+ gr.Examples(
397
+ [
398
+ "A futuristic city at night, slick with rain",
399
+ "The feeling of a cozy cabin during a blizzard",
400
+ "Joyful chaos at a summer music festival",
401
+ "Beach sunset with the palm tree",
402
+ "A calm and lonely winter morning",
403
+ "Vintage romance in a dusty library",
404
+ "Cyberpunk alleyway with neon signs",
405
+ ],
406
+ inputs=mood_input,
407
+ outputs=outputs_list,
408
+ fn=event_handler,
409
+ run_on_click=True,
410
+ )
411
+
412
+ submit_button.click(
413
+ fn=event_handler,
414
+ inputs=mood_input,
415
+ outputs=outputs_list,
416
+ )
417
+ clear_button.click(
418
+ fn=generator.clear_theme,
419
+ outputs=outputs_list,
420
+ )
421
+ # Also allow submitting by pressing Enter in the textbox
422
+ mood_input.submit(
423
+ fn=event_handler,
424
+ inputs=mood_input,
425
+ outputs=outputs_list,
426
+ )
427
+
428
+ gr.Markdown("""
429
+ ----
430
+
431
+ ## What is this?
432
+
433
+ This interactive application, the **Mood Palette Generator**, transforms your words into a vibrant color palette. Simply describe a mood, a scene, or a feeling and the app will generate a set of matching colors. As a unique touch, the entire user interface dynamically updates its theme to reflect the generated palette, immersing you in your chosen mood.
434
+
435
+ ## How It Works?
436
+
437
+ At its core, this tool is powered by [**EmbeddingGemma**](http://huggingface.co/google/embeddinggemma-300M), a state-of-the-art text embedding model. The process works in a few simple steps:
438
+
439
+ 1. **Text to Vector**: When you enter a description, EmbeddingGemma converts your text into a numerical representation called an **embedding**. This embedding captures the semantic essence, or the "vibe" of your words.
440
+ 2. **Semantic Color Search**: The application has a pre-defined library of colors, where each color is associated with its own descriptive text and a pre-computed embedding.
441
+ 3. **Finding the Match**: Your input embedding is compared against the entire library of color embeddings to find the closest matches based on a similarity score.
442
+ 4. **Palette Creation**: The colors with the highest similarity scores are selected and presented to you as a complete palette.
443
+
444
+ The Mood Palette Generator is a perfect example of how embeddings can be used for creative applications beyond simple text search.
445
+ """)
446
+
447
+ return demo
448
+
449
+ if __name__ == "__main__":
450
+ # Initialize application components
451
+ generator = MoodPaletteGenerator(config=Config(), color_data=COLOR_DATA)
452
+
453
+ demo = create_ui(generator)
454
+ demo.launch()
455
+
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ sentence-transformers
2
+ git+https://github.com/huggingface/transformers@v4.56.0-Embedding-Gemma