python進階—OpenCV之圖像處理(二)


本篇續接OpenCV圖像處理部分。到現在爲止已經2個多月了,才寫到opencv教程的第3部分,任重道遠。

圖像形態變換

圖像的形態轉換主要是基於二值化圖像,進行圖像的形狀操作。形態變換操作需要2個輸入;一個是二值化圖像,另外一個被稱爲“結構化元素”;主要的形態轉換操作是複試與膨脹,其它形態轉換操作都基於此兩者操作。

圖像的腐蝕

對輸入圖像用特定結構元素進行腐蝕操作,表現出的現象是圖像被縮小;此處的縮小不是指圖像的尺寸變小,而是圖像中的信息部分(主要是亮度較高的像素)縮小。
腐蝕算法:用結構元素對應的矩陣,例如3X3,掃描圖像的每一個像素,用結構元素與其覆蓋的圖像做“與”運算,如果都爲1,結構圖像的該像素爲1,否則爲0;結果:使圖像減小一圈。

  • 函數原型:dst = cv.erode( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
  • src:輸入始圖像,通道數不限,depth必須是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
  • dst:輸出圖像:size與type與原始圖像相同
  • kernel:用於腐蝕操作的結構元素,如果取值爲Mat(),那麼默認使用一個3 x 3 的方形結構元素,可以使用getStructuringElement來創建結構元素
  • anchor:卷積核的基準點(anchor),其默認值爲(-1,-1)表示位於kernel中心位置。基準點即選取的kernel中心位置與進行處理像素重合的點。
  • iterations:腐蝕操作被遞歸執行的次數
  • borderType:圖像邊界模式
  • borderValue:邊界值,默認值是BORDER_DEFAULT
import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations = 1)

在這裏插入圖片描述在這裏插入圖片描述

圖像的膨脹

對輸入圖像用特定結構元素進行膨脹操作,表現出的現象是圖像被放大;此處的放大不是指圖像的尺寸變大,而是圖像中的信息部分(主要是亮度較高的像素)放大。
膨脹算法:用結構元素對應的矩陣,例如3X3,掃描圖像的每一個像素,用結構元素與其覆蓋的圖像做“或”運算,如果都爲0,結構圖像的該像素爲0,否則爲1;結果:使圖像放大一圈。

  • 函數原型:dst = cv.dilate( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
  • src:輸入始圖像,通道數不限,depth必須是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
  • dst:輸出圖像:size與type與原始圖像相同
  • kernel:用於腐蝕操作的結構元素,如果取值爲Mat(),那麼默認使用一個3 x 3 的方形結構元素,可以使用getStructuringElement來創建結構元素
  • anchor:卷積核的基準點(anchor),其默認值爲(-1,-1)表示位於kernel中心位置。基準點即選取的kernel中心位置與進行處理像素重合的點。
  • iterations:腐蝕操作被遞歸執行的次數
  • borderType:圖像邊界模式
  • borderValue:邊界值,默認值是BORDER_DEFAULT
import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
dilation = cv.dilate(img,kernel,iterations = 1)

在這裏插入圖片描述

圖像的開操作

圖像的開操作是指,先腐蝕後膨脹,適合去除圖像有效信息外的白噪點

  • 函數原型:dst = cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]])
  • src:輸入始圖像,通道數不限,depth必須是CV_8U,CV_16U,CV_16S,CV_32F或CV_64F
  • dst:輸出圖像:size與type與原始圖像相同
  • op:形態學運算的類型,主要有
    cv.MORPH_OPEN: 開運算
    cv.MORPH_CLOSE :閉運算
    cv.MORPH_GRADIENT: 形態學梯度
    cv.MORPH_TOPHAT:頂帽運算
    cv.MORPH_BLACKHAT: 黑帽運算
    cv.MORPH_ERODE :腐蝕運算
    cv.MORPH_DILATE :膨脹運算
    cv.MORPH_HITMISS: 擊中擊不中運算(只支持CV_8UC1類型的二值圖像)
  • kernel:用於腐蝕操作的結構元素,如果取值爲Mat(),那麼默認使用一個3 x 3 的方形結構元素,可以使用getStructuringElement來創建結構元素
  • anchor:卷積核的基準點(anchor),其默認值爲(-1,-1)表示位於kernel中心位置。基準點即選取的kernel中心位置與進行處理像素重合的點。
  • iterations:腐蝕操作被遞歸執行的次數
  • borderType:圖像邊界模式
  • borderValue:邊界值,默認值是BORDER_DEFAULT
