2017-11-10 23:48:47 bu_xiang_zhu_ce 阅读数 216

原博客:http://blog.csdn.net/linshanxian/article/details/68944748


旋转有一个绕着什么转的问题。通常的做法是以图像的中心为圆心旋转,将图像上的所有像素都旋转一个相同的角度。图像的旋转变换是图像的位置变换,但旋转后图像的大小一般会改变。和平移变换一样,既可以把转出显示区域的图像截去,也可以扩大显示区域以显示完整的图像,如下图所示。


我们先讨论不裁剪转出部分,扩大显示区域的情况。在下图所示的平面坐标系中,A0逆时针旋转θ变成A1r是该点到原点的距离,则旋转前:


旋转后A1的坐标为


写成矩阵的形式为:


其逆变换矩阵如下:


上面公式是旋转变换的基本公式,坐标系是以图像的中心为原点,向右为x轴正方向,向上为y轴正方向。上述旋转是绕坐标原点进行的,如果是绕指定点(a,b)旋转,那么应该先将坐标系平移至改点,再旋转,然后平移至新的坐标原点。

下面推导坐标系平移的变换公式。坐标系是图像的坐标系,坐标系是旋转坐标系,坐标系的原点在坐标系中为(a,b),如下图所示。


两种坐标系之间的转换为:


逆变换为:


有了上面的公式,就可以很方便的推导图像旋转变换的表达式。假设图像未旋转时候旋转中心的坐标是a,b,旋转后中心点的坐标为c,d(在新的坐标系下,以旋转后图像的左上角为原点),则可以把变换分为3步:

第一步,将坐标系Ⅰ变成Ⅱ;

第二步,旋转θ(逆时针为正,顺时针为负);

第三步,将坐标系Ⅱ变换回Ⅰ。这样就得到了总的变换矩阵。

设原图像某像素点的坐标为(x0y0),旋转后在目标图像的坐标为(x1y1),则旋转变换的矩阵表达式为:


逆变换为:


有了上面的转换公式,就可以很方便的编写出实现图像旋转的程序。首先需要计算出公式中需要的几个参数:abcd和旋转后图像的尺寸。已知原是图像的宽度为w0,高度为h0,以图像的中心为坐标原点。则原图像四个角的坐标分别是:


按照旋转公式,旋转后这四个点的坐标分别是:


则新图像的高度和宽度分别为:



图像旋转的主要代码如下:

void RotIamge(const Mat &srcImage, Mat &dstImage, double angle)  
{  
    //弧度  
    double sita = angle * CV_PI / 180;  
    double a = (srcImage.cols - 1) / 2.0;  
    double b = (srcImage.rows - 1) / 2.0;  
  
    int srcRow = srcImage.rows;  
    int srcCol = srcImage.cols;  
  
    double x1 = -a * cos(sita) - b * sin(sita);  
    double y1 = -a * sin(sita) + b * cos(sita);  
  
    double x2 = a * cos(sita) - b * sin(sita);  
    double y2 = a * sin(sita) + b * cos(sita);  
  
    double x3 = a * cos(sita) + b * sin(sita);  
    double y3 = a * sin(sita) - b * cos(sita);  
  
    double x4 = -a * cos(sita) + b * sin(sita);  
    double y4 = -a * sin(sita) - b * cos(sita);  
  
    int w1 = cvRound(max(abs(x1 - x3), abs(x4 - x2)));  
    int h1 = cvRound(max(abs(y1 - y3), abs(y4 - y2)));  
    dstImage.create(h1, w1, srcImage.type());  
  
    double c = (w1 - 1) / 2.0;  
    double d = (h1 - 1) / 2.0;  
  
    double f1 = -c * cos(sita) + d * sin(sita) + a;  
    double f2 = -c * sin(sita) - d * sin(sita) + b;  
    int nRowNum = dstImage.rows;  
    int nColNum = dstImage.cols;  
    for (int i = 0; i < nRowNum; i++)  
    {  
  
        for (int j = 0; j < nColNum; j++)  
        {  
            int x = cvRound(j * cos(sita) - i * sin(sita) + f1);  
            int y = cvRound(j * sin(sita) + i * cos(sita) + f2);  
            if (x > 0 && x < srcCol && y > 0 && y < srcRow)  
            {  
                dstImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(y, x);  
            }  
        }  
    }  
}  


