一、介绍
图像拼接主要可以分为两个步骤:图像配准和图像融合。
- 其中图像配准的目的是将图一场景中不同视角的图像投影到同一平面并进行对准。比如我之前这篇博客 图像处理(一)基于特征的图像对齐算法 中介绍的 图像对齐算法,也就是图像配准。
- 本文则是接下去介绍 基于图像金字塔的 图像融合算法。
1.1 图像融合算法有哪些?
主流的图像融合算法有:
1)加权平均法。即 使用简单的使用加权的方式进行图片融合。这种方法效果一般,但算法实现极其简单,速度快。
2)图像金字塔融合。即 将图像建立一个拉普拉斯金字塔,其中金字塔的每一层都包含了图像不同的频段,分开不同频段进行融合效果出奇的好。因此,有些地方也叫作 多分辨率融合算法。这也是本文主要介绍的方法。
1.2 图像金字塔是什么?
图像处理常用的金字塔有两类,高斯金字塔和拉普拉斯金字塔。
一般情况下,我们要处理是一副具有固定分辨率的图像。但是有些情况下,我们需对同一图像的不同分辨率的子图像进行处理。比如,我们要在一幅图像中查找某个目标,比如脸,我们不知道目标在图像中的尺寸大小。
这种情况下,我们需要创建创建一组图像,这些图像是具有不同分辨率的原始图像。我们把这组图像叫做图像金字塔(简单来说就是同一图像的不同分辨率的子图集合)。
如果我们把最大的图像放在底部,最小的放在顶,看起来像一座金字塔 故而得名图像金字塔。
1.2 高斯金字塔是什么?
Opencv中使用pyrDown()函数就可以获得高斯金字塔。
高斯金字塔:高斯金字塔是最基本的图像塔。首先将原图像作为最底层图像G0(高斯金字塔的第0层),利用高斯核(5*5)对其进行卷积,然后对卷积后的图像进行下采样(去除偶数行和列)得到上一层图像G1,将此图像作为输入,重复卷积和下采样操作得到更上一层图像,反复迭代多次,形成一个金字塔形的图像数据结构,即高斯金字塔。
为了获取层级为 G_i+1 的金字塔图像,我们采用如下方法:
- 对图像G_i进行高斯内核卷积
- 将所有偶数行和列去除
得到的图像即为G_i+1的图像,显而易见,结果图像只有原图的四分之一。通过对输入图像G_i(原始图像)不停迭代以上步骤就会得到整个金字塔。同时我们也可以看到,向下取样会逐渐丢失图像的信息。
以上就是对图像的向下取样操作,即缩小图像。
高斯金字塔的顶部是将底部图像中的连续的行和列去除得到的。顶部图像中的每个像素值等于下一层图像中 5 个像素的高斯加权平均值。这样操作一次一个 MxN 的图像就变成了一个 M/2xN/2 的图像。
所以这幅图像的面积就变为原来图像面积的四分之一。这被称为 Octave。
连续进行这样的操作我们就会得到一个分辨率不断下降的图像金字塔。我们可以使用函数cv2.pyrDown() 和 cv2.pyrUp() 构建图像金字塔。
- 函数 cv2.pyrDown() 从一个高分辨率大尺寸的图像向上构建一个金子塔(尺寸变小,分辨率降低)
- 函数 cv2.pyrUp() 从一个低分辨小尺寸的图像向下构建一个金子塔(尺寸变大,但分辨率不会增加) 。
1.3 拉普拉斯金字塔
Opencv中使用pyrUp()函数 + 高斯金字塔,可以获得拉普拉斯金字塔。
拉普拉斯金字塔的构造需要用到高斯金字塔。拉普拉斯金字塔第i层的数学定义如下
每一层的定义,意思是拉普拉斯金字塔每一层的图像为同一层高斯金字塔的图像减去上一层的图像进行上采样并高斯模糊的结果。说的有点绕,可以看网上的这幅图进行理解。
1.4 图像金字塔的应用场景是什么?
- 图像融合:图像金字塔的一个重要应用场景。
例如,在图像缝合中,你需要将两幅图叠在一起,但是由于连接区域图像像素的不连续性,整幅图的效果看起来会很差。这时图像金字塔就可以排上用场了 他可以帮你实现无缝连接。 - 图像压缩:经常使用拉普拉斯金字塔。
- 图像拼接:图像对齐+图像融合。
二、经典案例
2.1 任务
使用 上文提到的 基于图像金字塔的图像融合算法 实现 两张 不同的水果图片融合成一个。
图片素材如下,右键可以直接下载。
2.2 步骤分解
1、读入两幅图像,苹果和橙子
2、构建苹果和橙子的高斯金字塔(6层)
3、根据高斯金字塔计算拉普拉斯金字塔
4、在拉普拉斯的每一层进行图像融合(苹果的左边和橙子的右边融合)
5、根据融合后的图像金字塔重建原始图像。
图像最后使用plt显示图片,遇到彩色图变蓝色的问题,分析发现因为cv2.imread()读图通道的是b/g/r,而plt.show()的通道是r/g/b,导致图片出现色彩发蓝。展示的代码也给了解决方案。
2.3 代码展示
使用的开发环境 如下
- conda 4.7.12
- Python 3.6.12
# -*- coding: utf-8 -*-
"""
Created on Sun Aug 29 15:54:08 2021
@author: sagewang
"""
import cv2
import numpy as np,sys
from matplotlib import pyplot as plt
A = cv2.imread('apple.jpg')
B = cv2.imread('orange.jpg')
dim = (512, 512)
A = cv2.resize(A, dim, interpolation = cv2.INTER_AREA)
B = cv2.resize(B, dim, interpolation = cv2.INTER_AREA)
# generate Gaussian pyramid for A
G = A.copy()
gpA = [G]
for i in np.arange(6):
G = cv2.pyrDown(G)
gpA.append(G)
# generate Gaussian pyramid for B
G = B.copy()
gpB = [G]
for i in np.arange(6):
G = cv2.pyrDown(G)
gpB.append(G)
# generate Laplacian Pyramid for A
lpA = [gpA[5]]
for i in np.arange(5,0,-1):
GE = cv2.pyrUp(gpA[i])
L = cv2.subtract(gpA[i-1],GE)
lpA.append(L)
# generate Laplacian Pyramid for B
lpB = [gpB[5]]
for i in np.arange(5,0,-1):
GE = cv2.pyrUp(gpB[i])
L = cv2.subtract(gpB[i-1],GE)
lpB.append(L)
# Now add left and right halves of images in each level
#numpy.hstack(tup)
#Take a sequence of arrays and stack them horizontally
#to make a single array.
LS = []
for la,lb in zip(lpA,lpB):
rows,cols,dpt = la.shape
# print(la.shape)
ls = np.hstack((la[:,0:cols//2], lb[:,cols//2:]))
LS.append(ls)
# now reconstruct
ls_ = LS[0]
for i in np.arange(1,6):
ls_ = cv2.pyrUp(ls_)
ls_ = cv2.add(ls_, LS[i])
rows,cols,dpt = A.shape
# 512, 512, 3
# print(A.shape)
# image with direct connecting each half
# real = np.hstack((A[:,:cols/2],B[:,cols/2:]))
real = np.hstack((A[:,:256],B[:,256:]))
cv2.imwrite('Pyramid_blending.jpg',ls_)
cv2.imwrite('Direct_blending.jpg',real)
# 解决彩色图像出现色差问题
# 1 彩色图像出现色差原因
# 使用cv2.imread()读取图像时,默认彩色图像的三通道顺序为B、G、R,这与我们所熟知的RGB中的R通道和B通道正好互换位置了。
# 而使用plt.imshow()函数却默认显示图像的通道顺序为R、G、B,导致图像出现色差发蓝。
# 2 灰度图像出现色差原因
# 那么为什么plt.imshow()显示灰度图(只有一个通道)还会出现色差呢?
# 上一段讲过,这是因为plt.imshow()函数默认显示三通道图像,把灰度图当作彩色图显示出来了,所以出现了发蓝的现象。
def bgr2rgb(img):
b,g,r = cv2.split(img) #分别提取B、G、R通道
img_new = cv2.merge([r,g,b]) #重新组合为R、G、B
return img_new
plt.subplot(221)
plt.imshow(bgr2rgb(A))
plt.title('apple Image'), plt.xticks([]), plt.yticks([])
plt.subplot(222)
plt.imshow(bgr2rgb(B))
plt.title('orange Image'), plt.xticks([]), plt.yticks([])
plt.subplot(223)
plt.imshow(bgr2rgb(real))
plt.title('Direct_blending Image'), plt.xticks([]), plt.yticks([])
plt.subplot(224)
plt.imshow(bgr2rgb(ls_))
plt.title('Pyramid_blending Image'), plt.xticks([]), plt.yticks([])
plt.show()
结果展示
ok,到此本章就结束了,希望大家都有所收获~
参考资料
- 《OpenCV-Python 中文教程》
欢迎 各位小伙伴 在本文评论或者私信我。