天天看點

解析小覓中通過雙目相機生成深度圖的代碼

最近在使用雙目錄影機生成深度圖,研讀一下自帶的代碼,做一個記錄。

第一部分:

第一部分是定義了一個命名空間,其中包含許多個類。

第一個類:

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;
}

           

繼續閱讀