圖像多尺度特徵提取及特點分析
Author:Tian YJ
語言:Python 3
IDE:Visual Studio 2019
題目要求:用手機或者相機拍攝圖像,利用高斯金字塔和拉普拉斯金字塔提取圖像多尺度特徵,並總結各自特點。
實現原理:
1. 圖像金字塔
一幅圖像的金字塔是一系列以金字塔形狀排列的分辨率逐步降低,且來源於同一張原始圖的圖像集合。其通過梯次向下採樣獲得,直到達到某個終止條件才停止採樣。金字塔的底部是待處理圖像的高分辨率表示,而頂部是低分辨率的近似。我們將一層一層的圖像比喻成金字塔,層級越高,則圖像越小,分辨率越低。就像這樣:
2. 如何實現
一般來說,我們可以先討論兩種典型的圖像金字塔:高斯金字塔(Gaussian pyramid)和拉普拉斯金字塔(Laplacian pyramid)。
●高斯金字塔(Gaussian pyramid): 高斯金字塔是由底部的最大分辨率圖像逐次向下採樣得到的一系列圖像。最下面的圖像分辨率最高,越往上圖像分辨率越低。假設表示原始圖像,表示第i次下采樣得到的圖像,那麼高斯金字塔的計算過程可以表示如下:
其中Down表示下采樣函數,下采樣可以通過拋去圖像中的偶數行和偶數列來實現,這樣圖像長寬各減少二分之一,面積減少四分之一。
●拉普拉斯金字塔(Laplacian pyramid): 拉普拉斯金字塔可以認爲是殘差金字塔,用來存儲下采樣後圖片與原始圖片的差異。我們知道,如果高斯金字塔中任意一張圖(比如爲最初的高分辨率圖像)先進行下采樣得到圖Down(),再進行上採樣得到圖Up(Down()),得到的Up(Down())與是存在差異的,因爲下采樣過程丟失的信息不能通過上採樣來完全恢復,也就是說下采樣是不可逆的。下面的公式就是前面的差異記錄過程:
3. 算法流程
高斯金字塔算法流程:
- 對圖像進行高斯卷積(高斯濾波)
- 刪除偶數行和偶數列(下采樣)
拉普拉斯金字塔算法流程:(用於低分辨率恢復高分辨率圖像時計算殘差)
對於高斯金字塔中的低分辨率圖像,
- 先將圖像每個方向放大至原來的兩倍(上採樣),新增的行和列以0填充
- 對圖像進行高斯卷積(高斯濾波)
- 用下一層的高分辨率圖像減去高斯卷積後的圖像
說明一下,這裏我上、下采樣都是採取雙線性插值實現圖片放縮來實現,效果應該會更好一點。
4. 代碼實現
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 12 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 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)
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
# 進行尺度增加
img_ = imresize(pyramids[layer], ratio)
print(img_.shape)
print(pyramids[layer-1].shape)
# 進行高斯濾波
img = gaussFilter(img_)
print('====濾波成功(Laplacian_Pyramid)====')
# 用下一層的高分辨率圖像減去高斯卷積後的圖像
out = pyramids[layer-1] - img
# 保存結果
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()
# 主函數
if __name__ == '__main__':
# 設置路徑
path = 'C:/Users/86187/Desktop/image/'
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)
5. 結果展示
(1.1)高斯金字塔(彩色)
第0層 | 第1層 | 第2層 | 第3層 | 第4層 | 第5層 |
---|---|---|---|---|---|
(1.2)拉普拉斯金字塔(彩色)
第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
(2.1)高斯金字塔(灰度)
第0層 | 第1層 | 第2層 | 第3層 | 第4層 | 第5層 |
---|---|---|---|---|---|
(1.2)拉普拉斯金字塔(灰度)
第0層 | 第1層 | 第2層 | 第3層 | 第4層 |
---|---|---|---|---|