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

2023-05-16

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

第一部分:
第一部分是定义了一个命名空间,其中包含许多个类。
第一个类:
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;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 让电脑使用手机的摄像头做直播

    一 xff0c 软件准备 xff08 1 xff09 直播软件 xff1a bilibili直播姬 xff08 2 xff09 摄像头工具 xff1a DroidCam xff0c PC端和手机端 xff08 提取码 xff1a 43n1
  • pixhawk 开发日记--开发环境搭建

    我用的是Ubuntu20 04 一 源码下载 编译 烧写 1 下载源码 git clone https github com PX4 PX4 Autopilot git recursive 2 下载子仓库代码 git submodule u
  • APM中SBUS信号解析

    一 SBUS信号概述 SBUS信号以串口传输 xff0c 波特率为100000bps 每2ms一帧 xff0c 一帧25个字节 其中第0字节为起始帧 xff0c 固定为0x0F 第24字节为帧尾 第1 22字节为1 16比例通道数据字节 第
  • 使用simulink分析APM日志信息

    一 将bin转化成mat文件 使用mission planner将bin文件转化成mat文件 二 将工作区变量转化成timeserial对象 RCIN TimeSerial 61 timeseries RCIN 3 RCIN 2 1e6 三
  • Alexa The required native libraries are named “libvlc.dll”

    The required native libraries are named 34 libvlc dll 34 and 34 libvlccore dll 34 In the text below lt libvlc path gt re
  • APM 新版电机电调校准

    旧版的APM电机电调校准需要将油门开到最大后给飞控上电 xff0c 使飞控黄灯常亮后断电 xff0c 继续保持最大油门再给飞控上电 xff0c 听到嘀嘀嘀三声后表示收到遥控器最大油门信号 xff0c 之后再将油门打到最低 xff0c 听到滴
  • APM日志格式表

    APM的日志格式文件在libraries AP Logger LogStructure h文件中 xff0c 二进制数据类型对照表如下 xff1a 单位对照表如下 xff1a
  • APM电机输出逻辑之二

    APM 飞控代码用c 43 43 编写 xff0c 代码中用了很多多态的特性 电机输出相关的函数在void Copter motors output 函数中 由于在Copter类中包含了AP MotorsMulticopter类的实例化对象
  • win11安装ubuntu子系统与桌面 填坑记录

    win11安装ubuntu子系统 win11可以直接从应用市场安装ubuntu子系统 详细安装步骤见参考资料 这里列出一些博主遇到的问题 填坑之路 从应用市场获取ubuntu系统时会报0x80240438或者0x80072efd等错误 网络
  • 机械革命电脑安装Ubuntu18.04+win10正版双系统

    一 电脑设置 系统默认安装了Win10 由于win10系统UEFI的限制 xff0c 其他系统无法加载 因此要装双系统 xff0c 必须先把UEFI模式改成Legacy模式 xff0c 并将Security Boot 改为Disabled
  • python+opencv3生成一个自定义纯色图

    一 图像在计算机中存储为矩阵 矩阵上一个点表示一个像素 若矩阵由一系列0 xff5e 255的整数值组成 xff0c 则表现为灰度图 便于理解 xff0c 以下贴出代码 import cv2 import numpy as np img 6
  • opencv+CUDA9.1+vs2015环境搭建,编译opencv库,调用GPU加速运算

    1 准备工作 xff08 需要用的软件安装 xff09 1 1安装VS2015 CUDA是以VS为基础的 xff0c 因此要先安装VS 安装CUDA的时候会自动检测VS的版本 安装步骤较简单 xff0c 下载在线安装程序之后双击即可 xff
  • px4 mavlink消息自定义及其发送

    在px4源码中自定义消息 xff0c 并发送主要有以下三个大步骤 xff1a 一 自定义mavlink消息 1 在myMessage XML文件中自定义消息 xff08 注意消息ID不要与原有的重复 xff09 具体消息ID可参考这里 2
  • px4自定义uORB消息,并实现收发

    如题 xff0c 实现以上需求 主要有三个步骤 xff1a 一 添加一个新的topic 话题 以uORB test msg为例 1 在根目录中msg文件下添加uORB test msg 并在该文件中加入所需的数据 xff08 在新版本中必须
  • 两台WIN10之间通过局域网共享文件的一些坑

    两台WIN10之间共享文件有两个步骤 准备工作 确保两台电脑在同一个局域网内 xff0c 可以用网线相连 xff0c 也可以链接到同一个路由器 步骤一 xff1a 在A电脑设置共享文件 xff08 夹 xff09 xff08 1 xff09
  • 使用Cifar10训练DenseNet121

    DenseNet默认就是DenseNet BC 相对于resnet xff0c densenet权重参数更少 xff0c 鲁棒性更强 xff10 下载数据集 xff1a Cifar 10在同级文件data下 预训练权重 xff1a 39 d
  • 四旋翼PID调试步骤

    0 将飞机固定在架子上 xff0c 只保留横滚一个自由度 1 将姿态外环参数置为0 xff0c 从姿态内环开始调 由于姿态外环参数为0 xff0c 遥控器的作用量和角度的误差无效 xff0c 飞机无法维持平衡 2 先调姿态内环P xff0c
  • Ubuntu 18.04 解决ssh连接远程服务器的问题

    具体错误为 xff1a connection closed by xx xx xx xx port 22 首先确认远程主机的IP地址是正确的 xff0c 然后进行以下操作 可能原因 1 被防火墙挡了 2 端口没开放 3 ssh服务开了没 网
  • leetcode字符串的全排列题解

    解法描述 xff1a 例如 xff0c 如果集合是 a b c 那么这个集合中元素的所有排列是 a b c a c b b a c b c a c a b c b a xff0c 显然 xff0c 给定n个元素共有n 种不同的排列 xff0

