画像内のクリスマスツリーを検出する方法、Pythonを使用して達成する

この記事のデータコードを取得する方法、パブリックアカウント:Xiao Zhang Python、舞台裏の返信キーワード:クリスマスツリー

みなさん、こんにちは。昨日はクリスマスでした。皆さんが楽しい時間を過ごしているかどうかはわかりません。とにかく、とても嬉しいです。夕方、ルームメイトはみんな寮に一人で出かけて、好きなことをしていました。 〜本当にかっこいい〜

この記事では、Pythonを使用して、画像内のクリスマスツリーの認識とマーキングを実現します。これは、コンピュータービジョンでのオブジェクト検出として理解できます。まず、ここではニューラルネットワークが使用されていないことを宣言します。これらはすべて従来の方法です。

最初に効果を見てください、以下は元の写真です

Snipaste_2020-12-26_14-45-50

検出の最終結果は次のとおりです。

Snipaste_2020-12-26_14-46-39

写真のクリスマスツリーの輪郭は赤い線でマークされており、効果は良さそうです〜、以下は3つの部分に分かれているアルゴリズム実装の全体的なアイデアです

1.画像の特徴点を抽出します(画像の明るさ、色相、彩度に応じて)

上記の6つの画像では、カラフルなライトにより、クリスマスツリーは、冷たくて青みがかった背景とは対照的に、画像全体で明るく暖かく見えます。

Snipaste_2020-12-26_15-18-53

上記の考え方に従って、まずクリスマスツリーの特徴点を抽出します。ここでは、明るさ、色相、彩度の3つの角度から条件付きで画像をフィルタリングし、画像に設定されているターゲット特徴点を除外します。基準は以下の通りです

  • 1.明るさのスクリーニングを行うときは、最初にRGBをグレースケール画像に変換し、グレースケール値が220より大きい領域を抽出します(元の画像標準0〜255)。
  • 2.画像​​をRGB(0-255)からHSV(0-1)色空間に変換し、色相(色相チャネル)値が0.2未満または0.95を超えるHSVの領域抽出します。0.2未満は抽出することです。画像の黄色がかった赤みがかった画像特徴点、0.95より大きい場合は、クリスマスツリーの端にある赤紫の領域に対応します。
  • 3.画像のHSV色空間で、彩度と値が0.7より大きい部分を抽出します。

ここにHSVの簡単な紹介があります。HSVはRGBの3つのチャネルに似た画像の色空間です。RGBはそれぞれ赤、緑、青の3つのチャネルを表し、HSVは色相(色相)、彩度(彩度)を表します。 、値(明るさ);

  • 色相H:角度で測定した値の範囲は、赤から反時計回りに0°〜360°、赤は0°、緑は120°、青は240°です。それらの補色は次のとおりです。黄色は60°、シアンは180°、マゼンタは300°です。(この記事では、0〜300度を0〜1.0の範囲の値に変換します)
  • 彩度S:値の範囲は0.0〜1.0です。
  • 明るさV:値の範囲は0.0(黒)から1.0(白)です。

ここでnumpyの上記3つのフィルタ、最終的にバイナリ白黒画像を得るために画像処理によるlogical_andlogical_or、上記の3つの条件の重合のための方法。

Snipaste_2020-12-26_15-48-34

上の写真からわかるように、写真の黒い点は抽出された特徴点(クリスマスツリー)です。基本的な輪郭は出ていますが、少しノイズがあります。図2と4を参照してください。照明と地平線建物内の特徴も抽出されていますが、これらは必要なものではないため、次の手順が必要です:クラスタリング、これらのノイズポイントを削除する

2. DBSCANアルゴリズムを使用して、特徴点をクラスター化します

前の手順で特徴点を取得した後、特徴点セットを以下にクラスタリングします。点セットのクラスタリングについては、ここでは空間密度に基づくDNSCANアルゴリズムを使用します。このアルゴリズムはscikit-learnパッケージにカプセル化されており、使用時に直接呼び出されます。ただし、いくつかのパラメーター設定が含まれるため、使用する場合は2つのパラメーターに注意する必要があります。

  • eps 、アルゴリズムのパラメータは、クラスとクラスサンプル間の最大距離を表します。異なるデータセットと距離関数の場合、このパラメータは異なる値に設定する必要があります。ここでは、画像の対角長の0.04倍に設定します。適応できるように大解像度の写真は小解像度の写真にも適用できます
  • min_samples 、ある点を中心として、周囲のサンプル数(サンプル自体を含む)。値が小さすぎると最終カテゴリが多すぎ、値が大きすぎると最終カテゴリも大きくなります。少数;この記事は10に設定されています;

特徴点が分類された後、クリスマスツリーの特徴点は最終的に赤でマークされ、効果は次のようになります。

Snipaste_2020-12-26_16-29-26ストロークを拡大した後の効果:

Snipaste_2020-12-26_16-19-55

図2、3、および4の特徴点は、異なる色でマークされた2つのカテゴリに分割されていることがわかります。条件付きフィルタは後で実行されます。画像内で特徴点の数が最も多いカテゴリのみ(クリスマス)ツリー)を選択できます画像のノイズ除去

3.ターゲットフィーチャポイントセットの凸包を計算し、元の画像に描画します

この最後のステップはscipy、凸包ConvexHullパッケージ計算方法を使用して特徴点を設定matplotlibし、元の凸包に描かれた凸包を再利用することではるかに簡単です。

Snipaste_2020-12-26_14-46-39

概要

特徴点をフィルタリングするためのしきい値条件としての前述の色相と彩度の使用、後でDBSCANクラスタリングアルゴリズムの使用など、記事のいくつかの技術的なポイントは、学ぶ価値があります。これらのアイデアは、クリスマスツリーだけでなく、使用済み他のオブジェクトを上から検出しますが、より多くの思考と練習が必要です

最後に、クラスタリングアルゴリズムが従来のKMeansの代わりにDBSCANを使用する理由は次のとおりです。KMeansは分類時にカテゴリの数を設定する必要があり(カテゴリの数は事前に決定されていません)、ユークリッド距離のみが参照として使用されるためです。分類。最終的な分類結果は理想的ではありません。下の図を参照してください。

KMeansアルゴリズム

Snipaste_2020-12-26_16-50-56

DBSCANアルゴリズム

Snipaste_2020-12-26_16-51-10

記事で使用されているコアコード

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

起動スクリプト

'''
@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()

さて、上記はこの記事の全内容です。内容が良いと思われる場合は、いいね、共有、メッセージを残してください。最後に、読んでいただきありがとうございます!

参照リンク:https://stackoverflow.com/questions/20772893/how-to-detect-a-christmas-tree

おすすめ

転載: blog.csdn.net/weixin_42512684/article/details/111771862