数字图像缩放之双三次插值

基本原理:双三次插值是一种更加复杂的插值方式,它能创造出比双线性插值更平滑的图像边缘。缩放后图像中某个象素的象素值是由源图像相应像素附近的(4 x 4)个邻近象素值计算出来的,即通过一个基函数进行拟合得到一个目的像素值,具体某点v(x,y) 的像素值是使用下式计算得到:

v(x,y) =∑∑aij*x^i*y^j;其中,0≤i,j≤3;16个系数aij由16个临近像素写出的未知方程确定。

C/C++实现如下:

void GeometryTrans::Zoom(float ratioX, float ratioY)
{

	//释放旧的输出图像缓冲区
	if(m_pImgDataOut!=NULL){
		delete []m_pImgDataOut;
    	m_pImgDataOut=NULL;
	}
    
	//输出图像的宽和高
	m_imgWidthOut=int(m_imgWidth*ratioX+0.5) ;
	m_imgHeightOut=int(m_imgHeight*ratioY+0.5); 

	//输入图像每行像素字节数
	int lineByteIn=(m_imgWidth*m_nBitCount/8+3)/4*4;

	//输出图像每行像素字节数
    int	lineByteOut=(m_imgWidthOut*m_nBitCount/8+3)/4*4;

	//申请缓冲区,存放输出结果

	m_pImgDataOut=new unsigned char[lineByteOut*m_imgHeightOut];

	//每像素字节数,输入图像与输出图像相同
	int pixelByte=m_nBitCount/8;

	//输出图像在输入图像中待插值的位置坐标
	int coordinateX, coordinateY;

	//循环变量,输出图像的坐标
	int i,j;

	//循环变量,像素的每个通道
	int k;
	//**************************************************************
	//对原图像进行拓展,上下分别拓展两行,左右分别拓展两列
	if(m_pImgData_temp!=NULL){
		delete []m_pImgData_temp;
    	m_pImgData_temp=NULL;
	}
	//拓展图像每行像素字节数
	int lineByteIn1=((m_imgWidth+4)*m_nBitCount/8+3)/4*4;
	//申请缓冲区,存放拓展后的图像

	m_pImgData_temp=new unsigned char[lineByteIn1*(m_imgHeight+4)];

	for(k=0;k<pixelByte;k++)
	{
		for(i=0;i<m_imgHeight;i++)//中间m_imgWidth*m_imgHeight的部分
			for(j=0;j<m_imgWidth;j++)
	             *(m_pImgData_temp + (i+2)* lineByteIn1 + (j+2)*pixelByte + k) 
					=*(m_pImgData+ i*lineByteIn+ j*pixelByte + k) ;

		for(i=0;i<2;i++)//拓展上面两行
			for(j=0;j<m_imgWidth;j++)
	             *(m_pImgData_temp + i * lineByteIn1 + (j+2)*pixelByte + k) 
					=*(m_pImgData+ i*lineByteIn+ j*pixelByte + k) ;

		for(i=0;i<2;i++)//拓展下面两行
			for(j=0;j<m_imgWidth;j++)
	             *(m_pImgData_temp + (i+m_imgHeight+2)* lineByteIn1 + (j+2)*pixelByte + k) 
					=*(m_pImgData+ (i+m_imgHeight-2)*lineByteIn+ j*pixelByte + k) ;

		for(i=0;i<m_imgHeight;i++)//拓展左边两列
			for(j=0;j<2;j++)
	             *(m_pImgData_temp + (i+2)* lineByteIn1 + j*pixelByte + k) 
					=*(m_pImgData+ i*lineByteIn+ j*pixelByte + k) ;

		for(i=0;i<m_imgHeight;i++)//拓展右边两列
			for(j=0;j<2;j++)
	             *(m_pImgData_temp + (i+2)* lineByteIn1 + (j+m_imgWidth+2)*pixelByte + k) 
					=*(m_pImgData+ i*lineByteIn+ (j+m_imgWidth-2)*pixelByte + k) ;

		for(i=0;i<2;i++)//左上角部分
			for(j=0;j<2;j++)
	             *(m_pImgData_temp + i* lineByteIn1 + j*pixelByte + k) 
					=*(m_pImgData+ i*lineByteIn+ j*pixelByte + k) ;

		for(i=0;i<2;i++)//右上角部分
			for(j=0;j<2;j++)
	             *(m_pImgData_temp + i* lineByteIn1 + (j+m_imgWidth+2)*pixelByte + k) 
					=*(m_pImgData+ i*lineByteIn+ (j+m_imgWidth-2)*pixelByte + k) ;

		for(i=0;i<2;i++)//左下角部分
			for(j=0;j<2;j++)
	             *(m_pImgData_temp + (i+m_imgHeight+2 )* lineByteIn1 + j*pixelByte + k) 
					=*(m_pImgData+ (i+m_imgHeight-2)*lineByteIn+ j*pixelByte + k) ;

		for(i=0;i<2;i++)//右下角部分
			for(j=0;j<2;j++)
	             *(m_pImgData_temp + (i+m_imgHeight+2)* lineByteIn1 + (j+m_imgWidth+2)*pixelByte + k) 
					=*(m_pImgData+ (i+m_imgHeight-2)*lineByteIn+ (j+m_imgWidth-2)*pixelByte + k) ;
    }    
	//***************************************************************
    float u,v;
	int ii,jj;
	double Array[3][16];//存储临时像素值的二维数组

	float a[4],c[4];//系数数组
	//双3次多项式插值
	for(i=0; i< m_imgHeightOut; i++)
	{
		coordinateY=i/ratioY+0.5;
		u=(float)(fmodf(i,ratioY)*(1/ratioY));
		/*计算系数矩阵*/
		a[0]=Sinc(1+u);
		a[1]=Sinc(u);
		a[2]=Sinc(1-u);
		a[3]=Sinc(2-u);		

		for(j=0; j<m_imgWidthOut; j++)
		{  
			coordinateX=j/ratioX+0.5;
            /*计算系数矩阵*/
			v=(float)(fmodf(j,ratioX)*(1/ratioX));
			c[0]=Sinc(1+v);
		    c[1]=Sinc(v);
		    c[2]=Sinc(1-v);
		    c[3]=Sinc(2-v);  
			                     
			//输出图像坐标为(j,i)的像素映射到原图中的坐标值,即插值位置

			//若插值位置在输入图像范围内,则双三次多项式插值
			if(0<=coordinateX&&coordinateX<(m_imgWidth)
				&& coordinateY>=0&&coordinateY<(m_imgHeight))
			{
			    for(k=0;k<pixelByte;k++)//三个通道,RGB
					for(ii=0;ii<4;ii++)
						for(jj=0;jj<4;jj++)  /*与目的像素相关的16个原始像素RGB*/
						{						  	  	  						  
						  Array[k][ii*4+jj] =(double)(*(m_pImgData_temp+ (coordinateY+ii)*lineByteIn1*+ (coordinateX+jj)*pixelByte + k)) ;
						}

				for(k=0;k<pixelByte;k++)
					*(m_pImgDataOut + i * lineByteOut + j*pixelByte + k) 
					=(unsigned char)ABC(a,Array[k],c);/*调用ABC求得目的像素*/

			}
			else //若不在输入图像范围内,则置255  
			{
				for(k=0;k<pixelByte;k++)
					*(m_pImgDataOut + i * lineByteOut + j*pixelByte+k) = 255;
			}
		}
	}
}

