日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

OpenCV学习——轮廓检测

發(fā)布時(shí)間:2023/12/13 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV学习——轮廓检测 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

輪廓檢測(cè)是傳統(tǒng)視覺(jué)中非常常用的功能,這里簡(jiǎn)單記錄一下opencv中的輪廓檢測(cè)算法使用方法,至于理論,后續(xù)有機(jī)會(huì)再去細(xì)品。

國(guó)際慣例:

OpenCV官方的輪廓檢測(cè)教程python版

OpenCV中的二值化方法教程

OpenCV輪廓層級(jí)官方文檔

維基百科:圖像矩(Image Moment)

調(diào)用流程和方法

OpenCV里面通常要求是針對(duì)二值圖像進(jìn)行二值化,所以輪廓檢測(cè)包含如下步驟:

  • 載入圖像
  • 灰度化
  • 二值化
  • 輪廓檢測(cè)

代碼實(shí)現(xiàn)如下:

img =cv2.imread("blackBG.jpg") # grayscale # https://docs.opencv.org/4.5.0/d7/d4d/tutorial_py_thresholding.html gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret,bin_img = cv2.threshold(gray_img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

二值化

注意二值化方法,這里使用的是threshold函數(shù),它的第三個(gè)參數(shù)代表的意義可以查詢此處的官方文檔,這里將方法截圖貼出來(lái)

其實(shí)除了threshold還有一個(gè)adaptiveThreshold函數(shù)可以做二值化,調(diào)用方法:

#dst=cv.adaptiveThreshold(src,maxValue,adaptiveMethod,thresholdType,blockSize,C[, dst]) bin_img1 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_MEAN_C,\cv.THRESH_BINARY,11,2) bin_img2 = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\cv.THRESH_BINARY,11,2)

從第三個(gè)參數(shù)可以發(fā)現(xiàn)也有兩個(gè)二值化方法:

  • ADAPTIVE_THRESH_MEAN_C:閾值是每個(gè)像素鄰域區(qū)域的均值減去常量C
  • ADAPTIVE_THRESH_GAUSSIAN_C::閾值是每個(gè)像素相鄰域區(qū)域的高斯加權(quán)和減去常量C

輪廓檢測(cè)

python的調(diào)用方法如下:

contours, hierarchy =cv.findContours(image,mode,method[,contours[, hierarchy[, offset]]])

返回的參數(shù)

  • contours:檢測(cè)到的輪廓,每個(gè)輪廓是由一些點(diǎn)構(gòu)成的向量組成
  • hierarchy:記錄輪廓之間的關(guān)系,四個(gè)維度分別代表:同級(jí)后一個(gè)輪廓的序號(hào)、同級(jí)上一個(gè)輪廓的序號(hào)、第一個(gè)孩子序號(hào),父親序號(hào)

第二個(gè)數(shù)參數(shù)mode是檢測(cè)輪廓的層級(jí)關(guān)系排列規(guī)則:

  • RETR_EXTERNAL:僅僅檢測(cè)外圈輪廓
  • RETR_LIST:檢測(cè)所有輪廓,但是沒(méi)有層級(jí)關(guān)系
  • RETR_CCOMP:僅僅兩層包含關(guān)系,即只有外層和內(nèi)層,假設(shè)有夾層,那么夾層也算外層,只要某個(gè)輪廓還包含有輪廓,都算外部輪廓
  • RETR_TREE:檢測(cè)所有的輪廓,并建議非常完整的層級(jí)關(guān)系
  • RETR_FLOODFILL:無(wú)描述

第三個(gè)參數(shù)method是輪廓點(diǎn)的存儲(chǔ)方式:

  • CHAIN_APPROX_NONE:相鄰的輪廓點(diǎn)坐標(biāo)只相差一個(gè)像素,所以是連續(xù)輪廓點(diǎn)
  • CHAIN_APPROX_SIMPLE:橫、豎、對(duì)角線段只保存斷點(diǎn)數(shù)據(jù),比如矩形就只保存四個(gè)頂點(diǎn)。
  • 還有兩種沒(méi)做過(guò)多敘述:CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS是Teh-Chin chain近似算法里面采取的兩種表示