import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)

在這裏插入圖片描述

圖像的閉操作

圖像的閉操作是指,先膨脹後腐蝕,適合去除圖像有效信息內的黑噪點
函數與開操作函數相同,只是op參數不同,取值爲:cv.MORPH_CLOSE

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)

在這裏插入圖片描述

圖像的形態學梯度

形態學梯度:膨脹圖減去腐蝕圖,dilation - erosion,這樣會得到物體的輪廓
函數與開操作函數相同,只是op參數不同,取值爲:cv.MORPH_GRADIENT

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)

在這裏插入圖片描述

圖像的頂帽操作

頂帽(tophat):原圖減去開操作後的圖:src - open_dst
因爲開操作帶來的結果是放大了裂縫或者局部低亮度的區域,因此,從原圖中減去開運算後的圖,得到的效果圖突出了比原圖輪廓周圍的區域更明亮的區域,且這一操作和選擇的核的大小相關。
頂帽運算往往用來分離比鄰近點亮一些的斑塊。當一幅圖像具有大幅的背景的時候,而微小物品比較有規律的情況下,可以使用頂帽運算進行背景提取。
函數與開操作函數相同,只是op參數不同,取值爲:cv.MORPH_TOPHAT

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)

在這裏插入圖片描述

圖像的黑帽操作

黑帽(blackhat):閉運算圖像與原圖像差值:close_dst - src
黑帽運算後的效果圖突出了比原圖輪廓周圍的區域更暗的區域,且這一操作和選擇的核的大小相關。所以,黑帽運算用來分離比鄰近點暗一些的斑塊,顯示出圖像的輪廓。
函數與開操作函數相同,只是op參數不同,取值爲:cv.MORPH_BLACKHAT

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)

在這裏插入圖片描述

圖像的梯度(Image Gradients)

opencv提供了三種梯度過濾器或高通過濾器Sobel、Scharr、Laplacian.(高通濾波器high-pass fliter(HPF) 尋找圖片的邊界)。
圖像上的高通濾波器怎麼理解,暫時不得而知,

Sobel and Scharr (索貝爾)算子

Sobel operators是高斯模糊操作 加上differentian(變異/分化);所以它對噪音更有抵抗;可以指明衍生物(derivatives)的方向,垂直或者是水平的(分別對應是yorder、xorder)。
在參數ksize中指明kernel 的大小。其中ksize=-1時,會使用3x3的Charr filter(在這個情況下,結果會更好)
Sobel算子是一階導數的邊緣檢測算子,在算法實現過程中,通過3×3模板作爲核與圖像中的每個像素點做卷積和運算,然後選取合適的閾值以提取邊緣。
採用3×3鄰域可以避免在像素之間內插點上計算梯度。Sobel算子也是一種梯度幅值。
Sobel算子算法的優點是計算簡單,速度快。但是由於只採用了2個方向的模板,只能檢測水平和垂直方向的邊緣,因此這種算法對於紋理較爲複雜的圖像,其邊緣檢測效果就不是很理想。
該算法認爲:凡灰度新值大於或等於閾值的像素點時都是邊緣點。這種判斷欠合理,會造成邊緣點的誤判,因爲許多噪聲點的灰度值也很大。
scharr算子與Sobel的不同點是在平滑部分,這裏所用的平滑算子是1/16∗[3,10,3],相比於1/4∗[1,2,1],中心元素佔的權重更重,這可能是相對於圖像這種隨機性較強的信號,鄰域相關性不大,所以鄰域平滑應該使用相對較小的標準差的高斯函數,也就是更瘦高的模板

  • 函數原型:dst = cv.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]])
  • src:輸入圖像
  • dst:輸出圖像size大小與channel數量與原始圖像相同
  • ddepth:輸出圖像的像素深度,組合如下
