Comprensión de imágenes con la API de Gemini: entrada de imagen y de imagen a texto en Python
Envía una foto a la API de Gemini desde Python y recibe texto a cambio. Este artículo muestra los bytes en línea, la Files API y los prompts con varias imágenes, y luego convierte la misma llamada en descripciones, OCR de recibos y cuadros delimitadores, con cálculo de tokens, límites de tamaño y soluciones a errores comunes.
Tienes una foto y necesitas palabras. Una imagen de producto que necesita texto alternativo, un recibo que necesita el total, un estante que necesita un recuento. La API de Gemini acepta la imagen como parte del prompt y devuelve texto, así que todo el trabajo cabe en una sola llamada de Python de unas diez líneas. Este artículo sigue el orden en que los problemas aparecen de verdad: configuración, tres formas de enviar una imagen, prompts que devuelven texto utilizable, costos en tokens y los errores que desperdician solicitudes.
Un cambio importa para el código de abajo. La documentación actual de Google muestra la entrada de imagen a través de la Interactions API (client.interactions.create) y marca el método anterior generateContent como legado, aunque confirma que sigue totalmente soportado. Aquí aparecen ambas versiones, así que puedes pegar la que coincida con tu proyecto.
Qué hace realmente la entrada de imagen
Los modelos de Gemini son multimodales, lo que significa que una sola solicitud puede contener partes de texto y partes de imagen juntas. Envías una foto con una instrucción, y el modelo responde en texto. No hay un endpoint de visión aparte, ni un paso de preprocesamiento, ni una biblioteca de OCR que instalar antes. La imagen es simplemente otra parte del prompt.
De imagen a texto en una sola solicitud
El mismo patrón de llamada sirve para trabajos muy distintos, según la instrucción que adjuntes:
Subtítulos: una frase para una publicación en redes o para la descripción de una página.
Texto alternativo: descripciones breves y literales para accesibilidad.
Preguntas visuales: "¿Cuántas sillas hay alrededor de la mesa?" o "¿La etiqueta mira hacia delante?"
OCR: texto extraído de recibos, letreros, formularios y notas manuscritas.
Detección: cajas delimitadoras con etiqueta devueltas como JSON.
Comparación: diferencias entre dos o más imágenes.
💡 Trata la instrucción como el producto. El modelo es el mismo en todos los casos. El prompt decide si obtienes un poema sobre una foto o un total limpio en JSON.
Modelos que aceptan imágenes
La página de modelos de Google enumera estos IDs actuales, todos con entrada de imagen:
ID del modelo
Estado
Descripción de Google
gemini-3.8-flash
Estable
Modelo Flash más inteligente
gemini-3.7-flash
Estable
Programación compleja y flujos de trabajo agénticos
Las gamas cambian rápido, así que revisa la página de modelos antes de fijar un ID en producción. Para trabajo con imágenes, un modelo Flash es la opción sensata por defecto. Pasa a un modelo Pro solo cuando las respuestas sobre escaneos densos o escenas complicadas salgan mal.
Configura tu entorno de Python
Dos minutos de configuración ahora ahorran una tarde de errores de importación confusos más tarde.
Instala el SDK
El paquete oficial es google-genai. Los ejemplos de abajo también usan Pydantic para la salida estructurada y Pillow para dibujar las cajas.
pip install -U google-genai pydantic pillow
No lo confundas con el paquete anterior google-generativeai. El nuevo se importa como from google import genai, y todos los fragmentos de aquí lo asumen.
Crea el cliente
Genera una credencial en Google AI Studio, guárdala en la variable de entorno que indica la página de configuración de Google y mantenla fuera del control de versiones. El cliente la lee automáticamente:
from google import genai
client = genai.Client()
Sin argumentos, sin secretos escritos en el script. Todos los ejemplos siguientes reutilizan este client.
Envía una imagen de tres formas
Elige el método según el tamaño del archivo y su reutilización, no por costumbre.
Bytes en línea para archivos pequeños
Los datos en línea son el camino más corto. Lees el archivo, lo codificas y lo envías junto con el prompt. La versión actual con la Interactions API se ve así:
import base64
from pathlib import Path
image_bytes = Path("street-market.jpg").read_bytes()
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=[
{"type": "text", "text": "Caption this image in one sentence."},
{
"type": "image",
"data": base64.b64encode(image_bytes).decode("utf-8"),
"mime_type": "image/jpeg",
},
],
)
print(interaction.output_text)
La versión generateContent heredada sigue siendo válida y algo más corta, porque el SDK se encarga de la codificación:
from google.genai import types
response = client.models.generate_content(
model="gemini-3.8-flash",
contents=[
types.Part.from_bytes(data=image_bytes, mime_type="image/jpeg"),
"Caption this image in one sentence.",
],
)
print(response.text)
Los datos en línea limitan la solicitud total (texto del prompt, instrucciones del sistema y bytes de la imagen juntos) a 20 MB. Una foto de teléfono cabe sin problema. Un lote de escaneos a resolución completa no.
Files API para imágenes más grandes
Cuando la solicitud superaría los 20 MB, o cuando quieres hacer varias preguntas sobre una misma imagen, súbela una vez y haz referencia a ella por su URI:
uploaded = client.files.upload(file="mountain-lake-print.jpg")
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=[
{"type": "text", "text": "Describe the scene and list any visible text."},
{
"type": "image",
"uri": uploaded.uri,
"mime_type": uploaded.mime_type,
},
],
)
print(interaction.output_text)
Los archivos subidos se almacenan temporalmente, así que trata la Files API como un mecanismo de entrega y no como un archivo. Conserva tus originales.
Varias imágenes en un mismo prompt
Añade más partes de imagen a la misma lista de input. Los documentos de Google permiten hasta 3600 archivos de imagen en una sola solicitud.
before = client.files.upload(file="living-room-before.jpg")
after = client.files.upload(file="living-room-after.jpg")
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=[
{
"type": "text",
"text": "The first image is BEFORE and the second is AFTER. "
"What is different between them?",
},
{"type": "image", "uri": before.uri, "mime_type": before.mime_type},
{"type": "image", "uri": after.uri, "mime_type": after.mime_type},
],
)
print(interaction.output_text)
Indica en el texto qué imagen es cuál. El modelo ve una lista ordenada, y un simple "compara estas" lo deja adivinando los roles.
Prompts que convierten fotos en texto
La llamada nunca cambia. Solo cambia la instrucción.
Objetivo
Patrón del prompt
Forma de la salida
Subtítulo
"Describe esta imagen con un subtítulo de una frase."
Texto plano
Texto alternativo
"Escribe texto alternativo de menos de 125 caracteres. Describe solo lo visible."
Texto plano
Pregunta visual
"¿Cuántas cajas rojas hay en el estante izquierdo?"
Respuesta corta
Extracción
"Extrae comercio, fecha y total."
JSON mediante esquema
Detección
"Detecta todos los elementos destacados de la imagen."
JSON mediante esquema
Subtítulos y texto alternativo
La mayor mejora de calidad viene de las restricciones. "Describe esta imagen" devuelve un párrafo. "Escribe texto alternativo de menos de 125 caracteres, sin frases de apertura como 'imagen de'" devuelve algo que puedes publicar.
prompt = (
"Write alt text for this photo in under 125 characters. "
"Describe only what is visible. Do not start with 'image of'."
)
Ejecútalo sobre una carpeta de fotos con un bucle sencillo y tendrás un primer borrador de cada atributo alt que falte en un sitio. Una persona sigue leyendo los borradores, porque un modelo puede juzgar mal qué importa en una escena.
OCR y recibos
Los recibos son una buena prueba porque mezclan texto impreso, números y arrugas. Pide salida estructurada en lugar de prosa. Define la forma con Pydantic y pasa su esquema JSON mediante response_format:
from pydantic import BaseModel
class LineItem(BaseModel):
name: str
price: float
class Receipt(BaseModel):
merchant: str
date: str
items: list[LineItem]
total: float
receipt = client.files.upload(file="receipt.jpg")
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=[
{
"type": "text",
"text": "Extract the merchant, date, line items, and total.",
},
{"type": "image", "uri": receipt.uri, "mime_type": receipt.mime_type},
],
response_format={
"type": "text",
"mime_type": "application/json",
"schema": Receipt.model_json_schema(),
},
)
data = Receipt.model_validate_json(interaction.output_text)
print(data.merchant, data.total)
Si el modelo devuelve algo que no encaja con el esquema, model_validate_json lanza un error en ese mismo momento en lugar de dejar que datos incorrectos lleguen a tu base de datos.
Detección de objetos con cajas
Gemini puede devolver cajas delimitadoras como [ymin, xmin, ymax, xmax], normalizadas a una escala de 0 a 1000. Pídelas con un esquema y luego conviértelas a píxeles y dibújalas:
from PIL import Image, ImageDraw
from pydantic import BaseModel, Field
class Box(BaseModel):
box_2d: list[int] = Field(
description="[ymin, xmin, ymax, xmax] normalized to 0-1000."
)
label: str
class Boxes(BaseModel):
boxes: list[Box]
aisle = client.files.upload(file="grocery-aisle.jpg")
interaction = client.interactions.create(
model="gemini-3.8-flash",
input=[
{"type": "text", "text": "Detect all of the prominent items in the image."},
{"type": "image", "uri": aisle.uri, "mime_type": aisle.mime_type},
],
response_format={
"type": "text",
"mime_type": "application/json",
"schema": Boxes.model_json_schema(),
},
)
result = Boxes.model_validate_json(interaction.output_text)
image = Image.open("grocery-aisle.jpg")
width, height = image.size
draw = ImageDraw.Draw(image)
for item in result.boxes:
ymin, xmin, ymax, xmax = item.box_2d
left, top = xmin / 1000 * width, ymin / 1000 * height
right, bottom = xmax / 1000 * width, ymax / 1000 * height
draw.rectangle((left, top, right, bottom), outline="red", width=4)
draw.text((left + 6, top + 6), item.label, fill="red")
image.save("grocery-aisle-boxes.jpg")
La segmentación sigue el mismo patrón. El esquema añade un campo mask con los puntos del polígono, también normalizados de 0 a 1000. Google recomienda fijar el nivel de razonamiento en minimal para la segmentación, ya que el razonamiento extendido añade latencia sin mejorar los polígonos.
Límites, tokens y costos
Las imágenes se cobran en tokens, y el recuento depende del tamaño. Conocer la regla te permite predecir la factura antes de que se ejecute el lote.
Cómo cuentan las imágenes como tokens
Situación de la imagen
Costo en tokens
Ambas dimensiones de 384 px o menos
258 tokens
Imagen más grande
Dividida en mosaicos de 768 x 768 px, 258 tokens por mosaico
Ejemplo: una imagen que se divide en cuatro mosaicos
4 x 258 = 1032 tokens
La documentación también describe un ajuste de media_resolution que limita el número máximo de tokens asignados a cada imagen de entrada. Bájalo para subtítulos masivos donde el detalle fino no importa. Súbelo cuando importen los textos pequeños u objetos lejanos. Consulta la referencia actual para ver cómo tu versión del SDK escribe esta opción.
El precio varía según el modelo, así que consulta la página de precios de Google para ver las cifras. La palanca que controlas es el recuento de tokens, y enviar una copia más pequeña del archivo es la forma más barata de reducirlo.
Límites de formato y tamaño
Límite
Valor
Formatos admitidos
PNG, JPEG, WEBP, HEIC, HEIF
Imágenes por solicitud
Hasta 3600 archivos
Tamaño de solicitud en línea
20 MB en total (texto, instrucciones y bytes)
Escala de las cajas delimitadoras
0 a 1000, orden [ymin, xmin, ymax, xmax]
💡 Si un trabajo envía muchas imágenes con un prompt largo, suma el total en línea antes de chocar con el límite de 20 MB. Cambiar a la Files API a mitad de proyecto es fácil, pero hacerlo pronto evita un fallo intermitente a las 2 de la madrugada.
Tres errores que desperdician llamadas
La mayoría de las solicitudes fallidas vienen de la misma lista corta.
Tipo MIME incorrecto
El mime_type debe coincidir con el archivo real. Etiquetar un PNG como image/jpeg o enviar un formato no compatible produce errores o malos resultados. Deja que Python lo determine en lugar de escribir las cadenas a mano:
Algunos sistemas no reconocen el tipo HEIC, así que añade un pequeño mapeo manual si aceptas originales de iPhone.
Cajas en el lugar equivocado
Si los rectángulos dibujados caen en lugares extraños, revisa dos cosas. Primero, el orden es [ymin, xmin, ymax, xmax], con el valor vertical primero. Mucha gente lo lee como x y luego y. Segundo, los números están en una escala de 0 a 1000, no en píxeles. Divide entre 1000 y luego multiplica por el ancho o alto real.
Texto libre donde debería ir JSON
Escribir "devuelve JSON" en el prompt funciona hasta que el modelo envuelve la respuesta en un bloque de código o añade una frase amable. Pasa un esquema mediante response_format y analiza con model_validate_json. El contrato queda entonces en el código, donde una respuesta incorrecta falla de forma ruidosa y una buena llega tipada.
Prueba Gemini 3.5 Flash sin código
Antes de escribir Python, prueba el prompt en el navegador. Gemini 3.5 Flash funciona en PicassoIA y acepta imágenes directamente, así que puedes afinar una instrucción en segundos y pegarla después en tu script.
Adjunta tus fotos en el campo Images. El modelo acepta hasta 10 imágenes por ejecución, de 7 MB cada una.
Escribe la instrucción en el campo Prompt, con la redacción exacta que pensabas enviar desde Python.
Opcionalmente, rellena System Instruction para fijar el rol, por ejemplo "Escribes texto alternativo de menos de 125 caracteres".
Elige un Thinking Level de none, low o high. Déjalo en none para subtítulos y súbelo para razonamientos complejos.
Pon Temperature baja para extracción y OCR, y más alta para subtítulos creativos.
Ejecútalo, compara la respuesta con lo que querías y ajusta la redacción antes de copiarla al código.
Campo
Qué hace
Valor inicial
Prompt
La instrucción enviada con las imágenes
Tu redacción exacta de producción
Images
Hasta 10 archivos, 7 MB cada uno
Una imagen mientras pruebas
System Instruction
Fija el rol del modelo
Una frase corta
Thinking Level
none, low o high
none
Temperature
Aleatoriedad de 0 a 2
0,2 para OCR, 1 para subtítulos
Max Output Tokens
Limita la longitud de la respuesta
El valor por defecto basta
Los límites de PicassoIA son distintos de los límites sin procesar de la API que aparecen arriba, así que trata la página como un laboratorio de prompts y la API como la vía de producción. Para una segunda opinión sobre una imagen difícil, ejecuta el mismo prompt con Gemini 3.1 Pro, Qwen3.7-Plus, que interpreta imágenes y texto, o Granite Vision 4.1 4B, diseñado para gráficos y tablas.
Crea tus propias imágenes de prueba
No necesitas una carpeta de fotos reales para empezar. Genera un escritorio desordenado, un estante de supermercado, una calle lluviosa o un recibo arrugado con PicassoIA Image o Seedream 4.5, y luego pasa cada resultado a Gemini 3.5 Flash para ver qué lee.
Prueba tres experimentos esta semana. Pide texto alternativo para cinco fotos generadas. Pide una caja delimitadora alrededor de un objeto en una escena concurrida. Pide un total en JSON de una imagen de recibo. Cada uno lleva minutos, y juntos muestran dónde el modelo es preciso y dónde tu prompt necesita ajustes.
Abre PicassoIA, crea tu primera imagen de prueba y ejecuta tu propio prompt. Explora todos los modelos disponibles en picassoia.com/en/all-models y combina un generador de imágenes con un modelo de visión para crear tu propio flujo de trabajo de imagen a texto.