邊緣/輪廓檢測
Canny邊緣檢測
Canny邊緣檢測原理
Canny邊緣檢測的一般步驟
- step1:去噪
- 邊緣檢測容易受到噪聲的影響。因此,在進行邊緣檢測前,通常需要先進行去噪
- 通常採用高斯濾波器去除噪聲
- step2:梯度
- 對平滑後的圖像採用sobel算子計算梯度和方向
- 梯度的方向一般總是與邊界垂直
- 梯度方向被歸爲四類:垂直、水平、兩個對角線
- step3:非極大值抑制
- 在獲得了梯度和方向後,遍歷圖像,去除所有不是邊界的點
- 實現方法:逐個遍歷像素點,判斷當前像素點是否是周圍像素點中具有相同方向梯度的最大值;若是,保留該點;否則,抑制(歸零)
- 例子:下圖黃色背景的值被保留,其餘點被抑制(處理爲0)
- step4:滯後閾值
- 兩個值:
minVal
:滯後閾值1;maxVal
:滯後閾值2 - 閾值越大,信息越少
- 例子:
- 兩個值:
Canny函數及使用
edges = cv2.Canny(image, threshold1, threshold2)
edges
:邊界圖像image
:原始圖像threshold1
:閾值1(minVal
)threshold2
:閾值2(maxVal
)
操作小記
import cv2
o = cv2.imread("lena.jpg", cv2.IMREAD_GRAYSCALE)
r = cv2.Canny(o, 100, 200)
cv2.imshow("ori", o)
cv2.imshow("res", r)
cv2.waitKey()
cv2.destroyAllWindows()
效果
輪廓查找和繪製
圖像輪廓
輪廓定義
- 邊緣檢測能夠測出邊緣,但是邊緣是不連續的
- 將邊緣連接爲一個整體,構成輪廓
注意問題
- 對象是二值圖像。所以需要預先進行閾值分割或者邊緣檢測處理
- 查找輪廓需要更改原始圖像。因此,通常使用原始圖像的一份拷貝操作
- 在OpenCV中,是從黑色背景中查找白色對象。因此,對象必須是白色的,背景必須是黑色的
使用函數
cv2.findContours()
:查找圖像輪廓的函數contours, hierarchy = cv2.findContours(image, mode, method)
contours
:輪廓hierarchy
:圖像的拓撲信息(輪廓層次)image
:原始圖像mode
:輪廓檢索模式cv2.RETR_EXTERNAL
:表示只檢測外輪廓cv2.RETR_LIST
:檢測的輪廓不建立等級關係cv2.RETR_CCOMP
:建立兩個等級的輪廓,上面的一層爲外邊界,裏面的一層爲內孔的邊界信息。如果內孔內還有一個連通物體,這個物體的邊界也在頂層cv2.RETR_TREE
:建立一個等級樹結構的輪廓
method
:輪廓的近似方法cv2.CHAIN_APPROX_NONE
:存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2), abs(y2-y1)) == 1
cv2.CHAIN_APPROX_SIMPLE
:壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點座標,例如一個矩形輪廓只需4個點來保存輪廓信息cv2.CHAIN_APPROX_TC89_L1
:使用teh-Chinl chain 近似算法cv2.CHAIN_APPROX_TC89_KCOS
:使用teh-Chinl chain 近似算法
cv2.drawContours()
:將查找的輪廓繪製到圖像上r = cv2.drawContours(o, contours, contourldx, color[, thickness])
r
:目標圖像,直接修改目標的像素點,實現繪製o
:原始圖像contours
:需要繪製的邊緣數組contourldx
:需要繪製的邊緣索引,如果全部繪製則爲 -1color
:繪製的顏色,爲BGR格式的Scalarthickness
:可選,繪製的密度,即描述輪廓時所用的畫筆粗細
注意:若是採用這個格式img, contours, hierarchy = cv2.findContours(image, mode, method)
會報下面錯誤,原因是opencv的方法改掉了,findCountours
方法被修改爲只返回coutours和hierarchy
Traceback (most recent call last):
File "D:/data/Code/PycharmProjects/helloworld/test.py", line 7, in <module>
img, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
ValueError: not enough values to unpack (expected 3, got 2)
Process finished with exit code 1
操作小記
import cv2
o = cv2.imread("money.png")
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
co = o.copy()
r = cv2.drawContours(co, contours, -1, (0, 0, 255), 1)
cv2.imshow("ori", o)
cv2.imshow("res", r)
cv2.waitKey()
cv2.destroyAllWindows()
效果
Hough輪廓檢測
Hough-直線檢測
霍夫直線變換介紹
- Hough Line Transform用來做直線檢測
- 前提條件:邊緣檢測已經完成
- 平面空間到極座標空間轉換
- 具體
操作小記
import cv2
import numpy as np
def line_detection(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi / 180, 200)
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * a)
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * a)
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow("line_detection", img)
def line_detect_possible_demo(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=50, maxLineGap=10)
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow("line_detect_possible_demo", img)
img = cv2.imread("Sudoku.png")
cv2.imshow("ori", img)
line_detection(img)
line_detect_possible_demo(img)
cv2.waitKey()
效果
Hough-圓檢測
霍夫圓檢測原理
- 從平面座標到極座標轉換三個參數C(x0, y0, r)其中x0, y0是圓心
- 假設平面座標的任意一個圓上的點,轉換到極座標中:C(x0, y0, r) 處有最大值,霍夫變換正是利用這個原理實現圓檢測
現實考量
- 因爲霍夫圓檢測對噪聲比較敏感,所以首先要對圖像做中值濾波
- 基於效率考慮,OpenCV中實現的霍夫變換圓檢測是基於圖像梯度的實現,分爲兩步:
- 檢測邊緣,發現可能的圓心
- 基於第一步的基礎上從候選圓心開始計算最佳半徑大小
操作小記
import cv2
import numpy as np
def detect_circles_demo(img):
dst = cv2.pyrMeanShiftFiltering(img, 10, 100)
cimg = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(cimg, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2)
cv2.circle(img, (i[0], i[1]), 2, (255, 0, 0), 2)
cv2.imshow("circles", img)
img = cv2.imread("money.png")
cv2.imshow("ori", img)
detect_circles_demo(img)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果
若採用dst = cv2.GaussianBlur(img, (23, 23), 0, 0)
來過濾,效果會更好