计算机视觉基础(2)-几何变换


在三维图形学中,几何变换大致分为三种,平移变换(Translation),缩放变换(Scaling),旋转变换(Rotation)。

1、 平移变换(Translation)

1、二维空间

二维空间中一个点[x,y],移动到另一个点[x’,y’],假设两个点分别沿着坐标轴x,y移动了Tx与Ty,即:
x’ = x + Tx
y’ = y + Ty
为了更好地描述和表达这种平移变换,我们将线性方程转换为矩阵的形式;
[ x ′ y ′ ] = [ 1 0 T x 0 1 T y ] ∗ [ x y ] \begin{bmatrix} x' \\y' \end{bmatrix}=\begin{bmatrix} 1& 0 &Tx \\ 0& 1 &Ty \end{bmatrix}*\begin{bmatrix} x\\ y \end{bmatrix} [xy]=[1001TxTy][xy]
但是这样是无法直接运算的,为了解决这个问题,我们引入了齐次坐标,所以将上式转换为:

#include <graphics.h>		// 引用图形库头文件
#include <conio.h>
#include <Eigen/core>
using namespace Eigen;
void transform(int x1, int y1, int x2, int y2,double Tx,double Ty)
{
	line(x1, y1, x2, y2);
	
	Eigen::Matrix3d  tranMatrix;
	tranMatrix << 1, 0, Tx, 0, 1, Ty, 0, 0, 01;
	Eigen::Vector3d vecbegin, vecend,vecb,vece;
	vecbegin << x1, y1, 1;
	vecend << x2, y2, 1;
	vecb = Eigen::Vector3d::Zero();
	vece = Eigen::Vector3d::Zero();

	vecb = tranMatrix * vecbegin;
	vece = tranMatrix * vecend;
	line(vecb[0], vecb[1], vece[0], vece[1]);

}
int main()
{
	//沿x平移100,沿y平移50;
	initgraph(640, 480);	// 创建绘图窗口,大小为 640x480 像素
	transform(200, 200, 300, 300,100,50);
	_getch();				// 按任意键继续
	closegraph();			// 关闭绘图窗口
	return 0;
}

在这里插入图片描述
此时需要使用Eigen库,Eigen库在windows上配置与Linux一样,只需要指定头文件就可以。
[ x ′ y ′ 1 ] = [ 1 0 T x 0 1 T y 0 0 1 ] ∗ [ x y 1 ] \begin{bmatrix} x' \\y' \\ 1 \end{bmatrix}=\begin{bmatrix} 1& 0 &Tx \\ 0& 1 &Ty \\ 0 &0&1 \end{bmatrix}*\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} xy1=100010TxTy1xy1

2、三维空间

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

x’ = x + Tx

y’ = y + Ty

z’ = z + Tz

平移变换的矩阵如下。
[ x ′ y ′ z ′ 1 ] = [ 1 0 0 T x 0 1 0 T y 0 0 1 T z 0 0 0 1 ] ∗ [ x y z 1 ] \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}*\begin{bmatrix} x\\y\\z\\1 \end{bmatrix} xyz1=100001000010TxTyTz1xyz1
请添加图片描述


2、 缩放变换(Scaling)

1、二维空间

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

y’ = y * Sy

同样我们需要将其转换为齐次坐标的形式,将线性表达式转换为矩阵:
[ x ′ y ′ 1 ] = [ S x 0 0 0 S y 0 0 0 1 ] ∗ [ x y 1 ] \begin{bmatrix} x'\\y'\\1 \end{bmatrix}=\begin{bmatrix} Sx& 0 & 0\\ 0& Sy & 0 \\ 0 & 0 &1 \end{bmatrix} *\begin{bmatrix} x\\y \\1 \end{bmatrix} xy1=Sx000Sy0001xy1

#include <graphics.h>		// 引用图形库头文件
#include <conio.h>
#include <Eigen/core>
using namespace Eigen;

void ScaleTransform(int x1, int y1, int x2, int y2, double scalex, double scaley)
{
	line(x1, y1, x2, y2);
	Eigen::Matrix3d  tranMatrix;
	tranMatrix << scalex, 0, 0, 0, scaley, 0, 0, 0, 1;
	Eigen::Vector3d vecbegin, vecend, vecb, vece;
	vecbegin << x1, y1, 1;
	vecend << x2, y2, 1;
	vecb = Eigen::Vector3d::Zero();
	vece = Eigen::Vector3d::Zero();

	vecb = tranMatrix * vecbegin;
	vece = tranMatrix * vecend;
	line(vecb[0], vecb[1], vece[0], vece[1]);
}
int main()
{
	initgraph(640, 480);	// 创建绘图窗口,大小为 640x480 像素
	ScaleTransform(200, 200, 300, 300, 2, 0.5); //横坐标放大二倍,纵坐标缩短一半
	_getch();				// 按任意键继续
	closegraph();			// 关闭绘图窗口
	return 0;
}

请添加图片描述
请添加图片描述

2、三维空间

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

x’ = x * Sx

y’ = y * Sy

z’ = z * Sz

