[OpenCV-Python] 22 Histograma

OpenCV-Python: Procesamiento de imágenes IV en OpenCV

22 Histograma

22.1 Cálculo, dibujo y análisis de histograma

Objetivo
  • Usar la función OpenCV o Numpy para calcular el histograma
  • Usar la función Opencv o Matplotlib para dibujar un histograma
  • Las funciones que se deben aprender son: cv2.calcHist (), np.histogram ()
principio
  ¿Qué es un histograma? A través del histograma, puede tener una comprensión general de la distribución de grises de toda la imagen. El eje x del histograma es el valor de gris (0 a 255) y el eje y es el número de puntos con el mismo valor de gris en la imagen.
El histograma es en realidad otra interpretación de la imagen. Tome la siguiente imagen como ejemplo: a través del histograma, podemos tener una comprensión intuitiva del contraste, el brillo y la distribución de la escala de grises de la imagen. Casi todo el software de procesamiento de imágenes proporciona una función de análisis de histograma. La siguiente imagen es del sitio web de Cambridge in Color. Se recomienda encarecidamente que visite este sitio web para obtener más información.
Echemos un vistazo a esta imagen y su histograma juntos. (Recuerde que el histograma se dibuja basándose en una imagen en escala de grises, no en una imagen en color). El área izquierda del histograma muestra el número de píxeles más oscuros y el lado derecho muestra el número de píxeles más brillantes. En esta imagen, puede ver que el área oscura es más grande que las dos áreas y que hay pocos píxeles en el medio.

Ejemplo de histograma

22.1.1 Histograma estadístico

Ahora que sabemos qué es un histograma, ¿cómo obtener un histograma de una imagen?
  Tanto OpenCV como Numpy tienen funciones integradas para hacer esto. Antes de utilizar estas funciones, debemos querer comprender la terminología relacionada con los histogramas.

BINS: el histograma anterior muestra el número de píxeles correspondientes a cada valor de gris. Si el valor de píxel es de 0 a 255, necesita 256 números para mostrar el histograma de arriba. Sin embargo, ¿qué sucede si no necesita saber el número de píxeles para cada valor de píxel, pero solo desea saber el número de píxeles entre dos valores de píxel? Por ejemplo, queremos saber el número de píxeles con valores de píxeles entre 0 y 15, seguido de 16 a 31, ..., 240 a 255. Solo necesitamos 16 valores para dibujar el histograma. El contenido demostrado por el ejemplo en OpenCVTutorials sobre histogramas.
¿Cómo hacerlo? Solo necesita dividir los 256 valores originales en 16 grupos y tomar la suma de cada grupo. Y cada grupo aquí se llama BIN. Hay 256 BIN en el primer ejemplo y 16 BIN en el segundo ejemplo. En la documentación de OpenCV, use histSize para representar BINS.
DIMS: Indica la cantidad de parámetros que recopilamos datos. En este ejemplo, solo consideramos una cosa sobre los datos recopilados: el valor gris. Así que aquí está 1.
RANGO: Es el rango de valores de gris a contar, generalmente hablando [0, 256], es decir, todos los valores de gris.

Usar la función de histograma estadístico OpenCV cv2.calcHist puede ayudarnos a contar el histograma de una imagen

Puede ayudarnos a contar el histograma de una imagen. Familiaricémonos con esta función y sus parámetros juntos:
cv2.calcHist (imágenes, canales, máscara, histSize, rangos [, hist [, acumular]])
  1. imágenes: imagen original (el formato de imagen es uint8 o float32). Al pasar una función, debe encerrarse entre corchetes [], por ejemplo: [img].
  2. canales: también es necesario encerrarlos entre corchetes, le indicará a la función que queremos contar el histograma de esa imagen. Si la imagen de entrada es una imagen en escala de grises, su valor es [0]; si es una imagen en color, los parámetros entrantes pueden ser [0], [1], [2], que corresponden a los canales B, G y R respectivamente.
  3. máscara: imagen de máscara. Para contar el histograma de toda la imagen, configúrelo en Ninguno. Pero si desea contar el histograma de una determinada parte de la imagen, debe crear una imagen de máscara y usarla. (Hay ejemplos a continuación)
  4. histSize: el número de BIN. También debe incluirse entre corchetes, por ejemplo: [256].
  5. rangos: rango de valor de píxel, generalmente [0, 256]
