日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

sensor、codec、display device都是基于pixel的,高分辨率圖像能呈現更多的detail,由于sensor制造和chip的限制,我們需要用到圖像插值(scaler/resize)技術,這種方法代價小,使用方便。同時,該技術還可以放大用戶希望看到的感興趣區域。圖像縮放算法往往基于插值實現,常見的圖像插值算法包括最近鄰插值(Nearest-neighbor)、雙線性插值(Bilinear)、雙立方插值(bicubic)、lanczos插值、方向插值(Edge-directed interpolation)、example-based插值、深度學習等算法。

插值縮放的原理是基于目標分辨率中的點,將其按照縮放關系對應到源圖像中,尋找源圖像中的點(不一定是整像素點),然后通過源圖像中的相關點插值得到目標點。本篇文章,我們介紹Nearest-neighbor和Bilinear插值的原理及C實現。

插值算法原理如下:

圖像插值算法及其實現

 

1. Nearest-neighbor

最近鄰插值,是指將目標圖像中的點,對應到源圖像中后,找到最相鄰的整數點,作為插值后的輸出。如下圖所示,P為目標圖像對應到源圖像中的點,Q11、Q12、Q21、Q22是P點周圍4個整數點,Q12與P離的最近,因此P點的值等于Q12

圖像插值算法及其實現

 

的值。這里寫圖片描述由于圖像中像素具有鄰域相關性,因此,用這種拷貝的方法會產生明顯的鋸齒。

2. Bilinear

雙線性插值使用周圍4個點插值得到輸出,雙線性插值,是指在xy方法上,都是基于線性距離來插值的。

如圖1,目標圖像中的一點對應到源圖像中點P(x,y),我們先在x方向插值:

圖像插值算法及其實現

 

然后,進行y方向插值:

圖像插值算法及其實現

 

可以驗證,先進行y方向插值再進行x方向插值,結果也是一樣的。值得一提的是,雙線性插值在單個方向上是線性的,但對整幅圖像來說是非線性的。

3. C實現

使用VS2010,工程包含三個文件,如下:

圖像插值算法及其實現

 

main.cpp

#include <string.h>
#include <IOStream>
#include "resize.h"

int main()
{
	const char *input_file = "D:\simuTest\teststream\00_YUV_data\01_DIT_title\data.yuv";		//absolute path
	const char *output_file = "D:\simuTest\teststream\00_YUV_data\01_DIT_title\data_out2.yuv";	//absolute path	
	int src_width = 720;
	int src_height = 480;
	int dst_width = 1920;
	int dst_height = 1080;
	int resize_type = 1;		//0:nearest, 1:bilinear

	resize(input_file, src_width, src_height, output_file, dst_width, dst_height, resize_type);
	return 0;
}

resize.cpp

#include "resize.h"

int clip3(int data, int min, int max)
{
	return (data > max) ? max : ((data < min) ? min : data);
	if(data > max)
		return max;
	else if(data > min)
		return data;
	else
		return min;
}

//bilinear takes 4 pixels (2×2) into account
/*
* 函數名:	bilinearHorScaler
* 說明:	水平方向雙線性插值
* 參數:
*/
void bilinearHorScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height)
{
	double resizeX = (double)dst_width / src_width;
	for(int ver = 0; ver < dst_height; ++ver){
		for(int hor = 0; hor < dst_width; ++hor){
			double srcCoorX = hor / resizeX;
			double weight1 = srcCoorX - (double)((int)srcCoorX);
			double weight2 = (double)((int)(srcCoorX + 1)) - srcCoorX;
			double dstValue = *(src_image + src_width * ver + clip3((int)srcCoorX, 0, src_width - 1)) * weight2 + *(src_image + src_width * ver + clip3((int)(srcCoorX + 1), 0, src_width - 1)) * weight1;
			*(dst_image + dst_width * ver + hor) = clip3((uint8)dstValue, 0, 255);
		}
	}
}

