一文講解圖像插值算法原理!附Python實現

2021-02-15 AI數據派

本文介紹了最近鄰插值法、雙線性插值法和三次樣條插值法的原理,並以圖像縮放例,對原理進行了C++及Python實現。在圖像處理中,幾何變換是將一幅圖像映射到另外一幅圖像內的操作,可以大概分為放縮、翻轉、仿射(平移、旋轉)、透視、重映射幾部分。在幾何變換時,無法給有些像素點直接賦值,例如,將圖像放大兩倍,必然會多出一些無法被直接映射的像素點,對於這些像素點,通過插值決定它們的值。且不同插值方式的結果不同。在一幅輸入圖像[u,v]中,灰度值僅在整數位置上有定義。然而,輸出圖像的坐標映射回原圖像後,一般為非整數的坐標。所以輸出圖像[x,y]的灰度值,一般由非整數坐標來決定,非整數坐標的像素值,就需要插值算法來進行處理。常見的插值算法有最近鄰插值、雙線性插值和三次樣條插值。本文目標

了解插值算法與常見幾何變換之間的關係

理解插值算法的原理

掌握OpenCV框架下插值算法API的使用

插值算法原理介紹

近鄰插值算法

1.原理簡介

將目標圖像中的點,對應到原圖像中後,找到最相鄰的整數坐標點的像素值,作為該點的像素值輸出。

如上圖所示,目標圖像中的某點投影到原圖像中的位置為點P,與P距離最近的點為Q11,此時易知,f(P)=f(Q11)。

2.例子說明

如圖所示:

將一幅3*3圖像放大到4*4,用f(x , y)表示原圖像,h(x ,y)表示目標圖像,我們有如下公式:

3.缺點

由最鄰近插值法,放大後的圖像有很嚴重的馬賽克,會出現明顯的塊狀效應;縮小後的圖像有很嚴重的失真。

這是一種最基本、最簡單的圖像縮放方式。變換後的每個像素點的像素值,只由原圖像中的一個像素點確定。例如上面,點(0,0.75)的像素只由(0,1)確定,這樣的效果顯然不好。點(0,0.75)的像素不止和(0,1)有關,和(0,0)也有關,只是(0,1)的影響更大。如果可以用附近的幾個像素點按權重分配,共同確定目標圖像某點的像素,效果會更好。下面的雙線性插值就解決了這個問題。

雙線性插值算法

1.線性插值

在講雙線性插值之前先了解一下線性插值。線性插值:使用連接兩個已知量的直線來確定在這兩個已知量之間的一個未知量的值。線性插值形式:

如下圖所示:

線性插值多項式:

其實,即使x不在x0到x1之間,這個公式也是成立的。在這種情況下,這種方法叫作線性外插。

線性插值的誤差:線性插值其實就是拉格朗日插值有2個結點時的情況。插值餘項為:

從插值餘項可以看出,隨著二階導數的增大,線性插值的誤差增大。即函數的曲率越大,線性插值近似的誤差也越大。

舉個例子。下圖中,左邊為原圖像,拉伸後,理想的輸出圖像的像素分布應該為綠色箭頭指向的,但是按照線性插值,會得到紅色箭頭指向的結果。

2.雙線性插值

雙線性插值形式:

雙線性插值是線性插值在二維時的推廣,在兩個方向上共做了三次線性插值。定義了一個雙曲拋物面與四個已知點擬合。

具體操作為在X方向上進行兩次線性插值計算,然後在Y方向上進行一次插值計算。如下圖所示:

首先,f(x,y)為二元函數,假設我們知道f(x0,y0),f(x1,y1),f(x0,y1),f(x1,y0)四個點的值。這四個點確定一個矩形,我們希望通過插值得到矩形內任意點的函數值。

先在x方向上進行兩次線性插值,得到:

再在y方向上進行一次線性插值,得到:

綜合起來,就是雙線性插值的結果:

如果選擇一個坐標系統,使f(x)已知的四個點的坐標分別為(0,0),(0,1),(1,0),(1,1),那麼確定一個單位正方形,四個點分別為正方形的四個頂點:

3.原圖像和目標圖像的幾何中心對稱

在計算目標圖像中,對應原圖像的虛擬坐標點時,一般的變換是:

這種變換下,原圖像的有些點沒有參與計算。舉個例子,把9∗9的原圖像縮小成3∗3,原圖像的原點(0,0)和目標圖像的原點(0,0)都為左上角,目標圖像右上角的坐標為(0,2),對應原圖像的坐標為(0∗(9/3),2∗(9/3))=(0,6)。目標圖像右邊已經沒有點了,(0,6)右邊的像素點也就用不到了。

原圖像和目標圖像的像素之間的對應關係如下:

從圖片可以看出,只有圈出來的紅色部分參與運算了。目標圖像的每個像素點的灰度值相對於原圖像偏左上方,右下角的元素實際上沒有參與運算。

為了讓原圖像和目標圖像的中心對齊,我們規定另外一種變換方式:

就是在原來的變換後面加了調節因子:

0.5(src_width/dst_width−1)

這種變換下,目標圖像的中心點(1,1),對應了原圖像的中心點(4,4),兩個圖像的幾何中心重合,能充分利用原圖像的點,並且目標圖像的每個像素點之間都是等間隔的,也都和兩邊有一定的邊距。實際上,在openCv中也是這種變換方式。

4.cv.resize()的計算過程

對於縮小圖像,目標圖像中每個點都能找到原圖像中包圍它的四個臨近點,每個點都進行雙線性插值即可。

對於放大圖像,邊界附近的點經過坐標變換可能超出了原圖像的範圍。舉個例子,把3∗3的原圖像放大成4∗4。

中間的點都能在原圖像中找到包圍它的四個臨近點,做雙線性插值即可。

例如,目標圖像中的點(1,3),對應原圖像的點為(0.625,2.125),原圖像的縱坐標最大為2,找不到包圍(0.625,2.125)四個點,所以用它最鄰近的兩個點(0,2)和(1,2)做線性插值(外插),得到目標圖像中(1,3)的像素值。

例如,目標圖像右上角的頂點(0,3),對於原圖像的點為(0,2.125),直接用原圖像右上角的頂點(0,2)作為它的值即可。

計算過程:

用h(x,y)表示目標圖像,f(x,y)表示原圖像

可以用代碼舉例子測試:

import cv2import numpy as npsrc = np.array([[56,23,15],[65,32,78],[12,45,62]],dtype=np.uint8)print(src)dst = cv2.resize(src,dsize=(4,4),interpolation=cv2.INTER_LINEAR)print(dst)

三次樣條插值算法

給定n+1個點,a=x_0<x_1<...<x_n=b,以及他們的函數值f(x_i),i=0,1,2,...n上,確定一個三次多項式:

每個三次多項式中有四個未知參數,有n個區間,n個多項式,共4n個未知參數。我們知道「n個未知數需要n個已知條件確定唯一解」,所以要確定這4n個未知參數,共需要4n個已知條件。

每個三次多項式滿足如下條件:

以上共4n−2個條件,還差2個條件,由如下三種邊界條件確定:

4n個條件有了,就可以確定每個區間上的三次多項式。

對於每個區間內的點,就可以用Si(x)得到插值結果。三次樣條插值具有良好的收斂性,穩定性和光滑性,優點明顯,是非常重要的插值工具。

這裡主要了解三次樣條插值的作用,具體的推導過程比較繁瑣,想了解的可以查閱資料。

兩種映射方法

向前映射和向後映射都是將一個圖像經過幾何變換得到另一個圖像的過程,它們的目的都是得到目標圖像的像素,只是方式不同。

向前映射

圖像變換的本質是將像素點的坐標通過某一種函數關係,映射到另外的位置。

向前映射的過程可以分解為兩步:坐標變換+分配像素值

向前映射的坐標變換:由原圖像坐標推算該像素在目標圖像的位置。

例如,我們知道原圖像的某個像素點的坐標(x,y),變換後在新圖像的坐標為(x′,y′),變換後的坐標一般為非整數的,而非整數的坐標是沒意義的,所以將這個點的像素按權重分配給周圍四個像素點。對於變換後坐標仍為整數的點,直接把其像素值分配給目標圖像中對應的點即可。將原圖像的所有像素點都進行這種坐標變換和分配像素值,就得到了新圖像。

所以,新圖像的每個像素點的像素值,都是由它周圍的非整數坐標的點的像素分配給它併疊加得到的(或者直接等於某個整數坐標點的像素值)。由於這個分配、疊加的特性,向前映射法有時也叫像素移交映射。

