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.

Comprensión de imágenes con la API de Gemini: entrada de imagen y de imagen a texto en Python
Cristian Da Conceicao
Fundador de Picasso IA

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

Mujer sosteniendo un teléfono sobre una foto impresa de un mercado junto a un equipo portátil

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 modeloEstadoDescripción de Google
gemini-3.8-flashEstableModelo Flash más inteligente
gemini-3.7-flashEstableProgramación compleja y flujos de trabajo agénticos
gemini-3.6-flashEstableTrabajo multimodal general
gemini-3.5-flash (Gemini 3.5 Flash)EstableCargas de trabajo de alto rendimiento
gemini-3.1-pro-preview (Gemini 3.1 Pro)Vista previaResolución de problemas complejos
gemini-3-flash-preview (Gemini 3 Flash)Vista previaTareas multimodales

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

Equipo portátil de un desarrollador sobre un escritorio de nogal con una terminal abierta al anochecer

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

Ranura de tarjeta de memoria de cámara con copias de un pueblo portuario detrás

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

Fotógrafo junto a una gran copia de un lago de montaña sosteniendo un disco duro

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

Dos copias casi idénticas de una sala de estar con una diferencia señalada

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.

ObjetivoPatrón del promptForma 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

Editor web escribiendo texto alternativo en una libreta junto a un monitor

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

Recibos arrugados y una lista escrita a mano sobre la barra de una cafetería

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

Pasillo de supermercado con cajas de naranjas, tomates y pimientos

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

Hoja de contactos con fotogramas pequeños sobre una mesa de luz con una lupa

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 imagenCosto en tokens
Ambas dimensiones de 384 px o menos258 tokens
Imagen más grandeDividida en mosaicos de 768 x 768 px, 258 tokens por mosaico
Ejemplo: una imagen que se divide en cuatro mosaicos4 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ímiteValor
Formatos admitidosPNG, JPEG, WEBP, HEIC, HEIF
Imágenes por solicitudHasta 3600 archivos
Tamaño de solicitud en línea20 MB en total (texto, instrucciones y bytes)
Escala de las cajas delimitadoras0 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:

import mimetypes

def mime_for(path: str) -> str:
    mime, _ = mimetypes.guess_type(path)
    if mime is None:
        raise ValueError(f"Unknown image type: {path}")
    return mime

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.

  1. Abre la página de Gemini 3.5 Flash en PicassoIA.
  2. Adjunta tus fotos en el campo Images. El modelo acepta hasta 10 imágenes por ejecución, de 7 MB cada una.
  3. Escribe la instrucción en el campo Prompt, con la redacción exacta que pensabas enviar desde Python.
  4. Opcionalmente, rellena System Instruction para fijar el rol, por ejemplo "Escribes texto alternativo de menos de 125 caracteres".
  5. Elige un Thinking Level de none, low o high. Déjalo en none para subtítulos y súbelo para razonamientos complejos.
  6. Pon Temperature baja para extracción y OCR, y más alta para subtítulos creativos.
  7. Ejecútalo, compara la respuesta con lo que querías y ajusta la redacción antes de copiarla al código.
CampoQué haceValor inicial
PromptLa instrucción enviada con las imágenesTu redacción exacta de producción
ImagesHasta 10 archivos, 7 MB cada unoUna imagen mientras pruebas
System InstructionFija el rol del modeloUna frase corta
Thinking Levelnone, low o highnone
TemperatureAleatoriedad de 0 a 20,2 para OCR, 1 para subtítulos
Max Output TokensLimita la longitud de la respuestaEl 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.

Compartir este artículo

Elige tu idioma