OpenCVMiddleCoding
Что такое гистограмма в обработке изображений и как её вычислять и выравнивать?
cv2.calcHist вычисляет гистограмму по каналам; cv2.equalizeHist выравнивает глобально (только uint8 grayscale); для локального и щадящего выравнивания используйте CLAHE через cv2.createCLAHE.
Гистограммы в обработке изображений: вычисление и выравнивание
Гистограмма изображения показывает, сколько пикселей имеют каждое значение яркости (0–255). Она используется для оценки экспозиции, контраста, а также как основа для нормализации и порогового метода Otsu.
Вычисление гистограммы через cv2.calcHist
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("photo.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv2.calcHist(images, channels, mask, histSize, ranges)
hist = cv2.calcHist(
[gray], # список изображений
[0], # индекс канала (0 для одноканального)
None, # маска (None = всё изображение)
[256], # число бинов
[0, 256] # диапазон значений
)
# hist.shape == (256, 1), dtype == float32
plt.plot(hist)
plt.xlabel("Яркость")
plt.ylabel("Количество пикселей")
plt.title("Гистограмма")
plt.show()
Гистограмма для цветного изображения (по каналам)
colors = {"b": 0, "g": 1, "r": 2}
for color_name, ch_idx in colors.items():
hist = cv2.calcHist([img], [ch_idx], None, [256], [0, 256])
plt.plot(hist, color=color_name, label=color_name.upper())
plt.legend()
plt.title("Гистограмма по каналам BGR")
plt.show()
Глобальное выравнивание гистограммы: cv2.equalizeHist
Перераспределяет яркости так, чтобы гистограмма стала примерно равномерной. Работает только с одноканальным uint8-изображением.
equalized = cv2.equalizeHist(gray)
# Сравнение до/после
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
axes[0, 0].imshow(gray, cmap="gray")
axes[0, 0].set_title("Оригинал")
axes[0, 1].imshow(equalized, cmap="gray")
axes[0, 1].set_title("После equalizeHist")
axes[1, 0].plot(cv2.calcHist([gray], [0], None, [256], [0, 256]))
axes[1, 0].set_title("Гистограмма оригинала")
axes[1, 1].plot(cv2.calcHist([equalized], [0], None, [256], [0, 256]))
axes[1, 1].set_title("Гистограмма после выравнивания")
plt.tight_layout()
plt.show()
Адаптивное выравнивание: CLAHE
Глобальное выравнивание может пересветить одни области и затемнить другие. CLAHE (Contrast Limited Adaptive Histogram Equalization) работает на тайлах и ограничивает усиление контраста.
# Создать объект CLAHE
clahe = cv2.createCLAHE(
clipLimit=2.0, # максимальное усиление контраста в тайле
tileGridSize=(8, 8) # изображение разбивается на сетку 8x8 тайлов
)
clahe_result = clahe.apply(gray)
# Для цветного изображения применяем только к L-каналу в LAB
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
l_clahe = clahe.apply(l)
lab_clahe = cv2.merge([l_clahe, a, b])
result_bgr = cv2.cvtColor(lab_clahe, cv2.COLOR_LAB2BGR)
Сравнение методов
- equalizeHist: быстро, глобально, агрессивно — хорошо для документов и QR-кодов.
- CLAHE: адаптивно, плавнее — рекомендуется для медицинских снимков, лиц, природных сцен.
- cv2.normalize: просто растягивает диапазон до [0,255] — самый мягкий вариант.
# normalize как простейшая альтернатива
norm = cv2.normalize(gray, None, 0, 255, cv2.NORM_MINMAX)
Подводные камни
- equalizeHist только для uint8: передача float32 или uint16 вызовет ошибку или неверный результат.
- Применение к BGR напрямую: equalizeHist/CLAHE к каждому BGR-каналу независимо искажает цвет — переводите в LAB или HSV и обрабатывайте только яркостный канал.
- clipLimit слишком мал: clipLimit=1.0 в CLAHE почти не меняет изображение; слишком большой (>4.0) — вносит шум.
- tileGridSize больше объектов: если тайл содержит только фон, выравнивание усилит шум, а не детали.
- Маска в calcHist: маска должна быть uint8 с значениями 0 и 255, не bool — иначе результат неверен.
- histSize и ranges несогласованы: histSize=[256] с ranges=[0,256] — стандарт; но histSize=[128] с ranges=[0,256] даст бины по 2 единицы — нужно знать, что считаете.
- Нет визуального сравнения: оценивать выравнивание только по метрикам (std яркости) недостаточно — артефакты могут быть незаметны в числах, но очевидны визуально.
- Применение к уже нормализованным данным: если изображение уже имеет равномерное распределение яркостей, повторное equalizeHist введёт артефакты.
Common mistakes
- Объяснять
histograms equalizationтолько синтаксисом без shape, dtype, состояния или режима выполнения. - Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
- Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.
What the interviewer is testing
- Может ли связать
histograms equalizationс реальным контрактом входов и выходов. - Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
- Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.