對於向前映射,雖然原圖像中的每個點分配係數之和為1。但目標圖像上每個點的像素值是多個分配值疊加而成的,所以不能保證所有分配到其上的權重之和為1。因此必須記錄下所有分配到其上的權重並累加起來,最後利用累加權重進行歸一化,才能得到正確的插值結果。所以,確定目標圖像某一點的像素值,需要遍歷原圖像的所有像素值,進行坐標變換和分配像素值。這是向前映射法的缺點。

向後映射

向後映射的過程可以分解為兩步:坐標變換+插值。

向後映射的坐標變換:由輸出圖像坐標反過來推算該像素在原圖像的位置

前面說的幾種插值方式,就是向後映射的例子。是由目標圖像的坐標算出在原圖像的坐標,再確定它的像素值由原圖像的哪幾個點按權重分配得到。然後進行插值操作,得到該點的像素值。某一點的像素值進行一次操作就可以得到,不需要遍歷全部像素點。向後映射法也叫像素填充算法。向後映射法解決了漏點的問題,出現了馬賽克。

動手實現

c++實現

1. 函數原型

void cv::resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )

2. 插值方式

註:目標圖像大小可以通過「參數dsize」和「參數fx和fy」兩種方式確定。兩種參數不能同時為0。

dsize的第一個參數為列數,第二個參數為行數,都為整數。若指定了dsize的值,無論是否指定了fx和fy的值,都由參數dsize來決定目標圖像的大小。

如果dsize的值None,目標圖像的大小通過fx和fy確定。fx為列數縮放的倍數,fy為行數縮放的倍數。

3.代碼實現

