如何檢測並提取圖像中的聖誕樹,可以用 Python 來實現 !

2021-03-02 小張Python

本文數據代碼獲取方式:在公眾號 小張Python 後臺,回復關鍵字:聖誕樹

大家好,我是 zeroing,前天是聖誕節,不知道大家過開心不,反正我是挺開心的,晚上室友都出去了自己一個人在宿舍,為所欲為~  真的是爽歪歪~

本篇文章將用 Python 來實現圖片中的聖誕樹的識別、標記,可理解為計算機視覺中的物體檢測,先聲明一下哈這裡沒有用到神經網絡,都是傳統方法

先看一下效果,以下是原圖

下面是最終檢測出來的效果圖:

圖中的聖誕樹的外輪廓都用紅線給標記出來了,效果看起來還不錯吧~,下面是算法實現的整體思路,分為三個部分

1,提取圖片特徵點(根據圖像明亮度,色調,飽和度)

上面展示的6張圖像中,因為彩燈原因,聖誕樹在整個圖片中呈現出偏亮、色調偏暖,與背景偏冷、偏青色形成對比;

根據上面提到的思路先對聖誕樹上特徵點進行提取,這裡對圖像分別以亮度、色調、飽和度三個角度對圖像做了條件篩選,篩選出圖像中目標特徵點集,篩選標準如下

1,做亮度篩選時,先將RGB 轉化為灰度圖,提取灰度值大於220的區域(原圖標準 0-255)2,把圖像將RGB(0-255) 轉化為 HSV(0-1)顏色空間,提取 HSV 中 hue (色調通道)值小於 0.2 或大於 0.95 的區域,小於 0.2 是為了提取圖片中偏黃色,紅色的特徵點,大於 0.95 對應聖誕樹邊緣的紫紅色區域3,圖像 HSV 顏色空間中,提取 saturation(飽和度) 和 value(值) 大於 0.7 的部分

這裡簡單介紹一下 HSV ,HSV 為圖片的一種顏色空間,與 RGB 三通道相似,RGB 分別表示紅、綠、藍三種通道;而 HSV 則代表 hue(色調),saturation(飽和度), value (亮度);

色調H:用角度度量,取值範圍為0°~360°,從紅色開始按逆時針方向計算,紅色為0°,綠色為120°,藍色為240°。它們的補色是:黃色為60°,青色為180°,品紅為300°;(本文將0-300度轉化為 0-1.0 範圍數值)亮度V:取值範圍為0.0(黑色)~1.0(白色)。

根據上面三個篩選條件,對圖像進行處理,最終得到一個黑白相間的二值化圖像,這裡用 numpy 中的logical_and 和 logical_or  方法來聚合上面的三種條件;

從上圖可以看到,圖片中的黑點即提取到的特徵點(聖誕樹),基本大致輪廓已經出來了,但會有少許噪點,見圖二、圖四,建築中的燈光、地平線特徵也被提取出來了,但這些不是我們所需要的,所以需要下面的一個步驟:聚類,來剔除這些噪點

2,用 DBSCAN 算法對特徵點進行聚類

上一步得到特徵點之後,下面就對特徵點集進行聚類,關於點集聚類,這裡用基於空間密度的 DNSCAN 算法,這個算法已經被封裝到 scikit-learn包中,使用時直接調用即可,但因為涉及一些參數設置問題,使用時需要注意兩個參數:

eps ,算法中的一個參數,表示類與類樣本間的最大距離,對於不同數據集和距離函數這個參數需要設置不同的值;這裡設置的是 圖片對角線長度的0.04倍,這樣的話既能適應大解析度圖片,也能適用於小解析度的圖片min_samples ,假設以某一點為中心,周圍的樣本數量(包括樣本本身) ;值太小時,最終類別會太多,值太大時,最終類別太少;本文設置為 10 ;

特徵點分類後,最終將聖誕樹特徵點部分全部標為紅色,效果如下:

描邊擴張後效果:

可以看到圖 2,3,4 中的特徵點分別分為兩類,用不同的顏色進行標記;後面再做一次條件篩選:只取圖片中特徵點數量最多的類(聖誕樹),就可以把圖像中的噪點去除

3,對目標特徵點集計算凸包,在原圖上繪製

最後這一步就簡單多了,有了特徵點集,利用 scipy 包 中的 ConvexHull 方法計算 凸包 ,之後再利用matplotlib 將凸包在原圖上進行繪製

小結

