前言

本篇文章将介绍和应用图像的算术运算和几何变换,并对插值算法进行说明。比较简单的我就不举例了。

图像的算术运算

图像相加

图像的加法运算是将一幅图像的内容叠加在另一幅图像上,或者给图像的每一个像素加一个常数来改变图像的亮度。主要应用是改变图像亮度和图像叠加。

函数:

1
Z = imadd(X,Y)

图像差分

主要作用是检测图像的变化和检测运动物体。

函数:

1
Z = imsubtract(X,Y)

图像乘法

主要作用是掩膜和缩放。

函数:

1
Z = immultiply(X,Y)

什么是掩膜:

可以对图像上一些区域起屏蔽作用。

图像除法

主要作用是矫正成像设备的非线性影响。

函数:

1
Z = imdivide(X,Y)

\(Z = \frac{X}{Y}\)

图像的线性组合

函数

1
2
3
Z = imlincomb(A,X,B,Y,C) % Z = A*X+B*Y+C
Z = imlincomb(A,X,C) % Z = A*X+C
Z = imlincomb(A,X,B,Y) % Z = A*X+B*Y

图像的几何变换

图像平移

假设图中一点为\(f(x_0,y_0)\), 对其水平平移tx个单位,垂直平移ty个单位,那么用矩阵表示应该为: \[ \begin{bmatrix} x_1\\ y_1 \\ 1 \end{bmatrix} = \begin{bmatrix} 1&0&tx\\ 0&1&ty \\ 0&0&1 \end{bmatrix}\begin{bmatrix} x_0\\ y_0 \\ 1 \end{bmatrix} \] matlab中并没有提供平移图像的函数,但是可以通过膨胀函数平移图像。

应用示例

1
2
3
4
5
6
7
close all;clc;clear;
I = imread('example2.jpg');
subplot(1,2,1),imshow(I);title("original 1");

se = translate(strel(1),[50,100]); % 将一个平面结构元素向下移动50,向右边移动100
X = imdilate(I,se); %利用膨胀平移图像
subplot(1,2,2),imshow(X);title("now img");

图片镜像

设图像矩阵为\((M,N)\)

图像镜像分为垂直镜像和水平镜像。

垂直镜像: \[ \begin{split} & x_1 = M-x_0 \\ & y_1 = y_0 \end{split} \] 水平镜像: \[ \begin{split} & x_1 = x_0 \\ & y_1 = N-y_0 \end{split} \] 应用示例

函数

1
2
3
4
X = flip(I,dim = _)
% dim = 1 水平镜像(翻转列)
% dim = 2 垂直镜像(翻转行)
% dim = 3 翻转第三维,可能是颜色?
1
2
3
4
5
6
7
8
9
10
close all;clc;clear;
I = imread('example2.jpg');
subplot(2,2,1),imshow(I);title("original");

X_1 = flip(I,1); % 翻转行
X_2 = flip(I,2); % 翻转列
X_3 = flip(I,3); % 翻转第三维
subplot(2,2,2),imshow(X_1);title("水平镜像");
subplot(2,2,3),imshow(X_2);title("垂直镜像");
subplot(2,2,4),imshow(X_3);title("?");

图片转置

\((x_0,y_0)\)是原图上的点,则转置为: \[ \begin{split} & x_1 = y_0 \\ & y_1 = x_0 \end{split} \] 表示为矩阵形式: \[ \begin{bmatrix}x_1&y_1&1\end{bmatrix}=\begin{bmatrix}0&1&0\\1&0&0\\0&0&1\end{bmatrix}\begin{bmatrix}y_0&x_0&1\end{bmatrix} = \begin{bmatrix}x_0&y_0&1\end{bmatrix} \]

注意:转置后图像的高度和宽度也会发生变化。

示例

matlab中需要构建转换矩阵,然后用imwarp变换图像。

1
2
3
4
5
6
7
close all;clc;clear;
I = imread('example2.jpg');
subplot(1,2,1),imshow(I);title("original");

