上采樣和下采樣
什么是上采樣和下采樣?
? 縮小圖像(或稱為下采樣(subsampled)或降采樣(downsampled))的主要目的有
兩個:1、使得圖像符合顯示區域的大小;2、生成對應圖像的縮略圖。
? 放大圖像(或稱為上采樣(upsampling)或圖像插值(interpolating))的主要目的
是放大原圖像,從而可以顯示在更高分辨率的顯示設備上。
注意:
如果想放大一個圖片或者一個圖片,應該想到,當圖片放大或縮小的時候,會增加或者減少像素點。比如說原來200×200的圖片,要是想變成400×400的圖片,就要多出3倍的像素點需要添加。而如何添加這些像素點就是問題的核心
原理:
上采樣原理:內插值 (增加像素點)
下采樣原理:(M/s) * (N/s) (等比例減少像素點)
最鄰近插值The nearest interpolation
思想:
最鄰近插值,思想就是他的名字,找插入像素點位置的最鄰近的像素點,將其作為自己的像素值插入。
如圖:
設i+u, j+v (i, j為正整數, u, v為大于零小于1的小數,下同)為待求象素坐標,則待求象素
灰度的值 f(i+u, j+v) 如下圖所示:
如果在A區域插入像素點,該點的像素值就與(i,j)的像素值相同,同理,在B區域插入的像素點就與(i+1,j)的像素值相同。
算法實現:
import cv2
import numpy as np"""
最鄰近插值The nearest interpolation實現
"""def function(img,aim_height,aim_width):height,width,channels = img.shape #獲得原圖像的長寬和維度empty_img = np.zeros((aim_height,aim_width,channels),np.uint8) #新建全0圖像transform_h = aim_height/height #找到長的放大/縮小倍數transform_w = aim_width/width #找到寬的放大/縮小倍數for i in range(aim_height):for j in range(aim_width):x = int(i/transform_h) #找到最近鄰點(這個點必須取整)y = int(j/transform_w)empty_img[i,j]=img[x,y] #將最近鄰點的數值賦予新圖像return empty_imgimg = cv2.imread("lenna.png")
transform_picture = function(img,800,800) #800,800是新圖像大小
cv2.imshow("transform picture",transform_picture)
cv2.waitKey(0)
實現結果:
雙線性插值:
單線性插值:
要想知道什么是雙線性插值,我們先來研究單線性插值:
如圖所示,我們想要求(x,y)的y坐標,現在已知:x,(x0,y0),(x1,y1),該怎么求?
通過等比例的方法:可以獲得如下推論
最后的結果就是紅框部分公式
雙線性插值:
這時候我們再看雙線性插值:
我們將其拆解為X方向和Y方向的單線性插值。
通過Q11和Q21能夠獲得R1的值,再通過Q12和Q22獲得R2的值,最后通過R1和R2推得P的值。
具體推理過程如下:
由于圖像雙線性插值只會用相鄰的4個點,因此上述公式的分母都是1。
幾何中心對齊:
目前還存在有問題:
坐標系的選擇問題:
如果源圖像和目標圖像的原點(0,0)均選擇左上角,然后根據插值公式計算目標圖像每點像素,假設你需要將一幅5x5的圖像縮小成3x3,那么源圖像和目標圖像各個像素之間的對應關系如下:
也就是說,圖像當以左上角為對齊點來看,會使偏右偏下的原像素點的價值變低,新插值點受偏左偏上的原像素點影響更大。
那么,讓坐標加1或者選擇右下角為原點怎么樣呢?很不幸,還是一樣的效果,不過這次得到的圖像將偏右偏下。
最好的方法就是,兩個圖像的幾何中心重合,并且目標圖像的每個像素之間都是等間隔的,并且都和兩邊有一定的邊距。
公式如下:
這里老師進行了公式的推導:
算法實現:
import cv2
import numpy as np"""
雙線性插值實現
"""def bilinear_interpolation(img,out_dim):input_h,input_w,channels = img.shapeout_h,out_w = out_dim[0],out_dim[1]if input_h == out_h and input_w == out_w: #若輸出圖像和輸入圖像要求相等,則輸出原圖像return img.copy()empty_img = np.zeros((out_h,out_w,channels),np.uint8)scale_x, scale_y = float(out_h / input_h),float(out_w / input_w)for i in range(channels):for dst_y in range(out_h):for dst_x in range(out_w):#幾何中心對齊src_x = (dst_x + 0.5) / scale_x - 0.5src_y = (dst_y + 0.5) / scale_y - 0.5#找到插值周圍點src_x0 = int(np.floor(src_x))src_x1 = min(src_x0 + 1, input_w - 1)src_y0 = int(np.floor(src_y))src_y1 = min(src_y0 + 1, input_h - 1)#雙線性插值公式計算temp0 = (src_x1 - src_x) * img[src_y0, src_x0, i] + (src_x - src_x0) * img[src_y0, src_x1, i]temp1 = (src_x1 - src_x) * img[src_y1, src_x0, i] + (src_x - src_x0) * img[src_y1, src_x1, i]empty_img[dst_y, dst_x, i] = int((src_y1 - src_y) * temp0 + (src_y - src_y0) * temp1)return empty_imgif __name__ == '__main__':img = cv2.imread('lenna.png')transform_picture = bilinear_interpolation(img,(800,800))cv2.imshow('transform picture',transform_picture)cv2.waitKey()
實現結果:
最鄰近插值與雙線性插值的對比:
雙線性差值法的計算比最鄰近插值法復雜,計算量較大,但沒有灰度不連續的缺點,圖像看起來更光滑。
而最鄰近插值更快,更簡單。