Imagem é array numpy, nada mais
Toda imagem em OpenCV é np.ndarray de shape (H, W, 3) com dtype uint8 (0–255). Essa é a mentalidade mais importante do módulo: se você sabe numpy, sabe manipular pixel. Inverter cores? 255 - img. Cropar? Slice. Resize? Broadcasting controlado.
import cv2
import numpy as np
img = cv2.imread("foto.jpg") # shape (H, W, 3) BGR uint8
print(img.shape, img.dtype) # (1080, 1920, 3) uint8
# Crop ROI = slicing numpy puro
roi = img[100:400, 200:600] # H-slice, W-slice
# Brilho +30 com saturação correta
brighter = cv2.add(img, 30) # cv2.add satura em 255
# vs img + 30 (numpy faz overflow wrap - bug comum)
# BGR -> RGB antes de mostrar em matplotlib
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)img + 30 (numpy) faz overflow: pixel 250 vira 24. cv2.add(img, 30) satura em 255. Diferença silenciosa que destrói imagem clara.
Color spaces: BGR, RGB, HSV, LAB
Escolher color space certo resolve problema antes de começar. HSV separa matiz (H) de brilho (V) — perfeito pra segmentar cor sob iluminação variável. LAB aproxima percepção humana — bom pra comparar similaridade de cor.
# Segmentar pixels verdes robustamente via HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_green = np.array([35, 50, 50]) # H, S, V
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
green_only = cv2.bitwise_and(img, img, mask=mask)Filtros: blur, Sobel, Canny
Convolução 2D é a base. Blur (suaviza), Sobel (derivada = borda), Canny (pipeline completo de detecção de bordas com non-max suppression e double threshold).
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# Canny com thresholds adaptativos via mediana
v = np.median(blur)
lower = int(max(0, 0.66 * v))
upper = int(min(255, 1.33 * v))
edges = cv2.Canny(blur, lower, upper)
# Fechar pequenas quebras de borda
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)Contornos e decisão: CV clássico ou DL?
findContours devolve lista de polígonos fechados. Útil pra contar objetos, medir área, detectar formas geométricas determinísticas.
contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Filtrar por área mínima (remove ruído)
big = [c for c in contours if cv2.contourArea(c) > 500]
cv2.drawContours(img, big, -1, (0, 255, 0), 2)Regra prática: se o problema tem especificação geométrica clara (cantos de documento, código QR, linhas de quadra), CV clássico em CPU é a melhor ferramenta. Se precisa de semântica (“isso é um gato?”), vá de CNN. A maioria dos pipelines reais combina os dois.