第2章 Python 數(shù)字圖像處理(DIP) --數(shù)字圖像基礎(chǔ)3 - 圖像內(nèi)插 - 最近鄰內(nèi)插 - 雙線(xiàn)性插值 - 雙三次內(nèi)插 - 圖像放大

轉(zhuǎn)載請(qǐng)注明出處

圖像內(nèi)插

內(nèi)插通常在圖像放大、縮小、旋轉(zhuǎn)和幾何校正等任務(wù)中使用。內(nèi)插并用它來(lái)調(diào)整圖像的大?。s小和放大),縮小和放大基本上采用圖像重取樣方法

最近鄰內(nèi)插,這種方法將原圖像中最近鄰的灰度賦給了每個(gè)新位置,這種方法簡(jiǎn)單,但會(huì)產(chǎn)生我們不想要的人為失真,如嚴(yán)重的直邊失真。更合適的方法是雙線(xiàn)性?xún)?nèi)插,它使用4個(gè)最近鄰的灰度來(lái)計(jì)算給定位置的灰度。令(x, y)表示待賦灰度值的位置(可將它相像為前面描述的網(wǎng)格點(diǎn))的坐標(biāo),令v(x, y)表示灰度值。對(duì)于雙線(xiàn)性?xún)?nèi)插方法,所賦的值由如下公式得到:
v(x, y) = ax + by + cxy + d \tag{2.17}
4 個(gè)系數(shù)可由點(diǎn)(x, y)的4個(gè)最近鄰點(diǎn)寫(xiě)出的4個(gè)未知方程求出。雙線(xiàn)性?xún)?nèi)插的結(jié)果要比最近鄰內(nèi)插的結(jié)果好得多,但計(jì)算量會(huì)隨之增大。

def nearest_neighbor_interpolation(img, new_h, new_w):
    """
    get nearest_neighbor_interpolation for image, can up or down scale image into any ratio
    param: img: input image, grady image, 1 channel, shape like [512, 512]
    param: new_h: new image height 
    param: new_w: new image width
    return a nearest_neighbor_interpolation up or down scale image
    """
    new_img = np.zeros([new_h, new_w])
    src_height, src_width = img.shape[:2]
    r = new_h / src_height
    l = new_w / src_width
    for i in range(new_h):
        for j in range(new_w):
            x0 = int(i / r)
            y0 = int(j / l)
            new_img[i, j] = img[x0, y0]
    return new_img
# 最近鄰插值法處理一通道的圖像
img = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0638(a)(lenna_RGB).tif', 0)

img_up = nearest_neighbor_interpolation(img, 1000, 1000)

plt.figure(figsize=(16, 8))
plt.subplot(121), plt.imshow(img, 'gray')
plt.subplot(122), plt.imshow(img_up, 'gray')
plt.tight_layout()
plt.show()
在這里插入圖片描述
# 最近鄰插值法處理RGB 3通過(guò)的圖像
img = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0638(a)(lenna_RGB).tif', -1)
img = img[..., ::-1]
img_up_1 = nearest_neighbor_interpolation(img[..., 0], 800, 800)
img_up_2 = nearest_neighbor_interpolation(img[..., 1], 800, 800)
img_up_3 = nearest_neighbor_interpolation(img[..., 2], 800, 800)
img_up = np.uint8(np.dstack((img_up_1, img_up_2, img_up_3)))

plt.figure(figsize=(16, 8))
plt.subplot(121), plt.imshow(img, 'gray')
plt.subplot(122), plt.imshow(img_up, 'gray')
plt.tight_layout()
plt.show()
在這里插入圖片描述

雙線(xiàn)性插值

又稱(chēng)為雙線(xiàn)性?xún)?nèi)挺。在數(shù)學(xué)上,雙線(xiàn)性插值是有兩個(gè)變量的插值函數(shù)的線(xiàn)性插值擴(kuò)展,其核心思想是在兩個(gè)方向分別進(jìn)行一次線(xiàn)性插值。

