Segmentación de imágenes sin entrenamiento previo con CLIPSeg

Segmentación de imágenes con CLIPSeg sin entrenamiento previo.

Esta guía muestra cómo puedes usar CLIPSeg, un modelo de segmentación de imágenes de cero disparos, utilizando 🤗 transformers. CLIPSeg crea máscaras de segmentación aproximadas que se pueden utilizar para la percepción de robots, el relleno de imágenes y muchas otras tareas. Si necesitas máscaras de segmentación más precisas, te mostraremos cómo puedes refinar los resultados de CLIPSeg en Segments.ai.

La segmentación de imágenes es una tarea conocida dentro del campo de la visión por computadora. Permite a una computadora no solo saber qué hay en una imagen (clasificación), dónde están los objetos en la imagen (detección), sino también cuáles son los contornos de esos objetos. Conocer los contornos de los objetos es esencial en campos como la robótica y la conducción autónoma. Por ejemplo, un robot tiene que conocer la forma de un objeto para agarrarlo correctamente. La segmentación también se puede combinar con el relleno de imágenes para permitir a los usuarios describir qué parte de la imagen desean reemplazar.

Una limitación de la mayoría de los modelos de segmentación de imágenes es que solo funcionan con una lista fija de categorías. Por ejemplo, no puedes simplemente usar un modelo de segmentación entrenado en naranjas para segmentar manzanas. Para enseñarle al modelo de segmentación una categoría adicional, debes etiquetar datos de la nueva categoría y entrenar un nuevo modelo, lo que puede ser costoso y llevar mucho tiempo. Pero, ¿qué pasaría si hubiera un modelo que pudiera segmentar casi cualquier tipo de objeto, sin necesidad de ningún entrenamiento adicional? Eso es exactamente lo que logra CLIPSeg, un modelo de segmentación de cero disparos.

Actualmente, CLIPSeg todavía tiene sus limitaciones. Por ejemplo, el modelo utiliza imágenes de 352 x 352 píxeles, por lo que la salida tiene una resolución bastante baja. Esto significa que no podemos esperar resultados perfectos a nivel de píxeles cuando trabajamos con imágenes de cámaras modernas. Si queremos segmentaciones más precisas, podemos ajustar finamente un modelo de segmentación de vanguardia, como se muestra en nuestra publicación de blog anterior. En ese caso, todavía podemos usar CLIPSeg para generar algunas etiquetas aproximadas y luego refinarlas en una herramienta de etiquetado como Segments.ai. Antes de describir cómo hacer eso, echemos un vistazo a cómo funciona CLIPSeg.

CLIP: el modelo mágico detrás de CLIPSeg

CLIP, que significa Contrastive Language- Image Pre-training, es un modelo desarrollado por OpenAI en 2021. Puedes darle a CLIP una imagen o un fragmento de texto, y CLIP generará una representación abstracta de tu entrada. Esta representación abstracta, también llamada embedding, es simplemente un vector (una lista de números). Puedes pensar en este vector como un punto en un espacio de alta dimensión. CLIP se entrena de manera que las representaciones de imágenes y textos similares también sean similares. Esto significa que si ingresamos una imagen y una descripción de texto que se ajuste a esa imagen, las representaciones de la imagen y el texto serán similares (es decir, los puntos de alta dimensión estarán cercanos entre sí).

Al principio, esto puede no parecer muy útil, pero en realidad es muy poderoso. Como ejemplo, veamos rápidamente cómo CLIP se puede utilizar para clasificar imágenes sin haber sido entrenado previamente en esa tarea. Para clasificar una imagen, ingresamos la imagen y las diferentes categorías entre las que queremos elegir a CLIP (por ejemplo, ingresamos una imagen y las palabras “manzana”, “naranja”, …). CLIP nos devuelve un embedding de la imagen y de cada categoría. Ahora simplemente tenemos que verificar qué embedding de categoría está más cerca del embedding de la imagen, ¡y listo! Se siente como magia, ¿verdad?

Ejemplo de clasificación de imágenes usando CLIP (fuente).

Además, CLIP no solo es útil para la clasificación, sino que también se puede utilizar para la búsqueda de imágenes (¿puedes ver cómo esto es similar a la clasificación?), modelos de texto a imagen (DALL-E 2 funciona con CLIP), detección de objetos (OWL-ViT) y, lo más importante para nosotros: segmentación de imágenes. Ahora ves por qué CLIP fue realmente un avance en el aprendizaje automático.

