圖像處理作業1-雙線性插值實現圖片放縮

在這裏插入圖片描述

Programming Assignment 1

Write a function “imresize” that gets the original image, original size, and target size as input and returns the output image at the target size using bilinear interpolation. Apply the imresize to one of the test images (256x256) to scale them to 1.5 and 0.75 (384x384 and 192x192 respectively).

提交報告內容包括實現原理,程序輸入與輸出圖像對比,結果分析,及代碼。編程語言不限。

實現原理:

在這裏插入圖片描述

首先,圖中有5個像素點:Q00Q01Q10Q11Q_{00},Q_{01},Q_{10},Q_{11}PP。其中四個紅色點QQ是原圖的點,綠色點PP是目標圖的像素點在原圖上的投影!四個紅色點QQ就是投影點PP的四周最近的點。通過四個紅色點QQ,可以計算出投影點PP的像素值,這樣目標圖上的像素點的像素值也就得到了。

已知:
四個紅色點QQ的座標值:h0h1w0w1h_0,h_1,w_0,w_1
四個紅色點QQ的像素值:f(Q00)f(Q01)f(Q10)f(Q11)f(Q_{00}),f(Q_{01}),f(Q_{10}),f(Q_{11})
投影點PP的座標值:hwh,w

目標圖的點如何投影到原圖上?

已知:

  1. 輸入圖的高和寬:height src,_{s r c}, width src_{s r c}
  2. 目標圖的高和寬:height dst,_{\text {dst}}, width dst_{\text {dst}}

投影的公式是:h=hdstg(x)h=h_{d s t} * \partial g(x) height src_{s r c} height dst_{d s t}

w=wdstwidthsrcwidthdstw=w_{d s t} * \frac{w i d t h_{s r c}}{w i d t h_{d s t}}

但是這個公式有一個問題,可能會導致目標圖的中心跟原圖的中心不對齊。例如:原圖是3x3,中心點座標(1, 1);目標圖是9x9,中心點座標(4, 4);通過上面的公式計算,目標圖中心點在原圖的投影座標:h=43/9=1.33331h=4 * 3 / 9=1.3333 \neq 1。之所以會出現中心點不對齊,原因是每個像素點實際上是一個邊長爲1的正方形,所以對於座標爲(h,w)(h, w),的像素點,它的中心其實是(h+0.5,w+0.5)(h+0.5, w+0.5)。所以精確計算應該是:h+0.5heightsrc=hdst+0.5heightdst\frac{h+0.5}{\text {height}_{\text {src}}}=\frac{h_{\text {dst}}+0.5}{\text {height}_{\text {dst}}}

g(x)w+0.5widthsrc=g(x)wdst+0.5widthdst\partial g(x) w+0.5 w i d t h_{s r c}=\partial g(x) w_{d s t}+0.5 w i d t h_{d s t}

轉換一下得到正確的投影公式:

h=(hdst+0.5)heightsrcheightdst0.5h=\left(h_{d s t}+0.5\right) * \frac{h e i g h t_{s r c}}{h e i g h t_{d s t}}-0.5

w=(wdst+0.5)widthsrcwidthdst0.5w=\left(w_{d s t}+0.5\right) * \frac{w i d t h_{s r c}}{w i d t h_{d s t}}-0.5

如何插值計算得到投影點的像素值?

已知:
四個紅色點QQ的座標值:h0h1w0w1h_0,h_1,w_0,w_1
四個紅色點QQ的像素值:f(Q00)f(Q01)f(Q10)f(Q11)f(Q_{00}),f(Q_{01}),f(Q_{10}),f(Q_{11})
投影點PP的座標值:hwh,w
思路是:每個QQ點的像素值乘以各自的權重,然後相加得到投影點PP像素值。QQ點跟PP點的距離越近,它的權重就越大。
雙線性插值給出的算法很是簡單粗暴:先在橫軸方向上進行兩次線性插值計算,然後在縱軸方向上進行一次插值計算。結合最開始那個圖例看,就是先求R0R_0R1R_1這兩個藍色點的像素值,然後再通過這兩個值,求得點PP的像素值。