輸入圖像 輸出圖像
CV_8U -1/CV_16S/CV_32F/CV_64F
CV_16U、CV_16S -1/CV_32F/CV_64F
CV_32F -1/CV_32F/CV_64F
CV_64F -1/CV_64F
  • dx: x 方向上的差分階數
  • dy: y 方向上的差分階數
  • ksize:Sobel kernel大小,必須是:1, 3, 5, or 7。
  • scale:可選的導數比例因子,默認未使用
  • delta:可選的delta值,輸出結果前疊加到結果上
  • borderType:圖像邊界模式

  • 函數原型:dst = cv.Scharr( src, ddepth, dx, dy[, dst[, scale[, delta[, borderType]]]] )
  • src:輸入圖像
  • dst:輸出圖像size大小與channel數量與原始圖像相同
  • ddepth:輸出圖像的像素深度。
  • dx: x 方向上的差分階數
  • dy: y 方向上的差分階數
  • ksize:Sobel kernel大小,必須是:1, 3, 5, or 7。
  • scale:可選的導數比例因子,默認未使用
  • delta:可選的delta值,輸出結果前疊加到結果上
  • borderType:圖像邊界模式
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('dave.jpg',0)
sobelx = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
sobely = cv.Sobel(img,cv.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

原圖見拉普拉斯算子原圖
在這裏插入圖片描述
注意:

  • 當輸出的值爲cv2.CV_8U或者是np.uint8時;Black- to -White transition 是positive slope(正數);反過來White- to - Black transition取negative slope(負數)。所以當輸出時轉化數據爲unit8時,所有的negative slope 都會被置爲0,這種情況都會丟失了一部分的邊緣。
  • 更好的處理方式是,讓輸出的數據類型保存到更高的形式,例如cv2.CV_16S,cv2.CV_64F等。取它的絕對值,然後再把值轉化回cv2.CV_8U。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('box.png',0)
# Output dtype = cv.CV_8U
sobelx8u = cv.Sobel(img,cv.CV_8U,1,0,ksize=5)
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx64f = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx8u,cmap = 'gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobel_8u,cmap = 'gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()

在這裏插入圖片描述

Laplacian (拉普拉斯)算子

Laplacian 算子是n維歐幾里德空間中的一個二階微分算子,定義爲梯度grad的散度div。
這裏不甚明白,還需要多加學習與練習,先看效果吧

  • 函數原型:dst=cv.Laplacian( src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]] )
  • src:輸入圖像
  • dst:輸出圖像size大小與channel數量與原始圖像相同
  • ddepth:期望輸出圖像的像素深度
  • dx: x 方向上的差分階數
  • dy: y 方向上的差分階數
  • ksize:Sobel kernel大小,必須是:1, 3, 5, or 7。
  • scale:可選的導數比例因子,默認未使用
  • delta:可選的delta值,輸出結果前疊加到結果上
  • borderType:圖像邊界模式
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('dave.jpg',0)
laplacian = cv.Laplacian(img,cv.CV_64F)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.show()

在這裏插入圖片描述

canny邊緣檢測

  1. 應用高斯濾波來平滑圖像,目的是去除噪聲
  2. 用一階偏導有限差分計算梯度幅值和方向,找尋圖像的強度梯度(intensity gradients)
  3. 應用非最大抑制(non-maximum suppression)技術來消除邊誤檢(本來不是但檢測出來是)
  4. 應用雙閾值的方法來決定可能的(潛在的)邊界
  5. 利用滯後技術來跟蹤邊界
    複雜的算法原理我描述不出來,只能上代碼看效果
  • 函數原型:edges=cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] )
  • image:8-bit input image.
  • edges:單通道存儲邊緣的輸出圖像,size大小與輸入圖像相同
  • threshold1:滯後處理的第一個閾值,(低閾值)
  • threshold2:滯後處理的第一個閾值,(高閾值)
  • apertureSize:算子內核大小(濾波計算矩陣的大小默認爲3)可以是1、3、5、7
  • L2gradient :標誌位,用於指示是否使用更準確的梯度計算方法,參數爲true時會增加計算量
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

在這裏插入圖片描述

圖像金字塔(Image Pyramids)