畫圖函數(shù)

就一個(gè)函數(shù)drawContours,調(diào)用方法如下:

image=cv.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]] )

輸入?yún)?shù):

  • contours:是list類型的數(shù)組,里面存儲(chǔ)了很多array數(shù)組去代表各個(gè)輪廓
  • contourIdx:從上面的輪廓list中取出哪一個(gè)畫出來(lái),-1代表全部
  • color:線條顏色
  • thickness:線條粗細(xì),-1代表填充式畫輪廓,整個(gè)輪廓內(nèi)部被指定顏色填充
  • lineType:線條類型,虛線、實(shí)線之類的

【注意】如果將原圖傳入畫圖函數(shù),這個(gè)原圖會(huì)被畫上輪廓,所以畫圖時(shí)候最好建立一個(gè)副本,在副本上畫圖。

輪廓檢測(cè)函數(shù)驗(yàn)證

主要驗(yàn)證檢測(cè)時(shí)的層級(jí)結(jié)構(gòu)和記錄關(guān)鍵點(diǎn)的方式,也就是第2和3個(gè)參數(shù)。

檢測(cè)黑色還是白色邊界

黑色背景圖,以下圖為例

先檢測(cè)所有的輪廓并且畫出來(lái)

img =cv2.imread("blackBG.jpg") gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret,bin_img = cv2.threshold(gray_img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img,contours,-1,(0,255,0),5) plt.imshow(img[...,::-1]) plt.axis('off')

白色背景圖以下圖左為例,同時(shí)以同樣的代碼盡心輪廓檢測(cè),輪廓圖為下圖右:

結(jié)論:檢測(cè)白色背景的圖片,會(huì)有一個(gè)和圖像寬高相等的輪廓,而黑色區(qū)域沒(méi)有;所以輪廓檢測(cè)是針對(duì)白色區(qū)域的邊緣進(jìn)行的,這個(gè)和圖像等寬高的輪廓經(jīng)常會(huì)影響一些邏輯的書寫。

層級(jí)關(guān)系

  • RETR_EXTERNAL:僅外圈輪廓

    # RETR_EXTERNAL:僅外圈輪廓 contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img,contours,-1,(0,255,0),5) plt.imshow(img[...,::-1]) plt.axis('off')print(hierarchy) ''' [[[ 1 -1 -1 -1][ 2 0 -1 -1][-1 1 -1 -1]]] '''

從輪廓圖可以發(fā)現(xiàn),僅僅只有確定為最外圈的輪廓被畫出來(lái),而且輸出的hierarchy數(shù)組可以發(fā)現(xiàn),前兩列分別代表當(dāng)前層級(jí)當(dāng)前輪廓的下一個(gè)輪廓和上一個(gè)輪廓索引,而后兩列分別代表當(dāng)前層級(jí)的子層級(jí)的第一個(gè)輪廓索引和父層級(jí)的輪廓索引,因?yàn)镽ETR_EXTERNAL只提取最外層輪廓,所以上下層級(jí)都是-1

  • RETR_LIST:所有輪廓都包含,但是沒(méi)有層級(jí)關(guān)系

    # RETR_EXTERNAL:全部輪廓,無(wú)層級(jí)關(guān)系 contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img,contours,-1,(0,255,0),5) plt.imshow(img[...,::-1]) plt.axis('off')print(hierarchy) ''' [[[ 1 -1 -1 -1][ 2 0 -1 -1][ 3 1 -1 -1][ 4 2 -1 -1][ 5 3 -1 -1][ 6 4 -1 -1][ 7 5 -1 -1][-1 6 -1 -1]]] '''

