Histogramas
Todo el código de ejemplo se encuentra en Google Colab para su ejecución directa. Si no puedes ejecutar OpenCV localmente, puedes usar Google Colab junto con matplotlib para visualizar imágenes. Además la imagen que se utilizará en los ejemplos son imagen.jpg.
Objetivo
- Comprender qué es un histograma en procesamiento de imágenes.
- Aprender a calcular histogramas de imágenes en escala de grises.
- Diferenciar el uso de
cv.calcHist()de OpenCV ynp.histogram()de NumPy. - Visualizar y analizar la distribución de intensidades de píxeles en una imagen.
Conceptos clave
- Histograma: Representación gráfica de la distribución de frecuencias de los niveles de intensidad de los píxeles de una imagen.
- Bins (intervalos): División del rango de intensidades en grupos (ejemplo: 256 bins para valores de 0 a 255).
- Rango: Intervalo de valores a analizar (en imágenes de 8 bits es [0,255]).
- Máscara: Permite calcular el histograma en una región de interés (ROI) en lugar de en toda la imagen.
Funciones principales en OpenCV
-
cv.calcHist(images, channels, mask, histSize, ranges): Calcula el histograma directamente desde una imagen.
images: Lista con la imagen de entrada.channels: Canal o canales a analizar (0=azul, 1=verde, 2=rojo en BGR).mask: Región de interés opcional.histSize: Número de bins.ranges: Rango de valores de los bins.
-
np.histogram(array, bins, range): Calcula el histograma a partir de un arreglo NumPy (por ejemplo, aplanando la imagen con
ravel()).- Devuelve dos valores:
histcon las frecuencias ybinscon los bordes de cada intervalo.
- Devuelve dos valores:
Teoría
Un histograma es una representación gráfica de la distribución de intensidades de una imagen. En el eje X se ubican los valores posibles de los píxeles (en imágenes de 8 bits varían de 0 a 255) y en el eje Y la cantidad de píxeles que tienen cada valor de intensidad.
En otras palabras, es una manera de “resumir” una imagen para entender su brillo, contraste y la distribución de intensidades. Al observar un histograma podemos intuir:
- Si la imagen es oscura (concentración en el lado izquierdo).
- Si es clara (concentración en el lado derecho).
- Si tiene buen contraste (valores distribuidos en todo el rango).
- Si está apagada o con poco contraste (valores agrupados en una zona reducida).
Cálculo del histograma
El cálculo consiste en contar cuántos píxeles tienen una determinada intensidad (en imágenes en escala de grises) o un determinado valor en un canal (en imágenes a color).
Luego, esos valores se representan gráficamente, con:
- Eje X → Intensidad de los píxeles (0 a 255).
- Eje Y → Número de píxeles que tienen esa intensidad.

Figura 1: Histograma para la imagen de ejemplo.
- Python (PC con entorno gráfico)
- Python (Google Colab)
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
# Leer imagen en escala de grises
img = cv.imread("imagen2.jpg", cv.IMREAD_GRAYSCALE)
# Histograma con OpenCV
calc_hist_opencv = cv.calcHist(
[img], # Lista con la imagen de entrada
[0], # Canal a analizar (0 = gris, en BGR sería 0=azul,1=verde,2=rojo)
None, # No se aplica máscara (si fuera una ROI se pasa aquí)
[256], # Número de bins (niveles de intensidad)
[0, 255]) # Rango de intensidades (0 a 255 -> 256 valores)
# Histograma con NumPy
calc_hist_numpy, bins = np.histogram(
img.ravel(), # Aplanar la imagen en un vector 1D
256, # Número de bins (256 intensidades posibles)
[0, 255]) # Rango de intensidades
# flatten convierte la matriz de OpenCV en un vector 1D para facilitar la lectura
print("calculo con opencv: ", calc_hist_opencv.flatten()[:10])
print("calculo con numpy: ", calc_hist_numpy[:10])
# Graficar con Matplotlib (Eje x -> valor de los píxeles, Eje y -> cantidad de píxeles).
plt.figure(figsize=(12,5))
# Histograma OpenCV
plt.subplot(1,2,1)
plt.plot(calc_hist_opencv, color='blue')
plt.title("Histograma con OpenCV")
plt.xlabel("Intensidad de píxeles")
plt.ylabel("Frecuencia")
# Histograma NumPy
plt.subplot(1,2,2)
plt.plot(calc_hist_numpy, color='red')
plt.title("Histograma con NumPy")
plt.xlabel("Intensidad de píxeles")
plt.ylabel("Frecuencia")
plt.tight_layout()
plt.show()
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# Subir archivo
uploaded = files.upload()
filename = list(uploaded.keys())[0]
# Leer imagen en escala de grises
img = cv.imread(filename, cv.IMREAD_GRAYSCALE)
# Histograma con OpenCV
calc_hist_opencv = cv.calcHist(
[img], # Lista con la imagen de entrada
[0], # Canal a analizar (0 = gris, en BGR sería 0=azul,1=verde,2=rojo)
None, # No se aplica máscara (si fuera una ROI se pasa aquí)
[256], # Número de bins (niveles de intensidad)
[0, 255]) # Rango de intensidades (0 a 255 -> 256 valores)
# Histograma con NumPy
calc_hist_numpy, bins = np.histogram(
img.ravel(), # Aplanar la imagen en un vector 1D
256, # Número de bins (256 intensidades posibles)
[0, 255]) # Rango de intensidades
# flatten convierte la matriz de OpenCV en un vector 1D para facilitar la lectura
print("calculo con opencv: ", calc_hist_opencv.flatten()[:10])
print("calculo con numpy: ", calc_hist_numpy[:10])
# Graficar con Matplotlib (Eje x -> valor de los píxeles, Eje y -> cantidad de píxeles).
plt.figure(figsize=(12,5))
# Histograma OpenCV
plt.subplot(1,2,1)
plt.plot(calc_hist_opencv, color='blue')
plt.title("Histograma con OpenCV")
plt.xlabel("Intensidad de píxeles")
plt.ylabel("Frecuencia")
# Histograma NumPy
plt.subplot(1,2,2)
plt.plot(calc_hist_numpy, color='red')
plt.title("Histograma con NumPy")
plt.xlabel("Intensidad de píxeles")
plt.ylabel("Frecuencia")
plt.tight_layout()
plt.show()