对于旋转以后图像大小不变的情况,旋转前后图像的中心点坐标都是a,b,那么旋转的变换矩阵就是:


逆变换为:


公式中,


主要代码如下:

void RotIamge2(const Mat &srcImage, Mat &dstImage, double angle)  
{  
    //弧度  
    double sita = angle * CV_PI / 180;  
    double a = (srcImage.cols - 1) / 2.0 + 0.5;  
    double b = (srcImage.rows - 1) / 2.0 + 0.5;  
  
    int nRowNum = srcImage.rows;  
    int nColNum = srcImage.cols;  
    dstImage.create(nRowNum, nColNum, srcImage.type());  
  
    double f1 = -a * cos(sita) + b * sin(sita) + a;  
    double f2 = -a * sin(sita) - b * cos(sita) + b;  
  
    for (int i = 0; i < nRowNum; i++)  
    {  
        for (int j = 0; j < nColNum; j++)  
        {  
            int x = cvRound(j * cos(sita) - i * sin(sita) + f1);  
            int y = cvRound(j * sin(sita) + i * cos(sita) + f2);  
            if (x > 0 && x < nColNum && y > 0 && y < nRowNum)  
            {  
                dstImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(y, x);  
            }  
        }  
    }  
}  

要注意的是,由于有浮点运算,计算出来的坐标可能不是整数,需要采取取整处理,使用cvRound()函数寻找最接近的点,这样会带来一些误差,图像可能会出现锯齿,更好的方式是采用插值,后续将会具体介绍。


2016-05-23 14:37:51 lpsl1882 阅读数 3034

