天天看點

opencv 圖像金字塔(python)

圖像金字塔

圖像金字塔是由一幅圖像的多個不同分辨率的子圖所構成的圖像集合

該組圖像是由單個圖像通過不斷地降采樣所産生的,最小的圖像可能僅僅有一個像素點。

圖像金字塔是一系列以金字塔形狀排列的、自底向上分辨率逐漸降低的圖像集合。

opencv 圖像金字塔(python)

通常情況下,圖像金字塔的底部是待處理的高分辨率圖像(原始圖像),而頂部則為其低分辨率的近似圖像。

向金字塔的頂部移動時,圖像的尺寸和分辨率都不斷地降低。通常情況下,每向上移動一級,圖像的寬和高都降低為原來的二分之一。

理論基礎

下采樣

圖像金字塔是同一圖像不同分辨率的子圖集合,是通過對原圖像不斷地向下采樣而産生的,即由高分辨率的圖像(大尺寸)産生低分辨率的近似圖像(小尺寸)。

最簡單的圖像金字塔可以通過不斷地删除圖像的偶數行和偶數列得到。

也可以先對原始圖像濾波,得到原始圖像的近似圖像,然後将近似圖像的偶數行和偶數列删除以擷取向下采樣的結果。有多種濾波器可以選擇。例如:

  • 鄰域濾波器:采用鄰域平均技術求原始圖像的近似圖像。該濾波器能夠産生平均金字塔。
  • 高斯濾波器:采用高斯濾波器對原始圖像進行濾波,得到高斯金字塔。這是OpenCV函數cv2.pyrDown()所采用的方式。

高斯金字塔是通過不斷地使用高斯濾波器濾波、采樣所産生的。

opencv 圖像金字塔(python)

原始圖像與各次向下采樣所得到的結果圖像共同構成了高斯金字塔。

例如,可以将原始圖像稱為第0層,第1次向下采樣的結果圖像稱為第1層,第2次向下采樣的結果圖像稱為第3層,以此類推。

opencv 圖像金字塔(python)

上采樣

在向上采樣的過程中,通常将圖像的寬度和高度都變為原來的2倍。

這意味着,向上采樣的結果圖像的大小是原始圖像的4倍。是以,要在結果圖像中補充大量的像素點。

對新生成的像素點進行指派,稱為插值處理,該過程可以通過多種方式實作,例如最臨近插值就是用最鄰近的像素點給目前還沒有值的像素點指派。

有一種常見的向上采樣,對像素點以補零的方式完成插值。通常是在每列像素點的右側插入值為零的列,在每行像素點的下方插入值為零的行。

opencv 圖像金字塔(python)

接下來,使用向下采樣時所用的高斯濾波器(高斯核)對補零後的圖像進行濾波處理,以擷取向上采樣的結果圖像。

但是需要注意,此時圖像中四分之三像素點的值都是零。是以,要将高斯濾波器系數乘以4,以保證得到的像素值範圍在其原有像素值範圍内。(補了很多0,導緻濾波後結果偏小)

向上采樣和向下采樣是相反的兩種操作。但是,由于向下采樣會丢失像素值,是以這兩種操作并不是可逆的。

也就是說,對一幅圖像先向上采樣、再向下采樣,是無法恢複其原始狀态的;同樣,對一幅圖像先向下采樣、再向上采樣也無法恢複到原始狀态。

pyrDown函數

函數cv2.pyrDown(),用于實作圖像高斯金字塔操作中的向下采樣,其文法形式為:

dst = cv2.pyrDown( src[, dstsize[, borderType]] )      
  • dst為目标圖像
  • src為原始圖像
  • dstsize為目标圖像的大小
  • borderType為邊界類型,預設值為BORDER_DEFAULT,且這裡僅支援BORDER_DEFAULT

預設情況下,輸出圖像的大小為Size((src.cols+1)/2, (src.rows+1)/2)。

在任何情況下,圖像尺寸必須滿足如下條件:

  • |dst. width∗2-src. cols|≤2
  • |dst. height∗2-src. rows|≤2

cv2.pyrDown()函數首先對原始圖像進行高斯濾波變換,以擷取原始圖像的近似圖像。在擷取近似圖像後,該函數通過抛棄偶數行和偶數列來實作向下采樣。

**例子:**使用函數cv2.pyrDown()對一幅圖像進行向下采樣

import cv2
o=cv2.imread("./img/hand1.png", cv2.IMREAD_GRAYSCALE)
r1=cv2.pyrDown(o)
r2=cv2.pyrDown(r1)
r3=cv2.pyrDown(r2)
print("o.shape=", o.shape)
print("r1.shape=", r1.shape)
print("r2.shape=", r2.shape)
print("r3.shape=", r3.shape)
cv2.imshow("original", o)
cv2.imshow("r1", r1)
cv2.imshow("r2", r2)
cv2.imshow("r3", r3)
cv2.waitKey()
cv2.destroyAllWindows()      
opencv 圖像金字塔(python)

