Cómo crear un generador de imágenes con IA en Python (paso a paso)
Escribe un script de Python que envíe un prompt de texto a un modelo de imágenes, espere a que termine el trabajo y guarde la imagen. Constrúyelo en cuatro pasos y luego añade una línea de comandos, lotes con hilos, una ruta de Flask y soluciones a los errores con los que te vas a topar.
La mayoría de los tutoriales sobre imágenes con IA se quedan en "pega un prompt en una web". Eso funciona para una imagen, pero se rompe en cuanto necesitas cincuenta fotos de producto, un lote nocturno de miniaturas para el blog o una pequeña aplicación en la que los visitantes escriben una frase y reciben una fotografía. Para eso necesitas código, y la buena noticia es que un generador de imágenes funcional en Python cabe en unas 60 líneas.
Este tutorial lo construye en cuatro pasos sencillos: enviar un prompt a un modelo alojado, esperar a que termine el trabajo, descargar el archivo y, después, envolverlo todo en una herramienta de línea de comandos que ejecute lotes. Cada paso termina con una comprobación rápida, para que sepas que funciona antes de avanzar.
💡 Respuesta rápida: un generador de imágenes con IA en Python es un script que envía un prompt de texto a un modelo por HTTP, consulta el estado hasta que la imagen está lista y después guarda el archivo. El modelo se ejecuta en la GPU de otra persona. Tu código necesita requests, un token secreto y un bucle.
Antes de escribir una sola línea
Necesitas tres cosas: Python 3.10 o una versión más reciente, una terminal y un token secreto para el servicio de imágenes. El código de abajo se comunica con la API de PicassoIA por HTTP sencillo, así que no hay que instalar ningún controlador de GPU ni descargar archivos de modelo.
Prepara el proyecto
Crea una carpeta, un entorno virtual e instala un solo paquete:
A continuación, crea un token en la página de la API y guárdalo en una variable de entorno en lugar de en el archivo de código. Una cuenta puede tener hasta dos tokens a la vez.
En PowerShell de Windows, la misma línea es $env:PICASSOIA_API_TOKEN = "pia_sk_your_token_here".
⚠️ Nunca pegues el token en tu script. Lo que se confirma en git queda en el historial, incluso si lo borras después. Una variable de entorno mantiene el secreto fuera del repositorio.
Elige primero un modelo
El modelo decide cómo se ven tus imágenes y con qué rapidez llegan. El script de este tutorial llama a picassoia/picassoia-image a través de la API. Los modelos de la tabla siguiente están disponibles en el navegador de PicassoIA, lo que los hace útiles para probar prompts antes de integrar nada en el código.
Un modelo de difusión parte de ruido aleatorio y lo elimina en muchas pasadas pequeñas, guiado por tu prompt en cada una. Cada pasada es un cálculo pesado en una red neuronal grande. Por eso la generación de imágenes necesita una GPU potente, y por eso la primera decisión de diseño de tu proyecto en Python es dónde vive esa GPU.
GPU local o API alojada. Ejecutar un modelo en tu propia máquina te da control total, pero también implica un conjunto de controladores, decenas de gigabytes de pesos y una tarjeta con mucha memoria. Una API alojada cambia parte de ese control por una configuración que se mide en minutos.
Factor
GPU local
API alojada
Tiempo de configuración
Horas
Minutos
Hardware
Tarjeta gráfica reciente con 12 GB o más de memoria
Cualquier equipo portátil
Elección de modelo
El que descargues
Lo que ofrezca el servicio
Escalado
Comprar otra tarjeta
Aumentar tus peticiones simultáneas
Dependencias de Python
Pesadas
Solo requests
Por qué los trabajos son asíncronos. Generar una imagen tarda entre menos de un segundo y cerca de un minuto, según el modelo. Mantener abierta una conexión HTTP durante tanto tiempo es frágil, así que la API funciona en tres movimientos: crear una predicción, consultar su estado y después descargar el resultado cuando el estado indique succeeded. Tu código replica esos tres movimientos en la siguiente sección.
Paso a paso: el primer script
Pon todo en un archivo llamado generate.py. Los cuatro pasos siguientes lo construyen de arriba abajo.
Paso 1: prepara la petición
La API sigue el estilo de Replicate: una URL base, un nombre de modelo en la ruta y un token Bearer en la cabecera.
import os
import time
from pathlib import Path
import requests
API_BASE = "https://api.picassoia.com/v1"
MODEL = "picassoia/picassoia-image"
TOKEN = os.environ["PICASSOIA_API_TOKEN"]
HEADERS = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
Si falta la variable, Python se detiene con un error justo aquí, que es exactamente lo que quieres. Un fallo claro al principio del archivo es mejor que un 401 confuso más adelante.
La función devuelve un ID de trabajo, no una imagen. Los prompts pueden tener hasta 4000 caracteres, mucho más de lo que vas a necesitar.
Paso 3: consulta hasta que termine
def wait_for_result(job_id: str, poll_every: float = 2.0, limit: float = 300.0) -> str:
url = f"{API_BASE}/predictions/{job_id}"
deadline = time.time() + limit
while time.time() < deadline:
response = requests.get(url, headers=HEADERS, timeout=30)
response.raise_for_status()
data = response.json()
status = data["status"]
if status == "succeeded":
output = data["output"]
return output[0] if isinstance(output, list) else output
if status in ("failed", "canceled"):
raise RuntimeError(f"Job {job_id} ended as {status}: {data.get('error')}")
time.sleep(poll_every)
raise TimeoutError(f"Job {job_id} did not finish within {limit} seconds")
💡 Consejo: los nombres de los campos siguen el formato de Replicate. Si alguna vez una respuesta parece distinta, imprime response.json() una vez y ajusta las dos líneas que dicen status y output. El bucle también fija su propio límite de tiempo, así que un trabajo atascado nunca congela tu script.
Paso 4: guarda el archivo
def download(image_url: str, folder: str = "output") -> Path:
Path(folder).mkdir(exist_ok=True)
name = image_url.split("/")[-1].split("?")[0] or f"image-{int(time.time())}.png"
target = Path(folder) / name
response = requests.get(image_url, timeout=60)
response.raise_for_status()
target.write_bytes(response.content)
return target
if __name__ == "__main__":
prompt = "A misty mountain lake at sunrise, 35mm photograph, soft natural light, fine film grain"
job_id = create_prediction(prompt)
image_url = wait_for_result(job_id)
print("Saved", download(image_url))
Ejecuta python generate.py. Después de unos segundos deberías ver Saved output/... y un archivo nuevo en la carpeta output. Punto de control: si puedes abrir esa imagen, los pasos del 1 al 4 funcionan, y todo lo que viene después es solo empaquetarlo.
Convierte el script en una herramienta
Añade una línea de comandos
Sustituye la parte final del archivo por un punto de entrada argparse para poder pasar prompts desde la terminal:
import argparse
def main() -> None:
parser = argparse.ArgumentParser(description="Generate an image from a text prompt")
parser.add_argument("prompt", help="what the image should show")
parser.add_argument("--out", default="output", help="folder for saved files")
args = parser.parse_args()
image_url = wait_for_result(create_prediction(args.prompt))
print(download(image_url, args.out))
if __name__ == "__main__":
main()
Ahora python generate.py "a red bicycle leaning on a brick wall, 50mm photo" --out bikes hace todo el trabajo en una sola línea.
Ejecuta lotes dentro de los límites
Un bucle que espera cada imagen en orden es lento. Los hilos lo solucionan, porque el script pasa casi todo el tiempo esperando la red. La API permite 5 predicciones simultáneas por cuenta, compartidas entre tus tokens y cualquier aplicación conectada, así que cinco hilos es el límite.
from multiprocessing.pool import ThreadPool
def generate(prompt: str) -> Path:
return download(wait_for_result(create_prediction(prompt)))
def run_batch(prompts: list[str]) -> list[Path]:
with ThreadPool(5) as pool:
return pool.map(generate, prompts)
Si también usas la web mientras se ejecuta un lote, baja a tres hilos para que ambos no compitan por las mismas cinco plazas. Un prompt fallido lanza un error dentro de pool.map, así que envuelve generate en un bloque try que registre el prompt y devuelva None cuando ejecutes cientos a la vez.
Lleva un registro. Añade cada prompt y nombre de archivo a un archivo log.jsonl, así podrás rastrear cualquier imagen hasta la redacción que la produjo. Llama a este ayudante dentro de generate justo después de download:
Dentro de seis semanas, cuando alguien pregunte qué prompt produjo la foto del amanecer, ese archivo responderá en segundos.
Exponlo como una ruta web
Los equipos rara vez quieren una terminal. Una pequeña aplicación de Flask permite a cualquiera enviar un prompt desde una página. Fíjate en que la ruta devuelve el ID del trabajo de inmediato y una segunda ruta informa del progreso, así ninguna petición web se queda colgada durante un minuto.
Tu frontend llama a /generate y después consulta /status/<id> cada dos segundos hasta que status sea succeeded. El token permanece en el servidor, nunca en el código del navegador.
Escribe prompts que se comporten
Construye los prompts por partes
Los prompts vagos dan imágenes vagas. Un prompt con un sujeto, un lugar, una fuente de luz y un objetivo te da algo repetible. Pon esa estructura en una función para que cada imagen de un lote la siga:
def build_prompt(subject: str, setting: str, light: str, lens: str = "50mm f/1.8") -> str:
return (
f"{subject}, {setting}, {light}, shot on a {lens} lens, "
"natural skin and surface texture, fine film grain, photorealistic"
)
print(build_prompt("a ceramic mug of black coffee", "on an oak desk", "soft window light from the left"))
Tres hábitos hacen los resultados más predecibles:
Una escena por prompt. Dos sujetos compitiendo por la atención producen imágenes confusas.
Nombra la luz. "Luz volumétrica de la mañana desde la izquierda" funciona mejor que "buena iluminación".
Nombra el objetivo. Palabras como 85mm y f/1.8 empujan al modelo hacia una profundidad de campo reducida.
Haz una lluvia de ideas con un modelo de lenguaje
Escribir 50 prompts a mano se vuelve aburrido hacia el prompt doce. Un modelo de lenguaje puede redactarlos por ti. Claude Sonnet 5 maneja bien las listas estructuradas, y Gemini 3.5 Flash devuelve borradores rápidos cuando solo necesitas volumen. Pide 20 prompts que sigan la estructura de tu sujeto, escenario, luz y objetivo, uno por línea, y guarda la respuesta como prompts.txt. Después pasa el archivo a tu ejecutor de lotes:
prompts = [line.strip() for line in Path("prompts.txt").read_text().splitlines() if line.strip()]
run_batch(prompts)
Cómo usar P Image en PicassoIA
Antes de dedicar tiempo al código, prueba tu redacción en el navegador. P Image responde en aproximadamente un segundo, así que puedes probar diez variaciones en el tiempo que tarda un modelo más lento en renderizar un solo resultado.
Abre la página del modelo y busca el cuadro del prompt.
Pega un prompt construido con el sujeto, el escenario, la luz y la estructura de objetivo de la sección anterior.
Elige la relación de aspecto 16:9 para las imágenes de artículos, o 1:1 para las miniaturas.
Genera tres variaciones y cambia un solo detalle entre ellas. Así sabrás qué palabra ha hecho el trabajo.
Copia la redacción ganadora en prompts.txt o en tu llamada a build_prompt.
💡 Si hay un campo de semilla disponible, fija la semilla mientras comparas redacciones. Con la semilla bloqueada, cualquier cambio en el resultado viene de tu prompt y de nada más.
Cuando P Image te da el encuadre correcto pero no el acabado adecuado, envía el mismo prompt a Flux 2 Pro o Seedream 4.5 y compara los resultados uno al lado del otro. Cada modelo tiene sus propias costumbres, y diez minutos de pruebas ahorran horas de reescritura después.
Soluciona los errores que vas a encontrar
Errores de autenticación
Un 401 o 403 casi siempre significa que el token nunca llegó a la petición. Comprueba que PICASSOIA_API_TOKEN esté definida en la misma terminal que ejecuta Python, porque una variable exportada en una ventana no existe en otra. Revisa también si hay comillas sobrantes o un espacio al final del valor.
Límites y trabajos atascados
Síntoma
Causa probable
Solución
Respuesta 429
Más de 5 trabajos ejecutándose a la vez
Baja ThreadPool a 3 o 4 y reintenta tras una pausa corta
Respuesta 400
Prompt de más de 4000 caracteres, o un nombre de campo incorrecto
Acorta el prompt e imprime el cuerpo del error
El trabajo nunca termina
Cola saturada o trabajo bloqueado
Mantén el límite de tiempo de limit y reintenta una vez
Estado failed
El modelo rechazó el prompt
Simplifícalo y quita los símbolos poco habituales
Para los reintentos, espera un poco más tras cada fallo (2, 4 y luego 8 segundos) y renuncia después de tres intentos. Bombardear la API con reintentos inmediatos solo te mantiene por encima del límite.
Resultados poco nítidos o borrosos
Si el encuadre es correcto pero el detalle está poco nítido, la causa suele ser la resolución, no el prompt. Elige un modelo que genere imágenes de alta resolución, como Seedream 4.5, o pasa la imagen terminada por un modelo de Super Resolution de PicassoIA para escalarla con un factor de 2x a 4x. Comprueba también que el prompt nombre una cámara y un objetivo, ya que "fotografía" a secas deja demasiado abierto.
Haz tu primera imagen hoy
Ahora tienes un script que convierte una frase en una fotografía, un ejecutor de lotes que respeta el límite de cinco trabajos y una plantilla de prompts que puedes reutilizar en cada proyecto. El siguiente paso más rápido es pequeño: abre P Image en tu navegador, prueba tres prompts, pega la mejor redacción en prompts.txt y ejecuta el lote.
Cuando quieras escalar, la página de la API de PicassoIA enumera los endpoints, y la lista completa de modelos muestra los modelos de texto a imagen, video y voz que puedes probar después. El mismo bucle de crear, consultar y descargar funciona también para trabajos de video, así que el script que escribiste hoy es la base de un generador de clips mañana. Elige un prompt, ejecuta el script y mira qué aparece en tu carpeta de salida.