Comencemos con una imagen simple. Cargue una imagen en formato de escala de grises y cuente el histograma de la imagen.

img = cv2.imread('home.jpg',0)
# 别忘了中括号 [img],[0],None,[256],[0,256] ,只有 mask 没有中括号
hist = cv2.calcHist([img],[0],None,[256],[0,256])

hist es una matriz de 256x1, cada valor representa el número de píxeles correspondientes al valor de subgris.

Utilice Numpy para contar histogramas La función np.histogram () en Numpy también puede ayudarnos a contar histogramas. También puede probar el siguiente código:

#img.ravel() 将图像转成一维数组,这里没有中括号。
hist,bins = np.histogram(img.ravel(),256,[0,256])

hist es el mismo que el calculado anteriormente. Pero los contenedores aquí son 257, porque Numpy calcula los
contenedores de las siguientes formas: 0-0,99, 1-1,99, 2-2,99, etc. Entonces, el último rango es 255-255.99.
Para expresarlo, se agrega 256 al final de los contenedores. Pero no necesitamos 256, 255 es suficiente.
Otros: Numpy también tiene una función np.bincount (), que se ejecuta diez veces más rápido que np.histgram. Entonces, para histogramas unidimensionales, será mejor que usemos esta función. No olvide establecer minlength = 256 cuando utilice np.bincount. P.ej,

hist = np.bincount (img.ravel (), minlength = 256)
Nota: La función OpenCV es 40 veces más rápida que np.histgram (). Así que apégate a las funciones de OpenCV.
Ahora es el momento de aprender a dibujar un histograma.

22.1.2 Dibujar un histograma

Hay dos formas de dibujar un histograma:
  1. Método corto (método simple): use la función de dibujo en Matplotlib.
  2. Long Way (método complejo): utilice la función de trazado de OpenCV
Utilice Matplotlib Matplotlib tiene una función de trazado de histograma: matplotlib.pyplot.hist (),
que puede contar y trazar histogramas directamente. Debe utilizar la función calcHist () o np.histogram () para
contar el histograma. el código se muestra a continuación:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()

Obtendrá una imagen como esta:

Trazado de histograma en Matplotlib

O simplemente puede usar la función de dibujo de matplotlib, que es útil para dibujar histogramas multicanal (BGR) al mismo tiempo. Pero primero tiene que decirle a la función de trazado dónde están los datos de su histograma. Ejecute el siguiente código:

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('home.jpg')
color = ('b','g','r')
# 对一个列表或数组既要遍历索引又要遍历元素时
# 使用内置 enumerrate 函数会有更加直接,优美的做法
#enumerate 会将数组或列表组成一个索引序列。
# 使我们再获取索引和索引内容的时候更加方便
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()

resultado:

Trazado de histograma en Matplotlib

Del histograma de arriba, puede inferir que la curva azul es la más en el lado derecho (obviamente, estos son el cielo).
Usar OpenCV para dibujar un histograma usando las funciones propias de OpenCV es más problemático. No lo presentaré aquí. está interesado, puede estudiarlo usted mismo. Puede consultar el ejemplo oficial de OpenCV-Python2.

22.1.3 Usar una máscara

Para contar el histograma de un área local determinada de la imagen, solo es necesario construir una imagen de máscara. Configure la parte que se contará como blanca y la parte restante como negra, lo que constituye una imagen de máscara. Luego pase la imagen de la máscara a la función.

img = cv2.imread('home.jpg',0)

# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img,img,mask = mask)

# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])

plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])

plt.show()

Los resultados son los siguientes, donde la línea azul es el histograma de toda la imagen y la línea verde es el histograma después del enmascaramiento.
    Ejemplo de histograma

22.2 Ecualización de histograma

Objetivo En
  esta sección, aprenderemos el concepto de ecualización de histograma y cómo usarlo para mejorar el contraste de las imágenes.