La razón por la que CLIP funciona tan bien es que el modelo se entrenó con un enorme conjunto de datos de imágenes con leyendas de texto. El conjunto de datos contenía impresionantes 400 millones de pares de imagen-texto tomados de Internet. Estas imágenes contienen una amplia variedad de objetos y conceptos, y CLIP es excelente para crear una representación para cada uno de ellos.

CLIPSeg: segmentación de imágenes con CLIP

CLIPSeg es un modelo que utiliza representaciones CLIP para crear máscaras de segmentación de imágenes. Fue publicado por Timo Lüddecke y Alexander Ecker. Lograron la segmentación de imágenes sin entrenamiento mediante un decodificador basado en Transformer sobre el modelo CLIP, que se mantiene congelado. El decodificador toma la representación CLIP de una imagen y la representación CLIP de la cosa que deseas segmentar. Utilizando estas dos entradas, el decodificador de CLIPSeg crea una máscara de segmentación binaria. Para ser más preciso, el decodificador no solo utiliza la representación CLIP final de la imagen que deseamos segmentar, sino que también utiliza las salidas de algunas de las capas de CLIP.

Fuente

El decodificador se entrena en el conjunto de datos PhraseCut, que contiene más de 340,000 frases con máscaras de segmentación de imágenes correspondientes. Los autores también experimentaron con varias técnicas de aumento para expandir el tamaño del conjunto de datos. El objetivo aquí no es solo poder segmentar las categorías presentes en el conjunto de datos, sino también segmentar categorías no vistas. Los experimentos muestran que el decodificador puede generalizar a categorías no vistas.

Una característica interesante de CLIPSeg es que tanto la consulta (la imagen que queremos segmentar) como la indicación (lo que queremos segmentar en la imagen) se ingresan como incrustaciones CLIP. La incrustación CLIP para la indicación puede provenir de un fragmento de texto (el nombre de la categoría) o de otra imagen. Esto significa que puedes segmentar naranjas en una foto al darle a CLIPSeg una imagen de ejemplo de una naranja.

Esta técnica, llamada “indicación visual”, es realmente útil cuando lo que quieres segmentar es difícil de describir. Por ejemplo, si quieres segmentar un logotipo en una imagen de una camiseta, no es fácil describir la forma del logotipo, pero CLIPSeg te permite simplemente usar la imagen del logotipo como indicación.

El artículo de CLIPSeg contiene algunos consejos para mejorar la efectividad de la indicación visual. Descubrieron que recortar la imagen de consulta (para que solo contenga el objeto que deseas segmentar) ayuda mucho. Desenfocar y oscurecer el fondo de la imagen de consulta también ayuda un poco. En la siguiente sección, mostraremos cómo puedes probar la indicación visual tú mismo utilizando 🤗 transformers.

Usando CLIPSeg con Hugging Face Transformers

Utilizando Hugging Face Transformers, puedes descargar y ejecutar fácilmente un modelo pre-entrenado de CLIPSeg en tus imágenes. Comencemos instalando transformers.

!pip install -q transformers

Para descargar el modelo, simplemente instancialo.

from transformers import CLIPSegProcessor, CLIPSegForImageSegmentation

processor = CLIPSegProcessor.from_pretrained("CIDAS/clipseg-rd64-refined")
model = CLIPSegForImageSegmentation.from_pretrained("CIDAS/clipseg-rd64-refined")

Ahora podemos cargar una imagen para probar la segmentación. Elegiremos una imagen de un delicioso desayuno tomada por Calum Lewis.

from PIL import Image
import requests

url = "https://unsplash.com/photos/8Nc_oQsc2qQ/download?ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjcxMjAwNzI0&force=true&w=640"
image = Image.open(requests.get(url, stream=True).raw)
image

Indicación de texto

Comencemos definiendo algunas categorías de texto que deseamos segmentar.

prompts = ["cubertería", "panqueques", "arándanos", "jugo de naranja"]

Ahora que tenemos nuestras entradas, podemos procesarlas e ingresarlas al modelo.

import torch

inputs = processor(text=prompts, images=[image] * len(prompts), padding="max_length", return_tensors="pt")
# predecir
with torch.no_grad():
  outputs = model(**inputs)
preds = outputs.logits.unsqueeze(1)

Finalmente, visualicemos la salida.

import matplotlib.pyplot as plt

