Cómo detectar el árbol de Navidad en la imagen, use Python para ayudarlo a lograr

El método para obtener el código de datos de este artículo, cuenta pública: Xiao Zhang Python, palabra clave de respuesta entre bastidores: árbol de Navidad

Hola a todos, estoy poniendo a cero. Ayer fue Navidad. No sé si lo están pasando bien. De todos modos, estoy muy feliz. Por la noche, todos mis compañeros de cuarto salieron solos al dormitorio, haciendo lo que quisieran. ~ Es realmente genial ~

Este artículo utilizará Python para realizar el reconocimiento y marcado del árbol de Navidad en la imagen, lo que puede entenderse como detección de objetos en visión por computadora. Primero, permítanme declarar que aquí no se usa una red neuronal. Todos son métodos tradicionales.

Primero mira el efecto, la siguiente es la imagen original

Snipaste_2020-12-26_14-45-50

El siguiente es el resultado final de la detección:

Snipaste_2020-12-26_14-46-39

El contorno del árbol de Navidad en la imagen está marcado con una línea roja, el efecto se ve bien ~, la siguiente es la idea general de la implementación del algoritmo, que se divide en tres partes

1. Extraiga los puntos característicos de la imagen (según el brillo, el tono y la saturación de la imagen)

En las 6 imágenes que se muestran arriba, debido a las luces de colores, el árbol de Navidad aparece más brillante y cálido en toda la imagen, en contraste con el fondo más frío y azulado;

Snipaste_2020-12-26_15-18-53

De acuerdo con las ideas mencionadas anteriormente, primero extraiga los puntos característicos del árbol de Navidad. Aquí, la imagen se filtra condicionalmente desde los tres ángulos de brillo, tono y saturación para filtrar el punto característico de destino establecido en la imagen. La selección los criterios son los siguientes

  • 1. Al realizar el tramado de brillo, primero convierta RGB a una imagen en escala de grises y extraiga el área con un valor de escala de grises superior a 220 (estándar de imagen original 0-255)
  • 2. Convierta la imagen del espacio de color RGB (0-255) a HSV (0-1) y extraiga el área en HSV cuyo valor de tono (canal de tono) sea menor que 0.2 o mayor que 0.95. Menos de 0.2 es para extraer imágenes amarillentas y rojizas en la imagen Puntos característicos, mayor que 0,95 corresponde al área rojo púrpura en el borde del árbol de Navidad
  • 3. En el espacio de color HSV de la imagen, extraiga las partes cuya saturación y valor sean superiores a 0,7 ;

Aquí hay una breve introducción a HSV. HSV es un espacio de color de la imagen, similar a los tres canales de RGB. RGB representa los tres canales de rojo, verde y azul respectivamente; y HSV representa tono (tono), saturación (saturación) , valor (brillo);

  • Tono H: medido por ángulo, el rango de valores es 0 ° ~ 360 °, comenzando desde el rojo y contando en sentido antihorario, el rojo es 0 °, el verde es 120 ° y el azul es 240 °. Sus colores complementarios son: amarillo es 60 °, cian es 180 ° y magenta es 300 °; (este artículo convierte 0-300 grados a valores de rango de 0-1.0)
  • Saturación S: El rango de valores es 0.0 ~ 1.0;
  • Brillo V: el rango de valores es de 0.0 (negro) a 1.0 (blanco).

De acuerdo con los tres filtros anteriores, el procesamiento de imágenes para obtener finalmente una imagen binaria en blanco y negro, aquí en números logical_andy logical_ormétodos para la polimerización de las tres condiciones anteriores;

Snipaste_2020-12-26_15-48-34

Como puede ver en la imagen de arriba, los puntos negros en la imagen son los puntos característicos extraídos (árbol de Navidad). El contorno básico se ha eliminado, pero habrá un poco de ruido. Consulte las Figuras 2 y 4. La iluminación y el horizonte También se han extraído las características del edificio, pero estas no son las que necesitamos, por lo que se necesita el siguiente paso: agrupamiento , para eliminar estos puntos de ruido