圖像金字塔有兩種:高斯金字塔、拉普拉斯金字塔

  • 高斯金字塔:向下降採樣,圖像每級縮小一半。金字塔從i層生成第i+1層(i從0開始),我們要先用高斯覈對Gi進行卷積,然後,刪除所有偶數行和偶數列。這樣,新得到的圖像面積會變爲源圖像的四分之一。循環上述過程,即可產生整個金字塔。
  • 拉普拉斯金字塔:從頂層圖像中向上採樣重建圖像,每一級圖像放大一倍。圖像首先在每個維度上擴大爲原來的兩倍,新增的行以0填充,然後給指定的濾波器進行卷積(實際上是一個在每一維上都擴大爲2倍的過濾器)去估計“丟失”像素的近似值。得到後的圖像與原來的圖像相比較會發覺比較模糊,丟失了一些信息。爲了恢復出原來的圖像,我們需要獲得這些丟失的信息,這些信息就構成了拉普拉斯金字塔。

高斯金字塔函數說明:

  • 函數原型:dst = cv.pyrUp( src[, dst[, dstsize[, borderType]]] )
  • src:輸入圖圖像
  • dst:輸出圖像,圖像的size爲dstsize指定,type保持與src相同
  • dstsize:輸出圖像大小
  • borderType:邊界模式,僅支持BORDER_DEFAULT
img = cv.imread('messi5.jpg')
lower_reso1 = cv.pyrDown(img)
lower_reso2 = cv.pyrDown(lower_reso1)
lower_reso3 = cv.pyrDown(lower_reso2)

在這裏插入圖片描述


拉普拉斯金字塔函數說明:

  • 函數原型:dst = cv.pyrDown( src[, dst[, dstsize[, borderType]]] )
  • src:輸入圖圖像
  • dst:輸出圖像,圖像的size爲dstsize指定,type保持與src相同
  • dstsize:輸出圖像大小
  • borderType:邊界模式,BORDER_CONSTANT不支持
img = cv.imread('messi5.jpg')
lower_reso = cv.pyrDown(higher_reso)
higher_reso2 = cv.pyrUp(lower_reso)

在這裏插入圖片描述

圖像的輪廓(Contours in opencv)

如何找到圖像輪廓

cv.findContours()函數可以查找圖像的輪廓

  • 函數原型:image, contours, hierarchy = cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] )
  • image:單通道二值圖像,一般是經過Canny、拉普拉斯等邊緣檢測算子處理過的二值圖像
  • contours:檢測到的輪廓集,每一個輪廓由一個vector保存的連續點組成。定義爲“vector<vector> contours”,是一個向量,並且是一個雙重向量,向量內每個元素保存了一組由連續的Point點構成的點的集合的向量,每一組Point點集就是一個輪廓;有多少輪廓,向量contours就有多少元素
  • hierarchy:是一個向量,向量內每個元素保存了一個包含4個int整型的數組。向量hiararchy內的元素和輪廓向量contours內的元素是一一對應的,向量的容量相同。hierarchy向量內每一個元素的4個int型變量——hierarchy[i][0] ~hierarchy[i][3],分別表示第 i個輪廓的後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號。如果當前輪廓沒有對應的後一個
    輪廓、前一個輪廓、父輪廓或內嵌輪廓的話,則hierarchy[i][0] ~hierarchy[i][3]的相應位被設置爲默認值-1
  • mode:輪廓的檢索模式,
    CV_RETR_EXTERNAL:只檢測最外圍輪廓,包含在外圍輪廓內的內圍輪廓被忽略
    CV_RETR_LIST:檢測所有的輪廓,包括內圍、外圍輪廓,但是檢測到的輪廓不建立等級關係,彼此之間獨立,沒有等級關係,這就意味着這個檢索模式下不存在父輪廓或內嵌輪廓,所以hierarchy向量內所有元素的第3、第4個分量都會被置爲-1
    CV_RETR_CCOMP:檢測所有的輪廓,但所有輪廓只建立兩個等級關係,外圍爲頂層,若外圍內的內圍輪廓還包含了其他的輪廓信息,則內圍內的所有輪廓均歸屬於頂層
    CV_RETR_TREE:檢測所有輪廓,所有輪廓建立一個等級樹結構。外層輪廓包含內層輪廓,內層輪廓還可以繼續包含內嵌輪廓
  • method:輪廓的近似方法
    CV_CHAIN_APPROX_NONE:保存物體邊界上所有連續的輪廓點到contours向量內
    CV_CHAIN_APPROX_SIMPLE:僅保存輪廓的拐點信息,把所有輪廓拐點處的點保存入contours向量內,拐點與拐點之間直線段上的信息點不予保留
    CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近 似算法
  • offset:偏移量,所有的輪廓信息相對於原始圖像對應點的偏移量,相當於在每一個檢測出的輪廓點上加上該偏移量,並且Point還可以是負值