Principio
  Imagínese lo que sucede si la mayoría de los valores de píxeles de una imagen se concentran en un rango de valores de píxeles. Por ejemplo, si una imagen es muy brillante en su conjunto, todos los valores de píxeles deben ser muy altos. Pero una imagen de alta calidad debe tener una amplia distribución de valores de píxeles. Por lo tanto, debe estirar su histograma horizontalmente (como se muestra en la figura siguiente). Esto es lo que hace la ecualización del histograma. Normalmente, esta operación mejorará el contraste de la imagen.

Ecualización de histogramas
Se recomienda que lea el artículo sobre ecualización de histogramas en Wikipedia. La explicación es muy poderosa, después de leerla, creo que tendrá una comprensión detallada de todo el proceso. Primero veremos cómo usar Numpy para realizar la ecualización del histograma, y ​​luego aprendemos a usar OpenCV para realizar la ecualización del histograma.

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('wiki.jpg',0)
#flatten() 将数组变成一维
hist,bins = np.histogram(img.flatten(),256,[0,256])
# 计算累积分布图
cdf = hist.cumsum()
cdf_normalized = cdf * hist.max()/ cdf.max()
plt.plot(cdf_normalized, color = 'b')
plt.hist(img.flatten(),256,[0,256], color = 'r')
plt.xlim([0,256])
plt.legend(('cdf','histogram'), loc = 'upper left')
plt.show()

Ecualización de histogramas

Podemos ver que la mayor parte del histograma está en la parte con mayor valor de gris y la distribución está muy concentrada. Y esperamos que la distribución del histograma esté relativamente dispersa y pueda cubrir todo el eje x. Por lo tanto, necesitamos una función de transformación que nos ayude a asignar el histograma actual a un histograma ampliamente distribuido. Esto es lo que hace la ecualización del histograma.
  Ahora queremos encontrar el valor mínimo (excepto 0) en el histograma y usarlo en la fórmula de ecualización del histograma en la wiki. Pero usé la matriz de máscaras de Numpy aquí. Todas las operaciones en matrices enmascaradas solo son válidas para elementos no enmascarados. Puede consultar la documentación de Numpy para obtener más información sobre las matrices de máscaras.

# 构建 Numpy 掩模数组, cdf 为原数组,当数组元素为 0 时,掩盖(计算时被忽略)。
cdf_m = np.ma.masked_equal(cdf,0)
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())
# 对被掩盖的元素赋值,这里赋值为 0
cdf = np.ma.filled(cdf_m,0).astype('uint8')

Ahora que hemos obtenido una tabla, podemos conocer el valor del píxel de salida correspondiente al píxel de entrada consultando la tabla. Solo necesitamos aplicar esta transformación a la imagen.

img2 = cdf[img]

Luego dibujamos el histograma y el mapa de distribución acumulativa de acuerdo con el método anterior, y los resultados son los siguientes:

Ecualización de histogramas

Otra característica importante es que incluso si nuestra imagen de entrada es una imagen más oscura (a diferencia de la imagen brillante general que usamos anteriormente), se puede obtener el mismo resultado después de la ecualización del histograma. Por lo tanto, la ecualización de histograma se usa a menudo como una herramienta de referencia para hacer que todas las imágenes tengan las mismas condiciones de brillo. Esto es útil en muchas situaciones. Por ejemplo, para el reconocimiento facial, antes de entrenar al clasificador, todas las imágenes del conjunto de entrenamiento deben ecualizarse en histograma para que alcancen la misma condición de brillo.

22.2.1 Ecualización de histograma en OpenCV

La función de ecualización de histograma en OpenCV es cv2.equalizeHist (). La imagen de entrada de esta función es solo una imagen en escala de grises y el resultado de salida es la imagen después de la ecualización del histograma.
El siguiente código aún realiza la ecualización de histograma en la imagen de arriba:

img = cv2.imread('wiki.jpg',0)
equ = cv2.equalizeHist(img)
res = np.hstack((img,equ)) #stacking images side-by-side
cv2.imwrite('res.png',res)

Ecualización de histogramas