_, ax = plt.subplots(1, len(prompts) + 1, figsize=(3*(len(prompts) + 1), 4))
[a.axis('off') for a in ax.flatten()]
ax[0].imshow(image)
[ax[i+1].imshow(torch.sigmoid(preds[i][0])) for i in range(len(prompts))];
[ax[i+1].text(0, -15, prompt) for i, prompt in enumerate(prompts)];

Inducción visual

Como se mencionó anteriormente, también podemos usar imágenes como indicaciones de entrada (es decir, en lugar de los nombres de las categorías). Esto puede ser especialmente útil si no es fácil describir la cosa que deseas segmentar. Para este ejemplo, utilizaremos una imagen de una taza de café tomada por Daniel Hooper.

url = "https://unsplash.com/photos/Ki7sAc8gOGE/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTJ8fGNvZmZlJTIwdG8lMjBnb3xlbnwwfHx8fDE2NzExOTgzNDQ&force=true&w=640"
prompt = Image.open(requests.get(url, stream=True).raw)
prompt

Ahora podemos procesar la imagen de entrada y la imagen de indicación y enviarlas al modelo.

encoded_image = processor(images=[image], return_tensors="pt")
encoded_prompt = processor(images=[prompt], return_tensors="pt")
# predecir
with torch.no_grad():
  outputs = model(**encoded_image, conditional_pixel_values=encoded_prompt.pixel_values)
preds = outputs.logits.unsqueeze(1)
preds = torch.transpose(preds, 0, 1)

Luego, podemos visualizar los resultados como antes.

_, ax = plt.subplots(1, 2, figsize=(6, 4))
[a.axis('off') for a in ax.flatten()]
ax[0].imshow(image)
ax[1].imshow(torch.sigmoid(preds[0]))

Intentemos una última vez utilizando los consejos de inducción visual descritos en el artículo, es decir, recortando la imagen y oscureciendo el fondo.

url = "https://i.imgur.com/mRSORqz.jpg"
alternative_prompt = Image.open(requests.get(url, stream=True).raw)
alternative_prompt

encoded_alternative_prompt = processor(images=[alternative_prompt], return_tensors="pt")
# predecir
with torch.no_grad():
  outputs = model(**encoded_image, conditional_pixel_values=encoded_alternative_prompt.pixel_values)
preds = outputs.logits.unsqueeze(1)
preds = torch.transpose(preds, 0, 1)

_, ax = plt.subplots(1, 2, figsize=(6, 4))
[a.axis('off') for a in ax.flatten()]
ax[0].imshow(image)
ax[1].imshow(torch.sigmoid(preds[0]))

En este caso, el resultado es prácticamente el mismo. Esto probablemente se debe a que la taza de café ya estaba separada del fondo en la imagen original.

Usando CLIPSeg para preetiquetar imágenes en Segments.ai

Como puedes ver, los resultados de CLIPSeg son un poco borrosos y de muy baja resolución. Si queremos obtener mejores resultados, puedes ajustar finamente un modelo de segmentación de vanguardia, como se explica en nuestra publicación de blog anterior. Para ajustar finamente el modelo, necesitaremos datos etiquetados. En esta sección, te mostraremos cómo puedes usar CLIPSeg para crear algunas máscaras de segmentación aproximadas y luego refinarlas en Segments.ai, una plataforma de etiquetado con herramientas inteligentes de etiquetado para segmentación de imágenes.

Primero, crea una cuenta en https://segments.ai/join e instala el SDK de Python de Segments. Luego, puedes inicializar el cliente de Python de Segments.ai utilizando una clave de API. Esta clave se puede encontrar en la página de la cuenta.

!pip install -q segments-ai

from segments import SegmentsClient
from getpass import getpass

api_key = getpass('Ingresa tu clave de API: ')
segments_client = SegmentsClient(api_key)

A continuación, carguemos una imagen de un conjunto de datos utilizando el cliente de Segments. Utilizaremos el conjunto de datos de conducción autónoma a2d2. También puedes crear tu propio conjunto de datos siguiendo estas instrucciones.

samples = segments_client.get_samples("admin-tobias/clipseg")

# Utiliza la última imagen como ejemplo
sample = samples[1]
image = Image.open(requests.get(sample.attributes.image.url, stream=True).raw)
image

También necesitamos obtener los nombres de las categorías de los atributos del conjunto de datos.

dataset = segments_client.get_dataset("admin-tobias/clipseg")
category_names = [category.name for category in dataset.task_attributes.categories]

Ahora podemos usar CLIPSeg en la imagen como antes. Esta vez, también ampliaremos las salidas para que coincidan con el tamaño de la imagen de entrada.

from torch import nn

