边缘检测,就是用数学方法找出一幅图像中像素值突变的点。
第一件事,就是找出灰度图像中的梯度,在x方向和y方向中找出可能有边缘的区域。
接着我们会使用canny边缘检测,包括图像降噪,找到图像梯度(利用在水平和竖直方向上的sobel核),非最大值抑制non-maximum suppresion,和滞后阈值hysteresis thresholding。
# 注意从黑向白映射是一个正的斜坡函数,但从白向黑映射是一个负的斜坡函数
# 所以需要用cv2.CV_64F这种格式
lap = cv2.Laplacian(image, cv2.CV_64F)
lap = cp.uint8(np.absolute(lap))
cv2.imshow("Laplacian", lap)
# 水平梯度
sobelX = cv2.Sobel(image, cv2.CV_64F, 1, 0)
sobelX = np.uint8(np.absolute(sobelX))
cv2.imshow("Sobel X", sobelX)
# 垂直梯度
sobelY = cv2.Sobel(image, cv2.CV_64F, 0, 1)
sobelY = np.uint8(np.absolute(sobelY))
cv2.imshow("Sobel Y", sobelY)
# 结合x和y两个方向的梯度
sobelCombined = cv2.bitwise_or(sobelX,sobelY)
cv2.imshow("Sobel Combined", sobelCombined)
canny边缘探测是一个多步骤的过程,涉及到滤波降噪,计算sobel在两个方向上的梯度,边缘抑制suppressing edeges,和滞后阈值hysteresis thresholding,最终决定一个像素是否像是个边缘。
# 所有的梯度和边缘探测算子都需要转换成灰度图
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 仅canny需要使用模糊
image = cv2.GaussianBlur(image, (5,5), 0)
# 使用canny算子
# 第一个阈值表示低于30的梯度将不会被判定成边缘
# 第二个阈值表示超过150的梯度才会被判定成边缘
canny = cv2.Canny(image, 30, 150)
轮廓是一条曲线上的点,并且这条曲线没有间断。
怎样找到图像的轮廓呢,首先要获得二值化图像,无论是使用边缘检测还是二值化。接下来是一个使用canny边缘检测算子来找出硬币的轮廓,然后找到实际硬币的轮廓的例子。
# 基础处理,二值化,模糊,探测边缘
gray = cv2.cvtColor()
blurred = cv2.GaussianBlur()
edged = cv2.Canny(blurred, 30, 150)
cv2.imshow("Edges", edged)
# 第二个参数表示返回外围最大轮廓,如果使用cv2.RETR_LIST可以返回所有轮廓
# 第三个参数表示分层架构,还可以使用cv2.RETR_COMP和cv2.RETR_TREE
(_, cnts, _) = cv2.findCoutours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print("{} coins in this image".format(len(cnts)))
coins = image.copy()
# 参数分别表示画布,轮廓,轮廓序号(-1代表全部),颜色,线条宽度
cv2.drawCountours(coins, cnts, -1, (0,255,0), 2)
cv2.imshow("coins", coins)
for (i,c) in enumerate(cnts):
# 裁切出一块长方形区域,用来放圆形
(x,y,w,h) = cv2.boundingRect(c)
print("coin #{}".format(i+1))
coin = image[y:y+h, x:x+w]
cv2.imshow("Coin", coin)
# 新建一块大的画布
mask = np.zeros(image.shape[:2], dtype = "uint8")
# 找出轮廓圆的圆心和半径
# 输入为轮廓,输出为圆心和半径
((centerX,centerY),radius) = cv2.minEnclosingCircle(c)
# 画出圆,这些圆本质上是一个白色蒙版,mask成为一个遮罩
cv2.circle(mask,(int(centerX), int(centerY)), int(radius), 255, -1)
# 把mask缩小到和coin一样大的长方形区域
mask = mask[y:y+h, x:x+w]
cv2.imshow("Masked Coin", cv2.bitwise_and(coin,coin,mask = mask))