假設(shè)我們想得到未知函數(shù)f在點(diǎn) P=(x,y)的值,假設(shè)我們已知函數(shù)f在Q_{11} = (x_1, y_1),Q_{12}=(x_1, y_2), Q_{21} = (x_2, y_1),Q_{22} = (x_2, y_2)四個(gè)點(diǎn)的值。首先在x方向進(jìn)行線(xiàn)性插值,得到:

f(R_1) \approx \frac{x_2 - x}{x_2 - x_1} f(Q_{11}) + \frac{x-x_1}{x_2 - x_1} f(Q_{21}), where R_1 = (x, y_1)
f(R_2) \approx \frac{x_2 - x}{x_2 - x_1} f(Q_{12}) + \frac{x-x_1}{x_2 - x_1} f(Q_{22}), where R_2 = (x, y_2)

然后在y方向進(jìn)行線(xiàn)性插值,得到
f(P) \approx \frac{y_2 - y}{y_2 - y_1} f(R_{1}) + \frac{y-y_1}{y_2 - y_1} f(R_{2})

def bilinear_interpolation(img, new_h, new_w):
    """
    get nearest_neighbor_interpolation for image, can up or down scale image into any ratio
    param: img: input image, grady image, 1 channel, shape like [512, 512]
    param: new_h: new image height 
    param: new_w: new image width
    return a nearest_neighbor_interpolation up or down scale image
    """
    src_height, src_width = img.shape[:2]
    if new_h == src_height and new_w == src_width:
        return img.copy()
    
    new_img = np.zeros([new_h, new_w])
    for i in range(new_h):
        for j in range(new_w):
            # 首先要找到在原圖中對(duì)應(yīng)點(diǎn)的(x, y)
            x = (i+0.5) * src_height / new_h - 0.5
            y = (j+0.5) * src_width / new_w - 0.5
            
            # find the coordinates of the points which will be used to compute the interpolation
            src_x0 = int(np.floor(x))
            src_x1 = min(src_x0 + 1 , src_height - 1)
            src_y0 = int(np.floor(y))
            src_y1 = min(src_y0 + 1, src_width - 1)

            # calculate the interpolation
            temp0 = (src_x1 - x) * img[src_x0, src_y0] + (x - src_x0) * img[src_x1, src_y0]
            temp1 = (src_x1 - x) * img[src_x0, src_y1] + (x - src_x0) * img[src_x1, src_y1]
            new_img[i, j] = int((src_y1 - y) * temp0 + (y - src_y0) * temp1)
    return new_img
# 最近鄰插值法處理一通道的圖像
img = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0638(a)(lenna_RGB).tif', 0)

img_up = bilinear_interpolation(img, 800, 800)

plt.figure(figsize=(16, 8))
plt.subplot(121), plt.imshow(img, 'gray')
plt.subplot(122), plt.imshow(img_up, 'gray')
plt.tight_layout()
plt.show()
在這里插入圖片描述

雙三次內(nèi)插

雙三次插值的目的就是通過(guò)找到一種關(guān)系,或者說(shuō)系數(shù),可以把這16個(gè)像素對(duì)于P處像素值的影響因子找出來(lái),從而根據(jù)這個(gè)影響因子來(lái)獲得目標(biāo)圖像對(duì)應(yīng)點(diǎn)的像素值,達(dá)到圖像縮放的目的。

Bicubic基函數(shù)形式如下:
W(x) =\begin{cases} (a+2) |x|^3 - (a+3)|x|^2 + 1, & |x| \leq 1 \\a|x|^3 -5a|x|^2 + 8a|x| -4a, & 1 < |x| < 2 \\ 0, & \text{otherwise}\end{cases}

a=-1
a=-0.5才能完美的實(shí)現(xiàn)內(nèi)插

插值計(jì)算公式:
\sum_{i=0}^3 \sum_{j=0}^3 a_{ij}x^iy^i

def bicubic(x):
    """
    BiCubic primary fuction
    """
    x = abs(x)
    a = -0.5
    if x <= 1:
        return (a + 2) * (x**3) - (a + 3) * (x**2) + 1
    elif x < 2:
        return a * (x**3) - 5 * a * (x**2) +  (8 * a * x) - (4 * a)
    else:
        return 0
