Bilinear双线性插值算法

双线性插值缩放算法

最近在研究图形图像的一些算法,看到SIFT算法时,上边“金字塔”的实现需要对原始图像缩放,于是乎需要先实现一下缩放这部分,为此特意查了一下,就选择了这个最基本的双线性插值法

双线性插值算法的原理各个博客上基本都有所描述了,其基本原理如下:


  • width * height的图像img1进行(scaleX, scaleY)的系数进行放大成为nWidth * nHeight的图像img2
  • img2中填充根据img1的颜色而计算出来的新颜色,此时有如下过程:

f(x, y)img1的(x, y)点的颜色值,则被放大的图片每一点的颜色函数F(x, y)满足下面公式

F(x,y)[1dxdx][f(x0,y0)f(x0,y1)f(x1,y0)f(x1,y1)][1dydy]

其中 dxF(x, y)折算为 f(x, y)x的小数部分, dyF(x, y)折算为 f(x, y)y的小数部分。

为了比较好的理解上面的部分,我们举个例子:

  • 将下面图片放大2倍
    [1324]>1??3(?)???????2??4

    上面的?是我们要填充的值,就是所谓的插值
  • 我们如何计算有边图像(0, 1)点也就是(?)的颜色?
  • 需要将(0, 1)这算成相对于左侧图片的坐标,由于放大系数为,那么我们会很正常的以为这个这算回去的坐标应该为(0, 0.5)
  • 上面的计算有个坑,之前看其他的博客中以及其程序中均未特别说明这个事。因为插值是在中间插,我们在计算缩放比率的时候应该使用坑的数目而不是点的数目,也就是这算回去的比率应该为
    Width1weight1=4121=3

    也就是算回去的坐标应该是(0, 0.33)

不注意上面的细节会导致计算右边界和下边界时这算回去的坐标超出原图范围,导致引用错误或者放大图出现黑边,该算法演示效果可点击demo

具体实现参加下(JS版):

/**
 * 双线性插值法
 * @file Bilinear.js
 * @author Naixor
 */
define(function (require, exports, module) {
    var util = require('../utils/util');

    var getPixelPosition = function (width, x, y) {
        return (x + y * width) << 2;
    }

    var calculateInterpolation = function (data, width, x, y) {
        var x0 = Math.floor(x);
        var y0 = Math.floor(y);
        var x1 = Math.ceil(x);
        var y1 = Math.ceil(y);
        x = x - x0;
        y = y - y0;
        // rgba
        var rgba = new Array(4);
        for (var n = 0; n < 3; n++) {
            rgba[n] = (1 - x) * (1 - y) * data[getPixelPosition(width, x0, y0) + n]
                    + x * y * data[getPixelPosition(width, x1, y1) + n]
                    + (1 - x) * y * data[getPixelPosition(width, x0, y1) + n]
                    + x * (1 - y) * data[getPixelPosition(width, x1, y1) + n];
        }
        rgba[3] = 255;
        return rgba;
    }

    var Bilinear = function (data, width, height, scaleX, scaleY) {
        scaleX = scaleX || 1;
        scaleY = scaleY || scaleX;

        if (scaleX === 1 && scaleX === scaleY) {
            // return {
            //     data: data,
            //     width: width,
            //     height: height
            // };
            return new ImageData(data, width, height);
        }

        var nWidth = Math.floor(scaleX * width);
        var nHeight = Math.floor(scaleY * height);

        // 插值法是插空,下面计算真实插空的宽度
        scaleX = (nWidth - 1) / (width - 1);
        scaleY = (nHeight - 1) / (height - 1);

        var imageScaled = new Uint8ClampedArray(nWidth * nHeight * 4);

        util.each.xDirection(imageScaled, nWidth, 0, 0, nWidth, nHeight, function (i, x, y) {
            calculateInterpolation(data, width, x / scaleX, y / scaleY).map(function (color, n) {
                imageScaled[i + n] = color;
            });
        });

        // return {
        //     data: imageScaled,
        //     width: nWidth,
        //     height: nHeight
        // }
        return new ImageData(imageScaled, nWidth, nHeight)
    }

    module.exports.process = Bilinear;
});

后续会陆续将学到的一些图形图像算法实现放在这里展示,欢迎喜欢图形图像的朋友们一起交流学习,https://github.com/Naixor/cv

你可能感兴趣的:(图形图像)