代表當(dāng)前層級(jí)父子層級(jí)的后兩個(gè)維度依舊為-1,但是輪廓全部都提取出來(lái)了。

  • RETR_CCOMP:僅僅兩層關(guān)系,是否為內(nèi)層或者是否為外層,而且這個(gè)內(nèi)層一定是這個(gè)外層的洞,這個(gè)洞的定義指內(nèi)外層組合構(gòu)成一片白色區(qū)域。如下圖代碼測(cè)試

    # RETR_CCOMP:全部輪廓,只有兩種層級(jí)關(guān)系 contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)img_show = img.copy() for i in range(len(contours)):if(hierarchy[0,i,3]!=-1):cv2.drawContours(img_show,contours,i,colormap[i],5)cv2.drawContours(img_show,contours,hierarchy[0,i,3],colormap[i],5) plt.imshow(img_show[...,::-1]) plt.axis('off')print(hierarchy) # 紅:0 橙;1 黃:2 綠:3 青:4 藍(lán):5 紫:6 灰:7 ''' [[ 1 -1 -1 -1][ 2 0 -1 -1][ 4 1 3 -1][-1 -1 -1 2][ 6 2 5 -1][-1 -1 -1 4][ 7 4 -1 -1][-1 6 -1 -1]]] '''

    上述代碼表示將當(dāng)前輪廓與其父輪廓用同色畫出來(lái):

可以發(fā)現(xiàn)四個(gè)輪廓組成的兩個(gè)白色區(qū)域被顯示出來(lái),綠色區(qū)域?yàn)?號(hào)輪廓,從hierarchy中找到3號(hào)輪廓的結(jié)構(gòu)為[-1 -1 -1 2],自行可視化可以發(fā)現(xiàn)這個(gè)3號(hào)輪廓是白色區(qū)域中最內(nèi)層的那個(gè)輪廓,而其父親索引為2,輪廓2的結(jié)構(gòu)為[ 4 1 3 -1],可以發(fā)現(xiàn)它的第一個(gè)孩子是3,而由于是外輪廓(不管是否為最外圈),所以父親索引為-1。其余輪廓同理分析。

【注】這個(gè)輪廓結(jié)構(gòu)有點(diǎn)繞,但是只需要記住只有內(nèi)、外輪廓,只要當(dāng)前輪廓有內(nèi)輪廓一起組成白色區(qū)域,那么這個(gè)輪廓就是外輪廓,不管它在不在其它輪廓內(nèi)部

可視化時(shí)候本來(lái)用當(dāng)前輪廓和子輪廓來(lái)顯示,但是想到hierarchy只記錄第一個(gè)子輪廓,當(dāng)時(shí)差點(diǎn)以為組成“洞”的只可能有兩個(gè)輪廓,也就是一個(gè)輪廓有且只可能有一個(gè)子輪廓,但是發(fā)現(xiàn)問(wèn)題,一個(gè)輪廓可能會(huì)有兩個(gè)子輪廓,所以必須用當(dāng)前輪廓與父輪廓可視化,而不是當(dāng)前輪廓和子輪廓可視化,比如下面這個(gè)圖,及其對(duì)應(yīng)的輪廓圖和層級(jí)關(guān)系:

輪廓對(duì)應(yīng)順序分別是紅、橙、黃,其CCOMP層級(jí)關(guān)系為:

[[[-1 -1 1 -1][ 2 -1 -1 0][-1 1 -1 0]]]

