解析小觅中通过双目相机生成深度图的代码

最近在使用双目摄像机生成深度图,研读一下自带的代码,做一个记录。

第一部分:
第一部分是定义了一个命名空间,其中包含许多个类。
第一个类:
1.代码
GrabCallbacks类主要用于抓取图片、计算程序运行所花时间(OnPost函数)、获取fps值(GetFPS函数)和返回处理图片数(GetCount函数)。

class GrabCallbacks {                          
/*抓取返回值*/  
/*这是一个用于返回运行程序所用始终周期数量、花费时间和填充图片的刷新率的类*/
public:
    GrabCallbacks()                        //类的默认函数
        : tick_beg_(0),
          fps_(0),
          count_(0) {
        tick_beg_ = static_cast<double>(cv::getTickCount());     //记录程序开始时间
    }

    ~GrabCallbacks() = default;          //析构函数构造函数方法取默认值

    void OnPre() {}

    /*抓取图片函数、cv::Mat为记录图片的一种数据格式,这里定义左右两个摄像头图片*/
    void OnPost(cv::Mat &left, cv::Mat &right) {    
        std::lock_guard<std::mutex> lk(mtx_);        //多线程,运行函数
        double elapsed = (static_cast<double>(cv::getTickCount()) - tick_beg_) / cv::getTickFrequency();                                        
        //计算程序运行时间
        ++count_;                                   //计算处理图片数量
        fps_ = count_ / elapsed;                    //计算fps值
    }

    double GetFPS() {                               //返回fps值的函数
        std::lock_guard<std::mutex> lk(mtx_);
        return fps_;
    }

    std::uint64_t GetCount() {                      //返回处理图片数的函数
        std::lock_guard<std::mutex> 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<double>(getTickCount());     //记录起始时间对应count数

time1=((double)getTickCount()-time1)/getTickFrequency();//计算程序运行时间

cout<<"此方法运行时间为:"<<time1<<“秒”<<endl;             //输出运行时间

(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<std::mutex> lk(mtx_);                  //多线程
            tick_beg_ = static_cast<double>(cv::getTickCount());   //计数
        }
    }

    /*生成深度图函数,同时计算时钟周期数量和花费时间 */
    void OnPost(cv::Mat &depthmap) {
        {
            std::lock_guard<std::mutex> lk(mtx_);          //多线程
            tick_end_ = static_cast<double>(cv::getTickCount());   //记录程序结束时钟周期数量
            time_cost_ = (tick_end_ - tick_beg_) / cv::getTickFrequency();   //计算花费的时间
            time_total_ += time_cost_;                     //计算总时间
            ++count_;                                      //处理图片数量计数
        }
    }

    /*返回时间花费函数*/
    double GetTimeCost() {   
        std::lock_guard<std::mutex> lk(mtx_);
        return time_cost_;
    }

    /*计算处理每张图片的平均数量*/
    double GetTimeAverage() {
        std::lock_guard<std::mutex> lk(mtx_);
        return time_total_ / count_;
    }
    /*返回处理图片数*/
    std::uint64_t GetCount() {
        std::lock_guard<std::mutex> 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<std::ios> GetStreamFormat(int wide, int prec, char fillch = ' ') {
    auto fmt = std::unique_ptr<std::ios>{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<typename T>
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<typename T>
std::stringstream &Append(std::stringstream &ss, T text,
        const std::unique_ptr<std::ios> &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<int> pInt(new int(5));
    cout << *pInt;
}
    std::unique_ptr<int>p2=p1;// 编译会出错
    std::unique_ptr<int>p3=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<typename T>
    void Show(const cv::Mat &image,
            std::function<std::string(const T &elem)> 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<T>(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<cv::Mat&>(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<DepthMapRegion*>(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 <functional>
#include <iostream>
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<IMUData> 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<cv::Vec3f>(pointcloud, [](const cv::Vec3f &elem) {
                        return std::to_string(static_cast<int>(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;
}

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值