2. Utilice el algoritmo DBSCAN para agrupar puntos característicos

Una vez que se obtienen los puntos de características en el paso anterior, el conjunto de puntos de características se agrupará a continuación. Con respecto a la agrupación de conjuntos de puntos, aquí se utiliza el algoritmo DNSCAN basado en la densidad espacial. Este algoritmo se ha encapsulado en el paquete scikit-learn y puede ser llamado directamente cuando se usa., Pero debido a que hay algunas configuraciones de parámetros involucradas, se debe prestar atención a dos parámetros al usar:

  • eps , Un parámetro del algoritmo representa la distancia máxima entre la clase y la muestra de la clase. Para diferentes conjuntos de datos y funciones de distancia, este parámetro debe establecerse en un valor diferente; aquí se establece en 0.04 veces la longitud diagonal de la imagen, para que se pueda adaptar Las imágenes de gran resolución también se pueden aplicar a las imágenes de pequeña resolución
  • min_samples , Asumiendo un cierto punto como centro, el número de muestras circundantes (incluida la propia muestra); si el valor es demasiado pequeño, la categoría final será demasiado, y si el valor es demasiado grande, la categoría final será demasiado pocos; este artículo se establece en 10;

Una vez clasificados los puntos característicos, los puntos característicos del árbol de Navidad se marcan finalmente en rojo y el efecto es el siguiente:

Snipaste_2020-12-26_16-29-26El efecto después del trazo se expande:

Snipaste_2020-12-26_16-19-55

Se puede observar que los puntos característicos en las Figuras 2, 3 y 4 se dividen en dos categorías, marcadas con diferentes colores; se hará un filtro condicional más adelante: solo la categoría con el mayor número de puntos característicos en la imagen (Navidad árbol) se puede seleccionar Eliminación de ruido en la imagen

3. Calcule el casco convexo del conjunto de puntos de características de destino y dibuje en la imagen original.

Este último paso es mucho más simple, con el conjunto de puntos de característica, utilizando el scipymétodo de cálculo del paquete ConvexHull del casco convexo, luego reutilizar matplotlibel casco convexo que se dibuja en el original

Snipaste_2020-12-26_14-46-39

resumen

Vale la pena aprender algunos puntos técnicos del artículo, como el uso mencionado anteriormente de tono y saturación como condiciones de umbral para filtrar puntos característicos, y el uso del algoritmo de agrupamiento DBSCAN más adelante; estas ideas no solo se limitan a árboles de Navidad, sino que también pueden ser usado Detecta algunos otros objetos desde arriba, pero requiere más pensamiento y más práctica

Finalmente, aquí es por qué el algoritmo de agrupación usa DBSCAN en lugar de los KMeans clásicos; porque KMeans necesita establecer el número de categorías al clasificar (el número de categorías no se determina de antemano), y solo la distancia euclidiana se usa como referencia cuando clasificación. El resultado de clasificación final no es ideal, consulte la figura siguiente

Algoritmo KMeans

Snipaste_2020-12-26_16-50-56

Algoritmo DBSCAN

Snipaste_2020-12-26_16-51-10

Código principal utilizado en el artículo

from PIL import Image
import numpy as np
import scipy
import matplotlib.colors as colors
from sklearn.cluster import DBSCAN
from math import ceil,sqrt




