OpenCV com Python: Introdução ao Processamento de Imagens
Comece com processamento de imagens usando OpenCV e Python: espaços de cores, filtros, contornos, limiarização, detecção de rostos e leitura de fluxo de webcam.
InSkillCoach
OpenCV com Python: Introdução ao Processamento de Imagens
OpenCV (Open Source Computer Vision Library) é a biblioteca de referência em visão computacional. Com sua interface Python, ela permite ler, transformar e analisar imagens e vídeos em poucas linhas: filtragem, detecção de contornos, reconhecimento de rostos, processamento de fluxo de webcam em tempo real. Este guia cobre as operações fundamentais que todo projeto de processamento de imagens utiliza, com exemplos completos e executáveis.
Instalação
O OpenCV é instalado por meio do pacote opencv-python, que inclui os módulos principais:
pip install opencv-python
# Se você precisar dos módulos adicionais (contrib)
pip install opencv-contrib-python
Verificação:
import cv2
print(cv2.__version__)
# 4.10.0 (ou versão mais recente)
Observe que o módulo é importado com o nome cv2, independentemente do nome do pacote instalado. O OpenCV representa as imagens como arrays NumPy, que é instalado automaticamente como dependência.
Ler, exibir e salvar uma imagem
import cv2
# Leitura de uma imagem a partir do disco
image = cv2.imread("photo.jpg")
# imread NÃO lança exceção se o arquivo não for encontrado: ele retorna None
if image is None:
raise FileNotFoundError("Impossible de lire photo.jpg")
# Uma imagem é um array NumPy: (altura, largura, canais)
print(image.shape)
# (720, 1280, 3)
print(image.dtype)
# uint8 (valores de 0 a 255)
# Exibição em uma janela
cv2.imshow("Ma photo", image)
cv2.waitKey(0) # aguarda uma tecla (0 = indefinidamente)
cv2.destroyAllWindows() # fecha todas as janelas
# Salvamento (o formato é deduzido pela extensão)
cv2.imwrite("photo_copie.png", image)
A verificação if image is None é indispensável: esquecer essa checagem é a principal causa de erros crípticos entre iniciantes.
Espaços de cores: a armadilha do BGR
Atenção à armadilha clássica: o OpenCV carrega as imagens em BGR (azul, verde, vermelho) e não em RGB. Se você exibir uma imagem do OpenCV com o Matplotlib sem conversão, os vermelhos e os azuis ficam invertidos.
import cv2
image = cv2.imread("photo.jpg")
# Conversão de BGR para RGB (necessária para Matplotlib, Pillow, etc.)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Conversão para tons de cinza (um único canal)
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
print(gris.shape)
# (720, 1280) : sem dimensão de cor
# Conversão para HSV (matiz, saturação, valor)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
O espaço HSV é valioso para isolar uma cor, pois o matiz é independente da luminosidade:
import numpy as np
# Isolar as áreas azuis da imagem
borne_basse = np.array([100, 100, 50]) # matiz, saturação, valor mínimos
borne_haute = np.array([130, 255, 255])
masque = cv2.inRange(hsv, borne_basse, borne_haute)
# Aplicar a máscara: somente as áreas azuis permanecem visíveis
resultat = cv2.bitwise_and(image, image, mask=masque)
Redimensionar e recortar
import cv2
image = cv2.imread("photo.jpg")
hauteur, largeur = image.shape[:2]
# Redimensionamento para dimensões específicas
petite = cv2.resize(image, (640, 360))
# Redimensionamento por fator (aqui: metade) mantendo as proporções
moitie = cv2.resize(image, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
# Recorte: uma imagem é um array NumPy, então fatiamos por slices
# Sintaxe: image[y1:y2, x1:x2]
recadree = image[100:400, 200:600]
cv2.imwrite("recadree.jpg", recadree)
Para reduzir uma imagem, a interpolação cv2.INTER_AREA dá o melhor resultado; para ampliá-la, prefira cv2.INTER_CUBIC ou cv2.INTER_LINEAR.
Desenhar em uma imagem
As funções de desenho modificam a imagem no próprio objeto. As cores estão em BGR:
import cv2
image = cv2.imread("photo.jpg")
# Retângulo: cantos opostos, cor BGR, espessura (-1 = preenchido)
cv2.rectangle(image, (50, 50), (250, 200), (0, 255, 0), 2)
# Círculo: centro, raio
cv2.circle(image, (400, 300), 60, (255, 0, 0), 3)
# Linha
cv2.line(image, (0, 0), (640, 360), (0, 0, 255), 2)
# Texto
cv2.putText(image, "Detection", (50, 40),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255), 2)
cv2.imwrite("annotee.jpg", image)
Desfoques e filtros
O desfoque gaussiano é o bloco básico do pré-processamento: ele atenua o ruído antes de uma detecção de contornos ou de uma limiarização.
import cv2
image = cv2.imread("photo.jpg")
# Desfoque gaussiano: o kernel (15, 15) deve ter dimensões ímpares
flou = cv2.GaussianBlur(image, (15, 15), 0)
# Desfoque mediano: muito eficaz contra o ruído "sal e pimenta"
median = cv2.medianBlur(image, 5)
# Filtro bilateral: suaviza as áreas planas preservando os contornos
bilateral = cv2.bilateralFilter(image, 9, 75, 75)
Quanto maior o kernel, mais pronunciado é o desfoque. Um kernel (5, 5) geralmente basta como pré-processamento.
Detecção de contornos: Canny e findContours
O algoritmo de Canny detecta os contornos (as áreas de forte mudança de intensidade):
import cv2
image = cv2.imread("photo.jpg")
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Reduzir o ruído antes da detecção
gris_flou = cv2.GaussianBlur(gris, (5, 5), 0)
# Canny: os dois limiares controlam a sensibilidade
# Abaixo de 50: rejeitado; acima de 150: contorno garantido
contours_canny = cv2.Canny(gris_flou, 50, 150)
cv2.imwrite("canny.jpg", contours_canny)
findContours vai além: ele extrai os contornos na forma de listas de pontos, utilizáveis para medir, contar ou delimitar objetos:
# findContours espera uma imagem binária (resultado de Canny ou de uma limiarização)
contours, _ = cv2.findContours(contours_canny,
cv2.RETR_EXTERNAL, # somente contornos externos
cv2.CHAIN_APPROX_SIMPLE) # compressão dos pontos
print(f"Nombre de contours trouvés : {len(contours)}")
# Delimitar cada contorno suficientemente grande
for contour in contours:
aire = cv2.contourArea(contour)
if aire > 500: # ignorar o ruído
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imwrite("objets_detectes.jpg", image)
Limiarização
A limiarização (thresholding) transforma uma imagem em tons de cinza em uma imagem binária (preto e branco), etapa prévia a diversas análises:
import cv2
gris = cv2.imread("document.jpg", cv2.IMREAD_GRAYSCALE)
# Limiarização simples: todo pixel > 127 vira 255, senão 0
_, binaire = cv2.threshold(gris, 127, 255, cv2.THRESH_BINARY)
# Limiarização de Otsu: o limiar ideal é calculado automaticamente
_, otsu = cv2.threshold(gris, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Limiarização adaptativa: limiar calculado localmente, ideal para um
# documento fotografado com iluminação irregular
adaptatif = cv2.adaptiveThreshold(gris, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
Detecção de rostos com cascatas de Haar
O OpenCV traz classificadores pré-treinados para detectar rostos, olhos ou sorrisos. Aqui está um detector de rostos completo:
import cv2
# Carregamento do classificador pré-treinado fornecido com o OpenCV
cascade_visages = cv2.CascadeClassifier(
cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)
image = cv2.imread("groupe.jpg")
if image is None:
raise FileNotFoundError("Impossible de lire groupe.jpg")
# A detecção trabalha sobre uma imagem em tons de cinza
gris = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detectMultiScale retorna um retângulo (x, y, largura, altura) por rosto
visages = cascade_visages.detectMultiScale(
gris,
scaleFactor=1.1, # redução da imagem a cada passada (10 %)
minNeighbors=5, # mais alto = menos falsos positivos
minSize=(40, 40), # tamanho mínimo de um rosto em pixels
)
print(f"{len(visages)} visage(s) détecté(s)")
# 3 visage(s) détecté(s)
# Delimitar cada rosto
for (x, y, w, h) in visages:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imwrite("visages_detectes.jpg", image)
cv2.imshow("Visages", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Ajuste minNeighbors se a detecção produzir muitos falsos positivos (aumente-o) ou deixar de detectar rostos (diminua-o). As cascatas de Haar são rápidas, mas sensíveis à orientação: elas detectam principalmente rostos de frente.
Ler um fluxo de vídeo ou a webcam
Um vídeo nada mais é que uma sequência de imagens: lemos os quadros um a um em um loop. O exemplo a seguir detecta rostos ao vivo na webcam:
import cv2
cascade_visages = cv2.CascadeClassifier(
cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)
# 0 = primeira webcam; substitua por "video.mp4" para um arquivo
capture = cv2.VideoCapture(0)
if not capture.isOpened():
raise RuntimeError("Impossible d'ouvrir la webcam")
while True:
# ret é False se a leitura falhar (fim do arquivo, webcam desconectada)
ret, frame = capture.read()
if not ret:
break
gris = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
visages = cascade_visages.detectMultiScale(gris, 1.1, 5, minSize=(40, 40))
for (x, y, w, h) in visages:
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Webcam - appuyez sur Q pour quitter", frame)
# waitKey(1): aguardar 1 ms; sair se a tecla Q for pressionada
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Liberar a webcam e fechar as janelas
capture.release()
cv2.destroyAllWindows()
Indo além: o módulo DNN
As cascatas de Haar datam de 2001. Para detecções mais precisas e mais robustas, o OpenCV integra um módulo de redes neurais profundas (cv2.dnn) capaz de carregar modelos pré-treinados nos formatos ONNX, TensorFlow ou Caffe:
import cv2
# Exemplo: carregar um modelo de detecção no formato ONNX
# net = cv2.dnn.readNetFromONNX("modele_detection.onnx")
Os caminhos naturais após este guia: a detecção de rostos por DNN (modelo YuNet integrado ao OpenCV via cv2.FaceDetectorYN), a detecção de objetos com YOLO e a estimativa de pose. O princípio continua o mesmo deste guia: ler a imagem, pré-processá-la, analisá-la, desenhar o resultado.
Boas práticas
- Verifique sempre o retorno de
imreade decapture.read(): o OpenCV retornaNoneouFalseem vez de lançar uma exceção. - Nunca esqueça a conversão BGR/RGB quando você troca imagens com Matplotlib, Pillow ou um modelo de deep learning.
- Pré-processe antes de analisar: tons de cinza seguidos de desfoque gaussiano quase sempre melhoram o Canny e a limiarização.
- Libere os recursos:
capture.release()ecv2.destroyAllWindows()ao fim do processamento de vídeo. - Trabalhe em cópias (
image.copy()) quando for desenhar, para manter a original intacta. - Filtre por área (
cv2.contourArea) apósfindContourspara eliminar o ruído.
Conclusão
Agora você tem os fundamentos do processamento de imagens com OpenCV: carregamento e salvamento, espaços de cores (e a armadilha do BGR), redimensionamento, filtros, detecção de contornos com Canny e findContours, limiarização, detecção de rostos por cascatas de Haar e processamento de um fluxo de webcam em tempo real. Essas operações cobrem a grande maioria das necessidades de um primeiro projeto de visão computacional.
Para se aprofundar, consulte a documentação oficial do OpenCV e seus tutoriais de Python, e depois explore o módulo DNN para passar às detecções por aprendizado profundo.
Sobre InSkillCoach
Especialista em treinamento e tecnologias
Coach especializado em tecnologias avançadas e IA, apoiado pela GNeurone Inc.
Certificações:
- AWS Certified Solutions Architect – Professional
- Certificações Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comentários
Os comentários são alimentados pelo GitHub Discussions
Conecte-se com o GitHub para participar da discussão