鹏北海,凤朝阳,又携书剑路茫茫。
图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低
高斯金字塔:用于下采样。高斯金字塔是最基本的图像塔。原理:首先将原图像作为最底层图像G0(高斯金字塔的第0层),利用高斯核(5*5)对其进行卷积,然后对卷积后的图像进行下采样(去除偶数行和列)得到上一层图像G1,将此图像作为输入,重复卷积和下采样操作得到更上一层图像,反复迭代多次,形成一个金字塔形的图像数据结构,即高斯金字塔。
拉普拉斯金字塔:用于重建图像,也就是预测残差,对图像进行最大程度的还原。比如一幅小图像重建为一幅大图,原理:用高斯金字塔的每一层图像减去其上一层图像上采样并高斯卷积之后的预测图像,得到一系列的差值图像即为 LP 分解图像。
代码如下:
import cv2 as cv
# 高斯金字塔
def pyramid_demo(image):
level = 3 # 金字塔层数
temp = image.copy()
pyramid_images = []
for i in range(level):
dst = cv.pyrDown(temp) # 降采样
pyramid_images.append(dst) # 降采样的结果添加进列表
cv.imshow('pyramid_down' + str(i), dst) # 金字塔第几层 imshow
temp = dst.copy() # 采样的图像又赋给temp 接着降采样
return pyramid_images
# 拉普拉斯金字塔
# 由高斯金字塔可以构建拉普拉斯金字塔
def lapalian_demo(image):
pyramid_images = pyramid_demo(image)
level = len(pyramid_images) # 求层数
for i in range(level - 1, -1, -1): # 每次递减
if (i - 1) < 0:
expand = cv.pyrUp(pyramid_images[i], dstsize=image.shape[:2]) # 升采样
lpls = cv.subtract(image, expand)
cv.imshow("lapalian_down" + str(i), lpls)
else:
expand = cv.pyrUp(pyramid_images[i], dstsize=pyramid_images[i - 1].shape[:2])
lpls = cv.subtract(pyramid_images[i - 1], expand)
cv.imshow("lapalian_down_" + str(i), lpls)
if __name__ == '__main__':
img = cv.imread(r'./test/004.jpg') # 图片须是2^n 格式大小 例如(512*512)的
cv.imshow("input image", img)
lapalian_demo(img)
cv.waitKey(0)
cv.destroyAllWindows()
运行效果如下:
图像梯度可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导。
cv2.Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)
import cv2
def sobel_demo(image):
grad_x = cv2.Sobel(image, cv2.CV_32F, 1, 0) # x方向一阶导数
grad_y = cv2.Sobel(image, cv2.CV_32F, 0, 1) # y方向一阶导数
# 一阶导数算出来可能有正负 最后全部转到8位的图像上去
gradx = cv2.convertScaleAbs(grad_x)
grady = cv2.convertScaleAbs(grad_y)
cv2.imshow("gradient_x", gradx)
cv2.imshow("gradient_y", grady)
# 计算两个数组的加权和
gradxy = cv2.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv2.imshow("gradient_xy", gradxy)
if __name__ == "__main__":
src = cv2.imread(r"./test/018.jpg")
src = cv2.resize(src, None, fx=0.5, fy=0.5)
cv2.imshow("image", src)
sobel_demo(src)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行效果如下:
原理跟Sobel算子类似,是Sobel算子的增强版本,当你用Sobel算子得不到很好的边缘效果时,用Scharr算子。
import cv2
def scharr_demo(image):
grad_x = cv2.Scharr(image, cv2.CV_32F, 1, 0) # x方向一阶导数
grad_y = cv2.Scharr(image, cv2.CV_32F, 0, 1) # y方向一阶导数
# 一阶导数算出来可能有正负 最后全部转到8位的图像上去
gradx = cv2.convertScaleAbs(grad_x)
grady = cv2.convertScaleAbs(grad_y)
cv2.imshow("gradient_x", gradx)
cv2.imshow("gradient_y", grady)
# 计算两个数组的加权和
gradxy = cv2.addWeighted(gradx, 0.5, grady, 0.5, 0)
cv2.imshow("gradient_xy", gradxy)
if __name__ == "__main__":
src = cv2.imread(r"./test/018.jpg")
src = cv2.resize(src, None, fx=0.5, fy=0.5)
cv2.imshow("image", src)
scharr_demo(src)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行效果如下:
拉普拉斯算子(Laplace Operator)是n维欧几里德空间中的一个二阶微分算子,定义为梯度(▽f)的散度(▽·f)。
cv2.Laplacian(src, ddepth, dst=None, ksize=None, scale=None, delta=None, borderType=None)
import cv2 as cv
import numpy as np
# 拉普拉斯算子
def Laplace_demo(image):
dst = cv.Laplacian(image, cv.CV_32F)
lpls_1 = cv.convertScaleAbs(dst)
cv.imshow("Laplace_1", lpls_1)
# 自定义拉普拉斯算子
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]])
dst = cv.filter2D(image, cv.CV_32F, kernel)
lpls_2 = cv.convertScaleAbs(dst)
cv.imshow("Laplace_2", lpls_2)
if __name__ == "__main__":
src = cv.imread(r"./test/018.jpg")
src = cv.resize(src, None, fx=0.5, fy=0.5)
cv.imshow("image", src)
Laplace_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行效果如下:
ksize参数用的默认值,此时Laplacian()函数采用以下3x3的孔径:
Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
Canny边缘检测算法的步骤
cv2.Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None):
import cv2 as cv
def edge_demo(image):
# 高斯模糊 降低噪声
blurred = cv.GaussianBlur(image, (3, 3), 0)
# 转为灰度图像
gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
# 计算x y 方向梯度
grad_x = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
grad_y = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
edge_output = cv.Canny(grad_x, grad_y, 50, 100)
cv.imshow('edge image_1', edge_output)
# 彩色图像
dist = cv.bitwise_and(image, image, mask=edge_output)
cv.imshow('edge image_2', dist)
if __name__ == "__main__":
src = cv.imread(r"./test/033.png")
src = cv.resize(src, None, fx=0.5, fy=0.5)
cv.imshow("image", src)
edge_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行效果如下: