图像缩放功能的插值算法
简介
在 Matlab 下对图像分别采用最近邻插值、双线性插值、三次插值 3 种算法进行 0. 5 倍、3 倍缩放,对这 3 种算法实现的缩放效果和原图做出比较。
最近邻插值算法
最近邻插值算法又称为零阶插值,就是令变换后像素的灰度值等于距它最近的输入像素的灰度值。
最近邻插值算法简单,在许多情况下都能得到满意的结果,但是当图像中的像素灰度级有细微变化时,
该方法会在图像中产生人为加工的痕迹。
function[im_n] = myNearest(im,ratio)
%最邻近插值,ratio是比例
%得到图像的行数、列数、以及每像素的维数(防止出现三幅图像)
[line,row,v]=size(im);
%新的行数和列数,预分配内存
line_n = round(ratio * line);
row_n = round(ratio * row);
%循环
x_n = 1 : line_n;
y_n = 1 : row_n;
%防止溢出
x = round(x_n * (line - 1) / (line_n - 1) + (line_n - line) / (line_n - 1) );
y = round(y_n * (row - 1) / (row_n - 1) + (row_n - row) / (row_n - 1) );
im_n(x_n,y_n) = im(x,y);
end
双线性插值算法
双线性插值算法又称双线性内插,经过放大或缩小若干倍之后,目标图像 Im(x y) 点坐标所对应的源图像坐标 (x ∗ m/m’ y ∗ n/n’) 通常为浮点数,假设为 P 点,用 P (i_x + u_x i_y + u_y) 表示, 其中 i_x,i_y 分别表示整数部分,u_x,u_y 分别表示小数部分。由其相邻 4 个点的灰度值的线性关系计算 P 点的灰度值,即 P 点的灰度值由这 4 个相邻点共同决定,距 P 点越近,则影响因子越大, 反之影响因子越小,对于 (i_x i_y) 点,x 方向上 u_x 的值越大,其影响因子越小,i_y 方向上 u_y 的值越大,其影响因子越小,所以 (i_x i_y) 点的影响值为 Im(i_x i_y) ∗ (1 − u_x) ∗ (1 − u_y),其余 3 个点类似。
P 点的灰度值计算公式:
P (x y) = (1 − u_x) ∗ (1 − u_y) ∗ Im(i_x i_y) + (1 − u_x) ∗ u_y ∗ Im(i_x i_y + 1) + u_x ∗ (1 − u_y) ∗ Im(i_x + 1 i_y) + u_x ∗ u_y ∗ Im(i_x + 1 i_y + 1)
function [im_b] = myBilinear(im, ratio)
%双线性内插算法
%得到图像的行数、列数、以及每像素的维数(防止出现三幅图像)
[line,row,v]=size(im);
%便于后面的计算
im = double(im);
%新的行数和列数,预分配内存
line_n = round(ratio * line);
row_n = round(ratio * row);
%向量化循环,防止溢出
x_n = 1 : line_n - 1;
y_n = 1 : row_n - 1;
%投影回去的位置
x = x_n * (line - 1) / (line_n - 1) + (line_n - line) / (line_n - 1);
y = y_n * (row - 1) / (row_n - 1) + (row_n - row) / (row_n - 1);
% x = x_n * (line - 1) / (line_n) + (line_n - line + 1) / (line_n);
% y = y_n * (row - 1) / (row_n) + (row_n - row + 1) / (row_n);
%对原坐标取整
i_x = floor(x);
i_y = floor(y);
u_x = x - i_x;
u_y = y - i_y;
w_x = 1 - u_x;
w_y = 1 - u_y;
im_b(x_n,y_n) = w_x' * w_y .* im(i_x,i_y) + w_x' * u_y .* im(i_x,i_y + 1)...
+ u_x' * w_y .* im(i_x + 1,i_y) + u_x' * u_y .* im(i_x + 1,i_y + 1);
%im_b(x_n,y_n) = u_x' .* (im(i_x + 1,i_y)-im(i_x,i_y)) ...
% + u_y .* (im(i_x ,i_y + 1)-im(i_x,i_y))...
% +u_x'* u_y .*(im(i_x+1,i_y+1) + im(i_x,i_y) - im(i_x,i_y+1) - im(i_x+1,i_y))...
% + im(i_x,i_y);
im_b = uint8(im_b);
end
双三次插值算法
三次插值又称立方卷积插值,利用 P 周围 16 个点的灰度值进行三次插值,可以得到更接近高分辨率图像的放大效果,也会导致运算量的急剧增加。该算法需要选取插值基函数来拟合数据,最常用的插值基函数表达式如下
y(x) =
1 − 2|x|2 + x3 |x| < 1
4 − 8|x| + 5|x|2 − |x|3 1 < |x| < 2
0 |x| > 2
同样 P 点为经过放大或缩小若干倍之后,目标图像 Im(x y) 点坐标所对应的源图像坐标 (x∗m/m’ y ∗n/n’) 通常为浮点数,假设为 P 点,用 P (i_x + u_x i_y + u_y) 表示,其中 i_x,i_y 分别表示整数部分,u_x,u_y 分别表示小数部分。此处需要分别计算 P 点周围 16 个点的系数,经过加权得到
P 点的灰度值。
下面对坐标点分别求行和列对应的系数: 行 X 轴方向上的 4 个点距 P 点的距离分别为 1 + u_x、u_x、1 − u_x、2 − u_x。列 Y 轴方向上的 4 个点距 P 点的距离分别为 1 + u_y、u_y、1 − u_y、2 − u_y。由插值基函数运算得到 Im(i_x i_y) 点行对应的系数为 y(1 + u_x),列对应的系数为 y(1 + u_y),该点的系数则为 K00 = y(1 + u_x) ∗ y(1 + u_y)。其余点类似计算。
function [im_c] = myBicubic(im,ratio)
%双三次插值算法
%得到图像的行数、列数、以及每像素的维数(防止出现三幅图像)
[line,row,v]=size(im);
im = im(:,:,1);
%便于后面的计算,扩展四行四列
a=im(1,:);%取im的第1行
c=im(line,:);%取im的第m行
%将待插值图像矩阵前后各扩展两行两列,共扩展四行四列
b=[im(1,1),im(1,1),im(:,1)',im(line,1),im(line,1)];
d=[im(1,row),im(1,row),im(:,row)',im(line,row),im(line,row)];
a1=[a;a;im;c;c];
b1=[b;b;a1';d;d];
im=b1';im=double(im);
%新的行数和列数,预分配内存
line_c = round(ratio * line);
row_c = round(ratio * row);
im_c = zeros(line_c,row_c);
%循环,防止溢出
for x_c = 1 : line_c
x = (x_c - 1) * (line - 1) / (line_c - 1) + 3;%投影回去的位置,投影回去的点,仅投影在原图像位置上
i_x = floor(x);u_x = x - i_x; %对原坐标取整
g_x = [GetWeight(1+u_x),GetWeight(u_x),GetWeight(1-u_x),GetWeight(2-u_x)];
for y_c = 1 : row_c
y = (y_c - 1) * (row - 1) / (row_c - 1) + 3;
i_y = floor(y);u_y = y - i_y; %对原坐标取整
g_y = [GetWeight(1+u_y),GetWeight(u_y),GetWeight(1-u_y),GetWeight(2-u_y)];
Need_c=[ im(i_x-1,i_y-1) im(i_x-1,i_y) im(i_x-1,i_y+1) im(i_x-1,i_y+2);
im(i_x,i_y-1) im(i_x,i_y) im(i_x,i_y+1) im(i_x,i_y+2);
im(i_x+1,i_y-1) im(i_x+1,i_y) im(i_x+1,i_y+1) im(i_x+1,i_y+2);
im(i_x+2,i_y-1) im(i_x+2,i_y) im(i_x+2,i_y+1) im(i_x+2,i_y+2)];
im_c(x_c,y_c)=(g_x*Need_c*g_y');
end
end
im_c=uint8(im_c);
end
function [key] = GetWeight(num)
%获得各个点的权重
if num<1 && num>-1
key = 1 - 2 * num^2 + abs(num)^3;
else
key = 4 - 8 * abs(num) + 5 * num^2 - abs(num)^3;
end
end
测试结果
本实验采用的原始图像为三幅灰度图像,分别采用最近邻插值、双线性插值、三次插值 3 种算法对图像进行 0.5 倍 (见图 1、图 3)、3 倍缩放 (见图 2、图 4),并对其缩放效果进行对比,主函数代码如下:
%%
clc;
clear;
close all;
%%
ratio_1 = 0.5; % 缩放因子1
ratio_2 = 3; % 缩放因子2
filename = 'lenna'; %测试图像1
% filename = 'cameraman'; %测试图像2
% filename = 'building'; %测试图像3
im = imread([filename, '.jpg']);
[row, col, channel] = size(im); %得到图像尺寸
im_center = im(floor(row*3/8):floor(row*5/8), floor(col*3/8):floor(col*5/8), :); %截取中间图像块,用于图像放大
%% 将图像长宽缩放为原图的 ratio_1 (<1)倍
im1_n = myNearest(im, ratio_1);
im1_b = myBilinear(im, ratio_1);
im1_c = myBicubic(im, ratio_1);
%% 将图像长宽缩放为原图的 ratio_2 (>1)倍
im2_n = myNearest(im_center, ratio_2);
im2_b = myBilinear(im_center, ratio_2);
im2_c = myBicubic(im_center, ratio_2);
%% 将结果保存到当前目录下的result文件夹下
imwrite(im1_n, sprintf('result/_%s_%.1f_n.jpg', filename, ratio_1));
imwrite(im1_b, sprintf('result/_%s_%.1f_b.jpg', filename, ratio_1));
imwrite(im1_c, sprintf('result/_%s_%.1f_c.jpg', filename, ratio_1));
imwrite(im2_n, sprintf('result/_%s_%.1f_n.jpg', filename, ratio_2));
imwrite(im2_b, sprintf('result/_%s_%.1f_b.jpg', filename, ratio_2));
imwrite(im2_c, sprintf('result/_%s_%.1f_c.jpg', filename, ratio_2));
%% 显示结果
figure(1);
subplot(221); imshow(im); title('原图'); axis on
subplot(222); imshow(im1_n); title('最近邻内插图像'); axis on
subplot(223); imshow(im1_b); title('双线性内插图像'); axis on
subplot(224); imshow(im1_c); title('双立方内插图像'); axis on
figure(2);
subplot(221); imshow(im_center); title('原图'); axis on
subplot(222); imshow(im2_n); title('最近邻内插图像'); axis on
subplot(223); imshow(im2_b); title('双线性内插图像'); axis on
subplot(224); imshow(im2_c); title('双立方内插图像'); axis on
4
从以上运行效果来看,最近邻插值算法在放大图上锯齿现象较为严重。双线性插值算法也依然存在因为计算模型考虑不周而造成的图像精度下降以及图像质量退化的缺陷。但与最近邻插值算法相比, 双线性插值运算量仅有少量提高,放大效果却有明显改善。在实际中对图像质量要求不高的情况下,可广泛应用。三次插值效果最好,可以得到更接近高分辨率图像的放大效果及更平滑的图像边缘,但也导致了运算量的急剧增加,可被应用于一些专业的制图软件、数码相机或打印机中。
结束语
在 Matlab 下分别对图像采用最近邻插值、双线性插值、三次插值 3 种算法进行 0. 5 倍、3 倍缩放,并对 3 种算法的缩放效果作出比较,其中三次插值的效果最好,但计算代价也最大,最近邻插值运行代价最小,但锯齿现象较严重。实际中可根据不同的缩放要求和机器性能选择合适的插值算法。另 外此次的实验是对灰度图进行的,对于彩色图,算法类似,可将其中的灰度值改成 R G B 值进行相应的计算。