影像通常是自然界景物的客觀反映,並以照片形式或影片記錄的介質連續儲存,獲取影像的目標是從感知的資料中產生數字影像,因此需要把連續的影像資料離散化,轉換為數字化影像,其工作主要包括兩方面——量化和取樣。數字化幅度值稱為量化,數字化座標值稱為取樣。本文主要講解影像量化和取樣處理的概念,並透過Python和OpenCV實現這些功能。
一.影像量化處理
1.概述
所謂量化(Quantization),就是將影像畫素點對應亮度的連續變化區間轉換為單個特定值的過程,即將原始灰度影像的空間座標幅度值離散化。量化等級越多,影像層次越豐富,灰度解析度越高,影像的質量也越好;量化等級越少,影像層次欠豐富,灰度解析度越低,會出現影像輪廓分層的現象,降低了影像的質量。圖1是將影像的連續灰度值轉換為0至255的灰度級的過程。
如果量化等級為2,則將使用兩種灰度級表示原始圖片的畫素(0-255),灰度值小於128的取0,大於等於128的取128;如果量化等級為4,則將使用四種灰度級表示原始圖片的畫素,新影像將分層為四種顏色,0-64區間取0,64-128區間取64,128-192區間取128,192-255區間取192,依次類推。
圖2是對比不同量化等級的“Lena”圖。其中(a)的量化等級為256,(b)的量化等級為64,(c)的量化等級為16,(d)的量化等級為8,(e)的量化等級為4,(f)的量化等級為2。
2.操作
下面講述Python影像量化處理相關程式碼操作。其核心流程是建立一張臨時圖片,接著迴圈遍歷原始影像中所有畫素點,判斷每個畫素點應該屬於的量化等級,最後將臨時影像顯示。下列程式碼將灰度影像轉換為兩種量化等級。
# -*- coding: utf-8 -*-# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('lena.png')#獲取影像高度和寬度height = img.shape[0]width = img.shape[1]#建立一幅影像new_img = np.zeros((height, width, 3), np.uint8)#影像量化操作 量化等級為2for i in range(height):
for j in range(width):
for k in range(3): #對應BGR三分量
if img[i, j][k] < 128:
gray = 0
else:
gray = 128
new_img[i, j][k] = np.uint8(gray)
#顯示影像cv2.imshow("src", img)cv2.imshow("Quantization", new_img)#等待顯示cv2.waitKey(0)cv2.destroyAllWindows()123456789101112131415161718192021222324252627282930313233
其輸出結果如圖3所示,它將灰度影像劃分為兩種量化等級。
下面的程式碼分別比較了量化等級為2、4、8的量化處理效果。
# -*- coding: utf-8 -*-# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('lena.png')#獲取影像高度和寬度height = img.shape[0]width = img.shape[1]#建立一幅影像new_img1 = np.zeros((height, width, 3), np.uint8)new_img2 = np.zeros((height, width, 3), np.uint8)new_img3 = np.zeros((height, width, 3), np.uint8)#影像量化等級為2的量化處理for i in range(height):
for j in range(width):
for k in range(3): #對應BGR三分量
if img[i, j][k] < 128:
gray = 0
else:
gray = 128
new_img1[i, j][k] = np.uint8(gray)#影像量化等級為4的量化處理for i in range(height):
for j in range(width):
for k in range(3): #對應BGR三分量
if img[i, j][k] < 64:
gray = 0
elif img[i, j][k] < 128:
gray = 64
elif img[i, j][k] < 192:
gray = 128
else:
gray = 192
new_img2[i, j][k] = np.uint8(gray)#影像量化等級為8的量化處理for i in range(height):
for j in range(width):
for k in range(3): #對應BGR三分量
if img[i, j][k] < 32:
gray = 0
elif img[i, j][k] < 64:
gray = 32
elif img[i, j][k] < 96:
gray = 64
elif img[i, j][k] < 128:
gray = 96
elif img[i, j][k] < 160:
gray = 128
elif img[i, j][k] < 192:
gray = 160
elif img[i, j][k] < 224:
gray = 192
else:
gray = 224
new_img3[i, j][k] = np.uint8(gray)#用來正常顯示中文標籤plt.rcParams['font.sans-serif']=['SimHei']#顯示影像titles = ['(a) 原始影像', '(b) 量化-L2', '(c) 量化-L4', '(d) 量化-L8'] images = [img, new_img1, new_img2, new_img3] for i in range(4):
plt.subplot(2,2,i+1), plt.imshow(images[i], 'gray'),
plt.title(titles[i])
plt.xticks([]),plt.yticks([]) plt.show()123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
輸出結果如圖4所示,該程式碼呼叫matplotlib.pyplot庫繪製了四幅影像,其中(a)表示原始影像,(b)表示等級為2的量化處理,(c)表示等級為4的量化處理,(d)表示等級為8的量化處理。
3.K-Means聚類量化處理
上一小節的量化處理是透過遍歷影像中的所有畫素點,進行灰度影像的幅度值離散化處理。本小節補充一個基於K-Means聚類演算法的量化處理過程,它能夠將彩色影像RGB畫素點進行顏色分割和顏色量化。注意,在第十九篇文章時詳細介紹了K-Means聚類知識,本小節只是帶領讀者重新回顧下該方法。
# coding: utf-8# BY:Eastmount CSDN 2020-11-10import cv2import numpy as npimport matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('nv.png') #影像二維畫素轉換為一維data = img.reshape((-1,3))data = np.float32(data)#定義中心 (type,max_iter,epsilon)criteria = (cv2.TERM_CRITERIA_EPS +
cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)#設定標籤flags = cv2.KMEANS_RANDOM_CENTERS#K-Means聚類 聚整合4類compactness, labels, centers = cv2.kmeans(data, 4, None, criteria, 10, flags)#影像轉換回uint8二維型別centers = np.uint8(centers)res = centers[labels.flatten()]dst = res.reshape((img.shape))#影像轉換為RGB顯示img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)#用來正常顯示中文標籤plt.rcParams['font.sans-serif']=['SimHei']#顯示影像titles = ['原始影像', '聚類量化 K=4'] images = [img, dst] for i in range(2):
plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray'),
plt.title(titles[i])
plt.xticks([]),plt.yticks([]) plt.show()1234567891011121314151617181920212223242526272829303132333435363738394041424344
輸出結果如圖5所示,它透過K-Means聚類演算法將彩色人物影像的灰度聚整合四種顏色。
二.影像取樣處理
1.概述
影像取樣(Image Sampling)處理是將一幅連續影像在空間上分割成M×N個網格,每個網格用一個亮度值或灰度值來表示,其示意圖如圖6所示。
影像取樣的間隔越大,所得影像畫素數越少,空間解析度越低,影像質量越差,甚至出現馬賽克效應;相反,影像取樣的間隔越小,所得影像畫素數越多,空間解析度越高,影像質量越好,但資料量會相應的增大。圖7展示了不同取樣間隔的“Lena”圖,其中圖(a)為原始影像,圖(b)為128×128的影像取樣效果,圖©為64×64的影像取樣效果,圖(d)為32×32的影像取樣效果,圖(e)為16×16的影像取樣效果,圖(f)為8×8的影像取樣效果。
數字影像的質量很大程度上取決於量化和取樣中所採用的樣本數和灰度級。現實生活中的影像,都需要經過離散化處理轉換成數字影像,從而進行後續的計算機處理和影像識別等操作。圖8展示了生活影像轉換為數字影像的過程。
2.操作
下面講述Python影像取樣處理相關程式碼操作。其核心流程是建立一張臨時圖片,設定需要取樣的區域大小(如16×16),接著迴圈遍歷原始影像中所有畫素點,取樣區域內的畫素點賦值相同(如左上角畫素點的灰度值),最終實現影像取樣處理。下列程式碼是進行16×16取樣的過程。
# -*- coding: utf-8 -*-# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('lena.png')#獲取影像高度和寬度height = img.shape[0]width = img.shape[1]#取樣轉換成16*16區域numHeight = int(height/16)numWidth = int(width/16)#建立一幅影像new_img = np.zeros((height, width, 3), np.uint8)#影像迴圈取樣16*16區域for i in range(16):
#獲取Y座標
y = i*numHeight for j in range(16):
#獲取X座標
x = j*numWidth #獲取填充顏色 左上角畫素點
b = img[y, x][0]
g = img[y, x][1]
r = img[y, x][2]
#迴圈設定小區域取樣
for n in range(numHeight):
for m in range(numWidth):
new_img[y+n, x+m][0] = np.uint8(b)
new_img[y+n, x+m][1] = np.uint8(g)
new_img[y+n, x+m][2] = np.uint8(r)
#顯示影像cv2.imshow("src", img)cv2.imshow("Sampling", new_img)#等待顯示cv2.waitKey(0)cv2.destroyAllWindows()12345678910111213141516171819202122232425262728293031323334353637383940414243444546
其輸出結果如圖8所示,它將灰度影像取樣成16×16的區域。
同樣,可以對彩色影像進行取樣處理,下面的程式碼將彩色風景影像取樣處理成8×8的馬賽克區域。
# -*- coding: utf-8 -*-# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('scenery.png')#獲取影像高度和寬度height = img.shape[0]width = img.shape[1]#取樣轉換成8*8區域numHeight = int(height/8)numwidth = int(width/8)#建立一幅影像new_img = np.zeros((height, width, 3), np.uint8)#影像迴圈取樣8*8區域for i in range(8):
#獲取Y座標
y = i*numHeight for j in range(8):
#獲取X座標
x = j*numwidth #獲取填充顏色 左上角畫素點
b = img[y, x][0]
g = img[y, x][1]
r = img[y, x][2]
#迴圈設定小區域取樣
for n in range(numHeight):
for m in range(numwidth):
new_img[y+n, x+m][0] = np.uint8(b)
new_img[y+n, x+m][1] = np.uint8(g)
new_img[y+n, x+m][2] = np.uint8(r)
#顯示影像cv2.imshow("src", img)cv2.imshow("Sampling", new_img)#等待顯示cv2.waitKey(0)cv2.destroyAllWindows()12345678910111213141516171819202122232425262728293031323334353637383940414243444546
其輸出結果如圖9所示,它將彩色風景影像取樣成8×8的區域。
但上述程式碼存在一個問題,當影像的長度和寬度不能被取樣區域整除時,輸出影像的最右邊和最下邊的區域沒有被取樣處理。這裡推薦讀者做個求餘運算,將不能整除部分的區域也進行相應的取樣處理。
3.區域性馬賽克處理
前面講述的程式碼是對整幅影像進行取樣處理,那麼如何對影像的區域性區域進行馬賽克處理呢?下面的程式碼就實現了該功能。當滑鼠按下時,它能夠給滑鼠拖動的區域打上馬賽克,並按下“s”鍵儲存影像至本地。
# -- coding:utf-8 --# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像im = cv2.imread('people.png', 1)#設定滑鼠左鍵開啟en = False#滑鼠事件def draw(event, x, y, flags, param):
global en #滑鼠左鍵按下開啟en值
if event==cv2.EVENT_LBUTTONDOWN:
en = True
#滑鼠左鍵按下並且移動
elif event==cv2.EVENT_MOUSEMOVE and flags==cv2.EVENT_LBUTTONDOWN:
#呼叫函式打馬賽克
if en:
drawMask(y,x)
#滑鼠左鍵彈起結束操作
elif event==cv2.EVENT_LBUTTONUP:
en = False
#影像區域性取樣操作 def drawMask(x, y, size=10):
#size*size取樣處理
m = int(x / size * size)
n = int(y / size * size)
print(m, n)
#10*10區域設定為同一畫素值
for i in range(size):
for j in range(size):
im[m+i][n+j] = im[m][n]#開啟對話方塊cv2.namedWindow('image')#呼叫draw函式設定滑鼠操作cv2.setMouseCallback('image', draw)#迴圈處理while(1):
cv2.imshow('image', im)
#按ESC鍵退出
if cv2.waitKey(10)&0xFF==27:
break
#按s鍵儲存圖片
elif cv2.waitKey(10)&0xFF==115:
cv2.imwrite('sava.png', im)#退出視窗cv2.destroyAllWindows()1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
其輸出結果如圖10所示,它將人物的臉部進行馬賽克處理,按下S鍵儲存圖片,按下ESC鍵退出。
三.影像金字塔
前面講解的影像取樣處理可以降低影像的大小,本小節將補充影像金字塔知識,瞭解專門用於影像向上取樣和向下取樣的pyrUp()和pyrDown()函式。
影像金字塔是指由一組影像且不同分別率的子圖集合,它是影像多尺度表達的一種,以多解析度來解釋影像的結構,主要用於影像的分割或壓縮。一幅影像的金字塔是一系列以金字塔形狀排列的解析度逐步降低,且來源於同一張原始圖的影像集合。如圖11所示,它包括了四層影像,將這一層一層的影像比喻成金字塔。影像金字塔可以透過梯次向下取樣獲得,直到達到某個終止條件才停止取樣,在向下取樣中,層級越高,則影像越小,解析度越低。
生成影像金字塔主要包括兩種方式——向下取樣、向上取樣。在圖11中,將影像G0轉換為G1、G2、G3,影像解析度不斷降低的過程稱為向下取樣;將G3轉換為G2、G1、G0,影像解析度不斷增大的過程稱為向上取樣。
1.影像向下取樣
在影像向下取樣中,使用最多的是高斯金字塔。它將對影像Gi進行高斯核卷積,並刪除原圖中所有的偶數行和列,最終縮小影像。其中,高斯核卷積運算就是對整幅影像進行加權平均的過程,每一個畫素點的值,都由其本身和鄰域內的其他畫素值(權重不同)經過加權平均後得到。常見的3×3和5×5高斯核如下:
高斯核卷積讓臨近中心的畫素點具有更高的重要度,對周圍畫素計算加權平均值,如圖12所示,其中心位置權重最高為0.4。
顯而易見,原始影像Gi具有M×N個畫素,進行向下取樣之後,所得到的影像Gi+1具有M/2×N/2個畫素,只有原圖的四分之一。透過對輸入的原始影像不停迭代以上步驟就會得到整個金字塔。注意,由於每次向下取樣會刪除偶數行和列,所以它會不停地丟失影像的資訊。
在OpenCV中,向下取樣使用的函式為pyrDown(),其原型如下所示:
-
dst = pyrDown(src[, dst[, dstsize[, borderType]]])
– src表示輸入影像,
– dst表示輸出影像,和輸入影像具有一樣的尺寸和型別
– dstsize表示輸出影像的大小,預設值為Size()
– borderType表示畫素外推方法,詳見cv::bordertypes
向下取樣的程式碼如下所示:
# -*- coding: utf-8 -*-# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('nv.png')#影像向下取樣r = cv2.pyrDown(img)#顯示影像cv2.imshow('original', img)cv2.imshow('PyrDown', r)cv2.waitKey()cv2.destroyAllWindows()1234567891011121314151617
輸出結果如圖13所示,它將原始影像壓縮成原圖的四分之一。
多次向下取樣的程式碼參考下列檔案。
# -*- coding: utf-8 -*-# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('nv.png')#影像向下取樣r1 = cv2.pyrDown(img)r2 = cv2.pyrDown(r1)r3 = cv2.pyrDown(r2)#顯示影像cv2.imshow('original', img)cv2.imshow('PyrDown1', r1)cv2.imshow('PyrDown2', r2)cv2.imshow('PyrDown3', r3)cv2.waitKey()cv2.destroyAllWindows()123456789101112131415161718192021
輸出結果如圖14所示,每次向下取樣均為上次的四分之一,並且影像的清晰度會降低。
2.影像向上取樣
在影像向上取樣是由小影像不斷放影像的過程。它將影像在每個方向上擴大為原影像的2倍,新增的行和列均用0來填充,並使用與“向下取樣”相同的卷積核乘以4,再與放大後的影像進行卷積運算,以獲得“新增畫素”的新值。如圖15所示,它在原始畫素45、123、89、149之間各新增了一行和一列值為0的畫素。
注意,向上取樣放大後的影像比原始影像要模糊。同時,向上取樣和向下取樣不是互逆的操作,經過兩種操作後,是無法恢復原始影像的。
在OpenCV中,向上取樣使用的函式為pyrUp(),其原型如下所示:
-
dst = pyrUp(src[, dst[, dstsize[, borderType]]])
– src表示輸入影像,
– dst表示輸出影像,和輸入影像具有一樣的尺寸和型別
– dstsize表示輸出影像的大小,預設值為Size()
– borderType表示畫素外推方法,詳見cv::bordertypes
向上取樣的程式碼如下所示:
# -*- coding: utf-8 -*-# BY:Eastmount CSDN 2020-11-10import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('lena.png')#影像向上取樣r = cv2.pyrUp(img)#顯示影像cv2.imshow('original', img)cv2.imshow('PyrUp', r)cv2.waitKey()cv2.destroyAllWindows()1234567891011121314151617
輸出結果如圖16所示,它將原始影像擴大為原影像的四倍,即向上一次取樣。
多次向上取樣的程式碼參考下列檔案。
# -*- coding: utf-8 -*-import cv2
import numpy as np
import matplotlib.pyplot as plt#讀取原始影像img = cv2.imread('lena2.png')#影像向上取樣r1 = cv2.pyrUp(img)r2 = cv2.pyrUp(r1)r3 = cv2.pyrUp(r2)#顯示影像cv2.imshow('original', img)cv2.imshow('PyrUp1', r1)cv2.imshow('PyrUp2', r2)cv2.imshow('PyrUp3', r3)cv2.waitKey()cv2.destroyAllWindows()1234567891011121314151617181920
輸出結果如圖17所示,每次向上取樣均為上次影像的四倍,但影像的清晰度會降低。
四.本章小結
本文主要講解了影像的量化處理和取樣處理,從基本機率到操作,再到擴充套件進行全方位講解,並且補充了基於K-Means聚類演算法的量化處理和區域性馬賽克特效處理,最後補充了影像金字塔相關知識。該章節知識點能夠將生活中的影像轉換為數字影像,更好地為後續的影像處理提供幫助。希望這篇基礎性文章對讀者有一定幫助,也希望這些知識點為讀者從事Python影像處理相關專案實踐或科學研究提供一定基礎。
時光嘀嗒嘀嗒的流失,這是我在CSDN寫下的第八篇年終總結,比以往時候來的更早一些。《敏而多思,寧靜致遠》,僅以此篇紀念這風雨兼程的一年,這感恩的一年。列車上只寫了一半,這兩天完成,思遠,思君O(∩_∩)O
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2733718/,如需轉載,請註明出處,否則將追究法律責任。