图像几何变换基础

    我们实际获取的图像,常常因为各种原因,相对于原图有一定的几何形变,或者出于处理考虑,需要进行几何变换。二维图像坐标的几何变换写为:(x,y)=r(x,y)。根据图像几何变换的程度,可以将图像几何变换划分为如下几类:

  • 刚体变换 如果一幅图像中的两点间的距离经变换到另一幅图像中后仍然保持不变,则这种变换称为刚体变换(Rigid Transform)。刚体变换仅局限于平移、旋转和反转(镜像)。
  • 仿射变换 如果一幅图像中的直线经过后映射到另一幅图像上仍为直线,并且保持平行关系,则这种变换称为仿射变换(Affine Transform。仿射变换适应于平移、旋转、缩放和反转(镜像)情况。
  • 投影变换 如果一幅图像中的直线经过后映射到另一幅图像上仍为直线,但平行关系基本不保持,则这种变换称为投影变换(Projective Transform )。
  • 非线性变换 非线性变换又称为弯曲变换(Curved Transform)或者弹性变换,经过非线性变换,一幅图像上的直线映射到另一幅图像上不一定是直线,可能是曲线。

广义来讲,非线性变换包含投影变换;投影变换包含仿射变换;仿射变换包含刚体变换。非线性变换下,新旧坐标的一类简单的表达式写为:

xkykwk=a11xmk+a12xm1kyk+...a1mymk+a1wk=a21xmk+a22xm1kyk+...a2mymk+a2wk=a31xmk+a32xm1kyk+...a3mymk+a3wk,n=1,2,3...(1)

在投影变换范围下,新旧坐标的变换呈线性,表达式较为简单:
xkykwk=a1xk+a2yk+a3wk=a4xk+a5yk+a6wk=a7xk+a8yk+a9wk,k=1,2,3...n(2)

same as x1x2x3...xny1y2y3ynw1w2w3wn=a1a4a7a2a5a8a3a6a9x1x2x3...xny1y2y3ynw1w2w3wnB=AX(3)

A称为空间变换矩阵。其中[a1a4a2a5]控制旋转、缩放和斜切,[a7a8]控制平移,[a3a6]控制透视变换;w是归一化因子,要求wkwk都是1,在特殊情况下为了便于分析可以不为1。三维图像空间变换同理,扩展出z变量,组成一个4×4的变换矩阵即可。
    如果我们知道需要做何种变换,直接在单位空间变化矩阵上修改相应系数即可;如果我们不知道图像相比原图像做了什么变换,那么就需要设法获取图像的变换信息。常见的方法是手工标记前后两幅图像中的对应特征点,根据特征点的变换来计算得到变换的矩阵。
    我这里的计算限制于透视变换,或者非弹性变换。当我们获取足够的变换特征点信息,就可以计算变换矩阵,方法是求解(2)式中的系数矩阵A。这里单独看方程式很像求解多元一次方程组,但是多元一次方程组求解的是x,y而不是系数;求解系数矩阵的一个著名方法,前面在【机器学习】线性回归小结提到,即最小二乘法。
   假设我要变换一个256x256的图像,目标图像的四个边角坐标为(0,0),(180,75),(255,255),(75,180)。由于图像通常是方形的,因此四个边角点可以作为变换特征点。在《数字图像处理》书中,对四边形的空间变换使用了双线性方程来描述,这里我们直接使用空间变换矩阵,写成:
0180255750752551801111=a1a4a7a2a5a8a3a6a902552550002552551111
用最小二乘法得到
A0.7060.29400.2940.7060001

原图
原图
空间变换
变换后

2019-10-17 17:27:12 ViatorSun 阅读数 1964

1、简介

旋转变换是计算机图像学中应用非常广泛的一种变换,为了后面解释的需要,我们也添加了平移变换、缩放变化等内容。文章重点介绍关于旋转的变换,包括二维旋转变换、三维旋转变换以及它的一些表达方式(旋转矩阵、四元数、欧拉角等)。

2、平移变换

将三维空间中的一个点 [x,y,z,1][x, y, z, 1] 移动到另外一个点 [x,y,z,1][x', y', z', 1],三个坐标轴的移动分量分别为dx=Tx,dy=Ty,dz=Tz,dx=Tx, dy=Ty, dz=Tz,

{x=x+Txy=y+Tyz=z+Tz\begin{cases} x' = x + Tx \\ y' = y + Ty \\ z' = z + Tz \end{cases}

平移变换的矩阵如下。

[xyz1]=[100Tx010Ty001Tz0001][xyz1]\begin{bmatrix} x'\\ y' \\ z' \\1 \end {bmatrix} = \begin {bmatrix} 1 & 0 & 0 & Tx \\ 0 & 1 &0 & Ty \\ 0 & 0 & 1 & Tz \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end {bmatrix}

3、缩放变换

将模型放大或者缩小,本质也是对模型上每个顶点进行放大和缩小(顶点坐标值变大或变小),假设变换前的点是 [x,y,z,1][x, y, z, 1],变换后的点是 [x,y,z,1][x', y', z', 1],那么

{x=xSxy=ySyz=zSz\begin{cases} x' = x * S_x \\ y' = y * S_y \\ z' = z * S_z \end{cases}

缩放变换的矩阵如下。

[xyz1]=[Sx0000Sy0000Sz00001][xyz1]\begin{bmatrix} x'\\ y' \\ z' \\1 \end {bmatrix} = \begin {bmatrix} S_x & 0 & 0 & 0 \\ 0 & S_y &0 & 0 \\ 0 & 0 & S_z & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end {bmatrix}

3、逆矩阵

平移变换矩阵的逆矩阵与原来的平移量相同,但是方向相反。

T1T=[100Tx010Ty001Tz0001][100Tx010Ty001Tz0001]=I T^{-1} \cdot T = \begin {bmatrix} 1 & 0 & 0 & -T_x \\ 0 & 1 & 0 & -T_y \\ 0 & 0& 1 & -T_z \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} 1 & 0 & 0 & T_x \\ 0 & 1 & 0 & T_y \\ 0 & 0& 1 & T_z \\ 0 & 0 & 0 & 1 \end {bmatrix} = I \quad

旋转变换矩阵的逆矩阵与原来的旋转轴相同但是角度相反。

R1R=[10000cosθsinθ00sinθcosθ00001][10000cosθsinθ00sinθcosθ00001]=I R^{-1} \cdot R = \begin {bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\theta & sin\theta & 0 \\ 0 & -sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin {bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix}= I

R1R=[cosθ0sinθ00100sinθ0cosθ00001][cosθ0sinθ00100sinθ0cosθ00001]=I R^{-1} \cdot R = \begin {bmatrix} cos\theta & 0 & -sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ sin\theta & 0 & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin {bmatrix} cos\theta & 0 & sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -sin\theta & 0 & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} = I

R1R=[cosθsinθ00sinθcosθ0000100001][cosθsinθ00sinθcosθ0000100001]=I R^{-1} \cdot R = \begin {bmatrix} cos\theta & sin\theta & 0 & 0 \\ -sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin {bmatrix} cos\theta & -sin\theta & 0 & 0 \\ sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} = I

4、旋转变换

4.1 绕原点二维旋转

首先要明确旋转在二维中是绕着某一个点进行旋转,三维中是绕着某一个轴进行旋转。二维旋转中最简单的场景是绕着坐标原点进行的旋转,如下图所示:

在这里插入图片描述

如图所示点 (x,y)(x, y) 绕原点 逆时针旋转 θ\theta ,得到点 (x,y)(x', y')

注:此处为逆时针旋转,因此旋转过后的 (x,y)(x',y') 坐标采用 (α+θ)(\alpha + \theta) 而不是 (αθ)(\alpha - \theta)

矩阵解释:

[xy]=[cosθsinθsinθcosθ][xy]\begin{bmatrix} x'\\ y' \end {bmatrix} = \begin {bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end {bmatrix} \cdot \begin{bmatrix} x\\ y \end {bmatrix}

利用矩阵乘法展开为

{x=xcosθysinθy=xsinθ+ycosθ\begin{cases}x' = x\cdot cos\theta - y\cdot sin\theta \\ y' =x\cdot sin\theta+y\cdot cos\theta\end{cases}

极坐标解释:

此处不用直角坐标系解释,极坐标解释更便于理解
(x,y)(x,y) 坐标:{x=rcosαy=rsinα\begin{cases}x = r\cdot cos\alpha \\ y =r \cdot sin\alpha \end{cases}

(x,y)(x',y') 坐标:{x=rcos(α+θ)     =rcosαcosθrsinαsinθ     =xcosθysinθy=rsin(α+θ)     =rsinαcosθ+rcosαsinθ     =xcosθ+ysinθ\begin{cases}x' = r\cdot cos(\alpha + \theta) \\ \space\space\space\space\space = r\cdot cos\alpha\cdot cos \theta - r \cdot sin \alpha\cdot sin \theta \\ \space\space\space\space\space = x\cdot cos \theta -y\cdot sin \theta \\ \\ y' =r \cdot sin(\alpha + \theta) \\ \space\space\space\space\space = r\cdot sin\alpha\cdot cos \theta + r \cdot cos \alpha\cdot sin \theta \\ \space\space\space\space\space = x\cdot cos \theta +y\cdot sin \theta \end{cases}

4.2 绕任意点的二维旋转

绕原点的旋转是二维旋转最基本的情况,当我们需要进行绕任意点旋转时,我们可以把这种情况转换到绕原点的旋转,思路如下:

  1. 首先将旋转点移动到原点处
  2. 执行如2所描述的绕原点的旋转
  3. 再将旋转点移回到原来的位置

在这里插入图片描述

也就是说在处理绕任意点旋转的情况下需要执行两次平移的操作。假设平移的矩阵是 T(x,y)T(x,y),也就是说我们需要得到的坐标 v=T(x,y)RT(x,y)v'=T(x,y)*R*T(-x,-y)(我们使用的是列坐标描述点的坐标,因此是左乘,首先执行 T(x,y)T(-x,-y)

在计算机图形学中,为了统一将平移、旋转、缩放等用矩阵表示,需要引入齐次坐标。(假设使用2x2的矩阵,是没有办法描述平移操作的,只有引入3x3矩阵形式,才能统一描述二维中的平移、旋转、缩放操作。同理必须使用4x4的矩阵才能统一描述三维的变换)。

对于二维平移,如下图所示,PP 点经过x和y方向的平移到 PP' 点,可以得到:

在这里插入图片描述

{x=x+txy=y+ty\begin{cases} x′=x+t_x \\ y′=y+t_y \end{cases}

由于引入了齐次坐标,在描述二维坐标的时候,使用 (xyw)(x,y,w) 的方式(一般 w=1w=1 ),于是可以写成下面矩阵的形式

[xy1]=[10tx01ty001][xy1]\begin{bmatrix} x'\\ y' \\1 \end {bmatrix} = \begin {bmatrix} 1 & 0 &tx \\ 0 & 1 & ty \\ 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end {bmatrix}

按矩阵乘法展开,正好得到上面的表达式。其中平移矩阵是

[10tx01ty001]\quad \begin {bmatrix} 1 & 0 &tx \\ 0 & 1 & ty \\ 0 & 0 & 1 \end {bmatrix}

此时将绕原点二维旋转中描述的旋转矩阵也扩展到 3x3 的方式,变为:

[xy1]=[cosθsinθ0sinθcosθ0001][xy1]\begin{bmatrix} x'\\ y' \\1 \end {bmatrix} = \begin {bmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end {bmatrix}

从平移和旋转的矩阵可以看出,3x3 矩阵的前 2x2 部分是和旋转相关的,第三列与平移相关。有了上面的基础之后,我们很容易得出二维中绕任意点旋转的旋转矩阵了,只需要把三个矩阵乘起来即可:

M=[10tx01ty001][cosθsinθ0sinθcosθ0001][10tx01ty001] M = \begin {bmatrix} 1 & -0 & tx \\ 0 & 1 & ty \\ 0 & 0 & 1 \end {bmatrix} \cdot \begin {bmatrix} cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1 \end {bmatrix} \cdot \begin {bmatrix} 1 & 0 & -tx \\ 0 & 1 & -ty \\ 0 & 0 & 1 \end {bmatrix}

4.3 三维基本旋转

我们可以把一个旋转转换为绕基本坐标轴的旋转,因此有必要讨论一下绕三个坐标值 x,y,zx,y,z 的旋转。

本文在讨论过程中使用的是类似于OpenGL中定义的右手坐标系,同时旋转角度的正负也遵循右手坐标系的约定。如下图所示

在这里插入图片描述

4.3.1 绕X轴的旋转

在这里插入图片描述

在三维场景中,当一个点 P(x,y,z)P(x,y,z)XX 轴旋转 θ\theta 角得到点 P(x,y,z)P'(x',y',z')。由于是绕 XX 轴进行的旋转,因此 XX 坐标保持不变,YYZZ 组成的 YOZYOZOO是坐标原点)平面上进行的是一个二维的旋转,可以参考上图(YY轴类似于二维旋转中的 XX 轴,ZZ 轴类似于二维旋转中的 YY轴),于是有:

{x=xy=ycosθzsinθz=ysinθ+zcosθ\begin{cases} x′=x \\ y′=ycos\theta−zsin\theta \\ z′=ysin\theta+zcos\theta \end{cases}

写成(4x4)矩阵的形式

[xyz1]=[10000cosθsinθ00sinθcosθ00001][xyz1]\begin{bmatrix} x'\\ y' \\ z' \\1 \end {bmatrix} = \begin {bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end {bmatrix}

4.3.2 绕Y轴旋转

在这里插入图片描述

YY 轴的旋转和绕 XX 轴的旋转类似,YY 坐标保持不变,除Y轴之外,ZOXZOX组成的平面进行一次二维的旋转(ZZ轴类似于二维旋转的XX轴,XX 轴类似于二维旋转中的 YY 轴,注意这里是 ZOXZOX ,而不是 XOZXOZ,观察上图中右手系的图片可以很容易了解到这一点),同样有:

{x=zsinθ+xcosθy=yz=zcosθxsinθ\begin{cases} x′=zsin\theta+xcos\theta \\ y′=y \\ z′=zcos\theta-xsin\theta \end{cases}

写成(4x4)矩阵的形式

[xyz1]=[cosθ0sinθ00100sinθ0cosθ00001][xyz1]\begin{bmatrix} x'\\ y' \\ z' \\1 \end {bmatrix} = \begin {bmatrix} cos\theta & 0 & sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -sin\theta & 0 & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end {bmatrix}

4.3.3 绕Z轴旋转

在这里插入图片描述

与上面类似,绕 ZZ 轴旋转,ZZ 坐标保持不变,XOYXOY组成的平面内正好进行一次二维旋转(和上面讨论二维旋转的情况完全一样)

[xyz1]=[cosθsinθ00sinθcosθ0000100001][xyz1]\begin{bmatrix} x'\\ y' \\ z' \\1 \end {bmatrix} = \begin {bmatrix} cos\theta & -sin\theta & 0 & 0 \\ sin\theta & cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end {bmatrix}

4.4 小结

上面描述了三维变换中绕单一轴旋转的矩阵表达形式,绕三个轴旋转的矩阵很类似,其中绕y轴旋转的矩阵与绕 XXZZ 轴旋转的矩阵略有点不同(主要是三个轴向顺序和书写矩阵的方式不一致导致的,绕三个不同坐标旋转轴以及其他二个坐标轴组成平面的顺序是: XYZXYZ (绕 XX 轴) YZXYZX(绕YY 轴) ZXYZXY(绕 ZZ 轴),其中绕 YY 轴旋转,其他两个轴是 ZXZX,这和我们书写矩阵按

[xyz1]\begin{bmatrix} x\\ y\\z\\1 \end {bmatrix}

的方式不一致,而导致看起来绕 YY 轴旋转的矩阵似乎是和其他两个矩阵不一致。如果我们颠倒写法,将公式写成

[zyx1]=[cosθ0sinθ00100sinθ0cosθ00001][zyx1]\begin{bmatrix} z' \\ y' \\ x' \\1 \end {bmatrix} = \begin {bmatrix} cos\theta & 0 & -sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ sin\theta & 0& cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} z \\ y \\ x \\ 1 \end {bmatrix}

的方式,那么这三个旋转矩阵看起来在形式上就统一了,都是

[cosθsinθcosθsinθ]\quad \begin {bmatrix} cos\theta & -sin\theta \\ cos\theta & sin\theta \end {bmatrix}

这种表现形式了(右上角都是 sinθ−sin\theta

5、绕任意轴的三维旋转

绕任意轴旋转的情况比较复杂,主要分为两种情况,一种是平行于坐标轴的,一种是不平行于坐标轴的。

5.1 平行坐标轴

对于平行于坐标轴的,我们首先将旋转轴平移至与坐标轴重合,然后进行旋转,最后再平移回去。

  1. 将旋转轴平移至与坐标轴重合,对应平移操作 TT
  2. 旋转,对应操作 RR
  3. 步骤1的逆过程,对应操作 T1T^{-1}

整个过程就是
P=T1RTPP' = T^{-1} \cdot R \cdot T \cdot P

5.2 不平行坐标轴

对于不平行于坐标轴的,可按如下方法处理。(该方法实际上涵盖了上面的情况)

绕任意轴的三维旋转可以使用类似于绕任意点的二维旋转一样,将旋转分解为一些列基本的旋转。绕任意轴旋转如下图所示:

  1. 将旋转轴平移至原点
  2. 将旋转轴旋转至 YOZYOZ 平面
  3. 将旋转轴旋转至于 ZZ 轴重合
  4. ZZ 轴旋转 θ\theta
  5. 执行步骤3的逆过程
  6. 执行步骤2的逆过程
  7. 执行步骤1的逆过程

图形详解过程:

步骤1:

将原旋转轴 uu 平移 vv 至过原点 OO ,对应矩阵操作

P1=[100Tx010Ty001Tz0001][xyz1]=T1PP_1 = \begin {bmatrix} 1 & 0 & 0 & Tx \\ 0 & 1 &0 & Ty \\ 0 & 0 & 1 & Tz \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x \\ y \\ z \\ 1 \end {bmatrix} =T_1 \cdot P

在这里插入图片描述

步骤2:

旋转操作,将 uuzz 轴旋转 α\alpha 角 至 XOZXOZ 平面,对应的图如下

P2=[cosαsinα00sinαcosα0000100001][x1y1z11]=RzP1 P_2 = \begin {bmatrix} cos\alpha & -sin\alpha & 0 & 0 \\ sin\alpha & cos\alpha & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x_1 \\ y_1 \\ z_1 \\ 1 \end {bmatrix} =R_z \cdot P_1

在这里插入图片描述

步骤3:

步骤3也是一个旋转操作,将 uuyy 旋转 β\beta 至与 xx 轴重合,对应的图如下

P3=[cosβ0sinβ00100sinβ0cosβ00001][x2y2z21]=RyP2P_3 = \begin {bmatrix} cos\beta & 0 & sin\beta & 0 \\ 0 & 1 & 0 & 0 \\ -sin\beta & 0 & cos\beta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x_2 \\ y_2 \\ z_2 \\ 1 \end {bmatrix} =R_y \cdot P_2

在这里插入图片描述

步骤4:

此时原旋转轴经过一系列的操作,已经与 xx 轴重合了 ,即 uu'' ,此时原来的 P,QP , Q 点已经映射到 P,QP' ,Q' ,只需将 PP'xx 轴旋转 θ\theta 角便可得到 QQ' 点,此时的操作为:

P4=[10000cosθsinθ00sinθcosθ00001][x3y3z31]=RxP3P_4 = \begin {bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos\theta & -sin\theta & 0 \\ 0 & sin\theta & cos\theta & 0 \\ 0 & 0 & 0 & 1 \end {bmatrix} \cdot \begin{bmatrix} x_3 \\ y_3 \\ z_3 \\ 1 \end {bmatrix} =R_x \cdot P_3

在这里插入图片描述

步骤5:

此时再将 步骤3,2,1逆操作即可得到原始点 PP 点绕旋转轴 uu 旋转 θ\theta 角后的点 QQ

对应的操作即为 Q=T1Rz1Ry1P4 Q = T^{-1} \cdot R_z^{-1} \cdot R_y^{-1} \cdot P_4

综述:

对于不平行于坐标轴的旋转其实已经包含了平行于坐标轴的旋转,因此对于绕任意轴的三维旋转可以总结为:

Q=MP Q = M \cdot P
其中 M=T1Rz1Ry1RxRyRxT M = T^{-1} \cdot R_z^{-1} \cdot R_y^{-1} \cdot R_x \cdot R_y \cdot R_x \cdot T

5.3 α,β,θ 角度补充

为了全文的排版,以及读者体验,上述解释全部采用的 α,β,θ\alpha ,\beta ,\theta ,具体从何得来并没有做解释说明,在此将对 α,β,θ\alpha ,\beta ,\theta 的缘由做个详细解释。

  1. 在此,假设旋转轴 uu[(a1,b1,c1),(a2b2,c2)][(a_1,b_1,c_1),(a_2,b_2,c_2)] 表示
    在这里插入图片描述
  1. 旋转轴 uu 经过平移 T1T_1 变换后过原点的旋转轴 uu'uu' 的坐标为 (a,b,c)(a,b,c)
    在这里插入图片描述
  1. 旋转轴 uu' 上的点 (a,b,c)(a,b,c)
    XOYXOY 平面上的投影为 OA(a,b,0)OA(a,b,0)
    XOZXOZ 平面上的投影为 OB(a,0,c)OB(a,0,c)
    那么
    sinα=ba2+b2  ,  cosα=aa2+b2sin\alpha = { b \over \sqrt{a^2 + b^2}} \space\space , \space\space cos\alpha = {a \over \sqrt{a^2 + b^2}}
    sinβ=ca2+c2  ,  cosβ=aa2+c2sin\beta = { c \over \sqrt{a^2 + c^2}}\space\space , \space\space cos\beta = {a \over \sqrt{a^2 + c^2}}
    在这里插入图片描述

参考链接:

https://www.cnblogs.com/zhoug2020/p/7842808.html
https://www.cnblogs.com/graphics/archive/2012/08/10/2627458.html

2016-11-09 15:16:15 u012428169 阅读数 359

作为代码界的菜鸟,最近在尝试着用vs实现《数字图像处理与机器视觉》一书中有关图像处理的VC++代码。目前先从简单的图像几何变换做起,希望能记录自己的成长^_^
本篇是图像旋转变换的实现。
代码中实现的是顺时针变换,如果想要逆时针变换,可以直接把角度设为负值,也可以在旋转函数中进行相应改变,改变方法可参照代码注释中变换方程式。
缺点是这个方法虽然显示了转换后的完整图像,但是旋转后显示的图像跟原图像相比,尺寸有所变化,目前还不知道怎么解决,希望在以后能够解决这个问题。
代码如下:

#include <iostream>
#include <mat.h>//头文件中增加了math.h
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;
/********************************************
void imgRotate(Mat img, Mat &dst, float ang)
功能: 以原点为中心的图像旋转
注: 图像左上顶点顺时针旋转x1=x0*cost+y0*sint;y1=x0*(-sint)+y0*cost
     图像左上顶点逆时针旋转x1=x0*cost-y0*sint;y1=x0*sint+y0*cost;
参数: Mat img:原图像
       Mat dst:转置后图像
       float ang: 旋转的角度
返回值: 无
*********************************************/
void imgRotate(Mat img, Mat &dst, float ang)
{
    float pi = 3.1415926;
    int nH = img.rows;
    int nW = img.cols;
    ang = ang*pi / 180;
    int i, j, u, v,minx=0,miny=0,maxx=0,maxy=0;
    //这个循环是为了确定旋转变换后原图像各像素对应的坐标,进而确定目标图像的大小,进而显示全部图像
    for (i = 0; i < nH; i++)
    {
        for (j = 0; j < nW; j++)
        {
            u = int(i*cos(ang) + j*sin(ang) + 0.5);
            v = int(-i*sin(ang) + j*cos(ang) + 0.5);
            if (minx>u)
                minx = u;
            if (maxx < u)
                maxx = u;
            if (miny>v)
                miny = v;
            if (maxy < v)
                maxy = v;
        }
    }
    int dH = abs(minx)+maxx;
    int dW = abs(miny)+maxy;
    dst.create(dH, dW, img.type());
    //注意这个循环的起始和终止条件
    //判断目标图像的像素经过逆变换后的像素坐标在不在原图像中来确定目标图像像素值
    for (i = minx; i < maxx; i++)
    {
        for (j = miny; j < maxy; j++)
        {
            u = int(i*cos(ang) - j*sin(ang) + 0.5);
            v = int(i*sin(ang) + j*cos(ang) + 0.5);
            if (u>0 && u<nH&&v>0 && v < nW)
                dst.at<Vec3b>(i+abs(minx), j+abs(miny)) = img.at<Vec3b>(u, v);//i和j都加上最小行和列值的绝对值是因为目标图像的像素坐标均为正值
            else
                dst.at<Vec3b>(i + abs(minx), j + abs(miny)) = 0;
        }
    }
}
int main()
{
    Mat img = imread("1.jpg");
    imshow("原图", img);
    Mat dst;
    //以图像左顶点为中心进行旋转变换
    imgRotate(img, dst, 30);
    cvNamedWindow("旋转变换",CV_WINDOW_FREERATIO);//这句是为了在显示结果图的时候可以自由控制结果图窗口的大小
    imshow("旋转变换", dst);
    waitKey(0);
    return 0;
}

结果图如下:
原图
顺时针30度旋转结果:
顺时针30度
逆时针30度旋转结果(角度设为-30):
逆时针30度


在网上看到了一篇大牛实现图像旋转的文章,非常全面,涵盖了MATLAB实现,IplImage实现和Mat实现,文章链接如下:


图像旋转代码实现非常全的文章

2018-05-10 22:37:12 qq_37486501 阅读数 1767

以灰度图像cameraman.tif为例,利用Matlab图像处理工具箱中的imrotate函数对图像进行旋转变换。要求:创建3个figure窗口,分别用于显示原始图像、逆时针旋转45°后的图像、顺时针旋转45°后的图像。并保存旋转后的所有图像文件到当前目录中。

I=imread('cameraman.tif');
I_45N=imrotate(I,45,'bilinear');
imwrite(I_45N,'cameraman_nishizhen45.tif');
I_30S=imrotate(I,-30,'bilinear');
imwrite(I_30S,'cameraman_shuunshizhen30.tif');
figure(1);
imshow(I);
figure(2);
imshow(I_45N);
figure(3);
imshow(I_30S);



没有更多推荐了,返回首页