Equityone commited on
Commit
8527b54
·
verified ·
1 Parent(s): f03e082

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +122 -156
app.py CHANGED
@@ -12,240 +12,206 @@ import numpy as np
12
  import cv2
13
  from skimage import exposure
14
  import torch
 
15
 
16
  logging.basicConfig(level=logging.DEBUG)
17
  logger = logging.getLogger(__name__)
18
  load_dotenv()
19
 
20
- def load_art_styles():
21
- return {
22
- "Styles Traditionnels": {
23
- "Renaissance": {"prompt": "renaissance masterpiece, anatomical precision, detailed texture, chiaroscuro lighting", "params": {"resolution": (4096, 4096), "detail_level": 0.95}},
24
- "Impressionnisme": {"prompt": "impressionist style painting, visible brushstrokes, natural light", "params": {"resolution": (3072, 3072), "noise_level": 0.3}},
25
- "Surréalisme": {"prompt": "surrealist dreamlike scene, symbolic elements, subconscious imagery", "params": {"randomization": 0.4}},
26
- "Cubisme": {"prompt": "cubist style, geometric forms, multiple perspectives", "params": {"geometric_strength": 0.8}}
27
  },
28
- "Rendus Numériques": {
29
- "Synthwave": {"prompt": "synthwave aesthetic, neon grid, retro-futuristic, vibrant", "params": {"saturation": 1.8, "neon": True}},
30
- "Cyberpunk": {"prompt": "cyberpunk style, neon-lit, high-tech, volumetric lighting", "params": {"volumetric": True, "neon": True}},
31
- "Sci-Fi": {"prompt": "sci-fi environment, futuristic technology, advanced architecture", "params": {"tech_level": 0.9}}
32
  },
33
- "Photographie": {
34
- "HDR": {"prompt": "HDR photography, extreme dynamic range, detailed shadows and highlights", "params": {"hdr_strength": 1.5}},
35
- "Macro": {"prompt": "macro photography, extreme close-up, fine details", "params": {"detail_scale": 0.5}},
36
- "Portrait": {"prompt": "professional portrait photography, studio lighting, bokeh", "params": {"bokeh": 0.7}},
37
- "Vintage": {"prompt": "vintage photography, aged effect, retro colors", "params": {"grain": 0.4}}
38
  },
39
- "Illustration": {
40
- "Aquarelle": {"prompt": "watercolor illustration, fluid transparency, soft edges", "params": {"transparency": 0.6}},
41
- "Encre": {"prompt": "ink drawing, bold strokes, high contrast", "params": {"contrast": 1.4}},
42
- "Huile": {"prompt": "oil painting, thick impasto, rich colors", "params": {"texture": 0.8}}
43
- },
44
- "Photoréalisme": {
45
- "Nature_Morte": {"prompt": "photorealistic still life, extreme detail, perfect lighting", "params": {"detail_level": 0.98}},
46
- "Paysage": {"prompt": "photorealistic landscape, natural lighting, atmospheric", "params": {"atmosphere": 0.7}}
47
- },
48
- "Fantasy": {
49
- "Fantasy": {"prompt": "fantasy art, magical atmosphere, mythical elements", "params": {"magic_effect": 0.8}},
50
- "Dark_Fantasy": {"prompt": "dark fantasy, gothic elements, mysterious atmosphere", "params": {"darkness": 0.7}},
51
- "Steampunk": {"prompt": "steampunk style, brass and copper, mechanical elements", "params": {"mechanical": 0.9}}
52
- },
53
- "Abstrait": {
54
- "Holographique": {"prompt": "holographic effect, iridescent colors, light refraction", "params": {"iridescence": 0.8}},
55
- "Fractal": {"prompt": "fractal art, recursive patterns, mathematical beauty", "params": {"complexity": 0.9}}
56
  },
57
- "Graphisme": {
58
- "Flat": {"prompt": "flat design, minimal shapes, solid colors", "params": {"simplification": 0.8}},
59
- "Material": {"prompt": "material design, subtle shadows, layered elements", "params": {"layers": 0.6}},
60
- "Isométrique": {"prompt": "isometric design, geometric precision, clean lines", "params": {"precision": 0.9}}
 
 
 
 
 
 
 
61
  },
62
- "Gaming": {
63
- "Pixel_Art": {"prompt": "pixel art style, retro gaming aesthetic, limited palette", "params": {"pixelation": 0.7}},
64
- "Cel_Shading": {"prompt": "cel shaded style, anime-like, bold outlines", "params": {"outline": 0.8}}
 
65
  }
66
  }
 