Ahora puedes tomar algunas fotos de diferente brillo y probarlas por ti mismo. Cuando los datos del histograma se concentran en un cierto rango de valores de gris, la ecualización del histograma es útil. Sin embargo, si el píxel cambia mucho y el rango de escala de grises ocupado es muy amplio, por ejemplo, cuando hay píxeles muy brillantes y píxeles muy oscuros. Consulte el enlace SOF en más recursos.

22.2.2 CLAHE

Ecualización de histograma adaptable de contraste limitado La ecualización de histograma que hicimos anteriormente cambiará el contraste de toda la imagen, pero en muchos casos, el efecto de esto no es bueno. Por ejemplo, las siguientes figuras son la imagen de entrada y la imagen de salida después de la ecualización del histograma.

Problema de la HE Global
  De hecho, después de la ecualización del histograma, se cambió el contraste del fondo de la imagen. Pero si comparas las caras de las estatuas en las dos imágenes, hemos perdido mucha información debido al brillo. La razón fundamental de este resultado es que el histograma de esta imagen no se concentra en un área determinada (intenta dibujar su histograma, lo entenderás).
Para resolver este problema, necesitamos utilizar la ecualización de histograma adaptativa. En este caso, la imagen completa se dividirá en muchos bloques pequeños, estos bloques pequeños se denominan "mosaicos" (el tamaño predeterminado de los mosaicos en OpenCV es 8x8), y luego se realiza la ecualización del histograma en cada bloque pequeño (similar a la anterior).
  Entonces, en cada área, el histograma se concentrará en un área pequeña (a menos que haya interferencia de ruido). Si hay ruido, el ruido se amplificará. Para evitar esta situación, utilice el límite de contraste. Para cada bloque pequeño, si el intervalo del histograma excede el límite superior de contraste, los píxeles que contiene se distribuirán uniformemente a otros intervalos y, a continuación, se ecualizará el histograma. Finalmente, para eliminar el límite "artificial" (debido al algoritmo) entre cada bloque pequeño, se utiliza la diferencia bilineal para unir los bloques pequeños.
El siguiente código muestra cómo usar CLAHE en OpenCV.

import numpy as np
import cv2
img = cv2.imread('tsukuba_l.png',0)
# create a CLAHE object (Arguments are optional).
# 不知道为什么我没好到 createCLAHE 这个模块
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
cv2.imwrite('clahe_2.jpg',cl1)

Aquí está el resultado, compárelo con el resultado anterior, especialmente el área de la estatua:

Resultado de CLAHE

22.3 Histograma 2D

Objetivo En
  esta sección aprenderemos a dibujar un histograma 2D, lo usaremos en la siguiente sección.

22.3.1 Introducción

En la parte anterior, presentamos cómo dibujar un histograma unidimensional. La razón por la que se llama unidimensional es porque solo consideramos una característica de la imagen: el valor de gris. Pero en el histograma 2D, tenemos que considerar dos características de la imagen. Para el histograma de imágenes en color, generalmente debemos considerar cada color (tono) y saturación (saturación). Dibuje un histograma 2D basado en estas dos características.
La documentación oficial de OpenCV contiene un ejemplo de cómo crear un histograma de color. Esta sección es para aprender a dibujar histogramas de color con todos, lo que nos ayudará a aprender sobre la proyección de histogramas en la siguiente sección.

22.3.2 Histograma 2D en OpenCV

Es simple y conveniente usar la función cv2.calcHist () para calcular el histograma. Si desea dibujar un histograma de color, primero debemos convertir el espacio de color de la imagen de BGR a HSV. (Recuerde, para calcular un histograma unidimensional, debe convertir de BGR a HSV). Para calcular un histograma 2D, los parámetros de la función deben modificarse de la siguiente manera:
  • canales = [0, 1] Porque necesitamos procesar los canales H y S al mismo tiempo.
  • bins = [180, 256] El canal H es 180 y el canal S es 256.
  • rango = [0, 180, 0, 256] El valor de H varía de 0 a 180 y el valor de S varía de 0 a 256.
el código se muestra a continuación:

import cv2
import numpy as np

img = cv2.imread('home.jpg')
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

¡Eso es todo, es fácil!

