generart / app.py
Equityone's picture
Update app.py
9bd137d verified
raw
history blame
11.2 kB
import gradio as gr
import os
from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageFilter
import requests
import io
import gc
import json
from typing import Tuple, Optional, Dict, Any, List
import logging
from dotenv import load_dotenv
import numpy as np
import cv2
from skimage import exposure
import torch
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
load_dotenv()
# Constantes pour les styles disponibles
STYLE_CATEGORIES = {
"TRADITIONAL": {
"Renaissance": {
"prompt": "renaissance style masterpiece, anatomical precision, detailed texture, chiaroscuro lighting, oil painting technique, museum quality",
"negative_prompt": "modern, abstract, simple, flat, digital art",
"params": {
"guidance_scale": 9.0,
"num_inference_steps": 50,
"resolution": (4096, 4096),
"detail_threshold": 0.95,
}
},
"Impressionnisme": {
"prompt": "impressionist style, loose brushstrokes, natural light, vivid colors, en plein air painting",
"negative_prompt": "sharp details, digital art, modern",
"params": {
"guidance_scale": 7.5,
"num_inference_steps": 40,
"resolution": (2048, 2048),
"brush_simulation": True,
}
},
# ... autres styles traditionnels
},
"DIGITAL": {
"Cyberpunk": {
"prompt": "cyberpunk style, neon lights, volumetric fog, tech noir, high contrast, futuristic city",
"negative_prompt": "natural, vintage, traditional art",
"params": {
"guidance_scale": 8.0,
"neon_intensity": 1.5,
"volumetric_lighting": True,
"resolution": (3840, 2160),
}
},
"Holographique": {
"prompt": "holographic effect, iridescent colors, light refraction, transparent layers, futuristic",
"negative_prompt": "flat, matte, solid colors",
"params": {
"iridescence": 1.0,
"transparency": 0.7,
"resolution": (2560, 1440),
}
},
# ... autres styles numériques
}
}
class TextEffectProcessor:
def __init__(self):
self.effects = {
"Réaliste": self._realistic_text,
"Néon": self._neon_text,
"Holographique": self._holographic_text,
"3D": self._3d_text,
"Vintage": self._vintage_text,
"Graffiti": self._graffiti_text,
"Matrix": self._matrix_text
}
def apply_effect(self, image: Image.Image, text: str, effect: str, position: Tuple[int, int]) -> Image.Image:
if effect in self.effects:
return self.effects[effect](image, text, position)
return self._default_text(image, text, position)
def _realistic_text(self, image: Image.Image, text: str, position: Tuple[int, int]) -> Image.Image:
try:
draw = ImageDraw.Draw(image)
# Chargement d'une police par défaut
font = ImageFont.load_default()
# Effet d'ombre réaliste
shadow_offset = 2
# Dessiner l'ombre
draw.text((position[0] + shadow_offset, position[1] + shadow_offset),
text, font=font, fill=(0, 0, 0, 128))
# Dessiner le texte principal
draw.text(position, text, font=font, fill=(255, 255, 255))
return image
except Exception as e:
logger.error(f"Erreur lors du rendu réaliste: {str(e)}")
return image
def _neon_text(self, image: Image.Image, text: str, position: Tuple[int, int]) -> Image.Image:
try:
# Création d'un calque pour le texte néon
text_layer = Image.new('RGBA', image.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(text_layer)
font = ImageFont.load_default()
# Effet de glow
glow_colors = [(255, 182, 193), (255, 192, 203), (255, 202, 213)]
for i, color in enumerate(glow_colors):
offset = (3 - i) * 2
draw.text((position[0] - offset, position[1] - offset),
text, font=font, fill=color + (150,))
# Texte principal
draw.text(position, text, font=font, fill=(255, 255, 255, 255))
# Fusion des calques
return Image.alpha_composite(image.convert('RGBA'), text_layer)
except Exception as e:
logger.error(f"Erreur lors du rendu néon: {str(e)}")
return image
# ... autres méthodes d'effets de texte
class ImageGenerator:
def __init__(self):
self.api_url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
self.headers = {"Authorization": f"Bearer {os.getenv('HUGGINGFACE_TOKEN')}"}
self.text_processor = TextEffectProcessor()
async def generate_image(self, prompt: str, style_category: str, style_name: str,
text: Optional[str] = None, text_effect: Optional[str] = None,
position: Optional[Tuple[int, int]] = None) -> Tuple[Optional[Image.Image], str]:
try:
# Récupération des paramètres du style
style_info = STYLE_CATEGORIES[style_category][style_name]
# Construction du prompt final
final_prompt = f"{style_info['prompt']}, {prompt}"
# Paramètres de génération
generation_params = {
"inputs": final_prompt,
"negative_prompt": style_info["negative_prompt"],
"num_inference_steps": style_info["params"]["num_inference_steps"],
"guidance_scale": style_info["params"]["guidance_scale"],
}
# Appel à l'API
response = requests.post(
self.api_url,
headers=self.headers,
json=generation_params,
timeout=30
)
if response.status_code != 200:
return None, f"Erreur API: {response.status_code}"
# Traitement de l'image générée
image = Image.open(io.BytesIO(response.content))
# Application des effets de style spécifiques
image = self._apply_style_effects(image, style_info["params"])
# Ajout de texte si demandé
if text and text_effect:
image = self.text_processor.apply_effect(
image, text, text_effect,
position or (image.width//2, image.height//2)
)
return image, "Génération réussie!"
except Exception as e:
logger.error(f"Erreur lors de la génération: {str(e)}")
return None, f"Erreur: {str(e)}"
def _apply_style_effects(self, image: Image.Image, style_params: Dict) -> Image.Image:
"""Application des effets spécifiques au style"""
try:
# Conversion pour traitement
img_array = np.array(image)
# Application des effets selon les paramètres
if style_params.get("neon_intensity"):
img_array = self._apply_neon_effect(img_array, style_params["neon_intensity"])
if style_params.get("volumetric_lighting"):
img_array = self._apply_volumetric_lighting(img_array)
# Reconversion en image PIL
return Image.fromarray(img_array)
except Exception as e:
logger.error(f"Erreur lors de l'application des effets: {str(e)}")
return image
def _apply_neon_effect(self, img_array: np.ndarray, intensity: float) -> np.ndarray:
"""Applique un effet néon à l'image"""
# Conversion en HSV pour manipulation des couleurs
hsv = cv2.cvtColor(img_array, cv2.COLOR_RGB2HSV)
# Augmentation de la saturation
hsv[..., 1] = np.clip(hsv[..., 1] * intensity, 0, 255)
return cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
def _apply_volumetric_lighting(self, img_array: np.ndarray) -> np.ndarray:
"""Ajoute un effet de lumière volumétrique"""
# Création d'un masque de luminosité
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
blur = cv2.GaussianBlur(gray, (0, 0), 15)
return cv2.addWeighted(img_array, 1, cv2.cvtColor(blur, cv2.COLOR_GRAY2RGB), 0.2, 0)
def create_interface():
generator = ImageGenerator()
with gr.Blocks() as demo:
gr.HTML("""<h1 style='text-align: center'>🎨 Equity Art Engine</h1>""")
with gr.Row():
with gr.Column(scale=1):
# Contrôles de génération
prompt = gr.Textbox(label="Description de l'image")
style_category = gr.Dropdown(
choices=list(STYLE_CATEGORIES.keys()),
label="Catégorie de Style"
)
style_name = gr.Dropdown(
label="Style Spécifique"
)
# Mise à jour dynamique des styles disponibles
def update_styles(category):
return gr.Dropdown.update(
choices=list(STYLE_CATEGORIES[category].keys())
)
style_category.change(
update_styles,
inputs=[style_category],
outputs=[style_name]
)
# Contrôles de texte
text_input = gr.Textbox(label="Texte à ajouter (optionnel)")
text_effect = gr.Dropdown(
choices=["Réaliste", "Néon", "Holographique", "3D", "Vintage", "Graffiti", "Matrix"],
label="Effet de texte"
)
with gr.Column(scale=2):
# Zone de résultat
image_output = gr.Image(label="Image générée")
status_output = gr.Textbox(label="Status")
# Bouton de génération
generate_btn = gr.Button("Générer")
# Logique de génération
def generate(prompt, category, style, text, effect):
if not prompt or not category or not style:
return None, "Veuillez remplir tous les champs requis"
image, status = generator.generate_image(
prompt=prompt,
style_category=category,
style_name=style,
text=text if text else None,
text_effect=effect if text else None
)
return image, status
generate_btn.click(
generate,
inputs=[prompt, style_category, style_name, text_input, text_effect],
outputs=[image_output, status_output]
)
return demo
if __name__ == "__main__":
demo = create_interface()
demo.launch()