最近在使用雙目錄影機生成深度圖,研讀一下自帶的代碼,做一個記錄。
第一部分:
第一部分是定義了一個命名空間,其中包含許多個類。
第一個類:
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;
}