本文分享自華為云社區(qū)《[Python圖像處理] 二十.圖像量化處理和采樣處理及局部馬賽克特效》,作者: eastmount。
本文主要講述如何進行圖像量化處理和采樣處理及局部馬賽克特效。
一.圖像量化處理
圖像通常是自然界景物的客觀反映,并以照片形式或視頻記錄的介質(zhì)連續(xù)保存,獲取圖像的目標是從感知的數(shù)據(jù)中產(chǎn)生數(shù)字圖像,因此需要把連續(xù)的圖像數(shù)據(jù)離散化,轉(zhuǎn)換為數(shù)字化圖像,其工作主要包括兩方面——量化和采樣。數(shù)字化幅度值稱為量化,數(shù)字化坐標值稱為采樣。本章主要講解圖像量化和采樣處理的概念,并通過Python/ target=_blank class=infotextkey>Python和OpenCV實現(xiàn)這些功能。
1.1 概述
所謂量化(Quantization),就是將圖像像素點對應亮度的連續(xù)變化區(qū)間轉(zhuǎn)換為單個特定值的過程,即將原始灰度圖像的空間坐標幅度值離散化。量化等級越多,圖像層次越豐富,灰度分辨率越高,圖像的質(zhì)量也越好;量化等級越少,圖像層次欠豐富,灰度分辨率越低,會出現(xiàn)圖像輪廓分層的現(xiàn)象,降低了圖像的質(zhì)量。圖6-1是將圖像的連續(xù)灰度值轉(zhuǎn)換為0至255的灰度級的過程。
如果量化等級為2,則將使用兩種灰度級表示原始圖片的像素(0-255),灰度值小于128的取0,大于等于128的取128;如果量化等級為4,則將使用四種灰度級表示原始圖片的像素,新圖像將分層為四種顏色,0-64區(qū)間取0,64-128區(qū)間取64,128-192區(qū)間取128,192-255區(qū)間取192;依次類推。
圖6-2是對比不同量化等級的“Lena”圖。其中(a)的量化等級為256,(b)的量化等級為64,(c)的量化等級為16,(d)的量化等級為8,(e)的量化等級為4,(f)的量化等級為2。
1.2 操作
下面講述Python圖像量化處理相關代碼操作。其核心流程是建立一張臨時圖片,接著循環(huán)遍歷原始圖像中所有像素點,判斷每個像素點應該屬于的量化等級,最后將臨時圖像顯示。下述代碼將灰度圖像轉(zhuǎn)換為兩種量化等級。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
#讀取原始圖像
img = cv2.imread('lena.png')
#獲取圖像高度和寬度
height = img.shape[0]
width = img.shape[1]
#創(chuàng)建一幅圖像
new_img = 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_img[i, j][k] = np.uint8(gray)
#顯示圖像
cv2.imshow("src", img)
cv2.imshow("", new_img)
#等待顯示
cv2.waitKey(0)
cv2.destroyAllwindows()
其輸出結(jié)果如圖6-3所示,它將灰度圖像劃分為兩種量化等級。
下面的代碼分別比較了量化等級為2、4、8的量化處理效果。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
#讀取原始圖像
img = cv2.imread('lena.png')
#獲取圖像高度和寬度
height = img.shape[0]
width = img.shape[1]
#創(chuàng)建一幅圖像
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 = [u'(a) 原始圖像', u'(b) 量化-L2', u'(c) 量化-L4', u'(d) 量化-L8']
images = [img, new_img1, new_img2, new_img3]
for i in xrange(4):
plt.subplot(2,2,i+1), plt.imshow(images[i], 'gray'),
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
輸出結(jié)果如圖6-4所示,該代碼調(diào)用matplotlib.pyplot庫繪制了四幅圖像,其中(a)表示原始圖像,(b)表示等級為2的量化處理,(c)表示等級為4的量化處理,(d)表示等級為8的量化處理。
1.3 K-Means聚類量化處理
上一小節(jié)的量化處理是通過遍歷圖像中的所有像素點,進行灰度圖像的幅度值離散化處理。本小節(jié)補充一個基于K-Means聚類算法的量化處理過程,它能夠?qū)⒉噬珗D像RGB像素點進行顏色分割和顏色量化。更多知識推薦大家學習前一篇文章。
# coding: utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
#讀取原始圖像
img = cv2.imread('people.png')
#圖像二維像素轉(zhuǎn)換為一維
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)
#圖像轉(zhuǎn)換回uint8二維類型
centers = np.uint8(centers)
res = centers[labels.flatten()]
dst = res.reshape((img.shape))
#圖像轉(zhuǎn)換為RGB顯示
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)
#用來正常顯示中文標簽
plt.rcParams['font.sans-serif']=['SimHei']
#顯示圖像
titles = [u'原始圖像', u'聚類量化 K=4']
images = [img, dst]
for i in xrange(2):
plt.subplot(1,2,i+1), plt.imshow(images[i], 'gray'),
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
輸出結(jié)果如圖6-4所示,它通過K-Means聚類算法將彩色人物圖像的灰度聚集成四種顏色。
二.圖像采樣處理
2.1 概述
圖像采樣(Image Sampling)處理是將一幅連續(xù)圖像在空間上分割成M×N個網(wǎng)格,每個網(wǎng)格用一個亮度值或灰度值來表示,其示意圖如圖6-5所示。
圖像采樣的間隔越大,所得圖像像素數(shù)越少,空間分辨率越低,圖像質(zhì)量越差,甚至出現(xiàn)馬賽克效應;相反,圖像采樣的間隔越小,所得圖像像素數(shù)越多,空間分辨率越高,圖像質(zhì)量越好,但數(shù)據(jù)量會相應的增大。圖6-6展示了不同采樣間隔的“Lena”圖。
2.2 操作
下面講述Python圖像采樣處理相關代碼操作。其核心流程是建立一張臨時圖片,設置需要采樣的區(qū)域大小(如16×16),接著循環(huán)遍歷原始圖像中所有像素點,采樣區(qū)域內(nèi)的像素點賦值相同(如左上角像素點的灰度值),最終實現(xiàn)圖像采樣處理。代碼是進行16×16采樣的過程。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
#讀取原始圖像
img = cv2.imread('scenery.png')
#獲取圖像高度和寬度
height = img.shape[0]
width = img.shape[1]
#采樣轉(zhuǎn)換成16*16區(qū)域
numHeight = height/16
numwidth = width/16
#創(chuàng)建一幅圖像
new_img = np.zeros((height, width, 3), np.uint8)
#圖像循環(huán)采樣16*16區(qū)域
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]
#循環(huán)設置小區(qū)域采樣
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("", new_img)
#等待顯示
cv2.waitKey(0)
cv2.destroyAllWindows()
輸出結(jié)果如下圖所示:
同樣,可以對彩色圖像進行采樣處理,下面的代碼將彩色風景圖像采樣處理成8×8的馬賽克區(qū)域。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
#讀取原始圖像
img = cv2.imread('scenery.png')
#獲取圖像高度和寬度
height = img.shape[0]
width = img.shape[1]
#采樣轉(zhuǎn)換成8*8區(qū)域
numHeight = height/8
numwidth = width/8
#創(chuàng)建一幅圖像
new_img = np.zeros((height, width, 3), np.uint8)
#圖像循環(huán)采樣8*8區(qū)域
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]
#循環(huán)設置小區(qū)域采樣
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()
其輸出結(jié)果如圖所示,它將彩色風景圖像采樣成8×8的區(qū)域。
但上述代碼存在一個問題,當圖像的長度和寬度不能被采樣區(qū)域整除時,輸出圖像的最右邊和最下邊的區(qū)域沒有被采樣處理。這里推薦讀者做個求余運算,將不能整除部門的區(qū)域也進行采樣處理。
2.3 局部馬賽克處理
前面講述的代碼是對整幅圖像進行采樣處理,那么如何對圖像的局部區(qū)域進行馬賽克處理呢?下面的代碼就實現(xiàn)了該功能。當鼠標按下時,它能夠給鼠標拖動的區(qū)域打上馬賽克,并按下“s”鍵保存圖像至本地。
# -- coding:utf-8 --
import 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:
#調(diào)用函數(shù)打馬賽克
if en:
drawMask(y,x)
#鼠標左鍵彈起結(jié)束操作
elif event==cv2.EVENT_LBUTTONUP:
en = False
#圖像局部采樣操作
def drawMask(x, y, size=10):
#size*size采樣處理
m = x / size * size
n = y / size * size
print m, n
#10*10區(qū)域設置為同一像素值
for i in range(size):
for j in range(size):
im[m+i][n+j] = im[m][n]
#打開對話框
cv2.namedWindow('image')
#調(diào)用draw函數(shù)設置鼠標操作
cv2.setMouseCallback('image', draw)
#循環(huán)處理
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()
其輸出結(jié)果如圖所示,它將人物的臉部進行馬賽克處理。