|
import gradio as gr |
|
import os |
|
from PIL import Image, ImageEnhance, ImageFilter |
|
import requests |
|
import io |
|
import gc |
|
import json |
|
import logging |
|
from dotenv import load_dotenv |
|
from typing import Tuple, Optional, Dict, Any, List |
|
import numpy as np |
|
from dataclasses import dataclass |
|
|
|
|
|
logging.basicConfig( |
|
level=logging.DEBUG, |
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' |
|
) |
|
logger = logging.getLogger("EquityArtisan") |
|
|
|
|
|
load_dotenv() |
|
|
|
@dataclass |
|
class RenderingParams: |
|
"""Paramètres de rendu avancés pour le contrôle fin de la génération""" |
|
steps: int |
|
cfg_scale: float |
|
width: int |
|
height: int |
|
sampler: str |
|
seed: Optional[int] |
|
|
|
@dataclass |
|
class StyleConfig: |
|
"""Configuration complète d'un style artistique""" |
|
prompt_prefix: str |
|
negative_prompt: str |
|
quality_modifiers: List[str] |
|
detail_modifiers: List[str] |
|
composition_rules: List[str] |
|
|
|
|
|
ART_STYLES = { |
|
"Ultra Photoréaliste": StyleConfig( |
|
prompt_prefix="masterpiece, ultra realistic photograph, octane render, unreal engine 5 rendering, ray tracing, global illumination, subsurface scattering, volumetric lighting, 8k uhd, photorealistic", |
|
negative_prompt="cartoon, illustration, painting, drawing, sketch, anime, low quality, blurry, noise, grain, deformed, ugly, disfigured", |
|
quality_modifiers=["hyperdetailed", "photographic", "award winning photography"], |
|
detail_modifiers=["intricate details", "sharp focus", "professional lighting"], |
|
composition_rules=["rule of thirds", "golden ratio", "perfect composition"] |
|
), |
|
"Cinématique": StyleConfig( |
|
prompt_prefix="cinematic scene, anamorphic lens, movie still, dramatic lighting, high production value, professional cinematography, ARRI camera", |
|
negative_prompt="amateur, low budget, poor lighting, webcam, phone camera", |
|
quality_modifiers=["film grain", "color grading", "bokeh effect"], |
|
detail_modifiers=["depth of field", "motion blur", "lens flare"], |
|
composition_rules=["wide aspect ratio", "leading lines", "frame within frame"] |
|
), |
|
|
|
} |
|
|
|
|
|
QUALITY_PRESETS = { |
|
"Standard": RenderingParams( |
|
steps=30, |
|
cfg_scale=7.5, |
|
width=768, |
|
height=768, |
|
sampler="Euler a", |
|
seed=None |
|
), |
|
"Haute Qualité": RenderingParams( |
|
steps=40, |
|
cfg_scale=8.5, |
|
width=1024, |
|
height=1024, |
|
sampler="DPM++ 2M Karras", |
|
seed=None |
|
), |
|
"Ultra Qualité": RenderingParams( |
|
steps=50, |
|
cfg_scale=9.0, |
|
width=1536, |
|
height=1536, |
|
sampler="DPM++ SDE Karras", |
|
seed=None |
|
) |
|
} |
|
|
|
|
|
COMPOSITION_PARAMS = { |
|
"Layouts": { |
|
"Dynamique": { |
|
"description": "Composition dynamique avec lignes de force", |
|
"modifiers": ["dynamic composition", "leading lines", "visual flow"], |
|
"weights": {"foreground": 1.2, "background": 0.8} |
|
}, |
|
"Équilibré": { |
|
"description": "Composition harmonieuse et équilibrée", |
|
"modifiers": ["balanced composition", "symmetrical layout", "visual harmony"], |
|
"weights": {"foreground": 1.0, "background": 1.0} |
|
} |
|
|
|
}, |
|
"Éclairages": { |
|
"Studio": { |
|
"description": "Éclairage professionnel type studio", |
|
"modifiers": ["professional studio lighting", "three-point lighting", "soft boxes"], |
|
"technical_params": {"contrast": 1.2, "highlights": 1.1} |
|
}, |
|
"Naturel": { |
|
"description": "Éclairage naturel avec lumière ambiante", |
|
"modifiers": ["natural lighting", "golden hour", "ambient occlusion"], |
|
"technical_params": {"contrast": 0.9, "shadows": 0.8} |
|
} |
|
|
|
} |
|
} |
|
|
|
class AdvancedImageGenerator: |
|
def __init__(self): |
|
self.API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0" |
|
self.token = os.getenv('HUGGINGFACE_TOKEN') |
|
if not self.token: |
|
raise EnvironmentError("HUGGINGFACE_TOKEN non configuré") |
|
self.headers = {"Authorization": f"Bearer {self.token}"} |
|
|
|
|
|
self.optimization_cache = {} |
|
logger.info("AdvancedImageGenerator initialisé avec paramètres enrichis") |
|
|
|
def _optimize_prompt(self, base_prompt: str, style_config: StyleConfig, params: Dict[str, Any]) -> str: |
|
"""Optimisation avancée du prompt avec analyse sémantique""" |
|
components = [ |
|
style_config.prompt_prefix, |
|
base_prompt |
|
] |
|
|
|
|
|
quality_level = params.get("quality", 50) |
|
if quality_level > 40: |
|
components.extend(style_config.quality_modifiers) |
|
|
|
|
|
components.extend(style_config.composition_rules) |
|
|
|
|
|
if "portrait" in base_prompt.lower(): |
|
components.extend([ |
|
"perfect facial features", |
|
"detailed skin texture", |
|
"professional portrait lighting" |
|
]) |
|
elif "paysage" in base_prompt.lower(): |
|
components.extend([ |
|
"atmospheric perspective", |
|
"perfect weather conditions", |
|
"golden hour lighting" |
|
]) |
|
|
|
return ", ".join(components) |
|
|
|
def _apply_advanced_processing(self, image: Image.Image, params: Dict[str, Any]) -> Image.Image: |
|
"""Application de traitements avancés post-génération""" |
|
try: |
|
|
|
if params.get("detail_enhancement", True): |
|
image = image.filter(ImageFilter.DETAIL) |
|
|
|
|
|
if params.get("local_contrast", True): |
|
enhancer = ImageEnhance.Contrast(image) |
|
image = enhancer.enhance(1.2) |
|
|
|
|
|
if params.get("color_optimization", True): |
|
enhancer = ImageEnhance.Color(image) |
|
image = enhancer.enhance(1.1) |
|
|
|
return image |
|
except Exception as e: |
|
logger.error(f"Erreur lors du post-traitement: {e}") |
|
return image |
|
|
|
def generate(self, params: Dict[str, Any]) -> Tuple[Optional[Image.Image], str]: |
|
"""Génération d'image avec paramètres avancés""" |
|
try: |
|
logger.info(f"Démarrage génération avec paramètres: {json.dumps(params, indent=2)}") |
|
|
|
style_config = ART_STYLES[params["style"]] |
|
prompt = self._optimize_prompt(params["subject"], style_config, params) |
|
|
|
|
|
quality_preset = QUALITY_PRESETS["Ultra Qualité" if params["quality"] > 45 else "Haute Qualité"] |
|
|
|
payload = { |
|
"inputs": prompt, |
|
"parameters": { |
|
"negative_prompt": style_config.negative_prompt, |
|
"num_inference_steps": quality_preset.steps, |
|
"guidance_scale": quality_preset.cfg_scale, |
|
"width": quality_preset.width, |
|
"height": quality_preset.height, |
|
"sampler": quality_preset.sampler |
|
} |
|
} |
|
|
|
response = requests.post( |
|
self.API_URL, |
|
headers=self.headers, |
|
json=payload, |
|
timeout=60 |
|
) |
|
|
|
if response.status_code != 200: |
|
raise Exception(f"Erreur API: {response.status_code}") |
|
|
|
|
|
image = Image.open(io.BytesIO(response.content)) |
|
image = self._apply_advanced_processing(image, params) |
|
|
|
return image, "✨ Génération réussie avec paramètres optimaux!" |
|
|
|
except Exception as e: |
|
logger.exception("Erreur pendant la génération:") |
|
return None, f"⚠️ Erreur: {str(e)}" |
|
|
|
def create_interface(): |
|
"""Création de l'interface utilisateur enrichie""" |
|
logger.info("Initialisation de l'interface avancée") |
|
|
|
css = """ |
|
.container { max-width: 1400px; margin: auto; padding: 20px; } |
|
.welcome { |
|
text-align: center; |
|
margin: 20px 0; |
|
padding: 30px; |
|
background: linear-gradient(135deg, #1e293b, #334155); |
|
border-radius: 15px; |
|
color: white; |
|
} |
|
.controls-group { |
|
background: #2d3748; |
|
padding: 20px; |
|
border-radius: 10px; |
|
margin: 15px 0; |
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
} |
|
""" |
|
|
|
generator = AdvancedImageGenerator() |
|
|
|
with gr.Blocks(css=css) as app: |
|
gr.HTML(""" |
|
<div class="welcome"> |
|
<h1>🎨 Equity Artisan 4.0 Pro</h1> |
|
<p>Studio de Création Visuelle Avancé</p> |
|
</div> |
|
""") |
|
|
|
with gr.Column(elem_classes="container"): |
|
|
|
with gr.Group(elem_classes="controls-group"): |
|
gr.Markdown("### 🎭 Style et Composition") |
|
with gr.Row(): |
|
style = gr.Dropdown( |
|
choices=list(ART_STYLES.keys()), |
|
value="Ultra Photoréaliste", |
|
label="Style Artistique" |
|
) |
|
quality_preset = gr.Dropdown( |
|
choices=list(QUALITY_PRESETS.keys()), |
|
value="Haute Qualité", |
|
label="Preset de Qualité" |
|
) |
|
|
|
|
|
with gr.Group(elem_classes="controls-group"): |
|
gr.Markdown("### 🎨 Paramètres Créatifs") |
|
with gr.Row(): |
|
subject = gr.Textbox( |
|
label="Description", |
|
placeholder="Décrivez votre vision en détail...", |
|
lines=3 |
|
) |
|
composition = gr.Dropdown( |
|
choices=list(COMPOSITION_PARAMS["Layouts"].keys()), |
|
value="Dynamique", |
|
label="Type de Composition" |
|
) |
|
|
|
|
|
with gr.Group(elem_classes="controls-group"): |
|
gr.Markdown("### ⚙️ Paramètres Techniques") |
|
with gr.Row(): |
|
quality = gr.Slider( |
|
minimum=30, |
|
maximum=100, |
|
value=75, |
|
label="Niveau de Qualité" |
|
) |
|
detail_level = gr.Slider( |
|
minimum=1, |
|
maximum=10, |
|
value=8, |
|
label="Niveau de Détail" |
|
) |
|
|
|
|
|
with gr.Row(): |
|
generate_btn = gr.Button("✨ Générer", variant="primary", size="lg") |
|
clear_btn = gr.Button("🗑️ Réinitialiser", variant="secondary") |
|
|
|
|
|
image_output = gr.Image( |
|
label="Aperçu", |
|
elem_classes="result-image" |
|
) |
|
status = gr.Textbox( |
|
label="Statut", |
|
interactive=False |
|
) |
|
|
|
|
|
def generate_image(*args): |
|
params = { |
|
"style": args[0], |
|
"quality_preset": args[1], |
|
"subject": args[2], |
|
"composition": args[3], |
|
"quality": args[4], |
|
"detail_level": args[5] |
|
} |
|
return generator.generate(params) |
|
|
|
|
|
generate_btn.click( |
|
generate_image, |
|
inputs=[ |
|
style, |
|
quality_preset, |
|
subject, |
|
composition, |
|
quality, |
|
detail_level |
|
], |
|
outputs=[image_output, status] |
|
) |
|
|
|
clear_btn.click( |
|
lambda: (None, "🔄 Interface réinitialisée"), |
|
outputs=[image_output, status] |
|
) |
|
|
|
logger.info("Interface créée avec succès") |
|
return app |
|
|
|
if __name__ == "__main__": |
|
app = create_interface() |
|
app.launch() |