图像插值算法及其实现

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分辨率
    这里写图片描述

Opencv
常用的opencv中resize调用方法也有两种

  1. dsize=0,指定fx和fy,此时目标图像大小会自动计算出,dsize=Size(round(fxsrc.cols),round(fysrc,rows))
resize(src, dst, Size(0, 0), 0.5, 0.5, 2);		//缩小为原来的1/2,使用双立方插值(2)
  1. fx和fy为0,指定dsize
resize(src, dst, Size(1024,1024), 0, 0, 1);		//缩放到1024x1024分辨率,使用双线性插值(1)

Opencv提供5种插值方法有5种:最近邻、双线性、双立方、面积关联、兰佐斯。
Resize函数声名及插值方式玫举定义:

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

enum
{
    INTER_NEAREST=CV_INTER_NN, //!< nearest neighbor interpolation
    INTER_LINEAR=CV_INTER_LINEAR, //!< bilinear interpolation
    INTER_CUBIC=CV_INTER_CUBIC, //!< bicubic interpolation
    INTER_AREA=CV_INTER_AREA, //!< area-based (or super) interpolation
    INTER_LANCZOS4=CV_INTER_LANCZOS4, //!< Lanczos interpolation over 8x8 neighborhood
    INTER_MAX=7,
    WARP_INVERSE_MAP=CV_WARP_INVERSE_MAP
};

enum
{
    CV_INTER_NN        =0,
    CV_INTER_LINEAR    =1,
    CV_INTER_CUBIC     =2,
    CV_INTER_AREA      =3,
    CV_INTER_LANCZOS4  =4
};

参考
[1] https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation
[2] https://en.wikipedia.org/wiki/Bilinear_interpolation

  • 28
    点赞
  • 175
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 《图像缩放算法的研究及其在FPGA上的实现》是一篇有关图像缩放算法及其在FPGA上的实现的研究论文。图像缩放图像处理领域中一个重要的问题,它可以改变图像的尺寸,使得图像适应不同的显示设备或满足特定的需求。 本文首先介绍了图像缩放算法的基本原理和常用方法,如最近邻插值、双线性插值和双三次插值等。这些方法基于不同的插值原理来实现图像缩放,各有优缺点。 接着,本文详细探讨了将图像缩放算法实现在FPGA上的可行性及其优势。FPGA是一种可编程逻辑器件,具有并行计算和高速处理的特点,非常适合用于图像处理任务。通过将图像缩放算法实现在FPGA上,可以加快计算速度,提高图像处理的效率。 然后,本文介绍了在FPGA上实现图像缩放算法的具体步骤和方法。首先将图像数据加载到FPGA内存中,然后通过并行计算的方法进行插值计算,最后将处理后的图像数据输出。本文还详细讨论了如何优化FPGA上图像缩放算法的性能,如通过分块计算、流水线等技术来提高计算速度和减少资源占用。 最后,本文通过实验证明了在FPGA上实现图像缩放算法的有效性和优势。实验结果显示,通过将图像缩放算法实现在FPGA上,可以加快计算速度,并且处理后的图像质量也能够满足需求。 综上所述,本文深入研究了图像缩放算法的原理和方法,并探讨了将其实现在FPGA上的可行性和优势。通过在FPGA上实现图像缩放算法,可以提高图像处理的效率和质量,具有重要的研究和应用价值。 ### 回答2: 《图像缩放算法的研究及其在FPGA上的实现.pdf》是一篇关于图像缩放算法及其在FPGA(现场可编程门阵列)上的实现的研究论文。 图像缩放是指通过改变图像的大小来适应不同的应用需求。在图像处理领域,图像缩放算法起着至关重要的作用。不同的图像缩放算法有着不同的特点和适用范围,因此对其进行研究和实现是很有意义和价值的。 该论文的研究目的是探究不同的图像缩放算法,并将其实现在FPGA上。FPGA是一种灵活可编程的硬件平台,能够高效地执行图像处理算法。通过在FPGA上实现图像缩放算法,可以实现高性能和低功耗的图像处理系统。 论文首先介绍了几种常见的图像缩放算法,如最邻近插值、双线性插值和双三次插值等。这些算法可以保持图像的视觉效果和细节,并且能够根据不同的需求进行选择。然后,论文详细描述了将这些算法实现在FPGA上的过程,包括硬件设计和优化技术。 通过实验和比较,论文得出了不同图像缩放算法在FPGA上的性能指标和实现效果。研究结果表明,FPGA能够有效地处理图像缩放算法,能够实现实时性能和较低的功耗。同时,论文还讨论了FPGA上图像缩放算法的优化方法和未来的研究方向。 总而言之,《图像缩放算法的研究及其在FPGA上的实现.pdf》是一篇对图像缩放算法及其在FPGA上实现的研究论文,介绍了不同的图像缩放算法和其在FPGA上的实现过程,对于图像处理领域的研究和应用具有一定的参考价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值