基函数实现如下

loat GeometryTrans::Sinc(float x) /*Sinc(x)是对 Sin(x*Pi)/x 的逼近(Pi是圆周率——π)*/
{
  if(abs(x)>=0&&abs(x)<1)
   return (1-2*abs(x)*abs(x)+abs(x)*abs(x)*abs(x));
  else
     if(abs(x)>=1&&abs(x)<2)
      return (4-8*abs(x)+5*abs(x)*abs(x)-abs(x)*abs(x)*abs(x));
     else  
       return 0;
}

矩阵内积计算函数实现如下

float GeometryTrans::ABC(float a[],double b[],float c[]) /*矩阵运算函数,求得像素值,内积*/
{
  int i,j;
  float abc=0;
  float tmp[4];
  for(i=0;i<4;i++)
    tmp[i]=0;
  for(i=0;i<4;i++)
     for(j=0;j<4;j++)
      tmp[i]+=a[j]*b[j*4+i];
  for(i=0;i<4;i++)
    abc+=tmp[i]*c[i];
  if (abc<0)
	  abc=0;
  if (abc>255)
	  abc=255;
  return abc;  
}
时间: 2024-10-13 11:34:34

数字图像缩放之双三次插值的相关文章

谈谈数字图像的缩放算法[转]

数字图像的缩放,是一个十分有趣的问题,又是一个看似简单,但又有些复杂的问题.许多朋友在具备一定的计算机图形编程的基础知识以后,都可以自己设计出一些简单的位图缩放算法.在计算机图形学和数字图像处理等学科里面,已经详细的研究过了数字图像缩放这个问题,并且已经有了成熟的算法.一些朋友由于没有学习过计算机图形学和数字图像处理,所以凭借自己的想法设计的位图缩放算法存在许多缺陷.在本文中,我将和大家一起来研究这个问题,并且学习前人所总结出来的算法. 图像的概念很容易理解,你睁开眼睛,所看到的都是图像了.而一