inputs = processor(text=category_names, images=[image] * len(category_names), padding="max_length", return_tensors="pt")

# predecir
with torch.no_grad():
  outputs = model(**inputs)

# redimensionar las salidas
preds = nn.functional.interpolate(
    outputs.logits.unsqueeze(1),
    size=(image.size[1], image.size[0]),
    mode="bilinear"
)

Y podemos visualizar los resultados nuevamente.

len_cats = len(category_names)
_, ax = plt.subplots(1, len_cats + 1, figsize=(3*(len_cats + 1), 4))
[a.axis('off') for a in ax.flatten()]
ax[0].imshow(image)
[ax[i+1].imshow(torch.sigmoid(preds[i][0])) for i in range(len_cats)];
[ax[i+1].text(0, -15, category_name) for i, category_name in enumerate(category_names)];

Ahora tenemos que combinar las predicciones en una sola imagen segmentada. Simplemente haremos esto tomando la categoría con el mayor valor de sigmoid para cada parche. También nos aseguraremos de que todos los valores por debajo de un umbral determinado no cuenten.

umbral = 0.1

flat_preds = torch.sigmoid(preds.squeeze()).reshape((preds.shape[0], -1))

# Inicializar una máscara ficticia "sin etiquetar" con el umbral
flat_preds_con_umbral = torch.full((preds.shape[0] + 1, flat_preds.shape[-1]), umbral)
flat_preds_con_umbral[1:preds.shape[0]+1,:] = flat_preds

# Obtener el índice de máscara superior para cada píxel
inds = torch.topk(flat_preds_con_umbral, 1, dim=0).indices.reshape((preds.shape[-2], preds.shape[-1]))

Vamos a visualizar rápidamente el resultado.

plt.imshow(inds)

Por último, podemos subir la predicción a Segments.ai. Para hacer esto, primero convertiremos el mapa de bits en un archivo png, luego subiremos este archivo a Segments y finalmente añadiremos la etiqueta a la muestra.

from segments.utils import bitmap2file
import numpy as np

inds_np = inds.numpy().astype(np.uint32)
unique_inds = np.unique(inds_np).tolist()
f = bitmap2file(inds_np, is_segmentation_bitmap=True)

asset = segments_client.upload_asset(f, "clipseg_prediction.png")

attributes = {
      'format_version': '0.1',
      'annotations': [{"id": i, "category_id": i} for i in unique_inds if i != 0],
      'segmentation_bitmap': { 'url': asset.url },
  }

segments_client.add_label(sample.uuid, 'ground-truth', attributes)

Si observas la predicción subida en Segments.ai, verás que no es perfecta. Sin embargo, puedes corregir manualmente los mayores errores y luego puedes utilizar el conjunto de datos corregido para entrenar un modelo mejor que CLIPSeg.

Conclusión

CLIPSeg es un modelo de segmentación de cero disparo que funciona tanto con texto como con imágenes. El modelo añade un decodificador a CLIP y puede segmentar casi cualquier cosa. Sin embargo, las máscaras de segmentación de salida todavía tienen una resolución muy baja por ahora, por lo que probablemente querrás afinar un modelo de segmentación diferente si la precisión es importante.

Ten en cuenta que actualmente se está realizando más investigación sobre segmentación de cero disparo, por lo que puedes esperar que se añadan más modelos en un futuro próximo. Un ejemplo es GroupViT, que ya está disponible en 🤗 Transformers. Para mantenerte al día con las últimas noticias en investigación de segmentación, puedes seguirnos en Twitter: @TobiasCornille, @NielsRogge y @huggingface.

Si estás interesado en aprender cómo afinar un modelo de segmentación de última generación, echa un vistazo a nuestra publicación anterior en el blog: https://huggingface.co/blog/fine-tune-segformer.

We will continue to update Zepes; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

Inteligencia Artificial

Cómo las industrias están cumpliendo las expectativas de los consumidores con la IA de voz

Gracias a los rápidos avances tecnológicos, los consumidores se han acostumbrado a un nivel sin precedentes de comodi...

Inteligencia Artificial

El ascenso de los chatbots de máquinas tontas a colaboradores creativos

El año 2023 fue un año revolucionario para muchos de nosotros, ya que dominamos el arte de la comunicación, la creati...

Inteligencia Artificial

Algoritmo ayuda en la detección temprana de enfermedades oculares relacionadas con la edad

Un nuevo algoritmo de aprendizaje profundo puede predecir si la degeneración macular relacionada con la edad de un in...