67
 
68
  class ImageProcessor:
69
  def __init__(self):
70
- self.effects = {
71
- "neon": self._apply_neon,
72
- "bokeh": self._apply_bokeh,
73
- "grain": self._apply_grain,
74
- "hdr": self._apply_hdr,
75
- "pixelation": self._apply_pixelation
76
- }
77
-
78
- def process_image(self, image: Image.Image, style_params: Dict) -> Image.Image:
79
  try:
 
80
  img_array = np.array(image)
81
 
82
- for effect, value in style_params.items():
83
- if effect in self.effects and value:
84
- img_array = self.effects[effect](img_array, value)
85
-
 
 
 
 
 
 
 
 
 
86
  return Image.fromarray(img_array)
 
87
  except Exception as e:
88
  logger.error(f"Erreur traitement: {str(e)}")
89
  return image
90
-
91
- def _apply_neon(self, image: np.ndarray, strength: float) -> np.ndarray:
92
  hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
93
- hsv[..., 1] = np.clip(hsv[..., 1] * strength, 0, 255)
94
- glow = cv2.GaussianBlur(cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB), (0, 0), 15)
95
- return cv2.addWeighted(image, 1, glow, 0.5, 0)
96
-
 
 
 
 
 
 
 
97
  def _apply_bokeh(self, image: np.ndarray, strength: float) -> np.ndarray:
98
  blur = cv2.GaussianBlur(image, (0, 0), int(30 * strength))
99
  mask = np.random.random(image.shape[:2]) > 0.5
100
  result = image.copy()
101
  result[mask] = blur[mask]
102
  return result
103
-
104
- def _apply_grain(self, image: np.ndarray, strength: float) -> np.ndarray:
105
- noise = np.random.normal(0, strength * 50, image.shape).astype(np.uint8)
106
- return np.clip(image + noise, 0, 255)
107
-
108
  def _apply_hdr(self, image: np.ndarray, strength: float) -> np.ndarray:
109
  return exposure.adjust_gamma(image, 1.0 / strength)
110
 
111
- def _apply_pixelation(self, image: np.ndarray, strength: float) -> np.ndarray:
112
- h, w = image.shape[:2]
113
- size = int(max(h, w) * (1 - strength))
114
- small = cv2.resize(image, (size, size), interpolation=cv2.INTER_LINEAR)
115
- return cv2.resize(small, (w, h), interpolation=cv2.INTER_NEAREST)
116
-
117
  class ImageGenerator:
118
  def __init__(self):
119
  self.processor = ImageProcessor()
120
  self.api_url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
121
- self.headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
122
- self.styles = load_art_styles()
123
-
124
- async def generate(self, prompt: str, style_category: str, style_name: str) -> Tuple[Optional[Image.Image], str]:
 
 
125
  try:
126
- style_info = self.styles[style_category][style_name]
 
 
 
127
  final_prompt = f"{prompt}, {style_info['prompt']}"
128
 
 
 
 
 
 
 
 
 
 
 
 
129
  response = requests.post(
130
  self.api_url,
131
  headers=self.headers,
132
- json={"inputs": final_prompt},
133
  timeout=30
134
  )
135
 
136
  if response.status_code != 200:
137
  return None, f"Erreur API: {response.status_code}"
138
-
 
139
  image = Image.open(io.BytesIO(response.content))
140
- processed = self.processor.process_image(image, style_info['params'])
141
 
142
  return processed, "✨ Génération réussie!"
 
143
  except Exception as e:
144
- logger.error(f"Erreur: {str(e)}")
145
  return None, f"Erreur: {str(e)}"
146
  finally:
147
  gc.collect()
148
- def create_interface():
149
- generator = ImageGenerator()
150
 
 
 
 
151
  with gr.Blocks() as demo:
152
  gr.HTML("""
153
  <div style='text-align: center; margin-bottom: 1rem'>
154
  <h1>🎨 Equity Art Engine</h1>
155
- <p>Générateur d'Images Avancé avec Styles Artistiques</p>
156
  </div>
157
  """)
158
 
159
  with gr.Row():
160
- with gr.Column(scale=1):
161
- prompt = gr.Textbox(
162
- label="Description",
163
- placeholder="Décrivez votre image..."
164
- )
165
 
166
  style_category = gr.Dropdown(
167
- choices=list(generator.styles.keys()),
168
- label="Catégorie de Style",
169
- value="Styles Traditionnels"
170
  )
171
 
172
- style_name = gr.Dropdown(
173
- label="Style Spécifique"
174
- )
175
 
 
176
  def update_styles(category):
177
- return gr.Dropdown.update(
178
- choices=list(generator.styles[category].keys()) if category else []
179
- )
180
-
181
- style_category.change(
182
- update_styles,
183
- inputs=style_category,
184
- outputs=style_name
185
- )
186
-
187
- advanced_params = gr.Checkbox(
188
- label="Paramètres avancés",
189
- value=False
190
- )
191
-
192
- with gr.Column(visible=False) as advanced_options:
193
- quality = gr.Slider(
194
- minimum=1,
195
- maximum=10,
196
- value=7,
197
- step=1,
198
- label="Qualité"
199
- )
200
 
201
- seed = gr.Number(
202
- label="Seed (-1 pour aléatoire)",
203
- value=-1
204
- )
205
-
206
- def toggle_advanced(show):
207
- return gr.update(visible=show)
208
-
209
- advanced_params.change(
210
- toggle_advanced,
211
- inputs=advanced_params,
212
- outputs=advanced_options
213
- )
214
 
215
- generate_btn = gr.Button("✨ Générer", variant="primary")
216
 
217
- with gr.Column(scale=2):
218
- output_image = gr.Image(label="Image Générée")
219
- status = gr.Textbox(label="Status")
220
 
221
- def generate_image(prompt, category, style, use_advanced, quality, seed):
222
  if not all([prompt, category, style]):
223
  return None, "⚠️ Veuillez remplir tous les champs requis"
224
 
225
- params = {
226
- "quality": quality if use_advanced else 7,
227
- "seed": seed if use_advanced and seed != -1 else None
228
- }
229
-
230
- image, status_msg = generator.generate(
231
- prompt=prompt,
232
- style_category=category,
233
- style_name=style
234
- )
235
-
236
- return image, status_msg
237
 
238
  generate_btn.click(
239
- generate_image,
240
- inputs=[
241
- prompt,
242
- style_category,
243
- style_name,
244
- advanced_params,
245
- quality,
246
- seed
247
- ],
248
- outputs=[output_image, status]
249
  )
250
 
251
  return demo
 
12
  import cv2
13
  from skimage import exposure
14
  import torch
15
+ import asyncio
16
 
17
  logging.basicConfig(level=logging.DEBUG)
18
  logger = logging.getLogger(__name__)
19
  load_dotenv()
20
 