22.3.3 Histograma 2D en Numpy

Numpy también proporciona una función para dibujar histogramas 2D: np.histogram2d (). (Recuerde, usamos np.histogram () al dibujar histogramas 1D).

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('home.jpg')
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])

El primer parámetro es el canal H, el segundo parámetro es el canal S, el tercer parámetro es el número de contenedores y el cuarto parámetro es el rango de valores.
Ahora vamos a ver cómo dibujar un histograma de colores.

22.3.4 Dibujar histograma 2D

Método 1: Usando cv2.imshow () obtenemos el resultado es una matriz bidimensional de 180x256. Entonces podemos usar la función cv2.imshow () para mostrarlo. Pero esta es una imagen en escala de grises. A menos que sepamos el valor del canal H de diferentes colores, simplemente no sabemos qué color representa.
  Método 2: Usar Matplotlib () También podemos usar la función matplotlib.pyplot.imshow () para dibujar un histograma 2D y luego emparejarlo con un mapa de color diferente (color_map). De esta forma, tendremos una comprensión más intuitiva de la magnitud del valor representado por cada punto. Pero al igual que la pregunta anterior, todavía no sabes qué representa el color. Sin embargo, sigo prefiriendo este método, es simple y fácil de usar.
Nota: Cuando utilice esta función, recuerde configurar el parámetro de interpolación al más cercano.
el código se muestra a continuación:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('home.jpg')
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
hist = cv2.calcHist( [hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] )

plt.imshow(hist,interpolation = 'nearest')
plt.show()

A continuación se muestra la imagen de entrada y el histograma de color. El eje X muestra el valor S y el eje Y muestra el valor H.

Histogramas 2D
En el histograma, puede ver que hay valores relativamente altos cerca de H = 100 y S = 100. Esta parte corresponde al azul del cielo. De manera similar, otro pico está cerca de H = 25 y S = 100. El color amarillo de este palacio corresponde. Puede modificar la imagen utilizando un software de edición de imágenes (GIMP) y luego dibujar un histograma para ver si estoy en lo cierto.
  Método 3: OpenCV Grid tiene un ejemplo de histograma de color en la documentación oficial. Ejecute este código, el histograma de color que ve también muestra el color correspondiente. En pocas palabras: la salida es un histograma codificado por color. El efecto es muy bueno (aunque hay que añadir mucho código).
En ese código, el autor primero creó un mapa de colores en formato HSV y luego lo convirtió a formato BGR. Luego multiplique el histograma obtenido por el histograma de color. El autor también dio algunos pasos para eliminar pequeños puntos aislados, obteniendo así un buen histograma.
Te dejo el análisis del código, ve y juega por ti mismo. El siguiente es el resultado obtenido después de ejecutar este código en el gráfico anterior:

Histogramas 2D usando muestras OpenCV-Python

Desde el histograma, podemos ver claramente los colores que representan, azul, cambio de color y blanco traído por el tablero de ajedrez, ¡hermoso! ! !

