Python OpenCV 之图像金字塔,高斯金字塔与拉普拉斯金字塔


Python OpenCV 365 天学习计划,与橡皮擦一块儿进入图像领域吧。算法


Python OpenCV

    • 高斯金字塔
    • cv2.pyrDown 与 cv2.pyrUp 函数原型
    • 基础知识铺垫
    • 拉普拉斯金字塔(Laplacian Pyramid, LP)
    • 橡皮擦的小节


基础知识铺垫

学习图像金字塔,发现网上的资料比较多,检索起来比较轻松。vim

图像金字塔是一张图像多尺度的表达,或者能够理解成一张图像不一样分辨率展现。ide

金字塔越底层的图片,像素越高,越向上,像素逐步下降,分辨率逐步下降。函数

高斯金字塔

咱们依旧不对概念作过多解释,第一遍学习应用,应用,毕竟 365 天的周期,时间长,后面补充理论知识。学习

高斯金字塔用于向下采样,同时它也是最基本的图像塔。测试

在互联网检索原理,获得最简单的说明以下:翻译

将图像的最底层(高斯金字塔的第 0 层),例如高斯核(5x5)对其进行卷积操做,这里的卷积主要处理掉的是偶数行与列,而后获得金字塔上一层图像(即高斯金字塔第 1 层),在针对该图像重复卷积操做,获得第 2 层,反复执行下去,便可获得高斯金字塔。3d

每次操做以后,都会将 M×N 图像变成 M/2 × N/2 图像,即减小一半。blog

还有实测中发现,须要用图像的宽和高一致的图片,而且宽高要是 2 的次幂数,例如,8 像素,16 像素,32 像素等等,一会你也能够实际测试一下。图片

图像金字塔应用到的函数有和。

cv2.pyrDown 与 cv2.pyrUp 函数原型

经过 help 函数获得函数原型以下:

pyrDown(src[, dst[, dstsize[, borderType]]]) -> dst
pyrUp(src[, dst[, dstsize[, borderType]]]) -> dst

两个函数原型参数一致,参数说明以下:

  • :输入图像;
  • : 输出图像;
  • : 输出图像尺寸,默认值按照 ((src.cols+1)/2, (src.rows+1)/2) 计算。

关于两个函数的补充说明:

  • 从一个相对高分辨率的大尺寸的图像上构建一个金字塔,运行以后的结果是,图像变小,分辨率下降(下采样);
  • 是一个上采样的过程,尽管相对尺寸变大,可是分辨率不会增长,图像会变得更模糊。

测试代码以下:

import cv2 as cv

src = cv.imread("./testimg.jpeg")print(src.shape[:2])cv.imshow("src", src)# 向下采样dst = cv.pyrDown(src)print(dst.shape[:2])cv.imshow("dst", dst)# 再次向下采样dst1 = cv.pyrDown(dst)print(dst1.shape[:2])cv.imshow("dst1", dst1)cv.waitKey()

运行代码以后,获得三张图片,大小依次减少,分辨率下降。

Python OpenCV 之图像金字塔,高斯金字塔与拉普拉斯金字塔
经过上面运行获得的最小图,在执行向上采样以后,图片会变的模糊,这也说明上采样和下采样是非线性处理,它们是不可逆的有损处理,所以下采样后的图像是没法还原的,即便放大图片也会变模糊(后面学习到拉普拉斯金字塔能够解决该问题)。

# 向上采样dst2 = cv.pyrUp(dst1)print(dst2.shape[:2])cv.imshow("dst2", dst2)

Python OpenCV 之图像金字塔,高斯金字塔与拉普拉斯金字塔
在总结一下上采样和下采样的步骤:

  1. 上采样:使用函数, 先将图像在每一个方向放大为原来的两倍,新增的行和列用 0 填充,再使用先前一样的内核与放大后的图像卷积,得到新增像素的近似值;
  2. 下采样:使用函数,先对图像进行高斯内核卷积 ,再将全部偶数行和列去除。

拉普拉斯金字塔(Laplacian Pyramid, LP)

拉普拉斯金字塔主要用于重建图像,由上文咱们已经知道在使用高斯金字塔的的时候,上采样和下采样会致使图像细节丢失。

拉普拉斯就是为了在放大图像的时候,能够预测残差,何为残差,即小图像放大的时候,须要插入一些像素值,在上文直接插入的是 0,拉普拉斯金字塔算法能够根据周围像素进行预测,从而实现对图像最大程度的还原。

学习到原理以下:用高斯金字塔的每一层图像,减去其上一层图像上采样并高斯卷积以后的预测图像,获得一系列的差值图像即为 LP 分解图像(其中 LP 即为拉普拉斯金字塔图像)。

关于拉普拉斯还存在一个公式(这是本系列课程第一次书写公式),其中 L 为拉普拉斯金字塔图像,G 为高斯金字塔图像

L n = G n − P y r U p ( P y r D o w n ( G n ) ) L_n = G_n-PyrUp(PyrDown(G_n)) Ln=Gn−PyrUp(PyrDown(Gn))

使用下面的代码进行测试。

import cv2 as cv

src = cv.imread("./testimg.jpeg")print(src.shape[:2])cv.imshow("src", src)# 向下采样一次dst = cv.pyrDown(src)print(dst.shape[:2])cv.imshow("dst", dst)# 向上采样一次dst1 = cv.pyrUp(dst)print(dst1.shape[:2])cv.imshow("dst1", dst1)# 计算拉普拉斯金字塔图像# 原图 - 向上采样一次的图laplace = cv.subtract(src, dst1)cv.imshow("laplace", laplace)cv.waitKey()

运行结果以下,相关的图像已经呈现出来,重点注意最右侧的图片。

Python OpenCV 之图像金字塔,高斯金字塔与拉普拉斯金字塔
这个地方须要注意下,若是你使用函数,获得的是上图效果,可是在使用还原的时候会发现问题,建议直接使用完成,匹配公式,修改代码以下:

# cv.subtract(src, dst1)laplace = src - dst1

代码运行效果以下。
Python OpenCV 之图像金字塔,高斯金字塔与拉普拉斯金字塔

学习过程当中发现这样一段话:图像尺寸最好是 2 的整次幂,如 256,512 等,不然在金字塔向上的过程当中图像的尺寸会不等,这会致使在拉普拉斯金字塔处理时因为不一样尺寸矩阵相减而出错。

这个我在实测的时候发现确实如此,例如案例中使用的图像,在向下采样 2 次的时候,图像的尺寸就会发生变化,测试代码以下:

import cv2 as cv

src = cv.imread("./testimg.jpeg")print(src.shape[:2])cv.imshow("src", src)# 向下采样1次dst1 = cv.pyrDown(src)print(dst1.shape[:2])cv.imshow("dst", dst1)# 向下采样2次dst2 = cv.pyrDown(dst1)print(dst1.shape[:2])cv.imshow("dst2", dst2)# 向上采样1次up_dst1 = cv.pyrUp(dst2)print(up_dst1.shape[:2])cv.imshow("up_dst1", up_dst1)# 计算拉普拉斯金字塔图像# 采样1次 - 向上采样1次的图laplace = dst1 - up_dst1
cv.imshow("laplace", laplace)cv.waitKey()

注意 print(up_dst1.shape[:2]) 部分的输出以下:

(710, 400)(355, 200)(355, 200)(356, 200)

若是在该基础上使用拉普拉斯图像金字塔,就会出现以下错误

Sizes of input arguments do not match

在总结一下拉普拉斯图像金字塔的执行过程:

  • 向下采样:用高斯金字塔的第 i 层减去 i+1 层作上采样的图像,获得拉普拉斯第 i 层的图像;
  • 向上采样:用高斯金字塔的 i+1 层向上采样加上拉普拉斯的第 i 层,获得第 i 层的原始图像。

向下采样上面的代码已经实现了,可是拉普拉斯向上采样还未实现,完善一下代码以下,为了代码清晰,咱们将变量命名进行修改。

import cv2 as cv

src = cv.imread("./testimg_rect.jpeg")print(src.shape[:2])cv.imshow("src", src)# 高斯金字塔第 0 层gus0 = src  # 原图# 高斯金字塔第 1 层gus1 = cv.pyrDown(gus0)# 高斯第 2 层gus2 = cv.pyrDown(gus1)# 拉普拉斯金字塔第 0 层lap0 = gus0 - cv.pyrUp(gus1)# 拉普拉斯金字塔第 1 层lap1 = gus1 - cv.pyrUp(gus2)# 显示拉普拉斯第一层代码cv.imshow("laplace", lap1)cv.waitKey()

下面用修改好的代码完成还原图片的操做。

import cv2 as cv

src = cv.imread("./testimg_rect.jpeg")print(src.shape[:2])cv.imshow("src", src)# 高斯金字塔第 0 层gus0 = src  # 原图# 高斯金字塔第 1 层gus1 = cv.pyrDown(gus0)# 高斯第 2 层gus2 = cv.pyrDown(gus1)# 拉普拉斯金字塔第 0 层lap0 = gus0 - cv.pyrUp(gus1)# 拉普拉斯金字塔第 1 层lap1 = gus1 - cv.pyrUp(gus2)rep = lap0 + cv.pyrUp(lap1 + cv.pyrUp(gus2))gus_rep = cv.pyrUp(cv.pyrUp(gus2))cv.imshow("rep", rep)cv.imshow("gus_rep", gus_rep)cv.waitKey()

以上代码最重要的部分为下面两句:

rep = lap0 + cv.pyrUp(lap1 + cv.pyrUp(gus2))gus_rep = cv.pyrUp(cv.pyrUp(gus2))

第一行代码中 lap1 + cv.pyrUp(gus2) 即文字公式 【用高斯金字塔的 i+1 层向上采样加上拉普拉斯的第 i 层,获得第 i 层的原始图像】的翻译。

第二行代码是使用直接向上采样,最终获得的是损失细节的图像。

上述代码运行的结果以下,经过拉普拉斯能够完美还原图像。

学习本案例以后,你能够在复盘本文开始部分的代码,将其进行修改。
Python OpenCV 之图像金字塔,高斯金字塔与拉普拉斯金字塔

最后在学习一种技巧,能够直接将两幅图片呈现,代码以下:

import cv2 as cvimport numpy as np
src = cv.imread("./testimg_rect.jpeg")print(src.shape[:2])cv.imshow("src", src)# 向下采样1次down_dst1 = cv.pyrDown(src)print(down_dst1.shape[:2])cv.imshow("dst", down_dst1)# 向上采样1次up_dst1 = cv.pyrUp(down_dst1)print(up_dst1.shape[:2])cv.imshow("up_dst1", up_dst1)res = np.hstack((up_dst1, src))cv.imshow('res', res)cv.waitKey()

运行以后,经过函数,将两个图像矩阵合并,实现效果以下:
Python OpenCV 之图像金字塔,高斯金字塔与拉普拉斯金字塔

橡皮擦的小节

但愿今天的 1 个小时,你有所收获,咱们下篇博客见~

相关文章
相关标签/搜索