/*
* 函數名:	bilinearVerScaler
* 說明:	垂直方向雙線性插值
* 參數:
*/
void bilinearVerScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height)
{
	double resizeY = (double)dst_height / src_height;
	for(int ver = 0; ver < dst_height; ++ver){
		for(int hor = 0; hor < dst_width; ++hor){
			double srcCoorY = ver / resizeY;
			double weight1 = srcCoorY - (double)((int)srcCoorY);
			double weight2 = (double)((int)(srcCoorY + 1)) - srcCoorY;
			double dstValue = *(src_image + src_width * clip3((int)srcCoorY, 0, src_height - 1) + hor) * weight2 + *(src_image + src_width * clip3((int)(srcCoorY + 1), 0, src_height - 1) + hor) * weight1;
			*(dst_image + dst_width * ver + hor) = clip3((uint8)dstValue, 0, 255);
		}
	}
}

/*
* 函數名:	yuv420p_NearestScaler
* 說明:	最近鄰插值
* 參數:
*/
void nearestScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height)
{
	double resizeX = (double)dst_width /src_width;			//水平縮放系數
	double resizeY = (double)dst_height / src_height;			//垂直縮放系數
	int srcX = 0;
	int srcY = 0;
	for(int ver = 0; ver < dst_height; ++ver) {
		for(int hor = 0; hor < dst_width; ++hor) {
			srcX = clip3(int(hor/resizeX + 0.5), 0, src_width - 1);
			srcY = clip3(int(ver/resizeY + 0.5), 0, src_height - 1);
			*(dst_image + dst_width * ver + hor) = *(src_image + src_width * srcY + srcX);
		}
	}
}

