1. 定义(摘自维基百科)
在计算机图形学中,图像缩放指的是通过增加或去掉像素来改变图片的尺寸。由于要在效率和图像质量比如平滑度和清晰度之间做折衷,图像缩放并不是个简单的过程。当图像尺寸增大的时候,组成图像的像素也越来越大,图像看上去就变"柔和"了。而缩小图像的时候,图像就变得平滑和清晰了。
除了为了适应显示区域而缩小图片外,图像缩小技术更多的是被用来产生预览图片。图像放大技术一般被用来令一个较小的图像填充一个大的屏幕。当你放大一张图像时,你不能可能获得更多的细节,因此图像的质量将不可避免的下降。不过也有很多技术可以保证在放大图像即增加像素的时候,图像的质量不变。
2.方法及实现
2.1 图像缩小
为了能够更好的观察效果,选用了比较小的图片(200*200)进行处理。
图像缩小相对比较简单,由于缩小必定损失信息量,所以个人感觉需要考虑的是怎么尽可能多的保留原始信息。
最简单的方法为直接选取部分数据。譬如,图像缩小一倍,则隔一行(列)取一个数据,图像缩小为原来的四分之一,则隔三行(列)取一个数据。
处理结果:
这种方式相当于直接舍去了1/2(3/4)的信息,于是尝试通过取相邻像素的平均值代替原来单纯的一个像素以尽可能多的保留原始信息。
分析:
可以看到,后者得到的图片效果明显好于前者。在进行图像缩小操作时,要考虑的是在图像信息必然损失的情况下,尽可能多的保留原始信息。
第一种处理关键代码为:
for (int i = 0; i < height; i+=3){
for (int j = 0; j < width; j+=3){
PicZoomOut[i / 4][j / 4] =Pic[i][j];
}
}
第二种处理关键代码为:
for(int i = 0; i < 100; i++){
for(int j = 0; j < 100; j++){
PicZoomOut[i][j] = (Pic[i * 2][j * 2]+ Pic[i * 2 + 1][j * 2] + Pic[i * 2][j * 2 + 1] + Pic[i * 2+ 1][j * 2 + 1])/4;
}
}
图像缩小,第二种方法,缩为原来1/4,代码
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#define height 200
#define width 200
typedef unsigned char BYTE; // 定义BYTE类型,占1个字节
int main(void)
{
FILE *fp = NULL;
BYTE PicZoomOut[100][100];
BYTE *ptr;
BYTE **Pic = new BYTE *[height];
for (int i = 0; i != height; ++i)
{
Pic[i] = new BYTE[width];
}
fp = fopen("1.raw", "rb");
ptr = (BYTE*)malloc(width * height * sizeof(BYTE));//创建内存
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
fread(ptr, 1, 1, fp);
Pic[i][j] = *ptr; // 把图像输入到2维数组中,变成矩阵型式
ptr++;
}
}
fclose(fp);
for (int
i = 0; i < 50; i++)
{
for (int
j = 0; j < 50; j++)
{
PicZoomOut[i][j]
= (Pic[i * 4][j * 4] + Pic[i * 4 + 1][j * 4] + Pic[i * 4 + 2][j *
4] + Pic[i * 4 + 3][j * 4] + Pic[i * 4][j * 4 + 1] + Pic[i * 4 + 1][j * 4 + 1] + Pic[i * 4 + 2][j * 4 + 1] + Pic[i * 4 + 3][j * 4 + 1] + Pic[i * 4][j * 4 + 2] + Pic[i * 4 + 1][j * 4 + 2] + Pic[i * 4 + 2][j * 4 + 2] + Pic[i * 4 + 3][j * 4 + 2] + Pic[i * 4][j * 4 + 3] + Pic[i * 4 + 1][j * 4 + 3] + Pic[i * 4 + 2][j * 4 + 3] + Pic[i * 4 + 3][j * 4 + 3])/16;
}
}
fp = fopen("output.raw", "wb");
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 50; j++)
{
fwrite(&PicZoomOut[i][j], 1, 1, fp);
}
}
fclose(fp);
return 0;
}
2.1 图像放大
为了能够更好的观察效果,选用了比较小的图片(100*100)进行处理。
图像放大由于信息量已定,所以个人感觉需要考虑的是怎么尽可能多的使用原始信息并且能呈现出比较好的放大效果。。
最简单的方法为近邻取样插值,也称零阶插值。它输出的像素灰度值就等于距离它映射到的位置最近的输入像素的灰度值。但当图像中包含像素之间灰度级有变化的细微结构时,最邻近算法会在图像中产生人为加工的痕迹。
测试如下:
放大后的图像保留了原始图像的所有细节,但结果并不让人满意,有阶梯状锯齿。于是尝试采用其他缩放方法。
双线型内插值算法是一种比较好的图像缩放算法,它充分的利用了源图中虚拟点四周的四个真实存在的像素值来共同决定目标图中的一个像素值,因此缩放效果比简单的最邻近插值要好很多。
对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(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)处的的像素值,以此类推。
效果如下:
可见,相比于邻取样插值算法,这个算法的效果就好很多。不过导致了不期望的细节柔化。
在PS软件中用来图像放大的最佳算法是二次立方,测试如下:
双立方插值计算涉及到16个像素点, 而最终插值后的图像中的(x, y)处的值即为16个像素点的权重卷积之和
将上面三种算法得到的结果依次放到一起对比观察如下
分析:显然,近邻取样插值效果最差。虽然保留了原始图像的所有细节,但出现了阶梯状锯齿。双线型内插值效果与二次立方插值效果都比近邻取样插值好,二者相比,双线型内插值有一点细节柔化(比如眼珠部位)。
综上,二次立方插值效果最好。
本工程源码已更新至github,欢迎star,欢迎PR:)