c#数字图像处理(十)图像缩放
图像几何变换(缩放、旋转)中的常用的插值算法
在图像几何变换的过程中,常用的插值方法有最邻近插值(近邻取样法)、双线性内插值和三次卷积法。
最邻近插值:
这是一种最为简单的插值方法,在图像中最小的单位就是单个像素,但是在旋转个缩放的过程中如果出现了小数,那么就对这个浮点坐标进行简单的取整,得到一个整数型坐标,这个整数型坐标对应的像素值就是目标像素的像素值。取整的方式就是:取浮点坐标最邻近的左上角的整数点。
举个例子:
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#数字图像处理(十)图像缩放的更多相关文章
- Win8 Metro(C#) 数字图像处理--1 图像打开,保存
原文:Win8 Metro(C#) 数字图像处理--1 图像打开,保存 作为本专栏的第一篇,必不可少的需要介绍一下图像的打开与保存,一便大家后面DEMO的制作. Win8Metro编程中,图像相关 ...
- Win8 Metro(C#)数字图像处理--4图像颜色空间描述
原文:Win8 Metro(C#)数字图像处理--4图像颜色空间描述 图像颜色空间是图像颜色集合的数学表示,本小节将针对几种常见颜色空间做个简单介绍. /// <summary> / ...
- OpenCV - opencv3 图像处理 之 图像缩放( python与c++实现 )
转自:https://www.cnblogs.com/dyufei/p/8205121.html 一. 主要函数介绍 1) 图像大小变换 cvResize () 原型: voidcvResize(co ...
- 数字图像处理,图像锐化算法的C++实现
http://blog.csdn.net/ebowtang/article/details/38961399 之前一段我们提到的算法都是和平滑有关, 经过平滑算法之后, 图像锐度降低, 降低到一定程度 ...
- 数字图像处理:图像的灰度变换(Matlab实现)
(1)线性变换:通过建立灰度映射来调整源图像的灰度. k>1增强图像的对比度:k=1调节图像亮度,通过改变d值达到调节亮度目的:0 i = imread('theatre.jpg');i = i ...
- 数字图像处理界标准图像 Lena 后面的故事
熟悉图像处理或者压缩的工程师.研究人员和学生,经常在他们的实验或者项目任务里使用"Lenna"或者"Lena"的图像.Lenna 图像已经成为被广泛使用的测试图 ...
- opencv3 图像处理(一)图像缩放( python与c++ 实现)
opencv3 图像处理 之 图像缩放( python与c++实现 ) 一. 主要函数介绍 1) 图像大小变换 Resize () 原型: void Resize(const CvArr* src,C ...
- 【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放具体解释
本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程<数字图像处理>及课件进行解说,主要通过MFC单文档视图实现显示BMP图片空间几何变换.包含图像平移.图形 ...
- 【python图像处理】图像的缩放、旋转与翻转
[python图像处理]图像的缩放.旋转与翻转 图像的几何变换,如缩放.旋转和翻转等,在图像处理中扮演着重要的角色,python中的Image类分别提供了这些操作的接口函数,下面进行逐一介绍. 1.图 ...
随机推荐
- gulp 批量添加类名 在一个任务中使用多个文件来源
1.首先安装环境 1.安装gulp: npm install gulp 2.安装gulp-clean-css npm install gulp-clean-css 3.安装gulp-css-wrap ...
- dotnet 手动解决 json 解析中不合法字符串
如果使用 Newtonsoft Json 解析字符串,字符串里面有不清真的格式,那么默认的解析将会炸掉.如果想要自己解决字符串中的不清真格式,可以使用传入 JsonSerializerSettings ...
- python监控模块
pip install psutil 获取内存信息: >>> import psutil >>> mem = psutil.virtual_memory() #获取 ...
- 小白学 Python 爬虫(30):代理基础
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
- Rxjava2 介绍与详解实例
目录 前言 RX介绍 Rx模式 Rx使用依赖: Rxjava的入门基础 1. Observable 2. Flowable 3. Single 4. Completable 5. Maybe 6. S ...
- Redis的高并发、持久化、高可用架构设计
就是如果你用redis缓存技术的话,肯定要考虑如何用redis来加多台机器,保证redis是高并发的,还有就是如何让Redis保证自己不是挂掉以后就直接死掉了,redis高可用 我这里会选用我之前讲解 ...
- 如何在Pypi发布上传你自己的Python库
Pypi上传包 准备 Windows环境 *以下教程只能在Windows上执行,MACOS系统不能使用 注册账号 你需要在Pypi上注册一个账号. 安装必要的库 setuptools 原则上安装了pi ...
- js原生深拷贝
/*****************************************************************************************/ 原生js实现深拷 ...
- es lucene搜索及聚合流程源码分析
本文以TermQuery,GlobalOrdinalsStringTermsAggregator为例,通过代码,分析es,lucene搜索及聚合流程.1:协调节点收到请求后,将search任务发到相关 ...
- 悄摸直播(二)—— 播流器实现(拉取rtmp服务器中的数据流,播放直播画面)
悄摸直播 -- JavaCV实现本机摄像头画面远程直播 播流器 一.功能说明 从rtmp服务器中获取视频流数据 + 展示直播画面 二.代码实现 /** * 播流器 * @param inputPath ...