import numpy as np
import cv2 as cv
im = cv.imread('test.jpg')
imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgray, 127, 255, 0)
im2, contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

cv.drawContours函數用於畫圖像的輪廓

  • 函數原型:image = cv.drawContours( image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]] )
  • image:用以繪製輪廓的圖像。和其他繪圖函數一樣,邊界圖像被感興趣區域(ROI)所剪切
  • contours:所有輸入圖像的輪廓集
  • contourIdx:指示輪廓集中的哪一個輪廓被畫出,如果是負值,則所有輪廓被畫出
  • color:輪廓顏色
  • thickness:繪製輪廓線的寬度。如果爲負值(例如,等於CV_FILLED),則contour內部將被繪製
  • lineType:輪廓線段的類型,具體查看cvLine的描述
  • hierarchy:可選的輪廓層次信息,如果只想畫某些層次的輪廓,才需要此參數。
  • maxLevel:繪製輪廓的最大等級。如果等級爲0,繪製單獨的輪廓。如果爲1,繪製輪廓及在其後的相同的級別下輪廓。如果值爲2,所有的輪廓。如果等級爲2,繪製所有同級輪廓及所有低一級輪廓,諸此種種。如果值爲負數,函數不繪製同級輪廓,但會升序繪製直到級別爲abs(max_level)-1的子輪廓。
  • offset:按照給出的偏移量移動每一個輪廓點座標.當輪廓是從某些感興趣區域(ROI)中提取的然後需要在運算中考慮ROI偏移量時,將會用到這個參數。
    在這裏插入圖片描述

輪廓的特點

  1. 輪廓矩Moments
    opencv中的矩主要包括以下幾種:空間矩,中心矩和中心歸一化矩。從圖像中計算出來的矩通常描述了圖像不同種類的幾何特徵如:大小、灰度、方向、形狀等,圖像矩廣泛應用於模式識別、目標分類、目標識別與防僞估計、圖像編碼與重構等領域。
import numpy as np
import cv2 as cv
img = cv.imread('star.jpg',0)
ret,thresh = cv.threshold(img,127,255,0)
im2,contours,hierarchy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
print( M )
  1. 輪廓面積Contour Area
area = cv.contourArea(cnt)
  1. 輪廓周長Contour Perimeter
perimeter = cv.arcLength(cnt,True)
  1. 輪廓近似圖Contour Approximation
epsilon = 0.1*cv.arcLength(cnt,True)
approx = cv.approxPolyDP(cnt,epsilon,True)
  1. 輪廓凸包Convex Hull
hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]
  1. 輪廓凸包檢查Checking Convexity
k = cv.isContourConvex(cnt)
  1. 輪廓矩形邊框Bounding Rectangle
x,y,w,h = cv.boundingRect(cnt)
cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
  1. 輪廓最小圓Minimum Enclosing Circle
rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img,[box],0,(0,0,255),2)

12.輪廓最橢圓 Fitting an Ellipse

(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv.circle(img,center,radius,(0,255,0),2)
  1. 輪廓直線Fitting a Line
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv.fitLine(cnt, cv.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)

輪廓的屬性

  1. 輪廓寬高比Aspect Ratio
    輪廓寬高比是輪廓的矩形邊框的寬高比
x,y,w,h = cv.boundingRect(cnt)
aspect_ratio = float(w)/h
  1. 輪廓區域比Extent
    輪廓區域比是輪廓的面積與矩形邊框的面積比
area = cv.contourArea(cnt)
x,y,w,h = cv.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area
  1. 輪廓固態Solidity
    輪廓固態是輪廓的面積與凸包面積的比
area = cv.contourArea(cnt)
hull = cv.convexHull(cnt)
hull_area = cv.contourArea(hull)
solidity = float(area)/hull_area
  1. 輪廓直徑Equivalent Diameter
area = cv.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)
  1. 輪廓方向Orientation
