目录
1. 介绍
2. 代码实现
1. 介绍
因为,一阶导数通常会产生粗边缘。二阶导数对精细细节(细线、孤立点、噪声)有更强的响应。
所以,检测孤立点应该以二阶导数为基础,而二阶导数的差分计算为:
这个差分计算可以通过一个 系数和为零 的kernel去完成
于是,如果滤波器的响应超过一个规定的阈值,那我们就认为在kernel的中心找到了一个点。将找到的点记作1,其他的点看作0,于是就得到了一副二值图像
直观上看,这个概念是孤立点的灰度完全不同于其周围像素的灰度
孤立点是那些灰度差超过某个阈值T,被认为是孤立点的像素点
为了更好的探测8邻域的灰度差值,将滤波器的权重换成下面类型的:
1 | 1 | 1 |
1 | -8 | 1 |
1 | 1 | 1 |
2. 代码实现
代码为:
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 = 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('img.tif',0)
ret = point_detect(img)
cv2.imshow('img',np.hstack((img,ret)))
cv2.waitKey()
cv2.destroyAllWindows()
处理的结果为:
原图、检测孤立点的二值图像
原图的拉普拉斯图像为:
这里对孤立点检测函数的代码做简单讲解:
1. 首先,建立一个全零的图片canvas,后面将检测的孤立点改为255,这样canvas就是二值图像了
2. filter2D 里面的ddepth 是CV_16S ,因为做差会出现负数,不能采用无符号的形式
3. 如果为了打印拉普拉斯图像,需要将图像转换成uint8,并且灰度值在0-255之间。这里用的是convertScaleAbs函数,就是取绝对值,然后超过255的都取255。
如果采用norm的形式是这个样子的,代码为:
img_norm = cv2.normalize(laplacian,None,0,255,norm_type=cv2.NORM_MINMAX,dtype=cv2.CV_8U)
4. 这里不管用convert还是norm的形式都要取对应的阈值T,最后就是遍历图像,将大于T的找出来,赋值为255就行了。为了方便观察,以孤立点绘制出一个圆弧,注:圆心的坐标是反过来的