缩放变换的矩阵如下:
[ x ′ y ′ z ′ 1 ] = [ S x 0 0 0 0 S y 0 0 0 0 S z 0 0 0 0 1 ] ∗ [ x y z 1 ] \begin{bmatrix} x'\\y'\\z'\\1 \end{bmatrix}=\begin{bmatrix} Sx& 0 & 0&0\\ 0& Sy & 0 &0\\ 0& 0&Sz&0\\ 0 & 0&0 &1 \end{bmatrix} *\begin{bmatrix} x\\y \\z\\1 \end{bmatrix} xyz1=Sx0000Sy0000Sz00001xyz1


3、 旋转变换(Rotation)

旋转矩阵的旋转其实包含两种意思,一是在同一个坐标系下,向量的旋转;二是坐标系的旋转,使得同一向量在不同的坐标系下有不同的坐标。
首先推导公式之前我们假设的旋转角都为正,即逆时针方向角度,所谓逆时针指的就是大拇指指向坐标正向时,四指旋转的抓握方向

1、 向量旋转

首先讨论二维平面坐标下的旋转,然后引申至三维。

1.1 平面二维旋转

如下图,XY坐标系中,向量OP旋转β角度到了OP’的位置:

在这里插入图片描述

根据三角函数关系,可以列出向量OP与OP’的坐标表示形式:

对比上面个两个式子,将第2个式子展开:
请添加图片描述
用矩阵形式重新表示为:
[ x ′ y ′ ] = [ c o s β − s i n β s i n β c o s β ] ∗ [ x y ] \begin{bmatrix} x' \\y' \end{bmatrix}=\begin{bmatrix} cos\beta & -sin\beta\\ sin\beta& cos\beta \end{bmatrix}* \begin{bmatrix} x \\y \end{bmatrix} [xy]=[cosβsinβsinβcosβ][xy]

这就是二维旋转的基本形式,中间的矩阵即二维旋转的旋转矩阵,坐标中的某一向量左乘该矩阵后,即得到这个向量旋转β角后的坐标。

void Rotation(int x1, int y1, int x2, int y2, double beta)
{
	//其中 beta 必须采用 弧度:PI/6
	line(x1, y1, x2, y2);
	Eigen::Matrix2d  tranMatrix;
	tranMatrix << cos(beta), -sin(beta), sin(beta), cos(beta);

	std::cout << tranMatrix << std::endl;
	Eigen::Vector2d point1, point2, point1_, point2_;

	point1 << x1, y1;
	point2 << x2, y2;

	point1_ = tranMatrix * point1;
	point2_ = tranMatrix * point2;

	setlinecolor(RGB(255,0,0));
	line(point1_[0], point1_[1], point2_[0], point2_[1]);
}

在这里插入图片描述

请添加图片描述

在这里插入图片描述

如果向量p顺时针旋转,则旋转矩阵为:

[ x ′ y ′ ] = [ c o s β s i n β − s i n β c o s β ] ∗ [ x y ] \begin{bmatrix} x' \\y' \end{bmatrix}=\begin{bmatrix} cos\beta & sin\beta\\ -sin\beta & cos\beta \end{bmatrix}*\begin{bmatrix} x\\y \end{bmatrix} [xy]=[cosβsinβsinβcosβ][xy]

1.2 三维旋转

三维旋转可借助二维旋转来理解,由于三维空间中可以任意轴旋转,为方便分析与使用,只考虑绕X、Y、Z轴的旋转。

  • 绕Z轴
    参照上面的图,添加一个Z轴,则上面的二维旋转实际上就是绕Z轴的三维旋转
    在这里插入图片描述
    照搬上面的推导公式,并添加Z坐标的变换关系(实际是没有变),然后改写成矩阵形式,红色方框即为绕Z轴的旋转矩阵。
    在这里插入图片描述

  • 绕Y轴
    绕Y轴旋转同理,这里直接改变坐标轴的符号表示,注意坐标顺序要符合右手系,我这里用颜色区分了不同的轴。最终的矩阵形式要进一步改写成XYZ的顺序。红色方框即为绕Y轴的旋转矩阵。
    在这里插入图片描述

  • 绕X轴
    参照绕Y轴的推导,可以得到绕X轴的结果。红色方框即为绕X轴的旋转矩阵。

在这里插入图片描述
在这里插入图片描述

1.3 助记

对于单位矩阵,绕哪个轴旋转,哪一列不用变,然后将二维旋转矩阵替换对应的4个位置,注意,绕Y的旋转矩阵看起来与另外两个不同,它的-sinβ是在左下!

在这里插入图片描述

2、 坐标系旋转

在这里插入图片描述

2.1平面二维旋转

如下图,xy坐标系中,有一向量OP,其坐标可表示为(x,y),该向量与X轴夹角为α。然后,坐标系绕原点逆时旋转了β角度,形成新的坐标系x’y’,此时OP在新的坐标系中的坐标表示为(x,y),根据几何关系,可以得到如下推导,最终得到绿色虚框的旋转矩阵。

在这里插入图片描述