java 缩放算法 双线性插值,双三次插值

双线性插值的效果对于放大的图像而言较领域插值来得平滑,但是却使得图像变得模糊而且仍然会有一部分锯齿现象. 双三次插值更好比双线性插值更好. 图像缩放之双三次插值法 数字图像处理之双线性插值 原文地址:https://www.cnblogs.com/hfultrastrong/p/9266606.html

OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)

图像的几何变换是在不改变图像内容的前提下对图像像素的进行空间几何变换,主要包括了图像的平移变换.镜像变换.缩放和旋转等.本文首先介绍了图像几何变换的一些基本概念,然后再OpenCV2下实现了图像的平移变换.镜像变换.缩放以及旋转,最后介绍几何的组合变换(平移+缩放+旋转). 1.几何变换的基本概念 1.1 坐标映射关系 图像的几何变换改变了像素的空间位置,建立一种原图像像素与变换后图像像素之间的映射关系,通过这种映射关系能够实现下面两种计算: 原图像任意像素计算该像素在变换后图像的坐标位置 变换

图像处理之基础---图像缩放中的一些 灰度插值算法

在图像缩放,旋转等一些图像处理中,对图像进行插值是不可缺少的一个步骤,下面对一些常用的插值算法进行介绍: 1.最近邻插值 这种插值方法是最简单的一种插值算法,图像输出的像素值的大小直接设为与其最邻近的点的大小即可,这个算法最简单,不需要多说,可以表示为 f(x,y) = g(  round(x)  ,   round(y)  ) 原图                                                                                  

[转]OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

[OpenCV入门教程之十三]OpenCV图像金字塔:高斯金字塔.拉普拉斯金字塔与图片尺寸缩放 2014-05-18 18:58 36007人阅读 评论(54) 收藏 举报 本文章已收录于:  OpenCV知识库 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http

【OpenCV入门教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 知乎:http://www.zhihu.com/people/mao-xing-yun 邮箱: [email protected] 写作当前博文时配套使用的OpenCV版本: 2.4.9 这篇文章里,我们将一起探讨图像金字塔的一

图像缩放——双线性插值算法

在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值.如果选择一个坐标系统使得  的四个已知点坐标分别为 (0, 0).(0, 1).(1, 0) 和 (1, 1),那么插值公式就可以化简为: 用矩阵运算来表示的话就是: 图像的空间变换,也称几何变换或几何运算,包括图像的平移.旋转.镜像变换.转置.缩放等.空间变换可如下表示:设(u,v)为源图像上的点,(x,y)为目标图像上的点,则空间变换就是将源图像上(u,v)处的颜色值与目标图像上(x,y)处

12、高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

一.引言 我们经常会将某种尺寸的图像转换为其他尺寸的图像,如果放大或者缩小图片的尺寸,笼统来说的话,可以使用OpenCV为我们提供的如下两种方式: (1)resize函数.这是最直接的方式, (2)pyrUp( ).pyrDown( )函数.即图像金字塔相关的两个函数,对图像进行向上采样,向下采样的操作. pyrUp.pyrDown其实和专门用作放大缩小图像尺寸的resize在功能上差不多,披着图像金字塔的皮,说白了还是在对图像进行放大和缩小操作.另外需要指出的是,pyrUp.pyrDown在O

UIScrollView的缩放原理

有些时候,我们可能要对某些内容进行手势缩放,如下图所示 UIScrollView不仅能滚动显示大量内容,还能对其内容进行缩放处理也就是说,要完成缩放功能的话,只需要将需要缩放的内容添加到UIScrollView中 当用户在UIScrollView身上使用捏合手势时,UIScrollView会给代理发送一条消息,询问代理究竟要缩放自己内部的哪一个子控件(哪一块内容) 当用户在UIScrollView身上使用捏合手势时,UIScrollView会调用代理的viewForZoomingInScroll