可以發(fā)現(xiàn),內(nèi)部?jī)蓚€(gè)輪廓的父親都是0,證明這個(gè)洞是由三個(gè)輪廓組成的。

  • RETR_TREE:這個(gè)是非常嚴(yán)謹(jǐn)?shù)谋磉_(dá)輪廓間層級(jí)關(guān)系的參數(shù)
    直接輸出hierarchy看看:

    contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) print(hierarchy) # 紅:0 橙;1 黃:2 綠:3 青:4 藍(lán):5 紫:6 灰:7 ''' [[[ 6 -1 1 -1][-1 -1 2 0][-1 -1 3 1][-1 -1 4 2][ 5 -1 -1 3][-1 4 -1 3][ 7 0 -1 -1][-1 6 -1 -1]]] '''

    真正的由外向內(nèi),一層一層的編號(hào);是CCOMP的更進(jìn)一步細(xì)化,如果CCOMP中構(gòu)成洞的兩個(gè)輪廓的外輪廓在其它輪廓內(nèi)部,那么就是從其它輪廓編號(hào)繼續(xù)編號(hào),即洞的外輪廓的父親是包含它的緊鄰著的輪廓編號(hào)。

    通過(guò)判斷父親是否相同,將輪廓按照層級(jí)畫出來(lái)

    img_show = img.copy() for i in range(len(contours)):cv2.drawContours(img_show,contours,i,colormap[hierarchy[0,i,3]+1],5) plt.imshow(img_show[...,::-1]) plt.axis('off')

可以發(fā)現(xiàn),紅色部分就是最外圈輪廓,父親為-1;而最內(nèi)部的青色(菱形、六角星)的孩子是-1,父親是綠色的輪廓3。

存儲(chǔ)方法

CHAIN_APPROX_NONE和CHAIN_APPROX_SIMPLE的區(qū)別就在于輪廓為線段的部分,是否僅存儲(chǔ)端點(diǎn)坐標(biāo)。

比如上述圖片的最外層的矩形輪廓,分別使用兩種存儲(chǔ)參數(shù)去存儲(chǔ)輪廓點(diǎn)的值:

使用SIMPLE只保存端點(diǎn)

contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) img_show = img.copy() cnt_idx = 0 cnt = contours[cnt_idx] for i in range(cnt.shape[0]):cv2.circle(img_show,(cnt[i,0,0],cnt[i,0,1]),5,(0,255,0),5) plt.imshow(img_show[...,::-1])

使用NONE按像素保存

contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) img_show = img.copy() cnt_idx = 0 cnt = contours[cnt_idx] for i in range(cnt.shape[0]):cv2.circle(img_show,(cnt[i,0,0],cnt[i,0,1]),5,(0,255,0),5) plt.imshow(img_show[...,::-1])

輪廓的其它特征和屬性

包括輪廓的圖像矩、面積、周長(zhǎng)、多邊形逼近、外接凸多邊形、凸性判斷、外接矩形、外接圓、外接橢圓、直線擬合。

圖像矩

維基百科中的解釋是:指圖像的某些特定像素灰度的加權(quán)平均值,或者是圖像具有類似功能或意義的屬性。可以通過(guò)圖像的矩來(lái)獲得圖像的部分性質(zhì),包括面積(或總體亮度),以及有關(guān)幾何中心和方向的信息。它可以被用來(lái)獲得相對(duì)于特定變換的不變性(平移、縮放、旋轉(zhuǎn)不變性) 。具體可查閱維基百科中圖像矩的描述,這里列一下矩的計(jì)算方法:

  • 對(duì)于二維連續(xù)函數(shù)f(x,y)f(x,y)f(x,y)(p+q)(p+q)(p+q)階的矩被定義為:
    Mpq=∫?∞∞∫?∞∞xpyqf(x,y)dxdyM_{pq}=\int_{-\infty}^{\infty}\int_{-\infty}^{\infty}x^py^qf(x,y)dxdy Mpq?=????xpyqf(x,y)dxdy

  • 對(duì)于灰度圖像的像素強(qiáng)度I(x,y)I(x,y)I(x,y),原始圖像的矩MijM_{ij}Mij?計(jì)算方法:
    Mij=∑x∑yxiyiI(x,y)M_{ij}=\sum_x\sum_yx^iy^iI(x,y) Mij?=x?y?xiyiI(x,y)

  • 原始矩包含以下的一些的有關(guān)原始圖像屬性的信息:

    • 二值圖像的面積或灰度圖像的像素總和,可以表示為M00M_{00}M00?
    • 圖像的幾何中心可以表示為{xˉ,yˉ}={M10M00,M01M00}\{\bar x,\bar y\}= \{\frac{M_{10}}{M_{00}},\frac{M_{01}}{M_{00}}\}{xˉ,yˉ?}={M00?M10??,M00?M01??}