2.2 三维旋转
  • 绕X轴
    在这里插入图片描述

  • 绕Y轴
    在这里插入图片描述

  • 绕Z轴
    在这里插入图片描述

#include <pangolin/pangolin.h>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <iostream>
using namespace std;
const double PI=3.1415926;
void initSystem()
{
    glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
    pangolin::glDrawLine(0, 0, 0, 1, 0, 0);
    // pangolin::GlFont *text_font = new pangolin::GlFont("/usr/share/fonts/truetype/lyx/cmmi10.ttf", 20.0); 
    // text_font->Text(">").Draw(1.0,0.0,0.0);  //设置文字;
    glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
    pangolin::glDrawLine(0, 0, 0, 0, 1, 0);

    glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
    pangolin::glDrawLine(0, 0, 0, 0, 0, 1);
}

void tranform(GLfloat x1,GLfloat y1,GLfloat z1,GLfloat x2,GLfloat y2,GLfloat z2,GLfloat Tx,GLfloat Ty,GLfloat Tz)
{
    Eigen::Matrix<GLfloat,4,4> tranMatrix;
    tranMatrix<<1,0,0,Tx,0,1,0,Ty,0,0,1,Tz,0,0,0,1;

    glColor4f(0.0f,1.0f,1.0f,1.0f);
    pangolin::glDrawLine(x1,y1,z1,x2,y2,z2);

    Eigen::Matrix<GLfloat,4,1> point1,point2,point1_,point2_;
    point1<<x1,y1,z1,1;
    point2<<x2,y2,z2,1;

    point1_=tranMatrix*point1;
    point2_=tranMatrix*point2;

    glColor4f(1.0f,1.0f,0.0f,1.0f);
    pangolin::glDrawLine(point1_[0],point1_[1],point1_[2],point2_[0],point2_[1],point2_[1]);

}
void scale(GLfloat x1,GLfloat y1,GLfloat z1,GLfloat x2,GLfloat y2,GLfloat z2,GLfloat Sx,GLfloat Sy,GLfloat Sz)
{
    Eigen::Matrix<GLfloat,4,4> tranMatrix;
    tranMatrix<<Sx,0,0,0,0,Sy,0,0,0,0,Sz,0,0,0,0,1;

    glColor4f(0.0f,1.0f,1.0f,1.0f);
    pangolin::glDrawLine(x1,y1,z1,x2,y2,z2);

    Eigen::Matrix<GLfloat,4,1> point1,point2,point1_,point2_;
    point1<<x1,y1,z1,1;
    point2<<x2,y2,z2,1;
    point1_=tranMatrix*point1;
    point2_=tranMatrix*point2;

    glColor4f(1.0f,1.0f,0.0f,1.0f);
    pangolin::glDrawLine(point1_[0],point1_[1],point1_[2],point2_[0],point2_[1],point2_[1]);
}

void rotationZ(GLfloat x1,GLfloat y1,GLfloat z1,GLfloat x2,GLfloat y2,GLfloat z2,GLfloat Beta)
{
    Eigen::Matrix<GLfloat,3,3> tranMatrix;
    tranMatrix<<cos(Beta),-sin(Beta),0,sin(Beta),cos(Beta),0,0,0,1;

    glColor4f(0.0f,1.0f,1.0f,1.0f);
    pangolin::glDrawLine(x1,y1,z1,x2,y2,z2);

    Eigen::Matrix<GLfloat,3,1> point1,point2,point1_,point2_;
    point1<<x1,y1,z1;
    point2<<x2,y2,z2;
 
    point1_=tranMatrix*point1;
    point2_=tranMatrix*point2;

    glColor4f(1.0f,1.0f,0.0f,1.0f);
    pangolin::glDrawLine(point1_[0],point1_[1],point1_[2],point2_[0],point2_[1],point2_[1]);
    
}


int main(int /*argc*/, char ** /*argv*/)
{
    pangolin::CreateWindowAndBind("Main", 640, 480);
    glEnable(GL_DEPTH_TEST);

    // Define Projection and initial ModelView matrix
    pangolin::OpenGlRenderState s_cam(
        pangolin::ProjectionMatrix(640, 480, 420, 420, 320, 240, 0.2, 100),
        pangolin::ModelViewLookAt(-2, 2, -2, 0, 0, 0, pangolin::AxisY));

    // Create Interactive View in window
    pangolin::Handler3D handler(s_cam);
    pangolin::View &d_cam = pangolin::CreateDisplay()
                                .SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f / 480.0f)
                                .SetHandler(&handler);

    while (!pangolin::ShouldQuit())
    {
        // Clear screen and activate view to render into
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        d_cam.Activate(s_cam);
        //pangolin::glDrawAxis(1);  //调用GL坐标轴;
        // Render OpenGL Cube
        initSystem();
        //tranform(0,0,0,1,1,1,0.5,0,0);
        //scale(0, 0, 0, 1, 1, 1,2,2,2);
        rotationZ(0, 0, 0, 1, 1, 1,PI/6);
        //Swap frames and Process Events
        pangolin::FinishFrame();
    }

    return 0;
}

知乎:码农爱学习
博客园:翰墨小生

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值