Vision par ordinateur avec des Transformers#
Dans ce notebook, nous utilisons la bibliothÚque Transformers de Hugging Face pour traiter des images. Pour éviter de surcharger le notebook, certaines fonctions se trouvent dans utils/util.py.
DĂ©tection dâobjets sans apprentissage (Zero-Shot)#
La dĂ©tection dâobjets dans une image est une tĂąche clĂ© en vision par ordinateur. Les modĂšles zero-shot sont super pratiques car ils dĂ©tectent nâimporte quel objet sans besoin de fine-tuning. Il suffit de leur donner une image et un prompt textuel avec les classes Ă dĂ©tecter.
Mise en Ćuvre#
On a opté pour le modÚle OWL-ViT de Google (google/owlvit-base-patch32) car il est compact et fonctionne sur la plupart des machines. Utilisons le pipeline de Hugging Face :
from transformers import pipeline
from PIL import Image
import cv2
import numpy as np
import matplotlib.pyplot as plt
zeroshot = pipeline("zero-shot-object-detection", model="google/owlvit-base-patch32")
/home/aquilae/anaconda3/envs/dev/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
Jetons un coup dâĆil Ă notre image.
image=Image.open("images/coco.jpg") # Image extraite de la base de données COCO (https://cocodataset.org/#home)
plt.imshow(image)
plt.axis('off')
plt.show()
Utilisons le modÚle pour dessiner les boßtes de détection prédites.
from utils.util import draw_box
text_prompt = "surfboard" # Vous pouvez changer la classe pour détecter autre chose "person" ou "surfboard"
output = zeroshot(image,candidate_labels = [text_prompt])
cv_image=draw_box(image,output)
plt.imshow(cv_image)
plt.axis('off')
plt.show()
Vous savez maintenant comment implĂ©menter un dĂ©tecteur dâobjets zero-shot en quelques lignes de code.
Génération de légendes pour images#
Lâimage captionning consiste Ă gĂ©nĂ©rer une description pour une image. Le modĂšle prend une image en entrĂ©e et produit une lĂ©gende.
Mise en Ćuvre#
Comme précédemment, on utilise le pipeline de Hugging Face. Ici, on utilise le modÚle BLIP de Salesforce (Salesforce/blip-image-captioning-base).
captionner = pipeline("image-to-text", model="Salesforce/blip-image-captioning-base")
On utilise la mĂȘme image pour gĂ©nĂ©rer une description.
result=captionner(image)
print(result[0]['generated_text'])
a man holding a surfboard in a room
On a gĂ©nĂ©rĂ© la description âun homme qui tient une planche de surf dans une piĂšceâ, ce qui est exact. Vous savez maintenant gĂ©nĂ©rer des descriptions dâimages. Câest super utile pour crĂ©er automatiquement des datasets par exemple.
Classification dâimages sans apprentissage (Zero-Shot)#
En plus de la dĂ©tection dâobjets zero-shot, on peut faire de la classification dâimages zero-shot. Le principe est similaire, mais cette fois on donne au moins deux phrases et le modĂšle nous donne la probabilitĂ© que lâimage corresponde Ă lâune ou lâautre.
Mise en Ćuvre#
Utilisons une photo de mon chat pour déterminer sa race :
image=Image.open("images/tigrou.png") # Image extraite de la base de données COCO (https://cocodataset.org/#home)
plt.imshow(image)
plt.axis('off')
plt.show()
On va voir si le modĂšle peut dĂ©terminer sâil sâagit dâun Maine Coon ou dâun chat europĂ©en.
On utilise le modĂšle CLIP dâOpenAI (openai/clip-vit-base-patch32). Pour varier, on utilise dâautres fonctions de la bibliothĂšque Hugging Face au lieu du pipeline.
from transformers import AutoProcessor, AutoModelForZeroShotImageClassification
processor = AutoProcessor.from_pretrained("openai/clip-vit-base-patch32")
model = AutoModelForZeroShotImageClassification.from_pretrained("openai/clip-vit-base-patch32")
/home/aquilae/anaconda3/envs/dev/lib/python3.11/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
labels = ["a photo of a european shorthair", "a photo of maine coon"]
inputs = processor(text=labels,images=image,return_tensors="pt",padding=True)
outputs = model(**inputs)
# Transformation des outputs pour obtenir des probabilités
print("Probabilité de a photo of a european shorthair : ",outputs.logits_per_image.softmax(dim=1)[0][0].item())
print("Probabilité de a photo of maine coon : ",outputs.logits_per_image.softmax(dim=1)[0][1].item())
Probabilité de a photo of a european shorthair : 0.9104425311088562
Probabilité de a photo of maine coon : 0.08955750614404678
Le modĂšle est plutĂŽt sĂ»r quâil sâagit dâun chat europĂ©en, et effectivement, il a raison.
Segmentation dâimages#
Pour cet exemple, on utilise le modĂšle SAM de Meta qui permet de segmenter nâimporte quel objet.
Mise en Ćuvre#
sam = pipeline("mask-generation","Zigeng/SlimSAM-uniform-77")
image=Image.open("images/coco.jpg") # Image extraite de la base de données COCO (https://cocodataset.org/#home)
plt.imshow(image)
plt.axis('off')
plt.show()
# ATTENTION : le traitement peut prendre plusieurs minutes
output=sam(image, points_per_batch=32)
masks=output["masks"]
from utils.util import draw_masks
image_np=draw_masks(image,masks)
plt.imshow(image_np)
plt.axis('off')
plt.show()
Comme vous pouvez le voir, on a segmentĂ© tous les objets de lâimage. Par contre, le temps de traitement Ă©tait assez long⊠Pour un temps dâinfĂ©rence plus raisonnable, on utilise un prompt de coordonnĂ©es dâun point de lâimage. Cela permet de spĂ©cifier le traitement et dâobtenir un rĂ©sultat plus rapidement. On ne peut pas utiliser le pipeline pour cette tĂąche.
from transformers import SamModel, SamProcessor
model = SamModel.from_pretrained("Zigeng/SlimSAM-uniform-77")
processor = SamProcessor.from_pretrained("Zigeng/SlimSAM-uniform-77")
Créons notre prompt de coordonnées et visualisons le point :
input_points = [[[300, 200]]]
image_np= np.array(image)
cv2.circle(image_np,input_points[0][0],radius=3,color=(255,0,0),thickness=5)
plt.imshow(image_np)
plt.axis('off')
plt.show()
inputs = processor(image,input_points=input_points,return_tensors="pt")
outputs = model(**inputs)
predicted_masks = processor.image_processor.post_process_masks(
outputs.pred_masks,
inputs["original_sizes"],
inputs["reshaped_input_sizes"]
)
Le traitement est bien plus rapide ! SAM produit 3 masques par dĂ©faut. Chaque masque reprĂ©sente une possibilitĂ© de segmentation de lâimage. Vous pouvez changer la valeur mask_number pour visualiser les diffĂ©rents masques.
mask_number=2 # 0,1 or 2
mask=predicted_masks[0][:, mask_number]
image_np=draw_masks(image,mask)
plt.imshow(image_np)
plt.axis('off')
plt.show()
Dans cet exemple, on voit que les 3 masques sont pertinents : le premier segmente la personne entiĂšre, le second segmente les vĂȘtements et le troisiĂšme segmente uniquement le t-shirt. Vous pouvez essayer de changer les coordonnĂ©es du point et de visualiser les masques gĂ©nĂ©rĂ©s.
Estimation de la profondeur#
Lâestimation de la profondeur est une tĂąche clĂ© en vision par ordinateur. Câest super utile pour des applications comme les voitures autonomes oĂč on estime la distance par rapport au vĂ©hicule devant nous. Pour lâindustrie, câest aussi intĂ©ressant pour organiser les objets dans un colis en fonction de lâespace restant. Pour cet exemple, on utilise le modĂšle DPT (Intel/dpt-hybrid-midas) qui prend une image en entrĂ©e et renvoie une carte de profondeur.
Mise en Ćuvre#
depth_estimator = pipeline(task="depth-estimation",model="Intel/dpt-hybrid-midas")
image=Image.open("images/coco2.jpg") # Image extraite de la base de données COCO (https://cocodataset.org/#home)
plt.imshow(image)
plt.axis('off')
plt.show()
outputs = depth_estimator(image)
outputs["predicted_depth"].shape
torch.Size([1, 384, 384])
On utilise PyTorch pour adapter la dimension de la carte de profondeur prédite à celle de notre image de base, puis on génÚre une image de la carte de profondeur.
import torch
prediction = torch.nn.functional.interpolate(outputs["predicted_depth"].unsqueeze(1),size=image.size[::-1],
mode="bicubic",align_corners=False)
output = prediction.squeeze().numpy()
formatted = (output * 255 / np.max(output)).astype("uint8")
depth = Image.fromarray(formatted)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
ax1.imshow(image)
ax1.axis('off')
ax2.imshow(depth)
ax2.axis('off')
plt.show()
Sur la carte de profondeur, les couleurs vives représentent les objets les plus proches. On voit bien la route proche en couleur trÚs vive et le bus en couleur assez vive. La carte de profondeur est donc précise.