void resize(const char *input_file, int src_width, int src_height, const char *output_file, int dst_width, int dst_height, int resize_type)
{
	//define and init src buffer
	int *src_y = new int[src_width * src_height];
	int *src_cb = new int[src_width * src_height / 4];
	int *src_cr = new int[src_width * src_height / 4];
	memset(src_y, 0, sizeof(int) * src_width * src_height);
	memset(src_cb, 0, sizeof(int) * src_width * src_height / 4);
	memset(src_cr, 0, sizeof(int) * src_width * src_height / 4);

	//define and init dst buffer
	int *dst_y = new int[dst_width * dst_height];
	int *dst_cb = new int[dst_width * dst_height / 4];
	int *dst_cr = new int[dst_width * dst_height / 4];
	memset(dst_y, 0, sizeof(int) * dst_width * dst_height);
	memset(dst_cb, 0, sizeof(int) * dst_width * dst_height / 4);
	memset(dst_cr, 0, sizeof(int) * dst_width * dst_height / 4);

	//define and init mid buffer
	int *mid_y = new int[dst_width * src_height];
	int *mid_cb = new int[dst_width * src_height / 4];
	int *mid_cr = new int[dst_width * src_height / 4];
	memset(mid_y, 0, sizeof(int) * dst_width * src_height);
	memset(mid_cb, 0, sizeof(int) * dst_width * src_height / 4);
	memset(mid_cr, 0, sizeof(int) * dst_width * src_height / 4);

	uint8 *data_in_8bit = new uint8[src_width * src_height * 3 / 2];
	memset(data_in_8bit, 0, sizeof(uint8) * src_width * src_height * 3 / 2);

	uint8 *data_out_8bit = new uint8[dst_width * dst_height * 3 / 2];
	memset(data_out_8bit, 0, sizeof(uint8) * dst_width * dst_height * 3 / 2);

	FILE *fp_in = fopen(input_file,"rb");
	if(NULL == fp_in)
	{
		//exit(0);
		printf("open file failure");
	}
	FILE *fp_out = fopen(output_file, "wb+");

	//data read
	fread(data_in_8bit, sizeof(uint8), src_width * src_height * 3 / 2, fp_in);
	//Y component
	for(int ver = 0; ver < src_height; ver++)
	{
		for(int hor =0; hor < src_width; hor++)
		{
			src_y[ver * src_width + hor] = data_in_8bit[ver * src_width + hor];
		}
	}
	//c component YUV420P
	for(int ver = 0; ver < src_height / 2; ver++)
	{
		for(int hor =0; hor < src_width / 2; hor++)
		{
			src_cb[ver * (src_width / 2) + hor] = data_in_8bit[src_height * src_width + ver * src_width / 2 + hor];
			src_cr[ver * (src_width / 2) + hor] = data_in_8bit[src_height * src_width + src_height * src_width / 4 + ver * src_width / 2 + hor];
		}
	}

	//resize
	if(0 == resize_type)
	{
		nearestScaler(src_y, dst_y, src_width, src_height, dst_width, dst_height);
		nearestScaler(src_cb, dst_cb, src_width / 2, src_height / 2, dst_width / 2, dst_height / 2);
		nearestScaler(src_cr, dst_cr, src_width / 2, src_height / 2, dst_width / 2, dst_height / 2);
	}
	else if(1 == resize_type)
	{
		bilinearHorScaler(src_y, mid_y, src_width, src_height, dst_width, src_height);
		bilinearHorScaler(src_cb, mid_cb, src_width / 2, src_height / 2, dst_width / 2, src_height / 2);
		bilinearHorScaler(src_cr, mid_cr, src_width / 2, src_height / 2, dst_width / 2, src_height / 2);

		bilinearVerScaler(mid_y, dst_y, dst_width, src_height, dst_width, dst_height);
		bilinearVerScaler(mid_cb, dst_cb, dst_width / 2, src_height / 2, dst_width / 2, dst_height / 2);
		bilinearVerScaler(mid_cr, dst_cr, dst_width / 2, src_height / 2, dst_width / 2, dst_height / 2);
	}	
	else
	{
		nearestScaler(src_y, dst_y, src_width, src_height, dst_width, dst_height);
		nearestScaler(src_cb, dst_cb, src_width / 2, src_height / 2, dst_width / 2, dst_height / 2);
		nearestScaler(src_cr, dst_cr, src_width / 2, src_height / 2, dst_width / 2, dst_height / 2);
	}

	//data write
	for(int ver = 0; ver < dst_height; ver++)
	{
		for(int hor =0; hor < dst_width; hor++)
		{
			data_out_8bit[ver * dst_width + hor] = clip3(dst_y[ver * dst_width + hor], 0, 255);
		}
	}

	for(int ver = 0; ver < dst_height / 2; ver++)
	{
		for(int hor = 0; hor < dst_width / 2; hor++)
		{
			data_out_8bit[dst_height * dst_width + ver * dst_width / 2 + hor] = clip3(dst_cb[ver * (dst_width / 2) + hor], 0, 255);
			data_out_8bit[dst_height * dst_width + dst_height * dst_width / 4 + ver * dst_width / 2 + hor] = clip3(dst_cr[ver * (dst_width / 2) + hor], 0, 255);
		}
	}
	fwrite(data_out_8bit, sizeof(uint8), dst_width * dst_height * 3 / 2, fp_out);

	delete [] src_y;
	delete [] src_cb;
	delete [] src_cr;
	delete [] dst_y;
	delete [] dst_cb;
	delete [] dst_cr;
	delete [] mid_y;
	delete [] mid_cb;
	delete [] mid_cr;
	delete [] data_in_8bit;
	delete [] data_out_8bit;
	fclose(fp_in);
	fclose(fp_out);

}

resize.h

#ifndef RESIZE_H
#define RESIZE_H

#include <stdio.h>
#include <string.h>

typedef unsigned char uint8;
typedef unsigned short uint16;

int clip3(int data, int min, int max);
void bilinearHorScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height);
void bilinearVerScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height);
void nearestScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height);
void resize(const char *input_file, int src_width, int src_height, const char *output_file, int dst_width, int dst_height, int resize_type);

#endif

效果比較

將720x480分辨率圖像放大到1080p,1:1截取局部畫面如下,左邊是最近鄰放大的效果,右邊是雙線性效果,可以看到,雙線性放大的鋸齒要明顯比最近鄰小。

圖像插值算法及其實現

 

Matlab

常用的matlab縮放方法有兩種,如下

  1. B = imresize(A, scale, method) B = imresize(A, 0.5, ‘bicubic’)使用雙立方插值將寬高各縮小1/2
  2. B = imresize(A, outputSize, method) B = imresize(A, [1080,1920], ‘bilinear’)使用雙線性插值縮放到1920x1080分辨率
圖像插值算法及其實現

 

分享到:
標簽:算法 圖像 插值
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定