随机推荐

  • 线性二次调节器(LQR)

    线性二次调节器 xff08 LQR xff09 线性二次调节器 xff08 LQR xff09 LQR原理LQR中的LLQR中的QLQR中的R 线性二次调节器 xff08 LQR xff09 线性二次调节器 xff08 Linear Qua
  • 二级倒立摆建模(一)

    目录 一 倒立摆系统简介 二 二级倒立摆模型构建 三 二级倒立摆的线性化模型 四 二级倒立摆的状态空间模型 一 倒立摆系统简介 倒立摆控制系统作为实验装置在控制理论领域的教学与科研工作中担任着不可或缺的角色 在对其稳定控制研究过程中 xff
  • 无人机目标检测

    导师给了个练手项目 xff0c 记录下第一个项目过程 xff1a 项目要求 xff1a 检测无人机降落时是否偏移 xff0c 识别降落点 想法是用opencv xff0c 使用传统的轮廓检测 首先把视频流转换成图片 xff1a 只提取最后两
  • 扩展卡尔曼滤波

    扩展卡尔曼滤波 xff08 Extended Kalman Filter xff0c EKF xff09 是标准卡尔曼滤波在非线性情形下的一种扩展形式 xff0c EKF算法是将非线性函数进行泰勒展开 xff0c 省略高阶项 xff0c 保
  • 相机标定(1)内\外参矩阵和畸变矩阵

    相机标定可以说是计算机视觉 机器视觉的基础 分为以下内容 xff1a 相机标定的目的和意义相机成像过程的简化与建模针孔相机模型的数学描述标定针孔相机模型的参数 相机标定的目的和意义 我们所处的世界是三维的 xff0c 而照片是二维的 xff
  • [Python|最优状态估计与滤波学习笔记] 最小均方滤波,卡尔曼滤波,神经网络滤波

    文章目录 前言 最优状态估计与滤波1 最小均方滤波 xff08 Least Mean Square LMS xff09 基本原理LMS设计步骤仿真代码 2 线性卡尔曼滤波 xff08 Linear Kalman Filter KF xff0
  • jetson机器上对lxde桌面设置中文输入法详细过程(图文展示)

    目录 1 需求2 解决2 1 中文输入法安装包2 2 系统设置 1 需求 手上的jetson nano机器使用的是lxde桌面 xff08 通常从台湾组装的jetson机器会有带有这种图标的界面 xff0c 就是lxde界面 xff0c 和
  • Django密码存储策略分析

    一 源码分析 Django 发布的 1 4 版本中包含了一些安全方面的重要提升 其中一个是使用 PBKDF2 密码加密算法代替了 SHA1 另外一个特性是你可以添加自己的密码加密方法 Django 会使用你提供的第一个密码加密方法 xff0
  • 配置k8s的dashboard

    在https github com kubernetes dashboard releases查看兼容的版本 xff08 使用的k8s是v1 15 1 xff0c dashboard版本是v2 0 0 beta4 xff09 如果安装了一次
  • AirSim学习(1)-介绍,安装,unity测试

    home AirSim是一款基于虚幻引擎的无人机 汽车等模拟器 我们现在也有一个实验性的Unity版本 它是开源的 xff0c 跨平台的 xff0c 支持使用流行的飞行控制器 如PX4和ArduPilot 进行软件在环模拟 xff0c 并支
  • docker数据迁移

    docker数据迁移 前言 开发服务器随着docker运行服务的变多 xff0c docker目录占用磁盘空间也在逐渐变大 xff0c 终于在最近磁盘彻底占满导致容器启动不起来并提示 34 no device space left 34 为
  • 树莓派3B+ VNC连接,cannot currently show the desktop 问题解决

    该问题是分辨率问题 xff0c 修改分辨率就可以 以下是修改过程 备份 span class token function cp span boot config txt config txt 打开文件 span class token f
  • 金蝶K/3 跟踪语句_业务单据

    跟踪语句 业务单据 BOM select from t TableDescription where Ftablename like 39 ICBOM 39 select from t FieldDescription where FTab
  • PX4的填坑经历

    2017年3月中旬正式决定入坑PX4 xff0c 以下是期间的经历 xff08 填坑中 xff09 1 2017年4月1日 按PX4 Development Guide xff0c 在ubuntu上建立PIXHAWK的PX4固件开发环境 x
  • Git 分支管理 和 查看提交历史 标签

    git branch 创建分支 gut branch d 删除分支 git checkout 切换分支命令 git merge 合并分支 用 git add 要告诉 Git 文件冲突已经解决 git log 查看历史 git log onl
  • Android 7.0 Launcher3 去掉应用抽屉

    年初做过一个项目 xff0c 有一个需求就是需要将桌面变为单层不需要二级菜单 最近几次有小伙伴有这个问我这个解决办法 现在我将分享给大家 先上效果图 xff1a 功能分解 去除Allapp键 xff0c 调整HotSeat布局将所有应用摆在
  • C++算法库网站 https://zh.cppreference.com/w/cpp/algorithm

    貌似要出国才能访问的C 43 43 标准库api网站 https zh cppreference com w cpp algorithm 有详细的函数介绍 如下图
  • 关于cv.cvtColor转换的问题及解决方法

    问题描述篇 xff1a 今天在调试困扰了我很久的一个问题 xff0c 在训练网络生成batch数据的时候读入原始图像 xff0c 输出的时候却老是出问题 我们都知道YUV和RGB之间的转换关系 xff1a R 61 Y 43 1 4075
  • Nuttx 驱动开发手册

    目录 Nuttx 代码获取编译 Nuttx 启动流程 Nuttx BootLoader 开发之源码分析 gpio 驱动分析 I2c驱动分析 PX4 框架分析 UORB 进程间通讯分析 PX4应用层驱动分析并实现例程 串口驱动GPS 驱动分析
  • 解析小觅中通过双目相机生成深度图的代码

    最近在使用双目摄像机生成深度图 xff0c 研读一下自带的代码 xff0c 做一个记录 第一部分 xff1a 第一部分是定义了一个命名空间 xff0c 其中包含许多个类 第一个类 xff1a 1 代码 GrabCallbacks类主要用于抓