# -*- coding: utf-8 -*-
#!/usr/bin/env python
import numpy as np
import cv2
from time import clock
import sys
import video
#video 模块也是 opencv 官方文档中自带的
if __name__ == '__main__':
    # 构建 HSV 颜色地图
    hsv_map = np.zeros((180, 256, 3), np.uint8)
    # np.indices 可以返回由数组索引构建的新数组。
    # 例如: np.indices (( 3,2 ));其中( 3,2 )为原来数组的维度:行和列。
    # 返回值首先看输入的参数有几维:( 3,2 )有 2 维,所以从输出的结果应该是
    # [[a],[b]], 其中包含两个 3 行, 2 列数组。
    # 第二看每一维的大小,第一维为 3, 所以 a 中的值就 0 到 2 (最大索引数),
    # a 中的每一个值就是它的行索引;同样的方法得到 b (列索引)
    # 结果就是
    # array([[[0, 0],
    # [1, 1],
    # [2, 2]],
    #
    # [[0, 1],
    # [0, 1],
    # [0, 1]]])
    h, s = np.indices(hsv_map.shape[:2])
    hsv_map[:,:,0] = h
    hsv_map[:,:,1] = s
    hsv_map[:,:,2] = 255
    hsv_map = cv2.cvtColor(hsv_map, cv2.COLOR_HSV2BGR)
    cv2.imshow('hsv_map', hsv_map)
    cv2.namedWindow('hist', 0)
    hist_scale = 10
    def set_scale(val):
        global hist_scale
        hist_scale = val
    cv2.createTrackbar('scale', 'hist', hist_scale, 32, set_scale)
    try: fn = sys.argv[1]
    except: fn = 0
    cam = video.create_capture(fn, fallback='synth:bg=../cpp/baboon.jpg:class=chess:noise=0.05')
    while True:
        flag, frame = cam.read()
        cv2.imshow('camera', frame)
        # 图像金字塔
        # 通过图像金字塔降低分辨率,但不会对直方图有太大影响。
        # 但这种低分辨率,可以很好抑制噪声,从而去除孤立的小点对直方图的影响。
        small = cv2.pyrDown(frame)
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        # 取 v 通道 ( 亮度 ) 的值。
        # 没有用过这种写法,还是改用最常见的用法。
        #dark = hsv[...,2] < 32
        # 此步操作得到的是一个布尔矩阵,小于 32 的为真,大于 32 的为假。
        dark = hsv[:,:,2] < 32
        hsv[dark] = 0
        h = cv2.calcHist( [hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] )
        #numpy.clip(a, a_min, a_max, out=None)[source]
        #Given an interval, values outside the interval are clipped to the interval edges.
        #For example, if an interval of [0, 1] is specified, values smaller
        #than 0 become 0, and values larger than 1 become 1.
        #>>> a = np.arange(10)
        #>>> np.clip(a, 1, 8)
        #array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8])
        h = np.clip(h*0.005*hist_scale, 0, 1)
        #In numpy one can use the 'newaxis' object in the slicing syntax to create an
        #axis of length one. one can also use None instead of newaxis,
        #the effect is exactly the same
        #h 从一维变成 3 维
        vis = hsv_map*h[:,:,np.newaxis] / 255.0
        cv2.imshow('hist', vis)
        ch = 0xFF & cv2.waitKey(1)
        if ch == 27:
            break
    cv2.destroyAllWindows()

22.4 Retroproyección del histograma

Objetivo En
  esta sección, aprenderemos el
principio de
  la retroproyección de histograma.La retroproyección de histograma es propuesta por Michael J. Swain y Dana H. Ballard en su artículo "Indexación a través de histogramas de color".
  ¿Así que qué es lo? Se puede utilizar para la segmentación de imágenes o para encontrar la parte de nuestro interés en la imagen. En pocas palabras, generará una imagen del mismo tamaño que la imagen de entrada (para buscar), y cada valor de píxel representa la probabilidad de que el punto correspondiente en la imagen de entrada pertenezca al objeto de destino. Para explicarlo en palabras más simples, cuanto mayor sea el valor de píxel (más blanco) el punto en la imagen de salida, más probable es que represente el objetivo que estamos buscando (donde se encuentra la imagen de entrada). Esta es una explicación intuitiva. La proyección de histograma se utiliza a menudo junto con el algoritmo de desplazamiento de levas, etc.
¿Cómo deberíamos implementar este algoritmo? Primero necesitamos crear un histograma de una imagen que contenga el objetivo que estamos buscando (en nuestro ejemplo, buscamos pasto, nada más).
El objeto que queremos encontrar debe llenar esta imagen tanto como sea posible (es decir, lo mejor es tener y solo el objeto que queremos encontrar en esta imagen). Es mejor utilizar un histograma de color, porque el color de un objeto se puede utilizar mejor para la segmentación de imágenes y el reconocimiento de objetos que su escala de grises. Luego proyectamos este histograma de color en la imagen de entrada para encontrar nuestro objetivo, es decir, para encontrar la probabilidad del valor de píxel de cada píxel en la imagen de entrada en el histograma, de modo que obtengamos una imagen de probabilidad y finalmente establezcamos el umbral apropiado. binarizar la imagen de probabilidad es tan simple como eso.

