python opencv轮廓提取_Python + Opencv2 实现轮廓提取,轮廓区域面积计算
對(duì)圖像處理時(shí),會(huì)遇到這樣一個(gè)場(chǎng)景:找到圖像主體輪廓,這是其一,可能為了凸顯輪廓,需要用指定的顏色進(jìn)行標(biāo)記;輪廓標(biāo)記完可能任務(wù)還沒有結(jié)束,還需對(duì)輪廓所勾勒的像素面積區(qū)域統(tǒng)計(jì)計(jì)算。
本篇文章的主要內(nèi)容就是要解決上面場(chǎng)景遇到的三個(gè)問問題
找到圖像主題輪廓;
用指定顏色對(duì)源圖像進(jìn)行輪廓標(biāo)記;
計(jì)算輪廓中的主體;
實(shí)驗(yàn)環(huán)境配置為 Python + Opencv 3.4, 處理的圖像如下:
第一步,提取輪廓,Opencv 中的 findContours() 函數(shù) 可以直接提取輪廓,但對(duì)輸入圖像有一定要求
一,輸入的圖像必須是單通道,三通道不允許;
二,輸入的圖像數(shù)據(jù)類型需是 8UC1;否則程序會(huì)報(bào)錯(cuò)的,報(bào)錯(cuò)信息如下:
error: (-210) [start]FindContours supports only CV_8UC1 images when mode != CV_RETR_FLOODFILL otherwise supports CV_32SC1 images only in function cvStartFindContours_Impl
解決方法,在讀取時(shí)加入下面這行代碼進(jìn)行數(shù)據(jù)格式轉(zhuǎn)換,同時(shí)解決上面兩個(gè)問題:
mat_img2 = cv2.imread(img_path,cv2.CV_8UC1)
三、輸入的圖像背景需是黑色的,否則輪廓提取失敗,就以本次圖像為例,如果直接提取效果如下:
圖片最外層是一層黑色部分,所以最終結(jié)果就是標(biāo)記最外層;對(duì)這類背景非黑色的圖片做輪廓提取時(shí),需要進(jìn)行預(yù)處理:把背景變?yōu)楹谏?/p>
提供一個(gè)簡(jiǎn)單辦法,閾值化處理:設(shè)定一個(gè)閾值 Threshold 和一個(gè)指定值 OutsideValue ,當(dāng)圖像中像素滿足某種條件(大于或小于設(shè)定的閾值時(shí)),像素值發(fā)生變化。
自適應(yīng)閾值化分割
這里用到的是 Opencv 提供的自適應(yīng)閾值分割算法,其函數(shù)格式為:
dst=cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
src 需要分割的圖像( adarray 類型);
maxValue ,滿足條件是替換的像素值,等價(jià)于上面提到的 OutsideValue;
adaptiveMetheod: 自適應(yīng)閾值分割算法,Opencv 中提供兩種方法
1,ADAPTIVE_THRESH_MEAN_C : 最后的像素值 $T(x,y)$ 為原像素值 $(x,y)$ $blocksize*blocksize$ 區(qū)域像素的平均值 $C$;
2,ADAPTIVE_THRESH_GAUSSIAN_C : 最后像素值 $T(x,y)$ 為原像素值 $(x,y)$ 附近 $blocksize*blocksize$ 區(qū)域大小最小值 $C$;
thresholdType 閾值分割方法,Opencv 提供了5 種;
1,THRESH_BINARY:
$$
dst(x,y) = \left{
\begin{aligned}
maxval & &if\ src(x,y)>thresh\
0 & & otherwise\
\end{aligned}
\right.
$$
2,THRESH_BINARY_INV:
$$
dst(x,y) = \left{
\begin{aligned}
0 & &if\ src(x,y)>thresh\
maxval & & otherwise\
\end{aligned}
\right.
$$
3,THRESH_TRUNC:
$$
dst(x,y)=\left{
\begin{aligned}
threshold & & if\ src(x,y)>thresh\
src(x,y)& &otherwise\
\end{aligned}
\right.
$$
4,THRESH_TOZERO:
$$
dst(x,y)=\left{\begin{aligned}src(x,y) & & if\ src(x,y)>thresh\0& &otherwise\\end{aligned}\right.
$$
5,THRESH_TOZERO_INV;
$$
dst(x,y)=\left{\begin{aligned}0 & & if\ src(x,y)>thresh\src(x,y)& &otherwise\\end{aligned}\right.
$$
dst : 返回的閾值分割圖像(是 ndarray 類型)
下面這行代碼就是本次實(shí)驗(yàn)設(shè)置的參數(shù):
dst = cv2.adaptiveThreshold(mat_img2,210,cv2.BORDER_REPLICATE,cv2.THRESH_BINARY_INV,3,10)
自適應(yīng)閾值分割的結(jié)果:
輪廓提取
接下來就是進(jìn)行輪廓提取了,用到的函數(shù):
image, contours, hierarchy=cv2.findContours(image, mode, method)
image 返回的圖像,在 Opencv 4.0 之后就沒有這個(gè)參數(shù)了;
contours 標(biāo)記的輪廓,以 list 形式存在,每個(gè)輪廓中都包含了輪廓像素的坐標(biāo)向量;
hierarchy 表示輪廓的繼承關(guān)系,一般用不到;d
image 后面image 表示需要標(biāo)記輪廓的圖像,以 ndarray 格式存在;
mode 標(biāo)記輪廓的模式,Opencv 提供了4種;
1,RETR_EXTERNAL;只提取整體外部輪廓;
2,RETR_LIST; 提取所有輪廓,不需要建立任何繼承關(guān)系;
3, RETR_CCOMP ;提取所有輪廓,最后形成連個(gè)水平集,外面一個(gè),內(nèi)部一個(gè);
4, RETR_TREE ;提取所有輪廓,構(gòu)建等級(jí)關(guān)系(父子繼承關(guān)系)
method :輪廓近似點(diǎn)連接方式,例如一個(gè)長(zhǎng)方形,可以由數(shù)百個(gè)點(diǎn)連接而成,單節(jié)省內(nèi)存的方式就是找到四個(gè)角點(diǎn)即可;
其中前者為 CHAIN_APPROX_NONE 后者為 CHAIN_APPROX_SIMPLE
這里分別對(duì) mode 設(shè)置不同的參數(shù),一個(gè)設(shè)為 RETR_TREE (提取全部輪廓),一個(gè)設(shè)置 RETR_EXTRENAL (只提取最外部輪廓 );可以看一下提取輪廓效果:
RETR_TREE 結(jié)果:
RETR_EXTRENAL 結(jié)果:
是不是感受到了mode 不同導(dǎo)致輪廓的差距;一般只提取一個(gè)輪廓用 RETR_EXTRENAL,多個(gè)的話用 RETR_TREE;
輪廓標(biāo)記
對(duì)輪廓顏色繪制,用到 的函數(shù)
cv2.drawContours(image, contours, contourIdx, color,thickness)
image 繪制輪廓的圖像 ndarray 格式;
contours ,findContours 函數(shù)找到的輪廓列表;
contourIdx 繪制輪廓的索引數(shù),取整數(shù)時(shí)繪制特定索引的輪廓,為負(fù)值時(shí),繪制全部輪廓;
color 繪制輪廓所用到的顏色,這里需要提醒一下, 想使用 RGB 彩色繪制時(shí),必須保證 輸入的 image 為三通道,否則輪廓線非黑即白;
thickness ,用于繪制輪廓線條的寬度,取負(fù)值時(shí)將繪制整個(gè)輪廓區(qū)域;
以下就是分別取 thickness 為3(左)、-3(右) 繪制的結(jié)果
輪廓區(qū)域面積計(jì)算
最后計(jì)算輪廓面積,用到 cv2.contourArea(contour) 函數(shù),里面的參數(shù)指的就是計(jì)算的輪廓
area = 0
for i in contours:
area += cv2.contourArea(i)
print(area)
>>>16397.5 #最后結(jié)果
本篇文章用到的完整代碼如下:
import cv2
img_path = "E:/data_ceshi/images.jpg"
#讀取文件
mat_img = cv2.imread(img_path)
mat_img2 = cv2.imread(img_path,cv2.CV_8UC1)
#自適應(yīng)分割
dst = cv2.adaptiveThreshold(mat_img2,210,cv2.BORDER_REPLICATE,cv2.THRESH_BINARY_INV,3,10)
#提取輪廓
img,contours,heridency = cv2.findContours(dst,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#標(biāo)記輪廓
cv2.drawContours(mat_img,contours,-1,(255,0,255),3)
#計(jì)算輪廓面積
area = 0
for i in contours:
area += cv2.contourArea(i)
print(area)
#圖像show
cv2.imshow("window1",mat_img)
cv2.waitKey(0)
總結(jié)
以上是生活随笔為你收集整理的python opencv轮廓提取_Python + Opencv2 实现轮廓提取,轮廓区域面积计算的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python列表求平均值_python与
- 下一篇: python预处理缺失值_[Scikit