一、輪廓檢測(實(shí)現(xiàn)步驟)
????????對于輪廓檢測的步驟可簡述為:讀取圖像 -> 圖像二值化 -> 找出輪廓 -> 在原圖像上畫出輪廓這么四個步驟。下面先是講每個步驟的代碼,步驟后會寫關(guān)鍵步驟的原理。
????????首先是讀取圖像,在本次實(shí)驗(yàn)中是將獲取到的輪廓畫在原圖像上所以需要獲取原圖像和灰度圖像兩個圖像,操作為(我是讀取了兩個圖像其中一個是一個女生后面會看到,另一個是規(guī)則的幾何圖形的圖像):
# 返回值有兩個:contours(輪廓), hierarchy(層級)
img = cv2.imread('6.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img2 = cv2.imread('10.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像(這個是lina(女性角色)的圖片)
img2_gray = cv2.imread('10.png' , cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像(這個是那個很多規(guī)則圖案的圖片)
? ? ? ? 隨后進(jìn)行對灰度圖像的二值化操作:
# 將兩張圖片進(jìn)行二值化處理(將值全部轉(zhuǎn)化為255和0只有兩種顏色)
ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh , 'thresh')
ret2 , thresh2 = cv2.threshold(img2_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh2 , 'thresh2')
? ? ? ? 第三步為找出輪廓,下面為操作步驟:
# 傳進(jìn)來的是一個經(jīng)過二值計算的灰度圖,contours就是我們所需要的輪廓
contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
contours2 , hierarchy2 = cv2.findContours(thresh2 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
? ? ? ? 第四步為在原圖像上畫出輪廓,操作為:
draw_img = img.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')
draw_img2 = img2.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
ret2 = cv2.drawContours(draw_img2 , contours2 , -1 , (0 , 0 , 255) , 2)
img_show(ret2 , 'ret2')
????????實(shí)驗(yàn)代碼:
import numpy as np
import cv2
import matplotlib.pyplot as plt
def img_show(img , title):
cv2.namedWindow(title, cv2.WINDOW_NORMAL) # 設(shè)置窗口標(biāo)題
cv2.resizeWindow(title, 300, 250) # 你可以根據(jù)需要設(shè)置不同的寬度和高度
cv2.imshow(title , img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 主要是利用一個圖像輪廓的檢測函數(shù)
# cv.findContours函數(shù)(查找輪廓):cv2.findContours(image, mode, method)
# ——image:輸入圖像,只能輸入單通道圖像,通常來說為灰度圖。(最好是經(jīng)過二值法處理的灰度圖像)
# ——mode:輪廓檢索模式
# cv2.RETR_EXTERNAL:只檢測外輪廓
# cv2.RETR_LIST:檢索所有的輪廓,并保存到一個鏈表之中。
# cv2.RETR_CCOMP:檢索所有的輪廓,并將他們分為兩個等級的輪廓。上一層是外部邊界,里一層是空洞的邊界。
# cv2.RETR_TREE:檢索所有輪廓,并建立一個等級樹結(jié)構(gòu)的輪廓。(一般只用這一個檢索模式)
# ——method:輪廓逼近方法
# cv2.CHAIN_APPROX_NONE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點(diǎn)的序列)
# cv2.CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數(shù)只保留他們的終點(diǎn)部分(就是保留一些關(guān)鍵點(diǎn))
# 一般是使用上面兩種輪廓逼近的方法。
# cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法
# cv2.CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法
# 返回值有兩個:contours(輪廓), hierarchy(層級)
img = cv2.imread('6.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img2 = cv2.imread('10.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像(這個是lina(女性角色)的圖片)
img2_gray = cv2.imread('10.png' , cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像(這個是那個很多規(guī)則圖案的圖片)
# 將兩張圖片進(jìn)行二值化處理(將值全部轉(zhuǎn)化為255和0只有兩種顏色)
# 函數(shù)原型:cv2.threshold(src, thresh, maxval, type, dst=None)
ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh , 'thresh')
ret2 , thresh2 = cv2.threshold(img2_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh2 , 'thresh2')
# 傳進(jìn)來的是一個經(jīng)過二值計算的灰度圖,contours就是我們所需要的輪廓
contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
contours2 , hierarchy2 = cv2.findContours(thresh2 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
# 畫出輪廓(相當(dāng)于把輪廓畫在原圖像上,注意畫的時候會改變原圖像的值,所以最好是先復(fù)制一份原圖像)
# 函數(shù)原型:cv2.drawContours(image, contours, contourIdx, color, thickness)
# ——image:原圖像
# ——contours:輪廓本身,是一個list,list中每個元素都是一個輪廓點(diǎn)集(不是每個輪廓點(diǎn)集都是一個閉合的輪廓)
# ——contourIdx:輪廓的索引(等于是畫第幾個輪廓,-1則為畫所有的輪廓)
# ——color:輪廓的顏色。
# ——thickness:輪廓的粗細(xì)。
draw_img = img.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')
draw_img2 = img2.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
ret2 = cv2.drawContours(draw_img2 , contours2 , -1 , (0 , 0 , 255) , 2)
img_show(ret2 , 'ret2')
????????最終效果為:
二、輪廓檢測(步驟原理)
1.灰度圖像二值化操作的原理
函數(shù)原型:cv2.threshold(src, thresh, maxval, type, dst=None)
——src:輸入圖,只能輸入單通道圖像,通常來說為灰度圖。
——thresh:閾值。
——maxval:當(dāng)像素值超過了閾值(或者小于閾值,根據(jù)type來決定),所賦予的值。
——type:二值化操作的類型,包含以下5種類型: ?
cv2.THRESH_BINARY:超過閾值部分取maxval(最大值),否則取0
cv2.THRESH_BINARY_INV:THRESH_BINARY的反轉(zhuǎn)
cv2.THRESH_TRUNC:大于閾值部分設(shè)為閾值,否則不變
cv2.THRESH_TOZERO:大于閾值部分不改變,否則設(shè)為0
cv2.THRESH_TOZERO_INV:THRESH_TOZERO的反轉(zhuǎn)
例如:cv2.THRESH_BINARY模式下,假設(shè)設(shè)置max=100,則像素點(diǎn)的值為120時則像素點(diǎn)的值設(shè)為100,若像素點(diǎn)的值為99小于100則
2.查找輪廓函數(shù)原型
函數(shù)原型:cv2.findContours(image, mode, method)
——image:輸入圖像,只能輸入單通道圖像,通常來說為灰度圖。(最好是經(jīng)過二值法處理的灰度圖像)
——mode:輪廓檢索模式
cv2.RETR_EXTERNAL:只檢測外輪廓
cv2.RETR_LIST:檢索所有的輪廓,并保存到一個鏈表之中。
cv2.RETR_CCOMP:檢索所有的輪廓,并將他們分為兩個等級的輪廓。上一層是外部邊界,里一層是空洞的邊界。
cv2.RETR_TREE:檢索所有輪廓,并建立一個等級樹結(jié)構(gòu)的輪廓。(一般只用這一個檢索模式)
——method:輪廓逼近方法
cv2.CHAIN_APPROX_NONE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點(diǎn)的序列一般就是所有點(diǎn))
cv2.CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數(shù)只保留他們的終點(diǎn)部分(就是保留一些幾何圖形的關(guān)鍵點(diǎn)(例如矩形的四個頂點(diǎn)))
一般是使用上面兩種輪廓逼近的方法。
cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法
cv2.CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法
返回值有兩個:contours(輪廓), hierarchy(層級),其中輪廓是一個list形式的參數(shù),list中每個元素都是一個輪廓點(diǎn)集。
3.畫出輪廓函數(shù)原型
函數(shù)原型:cv2.drawContours(image, contours, contourIdx, color, thickness)
實(shí)際操作:
img = cv2.imread('6.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像
ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)# 圖像二值化處理
contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)# 獲取到輪廓
draw_img = img.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')
——image:原圖像
——contours:輪廓本身,是一個list,list中每個元素都是一個輪廓點(diǎn)集(不是每個輪廓點(diǎn)集都是一個閉合的輪廓)
——contourIdx:輪廓的索引(等于是畫第幾個輪廓,-1則為畫所有的輪廓)
——color:輪廓的顏色。
——thickness:輪廓的粗細(xì)。
需要注意的是??!在畫圖的時候最好把需要被畫的原圖像先復(fù)制一份然后再將復(fù)制的圖像進(jìn)行畫輪廓操作,這樣可以防止直接對原圖像進(jìn)行畫導(dǎo)致再次操作時原圖像上已經(jīng)被畫上了輪廓。
三、輪廓的近似
輪廓的近似的實(shí)現(xiàn):實(shí)際上輪廓的近似的實(shí)現(xiàn)就是將復(fù)雜的曲線用一些直線來代替,使圖像更加的光滑和規(guī)則化。
1.輪廓近似函數(shù)原理
# 函數(shù)原型:cv2.approxPolyDP(curve, epsilon, closed)
實(shí)際操作:
img3 = cv2.imread('11.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img3_gray = cv2.imread('11.png',cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像
ret3 , thresh3 = cv2.threshold(img3_gray , 100 , 255 , cv2.THRESH_BINARY)# 二值化處理
contours3 , hierarchy3 = cv2.findContours(thresh3 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)# 查找輪廓
cnt3 = contours3[0] # 提取出第一個輪廓
eplision = 0.01 * cv2.arcLength(cnt3 , True) # 這里是指定的精度,也就是原始曲線與近似曲線之間的最大距離.
approx = cv2.approxPolyDP(cnt3 , eplision , True) # 這里是對輪廓進(jìn)行近似處理.
img_draw3 = img3.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
res3 = cv2.drawContours(img_draw3, [approx], -1, (0, 0, 255), 3)
img_show(res3 , 'res3')
——curve:輸入的點(diǎn)集
——epsilon:指定的精度,也就是原始曲線與近似曲線之間的最大距離。
——closed:曲線是否是閉合的
????????其中可以看到函數(shù)中有一個參數(shù)是需要我們自己進(jìn)行計算的:指定的精度(epsilon),這個參數(shù)也是由前面的系數(shù)決定的,在示例中用的系數(shù)是0.01,實(shí)際上可以根據(jù)自己對精度的需求自行調(diào)整。epsilon = 系數(shù) ? 輪廓的周長。
? ? ? ? 那么近似的具體原理是什么呢,如以下圖所示:
????????如圖所示,當(dāng)AB曲線中到AB虛線距離最大時,距離還是小于我們設(shè)置的epsilon參數(shù)則就用直線AB代替掉曲線AB。若最大距離小于epsilon時則有:
? ? ? ? 若AD,DB兩條直線還無法取代AB這條曲線則就再將AE,ED,DF,FB,連接起來看看最大距離是否小于epsilon,這樣便可以用多條直線來取代整條曲線,無論曲線是什么樣子都能實(shí)現(xiàn)。具體效果只決定于參數(shù)epsilon。
? ? ? ? 2.最終實(shí)現(xiàn)的效果
? ??? ? ?
計算精度系數(shù):? ? ? ? ?0.02? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0.01
? ??? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0.005? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0.001文章來源:http://www.zghlxwxcb.cn/news/detail-828437.html
? ? ? ? 至此便是輪廓近似的全部原理。以下提供整個輪廓實(shí)驗(yàn)的源代碼供大家學(xué)習(xí)參考。有寫錯的地方也歡迎大家指出!文章來源地址http://www.zghlxwxcb.cn/news/detail-828437.html
import numpy as np
import cv2
import matplotlib.pyplot as plt
def img_show(img , title):
cv2.namedWindow(title, cv2.WINDOW_NORMAL) # 設(shè)置窗口標(biāo)題
cv2.resizeWindow(title, 300, 250) # 你可以根據(jù)需要設(shè)置不同的寬度和高度
cv2.imshow(title , img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 主要是利用一個圖像輪廓的檢測函數(shù)
# cv.findContours函數(shù)(查找輪廓):cv2.findContours(image, mode, method)
# ——image:輸入圖像,只能輸入單通道圖像,通常來說為灰度圖。(最好是經(jīng)過二值法處理的灰度圖像)
# ——mode:輪廓檢索模式
# cv2.RETR_EXTERNAL:只檢測外輪廓
# cv2.RETR_LIST:檢索所有的輪廓,并保存到一個鏈表之中。
# cv2.RETR_CCOMP:檢索所有的輪廓,并將他們分為兩個等級的輪廓。上一層是外部邊界,里一層是空洞的邊界。
# cv2.RETR_TREE:檢索所有輪廓,并建立一個等級樹結(jié)構(gòu)的輪廓。(一般只用這一個檢索模式)
# ——method:輪廓逼近方法
# cv2.CHAIN_APPROX_NONE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點(diǎn)的序列)
# cv2.CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數(shù)只保留他們的終點(diǎn)部分(就是保留一些關(guān)鍵點(diǎn))
# 一般是使用上面兩種輪廓逼近的方法。
# cv2.CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法
# cv2.CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法
# 返回值有兩個:contours(輪廓), hierarchy(層級)
img = cv2.imread('6.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img2 = cv2.imread('10.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img_gray = cv2.imread('6.png',cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像(這個是lina(女性角色)的圖片)
img2_gray = cv2.imread('10.png' , cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像(這個是那個很多規(guī)則圖案的圖片)
# 將兩張圖片進(jìn)行二值化處理(將值全部轉(zhuǎn)化為255和0只有兩種顏色)
# 函數(shù)原型:cv2.threshold(src, thresh, maxval, type, dst=None)
ret , thresh = cv2.threshold(img_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh , 'thresh')
ret2 , thresh2 = cv2.threshold(img2_gray , 127 , 255 , cv2.THRESH_BINARY)
img_show(thresh2 , 'thresh2')
# 傳進(jìn)來的是一個經(jīng)過二值計算的灰度圖,contours就是我們所需要的輪廓
contours , hierarchy = cv2.findContours(thresh , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
contours2 , hierarchy2 = cv2.findContours(thresh2 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
# 畫出輪廓(相當(dāng)于把輪廓畫在原圖像上,注意畫的時候會改變原圖像的值,所以最好是先復(fù)制一份原圖像)
# 函數(shù)原型:cv2.drawContours(image, contours, contourIdx, color, thickness)
# ——image:原圖像
# ——contours:輪廓本身,是一個list,list中每個元素都是一個輪廓點(diǎn)集(不是每個輪廓點(diǎn)集都是一個閉合的輪廓)
# ——contourIdx:輪廓的索引(等于是畫第幾個輪廓,-1則為畫所有的輪廓)
# ——color:輪廓的顏色。
# ——thickness:輪廓的粗細(xì)。
draw_img = img.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
ret = cv2.drawContours(draw_img , contours , -1 , (0 , 0 , 255) , 2)
img_show(ret , 'ret')
draw_img2 = img2.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
ret2 = cv2.drawContours(draw_img2 , contours2 , -1 , (0 , 0 , 255) , 2)
img_show(ret2 , 'ret2')
# 輪廓特征
# 首先要計算輪廓的特征需要把第一個輪廓提取出來隨后再利用函數(shù)進(jìn)行計算。
cnt = contours[0] # 提取出第一個輪廓(可以理解為把輪廓第一個節(jié)點(diǎn)的地址給出來(因?yàn)閏ontours得到的是返回的一個list類型的變量))
cnt2 = contours2[0] # 提取出第一個輪廓(可以理解為把輪廓第一個節(jié)點(diǎn)的地址給出來(因?yàn)閏ontours得到的是返回的一個list類型的變量))
# 計算輪廓面積函數(shù)原型:cv2.contourArea(contours)
print(cv2.contourArea(cnt2))
# 計算輪廓周長函數(shù)原型:cv2.arcLength(contours , True)
print(cv2.arcLength(cnt , True)) # True表示輪廓是閉合的,F(xiàn)alse表示輪廓是不閉合的
# 輪廓的近似
# 函數(shù)原型:cv2.approxPolyDP(curve, epsilon, closed)
# ——curve:輸入的點(diǎn)集
# ——epsilon:指定的精度,也就是原始曲線與近似曲線之間的最大距離。
# ——closed:曲線是否是閉合的
img3 = cv2.imread('11.png') # 讀取彩色圖像(用來當(dāng)作后面畫輪廓的原圖像)
img3_gray = cv2.imread('11.png',cv2.IMREAD_GRAYSCALE) # 讀取灰度圖像
ret3 , thresh3 = cv2.threshold(img3_gray , 100 , 255 , cv2.THRESH_BINARY)# 二值化處理
contours3 , hierarchy3 = cv2.findContours(thresh3 , cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)# 查找輪廓
cnt3 = contours3[0] # 提取出第一個輪廓
eplision = 0.01 * cv2.arcLength(cnt3 , True) # 這里是指定的精度,也就是原始曲線與近似曲線之間的最大距離.
approx = cv2.approxPolyDP(cnt3 , eplision , True) # 這里是對輪廓進(jìn)行近似處理.
img_draw3 = img3.copy()# 這里是復(fù)制一份原圖像,是因?yàn)楫嬢喞臅r候會改變原圖像的值,所以復(fù)制一份可以保證原圖像不被破壞。
res3 = cv2.drawContours(img_draw3, [approx], -1, (0, 0, 255), 3)
img_show(res3 , 'res3')
到了這里,關(guān)于opencv中輪廓檢測以及輪廓近似的分析——輪廓近似原理,所有代碼開源,所有函數(shù)的參數(shù)。的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!