在OpenCV中的表示為:

cnt = contours[0] M = cv.moments(cnt)

中心為:

cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00'])

輪廓面積和周長(zhǎng)

獲取指定輪廓所包含的面積

area = cv.contourArea(cnt)

獲取指定輪廓所包含的周長(zhǎng),第二個(gè)參數(shù)指示當(dāng)前輸入為閉合輪廓(true)還是非閉合曲線(false)

perimeter = cv.arcLength(cnt,True)

輪廓多邊形逼近

通過(guò)具有更少輪廓點(diǎn)的形狀在允許誤差范圍內(nèi)逼近指定輪廓,比如你提取一個(gè)矩形,但是有鋸齒導(dǎo)致輪廓不是矩形,可以使用此功能將矩形近似逼近出來(lái)

epsilon = 0.1*cv.arcLength(cnt,True) approx = cv.approxPolyDP(cnt,epsilon,True)

這個(gè)意思就是新的輪廓的周長(zhǎng)和原始輪廓周長(zhǎng)的誤差范圍在原周長(zhǎng)的十分之一以內(nèi)。

比如最開(kāi)始的例子中,最內(nèi)部的六角星的輪廓點(diǎn)并不是規(guī)整的五角星輪廓,也就是說(shuō)使用SIMPLE存儲(chǔ)的時(shí)候不是存的每條邊的端點(diǎn)。

下圖就是使用這個(gè)逼近函數(shù)去找到端點(diǎn)的結(jié)果:

contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) img_show = img.copy() cnt_idx = 4 cnt = contours[cnt_idx] for i in range(cnt.shape[0]):cv2.circle(img_show,(cnt[i,0,0],cnt[i,0,1]),5,(0,255,0),5)epsilon = 0.1*cv2.arcLength(cnt,True) approx = cv2.approxPolyDP(cnt,epsilon,True) for i in range(approx.shape[0]):cv2.circle(img_show,(approx[i,0,0],approx[i,0,1]),5,(0,0,255),5)plt.imshow(img_show[...,::-1]) plt.axis('off')

綠色為原始輪廓點(diǎn),紅色為逼近后的輪廓點(diǎn),可以發(fā)現(xiàn)六角星的所有邊的頂點(diǎn)都保存了

輪廓凸多邊形逼近

上面的多邊形逼近不管簡(jiǎn)化的輪廓是否為凸的,所以又提供了一個(gè)檢測(cè)凸多邊形逼近的函數(shù)

hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]

輸入分別為:輪廓點(diǎn)、輸出(不管這個(gè)參數(shù))、順時(shí)針(true)/逆時(shí)針(false)、返回多邊形坐標(biāo)在原輪廓點(diǎn)序中的索引(False)/直接返回坐標(biāo)(true)

還是那個(gè)六角星:

contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) img_show = img.copy() cnt_idx = 4 cnt = contours[cnt_idx] for i in range(cnt.shape[0]):cv2.circle(img_show,(cnt[i,0,0],cnt[i,0,1]),5,(0,255,0),5)approx = cv2.convexHull(cnt) for i in range(approx.shape[0]):cv2.circle(img_show,(approx[i,0,0],approx[i,0,1]),5,(0,0,255),5)plt.imshow(img_show[...,::-1]) plt.axis('off')

綠色為原始輪廓點(diǎn),紅色為凸多邊形逼近后的輪廓點(diǎn),可以發(fā)現(xiàn)比多邊形逼近函數(shù)的結(jié)果少了內(nèi)凹角頂點(diǎn)。

凸性檢測(cè)

如何判斷一個(gè)輪廓是否為凸的,有一個(gè)函數(shù)k = cv.isContourConvex(cnt),返回true就是凸的。

