图像处理3
Q.21. 直方圖歸一化( Histogram Normalization )
歸一化直方圖吧!
有時直方圖會存在偏差。比如說,數據集中在 0 處(左側)的圖像全體會偏暗,數據集中在255 處(右側)的圖像會偏亮。如果直方圖有所偏向,那么其動態范圍( dynamic range )就會較低。為了使人能更清楚地看見圖片,讓直方圖歸一化、平坦化是十分必要的。
這種歸一化直方圖的操作被稱作灰度變換(Grayscale Transformation)。像素點取值范圍從 [c,d] 轉換到 [a,b] 的過程由下式定義。這回我們將imori_dark.jpg的灰度擴展到 [0, 255] 范圍。
xout = { a (xin < c)(b-a)/(d-c) * (xin-c) + a (c <= xin <= d)b (d < xin) import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori_dark.jpg").astype(np.float) H, W, C = img.shape# 范圍 [0, 255] a, b = 0., 255.vmin = img.min() vmax = img.max()out = img.copy() out[out<a] = a out[out>b] = b out = (b-a) / (vmax - vmin) * (out - vmin) + a out = out.astype(np.uint8)# 顯示直方圖 plt.hist(out.ravel(), bins=255, rwidth=0.8, range=(0, 255)) plt.savefig("out_his21.png") plt.show()# 保存圖像 cv2.imshow("result21", out) cv2.waitKey(0) cv2.imwrite("out21.jpg", out)輸入:
輸出:
Q.22. 直方圖操作
讓直方圖的平均值m0=128,標準差s0=52?吧!
這里并不是變更直方圖的動態范圍,而是讓直方圖變得平坦。
可以使用下式將平均值為m標準差為s的直方圖變成平均值為m0標準差為s0的直方圖:
xout = s0 / s * (xin - m) + m0
import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori_dark.jpg").astype(np.float) H, W, C = img.shape# 范圍 [0, 255] m0 = 128 s0 = 52m = np.mean(img) s = np.std(img)out = img.copy() out = s0 / s * (out - m) + m0 out[out < 0] = 0 out[out > 255] = 255 out = out.astype(np.uint8)# 顯示中方圖 plt.hist(out.ravel(), bins=255, rwidth=0.8, range=(0, 255)) plt.savefig("out_his22.png") plt.show()# 保存結果 cv2.imshow("result22", out) cv2.waitKey(0) cv2.imwrite("out22.jpg", out)輸入:
輸出:
Q.23. 直方圖均衡化( Histogram Equalization )
讓均勻化直方圖!
直方圖均衡化是使直方圖變得平坦的操作,是不需要計算上面的問題中的平均值、標準差等數據使直方圖的值變得均衡的操作。
均衡化操作由以下式子定義。S是總的像素數;Zmax是像素點的最大取值(在這里是 255);h(z)表示取值為z的累積分布函數:
Z’ = Zmax / S * Sum{i=0:z} h(z)
import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori.jpg").astype(np.float) H, W, C = img.shape# 直方圖平坦 S = H * W * C * 1.out = img.copy()sum_h = 0. z_max = 255.for i in range(1, 255):ind = np.where(img == i)sum_h += len(img[ind])z_prime = z_max / S * sum_hout[ind] = z_primeout = out.astype(np.uint8)# 顯示直方圖 plt.hist(out.ravel(), bins=255, rwidth=0.8, range=(0, 255)) plt.savefig("out_his23.png") plt.show()# 保存結果 cv2.imshow("result23", out) cv2.waitKey(0) cv2.imwrite("out23.jpg", out)輸入:
輸出:
Q.24. 伽瑪校正(Gamma Correction)
對imori_gamma.jpg進行伽馬校正(c=1,g=2.2)吧!
伽馬校正用來對照相機等電子設備傳感器的非線性光電轉換特性進行校正。如果圖像原樣顯示在顯示器等上,畫面就會顯得很暗。伽馬校正通過預先增大 RGB 的值來排除顯示器的影響,達到對圖像修正的目的。
由于下式引起非線性變換,在該式中,x被歸一化,限定在[0,1]范圍內。c是常數,g為伽馬變量(通常取2.2):
x’ = c * Iin ^ g
因此,使用下面的式子進行伽馬校正:
Iout = (1/c * Iin) ^ (1/g)
import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori_gamma.jpg").astype(np.float)# Gammma 校正 c = 1. g = 2.2out = img.copy() out /= 255.#歸一化 out = (1/c * out) ** (1/g)#伽馬校正out *= 255 out = out.astype(np.uint8)# 保存結果 cv2.imshow("result24", out) cv2.waitKey(0) cv2.imwrite("out24.jpg", out)輸入:
輸出:
Q.25. 最鄰近插值( Nearest-neighbor Interpolation )
使用最鄰近插值將圖像放大1.5倍吧!
最近鄰插值在圖像放大時補充的像素取最臨近的像素的值。由于方法簡單,所以處理速度很快,但是放大圖像畫質劣化明顯。
使用下面的公式放大圖像吧!I’為放大后圖像,I為放大前圖像,a為放大率,方括號為取整操作:
I’(x,y) = I([x/a], [y/a])
import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori.jpg").astype(np.float) H, W, C = img.shape# 最鄰近法 a = 1.5 aH = int(a * H) aW = int(a * W)y = np.arange(aH).repeat(aW).reshape(aW, -1) x = np.tile(np.arange(aW), (aH, 1)) y = np.round(y / a).astype(np.int) x = np.round(x / a).astype(np.int)out = img[y,x]out = out.astype(np.uint8)# 保存圖像 cv2.imshow("result25", out) cv2.waitKey(0) cv2.imwrite("out25.jpg", out)輸入:
輸出:
Q.26. 雙線性插值( Bilinear Interpolation )
使用雙線性插值將圖像放大1.5倍吧!
雙線性插值考察4鄰域的像素點,根據距離設置權值。雖然計算量增大使得處理時間變長,但是可以有效抑制畫質劣化。
I(x,y) I(x+1,y)
- (x’/a,y’/a)
I(x,y+1) I(x+1,y+1)
I’(x’,y’) = (1-dx)(1-dy)I(x,y) + dx(1-dy)I(x+1,y) + (1-dx)dyI(x,y+1) + dxdyI(x+1,y+1)
import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori.jpg").astype(np.float) H, W, C = img.shape# 雙線性 a = 1.5 aH = int(a * H) aW = int(a * W)y = np.arange(aH).repeat(aW).reshape(aW, -1) x = np.tile(np.arange(aW), (aH, 1)) y = (y / a) x = (x / a)ix = np.floor(x).astype(np.int) iy = np.floor(y).astype(np.int)ix = np.minimum(ix, W-2) iy = np.minimum(iy, H-2)dx = x - ix dy = y - iydx = 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[iy, ix] + dx * (1 - dy) * img[iy, ix+1] + (1 - dx) * dy * img[iy+1, ix] + dx * dy * img[iy+1, ix+1]out[out>255] = 255 out = out.astype(np.uint8)# 保存圖像 cv2.imshow("result26", out) cv2.waitKey(0) cv2.imwrite("out26.jpg", out)輸入:
輸出:
Q.27. 雙三次插值( Bicubic Interpolation )
使用雙三次插值將圖像放大1.5倍吧!
雙三次插值是雙線性插值的擴展,使用鄰域16像素進行插值。
I(x-1,y-1) I(x,y-1) I(x+1,y-1) I(x+2,y-1) I(x-1,y) I(x,y) I(x+1,y) I(x+2,y) I(x-1,y+1) I(x,y+1) I(x+1,y+1) I(x+2,y+1) I(x-1,y+2) I(x,y+2) I(x+1,y+2) I(x+2,y+2)各自像素間的距離由下式決定:
dx1 = x’/a - (x-1) , dx2 = x’/a - x , dx3 = (x+1) - x’/a , dx4 = (x+2) - x’/a
dy1 = y’/a - (y-1) , dy2 = y’/a - y , dy3 = (y+1) - y’/a , dy4 = (y+2) - y’/a
基于距離的權重函數由以下函數取得,a在大部分時候取-1:
利用上面得到的權重,通過下面的式子擴大圖像。將每個像素與權重的乘積之和除以權重的和。
I’(x’, y’) = (Sum{i=-1:2}{j=-1:2} I(x+i,y+j) * wxi * wyj) / Sum{i=-1:2}{j=-1:2} wxi * wyj
import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori.jpg").astype(np.float32) H, W, C = img.shape# 雙三次插值 a = 1.5 aH = int(a * H) aW = int(a * W)y = np.arange(aH).repeat(aW).reshape(aW, -1) x = np.tile(np.arange(aW), (aH, 1)) y = (y / a) x = (x / a)ix = np.floor(x).astype(np.int) iy = np.floor(y).astype(np.int)ix = np.minimum(ix, W-1) iy = np.minimum(iy, H-1)dx2 = x - ix dy2 = y - iy dx1 = dx2 + 1 dy1 = dy2 + 1 dx3 = 1 - dx2 dy3 = 1 - dy2 dx4 = 1 + dx3 dy4 = 1 + dy3dxs = [dx1, dx2, dx3, dx4] dys = [dy1, dy2, dy3, dy4]def weight(t):a = -1.at = np.abs(t)w = np.zeros_like(t)ind = np.where(at <= 1)w[ind] = ((a+2) * np.power(at, 3) - (a+3) * np.power(at, 2) + 1)[ind]ind = np.where((at > 1) & (at <= 2))w[ind] = (a*np.power(at, 3) - 5*a*np.power(at, 2) + 8*a*at - 4*a)[ind]return ww_sum = np.zeros((aH, aW, C), dtype=np.float32) out = np.zeros((aH, aW, C), dtype=np.float32)for j in range(-1, 3):for i in range(-1, 3):ind_x = np.minimum(np.maximum(ix + i, 0), W-1)ind_y = np.minimum(np.maximum(iy + j, 0), H-1)wx = weight(dxs[i+1])wy = weight(dys[j+1])wx = np.repeat(np.expand_dims(wx, axis=-1), 3, axis=-1)wy = np.repeat(np.expand_dims(wy, axis=-1), 3, axis=-1)w_sum += wx * wyout += wx * wy * img[ind_y, ind_x]out /= w_sum out[out>255] = 255 out = out.astype(np.uint8)# 保存結果 cv2.imshow("result27", out) cv2.waitKey(0) cv2.imwrite("out27.jpg", out)輸入:
輸出:
Q.28. 仿射變換( Afine Transformations )——平行移動
利用仿射變換讓圖像在x方向上+30,在y方向上-30吧!
仿射變換利用3x3的矩陣來進行圖像變換。
變換的方式有平行移動(問題28)、放大縮小(問題29)、旋轉(問題30)、傾斜(問題31)等。
原圖像記為(x,y),變換后的圖像記為(x’,y’)。
圖像放大縮小矩陣為下式:
[ x' ] = [a b][x]y' c d y另一方面,平行移動按照下面的式子計算:
[ x' ] = [x] + [tx]y' y + ty把上面兩個式子盤成一個:
x' a b tx x [ y' ] = [ c d ty ][ y ]1 0 0 1 1特別的,使用以下的式子進行平行移動:
x' 1 0 tx x [ y' ] = [ 0 1 ty ][ y ]1 0 0 1 1 import cv2 import numpy as np import matplotlib.pyplot as plt# 讀取圖像 img = cv2.imread("imori.jpg").astype(np.float32) H, W, C = img.shape# 仿射 a = 1. b = 0. c = 0. d = 1. tx = 30 ty = -30y = np.arange(H).repeat(W).reshape(W, -1) x = np.tile(np.arange(W), (H, 1))out = np.zeros((H+1, W+1, C), dtype=np.float32)x_new = a * x + b * y + tx y_new = c * x + d * y + tyx_new = np.minimum(np.maximum(x_new, 0), W).astype(np.int) y_new = np.minimum(np.maximum(y_new, 0), H).astype(np.int)out[y_new, x_new] = img[y, x] out = out[:H, :W] out = out.astype(np.uint8)# 保存圖像 cv2.imshow("result28", out) cv2.waitKey(0) cv2.imwrite("out28.jpg", out)輸入:
輸出:
Q.29. 仿射變換( Afine Transformations )——放大縮小
使用仿射變換,將圖片在x方向上放大1.3倍,在y方向上縮小至0.8倍。
在上面的條件下,同時在x方向上像右平移30(+30),在y方向上向上平移30(-30)。
輸入:
輸出:
Q.30. 仿射變換( Afine Transformations )——旋轉
使用仿射變換,逆時針旋轉30度。
使用仿射變換,逆時針旋轉30度并且能讓全部圖像顯現(也就是說,單純地做仿射變換會讓圖片邊緣丟失,這一步中要讓圖像的邊緣不丟失,需要耗費一些工夫)。
使用下面的式子進行逆時針方向旋轉A度的仿射變換:
輸入:
輸出:
總結
- 上一篇: 为什么这么多人转行产品经理?产品经理发展
- 下一篇: 2021-2027中国光刻掩膜版市场现状