'''
Inputs:
    
    rgbimg: M,N,3 numpy 包含 uint(0-255) color image
    
    hueleftthr: Scalar constant to maximum  hue in  yellow-green region
    
    huerightthr: Scalar constant to maximum allowed hue in blue-purple region
    
    satthr: Scalar constant to select minimum allow saturation
    
    valthre: Scalar constant to select minimum allow value
    
    monothr: Scalar constant to select minimum allow monochrome
    
    maxpoints: Scalar constant maximum number of pixels  to forward to the DBSCAN clustering algoritm

    proxthresh: Proximity threshold to use for DBSCAN, as da fraction of the diagonal size of thre image
                接近阈值占图像对角线尺寸


Outputs:
    
    borderseg: [K,2,2] Nested list containing K pairs of x- and y- pixel values for drawimg the tree border
    
    X:  [P,2] List of pixels that passed the threshold step
    
    labels: [Q,2] List of cluster labels for points in  Xslice(see below)
    
    Xslice: [Q,2] Reduced list of pixels to be passed to DBSCAN


'''

'''实现脚本'''

def findtree(rgbimg,
             hueleftthr = 0.2,
             huerightthr = 0.95,
             satthr =0.7,
             valthr = 0.7,
             monothr = 220,
             maxpoints = 5000,
             proxthresh = 0.04):
    # 将 RGB 图像转化为 灰度图
    grayimg = np.asarray(Image.fromarray(rgbimg).convert('L'))

    # 将 rbg => hsv(float [0,1.0])
    hsvimg = colors.rgb_to_hsv(rgbimg.astype(float)/255)

    # 二值化阈值图像初始化

    binimg = np.zeros((rgbimg.shape[0],rgbimg.shape[1]))

    #1, heu < 0.2 or hue > 0.95(red or yellow)
    #2, saturated and bright both greater than 0.7
    # 满足以上条件被认为是圣诞树上的灯
    boolidx = np.logical_and(
        np.logical_and(
            np.logical_or((hsvimg[:,:,0]<hueleftthr),
            (hsvimg[:,:,0]>huerightthr)),
            (hsvimg[:,:,1]>satthr)),
            (hsvimg[:,:,2]>valthr))


    # 找到满足 hsv 标准的像素,赋值为255
    binimg[np.where(boolidx)] = 255
    # 添加像素来满足garay brightness 条件
    binimg[np.where(grayimg>monothr)] = 255

    # 用 DBSCAN 聚类算法分割这些点
    X = np.transpose(np.where(binimg==255))
    Xslice = X
    nsample = len(Xslice)

    if nsample > maxpoints:
        # 确保样本数不超过 DNSCAN 算法最大限度
        Xslice = X[range(0,nsample,int(ceil(float(nsample/maxpoints))))] # 将样本每隔几个采样一次

    # 将 DNSCAN 阈值接近像素单位,并运行 DBSCAN
    pixproxthr = proxthresh * sqrt(binimg.shape[0]**2 + binimg.shape[1]**2) # 对角巷长*proxthresh
    db = DBSCAN(eps = pixproxthr,min_samples=10).fit(Xslice) # 拟合样本
    labels = db.labels_.astype(int)

    # 寻找最大聚类
    unique_labels = set(labels)
    maxclustpt = 0

    for k in unique_labels:
        class_numbers = [index[0] for index in np.argwhere(labels==k)]
        if(len(class_numbers) > maxclustpt):
            points = Xslice[class_numbers]
            hull = scipy.spatial.ConvexHull(points) # 建立凸包
            maxclustpt = len(class_numbers)
            borderseg = [[points[simplex,0], points[simplex,1]] for simplex in hull.simplices]


    return borderseg,X,labels,Xslice

Script de inicio

'''
@author:zeroing
@wx公众号:小张Python

'''

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from findtree import findtree
import os

path_dir = 'D:/ceshi_11/findtree'

path_list = [os.path.join(path_dir,str(i)) for i in os.listdir(path_dir)]


# 初始化figure size

fgsz = (16,8)

figthresh = plt.figure(figsize = fgsz,facecolor ='w')
figclust  = plt.figure(figsize = fgsz,facecolor ='w')
figcltwo = plt.figure(figsize = fgsz,facecolor = 'w')
figborder = plt.figure(figsize = fgsz,facecolor = 'w')
figorigin = plt.figure(figsize = fgsz,facecolor = 'w')