22.4.1 Algoritmos en Numpy

El algoritmo aquí es ligeramente diferente del algoritmo descrito anteriormente.
  Primero, tenemos que crear dos histogramas de color, el histograma de la imagen de destino ('M') y (para buscar) el histograma de la imagen de entrada ('I').

import cv2
import numpy as np
from matplotlib import pyplot as plt

#roi is the object or region of object we need to find
roi = cv2.imread('rose_red.png')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)

#target is the image we search in
target = cv2.imread('rose.png')
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)

# Find the histograms using calcHist. Can be done with np.histogram2d also
M = cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
I = cv2.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )

Calcula la relación:
Inserte la descripción de la imagen aquí
retroproyección R, es decir, crea una nueva imagen basada en la "paleta" de R, donde cada píxel representa la probabilidad de que este punto sea el objetivo. Por ejemplo, B (x, y) = R [h (x, y), s (x, y)], donde h es el valor de tono en el punto (x, y) y s es la
saturación en el punto (x , y) valor. Finalmente agregue otra condición B (x, y) = mínimo [B (x, y), 1].

h,s,v = cv2.split(hsvt)
B = R[h.ravel(),s.ravel()]
B = np.minimum(B,1)
B = B.reshape(hsvt.shape[:2])

Ahora use un operador de disco para hacer la convolución, B = D × B, donde D es el núcleo de convolución.

disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
B=cv2.filter2D(B,-1,disc)
B = np.uint8(B)
cv2.normalize(B,B,0,255,cv2.NORM_MINMAX)

Ahora, el valor de gris más grande en la imagen de salida es la ubicación del objetivo que estamos buscando. Si lo que buscamos es una región, podemos usar un umbral para binarizar la imagen, de modo que podamos obtener un buen resultado.

ret,thresh = cv2.threshold(B,50,255,0)

Eso es.

22.4.2 Retroproyección en OpenCV

La función cv2.calcBackProject () proporcionada por OpenCV se puede utilizar para hacer una retroproyección de histograma. Sus parámetros son básicamente los mismos que los de la función cv2.calcHist. Uno de los parámetros es el histograma del objetivo que queremos encontrar. De manera similar, antes de usar el histograma del objetivo para la retroproyección, primero debemos normalizarlo. El resultado devuelto es una imagen de probabilidad. Luego usamos un núcleo de convolución en forma de disco para realizar una operación de volumen en él, y finalmente usamos un umbral para la binarización. Aquí está el código y los resultados:

import cv2
import numpy as np
roi = cv2.imread('tar.jpg')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
target = cv2.imread('roi.jpg')
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
# calculating object histogram
roihist = cv2.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
# normalize histogram and apply backprojection
# 归一化:原始图像,结果图像,映射到结果图像中的最小值,最大值,归一化类型
#cv2.NORM_MINMAX 对数组的所有值进行转化,使它们线性映射到最小值和最大值之间
# 归一化之后的直方图便于显示,归一化之后就成了 0 到 255 之间的数了。
cv2.normalize(roihist,roihist,0,255,cv2.NORM_MINMAX)
dst = cv2.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
# Now convolute with circular disc
# 此处卷积可以把分散的点连在一起
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
dst=cv2.filter2D(dst,-1,disc)
# threshold and binary AND
ret,thresh = cv2.threshold(dst,50,255,0)
# 别忘了是三通道图像,因此这里使用 merge 变成 3 通道
thresh = cv2.merge((thresh,thresh,thresh))
# 按位操作
res = cv2.bitwise_and(target,thresh)
res = np.hstack((target,thresh,res))
cv2.imwrite('res.jpg',res)
# 显示图像
cv2.imshow('1',res)
cv2.waitKey(0)

A continuación se muestra una imagen que utilicé. Utilizo el área del rectángulo azul en la imagen como objeto de muestreo y luego busco todas las áreas similares (césped) en la imagen según esta muestra.
    Retroproyección de histograma en OpenCV

Para obtener más información, preste atención a la cuenta oficial:
img

Supongo que te gusta

Origin blog.csdn.net/yegeli/article/details/113427041
Recomendado
Clasificación