OpenCV学习篇2:viz模块简单用法

来源:互联网 发布:防蓝光近视眼镜 知乎 编辑:程序博客网 时间:2024/06/07 02:30

viz模块主要用于3D可视化显示。
首先看个简单示例程序,创建一个窗口并显示坐标系:

    //创建可视化窗口    viz::Viz3d window1("window1");    //构造一个坐标系,并显示到窗口中    window1.showWidget("Coordinate", viz::WCoordinateSystem());    //开启永久循环暂留    window1.spin();

第一句,创建窗口。
很简单,类型为Viz3d类型,参数为窗口名称。
第二句,在窗口中显示部件。
viz模块中,窗口内显示的一切东西通通为部件,也就是Widget,这里调用showWidget()函数,将部件显示在窗口中。
来看一下showWidget()定义:

/** @brief Shows a widget in the window.@param id A unique id for the widget. @param widget The widget to be displayed in the window.@param pose Pose of the widget.*/void showWidget(const String &id, const Widget &widget, const Affine3d &pose = Affine3d::Identity());

看看三个参数:
&id:这个参数是为了给部件定义一个unique名称,用于后面定位到此部件,并不是想当然的认为是窗口中部件的名称(类似于坐标轴的名字一样,运行发现窗口中除了坐标轴,并没有名称)。
&widget:自然就是要显示的部件了。这是用viz::WCoordinateSystem()直接当场创建了一个坐标系部件,WCoordinateSystem类是继承自Widget3D类,所以本质类型也是部件,可以在窗口中显示。
后面还有一个带有默认值的pose参数,示例程序中没有传入此参数,采用的默认值,关于Affine3d后面再说。
第三句,永久循环暂留。
spin()函数开启一个event loop永远循环。直观的用处就是让画面停在那里,如果没有这一句的话,画面基本就是秒闪一下然后消失了。
关于spin()函数有两点要说:
第一就是spin()真的会让程序停在那里,不会运行下方的语句,除非按q或者e建,才会继续走。
第二就是这个函数的变种:void spinOnce(int time = 1, bool force_redraw = false); 表示event loop循环time时间,也就是在词句的停留时间,time的单位为毫秒,第二个force_redraw还不知道有啥用,测试来看,没有区别。后面有这个函数的示例程序。

OK,下面就是这个最简单窗口的运行结果:
这里写图片描述

好,再来加点东西:

    viz::Viz3d window("window");    window.showWidget("Coordinate", viz::WCoordinateSystem());    viz::WPlane plane;    window.showWidget("plane", plane);    window.spin();

WPlane即为平面类,也是继承自Widget3D类。这里创建一个平面部件(各种参数都不写了,用默认值),添加到窗口中,画面如下:
这里写图片描述

OK,多了个白色正方形平面。

好了,到这里如果说苍老师.jpeg已经满足不了你了,那么请继续往下看,苍老师.mkv更精彩。

    viz::Viz3d window("window");    window.showWidget("Coordinate", viz::WCoordinateSystem());    //创建平面    viz::WPlane plane;    //添加平面,并设置一个ID为plane    window.showWidget("plane", plane);    //创建一个1*3的rotation vector    Mat rvec = Mat::zeros(1, 3, CV_32F);    //动画的本质:调整部件位姿并循环显示,控制条件是窗口没被停止,也就是主动按下了q或者e键    while(!window.wasStopped())    {        rvec.at<float>(0, 0) = 0.f;        rvec.at<float>(0, 1) += CV_PI * 0.01f;        rvec.at<float>(0, 2) = 0.f;        Mat rmat;        //罗德里格斯公式,将罗德里格斯向量转换成旋转矩阵        Rodrigues(rvec, rmat);        //构造仿射变换类型的pose,这个类型暂且看做OpenCV中的位姿类型,两个参数,一个旋转,一个平移        Affine3f pose(rmat, Vec3f(0, 0, 0));        //这一句就是整个可视化窗口能够动起来的核心语句了,        //说白了就是利用循环来不断调整上面plane部件的位姿,达到动画的效果        //另外这里就利用到了平面的ID,来表征调整的是平面的位姿        window.setWidgetPose("plane", pose);        //控制单帧暂留时间,调整time参数的效果就是平面转的快慢,本质上就是每一个位姿的停留显示时间。        window.spinOnce(1, false);    }

主要的区别就是下方多了一个循环。
定义一个旋转向量rvec,然后调用Rodrigues()函数将其转换成旋转矩阵rmat,再用rmat构造pose,最后用setWidgetPose()函数和构造的pose,来达到调整部件位姿的效果,循环执行后的结果就是看起来平面转起来了。
这里说一下Affine3f类型的 pose参数,Affine3是一个3维仿射变换类,先摆上构造函数源码:

        Affine3();        //! Augmented affine matrix        Affine3(const Mat4& affine);        //! Rotation matrix        Affine3(const Mat3& R, const Vec3& t = Vec3::all(0));        //! Rodrigues vector        Affine3(const Vec3& rvec, const Vec3& t = Vec3::all(0));        //! Combines all contructors above. Supports 4x4, 4x3, 3x3, 1x3, 3x1 sizes of data matrix        explicit Affine3(const Mat& data, const Vec3& t = Vec3::all(0));        //! From 16th element array        explicit Affine3(const float_type* vals);

默认构造函数不用说了。
看其他构造函数很容易发现,其实它就是一个变换T,此处用于表征位姿。构造函数就多种多样了:直接4*4矩阵构造、旋转向量和平移向量构造、旋转矩阵和平移向量构造、开放式构造(也就是旋转向量部分随你用什么矩阵了)、16元素的数组构造。示例中用的是开放式构造。
构造好了位姿,利用循环和window.setWidgetPose(“plane”, pose);这句,不断调整部件的pose数据,最终画面就动起来了。
由于不能传动图,所以只能传个截图,白色平面会绕着绿色坐标轴右方向转动:
这里写图片描述