Author:Tian YJ
语言:Python 3
IDE:Visual Studio 2019
题目要求:用手机或者相机拍摄图像,利用高斯金字塔和拉普拉斯金字塔提取图像多尺度特征,并总结各自特点。
实现原理:
一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。就像这样:
一般来说,我们可以先讨论两种典型的图像金字塔:高斯金字塔(Gaussian pyramid)和拉普拉斯金字塔(Laplacian pyramid)。
●高斯金字塔(Gaussian pyramid): 高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像。最下面的图像分辨率最高,越往上图像分辨率越低。假设 G 0 G_0 G0表示原始图像, G i G_i Gi表示第i次下采样得到的图像,那么高斯金字塔的计算过程可以表示如下:
G i = D o w n ( D i − 1 ) G_i=Down(D_{i-1}) Gi=Down(Di−1)
其中Down表示下采样函数,下采样可以通过抛去图像中的偶数行和偶数列来实现,这样图像长宽各减少二分之一,面积减少四分之一。
●拉普拉斯金字塔(Laplacian pyramid): 拉普拉斯金字塔可以认为是残差金字塔,用来存储下采样后图片与原始图片的差异。我们知道,如果高斯金字塔中任意一张图 G i G_i Gi(比如 G 0 G_0 G0为最初的高分辨率图像)先进行下采样得到图Down( G i G_i Gi),再进行上采样得到图Up(Down( G i G_i Gi)),得到的Up(Down( G i G_i Gi))与 G i G_i Gi是存在差异的,因为下采样过程丢失的信息不能通过上采样来完全恢复,也就是说下采样是不可逆的。下面的公式就是前面的差异记录过程:
L i = G i − U p ( D o w n ( G i ) ) L_i=G_i-Up(Down(G_i)) Li=Gi−Up(Down(Gi))
高斯金字塔算法流程:
拉普拉斯金字塔算法流程:(用于低分辨率恢复高分辨率图像时计算残差)
对于高斯金字塔中的低分辨率图像,
说明一下,这里我上、下采样都是采取双线性插值实现图片放缩来实现,效果应该会更好一点。
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 10 15:26:20 2020
@author: Tian YJ
"""
import cv2 # 我只用它来做图像读写和绘图,没调用它的其它函数哦
import numpy as np # 进行数值计算用的
import os # 用来新建文件夹
### 高斯金字塔和拉普拉斯金字塔
def gaussian_Laplacian_pyramid(img):
# img 为待处理图片
K_size = 3 # 滤波器即卷积核的尺寸,这里设置为3*3
pad = K_size // 2 # 需要在图像边缘填充的0行列数,
# 之所以我要这样设置,是为了处理图像边缘时,滤波器中心与边缘对齐
"""
padding 函数
"""
def padding(img):
# img 为需要处理图像
# K_size 为滤波器也就是卷积核的尺寸,这里我默认设为3*3,基本上都是奇数
# 获取图片尺寸
H, W, C = img.shape
# 先填充行
rows = np.zeros((pad, W, C), dtype=np.uint8)
# 再填充列
cols = np.zeros((H+2*pad, pad, C), dtype=np.uint8)
# 进行拼接
img = np.vstack((rows, img, rows)) # 上下拼接
img = np.hstack((cols, img, cols)) # 左右拼接
return img
"""
高斯滤波器系数设置
"""
def Kernel(K_sigma=1.5):
# K_size为滤波器即卷积核尺寸
# 对滤波器进行初始化0
K = np.zeros((K_size, K_size), dtype=np.float)
# 代入公式求高斯滤波器系数,并填入矩阵
for x in range(-pad, -pad+K_size):
for y in range(-pad, -pad+K_size):
K[y + pad, x + pad] = np.exp( -(x ** 2 + y ** 2) / (2 * (K_sigma ** 2)))
K /= K.sum() # 进行归一化
return K
"""
高斯滤波函数
"""
def gaussFilter(img):
# img 为需要处理图像
# K_size 为滤波器也就是卷积核的尺寸,这里我默认设为3*3,基本上都是奇数
# 获取图片尺寸
H, W, C = img.shape
## 对图片进行padding
img = padding(img)
# 滤波器矩阵
K = Kernel()
## 进行滤波
out = img.copy()
for h in range(H):
for w in range(W):
for c in range(C):
out[pad+h, pad+w, c] = np.sum(K * out[h:h+K_size, w:w+K_size, c])
# 截取像素合理值
out = np.clip(out, 0, 255)
out = out[pad: pad + H, pad: pad + W].astype(np.uint8)
return out
"""
resize函数(双线性插值)
"""
#def resize(img, SMALL=True, ratio=1):
# H, W, C = img.shape
# H_ = np.int(H*ratio)
# W_ = np.int(W*ratio)
# if SMALL == True:
# # 删除偶数行
# img_row = np.zeros((H_, W, 3)).astype(np.float)
# for h in range(np.int(H_)):
# img_row[h,:,:] = img[2*h+1,:,:]
# # 删除偶数列
# img_col = np.zeros((H_, W_, 3)).astype(np.float)
# for w in range(np.int(W_)):
# img_col[:,w,:] = img_row[:,2*w+1,:]
# return img_col
# elif SMALL == False:
# B = np.zeros((H_, W_, 3)).astype(np.float)
# for row in range(H_):
# # 如果是偶数
# if row % 2 == 0:
# for col in range(W_):
# if col % 2 == 0:
# pos_c = int(col/2)
# pos_r = int(row/2)
# B[row, col,:] = img[pos_r, pos_c, :]
# else:
# B[row, col,:] = 0
# return B
def imresize(img, ratio):
# img为原始图像
# ratio为resize的倍数
shape_s = (img.shape[0], img.shape[1]) # shape_s为原始图像大小
shape_d = (int(ratio*shape_s[0]), int(ratio*shape_s[1])) # shape_d为目标图像大小
H_s, W_s = shape_s
H_d, W_d = shape_d
# 获取目标图像位置
y = np.arange(H_d).repeat(W_d).reshape(W_d, -1)
x = y.T
# 获取原始图像位置
# 中心对齐
y = ((y+0.5) / (H_d / H_s)) - 0.5
x = ((x+0.5) / (W_d / W_s)) - 0.5
# 向下取整
x_int = np.floor(x).astype(np.int)
y_int = np.floor(y).astype(np.int)
# 防止越界
x_int = np.minimum(x_int, W_s-2)
y_int = np.minimum(y_int, H_s-2)
# 求四邻域点与对应点的距离
dx = x - x_int
dy = y - y_int
# 将距离矩阵扩展到三通道
dx = np.repeat(np.expand_dims(dx, axis=-1), 3, axis=-1)
dy = np.repeat(np.expand_dims(dy, axis=-1), 3, axis=-1)
# 进行插值计算
out = (1-dx) * (1-dy) * img[y_int, x_int] + dx * (1-dy) * img[y_int, x_int+1] + (
1-dx) * dy * img[y_int+1, x_int] + dx * dy * img[y_int + 1, x_int+1]
# cv.imshow需要将图片像素值转换为8位无符号数,否则无法正常显示
out = out.astype(np.uint8)
return out
"""
高斯金字塔函数
"""
def gaussian_pyramid(img, layers):
# img 为原始图像
# layers 为金字塔层数
pyramids = [img] # 金字塔数组,用于存放金字塔图片
ratio = 0.5
# 进行尺度缩放
for layer in range(layers-1):
# 获取图片尺寸
H, W, C = pyramids[layer].shape
# 进行高斯滤波
img_ = gaussFilter(pyramids[layer])
print('====滤波成功(gaussian_pyramid)====')
# 进行尺度缩减
img = imresize(img_, ratio)
#img = resize(img_, SMALL=True, ratio=0.5)
print(img.shape)
# 保存结果
pyramids.append(img)
return pyramids
"""
拉普拉斯金字塔函数
"""
def Laplacian_Pyramid(pyramids):
## pyramids为高斯金字塔每一层图片
L_pyramids = [] # 保存拉普拉斯金字塔图片
ratio = 2.
for layer in range(len(pyramids)-1,0,-1):
# 获取图片尺寸
H, W, C = pyramids[layer].shape
# 进行尺度增加 resize(img_, option=S, ratio=0.5)
#img_ = resize(pyramids[layer],SMALL=False, ratio=2.)
img_ = imresize(pyramids[layer], ratio)
print(img_.shape)
print(pyramids[layer-1].shape)
# 进行高斯滤波
img = gaussFilter(img_)
print('====滤波成功(Laplacian_Pyramid)====')
# 用下一层的高分辨率图像减去高斯卷积后的图像
# 处理负数情况
out = np.abs(pyramids[layer-1] - img)
out = out / out.max() * 255
out = out.astype(np.uint8)
# 保存结果
L_pyramids.append(out)
return L_pyramids
# 调用函数产生高斯金字塔
gauss_out = gaussian_pyramid(img, 6) # 返回列表pyramids
# 调用函数产生拉普拉斯金字塔
out = Laplacian_Pyramid(gauss_out) # 返回列表L_pyramids
return gauss_out, out
# 保存、显示图片函数
def save_img(imgs, path):
for i in range(len(imgs)):
cv2.imwrite(path+"layer_{}.jpg".format(i), imgs[i].astype(np.uint8))
cv2.imshow('result', imgs[i])
cv2.waitKey(0)
cv2.destroyAllWindows()
# 灰度化函数
def BGR2GRAY(img):
# 获取图片尺寸
H, W, C = img.shape
# 灰度化
out = np.ones((H,W,3))
for i in range(H):
for j in range(W):
out[i,j,:] = 0.299*img[i,j,0] + 0.578*img[i,j,1] + 0.114*img[i,j,2]
out = out.astype(np.uint8)
return out
# 主函数
if __name__ == '__main__':
# 设置路径
path = 'C:/Users/86187/Desktop/'
file_in = path + 'Cristiano_Ronaldo.jpg'
# 读取图片
img = cv2.imread(file_in)
# 灰度化
img = BGR2GRAY(img)
# 生成金字塔
gauss_out, out = gaussian_Laplacian_pyramid(img)
print(len(gauss_out))
print(len(out))
# 新建文件夹
file_name = ['gaussian_pyramid', 'Laplacian_pyramid']
for name in file_name:
os.mkdir(path+name)
path_1 = path + file_name[0] + '/'
path_2 = path + file_name[1] + '/'
# 保存图片
save_img(gauss_out, path_1)
save_img(out, path_2)
第0层 | 第1层 | 第2层 | 第3层 | 第4层 | 第5层 |
---|---|---|---|---|---|
第0层 | 第1层 | 第2层 | 第3层 | 第4层 |
---|---|---|---|---|
# 灰度化函数
def BGR2GRAY(img):
# 获取图片尺寸
H, W, C = img.shape
# 灰度化
out = np.ones((H,W,3))
for i in range(H):
for j in range(W):
out[i,j,:] = 0.299*img[i,j,0] + 0.578*img[i,j,1] + 0.114*img[i,j,2]
out = out.astype(np.uint8)
return out
第0层 | 第1层 | 第2层 | 第3层 | 第4层 | 第5层 |
---|---|---|---|---|---|
第0层 | 第1层 | 第2层 | 第3层 | 第4层 |
---|---|---|---|---|