def bicubic_interpolation(img, new_h, new_w):
    src_height, src_width = img.shape[:2]
    new_img = np.zeros([new_h, new_w])
    for h in range(new_h):
        for w in range(new_w):
            src_x = h * (src_height / new_h)
            src_y = w * (src_width / new_w)
            
            x = int(np.floor(src_x))
            y = int(np.floor(src_y))
            
            u = src_x - x
            v = src_y - y
            temp = 0
            for i in range(-1, 2):
                for j in range(-1, 2):
                    if x + i < 0 or y + j < 0 or x + i >= src_height or y + j >= src_width:
                        continue
                    temp += img[x+i, y+j] * bicubic(i - u) * bicubic(j - v)
            new_img[h, w] = np.clip(temp, 0, 255)
    return new_img
# 最近鄰插值法處理一通道的圖像
img = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH06/Fig0638(a)(lenna_RGB).tif', 0)

img_up = bicubic_interpolation(img, 800, 800)
# img_up = cv2.resize(img, (800, 800), cv2.INTER_CUBIC)

plt.figure(figsize=(16, 8))
plt.subplot(121), plt.imshow(img, 'gray')
plt.subplot(122), plt.imshow(img_up, 'gray')
plt.tight_layout()
plt.show()
在這里插入圖片描述
# 先裁剪圖像,然后把裁剪的圖像縮小,再進(jìn)行最近鄰、雙線(xiàn)內(nèi)插、雙三次內(nèi)插放大,對(duì)比效果
img = cv2.imread('DIP_Figures/DIP3E_Original_Images_CH02/Fig0220(a)(chronometer 3692x2812  2pt25 inch 1250 dpi).tif', 0)

img = img[1100:3500, 200:2600]
img = cv2.resize(img, (200, 200), interpolation=cv2.INTER_CUBIC)

new_h, new_w = 2400, 2400
img_nearest = nearest_neighbor_interpolation(img, new_h, new_w)
img_bilinear = bilinear_interpolation(img, new_h, new_w)
img_bicubic = bicubic_interpolation(img, new_h, new_w)

plt.figure(figsize=(18, 6))
plt.subplot(131), plt.imshow(img_nearest, 'gray'), plt.title('Nearest'), #plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_bilinear, 'gray'), plt.title('Bilinear'), #plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_bicubic, 'gray'), plt.title('Bicubic'), #plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()
在這里插入圖片描述

放大圖像

def up_sample_2d(img):
    """
    up sample 2d image, if your image is RGB, you could up sample each channel, then use np.dstack to merge a RGB image
    param: input img: it's a 2d gray image
    return a 2x up sample image
    """
    height, width = img.shape[:2]
    temp = np.zeros([height*2, width*2])
    temp[::2, ::2] = img
    temp[1::2, 1::2] = img
    temp[0::2, 1::2] = img
    temp[1::2, 0::2] = img
    return temp
# up sample image using Numpy
img = np.random.random([12, 12])
up = up_sample_2d(img)
down = np.zeros([12, 12])
down = up[::2, ::2]
plt.figure(figsize=(15, 5))
plt.subplot(1,3,1), plt.imshow(img),# plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2), plt.imshow(up),# plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3), plt.imshow(down),# plt.xticks([]), plt.yticks([])
plt.show()
在這里插入圖片描述
# 等比2倍放大
img_ori = cv2.imread("DIP_Figures/DIP3E_Original_Images_CH06/Fig0638(a)(lenna_RGB).tif")
img_ori = img_ori[:, :, ::-1]

temp = []
for i in range(img_ori.shape[-1]):
    temp.append(up_sample_2d(img_ori[:, :, i]))
up1 = np.uint8(np.dstack(temp))

temp = []
for i in range(up1.shape[-1]):
    temp.append(up_sample_2d(up1[:, :, i]))
up2 = np.uint8(np.dstack(temp))

plt.figure(figsize=(21, 7))
plt.subplot(1,3,1), plt.imshow(img_ori), plt.title("Original"),# plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2), plt.imshow(up1), plt.title("2X"),# plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3), plt.imshow(up2), plt.title("4X"),# plt.xticks([]), plt.yticks([])
plt.tight_layout()
plt.show()
在這里插入圖片描述
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容