最近在使用双目摄像机生成深度图,研读一下自带的代码,做一个记录。
第一部分:
第一部分是定义了一个命名空间,其中包含许多个类。
第一个类:
1.代码
GrabCallbacks类主要用于抓取图片、计算程序运行所花时间(OnPost函数)、获取fps值(GetFPS函数)和返回处理图片数(GetCount函数)。
class GrabCallbacks {
/*抓取返回值*/
/*这是一个用于返回运行程序所用始终周期数量、花费时间和填充图片的刷新率的类*/
public:
GrabCallbacks() //类的默认函数
: tick_beg_(0),
fps_(0),
count_(0) {
tick_beg_ = static_cast(cv::getTickCount()); //记录程序开始时间
}
~GrabCallbacks() = default; //析构函数构造函数方法取默认值
void OnPre() {}
/*抓取图片函数、cv::Mat为记录图片的一种数据格式,这里定义左右两个摄像头图片*/
void OnPost(cv::Mat &left, cv::Mat &right) {
std::lock_guard lk(mtx_); //多线程,运行函数
double elapsed = (static_cast(cv::getTickCount()) - tick_beg_) / cv::getTickFrequency();
//计算程序运行时间
++count_; //计算处理图片数量
fps_ = count_ / elapsed; //计算fps值
}
double GetFPS() { //返回fps值的函数
std::lock_guard lk(mtx_);
return fps_;
}
std::uint64_t GetCount() { //返回处理图片数的函数
std::lock_guard lk(mtx_);
return count_;
}
private:
double tick_beg_; //时钟周期数
double fps_; //fps数
std::uint64_t count_;
std::mutex mtx_;
};
2.知识点:
(1)namespace :命名空间,实际上就是一个由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。
如:
namespace ns1 //指定命名中间nsl
{ int a;
double b; }
namespace 是定义命名空间所必须写的关键字,nsl 是用户自己指定的命名空间的名字,在花括号内是声明块,在其中声明的实体称为命名空间成员(namespace member)。现在命名空间成员包括变量a和b,注意a和b仍然是全局变量,仅仅是把它们隐藏在指定的命名空间中而已。如果在程序中要使用变量a和b,必须加上命名空间名和作用域分辨符“::”,如nsl::a,nsl::b。这种用法称为命名空间限定(qualified),这些名字(如nsl::a)称为被限定名 (qualified name)
使用命名空间的主要目的是防止名字重复,比如一个变量a,ns1::a与ns2::a是截然不同的,使用命名空间能够很好的解决这个问题。
(2)opencv中有两个简单的计时函数:
getTickCount()
getTickCount()函数返回CPU自某个事件(如开机)以来走过的时钟周期数。
getTickFrequency()函数返回CPU一秒走过的时钟周期数。这样,我们可以轻松的以秒为单位对某运算计时。
这两个函数组合起来使用的示例如下:
double time1 = static_cast(getTickCount()); //记录起始时间对应count数
time1=((double)getTickCount()-time1)/getTickFrequency();//计算程序运行时间
cout<<"此方法运行时间为:"<
(3)std::lock_guardstd::mutex lk(mtx_)
std::unique_lock 与std::lock_guard都能实现自动加锁与解锁功能,但是std::unique_lock要比std::lock_guard更灵活,但是更灵活的代价是占用空间相对更大一点且相对更慢一点。
(4)cv::mat
Mat类型是opencv2.0后的类型,使用此类型无需进行内存管理,即无需手动分配内存,在不需要时自动释放内存,但因目前的许多嵌入式系统仅支持c语言,故而除了在某些嵌入式系统中使用cvMat或IplImage, 基本使用Mat类型。
Mat包含2个数据部分:the matrix header–包含matrix大小,存储方法,matrix存储地址等,matrix header内存大小固定;
第二个类
1.代码
class DepthMapCallbacks { //获取深度图的类
public:
DepthMapCallbacks() //默认函数
: tick_beg_(0), //开始时钟周期数量
tick_end_(0), //结束时钟周期数量
time_cost_(0), //生成深度图花费时间
time_total_(0), //一共花费时间
count_(0) {
} //处理图片数量
~DepthMapCallbacks() = default; //析构函数构造函数方法取默认值
/* 预运行函数,输入为左右两个摄像头图片的mat矩阵,同时开始计数 */
void OnPre(cv::Mat &left, cv::Mat &right) {
{
std::lock_guard lk(mtx_); //多线程
tick_beg_ = static_cast(cv::getTickCount()); //计数
}
}
/*生成深度图函数,同时计算时钟周期数量和花费时间 */
void OnPost(cv::Mat &depthmap) {
{
std::lock_guard lk(mtx_); //多线程
tick_end_ = static_cast(cv::getTickCount()); //记录程序结束时钟周期数量
time_cost_ = (tick_end_ - tick_beg_) / cv::getTickFrequency(); //计算花费的时间
time_total_ += time_cost_; //计算总时间
++count_; //处理图片数量计数
}
}
/*返回时间花费函数*/
double GetTimeCost() {
std::lock_guard lk(mtx_);
return time_cost_;
}
/*计算处理每张图片的平均数量*/
double GetTimeAverage() {
std::lock_guard lk(mtx_);
return time_total_ / count_;
}
/*返回处理图片数*/
std::uint64_t GetCount() {
std::lock_guard lk(mtx_);
return count_;
}
private:
double tick_beg_;
double tick_end_;
double time_cost_; // in seconds
double time_total_; // in seconds
std::uint64_t count_;
std::mutex mtx_;
};
第三部分:
1.代码
std::unique_ptr GetStreamFormat(int wide, int prec, char fillch = ' ') {
auto fmt = std::unique_ptr{new std::ios{nullptr}};
/*定义指针fmt四个属性*/
fmt->setf(std::ios::fixed);
fmt->width(std::move(wide));
fmt->precision(std::move(prec));
fmt->fill(std::move(fillch));
return fmt;
}
template
std::stringstream &Append(std::stringstream &ss, T text,
std::ios *fmt = nullptr, bool reset = false) {
if (reset) {
ss.str("");
ss.clear();
}
if (fmt) {
ss.copyfmt(*fmt);
}
ss << std::move(text);
return ss;
}
template
std::stringstream &Append(std::stringstream &ss, T text,
const std::unique_ptr &fmt, bool reset = false) {
return Append(ss, std::move(text), fmt.get(), reset);
}
2.知识点:
(1)unique_ptr
unique_ptr是C++智能指针,unique_ptr 独占所指向的对象,,同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现),定义于 memory (非memory.h)中,命名空间为 std.
例子:
int main()
{
// 创建一个unique_ptr实例
unique_ptr pInt(new int(5));
cout << *pInt;
}
std::unique_ptrp2=p1;// 编译会出错
std::unique_ptrp3=std::move(p1);// 转移所有权,那块内存归p3所有, p1成为无效的针.
p3.reset();//释放内存.
p1.reset();//无效
其中unique_ptr为指针命令,为int型变量,pint为指针变量,new int(5)为所占大小。
(2)std::move
std::move语句可以将左值变为右值而避免拷贝构造。
在使用std::move之后,B对A进行了浅拷贝,仅仅只赋值了key=1,2,3的指针。那么这里引发了一个新的问题:在move(mapA)之后,我们并不希望再进一步对A中key=1,2,3的对象做操作,否则会引起“不可预期”的结果,比如释放了同一个地址。所以我们需要约束对于move后的对象,应当马上“弃用”。
所以std::move即在赋值后启用原来的变量。
第四部分
1.代码
/*深度图上的类*/
class DepthMapRegion {
public: //公共属性
explicit DepthMapRegion(std::uint32_t n)
: n_(std::move(n)),
show_(false), //是否显示
selected_(false), //是否选中
point_(0, 0) {
} //记录坐标点
~DepthMapRegion() = default;
/* 定义鼠标移动检测函数 ,输入为鼠标动作种类,横纵坐标和标志位*/
void OnMouse(const int &event, const int &x, const int &y, const int &flags) {
/* 鼠标未移动且未点击时不响应,显示画面 */
if (event != CV_EVENT_MOUSEMOVE && event != CV_EVENT_LBUTTONDOWN) {
return;
}
show_ = true;
/* 鼠标移动时实时传送位置坐标,如果按钮被按下,则选中项置为true。*/
if (event == CV_EVENT_MOUSEMOVE) {
if (!selected_) {
point_.x = x;
point_.y = y;
}
} else if (event == CV_EVENT_LBUTTONDOWN) {
selected_ = true;
point_.x = x;
point_.y = y;
}
}
template
void Show(const cv::Mat &image,
std::function elem2string, //封装一个变量
int elem_space = 40) {
// depthmap: CV_8UC1
// xyz: Output 3-channel floating-point image of the same size as disparity
if (!show_) return;
int space = std::move(elem_space);
int n = 2 * n_ + 1;
cv::Mat im(space*n, space*n, CV_8UC3, cv::Scalar(255,255,255));
// 设置图像长宽和颜色
int x, y;
std::string str;
int baseline = 0;
/* 先确定区域范围,以x,y为中心,x范围: point_.x-n_ -- point_.x+n_;y范围: point_.y-n_ -- point_.y+n_*/
for (int i = -n_; i <= n; ++i) {
x = point_.x + i;
if (x < 0 || x >= image.cols) continue;
for (int j = -n_; j <= n; ++j) {
y = point_.y + j;
if (y < 0 || y >= image.rows) continue;
/* 将深度数值变为字符型 */
str = elem2string(image.at(y, x));
/* 先设置颜色,全黑,中心位置全蓝*/
cv::Scalar color(0,0,0);
if (i == 0 && j == 0) color = cv::Scalar(0,0,255);
/* 在图片中写字*/
cv::Size sz = cv::getTextSize(str,
cv::FONT_HERSHEY_PLAIN, 1, 1, &baseline);
/* 返回字符串的宽度和高度(这里的字符串是对应的深度数值)*/
cv::putText(im, str,
cv::Point((i+n_)*space + (space-sz.width)/2,
(j+n_)*space + (space+sz.height)/2),
cv::FONT_HERSHEY_PLAIN, 1, color, 1);
/* 在图上显示深度数值 */
}
}
cv::imshow("region", im);
}
/*绘制蓝色的框,即鼠标点到某一处后显示这附近的框*/
void DrawPoint(const cv::Mat &im) {
if (!show_) return;
std::uint32_t n = (n_ > 1) ? n_ : 1;
#ifdef USE_OPENCV2
cv::rectangle(const_cast(im),
#else
cv::rectangle(im,
#endif
cv::Point(point_.x-n, point_.y-n),
cv::Point(point_.x+n, point_.y+n),
cv::Scalar(0,0,255), 1);
}
private:
std::uint32_t n_;
bool show_;
bool selected_;
cv::Point point_;
};
void OnDepthMapMouseCallback(int event, int x, int y, int flags, void *userdata) {
DepthMapRegion *region = reinterpret_cast(userdata);
region->OnMouse(event, x, y, flags);
}
2.知识点
(1)std::function
类模版std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。std::function对象是对C++中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。
通常std::function是一个函数对象类,它包装其它任意的函数对象,被包装的函数对象具有类型为T1, …,TN的N个参数,并且返回一个可转换到R类型的值。std::function使用 模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为std::function。
最简单的理解就是:
通过std::function对C++中各种可调用实体(普通函数、Lambda表达式、函数指针、以及其它函数对象等)的封装,形成一个新的可调用的std::function对象;让我们不再纠结那么多的可调用实体。一切变的简单粗暴。
#include
#include
using namespace std;
std::function< int(int)> Functional;
// 普通函数
int TestFunc(int a)
{
return a;
}
// Lambda表达式
auto lambda = [](int a)->int{ return a; };
// 仿函数(functor)
class Functor
{
public:
int operator()(int a)
{
return a;
}
};
// 1.类成员函数
// 2.类静态函数
class TestClass
{
public:
int ClassMember(int a) { return a; }
static int StaticMember(int a) { return a; }
};
int main()
{
// 普通函数
Functional = TestFunc;
int result = Functional(10);
cout << "普通函数:"<< result << endl;
// Lambda表达式
Functional = lambda;
result = Functional(20);
cout << "Lambda表达式:"<< result << endl;
// 仿函数
Functor testFunctor;
Functional = testFunctor;
result = Functional(30);
cout << "仿函数:"<< result << endl;
// 类成员函数
TestClass testObj;
Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);
result = Functional(40);
cout << "类成员函数:"<< result << endl;
// 类静态函数
Functional = TestClass::StaticMember;
result = Functional(50);
cout << "类静态函数:"<< result << endl;
return 0;
}
也就是说,使用std::function构造一个函数,可以将它封装成任何形式的函数或实例,这是多态的一种典型应用。
(2)cv::Scalar(0,0,255)
括号中三个数值代表蓝绿红。
(3)cv::Mat image1(240,320,CV_8U,100);创建一个240行*320列的新图像,CV_8U用来表示每个像素对应1字节,用字母U表示无符号;S表示有符号。对于彩色图像用三通道(CV_8UC3),也可以定义16位和32位的整数(CV_16SC3)。cv::Scalar(0,0,255),括号中三个数值代表蓝绿红。
(4)cv::Size
在实践中,size类与对应的Point点类(一致类型的)类似,可以互相转换。主要的区别在size类中的两个数据成员叫做和,而在Point点类中的连个数据成员叫做和。size类的三个别名为:cv::Size, cv::Size2i, 和 cv::Size2f。前面两个是相同的用来表示整型size,而最后一个是32位浮点型size。
(5)GetTextSize
获得字符串的宽度和高度。
void cvGetTextSize( const char* text_string, const CvFont* font, CvSize* text_size, int* baseline );
text_string:文字内容
font : 字体结构体
text_string : 输入字符串。
text_size: 合成字符串的字符的大小。文本的高度不包括基线以下的部分。
baseline:相对于文字最底部点的基线的Y坐标。
(6)在图像中显示文本字符串。函数原型如下:
void PutText( CvArr* img, const char* text, CvPoint org, const CvFont* font, CvScalar color );
img:输入图像
text:要显示的字符串
org:第一个字符左下角的坐标。
font:字体结构体。
color:文本的字体颜色。
(7)cv::rectangle
C++: void rectangle(Mat& img, Point pt1,Point pt2,const Scalar& color, int thickness=1, int lineType=8, int shift=0)
img: 图像
pt1: 矩形的一个顶点
pt2 :矩形对角线上的另一个顶点
color: 线条颜色 (RGB) 或亮度(灰度图像 )
thickness:组成矩形的线条的粗细程度。
(9)宏定义:
包括:#define、#undef、#ifdef、#ifndef、#if、#elif、#else、#endif、defined
#define 定义一个预处理宏
#undef 取消宏的定义
#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined 与#if, #elif配合使用,判断某个宏是否被定义
第五部分:
1.代码:
/* 主函数*/
int main(int argc, char const *argv[]) {
std::string name; //定义名字
/* 先确定相机正常运行 */
if (argc >= 2) { //如果argc = 2,说明正常运行,给名字赋值;
name = argv[1];
} else { //如果argc < 2,则相机名错误
bool found = false;
name = FindDeviceName(&found);
}
cout << "Open Camera: " << name << endl; //显示文字说明结果
CalibrationParameters *calib_params = nullptr; //定义一个CalibrationParameters型指针变量
if (argc >= 3) { //如果argc > 2,则重新加载相机参数calib_params
stringstream ss;
ss << argv[2];
calib_params = new CalibrationParameters;
calib_params->Load(ss.str());
}
InitParameters init_params(name, calib_params);
/* 初始化结束 */
/* 使用相机初始化参数打开相机 */
Camera cam;
//cam.SetMode(Mode::MODE_CPU);
// Could test plugin here.
cam.Open(init_params);
if (calib_params) //如果calib_params被写入,则argc > 2,删除参数
delete calib_params;
if (!cam.IsOpened()) { //如果相机没打开,报错
std::cerr << "Error: Open camera failed" << std::endl;
return 1;
}
cout << "\033[1;32mPress ESC/Q on Windows to terminate\033[0m\n";
cam.ActivateAsyncGrabFeature(true); //抓取双目图片特征
cam.ActivateDepthMapFeature(); //生成深度图
cam.ActivatePointCloudFeature(); //生成点云图
using namespace std::placeholders; //使用命名空间placeholders
GrabCallbacks grab_callbacks; //创建GrabCallbacks对象
cam.SetGrabProcessCallbacks(nullptr,
std::bind(&GrabCallbacks::OnPost, &grab_callbacks, _1, _2));
DepthMapCallbacks depthmap_callbacks; //创建DepthMapCallbacks对象
cam.SetDepthMapProcessCallbacks(
std::bind(&DepthMapCallbacks::OnPre, &depthmap_callbacks, _1, _2),
std::bind(&DepthMapCallbacks::OnPost, &depthmap_callbacks, _1));
// Scale the grabbed images.
// cam.SetScale(0.5);
// Or, scale after process grab
// cam.SetGrabProcessCallbacks(nullptr, [](cv::Mat &left, cv::Mat &right) {
// cv::resize(left, left, cv::Size(), 0.5, 0.5, cv::INTER_LINEAR);
// cv::resize(right, right, cv::Size(), 0.5, 0.5, cv::INTER_LINEAR);
// });
// Or, scale after process rectification
// cam.SetRectifyProcessCallbacks(nullptr, [](cv::Mat &left, cv::Mat &right) {
// cv::resize(left, left, cv::Size(), 0.5, 0.5, cv::INTER_LINEAR);
// cv::resize(right, right, cv::Size(), 0.5, 0.5, cv::INTER_LINEAR);
// });
auto fmt_fps = GetStreamFormat(6, 2);
auto fmt_imu = GetStreamFormat(8, 4);
auto fmt_time = GetStreamFormat(7, 2);
std::stringstream ss; //定义字符串流
double t, fps = 0;
ErrorCode code; //定义报错代码对象
cv::Mat img_left, img_right; //定义左右图片、深度图、点云图和深度彩色图
cv::Mat depthmap, pointcloud;
cv::Mat depthmap_color;
DepthMapRegion depthmap_region(2); //设置数字显示深度图
vector imudatas;
std::uint32_t timestamp;
std::uint64_t count = 0;
std::uint64_t depthmap_count = 0;
cv::namedWindow("left"); //定义图像框(左、右、深度)
cv::namedWindow("right");
cv::namedWindow("depthmap");
//cv::namedWindow("region");
for (;;) { //死循环显示图像
t = (double)cv::getTickCount(); //开始计数
code = cam.Grab(); //接受图片报错情况
if (code != ErrorCode::SUCCESS) continue; //如果接受图片未成功,跳过余下步骤重新开始循环
// 如果左右图片全部成功接受,处理图片数加一
if (cam.RetrieveImage(img_left, View::VIEW_LEFT_UNRECTIFIED) == ErrorCode::SUCCESS &&
cam.RetrieveImage(img_right, View::VIEW_RIGHT_UNRECTIFIED) == ErrorCode::SUCCESS) {
++count;
cam.RetrieveIMUData(imudatas, timestamp);
// top left: width x height, count
// ss 显示宽度*高度,图片数,显示在两张图片的info左上角
Append(ss, img_left.cols, nullptr, true)
<< "x" << img_left.rows << ", " << count;
DrawInfo(img_left, ss.str(), Gravity::TOP_LEFT);
DrawInfo(img_right, ss.str(), Gravity::TOP_LEFT);
// top right: fps
//再两张图片右上角显示fps
Append(ss, "fps:", nullptr, true);
Append(ss, fps, fmt_fps);
cv::Rect rect = DrawInfo(img_left, ss.str(), Gravity::TOP_RIGHT);
DrawInfo(img_right, ss.str(), Gravity::TOP_RIGHT);
// grab_fps
double grab_fps = grab_callbacks.GetFPS();
Append(ss, "grab fps:", nullptr, true);
Append(ss, grab_fps, fmt_fps);
DrawInfo(img_left, ss.str(), Gravity::TOP_RIGHT, 5, 0, 5 + rect.height);
DrawInfo(img_right, ss.str(), Gravity::TOP_RIGHT, 5, 0, 5 + rect.height);
if (!imudatas.empty()) {
size_t size = imudatas.size();
IMUData &imudata = imudatas[size-1];
//如果imudata数据不为空,则在左图片的左下角写入accel数据,在右图片的左下角写入gyro数据
// bottom left: imudata
Append(ss, "accel(x,y,z): ", nullptr, true);
Append(ss, imudata.accel_x, fmt_imu) << ",";
Append(ss, imudata.accel_y, fmt_imu) << ",";
Append(ss, imudata.accel_z, fmt_imu);
DrawInfo(img_left, ss.str(), Gravity::BOTTOM_LEFT);
Append(ss, "gyro(x,y,z): ", nullptr, true);
Append(ss, imudata.gyro_x, fmt_imu) << ",";
Append(ss, imudata.gyro_y, fmt_imu) << ",";
Append(ss, imudata.gyro_z, fmt_imu);
DrawInfo(img_right, ss.str(), Gravity::BOTTOM_LEFT);
/*
cout << "IMU count: " << size << endl;
for (size_t i = 0; i < size; ++i) {
auto &imudata = imudatas[i];
cout << " IMU[" << i << "] time: " << (imudata.time / 10) << " ms"
<< ", accel(" << imudata.accel_x << "," << imudata.accel_y << "," << imudata.accel_z << ")"
<< ", gyro(" << imudata.gyro_x << "," << imudata.gyro_y << "," << imudata.gyro_z << ")"
<< endl;
}
*/
}
cv::imshow("left", img_left);
cv::imshow("right", img_right);
};
auto depthmap_count_ = depthmap_callbacks.GetCount(); //深度图计数
if (depthmap_count != depthmap_count_) {
depthmap_count = depthmap_count_;
//cout << "depthmap_count: " << depthmap_count << endl;
// only retrieve when depthmap changed
code = cam.RetrieveImage(depthmap, View::VIEW_DEPTH_MAP);
//接收深度图
if (code == ErrorCode::SUCCESS) {
#ifdef USE_OPENCV2
// `applyColorMap` provided by contrib libs in opencv 2.x
depthmap_color = depthmap; // do nothing
//深度图赋值
#else
// ColormapTypes
// http://docs.opencv.org/master/d3/d50/group__imgproc__colormap.html#ga9a805d8262bcbe273f16be9ea2055a65
cv::applyColorMap(depthmap, depthmap_color, cv::COLORMAP_JET);
#endif
// top left: count
//左上角写入宽*高,处理图片数量,右上角写入所花时间和平均所用时间
Append(ss, img_left.cols, nullptr, true)
<< "x" << img_left.rows << ", " << depthmap_count;
DrawInfo(depthmap_color, ss.str(), Gravity::TOP_LEFT);
// top right: cost, avg
Append(ss, "cost:", nullptr, true);
Append(ss, depthmap_callbacks.GetTimeCost() * 1000, fmt_time) << "ms";
cv::Rect rect = DrawInfo(depthmap_color, ss.str(), Gravity::TOP_RIGHT);
Append(ss, "average:", nullptr, true);
Append(ss, depthmap_callbacks.GetTimeAverage() * 1000, fmt_time) << "ms";
DrawInfo(depthmap_color, ss.str(), Gravity::TOP_RIGHT, 5, 0, 5 + rect.height);
// bottom left: dropped
// 右下角写入丢失图片数量
Append(ss, "dropped: ", nullptr, true)
<< cam.GetDroppedCount(Process::PROC_GRAB) << ","
<< cam.GetDroppedCount(Process::PROC_RECTIFY) << ","
<< cam.GetDroppedCount(Process::PROC_DEPTH_MAP) << ","
<< cam.GetDroppedCount(Process::PROC_POINT_CLOUD);
DrawInfo(depthmap_color, ss.str(), Gravity::BOTTOM_RIGHT);
//鼠标点击画框,并输出深度图
cv::setMouseCallback("depthmap", OnDepthMapMouseCallback, &depthmap_region);
depthmap_region.DrawPoint(depthmap_color);
cv::imshow("depthmap", depthmap_color);
//输出数字点云图
code = cam.RetrieveImage(pointcloud, View::VIEW_POINT_CLOUD);
if (code == ErrorCode::SUCCESS) {
depthmap_region.Show(pointcloud, [](const cv::Vec3f &elem) {
return std::to_string(static_cast(elem[2]));
}, 80);
}
}
}
// 等待键盘输入,退出
char key = (char) cv::waitKey(1);
if (key == 27 || key == 'q' || key == 'Q') { // ESC/Q
break;
}
// 更新图片处理数量和处理频率
t = (double)cv::getTickCount() - t;
fps = cv::getTickFrequency() / t;
}
//关闭摄像头
cam.Close();
cv::destroyAllWindows();
return 0;
}