图像的几何空间变换

介绍

  图像的几何空间变换可以改进图像中像素间的空间关系,主要由两个基本操作组成:

  • 坐标空间的变换。
  • 图像内插,对空间变换后的像素赋值。

空间坐标变换

  可由下式表示:

(x,y)=T{(v,w)}(x, y) = T\{(v, w)\}

  上式中(v,w)(v, w)是原图中像素的坐标,(x,y)(x, y)是变换后图像的坐标,TT是变换函数,也就是原图中某个位置的像素通过变换函数,映射到目标图像中。比如:(x,y)=T(v,w)=(v/2,w/2)(x, y) = T{(v, w)} = (v/2, w/2)表示在两个方向上将原图缩小一半。

图像内插

  请参考我的另一篇博文图像内插

仿射变换

  仿射变换是最常用的空间坐标变换之一,其一般形式如下:

[xy1]=[vw1]T=[vw1][t11t120t21t220t31t321]\left[\begin{matrix} x & y & 1 \end{matrix}\right] = \left[\begin{matrix} v & w & 1 \end{matrix}\right]T = \left[\begin{matrix} v & w & 1 \end{matrix}\right] \left[\begin{matrix} t_{11} & t_{12} & 0 \\ t_{21} & t_{22} & 0 \\ t_{31} & t_{32} & 1 \end{matrix}\right]

  上式中,TT表示变换矩阵,可根据TT中元素所选择的值,对一组坐标点做尺度、旋转、平移或偏移。

变换名称 变换矩阵TT 坐标公式 例子
恒等变换 [100010001]\left[\begin{matrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{matrix}\right] x=vy=wx = v \\ y = w
尺度变换 [cx000cy0001]\left[\begin{matrix} c_x & 0 & 0 \\ 0 & c_y & 0 \\ 0 & 0 & 1\end{matrix}\right] x=cxvy=cywx = c_x v \\ y = c_y w
平移变换 [100010txty1]\left[\begin{matrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ t_x & t_y & 1\end{matrix}\right] x=v+txy=w+tyx = v + t_x \\ y = w + t_y
旋转变换 [cosθsinθ0sinθcosθ0001]\left[\begin{matrix} \cos\theta & \sin\theta & 0 \\ -\sin\theta & \cos\theta & 0 \\ 0 & 0 & 1\end{matrix}\right] x=vcosθwsinθy=vsinθ+wcosθx = v\cos\theta - w\sin\theta \\ y = v\sin\theta + w\cos\theta
垂直偏移变换 [1sh0010001]\left[\begin{matrix} 1 & s_h & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1\end{matrix}\right] x=vy=w+shvx = v \\ y = w + s_hv
水平偏移变换 [100sv10001]\left[\begin{matrix} 1 & 0 & 0 \\ s_v & 1 & 0 \\ 0 & 0 & 1\end{matrix}\right] x=v+svwy=wx = v + s_vw \\ y = w

实现

  实现空间变换的方法有两种:

  • 前向映射:整体思想是扫描原图像,对于原图像上的每个像素,根据它在原图中的坐标(v,w)(v, w),利用(x,y)=T{(v,w)}(x, y) = T\{(v, w)\}计算其在目标图像上的坐标(x,y)(x, y),然后将原图中坐标为(v,w)(v, w)的像素值赋值给目标图像中坐标为(x,y)(x, y)的像素。
  • 反向映射:整体思想是扫描目标图像的每个像素,对于坐标为(x,y)(x, y)的像素,利用(v,w)=T1{(x,y)}(v, w) = T^{-1}\{(x, y)\}计算其在原图像上的坐标(v,w)(v, w),然后用内插的方法计算该像素的值。
      这里T1T^{-1}表示变换函数的反函数,对于仿射变换来说,相当于[xy1]\left[\begin{matrix} x & y & 1 \end{matrix}\right]乘变换矩阵的逆矩阵。

  使用前向映射可能会存在两个问题,一个是原图像中多个像素被变换到目标图像的同一个位置,产生冲突,另一个问题是计算出的目标像素坐标不存在,比如不是个整数坐标。而反向映射不存在这两个问题,因此反向映射用的多一点。
  这里给出用反向映射实现仿射变换的C++代码,代码用的是opencv库处理图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;

/* 原图路径 */
char img_path[100] = "img/affine.png";
/* 变换后图像的大小 */
Size img_out_size = {300, 300};
/* 仿射矩阵 */
double T[3][3] =
{
{1, 0, 0},
{0, 1, 0},
{0, 0, 1},
};

/* 利用双线性内插计算原图中的浮点像素值 */
double bilinear_interpolation(Mat src, double x, double y)
{
if(x < 0 || x > src.cols || y < 0 || y > src.rows)
{
return 0;
}

/* 表示在原图中与浮点像素最近的四个整数坐标值(lx:左x、rx:右x、uy:上y、dy:下y) */
double lx = (int)x;
double rx = lx + 1;
double uy = (int)y;
double dy = uy + 1;

/* 先计算y方向的两个像素点值f1、f2,再计算xy两个方向上的像素点值 */
double f1 = (rx - x) * src.at<uchar>(uy, lx) + (x - lx) * src.at<uchar>(uy, rx);
double f2 = (rx - x) * src.at<uchar>(dy, lx) + (x - lx) * src.at<uchar>(dy, rx);
double xy = (dy - y) * f1 + (y - uy) * f2;
return xy;
}

int main()
{
int i, j;
Mat src = imread(img_path, cv::IMREAD_GRAYSCALE);
src.rows -= 2;
Mat dst = Mat(img_out_size, src.type());

Mat t_s = Mat(3, 3, CV_64FC1, T);
Mat t_inv = Mat(3, 3, CV_64FC1);
invert(t_s, t_inv); // t_inv为仿射矩阵的逆矩阵

Mat src_vec, dst_vec;
for(i = 0; i < dst.rows; i++)
{
for(j = 0; j < dst.cols; j++)
{
/* 目标图像的坐标向量 */
dst_vec = (Mat_<double>(1, 3) << j, i, 1);
/* 计算dst_vec对应于原图中的坐标向量src_vec */
src_vec = dst_vec * t_inv;
/* 计算目标图像的浮点像素值 */
dst.at<uchar>(i, j) = (uchar)bilinear_interpolation(src, src_vec.at<double_t>(0, 0), src_vec.at<double_t>(0, 1));
}
}
imshow("dst", dst);
imwrite("img/t_1.png", dst);

waitKey(0);
return 0;
}

分享到:
0%