21
+ STYLES = {
22
+ "Styles Traditionnels": {
23
+ "Renaissance": {
24
+ "prompt": "renaissance masterpiece, anatomical precision, detailed texture, chiaroscuro lighting, oil painting technique, museum quality",
25
+ "negative_prompt": "modern, abstract, simple, digital",
26
+ "params": {"resolution": 4096, "detail_level": 0.95}
 
27
  },
28
+ "Impressionnisme": {
29
+ "prompt": "impressionist style, visible brushstrokes, natural light, plein air painting",
30
+ "negative_prompt": "sharp, digital, modern",
31
+ "params": {"resolution": 3072, "noise_level": 0.3}
32
  },
33
+ "Surréalisme": {
34
+ "prompt": "surrealist dreamlike scene, symbolic imagery, unconscious imagination",
35
+ "negative_prompt": "realistic, ordinary",
36
+ "params": {"resolution": 3072, "randomization": 0.4}
 
37
  },
38
+ "Cubisme": {
39
+ "prompt": "cubist style, geometric forms, multiple perspectives, abstract interpretation",
40
+ "negative_prompt": "realistic, photographic",
41
+ "params": {"resolution": 2048, "geometric_strength": 0.8}
42
+ }
43
+ },
44
+ "Rendus Numériques": {
45
+ "Synthwave": {
46
+ "prompt": "synthwave aesthetic, neon grid, retro-futuristic, dramatic lighting, vibrant colors",
47
+ "negative_prompt": "natural, realistic, muted colors",
48
+ "params": {"saturation": 1.8, "neon_effect": True}
 
 
 
 
 
 
49
  },
50
+ "Cyberpunk": {
51
+ "prompt": "cyberpunk style, neon-lit cityscape, high-tech, volumetric fog, futuristic",
52
+ "negative_prompt": "natural, vintage, rural",
53
+ "params": {"volumetric": True, "neon_intensity": 1.5}
54
+ }
55
+ },
56
+ "Photographie": {
57
+ "HDR": {
58
+ "prompt": "HDR photography, extreme dynamic range, rich details in shadows and highlights",
59
+ "negative_prompt": "flat lighting, low contrast",
60
+ "params": {"hdr_strength": 1.5, "resolution": 4096}
61
  },
62
+ "Portrait Studio": {
63
+ "prompt": "professional studio portrait, perfect lighting, bokeh effect, sharp focus",
64
+ "negative_prompt": "blurry, noisy",
65
+ "params": {"bokeh": 0.7, "sharpness": 1.4}
66
  }
67
  }
68
+ }
69
 
70
  class ImageProcessor:
71
  def __init__(self):
72
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
73
+
74
+ def process_image(self, image: Image.Image, params: Dict) -> Image.Image:
 
 
 
 
 
 
75
  try:
76
+ # Conversion en array numpy
77
  img_array = np.array(image)
78
 
79
+ # Application des effets selon les paramètres
80
+ if params.get("neon_effect"):
81
+ img_array = self._apply_neon_effect(img_array)
82
+
83
+ if params.get("volumetric"):
84
+ img_array = self._apply_volumetric(img_array)
85
+
86
+ if params.get("bokeh"):
87
+ img_array = self._apply_bokeh(img_array, params["bokeh"])
88
+
89
+ if params.get("hdr_strength"):
90
+ img_array = self._apply_hdr(img_array, params["hdr_strength"])
91
+
92
  return Image.fromarray(img_array)
93
+
94
  except Exception as e:
95
  logger.error(f"Erreur traitement: {str(e)}")
96
  return image
97
+
98
+ def _apply_neon_effect(self, image: np.ndarray) -> np.ndarray:
99
  hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
100
+ hsv[..., 1] = np.clip(hsv[..., 1] * 1.5, 0, 255) # Boost saturation
101
+ rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
102
+ # Ajout de glow
103
+ blur = cv2.GaussianBlur(rgb, (0, 0), 15)
104
+ return cv2.addWeighted(rgb, 1, blur, 0.5, 0)
105
+
106
+ def _apply_volumetric(self, image: np.ndarray) -> np.ndarray:
107
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
108
+ fog = cv2.GaussianBlur(gray, (0, 0), 20)
109
+ return cv2.addWeighted(image, 1, cv2.cvtColor(fog, cv2.COLOR_GRAY2RGB), 0.2, 0)
110
+
111
  def _apply_bokeh(self, image: np.ndarray, strength: float) -> np.ndarray:
112
  blur = cv2.GaussianBlur(image, (0, 0), int(30 * strength))
113
  mask = np.random.random(image.shape[:2]) > 0.5
114
  result = image.copy()