#include <opencv2/opencv.hpp>#include <iostream>
using namespace cv;using namespace std;
int main(int argc, char* argv[]){ Mat img = imread(""C:/Users/94890/Desktop/picture/luelue.jpg""); if (img.empty()) { cout << "無法讀取圖像" << endl; return 0; }
int height = img.rows;//原圖像行數 int width = img.cols;//原圖像列數 // 縮小圖像,比例為(0.2, 0.2),行列數必須為整數 Size dsize = Size(round(0.2 * width), round(0.2 * height)); Mat shrink; //使用雙線性插值 resize(img, shrink, dsize, 0, 0, INTER_LINEAR);
// 在縮小圖像的基礎上,放大圖像,比例為(1.5, 1.5) float fx = 1.5; float fy = 1.5; Mat enlarge1, enlarge2; resize(shrink, enlarge1, Size(), fx, fy, INTER_NEAREST); resize(shrink, enlarge2, Size(), fx, fy, INTER_LINEAR);
// 顯示 imshow("src", img); imshow("shrink", shrink); imshow("INTER_NEAREST", enlarge1); imshow("INTER_LINEAR", enlarge2);
//保存圖像 imwrite("C:/Users/94890/Desktop/picture/shrink2.jpg",shrink); imwrite("C:/Users/94890/Desktop/picture/INTER_NEAREST2.jpg", enlarge1); imwrite("C:/Users/94890/Desktop/picture/INTER_LINEAR2.jpg", enlarge2); waitKey(0); return 0;}

原圖像

0.2倍縮小,雙線性插值

縮小後的圖像1.5倍放大,最近鄰插值

縮小後的圖像1.5倍放大,雙線性插值

python實現

1. 函數原型

#dst為輸出圖像,類型與原圖像相同dst = cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])

2. 插值方式

通常,縮小圖像使用區域插值(cv.INTER_AREA),放大圖像使用三次樣條插值(cv.INTER_CUBIC)和雙線性插值(cv.INTER_LINEAR)。三次樣條插值方式速度較慢,雙線性插值方式較快且效果也不錯。

3. 代碼實現

import cv2
if __name__ == "__main__": img = cv2.imread('C:/Users/94890/Desktop/smile.jpg', cv2.IMREAD_UNCHANGED) print('Original shape : ', img.shape)#img.shape屬性種第一個值對應行數,第二個值對應列數 width = int(img.shape[1] * 0.3)#列數必須是整數 height = int(img.shape[0] * 0.3)#行數必須是整數 dsize = (width, height)#dsize屬性值第一個數對應列數,第二個數對應行數 # resize image resized = cv2.resize(img, dsize, interpolation=cv2.INTER_LINEAR)#雙線性插值方式 print('Resized shape : ', resized.shape)
fx = 1.5#列數變為原來的1.5倍 fy = 1.5#行數變為原來的1.5倍 resized1 = cv2.resize(resized, dsize=None, fx=fx, fy=fy, interpolation=cv2.INTER_NEAREST)#最鄰近插值 resized2 = cv2.resize(resized, dsize=None, fx=fx, fy=fy, interpolation=cv2.INTER_LINEAR)#雙線性插值 print('Resized1 shape : ', resized1.shape)
#顯示圖像 cv2.imshow("Resized image", resized) cv2.imshow("INTER_NEAREST image", resized1) cv2.imshow("INTER_LINEAR image", resized2) #保存圖像 cv2.imwrite("C:/Users/94890/Desktop/Resized_image.jpg", resized) cv2.imwrite("C:/Users/94890/Desktop/INTER_NEAREST_image.jpg", resized1) cv2.imwrite("C:/Users/94890/Desktop/INTER_LINEAR_image.jpg", resized2) cv2.waitKey(0) cv2.destroyAllWindows()

原圖像

0.3倍縮小,雙線性插值

縮小後的圖像1.5倍放大,最近鄰插值

縮小後的圖像1.5倍放大,雙線性插值

——END——

相關焦點

  • 圖像變清楚了!超實用Real-ESRGAN超解析度網絡算法
    T^T推薦你來嘗試一下圖像超解析度重建算法Real-ESRGAN。這個算法是2021年7月由騰訊ARC實驗室發表,可以大大提高圖像解析度。不需要CUDA,也不需要GPU,下載exe即可運行。大家可以先看一下我自己的實驗效果,解析度提高了很多,細節也更加豐富:
  • python圖像處理-濾鏡處理
    今天我們就嘗試用python的PIL庫對圖片做一些濾鏡處理,希望可以帶給你一些想法。打開原始圖片這裡我用的是一張貓的圖片,先打開原圖查看。進行模糊濾鏡處理PIL中的ImageFilter模塊中已經有很多集成好的濾鏡方法,這裡我們直接調用,原理下一篇會詳細講解並自己嘗試者去實現同樣的效果。
  • 人臉識別系統實現與原理
    本書全面、系統地介紹「刷臉」背後的技術,包括人臉檢測、人臉識別、人臉檢索相關的算法原理和實現技術。本書中講解的算法具有高度的可操作性和實用性。通過學習本書,研究人員、工程師能夠在3~5個月內,系統了解、掌握人臉檢測、人臉識別、人臉檢索相關的原理和技術。本書內容新穎、層次清晰,適合高校教師、研究人員、研究生、高年級本科生、人臉識別愛好者使用。
  • python圖像處理-gif動圖
    利用這個原理只要控制好時間,就能夠將一系列圖片組合在一起形成動圖了,更長的可以形成動畫等等。比如下面的圖片,每一張我們稱為一幀,我們看到的動態效果,其實就好像在不斷翻圖片給你看。python圖像處理-1
  • 藉助攝像頭在Python中實現人臉檢測
    工作中的攝像頭一隻代碼實現我們從GitHub上的倉庫中直接獲得代碼。frame) if cv2.waitKey(1) & 0xFF == ord('q'): break# When everything is done, release the capturevideo_capture.release()cv2.destroyAllWindows()現在我們來分段講解
  • OpenCV 實戰:3 步實現圖像降噪
    本文將展示如何通過三個簡單的步驟來實現降噪
  • python代碼實現OpenCV 輪廓近似原理
    Contour approximation 使用Ramer - Douglas - Peucker (RDP)算法,旨在通過減少給定閾值的頂點來簡化折線。通俗地說,我們採用一條曲線並減少其頂點數量,同時保留其大部分形狀。我將在這裡給出算法的粗略概念。給定曲線的起點和終點,算法將首先找到距離連接兩個參考點的線最大距離的頂點。
  • 場地特徵周期Tg插值算法的討論及應用
    場地特徵周期Tg插值算法的討論及應用抗震設計是結構師必須了解掌握的知識、也是實際工作中必然面對的內容。
  • Python也能成為畢卡索?我用Python給小姐姐畫了幅油畫
    小編不僅給大家展示結果,還要用白話給大家解釋背後的原理,嘿嘿!一起來看看吧!01.程序原理下圖展示的是圖片轉為油畫的原理。得到數值2和它出現的次數6之後,就將模板對應到原圖中去;最後,統計對應位置的原圖像素值,並將其求均值,而均值作為像素值被填充到油畫圖當中;經常上面幾步,就實現了圖片轉油畫圖的算法03.程序實現那我們接下來看一下程序是如何實現的吧
  • python利用opencv實現證件照換底
    1.強大的opencv庫說到圖像處理,不得不提opencv庫。它是一個跨平臺的計算機視覺庫,可以運行在不同作業系統上,它由一些列c函數和少量c++函數組成,並提供python,matlab等語言的接口,實現了圖像處理和計算機視覺方面的很多通用算法。
  • 機器視覺常用算法原理及 opencv 實現源碼
    因此,需要關注圖像的生成過程。圖像的生成包括兩個關鍵問題,一是物理世界中點與圖像中點的對應,二是圖像中點的亮度。1.1相機模型相機成像的小孔模型: 但這一理想透鏡只會對特定距離的物體才會準確聚焦,物體前後移動生成的光斑小於傳感器解析度的範圍被稱為成像域深度,也稱景深。透鏡直徑越大,成像域深度就越小,因此在調節視覺系統時越有可能造成的聚焦誤差。 任何簡單透鏡都會產生缺陷和像差,但根據理想簡單透鏡的成像原理和光路的可逆性,將一塊透鏡放到另一塊透鏡的焦距附近,可以提高成像質量,因此將多個透鏡沿光軸仔細排列成為組合透鏡。
  • Python-Basemap核密度空間插值可視化繪製
    上一篇的推文我們使用geopandas+plotnine 完美繪製高斯核密度插值的空間可視化結果,並提供了一個簡單高效的裁剪方法,具體內容點擊連結:Python-plotnine 核密度空間插值可視化繪製Python-plotnine 核密度空間插值可視化繪製。
  • AI 圖像智能修復老照片,效果驚豔到我了!| 附代碼
    圖像識別技術本身的原理並不複雜,信息的處理是這一技術的關鍵點所在。M.Bertalmio首次提出許多圖像修復能被簡化為一個數學表達式,利用計算機能自動加以實現。圖像修復現已是計算機圖形學和計算機視覺中的一個研究熱點,在文物保護、影視特技製作、虛擬實境、多餘物體剔除(如視頻圖像中刪除部分人物、文字、小標題等)等方面有著重大的應用價值。
  • golang 調用 python 實戰路徑規劃之 A* 算法
    算法介紹A*(念做:A Star)算法是一種很常用的路徑查找和圖形遍歷算法。它有較好的性能和準確度。本文在講解算法的同時也會提供Python語言的代碼實現,並會藉助matplotlib庫動態的展示算法的運算過程。
  • 協同過濾的原理及Python實現
    本文就ALS的基本原理進行講解,並手把手、肩並肩地帶您實現這一算法。完整實現代碼請參考:https:https:1. 原理篇我們用人話而不是大段的數學公式來講講ALS是怎麼一回事。1.1 你聽說過推薦算法麼假如我是豆瓣的CEO,很多豆瓣的用戶在豆瓣電影上都會對電影進行評分。那麼根據這個評分數據,我們有可能知道這些用戶除了自己評過分的電影之外還喜歡或討厭哪些電影嗎?這就是一個典型的推薦問題,解決這一類問題的算法被稱為推薦算法。
  • 揭秘用算法「脫掉」女性衣服的DeepNude幕後技術
    概述:本文從用算法「脫掉」女性衣服的DeepNude的不良應用及其消亡的現象為引子,介紹了其應用的2種python技術。支持生成Nude圖像的pix2pix算法,和支持對python程序打包的PyOxidizer庫做了介紹。旨在拋磚引玉,對讀者的python技術提高有幫助。
  • 一文概覽密碼學發展史、基本原理與常見算法
    小編:記得關注哦 來源:以太坊愛好者 原文標題:一文概覽密碼學發展史、基本原理與常見算法 原文標題:《科普 | 從數學到物理學:加密算法簡介》 撰文:George Moraetes
  • 25本《Python+TensorFlow機器學習實戰》免費包郵到家!
    書中的每個知識點都通過實例進行通俗易懂的講解,便於讀者輕鬆掌握有關TensorFlow 開發的內容和技巧,並能夠得心應手地使用TensorFlow 進行開發。  本書內容共分為11 章,首先介紹TensorFlow 的基本知識,通過實例逐步深入地講解線性回歸、支持向量機、神經網絡算法和無監督學習等常見的機器學習算法模型。