图像几何变换(缩放、旋转)中的常用的插值算法

在图像几何变换的过程中,常用的插值方法有最邻近插值(近邻取样法)、双线性内插值和三次卷积法。

最邻近插值:

这是一种最为简单的插值方法,在图像中最小的单位就是单个像素,但是在旋转个缩放的过程中如果出现了小数,那么就对这个浮点坐标进行简单的取整,得到一个整数型坐标,这个整数型坐标对应的像素值就是目标像素的像素值。取整的方式就是:取浮点坐标最邻近的左上角的整数点。 
举个例子: 
3*3的灰度图像,其每一个像素点的灰度如下所示

我们要通过缩放,将它变成一个4*4的图像,那么其实相当于放大了4/3倍,从这个倍数我们可以得到这样的比例关系:

根据公式可以计算出目标图像中的(0,0)坐标与原图像中对应的坐标为(0,0) 
(由于分母不能为0,所以我们将公式改写)

然后我们就可以确定出目标图像中(0,0)坐标的像素灰度了,就是234。

然后我们在确定目标图像中的(0,1)坐标与原图像中对应的坐标,同样套用公式: 

我们发现,这里出现了小数,也就是说它对应的原图像的坐标是(0,0.75),显示这是错误的,如果我们不考虑亚像素情况,那么一个像素单位就是图像中最小的单位了,那么按照最临近插值算法,我们找到距离0.75最近的最近的整数,也就是1,那么对应的原图的坐标也就是(0,1),像素灰度为67。

双线性内插值:

对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(i+u,j+v),其中i、j均为非负整数,u、v为[0,1)区间的浮点数,则这个像素得值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:

f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)

其中f(i,j)表示源图像(i,j)处的的像素值。

那么还是上面的例子,目标图像中(0,1)对应的原图像浮点坐标是(0,0.75),套用上面的公式这个坐标可以写成(0+0,0+0.75),其中i=0,j=0,u=0,v=0.75 
我们套用公式看一下它最后的灰度 
f(i+u,j+v) = 0.25*f(0,0)+0.75*f(0,1)=0.25*234+0.75*67 
约等于108

这就是双线性内插值法。双线性内插值法计算量大,但缩放后图像质量高,不会出现像素值不连续的的情况。由于双线性插值具有低通滤波器的性质,使高频分量受损,所以可能会使图像轮廓在一定程度上变得模糊。

三次卷积法:

其实这个方法在好像有很多叫法,它在OpenCV中被命名为INTER_CUBIC,就是立方(三次)的意思,现在我把它和三次卷积法认为是同一种算法,引用一个帖子里面的话:

全称双立方(三次)卷积插值。 
代码或许有不同写法,实现方式就一种 
该算法是对函数 sin x / x 的一种近似,也就是说 原图像对目标图像的影响 
等于 目标点对应于原图像点周围 x距离的点,按照 sin x / x 比例 的加权平均 。 
这里x代表,周围得点跟目标点, x或者 y 轴 对应于原图的相对位置。 
sin x / x 是归一化了的,实际应用的是近似公式

f(i+u,j+v) = [A] * [B] * [C] 
[A]=[ S(u + 1) S(u + 0) S(u - 1) S(u - 2) ] 
  ┏ f(i-1, j-1) f(i-1, j+0) f(i-1, j+1) f(i-1, j+2) ┓ 
[B]=┃ f(i+0, j-1) f(i+0, j+0) f(i+0, j+1) f(i+0, j+2) ┃ 
  ┃ f(i+1, j-1) f(i+1, j+0) f(i+1, j+1) f(i+1, j+2) ┃ 
  ┗ f(i+2, j-1) f(i+2, j+0) f(i+2, j+1) f(i+2, j+2) ┛ 
  ┏ S(v + 1) ┓ 
[C]=┃ S(v + 0) ┃ 
  ┃ S(v - 1) ┃ 
  ┗ S(v - 2) ┛ 
   ┏ 1-2*Abs(x)^2+Abs(x)^3      , 0<=Abs(x)<1 ┓ 
