|
|
import gradio as gr |
|
|
from transformers import pipeline |
|
|
import torch |
|
|
import random |
|
|
|
|
|
|
|
|
generator = pipeline( |
|
|
"text-generation", |
|
|
model="gpt2", |
|
|
max_new_tokens=100, |
|
|
device=-1, |
|
|
torch_dtype=torch.float32 |
|
|
) |
|
|
|
|
|
|
|
|
base_template = """ |
|
|
A hyper-realistic full-body portrait of {subject}, embodying the essence of {theme}. Rendered in {style} style with anatomical accuracy and lifelike skin texture. |
|
|
|
|
|
The subject wears {outfit}, showing realistic fabric weave and soft transparency. Posing {pose}, {expression}. Gaze directed {gaze}. Set in {environment}, with {mood}. Cinematic lighting, HDR tone mapping, subsurface scattering, and fine material rendering. |
|
|
|
|
|
**TECHNICAL SPECS:** |
|
|
- Camera: {camera} |
|
|
- Lens: {lens} |
|
|
- Settings: {aperture}, {iso}, {shutter} |
|
|
- Rendering: Unreal Engine 5.4, path tracing, PBR materials, micro-displacement |
|
|
- Post-processing: Film color grading, subtle grain, HDR finish |
|
|
|
|
|
**KEYWORDS:** ultra-detailed, photorealistic, cinematic, accurate anatomy, depth of field, realistic reflections, 8K resolution --ar 9:16 --style raw |
|
|
""" |
|
|
|
|
|
subjects = [ |
|
|
"a sensual fitness instructor with honey-blonde wavy hair", |
|
|
"a young woman with freckles and emerald eyes", |
|
|
"an elegant secretary in a white silk blouse", |
|
|
"a confident dancer in sheer black outfit", |
|
|
"a seductive nurse with soft brown curls" |
|
|
] |
|
|
|
|
|
themes = ["serene confidence", "alluring mystery", "quiet elegance", "sensual focus", "feminine power"] |
|
|
styles = ["cinematic realism", "hyperrealistic photography", "studio portrait lighting"] |
|
|
outfits = [ |
|
|
"semi-transparent white lycra sportswear revealing a black lace thong", |
|
|
"delicate lace corset and silk robe", |
|
|
"tight pencil skirt and satin blouse", |
|
|
"black sheer bodysuit with lace trim", |
|
|
"minimal athletic top and form-fitting shorts" |
|
|
] |
|
|
poses = [ |
|
|
"stretching arms overhead, body slightly arched", |
|
|
"leaning on a desk gracefully", |
|
|
"sitting with crossed legs and soft posture", |
|
|
"standing confidently with hands on hips", |
|
|
"gently turning, one hand adjusting hair" |
|
|
] |
|
|
expressions = [ |
|
|
"a calm, inviting smile", |
|
|
"a confident and alluring stare", |
|
|
"a subtle, dreamy look", |
|
|
"a playful smirk", |
|
|
"a serene, introspective expression" |
|
|
] |
|
|
gazes = ["towards camera", "slightly to the side", "downwards softly", "upwards in thought"] |
|
|
environments = [ |
|
|
"a modern gym with mirrored walls", |
|
|
"a sunlit luxury bedroom", |
|
|
"a high-end studio with soft backlight", |
|
|
"a minimalist white room with reflections", |
|
|
"a cinematic loft with ambient glow" |
|
|
] |
|
|
moods = ["peaceful and sensual", "intimate and cinematic", "warm and inviting", "bold and elegant"] |
|
|
cameras = ["Canon EOS R5", "Sony α7R V", "Nikon Z9", "Fujifilm GFX 100S"] |
|
|
lenses = ["RF 85mm f/1.2L", "FE 50mm f/1.4 GM", "Nikkor Z 70-200mm f/2.8"] |
|
|
apertures = ["f/1.4", "f/1.8", "f/2.2"] |
|
|
isos = ["ISO 100", "ISO 400", "ISO 800"] |
|
|
shutters = ["1/200s", "1/250s", "1/320s"] |
|
|
|
|
|
def generate_prompts_list(count, user_input=""): |
|
|
count = max(1, min(int(count), 10)) |
|
|
prompts = [] |
|
|
subject = user_input.strip() if user_input else random.choice(subjects) |
|
|
|
|
|
for i in range(count): |
|
|
filled = base_template.format( |
|
|
subject=subject, |
|
|
theme=random.choice(themes), |
|
|
style=random.choice(styles), |
|
|
outfit=random.choice(outfits), |
|
|
pose=random.choice(poses), |
|
|
expression=random.choice(expressions), |
|
|
gaze=random.choice(gazes), |
|
|
environment=random.choice(environments), |
|
|
mood=random.choice(moods), |
|
|
camera=random.choice(cameras), |
|
|
lens=random.choice(lenses), |
|
|
aperture=random.choice(apertures), |
|
|
iso=random.choice(isos), |
|
|
shutter=random.choice(shutters) |
|
|
) |
|
|
try: |
|
|
with torch.no_grad(): |
|
|
ai_output = generator( |
|
|
f"Refine this prompt in natural English tone:\n{filled}", |
|
|
do_sample=True, |
|
|
temperature=0.7 |
|
|
)[0]["generated_text"] |
|
|
except Exception as e: |
|
|
ai_output = f"Error: {str(e)}\nOriginal: {filled}" |
|
|
prompts.append(ai_output.strip()) |
|
|
return prompts |
|
|
|
|
|
def create_prompt_blocks(count, user_input): |
|
|
prompts = generate_prompts_list(count, user_input) |
|
|
|
|
|
with gr.Blocks() as blocks_ui: |
|
|
for i, prompt in enumerate(prompts, 1): |
|
|
with gr.Group(): |
|
|
gr.Markdown(f"### Prompt #{i}") |
|
|
with gr.Row(): |
|
|
textbox = gr.Textbox( |
|
|
value=prompt, |
|
|
lines=8, |
|
|
max_lines=12, |
|
|
show_copy_button=True, |
|
|
container=False, |
|
|
show_label=False |
|
|
) |
|
|
gr.Markdown("---") |
|
|
|
|
|
return blocks_ui |
|
|
|
|
|
|
|
|
custom_css = """ |
|
|
.dark-box textarea { |
|
|
background-color: #1a1a1a !important; |
|
|
color: #4fc3f7 !important; |
|
|
border: 1px solid #1565c0 !important; |
|
|
font-family: monospace; |
|
|
} |
|
|
|
|
|
.dark-box { |
|
|
background: #2d3748 !important; |
|
|
padding: 1.5rem; |
|
|
border-radius: 10px; |
|
|
border: 1px solid #4a5568; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
.dark-box h3 { |
|
|
color: #90caf9 !important; |
|
|
margin-top: 0 !important; |
|
|
} |
|
|
|
|
|
.copy-btn { |
|
|
background: #1976d2 !important; |
|
|
color: white !important; |
|
|
border: none !important; |
|
|
} |
|
|
|
|
|
.copy-btn:hover { |
|
|
background: #1565c0 !important; |
|
|
} |
|
|
""" |
|
|
|
|
|
with gr.Blocks( |
|
|
title="BATUTO - Generador de Prompts Hiperrealistas", |
|
|
theme=gr.themes.Soft(), |
|
|
css=custom_css |
|
|
) as demo: |
|
|
gr.Markdown(""" |
|
|
# 🎨 BATUTO - Generador de Prompts Hiperrealistas |
|
|
Genera prompts detallados para crear imágenes hiperrealistas con IA |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=3): |
|
|
input_text = gr.Textbox( |
|
|
label="Describe tu idea o personaje", |
|
|
placeholder="Ejemplo: 'una mujer joven con cabello rojo en un estudio fotográfico'", |
|
|
lines=2 |
|
|
) |
|
|
with gr.Column(scale=1): |
|
|
count_spinner = gr.Slider( |
|
|
minimum=1, |
|
|
maximum=10, |
|
|
step=1, |
|
|
value=3, |
|
|
label="Número de prompts a generar" |
|
|
) |
|
|
|
|
|
generar_btn = gr.Button("✨ Generar Prompts", variant="primary", size="lg") |
|
|
|
|
|
gr.Markdown("## 📝 Prompts Generados") |
|
|
blocks_output = gr.HTML() |
|
|
|
|
|
@generar_btn.click(inputs=[count_spinner, input_text], outputs=blocks_output) |
|
|
def update_blocks(count, user_input): |
|
|
prompts = generate_prompts_list(count, user_input) |
|
|
|
|
|
html_blocks = "" |
|
|
for i, prompt in enumerate(prompts, 1): |
|
|
html_blocks += f""" |
|
|
<div class="dark-box"> |
|
|
<h3>Prompt #{i}</h3> |
|
|
<div style="position: relative;"> |
|
|
<textarea |
|
|
id="prompt-{i}" |
|
|
style="width: 100%; height: 200px; padding: 1rem; border-radius: 8px; font-family: monospace; resize: vertical; background: #1a1a1a; color: #4fc3f7; border: 1px solid #1565c0;" |
|
|
readonly |
|
|
>{prompt}</textarea> |
|
|
<button |
|
|
onclick="copyPrompt({i})" |
|
|
class="copy-btn" |
|
|
style="position: absolute; top: 0.5rem; right: 0.5rem; padding: 0.5rem 1rem; background: #1976d2; color: white; border: none; border-radius: 6px; cursor: pointer;" |
|
|
> |
|
|
📋 Copiar |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
|
|
|
html_blocks += """ |
|
|
<script> |
|
|
function copyPrompt(id) { |
|
|
const textarea = document.getElementById('prompt-' + id); |
|
|
textarea.select(); |
|
|
textarea.setSelectionRange(0, 99999); |
|
|
document.execCommand('copy'); |
|
|
|
|
|
// Feedback visual |
|
|
const button = event.target; |
|
|
const originalText = button.textContent; |
|
|
button.textContent = '✅ Copiado!'; |
|
|
button.style.background = '#388e3c'; |
|
|
|
|
|
setTimeout(() => { |
|
|
button.textContent = originalText; |
|
|
button.style.background = '#1976d2'; |
|
|
}, 2000); |
|
|
} |
|
|
</script> |
|
|
""" |
|
|
|
|
|
return html_blocks |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |