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 個小時,你有所收穫,咱們下篇博客見~

相關文章
相關標籤/搜索