具體計算如下:
f(R0)w1ww1w0f(Q00)+ww0w1w0f(Q01)f\left(R_{0}\right) \approx \frac{w_{1}-w}{w_{1}-w_{0}} f\left(Q_{00}\right)+\frac{w-w_{0}}{w_{1}-w_{0}} f\left(Q_{01}\right)
f(R1)w1ww1w0f(Q10)+ww0w1w0f(Q11)f\left(R_{1}\right) \approx \frac{w_{1}-w}{w_{1}-w_{0}} f\left(Q_{10}\right)+\frac{w-w_{0}}{w_{1}-w_{0}} f\left(Q_{11}\right)
f(P)h1hh1h0f(R0)+hh0h1h0f(R1)f(P) \approx \frac{h_{1}-h}{h_{1}-h_{0}} f\left(R_{0}\right)+\frac{h-h_{0}}{h_{1}-h_{0}} f\left(R_{1}\right)
h1hh1h0(w1ww1w0f(Q00)+ww0w1w0f(Q01))+hh0h1h0(w1ww1w0f(Q10)+ww0w1w0f(Q11))\approx \frac{h_{1}-h}{h_{1}-h_{0}}\left(\frac{w_{1}-w}{w_{1}-w_{0}} f\left(Q_{00}\right)+\frac{w-w_{0}}{w_{1}-w_{0}} f\left(Q_{01}\right)\right)+\frac{h-h_{0}}{h_{1}-h_{0}}\left(\frac{w_{1}-w}{w_{1}-w_{0}} f\left(Q_{10}\right)+\frac{w-w_{0}}{w_{1}-w_{0}} f\left(Q_{11}\right)\right)
=1(w1w0)(h1h0)((h1h)(w1w)f(Q00)+(h1h)(ww0)f(Q01)+(hh0)(w1w)f(Q10)+(hh0)(ww0)f(Q11))=\frac{1}{\left(w_{1}-w_{0}\right)\left(h_{1}-h_{0}\right)}\left(\left(h_{1}-h\right)\left(w_{1}-w\right) f\left(Q_{00}\right)+\left(h_{1}-h\right)\left(w-w_{0}\right) f\left(Q_{01}\right)+\left(h-h_{0}\right)\left(w_{1}-w\right) f\left(Q_{10}\right)+\left(h-h_{0}\right)\left(w-w_{0}\right) f\left(Q_{11}\right)\right)

前面說了4個紅色點QQ是投影點PP四周最近的點,顯然4個紅色點彼此間的距離都是1,也即w1w0=1h1h0=1w_1-w_0=1,h_1-h_0=1。因此上式可寫成f(P)(h1h)(w1w)f(Q00)+(h1h)(ww0)f(Q01)+(hh0)(w1w)f(Q10)+(hh0)(ww0)f(Q11)f(P) \approx\left(h_{1}-h\right)\left(w_{1}-w\right) f\left(Q_{00}\right)+\left(h_{1}-h\right)\left(w-w_{0}\right) f\left(Q_{01}\right)+\left(h-h_{0}\right)\left(w_{1}-w\right) f\left(Q_{10}\right)+\left(h-h_{0}\right)\left(w-w_{0}\right) f\left(Q_{11}\right)再令u=hh0v=ww0u=h-h_0,v=w-w_0式子可進一步寫成:f(P)f(Q00)(1u)(1v)+f(Q01)(1u)v+f(Q10)u(1v)+f(Q11)uvf(P) \approx f\left(Q_{00}\right)(1-u)(1-v)+f\left(Q_{01}\right)(1-u) v+f\left(Q_{10}\right) u(1-v)+f\left(Q_{11}\right) u v

具體編程實現步驟