115
  result[mask] = blur[mask]
116
  return result
117
+
 
 
 
 
118
  def _apply_hdr(self, image: np.ndarray, strength: float) -> np.ndarray:
119
  return exposure.adjust_gamma(image, 1.0 / strength)
120
 
 
 
 
 
 
 
121
  class ImageGenerator:
122
  def __init__(self):
123
  self.processor = ImageProcessor()
124
  self.api_url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
125
+ token = os.getenv('HUGGINGFACE_TOKEN')
126
+ if not token:
127
+ raise ValueError("HUGGINGFACE_TOKEN non trouvé dans les variables d'environnement!")
128
+ self.headers = {"Authorization": f"Bearer {token}"}
129
+
130
+ def generate(self, prompt: str, style_category: str, style_name: str) -> Tuple[Optional[Image.Image], str]:
131
  try:
132
+ # Récupération du style
133
+ style_info = STYLES[style_category][style_name]
134
+
135
+ # Construction du prompt final
136
  final_prompt = f"{prompt}, {style_info['prompt']}"
137
 
138
+ # Paramètres de génération
139
+ params = {
140
+ "inputs": final_prompt,
141
+ "negative_prompt": style_info["negative_prompt"],
142
+ "num_inference_steps": 50,
143
+ "guidance_scale": 7.5,
144
+ "width": style_info["params"].get("resolution", 1024),
145
+ "height": style_info["params"].get("resolution", 1024),
146
+ }
147
+
148
+ # Appel API
149
  response = requests.post(
150
  self.api_url,
151
  headers=self.headers,
152
+ json=params,
153
  timeout=30
154
  )
155
 
156
  if response.status_code != 200:
157
  return None, f"Erreur API: {response.status_code}"
158
+
159
+ # Traitement de l'image
160
  image = Image.open(io.BytesIO(response.content))
161
+ processed = self.processor.process_image(image, style_info["params"])
162
 
163
  return processed, "✨ Génération réussie!"
164
+
165
  except Exception as e:
166
+ logger.error(f"Erreur génération: {str(e)}")
167
  return None, f"Erreur: {str(e)}"
168
  finally:
169
  gc.collect()
 
 
170
 
171
+ def create_interface():
172
+ generator = ImageGenerator()
173
+
174
  with gr.Blocks() as demo:
175
  gr.HTML("""
176
  <div style='text-align: center; margin-bottom: 1rem'>
177
  <h1>🎨 Equity Art Engine</h1>
178
+ <p>Générateur d'Images avec Styles Artistiques</p>
179
  </div>
180
  """)
181
 
182
  with gr.Row():
183
+ with gr.Column():
184
+ prompt = gr.Textbox(label="Description", placeholder="Décrivez votre image...")
 
 
 
185
 
186
  style_category = gr.Dropdown(
187
+ choices=list(STYLES.keys()),
188
+ label="Catégorie de Style"
 
189
  )
190
 
191
+ style_name = gr.Dropdown(label="Style Spécifique")
 
 
192
 
193
+ # Mise à jour dynamique des styles
194
  def update_styles(category):
195
+ return gr.Dropdown.update(choices=list(STYLES[category].keys()) if category else [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ style_category.change(update_styles, inputs=style_category, outputs=style_name)
 
 
 
 
 
 
 
 
 
 
 
 
198
 
199
+ generate_btn = gr.Button("✨ Générer")
200
 
201
+ with gr.Column():
202
+ image_output = gr.Image(label="Résultat")
203
+ status_output = gr.Textbox(label="Status")
204
 
205
+ def generate(prompt, category, style):
206
  if not all([prompt, category, style]):
207
  return None, "⚠️ Veuillez remplir tous les champs requis"
208
 
209
+ return generator.generate(prompt, category, style)
 
 
 
 
 
 
 
 
 
 
 
210
 
211
  generate_btn.click(
212
+ generate,
213
+ inputs=[prompt, style_category, style_name],
214
+ outputs=[image_output, status_output]
 
 
 
 
 
 
 
215
  )
216
 
217
  return demo