Task01 圖像插值算法

anaconda換源鏈接

Datawhale 計算機視覺基礎-圖像處理(上)-Task01 OpenCV框架與圖像插值算法

1.1 簡介

  在圖像處理中,平移變換、旋轉變換以及放縮變換是一些基礎且常用的操作。這些幾何變換並不改變圖象的象素值,只是在圖象平面上進行象素的重新排列。在一幅輸入圖象[uv][u,v]中,灰度值僅在整數位置上有定義。然而,輸出圖象[x,y]的灰度值一般由處在非整數座標上的uv(u,v)值來決定。這就需要插值算法來進行處理,常見的插值算法有最近鄰插值、雙線性插值和三次樣條插值。

1.2 學習目標

  • 瞭解插值算法與常見幾何變換之間的關係
  • 理解插值算法的原理
  • 掌握OpenCV框架下插值算法API的使用

1.2.1 最近鄰插值算法原理

  最近鄰插值,是指將目標圖像中的點,對應到源圖像中後,找到最相鄰的整數點,作爲插值後的輸出。

  如上圖所示,目標圖像中的某點投影到原圖像中的位置爲點P,此時易知,f(P)=f(Q11)f(P) = f(Q11).

一個例子:

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

f(dstX,dstY)=h(dstXsrcWidthdstWidth,dstYsrcHeightdstHeight) \begin{array}{c} f(dst_{X}, dst_{Y}) = h(\frac{dst_{X}src_{Width}} {dst_{Width}}, \frac{dst_{Y}src_{Height}} {dst_{Height}}) \end{array}

f(0,0)=h(0,0)f(0,1)=h(0,0.75)=h(0,1)f(0,2)=h(0,1.50)=h(0,2)f(0,3)=h(0,2.25)=h(0,2)... \begin{array}{c} f(0,0)=h(0,0) \\ f(0,1)=h(0,0.75)=h(0,1) \\ f(0,2)=h(0,1.50)=h(0,2) \\ f(0,3)=h(0,2.25)=h(0,2) \\ ...\\ \end{array}

缺點:
用該方法作放大處理時,在圖象中可能出現明顯的塊狀效應

1.2.2 雙線性插值

  在講雙線性插值之前先看以一下線性插值,線性插值多項式爲:

f(x)=a1x+a0 f(x)=a_{1} x+a_{0}

y=y0+(xx0)y1y0x1x0=y0+(xx0)y1(xx0)y0x1x0 y=y_{0}+\left(x-x_{0}\right) \frac{y_{1}-y_{0}}{x_{1}-x_{0}}=y_{0}+\frac{\left(x-x_{0}\right) y_{1}-\left(x-x_{0}\right) y_{0}}{x_{1}-x_{0}}

  雙線性插值就是線性插值在二維時的推廣,在兩個方向上做三次線性插值,具體操作如下圖所示:

  令f(xy)f(x,y)爲兩個變量的函數,其在單位正方形頂點的值已知。假設我們希望通過插值得到正方形內任意點的函數值。則可由雙線性方程:
f(x,y)=ax+by+cxy+d f(x, y)=a x+b y+c x y+d

  來定義的一個雙曲拋物面與四個已知點擬合。

  首先對上端的兩個頂點進行線性插值得:

f(x,0)=f(0,0)+x[f(1,0)f(0,0)] f(x, 0)=f(0,0)+x[f(1,0)-f(0,0)]

  類似地,再對底端的兩個頂點進行線性插值有:
f(x,1)=f(0,1)+x[f(1,1)f(0,1)] f(x, 1)=f(0,1)+x[f(1,1)-f(0,1)]

  最後,做垂直方向的線性插值,以確定:

f(x,y)=f(x,0)+y[f(x,1)f(x,0)] f(x, y)=f(x, 0)+y[f(x, 1)-f(x, 0)]

  整理得:

f(x,y)=[f(1,0)f(0,0)]x+[f(0,1)f(0,0)]y+[f(1,1)+f(0,0)f(0,1)f(1,0)]xy+f(0,0) \begin{array}{l} f(x, y)=[f(1,0)-f(0,0)] x+[f(0,1)-f(0,0)] y \\ +[f(1,1)+f(0,0)-f(0,1)-f(1,0)] x y+f(0,0) \end{array}

1.4.3 映射方法

向前映射法

  可以將幾何運算想象成一次一個象素地轉移到輸出圖象中。如果一個輸入象素被映射到四個輸出象素之間的位置,則其灰度值就按插值算法在4個輸出象素之間進行分配,輸出圖像的像素由其周圍映射過來的原圖像像素按權重分配疊加。稱爲向前映射法,或象素移交映射。