(x,y),(MA,ma),angle = cv.fitEllipse(cnt)
  1. 掩膜與像素位置Mask and Pixel Points
mask = np.zeros(imgray.shape,np.uint8)
cv.drawContours(mask,[cnt],0,255,-1)
pixelpoints = np.transpose(np.nonzero(mask))
pixelpoints = cv.findNonZero(mask)
  1. 最大與最小值位置Maximum Value, Minimum Value and their locations
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray,mask = mask)
  1. 平均顏色與強度Mean Color or Mean Intensity
mean_val = cv.mean(im,mask = mask)
  1. 輪廓極點Extreme Points
leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])

輪廓的層次

cv2.findContours()函數來找輪廓的時候,我們傳入了一個參數,Contour Retrieval Mode。我們一般傳的是cv2.RETR_LIST或者cv2.RETR_TREE這樣就可以了。但是這個參數實際是什麼意思呢?
函數輸出時我們得到了三個數組,第一個是圖像,第二個是我們的輪廓,第三個輸出名字是hierarchy層次。
什麼是層次呢?
用cv2.findContours()函數來檢測圖像裏的輪廓,有時候輪廓在不同的地方,但是在有些情況下,有些圖形在別的圖形裏面,就像圖形嵌套,在這種情況下,我們把外面那層圖形叫做parent,裏面的叫child。這樣圖形裏的輪廓之間就有了關係。我們可以指定一個輪廓和其他之間的是如何連接的,這種關係就是層級。

每個輪廓有他自己的關於層級的信息,誰是他的孩子,誰是他的父親等。OpenCV用一個包含四個值得數組來表示:[Next, Previous, First_Child, Parent]"Next表明同一層級的下一個輪廓"比如,在我們的圖片裏的contour-0,水上hi他相同層級的下一個輪廓?是contour-1,所以Next=1,對於Contour-1,下一個是contour-2,所以Next=2那對於contour-2呢?沒有同層級的下一個輪廓,所以Next=-1。那麼對於contour-4呢?同層級的下一個是contour-5,所以下一個輪廓是contour-5.Next=5"Previous指同層級的前一個輪廓"和上面一樣,contour-1的前一個是contour-0.contour-2的前一個contour-1.對於contour-0沒有前序,所以-1"First_Child指它的第一個孩子輪廓"不用解釋,對於contour-2,孩子是contour-2a,所以這裏是contour-2a的索引,contour-3a有兩個孩子,但我們只取第一個,是contour-4,所以First_Child=4."Parent指它的父輪廓索引"和First_Child相反,contour-4和contour-5的parent都是contour-3a,對於contour-3a,是contour-3注意:如果沒有孩子或者父親,就爲-1
在這裏插入圖片描述

  1. RETR_LIST
    獲取所有輪廓,但是不建立父子關係,他們都是一個層級。所以,層級屬性第三個和第四個字段(父子)都是-1,但是Next和Previous還是有對應值。
  2. RETR_EXTERNAL
    這個模式,它返回最外層的。所有孩子輪廓都不要,我們可以說在這種情況下,只有家族裏最老的會被照顧,其他都不管。
  3. RETR_CCOMP
    這個模式獲取所有輪廓並且把他們組織到一個2層結構裏,對象的輪廓外邊界在等級1裏,輪廓內沿(如果有的話)放在層級2裏。如果別的對象在它裏面,裏面的對象輪廓還是放在層級1裏,它的內沿在層級2
  4. RETR_TREE
    這個取回所有的輪廓並且創建完整的家族層級列表,它甚至能告訴你誰是祖父,父親,兒子,孫子。

寫到這裏發現,OPENCV的內容越來越多,需要消化的時間越來越長,往後幾篇時間也許會在1個月以後了,就現在這些寫的東西也需要多次的練習與理解,繼續努力吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章