一阶导数通常会产生粗边缘。二阶导数对精细细节(细线、孤立点、噪声)有更强的响应。
所以,检测孤立点应该以二阶导数为基础,而二阶导数的差分计算为
这个差分计算可以通过一个 系数和为零 的kernel去完成
如果滤波器的响应超过一个规定的阈值,那我们就认为在kernel的中心找到了一个点。将找到的点记作1,其他的点看作0,于是就得到了一副二值图像
import numpy as np
import cv2
def point_detect(img):
canvas = np.zeros(img.shape, dtype=np.uint8) # 拷贝图像
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]]) # 定义卷积核
laplacian = cv2.filter2D(img, cv2.CV_16S, kernel) # 拉普拉斯变换
img_lap = cv2.convertScaleAbs(laplacian) # 拉普拉斯图像
# cv2.imshow('laplacian', img_lap)
"""
孤立点是那些灰度差超过某个阈值T,被认为是孤立点的像素点
"""
T = 0.9 * np.abs(laplacian).max() # 阈值 T
for i in range(img.shape[0]): # 遍历图像,寻找孤立点
for j in range(img.shape[1]):
if (laplacian[i, j] > T) or (laplacian[i, j] < -T): # 绝对值大于阈值的点
canvas[i, j] = 255 # 二值处理
cv2.circle(canvas, (j, i), 10, 255) # 绘制圆
return canvas
img = cv2.imread('img1.png', 0)
for i in range(20): # 添加2000个胡椒噪声
x = np.random.randint(0,img.shape[0])
y = np.random.randint(0,img.shape[1])
img[x][y] = 0 # 0 为pepper 噪声 255 为salt 噪声
ret = point_detect(img)
cv2.imshow('img', np.hstack((img, ret)))
cv2.imwrite('./image.png',np.hstack((img,ret)))
cv2.waitKey()
cv2.destroyAllWindows()
复杂度更高的是线检测
由于二阶导数有更强的滤波器响应,并且比一阶导数有更细的线。但是二阶导数对噪声点会格外敏感,并且会产生双边缘响应
import numpy as np
import cv2
img = cv2.imread('./img1.png', 0)
kernel1 = np.array([[-1, -1, -1], [2, 2, 2], [-1, -1, -1]]) # 水平
kernel2 = np.array([[2, -1, -1], [-1, 2, -1], [-1, -1, 2]]) # +45
kernel3 = np.array([[-1, 2, -1], [-1, 2, -1], [-1, 2, -1]]) # 垂直
kernel4 = np.array([[-1, -1, 2], [-1, 2, -1], [2, -1, -1]]) # -45
dst1 = cv2.filter2D(img, cv2.CV_16S, kernel1) # 水平检测
dst1 = cv2.convertScaleAbs(dst1)
dst2 = cv2.filter2D(img, cv2.CV_16S, kernel2) # 45 检测
dst2 = cv2.normalize(dst2, None, 0, 255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
dst = dst2.copy() # 拷贝归一化后的图像
dst[dst2 <= 254] = 0
dst3 = cv2.filter2D(img, cv2.CV_16S, kernel3) # 垂直检测
dst3 = cv2.convertScaleAbs(dst3)
dst4 = cv2.filter2D(img, cv2.CV_16S, kernel4) # -45 检测
dst4 = cv2.convertScaleAbs(dst4)
cv2.imshow('img', np.hstack((img, dst2, dst)))
cv2.waitKey()
cv2.destroyAllWindows()
import numpy as np
import cv2
img = cv2.imread('./img1.png', 0)
"""
Canny 是基于一阶微分的
首先用高斯滤波平滑图像。
计算梯度幅值图像和方向角度图像
对梯度图像应用非极大值抑制(根据第二步求出的角度图像,用该像素点 比较 在梯度方向和其反方向的像素点。如果该像素点的梯度幅值最大则保留;否则,删去。)
使用双阈值处理和连通性分析来检测连接边缘 (根据设置的两个阈值( 为低阈值 为高阈值),就会将幅值图像划为三部分,进行如下的分割
像素点的响应强度是 > TH 的,那么认为是真正的边缘
像素点的响应强度是 < TL的,那么认为是伪边缘,删去
如果介于两者之间,则检查周围的8领域,如果存在真正的边缘就保留,为了连接边缘。否则就删去)
"""
dst = cv2.Canny(img, 100, 200) # Canny 边缘检测
cv2.imshow("img", np.hstack((img, dst)))
cv2.imwrite('./image.png',np.hstack((img,dst)))
cv2.waitKey()
cv2.destroyAllWindows()
import numpy as np
import cv2
img = cv2.imread('./img2.png', 0) # 读取图像
img = cv2.GaussianBlur(img, (7, 7), sigmaX=1) # 高斯模糊图像
img_bin = cv2.Canny(img, 50, 100) # 得到二值图像
lines = cv2.HoughLinesP(img_bin, 1, np.pi / 180, threshold=10) # Hough 变换
for line in lines:
x1, y1, x2, y2 = line[0] # 提取线段
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 1) # 在原图上绘制线段
cv2.imshow('Canny', img_bin) # 显示 Canny 检测的图像
cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()
Hough 变换除了能用来检测直线外,也能用来检测圆环
import cv2
import numpy as np
img = cv2.imread('./img2.png', 1)
img_blur = cv2.GaussianBlur(img, (7, 7), sigmaX=1) # 高斯模糊
img_gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY) # 灰度化
circles = cv2.HoughCircles(img_gray, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=50, minRadius=50,
maxRadius=100) # Hough圆环检测
circles = np.uint(np.around(circles)) # 将数组元素四舍五入成整数
for i in circles[0]: # 遍历圆环
x, y, r = i # 取出圆横坐标、纵坐标、半径
cv2.circle(img, (x, y), r, (0, 0, 255), 2) # 绘制圆
cv2.circle(img, (x, y), 1, (0, 0, 255), 3) # 绘制圆心
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()