OpenCV對(duì)這個(gè)凸多邊形還提供了提取更詳細(xì)信息的函數(shù)convexityDefects,用于獲取凸多邊形和輪廓之間的關(guān)系:

void convexityDefects(InputArray contour, InputArray convexhull, OutputArrayconvexityDefects)

輸入:原始輪廓點(diǎn)、凸多邊形頂點(diǎn)對(duì)應(yīng)原輪廓中的索引、輸出(不管)

所以針對(duì)那個(gè)五角星的調(diào)用方法是:

contours, hierarchy = cv2.findContours(bin_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt_idx = 4 cnt = contours[cnt_idx] approx = cv2.convexHull(cnt,returnPoints=False)#使用false獲取多邊形頂點(diǎn)索引print(cv2.convexityDefects(cnt,approx)) ''' [[[ 321 0 357 4403]][[ 0 67 31 4403]][[ 67 128 96 4230]][[ 128 194 163 4223]][[ 194 260 225 4223]][[ 260 321 292 4230]]] '''

得到了和多邊形逼近線段個(gè)數(shù)相同行的列為4的矩陣,分別代表:起始點(diǎn)索引、結(jié)束點(diǎn)索引、當(dāng)前線段截取的輪廓點(diǎn)中距離線段最遠(yuǎn)的點(diǎn)索引、這個(gè)最遠(yuǎn)點(diǎn)與當(dāng)前線段的距離

驗(yàn)證一下,把第三個(gè)維度,也就是距離每條邊最遠(yuǎn)的輪廓點(diǎn)畫出來(lái):

邊界框

分為矩形、圓形邊界

  • 矩形:不考慮形狀的旋轉(zhuǎn),獲取直邊界矩形

    x,y,w,h = cv2.boundingRect(cnt)
  • 矩形考慮旋轉(zhuǎn),獲取最小的外接矩形

    rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.int0(box)

畫圖看看區(qū)別,直邊界用綠色,最小外接矩形用紅色

img_show = img.copy() #無(wú)旋轉(zhuǎn)矩形 cv2.rectangle(img_show,(x,y),(x+w,y+h),(0,255,0),4) #有旋轉(zhuǎn)矩形 cv2.drawContours(img_show,[box],0,(0,0,255),4)plt.imshow(img_show[...,::-1]) plt.axis('off')

外接圓:minEnclosingCircle

# 最小外接圓 (x,y),radius = cv2.minEnclosingCircle(cnt) center = (int(x),int(y)) radius = int(radius)img_show = img.copy() cv2.circle(img,center,radius,(0,255,0),10) plt.imshow(img_show[...,::-1]) plt.axis('off')

形狀擬合

包括橢圓、直線擬合

橢圓擬合:fitEllipse,將里面的那個(gè)菱形擬合

# 橢圓擬合 ellipse = cv2.fitEllipse(contours[5])img_show = img.copy() cv2.ellipse(img_show,ellipse,(0,255,0),10) plt.imshow(img_show[...,::-1]) plt.axis('off')

直線擬合:fitLine,使得當(dāng)前輪廓所有點(diǎn)與直線距離和最短

rows,cols = img.shape[:2] [vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01) lefty = int((-x*vy/vx) + y) righty = int(((cols-x)*vy/vx)+y)img_show = img.copy() cv2.line(img_show,(cols-1,righty),(0,lefty),(0,255,0),2) plt.imshow(img_show[...,::-1]) plt.axis('off')

點(diǎn)與輪廓的關(guān)系

通過(guò)pointPolygonTest函數(shù)判斷某個(gè)點(diǎn)是否在輪廓內(nèi)部后者外部,然后返回距離輪廓的最短距離

retval=cv.pointPolygonTest(contour,pt,measureDist)

輸入分別為:輪廓、某個(gè)點(diǎn)、是否返回距離;如果僅僅需要判斷點(diǎn)是否再輪廓內(nèi)部,第三個(gè)參數(shù)設(shè)置False,在內(nèi)部為+1,外部為-1,在輪廓上為0。

形狀匹配

可以利用matchShapes輸入兩個(gè)輪廓,計(jì)算相似度,得分越低越相似

retval = cv.matchShapes( contour1, contour2, method, parameter )

輸入為:第一個(gè)形狀的輪廓、第二給形狀的輪廓、匹配算法、參數(shù)(暫不支持,不管)

匹配算法是基于圖像的Hu矩,計(jì)算方法為:
mi=sign(hi)?log?him_i = sign(h_i)\cdot \log h_i mi?=sign(hi?)?loghi?
其中hih_ihi?代表Hu矩。

匹配算法分為:

使用案例,先構(gòu)建一些圖像,然后計(jì)算相似度:

img1 = cv2.imread('shape1.png',0) img2 = cv2.imread('shape2.png',0) img3 = cv2.imread('shape3.png',0) ret, thresh = cv2.threshold(img1, 127, 255,0) ret, thresh2 = cv2.threshold(img2, 127, 255,0) ret, thresh3 = cv2.threshold(img3, 127, 255,0) contours,hierarchy = cv2.findContours(thresh,2,1) cnt1 = contours[0] contours,hierarchy = cv2.findContours(thresh2,2,1) cnt2 = contours[0] contours,hierarchy = cv2.findContours(thresh3,2,1) cnt3 = contours[0] ret1 = cv2.matchShapes(cnt1,cnt2,1,0.0) ret2 = cv2.matchShapes(cnt1,cnt3,1,0.0) print( ret1,ret2 ) plt.subplot(131) plt.imshow(img1,cmap='gray') plt.axis('off') plt.subplot(132) plt.imshow(img2,cmap='gray') plt.axis('off') plt.subplot(133) plt.imshow(img3,cmap='gray') plt.axis('off') ''' 0.14475720763533126 0.3168697153308031 '''

可以發(fā)現(xiàn)形狀1和2的非常接近,一個(gè)四角星一個(gè)五角星,他倆得分很低,越相似。

其它屬性

  • 獲取掩膜(mask)

    mask = np.zeros(gray_img.shape,np.uint8) cv2.drawContours(mask,[cnt],0,255,-1) pixelpoints = np.transpose(np.nonzero(mask)) #pixelpoints = cv.findNonZero(mask) plt.imshow(mask,cmap='gray')

注意使用可以使用numpy或者OpenCV去查找到掩膜內(nèi)所有像素坐標(biāo),但是他倆的位置不一樣,因此numpy的坐標(biāo)需要轉(zhuǎn)置才能與OpenCV保持一致,列是x,行是y

  • 獲取局部最大值、最小值級(jí)它們的位置

    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray,mask = mask)

    注意第一個(gè)參數(shù)必須是單通道的圖,第二個(gè)參數(shù)可有可無(wú),用于選擇特定區(qū)域。

    這個(gè)在Openpose中,從每個(gè)關(guān)節(jié)的特征圖中提取關(guān)節(jié)坐標(biāo)用到過(guò),具體可看之前解析OpenPose的文章。

  • 均值:通道分開(kāi)

    mean_val = cv.mean(im,mask = mask)

后記

圖像處理經(jīng)常遇到輪廓相關(guān)的問(wèn)題,比如二維碼檢測(cè)定位之類的大都是用二維碼四個(gè)角的定位符和矯正符的比例特征來(lái)定位。這里對(duì)官方的教程做了簡(jiǎn)單的綜合整理。

完整的python腳本實(shí)現(xiàn)放在微信公眾號(hào)的簡(jiǎn)介中描述的github中,有興趣可以去找找,同時(shí)文章也同步到微信公眾號(hào)中,有疑問(wèn)或者興趣歡迎公眾號(hào)私信。

總結(jié)

以上是生活随笔為你收集整理的OpenCV学习——轮廓检测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。