S(x)={ 4-8*Abs(x)+5*Abs(x)^2-Abs(x)^3 , 1<=Abs(x)<2 ┃ 
   ┗ 0                , Abs(x)>=2 ┛

S(x)是对 Sin(x*Pi)/x 的逼近(Pi是圆周率——π)

        public enum ZoomType { NearestNeighborInterpolation , BilinearInterpolation }
/// <summary>
/// 图像缩放
/// </summary>
/// <param name="srcBmp">原始图像</param>
/// <param name="width">目标图像宽度</param>
/// <param name="height">目标图像高度</param>
/// <param name="dstBmp">目标图像</param>
/// <param name="GetNearOrBil">缩放选用的算法</param>
/// <returns>处理成功 true 失败 false</returns>
public static bool Zoom(Bitmap srcBmp, double ratioW, double ratioH, out Bitmap dstBmp, ZoomType zoomType)
{//ZoomType为自定义的枚举类型
if (srcBmp == null)
{
dstBmp = null;
return false;
}
//若缩放大小与原图一样,则返回原图不做处理
if ((ratioW == 1.0) && ratioH == 1.0)
{
dstBmp = new Bitmap(srcBmp);
return true;
}
//计算缩放高宽
double height = ratioH * (double)srcBmp.Height;
double width = ratioW * (double)srcBmp.Width;
dstBmp = new Bitmap((int)width, (int)height); BitmapData srcBmpData = srcBmp.LockBits(new Rectangle(, , srcBmp.Width, srcBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
BitmapData dstBmpData = dstBmp.LockBits(new Rectangle(, , dstBmp.Width, dstBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
unsafe
{
byte* srcPtr = null;
byte* dstPtr = null;
int srcI = ;
int srcJ = ;
double srcdI = ;
double srcdJ = ;
double a = ;
double b = ;
double F1 = ;//横向插值所得数值
double F2 = ;//纵向插值所得数值
if (zoomType==ZoomType.NearestNeighborInterpolation)
{//邻近插值法 for (int i = ; i < dstBmp.Height; i++)
{
srcI = (int)(i / ratioH);//srcI是此时的i对应的原图像的高
srcPtr = (byte*)srcBmpData.Scan0 + srcI * srcBmpData.Stride;
dstPtr = (byte*)dstBmpData.Scan0 + i * dstBmpData.Stride;
for (int j = ; j < dstBmp.Width; j++)
{
dstPtr[j * ] = srcPtr[(int)(j / ratioW) * ];//j / ratioW求出此时j对应的原图像的宽
dstPtr[j * + ] = srcPtr[(int)(j / ratioW) * + ];
dstPtr[j * + ] = srcPtr[(int)(j / ratioW) * + ];
}
}
}
else if (zoomType==ZoomType.BilinearInterpolation)
{//双线性插值法
byte* srcPtrNext = null;
for (int i = ; i < dstBmp.Height; i++)
{
srcdI = i / ratioH;
srcI = (int)srcdI;//当前行对应原始图像的行数
srcPtr = (byte*)srcBmpData.Scan0 + srcI * srcBmpData.Stride;//指原始图像的当前行
srcPtrNext = (byte*)srcBmpData.Scan0 + (srcI + ) * srcBmpData.Stride;//指向原始图像的下一行
dstPtr = (byte*)dstBmpData.Scan0 + i * dstBmpData.Stride;//指向当前图像的当前行
for (int j = ; j < dstBmp.Width; j++)
{
srcdJ = j / ratioW;
srcJ = (int)srcdJ;//指向原始图像的列
if (srcdJ < || srcdJ > srcBmp.Width - || srcdI < || srcdI > srcBmp.Height - )
{//避免溢出(也可使用循环延拓)
dstPtr[j * ] = ;
dstPtr[j * + ] = ;
dstPtr[j * + ] = ;
continue;
}
a = srcdI - srcI;//计算插入的像素与原始像素距离(决定相邻像素的灰度所占的比例)
b = srcdJ - srcJ;
for (int k = ; k < ; k++)
{//插值 公式:f(i+p,j+q)=(1-p)(1-q)f(i,j)+(1-p)qf(i,j+1)+p(1-q)f(i+1,j)+pqf(i+1, j + 1)
F1 = ( - b) * srcPtr[srcJ * + k] + b * srcPtr[(srcJ + ) * + k];
F2 = ( - b) * srcPtrNext[srcJ * + k] + b * srcPtrNext[(srcJ + ) * + k];
dstPtr[j * + k] = (byte)(( - a) * F1 + a * F2);
}
}
}
}
}
srcBmp.UnlockBits(srcBmpData);
dstBmp.UnlockBits(dstBmpData);
return true;
}

最近邻插值放大5倍:

双线性插值放大5倍:

c#数字图像处理(十)图像缩放的更多相关文章

  1. Win8 Metro(C#) 数字图像处理--1 图像打开,保存

    原文:Win8 Metro(C#) 数字图像处理--1 图像打开,保存 作为本专栏的第一篇,必不可少的需要介绍一下图像的打开与保存,一便大家后面DEMO的制作.   Win8Metro编程中,图像相关 ...

  2. Win8 Metro(C#)数字图像处理--4图像颜色空间描述

    原文:Win8 Metro(C#)数字图像处理--4图像颜色空间描述  图像颜色空间是图像颜色集合的数学表示,本小节将针对几种常见颜色空间做个简单介绍. /// <summary> / ...

  3. OpenCV - opencv3 图像处理 之 图像缩放( python与c++实现 )

    转自:https://www.cnblogs.com/dyufei/p/8205121.html 一. 主要函数介绍 1) 图像大小变换 cvResize () 原型: voidcvResize(co ...

  4. 数字图像处理,图像锐化算法的C++实现

    http://blog.csdn.net/ebowtang/article/details/38961399 之前一段我们提到的算法都是和平滑有关, 经过平滑算法之后, 图像锐度降低, 降低到一定程度 ...

  5. 数字图像处理:图像的灰度变换(Matlab实现)

    (1)线性变换:通过建立灰度映射来调整源图像的灰度. k>1增强图像的对比度:k=1调节图像亮度,通过改变d值达到调节亮度目的:0 i = imread('theatre.jpg');i = i ...

  6. 数字图像处理界标准图像 Lena 后面的故事

    熟悉图像处理或者压缩的工程师.研究人员和学生,经常在他们的实验或者项目任务里使用"Lenna"或者"Lena"的图像.Lenna 图像已经成为被广泛使用的测试图 ...

  7. opencv3 图像处理(一)图像缩放( python与c++ 实现)

    opencv3 图像处理 之 图像缩放( python与c++实现 ) 一. 主要函数介绍 1) 图像大小变换 Resize () 原型: void Resize(const CvArr* src,C ...

  8. 【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放具体解释

    本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说,主要通过MFC单文档视图实现显示BMP图片空间几何变换.包含图像平移.图形 ...

  9. 【python图像处理】图像的缩放、旋转与翻转

    [python图像处理]图像的缩放.旋转与翻转 图像的几何变换,如缩放.旋转和翻转等,在图像处理中扮演着重要的角色,python中的Image类分别提供了这些操作的接口函数,下面进行逐一介绍. 1.图 ...

随机推荐

  1. Vue学习笔记-目录结构

    1.采用脚手架构建的项目基本目录结构 可能会有些许差别,但是大致基本目录都差不多 2.项目入口(index.html,main.js,App.vue) 一般情况下,我们都习惯性将 index.html ...

  2. UE4 C++ 代码编译方式

    Unreal 有一个非常酷的特性 —> 不必关闭编辑器就可以编译 C++ 更改! 有两种方法可以达到这个目的: 1.直接点击编辑器主工具栏中的 编译(Compile) 按钮. 2.在编辑器继续运 ...

  3. Batch Normalization批量归一化

    BN的深度理解:https://www.cnblogs.com/guoyaohua/p/8724433.html BN: BN的意义:在激活函数之前将输入归一化到高斯分布,控制到激活函数的敏感区域,避 ...

  4. 第二阶段:2.商业需求分析及BRD:6.商业需求文档2

    BRD的三个诉求:1.项目很重要,支持.2.有价值,获得重视,纳入战略规划中.3.需要资源,横向的协调资源.   方法:知道决策层是哪些组成,同时找到合适的决策层. BRD决策分类:1.找资本类(CF ...

  5. PLsql下载官网下载地址

    https://www.allroundautomations.com/registered/plsqldev.html

  6. Java中的Redis 哨兵高可用性

    让我们探索Redis Sentinel,看看如何在Java上运行它,一起来看看,最近get了很多新知识,分享给大家参考学习.需要详细的java架构思维导图路线也可以评论获取! 什么是Redis哨兵? ...

  7. unity3D 自定义公告牌

    前言 有时候我们希望公告牌跟随镜头旋转永远平行面向屏幕,同时跟随镜头缩放缩放大小不变(镜头远离物体,正常物体视觉效果变小,但公告牌视觉大小比例不变),或者跟随镜头缩放变化,本文记录C#脚本的两种实现方 ...

  8. 使用原生JDBC方式对数据库进行操作

    使用原生JDBC方式对数据库进行操作,包括六个步骤: 1.加载JDBC驱动程序 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM.可以通过java.lang.Class类的静态方法forNa ...

  9. lua字符串分割函数[适配中文特殊符号混合]

    lua的官方函数里无字符串分割,起初写了个简单的,随之发现如果是中文.字符串.特殊符号就会出现分割错误的情况,所以就有了这个zsplit. function zsplit(strn, chars) f ...

  10. HTML和JavaScript代码分离、平稳退化(1)

    使用的编辑器是Hbuilder,浏览器是Chrome. HTML和JavaScript代码分离,会使得修改网页功能和代码的阅读与维护会轻松的许多,不用在DOM中阅读大量的JavaScript代码. 文 ...