文章中的一些技術點是值得借鑑,例如前面提到的用色調、飽和度作為閾值條件來篩選特徵點,及後面的 DBSCAN 聚類算法的使用;這些 Idea 不僅局限在聖誕樹上,也可以用於檢測其它的一些物體上面來,但需要多思考,多實踐

最後在這裡提一下為什麼聚類算法這裡用 DBSCAN,而不是經典的 KMeans;因為 KMeans 分類時需要設置類別數量(類別數量是我們提前沒有辦法確定的),並且在分類時僅以歐式距離作為參考,最終分類結果並不理想,參照下圖

KMeans 算法

DBSCAN 算法

文章中用到核心代碼

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

相關焦點

  • 三種用Python從圖像數據中提取特徵的技術
    所以在這篇適合初學者的文章中,你將了解到從圖像中生成特徵的不同方法,而後,可以將這些方法應用到自己喜歡的機器學習算法中。目錄1.機器是如何存儲圖像的?2.在Python中讀取圖像數據3.從圖像數據中提取特徵的方法#1:灰度像素值特徵4.從圖像數據中提取特徵的方法#2:通道的平均像素值5.從圖像數據中提取特徵的方法#3:提取邊緣機器是如何存儲圖像的?請從這些基礎開始。在查看其他任何內容前,了解如何在計算機上讀取與存儲圖像非常重要。
  • 檢測圖像中的聖誕樹,不用深度學習,好傢夥,還可以怎麼搞!
    源碼和素材:https://github.com/Largefreedom/Opencv_pra/tree/master/Tree_Detect本篇文章將用 Python 來實現圖片中的聖誕樹的識別、標記,可理解為計算機視覺中的物體檢測,先聲明一下哈這裡沒有用到神經網絡,都是傳統方法
  • python 基於opencv 繪製圖像輪廓
    這篇文章主要介紹了python 基於opencv 繪製圖像輪廓的示例,幫助大家更好的利用python的opencv庫處理圖像,感興趣的朋友可以了解下
  • 使用Python+OpenCV+Dlib實現人臉檢測與人臉特徵關鍵點識別
    今天,我們將學習如何檢測圖像中的人臉並提取面部特徵
  • python圖像處理-濾鏡處理
    前言很多時候用手機拍完照,為了讓照片看上去更好看,我們都會對照片做一些處理,而這裡用的最多的方法就是濾鏡了,常用的濾鏡一般有模糊濾鏡,其它的就是一些風格的變換了,比如黑白老照片,懷舊復古風,素描鉛筆藝術風等。
  • 10行代碼實現python人臉識別
    用攝像機或攝像頭採集含有人臉的圖像或視頻流,並自動在圖像中檢測和跟蹤人臉,進而對檢測到的人臉進行臉部識別的一系列相關技術,通常也叫做人像識別、面部識別。目前的人臉識別技術已經非常成熟了,還發展成3D人臉識別。而且現在各大廠商也都提供了人臉識別的API接口供我們調用,可以說幾行代碼就可以完成人臉識別。但是人臉識別的根本還是基於圖像處理。
  • 用dlib實現人臉識別的技巧
    很多人都認為人臉識別是一項非常難以實現的工作,看到名字就害怕,然後心懷忐忑到網上一搜,看到網上N頁的教程立馬就放棄了。這些人裡包括曾經的我自己。其實如果如果你不是非要深究其中的原理,只是要實現這一工作的話,人臉識別也沒那麼難。今天我們就來看看如何在40行代碼以內簡單地實現人臉識別。
  • Python超級教程,使用Python進行檢測面部特徵
    今天,我們將學習如何使用圖像來檢測面部並提取諸如眼睛,鼻子,嘴巴等面部特徵。作為捕捉面部的預處理步驟,我們可以做很多令人難以置信的事情,例如捕捉面部用於標記照片中的人物(手動或通過機器學習),創建效果以「增強」我們的圖像(類似於Snapchat等應用中的圖像),對面部進行情感分析等等。
  • 基於opencv 的圖像處理入門教程
    安裝OpenCV 的安裝還是比較簡單的,直接用 pip 命令在命令行安裝即可,輸入以下命令:pip install opencv-python驗證是否安裝成功,可以運行 python 命令,然後分別輸入以下命令:import cv2
  • 換臉技術,用 Python — OpenCV 來實現!
    1,Face Swap 技術介紹好了,下面將詳細介紹 人臉替換技術並用 OpenCV  來實現;介紹到這裡,如果沒有看過之前寫的幾篇文章實現人臉識別、人臉68個特徵點提取,或許這個 Python 庫能幫到你!
  • Python實現手勢識別
    ,python基本語法,圖像處理基礎知識。最終實現結果:獲取視頻(攝像頭)這部分沒啥說的,就是獲取攝像頭。這裡使用的是橢圓膚色檢測模型在RGB空間裡人臉的膚色受亮度影響相當大,所以膚色點很難從非膚色點中分離出來,也就是說在此空間經過處理後,膚色點是離散的點,中間嵌有很多非膚色,這為膚色區域標定(人臉標定、眼睛等)帶來了難題。
  • 使用OpenCv和Dlib進行打哈欠檢測
    OpenCV: OpenCV是一個用於計算機視覺的庫函數,最初是用c++編寫的。Dlib: Dlib是一個Python庫,包含了數據挖掘、數據結構、機器學習、圖像處理等工具。打哈欠檢測與應用打哈欠檢測就是使用OpenCV和Dlib來檢測打哈欠(由於疲勞或無聊而張大嘴巴深吸氣)。可廣泛應用於自駕車、駕駛員疲勞檢測、駕駛員睡意檢測、駕駛員意識檢測等領域。
  • python利用opencv實現證件照換底
    1.強大的opencv庫說到圖像處理,不得不提opencv庫。它是一個跨平臺的計算機視覺庫,可以運行在不同作業系統上,它由一些列c函數和少量c++函數組成,並提供python,matlab等語言的接口,實現了圖像處理和計算機視覺方面的很多通用算法。
  • python圖像處理-gif動圖
    圖片合成動圖導入os庫,利用listdir方法將lion文件夾中的圖片全部讀取出來,循環打開每一張圖片,接著將圖片對象添加到frames列表中,最後利用save方法,保存為一張動圖。python圖像處理-1
  • 如何將深度學習應用於無人機圖像的目標檢測
    對象重疊:分割圖像的問題之一是同一個對象可能出現在兩張不同的圖像中。這會導致重複檢測和計數錯誤。此外,在檢測過程中,某些彼此非常接近的對象也可能具有重疊的邊框。克服這個問題的方法之一是通過滑動窗口向上採樣,以尋找小的、密集的對象。
  • Python實現人臉口罩檢測!這玩意太強大了啊!
    前段時間在博客上看到幾個有趣的開源項目,它能檢測我們是否有戴口罩,跑起程序測試後,發現識別率挺高的,也適應不同環境;於是分享給大家。我用了其中兩種並整合在一個GUI下面,一種是基於深度學習的,一種是基於opencv訓練的。
  • 一文講解圖像插值算法原理!附Python實現
    並以圖像縮放例,對原理進行了C++及Python實現。然而,輸出圖像的坐標映射回原圖像後,一般為非整數的坐標。所以輸出圖像[x,y]的灰度值,一般由非整數坐標來決定,非整數坐標的像素值,就需要插值算法來進行處理。常見的插值算法有最近鄰插值、雙線性插值和三次樣條插值。
  • 那些讓人驚豔的Python庫
    python-magic- 文件類型檢測的第三方庫libmagic的Python接口。Unipath-用面向對象的方式操作文件和目錄。watchdog–管理文件系統事件的API和shell工具。圖像處理用來操作圖像的庫。pillow–Pillow是一個更加易用版的PIL。hmap–圖像直方圖映射。imgSeek–一個使用視覺相似性搜索一組圖片集合的項目。nude.py–裸體檢測。
  • 使用OpenCV為視頻中美女加上眼線
    在本文中,我們將嘗試創建一個人造眼線筆來模仿Snapchat或Instagram濾波器,為視頻中的美女添加上美麗的眼線。最終的結果可以通過下面的動圖觀察到。本文介紹的內容適合想要通過計算機視覺來實現一個具有一定展示性功能的計算機視覺初學者。因此,在本文重我們會儘量簡化說明,如果您對完整的程序感興趣,可以在Github上找到完整的代碼。Github的連結在本文的文末給出。
  • 見圖像傳統處理算法-邊緣檢測-分割-增強-降噪
    區域分裂合併:區域生長是從某個或者某些像素點出發,最終得到整個區域,進而實現目標的提取。而分裂合併可以說是區域生長的逆過程,從整幅圖像出發,不斷的分裂得到各個子區域,然後再把前景區域合併,得到需要分割的前景目標,進而實現目標的提取。