Por que funciona: Unidade 1.2: Captura da imagem, Unidade 1.3: A imagem como dado
Equalização de histograma e CLAHE no OpenCV
Teste as correções automáticas de contraste do OpenCV, cv2.equalizeHist e CLAHE, numa foto escura com um abajur aceso, e meça por que elas ficam mais longe da exposição alvo (30,6 e 35,5) do que um simples multiplicador (9,1).
Tradução assistida por máquina do original em inglês. Números, código e referências foram conferidos com ele; o texto não foi revisado por um falante nativo. Ler o original. Os labs interativos e o código estão em inglês.
A equalização de histograma é o botão de contraste automático: ela espalha o brilho de uma foto por toda a faixa de valores, sem precisar saber o quão escura a foto estava.

cv2.equalizeHist no seu brilho. Quadro do HDR Photographic Survey [5].Onde você já viu isso: os botões Contraste automático e Níveis automáticos dos editores de foto; Cores › Automático › Equalizar no GIMP.
TL;DR. A equalização automática faz qualquer foto usar a faixa inteira de brilho, o que é diferente de fazê-la parecer uma exposição melhor da cena. Na foto escura ela deixa uma distância de 30,6 até a exposição alvo, e sua variante local, o CLAHE, 35,5, contra 9,1 de um simples multiplicador.
O problema
As lições 3 e 4 do projeto corrigiram a foto com um único número, escolhido comparando com o alvo. Na maior parte das vezes não existe alvo. Uma correção automática precisa decidir só a partir da foto, e a regra óbvia é deixar o histograma plano: usar cada nível de brilho mais ou menos na mesma quantidade.
Equalize o brilho
cv2.equalizeHist recebe um canal de 8 bits e remapeia seus valores para que o histograma fique o mais plano que os valores permitem [1, 2]. Para preservar as cores, ela é aplicada só ao brilho: converta para YCrCb, cujo canal Y é o brilho e Cr, Cb carregam a cor, equalize Y e converta de volta.
O CLAHE (contrast-limited adaptive histogram equalization, equalização adaptativa de histograma com contraste limitado) faz o mesmo em pequenos blocos da imagem (8 × 8 por padrão) e limita quanto cada bloco pode ser esticado, para que o ruído em áreas uniformes não seja amplificado [2]. A versão por blocos foi descrita por Pizer e colegas em 1987 [4], e a de contraste limitado é de Zuiderveld, publicada em 1994 [3].
import cv2
import numpy as np
img = cv2.imread("dark.webp")
ref = cv2.imread("reference.webp")
def distance(a, b):
return np.abs(a.astype(int) - b.astype(int)).mean()
def on_brightness(img, op):
# equaliza só o brilho: Y do YCrCb, cor intacta
ycc = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
ycc[..., 0] = op(ycc[..., 0])
return cv2.cvtColor(ycc, cv2.COLOR_YCrCb2BGR)
equalized = on_brightness(img, cv2.equalizeHist)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
local = on_brightness(img, clahe.apply)
print("equalizeHist:", round(distance(equalized, ref), 1))
print("CLAHE: ", round(distance(local, ref), 1))equalizeHist: 30.6 CLAHE: 35.5
| Função | Argumentos | O que faz |
|---|---|---|
cv2.equalizeHist(channel) | um canal uint8 | deixa o histograma plano |
cv2.createCLAHE(clipLimit, tileGridSize) | quanto um bloco pode esticar; a grade de blocos | um objeto CLAHE |
clahe.apply(channel) | um canal uint8 | CLAHE nesse canal |
cv2.COLOR_BGR2YCrCb | — | brilho (Y) separado da cor (Cr, Cb) |
Os dois ganham de não fazer nada (47,0) e os dois perdem para um único multiplicador (9,1).
def half(x):
return cv2.resize(x, None, fx=0.5, fy=0.5)
small = [half(x) for x in (equalized, local, ref)]
cv2.imshow("equalizeHist | CLAHE | reference", np.hstack(small))
A equalização gasta a faixa de brilho no que é mais comum, e esta foto é quase toda parede e mesa escuras. Elas são esticadas por quase toda a faixa e ficam cinza-médio, enquanto o abajur e a área iluminada, uma pequena parte dos pixels, são espremidos nos últimos níveis do topo. O CLAHE evita que o abajur estoure ainda mais, mas clareia cada bloco escuro por conta própria, e assim a parede e a mesa perdem a transição do escuro para o claro que a exposição alvo tem. O próprio tutorial do OpenCV cita esse caso: a equalização “won’t work good in places where there is large intensity variations”, quando há pixels claros e escuros ao mesmo tempo [2].
Quebre
equalizeHist aceita um único canal. Passe a imagem colorida e o OpenCV para com uma asserção.
# equalizeHist recebe um canal de 8 bits, não uma imagem colorida
cv2.equalizeHist(img)Traceback (most recent call last): File "<exec>", line 2, in <module> cv2.error: OpenCV(4.11.0) /home/runner/work/pyodide-recipes/pyodide-recipes/packages/opencv-python/build/opencv-python-4.11.0.86/opencv/modules/imgproc/src/histogram.cpp:3440: error: (-215:Assertion failed) _src.type() == CV_8UC1 in function 'equalizeHist'
A mensagem mostra a verificação que falhou, _src.type() == CV_8UC1: um canal (C1) de valores de 8 bits sem sinal (8U).
Sua vez
Aplique o objeto CLAHE da primeira célula à versão em cinza de img e guarde o resultado em out.
out = None # CLAHE na versão em cinza de img
print(out)Quando usar
A equalização e o CLAHE são as ferramentas certas quando não há exposição alvo para alcançar e o objetivo é enxergar detalhes, ou quando a imagem alimenta um algoritmo que precisa mais de contraste do que de realismo. São a ferramenta errada para devolver a uma foto a aparência que ela deveria ter, e foi isso que este projeto mediu.
O projeto fica com a foto multiplicada e a finaliza na lição 6.
Por que funciona
Deixar o histograma plano passa cada valor pela distribuição acumulada da imagem, de modo que fatias iguais dos pixels caem em fatias iguais da faixa de saída [1]. Essa regra não sabe nada sobre a cena, e por isso se saiu mal aqui. Por que a exposição alvo tem a distribuição de tons que tem, e por que nenhuma exposição única segura o abajur e a mesa escura ao mesmo tempo, está em Noise and dynamic range (em inglês) e High dynamic range (em inglês).
Erros comuns
- Uma imagem colorida em
equalizeHist. A célula Quebre. - Equalizar B, G e R separadamente. Cada canal ganha seu próprio remapeamento, e o equilíbrio de cor muda. Equalize só o brilho.
- Um
clipLimitgrande numa imagem com ruído. O CLAHE então amplifica o ruído nas regiões uniformes, o problema que o limite existe para evitar [2]. - Ler “mais contraste” como “melhor”. Uma foto equalizada costuma parecer mais viva e muitas vezes está mais longe da realidade. Meça contra uma referência quando tiver uma.
Referências
[1] Gonzalez, R. C., & Woods, R. E. (2018). Digital Image Processing (4th ed.), section 3.3 “Histogram Processing”, Histogram Equalization, p. 148, and Local Histogram Processing, p. 173. Pearson.
[2] OpenCV. Histograms - 2: Histogram Equalization (Python tutorial, doc/py_tutorials/py_imgproc/py_histograms/py_histogram_equalization/py_histogram_equalization.markdown). OpenCV 4.11.0, commit 31b0eee. github.com/opencv/opencv
[3] Zuiderveld, K. (1994). Contrast Limited Adaptive Histogram Equalization. In Graphics Gems (ISBN 978-0-12-336156-1), pp. 474–485. Elsevier. doi:10.1016/B978-0-12-336156-1.50061-6
[4] Pizer, S. M., Amburn, E. P., Austin, J. D., Cromartie, R., Geselowitz, A., Greer, T., ter Haar Romeny, B., Zimmerman, J. B., & Zuiderveld, K. (1987). Adaptive histogram equalization and its variations. Computer Vision, Graphics, and Image Processing, 39(3), 355–368. doi:10.1016/S0734-189X(87)80186-X
[5] Fairchild, M. D. (2007). The HDR Photographic Survey. IS&T 15th Color Imaging Conference, pp. 233–238. doi:10.2352/CIC.2007.15.1.art00044