經過向下采樣後,圖像的分辨率會變低。

pyrUp函數

在OpenCV中,使用函數cv2.pyrUp()實作圖像金字塔操作中的向上采樣,其文法形式如下:

dst = cv2.pyrUp( src[, dstsize[, borderType]] )      
  • dst為目标圖像
  • src為原始圖像
  • dstsize為目标圖像的大小
  • borderType為邊界類型,預設值為BORDER_DEFAULT,且這裡僅支援BORDER_DEFAULT。

目标圖像的大小為Size(src.cols*2, src.rows*2)。

在任何情況下,圖像尺寸需要滿足下列條件:

  • |dst. width-src. cols∗2|≤mod(dst. widh,2)
  • |dst. height-src. rows∗2|≤mod(dst. height,2)

對圖像向上采樣時,在每個像素的右側、下方分别插入零值列和零值行,得到一個偶數行、偶數列(即新增的行、列)都是零值的新圖像New。接下來,用向下采樣時所使用的高斯濾波器對新圖像New進行濾波,得到向上采樣的結果圖像。

為了確定像素值區間在向上采樣後與原始圖像保持一緻,需要将高斯濾波器的系數乘以4。

**例子:**使用函數cv2.pyrUp()對一幅圖像進行向上采樣

import cv2
o=cv2.imread("./img/hand2.png")
r1=cv2.pyrUp(o)
r2=cv2.pyrUp(r1)
r3=cv2.pyrUp(r2)
print("o.shape=", o.shape)
print("r1.shape=", r1.shape)
print("r2.shape=", r2.shape)
print("r3.shape=", r3.shape)
cv2.imshow("original", o)
cv2.imshow("r1", r1)
cv2.imshow("r2", r2)
cv2.imshow("r3", r3)
cv2.waitKey()
cv2.destroyAllWindows()      
opencv 圖像金字塔(python)
采樣可逆性的研究

拉普拉斯金字塔

為了在向上采樣時能夠恢複具有較高分辨率的原始圖像,就要擷取在采樣過程中所丢失的資訊,這些丢失的資訊就構成了拉普拉斯金字塔。

拉普拉斯金字塔的定義形式為:

Li=Gi - pyrUp(Gi+ 1)      
  • Li表示拉普拉斯金字塔中的第i層
  • Gi表示高斯金字塔中的第i層

拉普拉斯金字塔中的第i層,等于“高斯金字塔中的第i層”與“高斯金字塔中的第i+1層的向上采樣結果”之差。

opencv 圖像金字塔(python)

例子: 使用函數cv2.pyrDown()和cv2.pyrUp()構造拉普拉斯金字塔。

import cv2
O=cv2.imread("./img/hand2.png")
G0=O
G1=cv2.pyrDown(G0)
G2=cv2.pyrDown(G1)
G3=cv2.pyrDown(G2)
### 注意: 可能兩個圖像的大小不一樣
L0=G0-cv2.resize(cv2.pyrUp(G1),G0.shape[0:2][::-1])
L1=G1-cv2.resize(cv2.pyrUp(G2),G1.shape[0:2][::-1])
L2=G2-cv2.resize(cv2.pyrUp(G3),G2.shape[0:2][::-1])
print("L0.shape=", L0.shape)
print("L1.shape=", L1.shape)
print("L2.shape=", L2.shape)
cv2.imshow("L0", L0)
cv2.imshow("L1", L1)
cv2.imshow("L2", L2)
cv2.waitKey()
cv2.destroyAllWindows()      
opencv 圖像金字塔(python)

拉普拉斯金字塔的作用在于,能夠恢複高分辨率的圖像。

opencv 圖像金字塔(python)

向上采樣恢複高分辨率圖像:

G0=L0+cv2.pyrUp(G1)
G1=L1+cv2.pyrUp(G2)
G2=L2+cv2.pyrUp(G3)      
import cv2
import numpy as np
O=cv2.imread("./img/hand1.png")
G0=O
G1=cv2.pyrDown(G0)
L0=O-cv2.resize(cv2.pyrUp(G1),G0.shape[0:2][::-1])
RO=L0+cv2.resize(cv2.pyrUp(G1),G0.shape[0:2][::-1])
# 通過拉普拉斯圖像複原的原始圖像 
print("O.shape=", O.shape)
print("RO.shape=", RO.shape)
result=RO-O  # 将O和RO做減法運算 
# 計算result的絕對值
result=abs(result)
# 計算result所有元素的和 
print("原始圖像O與恢複圖像RO之差的絕對值和:", np.sum(result))
cv2.imshow("origin",O)
cv2.imshow("ReO",RO)
cv2.waitKey()
cv2.destroyAllWindows()