1. 程式人生 > >影象演算法的基礎知識(雙線性插值,協方差矩陣,矩陣的特徵值、特徵向量)

影象演算法的基礎知識(雙線性插值,協方差矩陣,矩陣的特徵值、特徵向量)

0. 前言

MATLAB或者OpenCV裡有很多封裝好的函式,我們可以使用一行程式碼直接呼叫並得到處理結果。然而當問到具體是怎麼實現的時候,卻總是一臉懵逼,答不上來。前兩天參加一個演算法工程師的筆試題,其中就考到了這幾點,感到非常汗顏!趕緊補習!

1. 雙線性插值

在影象處理中,我們有時需要改變影象的尺寸,放大或者縮小。線性插值則是這類操作的關鍵演算法。不管是放大還是縮小操作,其實都是一個畫素對映的處理。如下圖從小圖到大圖的對映,以及從大圖到小圖的對映。

影象來源:https://www.cnblogs.com/sdxk/p/4056223.html

然而,這兩種操作都有一定的缺點。對於把小圖放大的操作,因為小圖中的畫素點到大圖中的畫素點不是滿射,因此大圖中的點不能完全有畫素值;對於將大圖縮小的操作,大圖中的點逆對映為小圖中的點時,得到的畫素座標值可能不是整數。一種解決辦法是採用最近鄰方法,即將得到的座標值與相鄰的原影象中的畫素座標值比較,取離得最近的座標值對應的畫素值作為縮放後的影象對應的座標值的畫素值,但是這種辦法可能導致影象失真,因此可以採用雙線性差值的辦法來進行計算相應的畫素值。

 

對於圖中紅色的四個點(Q11,Q12,Q21,Q22)為源影象中存在的點,需要求在目標影象的插值(綠色點P)的座標對應的畫素值。

首先在X軸進行插值,R1,R2是兩個插值過程中過渡的點.

然後在 y 方向進行線性插值,得到:

 

這樣就得到所要的結果 f \left( x, y \right),

講一個具體的例子:

比如源影象是尺寸是(100,150),現在要縮小尺寸0.6倍,即目標影象的尺寸是(60,90),則求目標影象在座標為P[10,4]的點的畫素值怎麼求呢?

假設源影象是ori_im,目標影象是tar_im,tra_im[10,4]表示在行列分別是10和4時候影象的畫素值。

此時,x=10/0.6=16.67,  y=4/0.6=6.67,而x1=16,x2=17,y1=6,y2=7, (x1,y1), (x1,y2), (x2,y1), (x2,y2)是在源影象中最接近tra_im[10,4]的4個點。

tra_im[10,4]=ori_im[x1,y1]*(17-16.67)*(7-6.67)+ori_im[x2,y1]*(16.67-16)*(6.67-6)+ori_im[x2,y1]*(17-16.67)*(6.67-6)+ori_im[x2,y2]*(16.67-16)*(6.67-6)

帶入4個點在源影象中對應的畫素值即可得到縮小後圖像的畫素值。

下面我用python實現了這個雙線性插值,並和python中自帶的skimage函式裡封裝的resize進行了效果對比,感覺效果差不多。(程式碼可能有點冗餘)

我的程式碼:

# -*- coding: utf-8 -*-
# Author: lmh
# Time: 2018.10.22
from skimage import transform
import matplotlib.pyplot as plt
import matplotlib.image as mping
import numpy as np
def chazhi(x,y,im):
#x,y分別是縮放或者放大後對應源影象的浮點座標位置,im是源影象,返回目標影象根據插值計算得到的畫素值
    x1,y1=int(x),int(y) #x1,x2,x3,x4分別是插值座標對應在源影象上下左右最近的點的座標
    x2,y2=x1+1,y1+1
    pixel11,pixel21,pixel12,pixel22=im[x1-1,y1-1],im[x2-1,y1-1],im[x2-1,y1-1],im[x2-1,y2-1]
	#以下是根據雙線性插值的公式求得的目標影象的該位置的畫素值
    new_pixel=(x2-x)*(y2-y)*pixel11+(x-x1)*(y2-y)*pixel21+(x2-x)*(y-y1)*pixel12+(x-x1)*(y-y1)*pixel22
    return new_pixel


im=mping.imread('C:\\Users\\shou\\Desktop\\photo.png')
im11=im
scale=0.4 #縮小程度
row,col=int(im.shape[0]*scale),int(im.shape[1]*scale)
im_sml=np.zeros([row,col,3])
for k in range(3):
    im1 = im[:, :, k]
    for i in range(row):
        for j in range(col):
            value=chazhi(i/scale,j/scale,im1)#3通道影象逐畫素計算縮小或者放大後的新畫素值
            im_sml[i][j][k]=value

im_narrow=im_sml

scale=1.7  #擴大程度
row, col = int(im.shape[0] * scale), int(im.shape[1] * scale)
im_sml = np.zeros([row, col, 3])
for k in range(3):
    im1 = im[:, :, k]
    for i in range(row):
        for j in range(col):
            value = chazhi(i / scale, j / scale, im1)
            im_sml[i][j][k] = value

im_enlarge=im_sml
python_narrow=transform.resize(im, (175, 145)) #使用skimage自帶函式resize影象,也可以直接寫像上面一樣寫比例(0.4,1.7)
python_enlarge=transform.resize(im, (746, 617))#為了對比,兩種方法特意放大和縮小一樣大小

plt.figure()
plt.subplot(151)
plt.imshow(im11,plt.cm.gray)
plt.title('Original')
# plt.axis('off')

plt.subplot(152)
plt.imshow(im_narrow,plt.cm.gray)
plt.title('my_narrow')
# plt.axis('off')

plt.subplot(153)
plt.imshow(python_narrow,plt.cm.gray)
plt.title('skimage_narrow')
# plt.axis('off')

plt.subplot(154)
plt.imshow(im_enlarge,plt.cm.gray)
plt.title('my_enlarge')
# plt.axis('off')

plt.subplot(1,5,5)
plt.imshow(python_enlarge,plt.cm.gray)
plt.title('skimage_enlarge')
# plt.axis('off')
plt.savefig('image_comp.png')

skimage的原始碼:

 

 效果圖對比(原圖[439,363,3];縮小0.4倍後為[175,145,3];放大1.7倍後為[746,617,3])

在部落格:https://www.cnblogs.com/sdxk/p/4056223.html中,博主講根據雙線性插值的定義,我們自己寫的函式影象處理的結果會因為座標系的原因,而和MATLAB,OpenCV結果的完全不同。最好的解決方法就是,兩個影象的幾何中心重合,並且目標影象的每個畫素之間都是等間隔的,並且都和兩邊有一定的邊距,這也是matlab和openCV的做法。並給出了以下解決方法,其中m,n 是源影象尺寸;a,b是目標函式尺寸。然而我沒有這些修改,與python封裝的方法也沒發現太大區別。

int x=(i+0.5)*m/a-0.5

int y=(j+0.5)*n/b-0.5

代替

int x=i*m/a

int y=j*n/b

補:經過檢視Skimge的resize原始碼,發現確實裡面添加了上面所說的策略(下面的程式碼),那麼為什麼我寫的雙線性程式碼沒有這樣做,影象的結果確和這樣做的幾乎一樣?更加疑惑了

# take into account that 0th pixel is at position (0.5, 0.5)
dst_corners[:, 0] = col_scale * (src_corners[:, 0] + 0.5) - 0.5
dst_corners[:, 1] = row_scale * (src_corners[:, 1] + 0.5) - 0.5

2. 協方差矩陣

 

 

 

3. 求矩陣的特徵值,特徵向量,以及主成分