# 每张图设置一个 窗口名
figthresh.canvas.set_window_title('Thresholded HSV and Monochrome Brightness')
figclust.canvas.set_window_title('DBSCAN Clusters (Raw Pixel Output)')
figcltwo.canvas.set_window_title('DBSCAN Clusters (Slightly Dilated for Display)')
figborder.canvas.set_window_title('Trees with Borders')
figorigin.canvas.set_window_title("Original Image")


for ii,name in enumerate(path_list):
    # 打开图片
    rgbimg = np.asarray(Image.open(str(name)))

    # 运行脚本找到 bordeseg,X,Labels,Xslce
    borderseg,X,labels,Xslice = findtree(rgbimg)

    # 展示阈值分割后的图像
    axthresh =  figthresh.add_subplot(2,3,ii+1)
    axthresh.set_xticks([])
    axthresh.set_yticks([])
    binimg = np.zeros((rgbimg.shape[0],rgbimg.shape[1]))
    for v,h in X:
        binimg[v,h] = 255 # 初步筛选之后坐标点

    axthresh.imshow(binimg,interpolation = 'nearest',cmap = 'Greys')

    # Display color-coded clusters
    axclust = figclust.add_subplot(2,3,ii+1)
    axclust.set_xticks([])
    axclust.set_yticks([])
    axcltwo = figcltwo.add_subplot(2,3,ii+1)
    axcltwo.set_xticks([])
    axcltwo.set_yticks([])
    axcltwo.imshow(binimg,interpolation = 'nearest',cmap = 'Greys')

    clustimg = np.ones(rgbimg.shape)
    unique_labels = set(labels)
    # 为每个聚类生成单个颜色
    plcol = cm.rainbow_r(np.linspace(0,1,len(unique_labels)))
    print('plcol',plcol)
    for lbl,pix in zip(labels,Xslice):
        for col,unqlbl in zip(plcol,unique_labels):
            if lbl == unqlbl:
                # -1 表示无聚类成员
                if lbl == -1:
                    col = [0.0,0.0,0.0,1.0]
                for ij in range(3):
                    clustimg[pix[0],pix[1],ij] = col[ij]
            # 扩张 图像,用于更好展示
                axcltwo.plot(pix[1],pix[0],'o',markerfacecolor= col,markersize = 1,markeredgecolor = col)

    axclust.imshow(clustimg)
    axcltwo.set_xlim(0,binimg.shape[1]-1)
    axcltwo.set_ylim(binimg.shape[0],-1)

    # 在原图树边缘进行绘制

    axborder = figborder.add_subplot(2,3,ii+1)
    axborder.set_axis_off()
    axborder.imshow(rgbimg,interpolation ='nearest')
    for vseg,hseg in borderseg:
        axborder.plot(hseg,vseg,'g-',lw =3)
    axborder.set_xlim(0,binimg.shape[1]-1)
    axborder.set_ylim(binimg.shape[0],-1)


    # 保存原图
    origin_fig1 = figorigin.add_subplot(2, 3, ii + 1)
    origin_fig1.set_axis_off()
    origin_fig1.imshow(rgbimg, interpolation='nearest')
    axborder.set_xlim(0, binimg.shape[1] - 1)
    axborder.set_ylim(binimg.shape[0], -1)


    # axborder.savefig("D:/ceshi_11/findtree/final_")

    print(name,'Sucessfully find it !!!!!!!!')

plt.show()

Bueno, lo anterior es todo el contenido de este artículo. Si crees que el contenido es bueno, dale me gusta, comparte y deja un mensaje. Finalmente, ¡gracias por leer!

Enlace de referencia: https://stackoverflow.com/questions/20772893/how-to-detect-a-christmas-tree

Supongo que te gusta

Origin blog.csdn.net/weixin_42512684/article/details/111771862
Recomendado
Clasificación