开发环境:Ubuntu 18.04 LTS + ROS Melodic + ViSP 3.3.1
文章内容主要参考ViSP官方教学文档:https://visp-doc.inria.fr/doxygen/visp-daily/tutorial_mainpage.html
本文主要介绍了如何使用ViSP实现运动的边缘跟踪,调用摄像头获取视频图像,并从第一帧图像中选择需要跟踪的物体边缘,确定好边缘后,图中的红色线条会跟随物体移动。本文主要参考了moving-edges中的tutorial-me-line-tracker.cpp例程。首先要获取这个例程文件并编译它
svn export https://github.com/lagadic/visp.git/trunk/tutorial/tracking/moving-edges
cd moving-edges/
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DVISP_DIR=$VISP_WS/visp-build
make
执行例程,查看效果
./tutorial-me-line-tracker
打开第一帧使用鼠标左键选取待追踪的物体边缘的两个顶点
选择好边缘后,移动物体可以观察到边缘线条会跟随物体移动
直接运行程序可能会出现“No cameras found”或者“VIDIOC_QBUF: EINVAL”的报错。首先要保证摄像头正确连接,这里以普通的USB摄像头或笔记本自带摄像头为例,重新插拔一下USB摄像头,如果使用虚拟机还需要保证摄像头连接到虚拟机而不是主机。然后确保第三方库文件安装好了,Ubuntu环境下使用USB摄像头主要依赖v4l2库,安装方式
sudo apt-get install libv4l-dev
最后查看是否调用了正确的设备号,程序中默认调用的设备号为video0,先查看一下自己的摄像头编号,Ubuntu环境下在dev文件夹下有以video开头的文件,如果文件名为video0,那说明设备编号为0,如果文件名不是video0,则需要修改程序,原代码为
#if defined(VISP_HAVE_DC1394)
vp1394TwoGrabber g(false);
#elif defined(VISP_HAVE_CMU1394)
vp1394CMUGrabber g;
#elif defined(VISP_HAVE_V4L2)
vpV4l2Grabber g;
#elif defined(VISP_HAVE_OPENCV)
cv::VideoCapture g(0); // open the default camera
我的修改方式如下
//把DC1394这个库屏蔽了
//#if defined(VISP_HAVE_DC1394)
// vp1394TwoGrabber g(false);
#if defined(VISP_HAVE_CMU1394)
vp1394CMUGrabber g;
#elif defined(VISP_HAVE_V4L2)
vpV4l2Grabber g;
std::ostringstream device;
device << "/dev/video" << 0;//此处的0就表示设备编号,可根据自己的情况修改成其他数字
g.setDevice(device.str());
g.setScale(1);
#elif defined(VISP_HAVE_OPENCV)
cv::VideoCapture g(0); // 此处的0就表示设备编号,可根据自己的情况修改成其他数字
下面介绍一下代码实现过程
#include <visp3/core/vpConfig.h>
#ifdef VISP_HAVE_MODULE_SENSOR
#include <visp3/sensor/vp1394CMUGrabber.h>
#include <visp3/sensor/vp1394TwoGrabber.h>
#include <visp3/sensor/vpV4l2Grabber.h>
#endif
#include <visp3/gui/vpDisplayGDI.h>
#include <visp3/gui/vpDisplayOpenCV.h>
#include <visp3/gui/vpDisplayX.h>
#include <visp3/me/vpMeLine.h>
int main()
{
#if (defined(VISP_HAVE_DC1394) || defined(VISP_HAVE_CMU1394) || defined(VISP_HAVE_V4L2))
try {
vpImage<unsigned char> I;
#if defined(VISP_HAVE_DC1394)
vp1394TwoGrabber g(false);//unix环境下调用firewire相机
#elif defined(VISP_HAVE_CMU1394)
vp1394CMUGrabber g;//windows环境下调用firewire相机
#elif defined(VISP_HAVE_V4L2)
vpV4l2Grabber g;//unix环境下调usb相机
#elif defined(VISP_HAVE_OPENCV)
cv::VideoCapture g(0); // unix环境下调usb相机
//以上代码都是寻找正确的打开摄像头的库
if (!g.isOpened()) { // check if we succeeded
std::cout << "Failed to open the camera" << std::endl;
return -1;
}
cv::Mat frame;
g >> frame; // 从摄像头获取第一帧图像
vpImageConvert::convert(frame, I);
#endif
g.open(I);
g.acquire(I);
#if defined(VISP_HAVE_X11)
vpDisplayX d(I, 0, 0, "Camera view");
#elif defined(VISP_HAVE_GDI)
vpDisplayGDI d(I, 0, 0, "Camera view");
#elif defined(VISP_HAVE_OPENCV)
vpDisplayOpenCV d(I, 0, 0, "Camera view");
#else
std::cout << "No image viewer is available..." << std::endl;
#endif
vpDisplay::display(I);
vpDisplay::flush(I);
vpMe me;
//初始化待跟踪的物体边缘的参数
me.setRange(25);//相邻帧的跟踪范围是边缘法线方向上25个像素内
me.setThreshold(15000);//对于沿着法线的每个像素,我们将计算有向卷积。
//移动边缘算法选择的像素将是卷积大于15000的像素。
me.setSampleStep(10);//在轮廓上两个连续移动的边缘之间,我们保持10个像素的空间。
vpMeLine line;
line.setMe(&me);
line.setDisplay(vpMeSite::RANGE_RESULT);
line.initTracking(I);
while (1) {
#if defined(VISP_HAVE_DC1394) || defined(VISP_HAVE_V4L2) || defined(VISP_HAVE_CMU1394)
g.acquire(I);
#elif defined(VISP_HAVE_OPENCV)
g >> frame;
vpImageConvert::convert(frame, I);
#endif
vpDisplay::display(I);
line.track(I);
line.display(I, vpColor::red);
vpDisplay::flush(I);
}
} catch (const vpException &e) {
std::cout << "Catch an exception: " << e << std::endl;
}
#endif
}
如果大家对于深度学习与计算机视觉领域感兴趣,希望获得更多的知识分享与最新的论文解读,欢迎关注我的个人公众号“深视”。