通過Opencv進行邊緣檢測可以說是十分常見了,接下來讓我們聊一聊如何通過python opencv一步一步實現邊緣檢測
pic = cv2.imread(file_path, flag=None)
參數:
返回值:
注意:結果可能與 cvtColor() 的輸出不同。編解碼器附帶 OpenCV 圖像(libjpeg、默認使用 libpng、libtiff 和 libjasper)。因此,OpenCV 始終可以讀取 JPEG、PNG、TIFF。而Linux需要安裝隨操作系統映像提供的編解碼器libjpeg-dev
cap = cv2.VideoCapture(CAMERA_ID)
參數
返回值
ret, frame = cap.read()
返回值
gs_frame=cv2.GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
說明:將源圖像與指定的高斯核進行卷積
參數
OpenCV中的inRange()函數可實現二值化功能(這點類似threshold()函數inRange_hsv=cv2.inRange((InputArray src, InputArray lowerb,InputArray upperb, OutputArray dst)
參數
返回值
img_erosion=cv2.erode(src, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None)
說明:通過使用特定的結構元素侵蝕圖像。該函數使用確定的指定結構元素侵蝕源圖像,取最小值的像素鄰域的形狀.侵蝕可以應用幾次(迭代),對於多通道圖像,每個通道都是獨立處理的。
參數
返回值
說明:通過使用特定的結構元素來擴大圖像。該函數使用指定的結構元素來擴展源圖像,該結構元素確定取最大值的像素鄰域的形狀。dilate_img=cv2.dilate(src, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None)
參數
返回值
morphologyEx_frame=cv2.morphologyEx(src, op, kernel, dst=None, anchor=None, iterations=None, borderType=None, borderValue=None)
說明:使用腐蝕和膨脹來執行高級形態變換,開操作就是先腐蝕後膨脹
參數
返回值
sobel算子簡介:sobel算子認為,鄰域的像素對當前像素產生的影響不是等價的,所以距離不同的像素具有不同的權值,對算子結果產生的影響也不同。一般來說,距離越遠,產生的影響越小。
sobel算子原理: 對傳進來的圖像像素做卷積,卷積的實質是在求梯度值,或者說給了一個加權平均,其中權值就是所謂的卷積核;然後對生成的新像素灰度值做阈值運算,以此來確定邊緣信息。x方向的梯度會加強圖像水平方向的特征,而y方向的梯度會加強圖像豎直方向的特征
sobel=cv2.Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)
說明:Sobel 算子結合了高斯平滑和微分,所以結果或多或少耐噪音。大多數情況下,函數被調用( xorder = 1, yorder = 0, ksize = 3)或 ( xorder = 0, yorder = 1, ksize = 3) 計算第一個 x 或 y 圖像導數。
參數
dst=cv2.convertScaleAbs(src, dst=None, alpha=None, beta=None)
說明:由於sobel算子算出的值會產生負值,否則負數會一律當做0處理,所以要使用取絕對值函數。在輸入數組的每個元素上,函數 convertScaleAbs依次執行三個操作:縮放、取絕對值,轉換為無符號 8 位類型。在多通道數組的情況下,該函數處理每個通道獨立。當輸出不是 8 位時,操作可以是通過調用 Mat::convertTo 方法(或使用矩陣表達式),然後計算結果的絕對值。
參數
add_weight_img=cv2.addWeighted(src1, alpha, src2, beta, gamma, dst=None, dtype=None)
說明:計算兩個數組的加權和: dst = src1alpha + src2beta + gamma
參數
scharr_img=cv2.Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None)
說明:雖然Sobel算子可以有效的提取圖像邊緣,但是對圖像中較弱的邊緣提取效果較差。因此為了能夠有效的提取出較弱的邊緣,需要將像素值間的差距增大。Scharr算子是對Sobel算子差異性的增強,因此兩者之間的在檢測圖像邊緣的原理和使用方式上相同。Scharr算子的邊緣檢測濾波的尺寸為3×3,因此也有稱其為Scharr濾波器。可以通過將濾波器中的權重系數放大來增大像素值間的差異.
參數
canny_img=cv2.Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)
說明:在輸入圖像中找到邊緣,並使用精明算法。阈值 1 和阈值 2 之間的最小值用於邊緣鏈接。這最大值用於查找強邊緣的初始段。
參數
contours, hierarchy=cv2.findContours(image, mode, method, contours=None, hierarchy=None, offset=None):
說明:從二進制圖像中檢索輪廓。
參數
image:一般為sobel算子或者scharr算子得到的二值化圖像。8 位單通道圖像。非零像素被視為 1。零像素保持為 0,因此圖像被視為 binary
mode:
method
contours:檢測的輪廓數組,每一個輪廓用一個point類型的vector表示
hierarchy:和輪廓個數相同,每個輪廓contours[ i ]對應4個hierarchy元素hierarchy[ i ][0 ] ~hierarchy[ i ][ 3],分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,該值設置為負數。
offset:每個輪廓點移動的可選偏移量
返回值
cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
說明:繪制輪廓輪廓或填充輪廓
參數
源代碼:
import cv2
import numpy as np
# 選擇圖片檢測或者選擇視頻檢測
is_picture = True
file_path = '../Picture/Badminton.jpeg'
# 識別OpencCV書的阈值:'red': {'Lower': np.array([0, 53, 66]), 'Upper': np.array([74, 76, 187])
# 這個是顏色的HSV的范圍值(分別代表的是H,S,V),可以根據需求進行刪改
color_dist = {
'red': {
'Lower': np.array([0, 53, 66]), 'Upper': np.array([74, 76, 187])},
'blue': {
'Lower': np.array([100, 80, 46]), 'Upper': np.array([124, 255, 255])},
'green': {
'Lower': np.array([77, 54, 47]), 'Upper': np.array([166, 255, 255])},
'yellow': {
'Lower': np.array([26, 43, 46]), 'Upper': np.array([34, 255, 255])},
}
class DetectProcess(object):
def __init__(self):
super(DetectProcess, self).__init__()
# 形態學腐蝕操作:cv2.erode
def erode(self, img):
# 創建腐蝕使用的內核:3x3和5x5
kernel = np.ones((3, 3), np.uint8)
# 執行腐蝕操作 iterations表示執行腐蝕的次數
img_erosion = cv2.erode(img, kernel, iterations=1)
return img_erosion
# 形態學膨脹:cv2.dilate
def dilate(self, img):
kernel = np.ones((5, 5), np.uint8)
dilation = cv2.dilate(img, kernel, iterations=1)
return dilation
# 開運算:cv2.morphologyEx() :先腐蝕再膨脹,有助於消除噪音.
def morphologyExOpening(self, img):
kernel = np.ones((5, 5), np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
return opening
# 閉運算:用於消除前景對象內的小孔或對象上的小黑點
def morphologyExClosing(self, img):
kernel = np.ones((5, 5), np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
return closing
# sobel算子:返回的圖像通道和原圖相同,即彩色圖像處理後仍為3通道
# sobel得到的圖像是有許多小點的的
def sobel(self, img):
# 核函數的取值范圍:1,3,5,7,9,核函數過大效果不好
Ksize = 3
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=Ksize)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=Ksize)
# sobel-x方向
sobel_X = cv2.convertScaleAbs(sobelx)
# sobel-y方向
sobel_Y = cv2.convertScaleAbs(sobely)
# sobel-xy方向
sobel_XY = cv2.addWeighted(sobel_X, 0.5, sobel_Y, 0.5, 0)
return sobel_XY
# Scharr算子:返回的圖像通道和原圖相同,即彩色圖像處理後仍為3通道
def scharr(self, img):
scharr_x = cv2.Scharr(img, cv2.CV_8U, 1, 0)
scharr_y = cv2.Scharr(img, cv2.CV_8U, 0, 1)
scharrX = cv2.convertScaleAbs(scharr_x)
scharrY = cv2.convertScaleAbs(scharr_y)
scharr_XY = cv2.addWeighted(scharrX, 0.5, scharrY, 0.5, 0)
return scharr_XY
# Canny:返回的為單通道圖像
# 得到的是一條淡顏色的線
def canny(self, img):
# 核函數的取值為3,5,7
# 高低阈值maxVal和minVal的取值范圍1-255
# L2g為“精准”標識符,參數為True和False
Ksize = 3
minVal = 20
maxVal = 40
L2g = True
Canny = cv2.Canny(img, minVal, maxVal, apertureSize=Ksize, L2gradient=False)
return Canny
# 對輪廓的面積進行篩選
def areaFilter(contours):
""" 對面積進行篩選,若大於900的面積我們才認為是有效的邊緣,這個可以根據自己的需求進行調整 :param contours: 輪廓的集合 :return: 識別到的有效的輪廓集合 """
areas = []
for i in range(len(contours)):
if cv2.contourArea(contours[i]) <= 900:
continue
else:
areas.append(contours[i])
return areas
# areas.sort()
# print("area is:", areas)
if __name__ == '__main__':
detect = DetectProcess()
# 調用攝像頭
cap = cv2.VideoCapture(0)
while True:
if is_picture:
frame = cv2.imread(file_path)
else:
# 讀取視頻幀,ret標志讀取的結果,frame為讀取到的視頻幀圖像
ret, frame = cap.read()
# 高斯濾波
gs_frame = cv2.GaussianBlur(frame, (5, 5), 0)
# 轉化成HSV圖像
hsv = cv2.cvtColor(gs_frame, cv2.COLOR_BGR2HSV)
# 規定紅色區域的HSV.
# OpenCV中的inRange()函數可實現二值化功能(這點類似threshold()函數
# cv2.inRange((InputArray src, InputArray lowerb,InputArray upperb, OutputArray dst);
# 參數1:輸入要處理的圖像,可以為單通道或多通道。
# 參數2:包含下邊界的數組或標量。
# 參數3:包含上邊界數組或標量。
# 參數4:輸出圖像,與輸入圖像src 尺寸相同且為CV_8U 類型。也可以作為返回值處理
inRange_hsv = cv2.inRange(hsv, color_dist['green']['Lower'], color_dist['green']['Upper'])
cv2.imshow('inRange_hsv', inRange_hsv)
# 形態學腐蝕操作
erode_frame = detect.erode(inRange_hsv)
cv2.imshow('erode', erode_frame)
# 形態學膨脹操作
dilate_frame = detect.dilate(erode_frame)
cv2.imshow('dilate', dilate_frame)
# 開運算
morphologyExOpening_frame = detect.morphologyExOpening(inRange_hsv)
cv2.imshow('morphologyExOpening', morphologyExOpening_frame)
# 閉運算
morphologyExClosing_frame = detect.morphologyExClosing(morphologyExOpening_frame)
cv2.imshow('morphologyExClosing', morphologyExClosing_frame)
# Sobel_frame
Sobel_frame = detect.sobel(morphologyExClosing_frame)
cv2.imshow('Sobel_frame', Sobel_frame)
# 尋找外部的點:建立RETR_EXTERNAL來統計最大外圍的點。
contours, hierarchy = cv2.findContours(Sobel_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# cnt = contours[1:50] 這個表示的是提取的圖像的范圍
cv2.drawContours(frame, areaFilter(contours), -1, (0, 0, 255), 2)
cv2.imshow('result', frame)
if cv2.waitKey(1) & 0xFF == 27:
break
cv2.destroyAllWindows()
效果: