zoukankan      html  css  js  c++  java
  • 谈谈数字图像的缩放算法[转]

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

    图像的概念很容易理解,你睁开眼睛,所看到的都是图像了。而一幅画、一张照片,则是现实生活中记录图像的手段和载体。在科学上,我们需要对我们的研究对象建立起数学模型,因此有必要建立起图像的数学模型。一幅图像的数学模型可以简单的定义如下:
    +-----------------------------------------------------+
        Image = f(x, y);
        其中 x, y 为 [0, 1] 上的实数
        对于灰度图像 Image 也为 [0, 1] 上的实数
        对于彩色图像 Image 则由 R, G, B 三个分量组成
    +-----------------------------------------------------+
    由于定义域和值域都在 [0, 1] 上,因此被称为连续图像模型
    连续图像模型可以精确而完整的刻画所要描述的图像,然而在现实世界中,绝大多数图像都是无法通过这个数学模型进行描述的,因为现实世界中的图像,是不可能通过函数解析式的方法进行描述的。更多的时候,我们只能使用相机将现实图像的一部分信息,保存在胶片上,或者是使用画笔在画纸上绘制出图像。正是由于图像的这个特点,我们所建立的连续的图像模型,对研究图像而言,并没有什么用武之地,而传统的数学研究方法也因此无法用运用到图像上。

    为了更加有效的研究和处理图像,我们利用离散数学的理论知识,为现实图像建立起了数字图像模型,并且使用计算这个强大的工具来帮助我们研究和处理图像。数字图像模型的定义如下:
    +-----------------------------------------------------+
        Image = array(i, j);
        通常情况下 i 是大于等于 0, 小于 w 的整数
        而 j 是大于等于 0, 小于 h 的整数
        array 可以理解为一个矩阵
        Image 的取值范围为大于等于 0, 小于等于 255 的整数
        对于灰度图像 Image 表示某点的亮度值
        对于彩色图像 Image 则由 R, G, B 三个分量组成
    +-----------------------------------------------------+
    其中的 w, h 通常被称为一幅数字图像的宽度和高度。这里,数字图像的宽度和高度,与一幅数字图像在显示器上实际的宽度和高度,有着一定的对应关系的,这个大家应该都很容易理解。我们所要讨论的数字图像的缩放问题,就是要改变一幅图像的宽度和高度,并且使 array(i, j) 这个矩阵中的数据相应的改变,使得图像按比例的进行缩放。

    在数字图像处理上,数字图像的缩放又被称作重采样滤波,这很抽象但是却又深刻揭示出数字图像缩放的本质。当你将重采样滤波这个概念理解了,你会发现位图缩放是如此的简单明了,并会惊奇的发现重采样滤波是如此的神奇而深刻。

    现在为了叙述的方便,我们将现实生活中所遇到的图像称为现实图像,将照片等称为物理图像,将连续图像模型成为连续图像,将数字图像模型成为数字图像或位图。那么,我们来看看一幅数字图像的产生过程。

    首先,需要使用相机对现实图像进行拍照,产生照片,即物理图像。在物理图像中,仅仅记录了现实图像中的一部分信息。然后要将照片放到扫描仪上进行扫描,这个过程中图像从物理图像变换为了数字图像,在计算机中产生通常所说的位图。在图像从物理图像变换为数字图像这个过程中,最关键的地方就是采样与量化,这两个概念大家也许都非常熟悉,但是我们仍然需要深刻的理解和思考。

    假如说,在扫描的过程中,我们将扫描的分辨率设置较大,也就是采样率设置较大,则扫描出来的数字图像的分辨率也较大,也就是图像的宽度和高度都较大,反之,则是变小。让我们再来深刻的理解下重采样滤波这个概念,所谓重采样滤波,指的是根据数字图像,以某种方式重建出物理图像,并且对这个重建出来的物理图像,以所需要的采样率进行重新采样。

    让我们来设想这样一个过程,我们有一幅宽高分别为 (w1, h1) 的位图 A,我们想要将其缩放为宽高为 (w2, h2) 的位图 B. 我们可以通过这样的手段来完成缩放,也就是花一千块左右,买一台佳能的彩色激光打印机,将位图 A 用打印机打印出来。然后再买一个惠普的扫描仪,以 (w2, h2) 的扫描分辨率,将前面打印出来的图片扫描到电脑。这样,我们就顺利地完成了位图的缩放。当然,这样的做法成本太高,先后需要花去一千多块的大洋,而且费时又费力,是个理论上可行却不实用的办法。但是,这个办法,足以生动而清晰地说明数字图像缩放的本质和方法。

    让我们再来体会一下重采样滤波的深刻吧,即重建物理图像,然后以你所需要的分辨率进行重新采样。在计算机世界里,数字图像是很容易描述的,用一个二维数组就可以简单的描述一幅数字图像。然而,我们怎样才能从已有的数字图像,重建其物理图像呢?好好想想吧,想不出来就只快点准备两千块大洋,去买打印机和扫描仪吧。不是吧大哥,难道我真要去买打印机,赶快揭晓答案。

    好吧不开玩笑了,现在揭晓答案。在计算机中,重建物理图像其实是一种计算模型而已,物理图像在计算机世界里面是无法真正的重建的,毕竟计算机是离散系统,而物理图像是连续的事物,计算机无法完整而精确的进行描述。然而我们却可以找到多种的计算模型,来描述我们需要重建的物理图像。需要注意的是,我们找到的是计算模型,然后我们要根据这个计算模型,来进行重新采样。

    图像从现实图像到物理图像再到数字图像的变换过程,是一个不可逆的变换过程,在每一次变换过程中,都会丢失掉大量的信息,是不可逆的。如果想要从数字图像重建物理图像,其实是在已有的数字图像数据的基础上,对物理图像做出的一种近似而已。对于已有的数字图像,我们有多种计算模型,来重建物理图像,而这个重建的物理图像,一般都是通过前面所讲的连续图像模型来描述。先介绍最简单的计算模型,最近邻算法。

    请大家思考这样一些问题:
        1. 我有一张 320 * 240 * 24bit 色的 BMP 图片,对于图片上任一点 (i, j) 我们都可以知道它的 RGB 颜色值,但是如果我想知道 (101.3, 98.6) 点的颜色值,我们该怎么办呢?

        2. 我希望将一张 320 * 240 * 24bit 色的 BMP 图片,缩放为 1005 * 754 * 24bit 色的 BMP 图片,该如何进行重新采样呢?

    第一个问题,也就是数字图像缩放的第一步,重建物理图像。如果我们采用最近邻算法,对于一个非整数的坐标点,我们选取距离这个点最近的整数坐标点的颜色值,作为其颜色值。也就是说,我们简单的将 (101.3, 98.6) 的颜色值取为点 (101, 99) 的颜色值。解决了这个问题,我们的脑子里面其实就放着一幅连续的图像了,并且对于这个连续图像上的任意一个像素点,我们都可以计算出其颜色值。

    第二个问题,则是如何在重建起来的物理图像上进行重采样。所谓采样,就是要取得每个需要采样的颜色值,也就是要用一个二重循环,处理完 1005 * 754 个点,并且计算出每个点的颜色值。简单的代码如下:
    +-------------------------------------------------------------------------+
        DWORD color;
        int   i;
        int   j;
        for (j=0; j<754; j++)
        {
            for (i=0; i<1005; i++)
            {
                color = resample(i, j);         // 计算重采样点的颜色值
                putpixel(destbmp, i, j, color); // 将采样点绘制到目的位图上
            }
        }
    +-------------------------------------------------------------------------+

    对于每个重采样点的颜色值的计算方法,则是需要根据原始的数字图像、你所选用的计算模型和由计算模型所重建的物理图像来共同决定。对于我们上面的例子,可以先计算出宽高缩放比,进而建立起原始图像和目的图像中像素点的对应关系和变换公式,根据变换公式,计算出目的图像中 (i, j) 点在 原始图像中对应的点的坐标 (x, y),根据重建物理图像的计算模型,计算出原始图像中 (x, y) 点的颜色值 c,然后将 c 作为结果绘制到目的图像中的 (i, j) 上。当你处理完目的图像中的每一个像素点,也就完成了数字图像的缩放了。

    对于我们所举的例子,可以写出如下的代码:
    +-------------------------------------------------------------------------+
        int   w1     = 320;
        int   h1     = 240;
        int   w2     = 1005;
        int   h2     = 754;
        float xratio = (float) w1 / w2;
        float yratio = (float) h1 / h2;
        int   i;
        int   j;
        float x;
        float y;

        for (j=0; j<754; j++)
        {
            for (i=0; i<1005; i++)
            {
                x = i * xratio;
                y = j * yratio;
                color = getpixel(srcbmp, (int)(x + 0.5), (int)(y + 0.5));
                putpixel(destbmp, i, j, color);
            }
        }
    +-------------------------------------------------------------------------+

    其中 | x = i * xratio; y = j * yratio; | 两句代码体现采样的位置,即在重建出来的图像的什么位置进行采样,而 | color = getpixel(srcbmp, (int)(x + 0.5), (int)(y + 0.5)); | 这句代码则体现了采样的结果,即在重建出来的图像的 (x, y) 处的采样的颜色值到底是多少。

    请大家认真体会其中的深刻意义,所谓重建物理图像,实际上是无法真正重建的,也就是说这只是一种计算模型,一种方法。而重采样滤波,则是在计算模型的基础上进行的重新采样。数字图像缩放的精妙和深刻之处,就被重采样滤波这样一段话,简短而深刻地概括了。

    通过以上的介绍,大家应当明白了数字图像缩放的基本原理和实现方法,并且能够利用最近邻法的计算模型,写出通用的位图缩放程序。可以看到,位图缩放的算法框架其实是非常简单的,关键还是需要理解其中的原理和方法。位图缩放的最近邻算法,在我的图形库中已经实现,大家可以在我的图形库中找到代码进行参考。

    接下来介绍更多的重建物理图像的计算模型。除了最近邻点算法之外,还有双线性插值算法和曲面插值算法。其中双线性插值算法比较简单,且易于计算,因此在工程上得到了广泛的运用。而曲面插值可以得到更好的图像效果,但是其插值原理和计算方法都比较复杂,本文不再介绍。

    所谓线性插值,大家应该不陌生,举个最简单的例子来说明问题。f(99) = 32, f(100) = 65, 用线性插值法求 f(99.3) 的值。方法如下:

      f(100) - f(99)      f(100) - f(99.3)
    ----------------- = --------------------
        100 - 99             100 - 99.3

    根据以上方程即可求出 f(99.3) 的值。之所以称为线性,是由于三个点都位于一条直线上。将以上的线性插值推广到二维情况,即是我们通常所说的双线性插值。

    同样用一个例子说明问题:
    f(123, 221) = 231, f(124, 221) = 35
    f(123, 222) = 213, f(124, 222) = 86
    求 f(123.8, 221.2) 的颜色值。
    解,利用一维的线性插值,根据 f(123, 221)   和 f(123, 222)   求得 f(123, 221.2)
        利用一维的线性插值,根据 f(124, 221)   和 f(124, 222)   求得 f(124, 221.2)
        利用一维的线性插值,根据 f(123, 221.2) 和 f(124, 221.2) 求得 f(123.8, 221.2)
        问题得解。

    在双线性插值中,大家可以证明先沿 x 坐标计算和先沿 y 坐标计算,最终的结果都是一样的。当然,在实际运用中,大家还需要推算出通用的计算公式,在这里仅仅是以例子说明问题。

    最后我们来总结一下吧。
    数字图像缩放的基本原理:
        1. 根据已有的数字图像重建物理图像
        2. 对重建的物理图像以所需要的分辨率重采样

    数字图像缩放的基本方法:
        1. 建立起重建物理图像的计算模型
        2. 建立起目的图像与原始图像的坐标变换关系
        3. 计算目的图像中点 (i, j) 在原始图像中对应点的坐标 (x, y)
        4. 根据计算模型计算出原始图像中点 (x, y) 处的颜色值 c
        5. 将目的图像中点 (i, j) 的颜色值设置为颜色值 c
        6. 处理完目的图像上每一个像素点,即可完成缩放

    讲了这么多,应该是把数字图像缩放这个问题介绍得比较清楚了。大家关键还是需要理解数字图像的本质,采样和量化的概念,以及根据数字图像重建物理图像和重采样,即重采样滤波。最后再告诉大家两个概念,即数字图像的缩小,被称为降采样滤波,而放大则称为过采样滤波(这个不知道是不是这样叫的,就算我自己发明的叫法吧)。希望大家再看完本文以后,能够有所收获,理解数字图像缩放的原理和方法,并且实现自己的位图缩放程序。

    原文:http://bbs.bccn.net/thread-198397-1-2.html

  • 相关阅读:
    String&StringBuffer&StringBuilder区别
    linux启动流程简介
    nginx视频直播/点播服务干货分享
    Http协议的认识
    php中对象是引用类型吗?
    nginx与php-fpm 504 Gateway Time-out 排查与解决案例
    php header函数常见用途
    php魔术方法
    ajax 和jsonp 不是一码事 细读详解
    php session redis 配置
  • 原文地址:https://www.cnblogs.com/hwl1023/p/4783486.html
Copyright © 2011-2022 走看看