WarpPrism / Blog

Do Epic Things
51 stars 8 forks source link

关于图像处理中的插值算法(Matlab语言描述) #1

Open WarpPrism opened 7 years ago

WarpPrism commented 7 years ago

数字图像处理的第一次作业就是用图像的插值算法实现图片的放缩,在网上查了一些资料后,总结如下:

首先我们定义几个变量:

- 原图矩阵: Source
- 目标图矩阵: Destination
- 宽度缩放比: ratioW
- 高度缩放比: ratioH

先得到原图的宽和高

Source = imread('src.png');
[srcM, srcN] = size(Source);

接着根据下面的公式计算出目标图的宽和高:

ratioW = dstM / srcM;
ratioH = dstN / srcN;

新建目标图,根据目标图的大小建立一个空的Matrix,记作M

Destination = zeros(dstM, dstN);
M = Destination;

然后要对M中的每个点赋值,即所谓的插值运算,这个值从哪里来呢?当然是原图。

现在 设M矩阵的坐标点为(dx, dy),则它对应的原图坐标(sx, sy)满足:

dx / sx = dstM / srcM;
dy / sy = dstN / srcN;

这样我们就能求出

(sx, sy) = (dx * srcM / dstM, dy * srcN / dstN);

到此,我们就把目标图中的坐标点映射到原图中去了

但是这个坐标点不一定是整数,假如(2, 0) 映射的结果为 (0.75, 0),这显然是不科学的,怎么办呢? 第一种方法,四舍五入 得到 (1, 0),然后把原图中(1, 0)点的像素值赋给目标图的(2, 0)位置即可:

fd(2, 0) = fs(1, 0);

这种放大图像的方法就叫做 最临近插值算法,这是一种最基本、最简单的图像缩放算法,效果也是最不好的,放大后的图像有很严重的马赛克,缩小后的图像有很严重的失真。

另一种算法就是双线性插值算法,它的失真情况会好很多。

算法思路是: 对于一个目的像素,映射后得到的浮点坐标为(i+u,j+v), (其中i、j均为浮点坐标的整数部分,u、v为浮点坐标的小数部分,是取值[0,1)区间的浮点数),则这个像素得值 f(dx,dy) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:

f(dx, dy) = (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)

这里我们就可以由原图中的 4 个像素点推出了目标图中1个像素点,最后遍历目标图,我们就能实现图片的放缩了。

双线性插值算法代码如下所示:

% Bilinear Function
function [ output_img ] = scale( input_img, Size )
    % Using bilinear algorithm to change the SIZE of input_img.
    dstM = Size(1);
    dstN = Size(2);
    [srcM srcN]=size(input_img);
    output_img = zeros(dstM, dstN);
    for i = 1:dstM - 1
        for j = 1:dstN - 1
            src_i = i * (srcM / dstM);
            src_j = j * (srcN / dstN);
            int_i = fix(src_i);
            int_j = fix(src_j);
            u = src_i - int_i;
            v = src_j - int_j;
            if int_i == 0
                int_i = int_i + 1;
            end
            if int_j == 0
                int_j = int_j + 1;
            end
            output_img(i,j)=(1-u)*(1-v)*input_img(int_i,int_j)+(1-u)*v*input_img(int_i,int_j+1)
                            +u*(1-v)*input_img(int_i+1,int_j)+u*v*input_img(int_i+1,int_j+1);
        end
    end
    output_img = uint8(output_img);
    figure,imshow(input_img)
    axis on
    figure,imshow(output_img)
    axis on
end

% call function scale()
close all
clear all
clc

input_img = imread('test.png')
size = [100, 100]
output_img = scale(input_img, size)
imtool(output_img)