在這裏插入圖片描述

  1. 放大後圖像的座標(x,y)(x',y')除以放大率aa,可以得到對應原圖像的座標(xa,ya)(\lfloor \frac{x'}{a}\rfloor , \lfloor \frac{y'}{a}\rfloor)

  2. 求原圖像的座標(xa,ya)(\lfloor \frac{x'}{a}\rfloor , \lfloor \frac{y'}{a}\rfloor)周圍44鄰域的座標I(x,y)I(x,y)I(x+1,y)I(x+1,y)I(x,y+1)I(x,y+1)I(x+1,y+1)I(x+1, y+1)

  3. 分別求這4個點與(xa,ya)(\frac{x'}{a}, \frac{y'}{a})的距離,根據距離設置權重:w=d dw = \frac{d}{\sum\ d}

  4. 根據下式求得放大後圖像(x,y)(x',y')處的像素值:

dx=xaxdy=yayI(x,y)=(1dx) (1dy) I(x,y)+dx (1dy) I(x+1,y)+(1dx) dy I(x,y+1)+dx dy I(x+1,y+1) d_x = \frac{x'}{a} - x\\ d_y = \frac{y'}{a} - y\\ I'(x',y') = (1-d_x)\ (1-d_y)\ I(x,y) + d_x\ (1-d_y)\ I(x+1,y) + (1-d_x)\ d_y\ I(x,y+1) + d_x\ d_y\ I(x+1,y+1)

輸入與輸出圖像對比

輸出(0.75倍) 輸入原始圖像 輸出(1.5倍)
在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述
輸出(0.75倍) 輸入原始圖像 輸出(1.5倍)
在這裏插入圖片描述 在這裏插入圖片描述 在這裏插入圖片描述

代碼

"""
AUTHOR: Tian YJ
CREATE: 2020-03-15
FUNCTION: Bi-Linear interpolation
"""

import cv2 # 我只用它來做圖像讀寫和繪圖,沒調用它的其它函數哦
import numpy as np # 進行數值計算

def imresize(img, shape_s, shape_d):
	# img爲原始圖像
	# shape_s爲原始圖像大小
	# shape_d爲目標圖像大小
	print("原始圖片大小:", shape_s)
	print("目標圖片大小:", shape_d)

	H_s, W_s = shape_s
	H_d, W_d = shape_d

	# 獲取目標圖像位置
	y = np.arange(H_d).repeat(W_d).reshape(W_d, -1)
	x = y.T

	# 獲取原始圖像位置
	# 中心對齊
	y = ((y+0.5) / (H_d / H_s)) - 0.5
	x = ((x+0.5) / (W_d / W_s)) - 0.5

	# 向下取整
	x_int = np.floor(x).astype(np.int)
	y_int = np.floor(y).astype(np.int)

	# 防止越界
	x_int = np.minimum(x_int, W_s-2)
	y_int = np.minimum(y_int, H_s-2)

	# 求四鄰域點與對應點的距離
	dx = x - x_int
	dy = y - y_int

	# 將距離矩陣擴展到三位通道
	dx = 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[y_int, x_int] + dx * (1-dy) * img[y_int, x_int+1] + (
    1-dx) * dy * img[y_int+1, x_int] + dx * dy * img[y_int + 1, x_int+1]

	# cv.imshow需要將圖片像素值轉換爲8位無符號數,否則無法正常顯示
	out = out.astype(np.uint8)
	
	return out

####################################################################
# 主函數
if __name__ == '__main__':

	ratio = str(input("請輸入需要放大的倍數:"))

	# 設置路徑
	path_work = 'C:/Users/86187/Desktop/image_result/'
	img_name = 'CARTOON'
	file_in = path_work + str(img_name) + '.jpg'
	file_out = path_work + str(img_name) + '_resize_' + ratio + '.jpg'

	# 讀取圖片
	img = cv2.imread(file_in).astype(np.float)

	# 獲取圖片尺寸
	ratio = float(ratio)
	shape_s = (img.shape[0], img.shape[1])
	shape_d = (int(ratio*shape_s[0]), int(ratio*shape_s[1]))

	#調用函數
	out = imresize(img, shape_s, shape_d)

	# 圖片展示
	cv2.imshow("result",out)

	# 固定繪圖窗口
	cv2.waitKey(0)
	cv2.destroyAllWindows()

	# 保存結果
	cv2.imwrite(file_out, out)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章