图画缩放是数字图画处理中常用的技术之一。跟着数字媒体的遍及,图画缩放算法变得越来越重要。本文将探讨图画缩放的原理,侧重介绍两种常用的插值算法——最近邻插值和双线性插值,并供给对应的代码完结。咱们将解释这些算法的作业原理,以及怎么挑选最适合您应用场景的算法。
开发中或许非专业的开发人员外,其他人不会对其涉猎,可是图画的缩放这个话题确实值得咱们学习和研究。
图画:包含图片和视频,当你看视频时全屏变小窗,小窗变大屏等都用到了图画缩放,就视频而言,不同分辨率的切换也用到了缩放算法,比方,电影分辨率是 1080P,播映器的窗口大小是 720P,则需求将电影画面从 1080P 缩小到 720P 再播映。假如你点击全屏播映,播映窗口变成了 4K,则需求将电影画面做扩大处理,即扩大到 4K 之后再播映。
- 那图画缩放算法都有哪些呢?
- 他们的原理是什么?
- 在Android 开发中怎么运用

这便是我今日要评论的问题。
图画缩放算法
图画缩放算法能够分为两类:插值算法和基于改换的算法。下面是一些常见的图画缩放算法:
- 最近邻插值算法(Nearest Neighbor Interpolation):最简略的插值算法,关于每个缩放后的像素点,挑选与其最近的原始像素点的值作为它的值。该算法容易完结,但会导致图画呈现锯齿状的边际。
- 双线性插值算法(Bilinear Interpolation):该算法在最近邻插值算法的基础上,加入了对相邻四个像素点的加权均匀,使得图画边际愈加滑润。
- 双三次插值算法(Bicubic Interpolation):该算法在双线性插值算法的基础上,对相邻16个像素点进行加权均匀,得到愈加滑润的图画。
- Lanczos插值算法(Lanczos Interpolation):该算法运用了一种卷积办法,经过对像素点周围的采样点进行加权均匀,得到缩放后像素点的值。该算法在保持图画细节的一起,会对图画进行细微含糊。
- Sinc插值算法(Sinc Interpolation):该算法基于信号处理中的Sinc函数,运用卷积办法对像素点进行加权均匀。该算法在保持图画细节的一起,会对图画进行必定的含糊,但比Lanczos插值算法的含糊程度小。
- 基于改换的算法:除了插值算法,还有一些基于改换的算法,如双线性改换、双三次改换、图画金字塔等。这些算法会对原始图画进行必定的改换,再进行缩放。这些算法通常能够发生更高质量的缩放成果,但核算杂乱度也更高。
这些算法已经在职业中有较多的材料和运用文献,我这里简略梳理一下他们的原理以及在Android中怎么运用这些算法。
缩放的原理
首先要清晰一点便是图画的缩放便是将原图画的已有像素经过加权运算得到方针图画的方针像素。
比方说,咱们已有图画是 720P 的分辨率,称之为原图画,咱们需求扩大到 1080P,咱们称这个 1080P 图画是方针图画。
方针图画在宽度方向上扩大了 1920 / 1280 = 1.5 倍,高度方向上也扩大了 1080 / 720 = 1.5 倍。
那怎么经过720的图画生成1080的图画呢?扩大之后会不会存在空隙,这个空隙是否会导致图画“发虚”呢?
做法如下:
先将方针图画的像素方位映射到原图画的对应方位上,然后把经过插值核算得到的原图画对应方位的像素值作为方针图画相应方位的像素值
这样操作之后是不是就没有空隙了。这便是插值算法要干的事了。
1080P 方针图画中的(0,0)方位就映射到 720P 原图画的(0,0)方位,取原图画(0,0)方位的像素值作为方针图画(0,0)方位的像素值。方针图画的(1,1)方位就映射到原图画中的(0.67, 0.67)方位。最后,经过原图画已有像素插值得到(0.67,0.67)方位的像素值,并将该像素值作为方针图画(1,1)方位的像素值。
下图中,显现了720p 扩大到1080p的图示和 720p缩小到360p的图示

经过这个演示,能够总结一下图画缩放的一般进程:
扩大进程 关于 1080P 方针图画中的每一个像素点(x,y),咱们只需求将它映射到 720P 原图画的(x / 1.5,y / 1.5)方位,经过原图画已有的像素值插值得到(x / 1.5,y / 1.5)的像素值就能够了。
缩小进程 关于 360P 方针图画中的每一个像素点(x,y),咱们只需求将它映射到 720P 原图画的(x * 2,y * 2)方位,经过原图画已有的像素值插值得到(x * 2,y * 2)的像素值就能够了。
然后二者的操作进程是,遍历一下方针图画中的每一个像素点方位,都能找到他们在原图画中的映射方位,并经过插值求出映射方位的像素值,这样就能够得到方针图画了
咱们能够推测出,缩放的代码逻辑是:
- 获取方针图画的宽高;
- 然后获取获取像素,完结像素插值
- 将处理过的像素创立新的图画
不用着急 后边能够验证该推测。
缩放的通用表达式
假定原图画的分辨率是 w0 x h0,咱们需求缩放到 w1 x h1。那咱们只需求将方针图画中的像素方位(x,y)映射到原图画的(x * w0 / w1,y * h0 / h1),再插值得到这个像素值就能够了,这个插值得到的像素值便是方针图画像素点(x,y)的像素值。注意,(x * w0 / w1,y * h0 / h1)绝大多数时分是小数。这便是图画缩放算法原理的通用表达 示图如下:

目前比较流行的插值算法根本有三种。
插值算法
最近邻插值算法
最近邻插值算法是一种根本的图画插值算法,它的根本思想是关于待插值像素的方位,挑选间隔该方位最近的一个已知像素值作为插值成果。因而,该算法得名为“最近邻插值”
最近邻插值算法的详细完结进程如下:
- 确认待插值像素方位,假定其坐标为(x,y)。
- 找到离(x,y)最近的已知像素,假定其坐标为(x1,y1)。
- 将该已知像素的像素值赋值给待插值像素。
还是看图画从720p 扩大到 1080p,这个例子,咱们上面说了缩放的原理便是映射像素,行将方针图画的像素映射到原图画。
1080p 假定(2,2)的方位,最附近插值法的取值进程是:
映射到 720P 图画,映射方位是(2 * 1280 / 1920,2 * 720 / 1080),也便是(1.33,1.33)方位,其周围 4 个像素别离是(1,1)、(1,2)、(2,1)和(2,2),很显着(1,1)离(1.33,1.33)方位最近,那咱们取原图画(1,1)的像素值赋值给 1080P 图画的(2,2)方位的像素点。

左图为扩大进程,右图为扩大时取值图示
- 所以1080 待插值的方位(2,2)
- 找到了离(2,2)最近的已知像素(1.33,1.33)
- 将(1.33,1.33)方位的像素赋值给(2,2),完结插值。
代码完结
- Java代码开发
最近邻插值算法java代码_图画最近邻插值算法_异域拾荒人的博客-CSDN博客
- 运用Android JNI开发
运用opencv 库开发,由于需求获取像素等操作,最核心的代码为:
// 依据缩放份额核算输出图画的宽高
int new_width = static_cast<int>(inputMat.cols * scale);
int new_height = static_cast<int>(inputMat.cols * scale);
//创立输出的Mat对象
outputMat.create(new_height, new_width, CV_8UC4);
// 遍历每个像素
for (int i = 0; i < new_height; i++) {
for (int j = 0; j < new_width; j++) {
//获取原始像素方位
int orig_i = static_cast<int>(i / scale);
int orig_j = static_cast<int>(j / scale);
//将原始像素拷贝到输出Mat对象中
outputMat.at<Vec4b>(i, j) = inputMat.at<Vec4b>(orig_i, orig_j);
}
}
优缺陷
- 长处:
是简略易完结,核算速度快
- 缺陷
- 于其只考虑最近邻像素的值,而疏忽了其他像素的信息,因而会导致图画插值后的成果较为粗糙,缺乏细节和滑润性
- 直接运用离插值方位最近的整数方位的像素作为插值像素,这样会导致相邻两个插值像素有很大的概率是相同的
应用场景
快速预览图画或图画缩小等场景
双线性插值算法
双线性插值算法是一种常用的图画插值算法,它能够经过对周围4个已知像素进行加权均匀来核算待插值像素的像素值,从而获得更为滑润和细腻的插值成果。听着就比最近邻插值算法杂乱,可是作用肯定比它优。
双线性插值算法的详细完结进程如下:
- 确认待插值像素方位,假定其坐标为(x,y)。
- 找到间隔(x,y)最近的四个已知像素,假定它们的坐标为(x1,y1)、(x2,y2)、(x3,y3)、(x4,y4)。
- 核算待插值像素的像素值。假定待插值像素的像素值为f(x,y),则能够经过下面的公式核算:
其间,w和h别离表明待插值像素相关于(x1,y1)和(x2,y2)的水平间隔和垂直间隔,详细核算方法如下:
- 将核算得到的像素值赋给待插值像素。
线性插值
线性插值是一种数学办法,用于在两个已知点之间估量不知道点的值。它假定两个已知点之间的函数是线性的,因而能够运用直线方程来核算不知道点的值。
线性插值以为,这个需求插值得到的点跟这两个已知点都有必定的联系,并且,待插值点与离它近的那个点更类似。因而,线性插值是一种以间隔作为权重的插值方法,间隔越近权重越大,间隔越远权重越小。
比方,如下图所示,已知 (x1,y1) 与 (x2,y2)两个点,需求得 x 对应的 y 值

它表明在两个点 (x1,y1) 和 (x2,y2) 之间,关于给定的 x 值,对应的 y 值能够经过这个公式核算得到。当 x=x1 时,y=y1;当 x=x2 时,y=y2。当 x 在 x1 和 x2 之间时,y 的值在 y1 和 y2 之间线性变化。
双线性插值
双线性插值本质上便是在两个方向上做线性插值。由于图画是两个方向的二维数据,正好适合运用双线性插值算法
双线性插值其实便是三次线性插值的进程,咱们先经过两次线性插值得到两个中心值,然后再经过对这两个中心值进行一次插值得到终究的成果
假定咱们有一个矩形网格,其间四个顶点的坐标别离为 (x1,y1),(x1,y2),(x2,y1) 和 (x2,y2),并且这些点的函数值别离为 f(x1,y1),f(x1,y2),f(x2,y1) 和 f(x2,y2)。咱们想要核算点 (x,y) 的函数值,其间 x 和 y 别离在 x1 和 x2 之间,在 y1 和 y2 之间。

沿着x轴进行线性插值,得到两个临时值
这个即图中的n点
即图的m点
再沿着y轴线性插值
即图中p
以 720P 扩大到 1080P 为例,那么 1080P 图画中的方针像素点(2,2)的双线性插值进程是怎么样的呢?
首先,将方针像素点(2,2)映射到原图画的(1.33,1.33)方位,对应下面图中的点p。找到(1.33,1.33)周围的 4 个像素(1,1)、(2,1)、(1,2)和(2,2),别离对应图中的点a、b、c和d。

依据上述的公式,咱们能够核算出图中的n、m 最后求出p 的像素
首先n
m:
p点:
值求得(1.33,1.33)的值之后,将其赋值给 1080P 方针图画的(2,2)方位的像素点就能够了。这便是双线性插值的进程。
优缺陷
- 长处
能够获得相对较为滑润和细腻的插值成果,并且核算速度也比较快
- 缺陷
- 只考虑了周围4个像素的信息,因而在图画扩大的情况下,仍然会呈现锯齿状的作用
- 在图画存在大幅度变化或杂乱纹理的情况下,双线性插值算法或许会导致图画失真或呈现马赛克作用
应用场景
图画扩大、缩小和旋转等场景
双三次插值算法
//todo
总结
此文章主在弄明白图画缩放的原理,并完结了经典算法插值算法的最近邻插值算法,当然,最近邻插值算法的运用场景单一,也不是作业中常用的算法,后续会连续完结双三次插值算法。 双三次算法图画质量方面有显着提升,特别是在缩小图画时,双三次插值算法能够更好地保留图画的细节和纹理。
源码
- 已完结最附近插值算法代码
Android-Scaling-algorithm-for-image
参考
- OpenCV On Android最佳环境配置攻略(Android Studio篇)
- 缩放算法:怎么高质量的缩放图画?