T=affine2d([0 1 0;1 0 0;0 0 1]);%构造空间变换结构T.这里为转置变换矩阵
X=imwarp(I,T); % 根据位移场变换图像。
subplot(1,2,2),imshow(X);title("now");

图像旋转

设点\((x_0,y_0)\)经过旋转\(\theta\) 角度后,坐标变为\((x_1,y_1)\)

旋转前: \[ \left\{ \begin{aligned} x_0 = rcos\theta\\ y_0 = rsin\theta\\ \end{aligned} \right. \] 旋转后: \[ \left\{ \begin{aligned} x_1 = rcos(\alpha-\theta) = rcos\alpha cos\theta + rsin\alpha sin\theta = x_0cos\theta+y_0sin\theta\\ y_1 = rsin(\alpha-\theta) = rsin\alpha cos\theta - rcos\alpha sin\theta = -x_0sin\theta+y_0cos\theta\\ \end{aligned} \right. \] 矩阵形式: \[ \begin{bmatrix}x_1\\y_1\\1\end{bmatrix}=\begin{bmatrix}cos\theta&sin\theta&0\\-sin\theta&cos\theta&0\\0&0&1\end{bmatrix}\begin{bmatrix}x_0\\y_0\\1\end{bmatrix} \] 对矩阵求逆可得逆变换: \[ \begin{bmatrix}x_0\\y_0\\1\end{bmatrix}=\begin{bmatrix}cos\theta&-sin\theta&0\\sin\theta&cos\theta&0\\0&0&1\end{bmatrix}\begin{bmatrix}x_1\\y_1\\1\end{bmatrix} \] 示例

函数:

1
2
3
X = imrotate(A,angle) 
X = imrotate(A,angle,method)
X = imrotate(A,angle,method,bbox)
  • angle:角度,大于0顺时针旋转,小于0逆时针旋转
  • method:插值方法,method取值为:'nearest'(默认)最近邻插值、'bilinear'双线性插值、'bicubic'双三次插值(关于插值算法将在下面介绍)
  • bbox:返回图像的大小,取值为'crop'或'loose'。'crop'输出大小和输入图像大小想等,对旋转后的图像进行裁剪;'loose'(默认)表示使输出图像足够大,包含完整的旋转图像.
1
2
3
4
5
6
7
8
9
10
close all;clc;clear;
I = imread('example2.jpg');
subplot(2,2,1),imshow(I);title("original");

X_1 = imrotate(I,45);
subplot(2,2,2),imshow(X_1);title("angle 45");
X_2 = imrotate(I,45,'bicubic');
subplot(2,2,3),imshow(X_2);title("angle 45 bicubic");
X_3 = imrotate(I,45,'bicubic','crop');
subplot(2,2,4),imshow(X_3);title("angle 45 bicubic crop");

图像缩放

将放大或者缩小后的图像将其的坐标(长宽)拉伸或者压缩到和原图一样大时,其像素坐标点对应在原图上的位置就是其映射位置。假设图像x轴方向缩放比为\(f_x\),y轴方向缩放比为\(f_y\) ,则原图中的点\((x_0,y_0)\)缩放后对应的新位置为: \[ \begin{bmatrix}x_1\\y_1\\1\end{bmatrix}=\begin{bmatrix}f_x&0&0\\0&f_y&0\\0&0&1\end{bmatrix}\begin{bmatrix}x_0\\y_0\\1\end{bmatrix} \]

注:以下图示来自(三)图像的放大和缩小_淡定的炮仗的博客-CSDN博客_图像缩放原理

仔细看一下图就懂了。但是对于图像放大而言,这只是第一步。

观察图像放大的图示,会发现中间空了很多没有值的像素点位。上述图像来源的博客中用python具体实现了一下直接放大的代码,详情请见博客。该博客在最后提到,“放大的图像由于是原图像素直接搬移到放大后的画布上,导致放大后的画布上的一些像素位置没有值(值为0)”。为了解决该问题,需要用到图像插值算法进行补全。关于图像插值算法将在下一节说明。

示例

函数

1
2
3
4
5
X = imresize(I,m) % I可以是灰度、RGB、二值图像。m为缩放尺寸,m大于0小于1时为缩小;m大于1时为放大。
X = imresize(I,[mrows,ncols]) % [mrows ncols]为放大后的行和列。当mrows或ncols取值为NaN,函数会根据I的纵横比,结合另一个已知值算出mrows或ncols的值。mrows和ncols不可同时为NaN.
[X,newmap] = imresize(I,map,m) % 对索引图像进行缩放
[...] = imresize(...,method) % ...表示可为之前说过的任意一种方式,method表示选用的插值算法。method值可选择插值方法的类型:'nearest'(默认)最近邻插值、'bilinear'双线性插值、'bicubic'双三次插值;或者选择插值的核函数:'box'Box型核函数、'triangle' 三角型核函数(bilinear相同)、'Cubic'立方体型核函数(bicubic相同)等。
[...] = imresize(...,parameter,value,...) % 通用形式,这里不对该方法进行详细说明。

关于最后那种方法,详细取值表格请见连接:图像处理之图像的几何变换_Hard Coder的博客-CSDN博客_图像几何变换

1
2
3
4
5
6
7
8
9
10
11
close all;clc;clear;
I = imread('example2.jpg');
figure();imshow(I);title("original");
X_1 = imresize(I,[NaN,100],'box');
figure();imshow(X_1);title("[mrows ncols],box");
X_2 = imresize(I,0.5);
figure();imshow(X_2);title("m");
[X,map] = rgb2ind(I,16);
[X_3,newmap] = imresize(X,map,1.3);
figure();imshow(X_3,newmap);title("index img");

图像插值算法

参考文章:一篇文章为你讲透双线性插值 - 知乎 (zhihu.com) 不过要注意的是,参考文章中有一些错误。

简单来说,插值指利用已知的点来“猜”未知的点,图像领域插值常用在修改图像尺寸的过程,由旧的图像矩阵中的点计算新图像矩阵中的点并插入,不同的计算过程就是不同的插值算法。

常用的插值算法有三种:

  1. 最近邻(Nearest Interpolation):计算速度最快,效果最差。
  2. 双线性插值(Bilinear Interpolation):双线性插值是用原图像中4(2*2)个点计算新图像中1个点,效果略逊于双三次插值,速度比双三次插值快,较为平衡,在很多框架中属于默认算法。
  3. 双三次插值(Bicubic interpolation):双三次插值是用原图像中16(4*4)个点计算新图像中1个点,效果比较好,但是计算代价过大。

最近邻插值算法

又称邻接插值算法。选取距离插入的像素点最近的一个像素点,用它的像素值代替插入的像素点。

公式: \[ \begin{split} & src_x = des_x\times(src_w/des_w) \\ & src_y = des_y\times(src_h/des_h) \end{split} \] 此公式是四舍五入的规则。其中:

  • \(src_x\):原图像中像素点的x坐标
  • \(src_y\):原图像中像素点的y坐标
  • \(des_x\):变换后图像的像素点的x坐标
  • \(dex_y\):变换后图像的像素点的y坐标
  • \(src_w\):原图像宽度(width)
  • \(src_h\):原图像高度(height)
  • \(des_w\):变换后图像的宽度
  • \(des_h\):变换后图像的高度

有的小伙伴可能就要问了,为什么src在等式左边啊?

实际上这个公式是先有了变换后的图像,然后这个图像中有些像素值缺失的点位。现在要计算这些点位在原图像中离哪个像素点位最近,然后用其替换,所以src在左边。可以多看看参看文章中计算示例。

最近邻法不需要计算只需要寻找原图中对应的点,所以最近邻法速度最快,但是会破坏原图像中像素的渐变关系,原图像中的像素点的值是渐变的,但是在新图像中局部破坏了这种渐变关系

双线性插值算法

单线性插值

(以下图示来自之前提到的参考文章)

已知中P1点和P2点,坐标分别为\((x1,y1)\)\((x2,y2)\),要计算 $[x1,x2] $区间内某一位置 x 在直线上的y值。

看图就知道了,实际上单线型插值就是建立(线性)函数,然后找x对应的\(f(x)\)即可。

根据两点求一条直线公式: \[ \frac{y-y_1}{x-x_1} = \frac{y_2-y_1}{x_2-x_1} \] 整理: \[ y = \frac{x_2-x}{x_2-x_1}y_1+\frac{x-x_1}{x_2-x_1}y_2 \] 上述是对于一维(图像)而言,x即像素点位,y为像素值。为了便于后续理解,将y改写为\(f(x)\) 。右式\(f(x_i)\)前的系数改称为权重。 \[ f(x) = \frac{x_2-x}{x_2-x_1}f(x_1)+\frac{x-x_1}{x_2-x_1}f(x_2) \] 现在将一维图像拓展为二维图像。

双线性插值

已知四个点\(Q_{11}(x_1,y_1),Q_{12}(x_1,y_2),Q_{21}(x_2,y_1),Q_{22}(x_2,y_2)\)。根据该求点\(P(x,y)\) 的像素值。

双线性插值是分别在两个方向计算了共3次单线性插值:在x方向求2次单线性插值,获得\(R1(x, y_1)\)\(R2(x, y_2)\)两个临时点,再在y方向计算1次单线性插值得出\(P(x, y)\)

第一步: \[ \begin{split} & f(R_1) = \frac{x_2-x}{x_2-x_1}f(Q_{11})+\frac{x-x_1}{x_2-x_1}f(Q_{21}) \\ & f(R_2) = \frac{x_2-x}{x_2-x_1}f(Q_{12})+\frac{x-x_1}{x_2-x_1}f(Q_{22}) \end{split} \]

为什么权值计算没有涉及y轴?

因为y轴没变,所以权值仅取决于x轴。在接下来的第二步中也是同样的道理,x轴没变,权值仅取决于y轴。

第二步: \[ f(P) = \frac{y_2-y}{y_2-y_1}f(R_1)+\frac{y-y_1}{y_2-y_1}f(R_2) \] 先暂时不联立第一步和第二步,我们先想想已知四个点的关系。我们进行插值时,找的四个点应该是靠在一起的,所以有: \[ \begin{split} & x_2-x_1 = 1 \\ & y_2-y_1 = 1 \end{split} \] 化简

第一步: \[ \begin{split} & f(R_1) = (x_2-x)f(Q_{11})+{(x-x_1)}f(Q_{21}) \\ & f(R_2) = {(x_2-x)}f(Q_{12})+{(x-x_1)}f(Q_{22}) \end{split} \] 第二步: \[ f(P) = (y_2-y)f(R_1)+(y-y_1)f(R_2) \] 联立第一步和第二步: \[ \begin{split} f(P) & = (y_2-y)f(R_1)+(y-y_1)f(R_2) \\ & = (y_2-y)[(x_2-x)f(x_1,y_1)+(x-x_1)f(x_2,y_1)]+(y-y_1)[{(x_2-x)}f(x_1,y_2)+{(x-x_1)}f(x_2,y_2)] \\ & = (y_2-y)(x_2-x)f(x_1,y_1)+(y_2-y)(x-x_1)f(x_2,y_1)+(y-y_1)(x_2-x)f(x_1,y_2)+(y-y_1)(x-x_1)f(x_2,y_2) \\ & = (y_2-y)(x_2-x)f(Q_{11})+(y_2-y)(x-x_1)f(Q_{21})+(y-y_1)(x_2-x)f(Q_{12})+(y-y_1)(x-x_1)f(Q_{22}) \end{split} \] 总结

双线性插值算法是有缺陷的,比如边界像素点还是存在有的像素只是进行了单线性插值,并不能保证每一个像素都是双线性插值。不过这里暂时不讨论。

双三次插值算法

又称三次卷积插值。它更复杂,不仅考虑了4个邻近点,还考虑了灰度值的变换率。双三次插值算法可以克服最近邻和双线性插值算法的缺陷,计算精度高。但由此也会导致计算量较大。

这里暂时不对双三次插值算法进行详细讨论。