图像梯度
概念: 把图片想象成连续函数,因为边缘部分的像素值是与旁边像素明显有区别的,所以对图片局部求极值,就可以得到整幅图片的边缘信息了。不过图片是二维的离散函数,导数就变成了差分,这个差分就称为图像的梯度。
垂直边缘提取:
滤波是应用卷积来实现的,卷积的关键就是卷积核,我们来考察下面这个卷积核:
当前列左右两侧的元素进行差分,由于边缘的值明显小于(或大于)周边像素,所以边缘的差分结果会明显不同,这样就提取出了垂直边缘。同理,把上面那个矩阵转置一下,就是提取水平边缘。这种差分操作就称为图像的梯度计算:
img = cv2.imread("sudoku.jpg", 0)
# 提取垂直边缘
kernel = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]], dtype=np.float32)
# 卷积操作,-1表示通道数与原图相同
dst_v = cv2.filter2D(img, -1, kernel)
# 提取水平边缘
dst_h = cv2.filter2D(img, -1, kernel.T)
cv2.imshow('img', np.hstack((img, dst_v, dst_h)))
cv2.waitKey(0)
Sobel算子
sobel 算子市高斯平滑和微分操作的结合体,因此它的抗噪声能力比较好。
它先在垂直方向计算梯度 $G_x=k_1×src$
,再在水平方向计算梯度 $G_y=k_2×src$
,最后求出总梯度: $G=sqrt{Gx^2+Gy^2}$
因此上面的代码可以改为:
# 参数:img, -1:深度与原图一致, 1,0:只在x方向计算, 0,1:只在y方向计算
sobelx = cv2.Sobel(img, -1, 1, 0, ksize=3) #只计算x方向
sobley = cv2.Sobel(img, -1, 0, 1, ksize=3) #只计算y方向
Prewitt算子:
还有比Sobel更好用的Scharr算子:
# 3.使用Scharr算子
sobelx = cv2.Scharr(img, -1, 1, 0) # 只计算x方向
sobely = cv2.Scharr(img, -1, 0, 1) # 只计算y方向
Laplacian算子
Laplacian算子是二阶边缘检测的典型代表 数学可参考:http://ex2tron.wang/opencv-python-extra-image-gradients/
laplacian = cv2.Laplacian(img, -1) # 使用Laplacian算子
Canny边缘检测
Canny边缘提取的具体步骤如下:
- 使用5×5高斯滤波消除噪声:
- 边缘检测本身属于锐化操作,对噪点比较敏感,所以需要进行平滑处理
2. 计算图像梯度的方向:
-
- 首先使用Sobel算子计算两个方向上的梯度Gx和Gy,然后算出梯度的方向:
$θ=arctan(frac{G_y}{G_x})$
, 保留这四个方向的梯度:0°/45°/90°/135°.
- 首先使用Sobel算子计算两个方向上的梯度Gx和Gy,然后算出梯度的方向:
3. 取局部极大值:
-
- 梯度其实已经表示了轮廓,但为了进一步筛选,可以在上面的四个角度方向上再取局部极大值:
4. 滞后阈值: 经过前面三步,就只剩下0和可能的边缘梯度值了,为了最终确定下来,需要设定高低阈值:
-
- 像素点的值大于最高阈值,那肯定是边缘(上图A)
- 同理像素值小于最低阈值,那肯定不是边缘
- 像素值介于两者之间,如果与高于最高阈值的点连接,也算边缘,所以上图中C算,B不算.
- Canny推荐的高低阈值比在2:1到3:1之间。
❣️阈值分割后再检测边缘,效果会更好
ret, th = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 30,70 分别为低、高阈值
edge = cv2.Canny(th, 30, 70)
图像金字塔
我们将一层层的图像比喻为金字塔,层级越高,则图像尺寸越小,分辨率越低。
☝️ 两种类型的金字塔: - 高斯金字塔:用于下采样,主要的图像金字塔。 - 拉普拉斯金字塔:用于重建图像,也就是预测残差(拉普拉斯金字塔是通过源图像减去先缩小后再放大的图像的一系列图像构成的),对图像进行最大程度的还原,比如一幅小图像重建为一幅大图像。
☝ ️图像金字塔有两个高频出现的名词:上采样和下采样。现在说说他们俩。 - 上采样:就是图片放大(所谓上嘛,就是变大),使用PryUp函数。 - 下采样:就是图片缩小(所谓下嘛,就是变小),使用PryDown函数。
☝️ 下采样将步骤:
- 对图像进行高斯内核卷积。
- 将所有偶数行和列去除。 下采样就是图像压缩,会丢失图像信息。
☝️ 上采样步骤:
- 将图像在每个方向放大为原来的两倍,新增的行和列用0填充。
- 使用先前同样的内核(乘以4)与放大后的图像卷积,获得新增像素的近似值。 上、下采样都存在一个严重的问题,那就是图像变模糊了,因为缩放的过程中发生了信息丢失的问题。要解决这个问题,就得看拉普拉斯金字塔了。
# 尺寸变小 分辨率降低 。
lower_reso = cv2.pyrDown(higher_reso)
# 从一个低分辨率小尺寸的图像向下构建一个金子塔,尺寸变大但分辩率不会增加
higher_reso2 = cv2.pyrUp(lower_reso)
金字塔混合
图像金字塔的一个应用就是图像融合。例如,在图像缝合中,如果你需要将两幅图叠在一起,但是由于连接区域图像像素不连续性,政府图像效果看起来很差,这时就可以通过图像金字塔进行融合。
步骤如下:
1、读入两幅图像
2、构建img1和img2的高斯金字塔
3、根据高斯金字塔计算拉普拉斯金字塔
4、在拉普拉斯的每一层进行图像融合
5、根据融合后的图像金字塔重建原始图像
import cv2 as cv
import numpy as np,sys
A = cv.imread('sheep.jpg')
B = cv.imread('final3.jpg')
A = cv.resize(A,(256,256),cv.INTER_LINEAR)
B = cv.resize(B,(256,256),cv.INTER_LINEAR)
print(A.shape,B.shape)
# generate Gaussian pyramid for A
G = A.copy()
gpA = [G]
for i in range(6):
G = cv.pyrDown(G)
# print(G.shape)
gpA.append(G)
# generate Gaussian pyramid for B
G = B.copy()
gpB = [G] #将橘子进行高斯金字塔处理,总共六级处理
for i in range(6):
G = cv.pyrDown(G)
gpB.append(G)
# generate Laplacian Pyramid for A
lpA = [gpA[5]] #将苹果进行拉普拉斯金字塔处理,总共5级处理
for i in range(5,0,-1):
GE = cv.pyrUp(gpA[i])
# print(GE.shape)
# print(gpA[i].shape)
L = cv.subtract(gpA[i-1],GE)
lpA.append(L)
# generate Laplacian Pyramid for B
lpB = [gpB[5]] #将橘子进行拉普拉斯金字塔处理,总共5级处理
for i in range(5,0,-1):
GE = cv.pyrUp(gpB[i])
L = cv.subtract(gpB[i-1],GE)
lpB.append(L)
# Now add left and right halves of images in each level
LS = []
for la,lb in zip(lpA,lpB):
rows,cols,dpt = la.shape
#print(la.shape)
#print(la[:,0:cols//2,:])
ls = np.hstack((la[:,0:cols//2,:], lb[:,cols//2:,:]))
LS.append(ls)
# now reconstruct
ls_ = LS[0]
for i in range(1,6):
ls_ = cv.pyrUp(ls_)
ls_ = cv.add(ls_, LS[i])
# image with direct connecting each half
real = np.hstack((A[:,:cols//2,:],B[:,cols//2:,:]))
cv.imwrite('Pyramid_blending2.jpg',ls_)
cv.imwrite('Direct_blending.jpg',real)
------------------------------------------可爱の分割线------------------------------------------
更多Opencv教程可以 Follow github的opencv教程,中文&English 欢迎Star❤️❤️❤️
https://github.com/JimmyHHua/opencv_tutorialsgithub.com参考
❗ ❗ ❗ Thanks: ❗ ❗ ❗
- ex2tron博客
- OpenCv官网