注:從原圖象座標計算出目標圖象座標鏡像、平移變換使用這種計算方法。該方法的輸出圖像不可以直接得到,需要遍歷每個原像素點。

在這裏插入圖片描述
雙線性插值公式
在這裏插入圖片描述

向後映射法

  向後映射法(或象素填充算法)是輸出象素一次一個地映射回到輸入象素中,以便確定其灰度級。如果一個輸出象素被映射到4個輸入象素之間,則其灰度值插值決定,向後空間變換是向前變換的逆。

注:從結果圖象的座標計算原圖象的座標

  • 旋轉、拉伸、放縮可以使用
  • 解決了漏點的問題,出現了馬賽克
    在這裏插入圖片描述

1.5 基於OpenCV的實現

1.5.1 C++

函數原型:

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

src:輸入圖像
dst:輸出圖像
dsize:輸出圖像尺寸
fx、fy:x,y方向上的縮放因子
INTER_LINEAR:插值方法,總共五種
    1. INTER_NEAREST - 最近鄰插值法
    2. INTER_LINEAR - 雙線性插值法(默認)
    3. INTER_AREA - 基於局部像素的重採樣(resampling using pixel area relation)。對於圖像抽取(image decimation)來說,這可能是一個更好的方法。但如果是放大圖像時,它和最近鄰法的效果類似。
    4. INTER_CUBIC - 基於4x4像素鄰域的3次插值法
    5. INTER_LANCZOS4 - 基於8x8像素鄰域的Lanczos插值

代碼實踐:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
	Mat img = imread("D:/image/yuner.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);
	waitKey(0);
    return 0;
}

原圖

0.2倍縮小,雙線性插值

1.5倍放大,最近鄰插值

1.5倍放大,雙線性插值

1.5.2 Python

函數原型:

cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])

參數:

參數 描述
src 【必需】原圖像
dsize 【必需】輸出圖像所需大小
fx 【可選】沿水平軸的比例因子
fy 【可選】沿垂直軸的比例因子
interpolation 【可選】插值方式

插值方式:

cv.INTER_NEAREST 最近鄰插值
cv.INTER_LINEAR 雙線性插值
cv.INTER_CUBIC 基於4x4像素鄰域的3次插值法
cv.INTER_AREA 基於局部像素的重採樣

通常,縮小使用cv.INTER_AREA,放縮使用cv.INTER_CUBIC(較慢)和cv.INTER_LINEAR(較快效果也不錯)。默認情況下,所有的放縮都使用cv.INTER_LINEAR。

代碼實踐:

import cv2 as cv

src = cv.imread("1.jpg")
h,w,_ = src.shape
dim = (int(1.5*w),int(1.5*h))
img = cv.resize(src,dim,cv.INTER_LINEAR)
cv.imwrite("1.1.jpg",img)
img = cv.resize(src,dim,cv.INTER_NEAREST)
cv.imwrite("1.2.jpg",img)
img = cv.resize(src,dim,cv.INTER_CUBIC)
cv.imwrite("1.3.jpg",img)
# cv.waitKey(0)
# cv.destroyAllWindows()

1.5倍放大,最近鄰插值

在這裏插入圖片描述
1.5倍放大,雙線性插值

在這裏插入圖片描述
1.5倍放大,基於4x4像素鄰域的3次插值法
在這裏插入圖片描述

  • 推薦書籍:學習OpenCV中文版
  • 推薦博客:https://blog.csdn.net/hongbin_xu/category_6936122.html

1.6 總結

  插值算法是很多幾何變換的基礎和前置條件,對插值算法細節的掌握有助於對其他算法的理解,爲自己的學習打下堅實的基礎。


Task01 OpenCV框架與圖像插值算法 END.

By: Aaron

博客:https://sandy1230.github.io/

博客:https://blog.csdn.net/weixin_39940512

關於Datawhale

Datawhale是一個專注於數據科學與AI領域的開源組織,彙集了衆多領域院校和知名企業的優秀學習者,聚合了一羣有開源精神和探索精神的團隊成員。Datawhale以“for the learner,和學習者一起成長”爲願景,鼓勵真實地展現自我、開放包容、互信互助、敢於試錯和勇於擔當。同時Datawhale 用開源的理念去探索開源內容、開源學習和開源方案,賦能人才培養,助力人才成長,建立起人與人,人與知識,人與企業和人與未來的聯結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章