C++中的類加多執行緒程式碼修煉之二
背景:在上一篇文章中 寫到了我第一次使用C++使用多個類多個執行緒進行程式設計,由於是第一接手“這麼大一個工程”,所以還是要有個參照物的,由於我呢之前好幾年一直在看的一個C++程式碼工程就是ORB-SLAM了,這個工程使用C++語言,工程內有三個執行緒,多個類,程式碼風格很好,通俗易懂,有很多值得我借鑑和學習的地方,所以我就按照ORB-SLAM工程的思路來組織我的程式碼。
前情回顧:
我首先建立了四個類:
(1). Capturing類主要專注於相機相關的操作,比如相機的初始化,影象獲取,開始,暫停等。
(2). Tracking類主要專注於目標跟蹤相關操作。
(3). System類相當於是一個管理者,就像ORB-SLAM中的System類,完成一些系統初始化的任務,其中我主要讓他完成,兩個執行緒的建立以及執行緒之間指標變數的設定(這一點很重要,我剛開始沒做相關設定,導致兩個執行緒之間資料傳輸時出了問題,下面會講到,嘻嘻)
(4). Frame類,這個類是我後來新增的,目前暫時封裝了“影象本身”以及影象的“時間戳”屬性。
兩個執行緒:我把主執行緒先保留了出來,又單獨建立了兩個執行緒
(1). 將Capturing中Run函式(用來實現連續從相機去影象的工作)作為CaptureThread執行緒的執行緒入口。
(2). 將Tracking中Run函式(用來實現目標跟蹤的工作)作為TrackThread執行緒的執行緒入口。
我在上一篇文章中https://blog.csdn.net/weixin_38636815/article/details/112848772中講到了我完成了類的建立和執行緒的建立,並且測試兩個執行緒可以正常的同步工作,在這片文章中我主要解決的是我在兩個執行緒之間的資料傳輸過程中遇到的問題。
首先,我要明確我想要的什麼,我需要將CaptureThread執行緒中從相機獲取的影象傳給TrackingThread執行緒中去,繼續目標跟蹤處理,兩個執行緒,CaptureThread執行緒只負責獲取影象資料,TrackingThread執行緒只負責目標跟蹤。但是最重點的是我要把CaptureThread執行緒中獲取的影象傳給TrackingThread執行緒。
我學著ORB-SLAM的樣子,在Tracking類中建立了一個list變數,在Capturing中將獲取的影象都插入到list中來,這樣在TrackingThread中就可以處理這些影象了。
System.h
#ifndef SYSTEM_H
#define SYSTEM_H
#include <thread>
#include "../include/Tracking.h"
#include "../include/Capturing.h"
namespace FDSST
{
class Tracking;
class CaptureThread;
class System {
public:
System();
~System();
private:
Tracking* mpTracker;
Capturing* mpCapturer;
bool mbCameraInited;
//定義兩個執行緒
std::thread* mptTrackThread;
std::thread* mptCaptureThread;
};
}
#endif
System.cpp
#include <thread>
#include "../include/System.h"
namespace FDSST {
System::System()
{
mbCameraInited = false;
//建立相機獲取影象執行緒
mpCapturer = new Capturing();
mptCaptureThread = new std::thread(&FDSST::Capturing::Run, mpCapturer);
std::cout << "Capture thread has been created" << std::endl;
//建立目標跟蹤執行緒
mpTracker = new Tracking();
mptTrackThread = new std::thread(&FDSST::Tracking::Run, mpTracker);
std::cout << "Tracking thread has been created" << std::endl;
}
System::~System(){}
}
Capturing.h
#ifndef Capturing_H
#define Capturing_H
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <list>
#include "../include/Tracking.h"
#include "../include/Frame.h"
using namespace std;
using namespace cv;
namespace FDSST {
class System;
class Tracking;
class Frame;
class Capturing {
public:
Capturing();
~Capturing();
void Run();
void Stream();
void Pause();
void Stop();
bool quit;
public:
Frame mCurrentFrame;
protected:
Tracking* mpTracker;
};
}
Capturing.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
#include "../include/Capturing.h"
namespace FDSST
{
Capturing::Capturing(){}
Capturing::~Capturing(){}
void Capturing::Run()
{
//從本地讀入視訊來模擬從相機獲取影象,要換成自己的視訊路徑呦
std::string videoPath = "F:\\Programme\\object_detection\\fdsst\\data\\012.avi";
cv::VideoCapture cap(videoPath);
if (!cap.isOpened())
{
std::cout << "視訊開啟失敗" << std::endl;
return;
}
while (1)
{
cv::Mat currentImg;
double timestamp = (double)cv::getTickCount();
cap >> currentImg;
if (currentImg.rows <= 0 || currentImg.cols <= 0)
{
break;
}
//將獲取的影象例項化為Frame類的物件。
Frame* pFrame = new Frame(currentImg, timestamp);
//將Frame的指標物件插到在Tracking執行緒中建立的mlNewFrame的list中
mpTracker->InsertFrames(pFrame);
cv::imshow("test1", currentImg);
cv::waitKey(1);
}
return;
}
void Capturing::Stream()
{
pause_status = false;
}
void Capturing::Pause()
{
pause_status = true;
}
void Capturing::Stop()
{
pause_status = true;
quit = true;
}
Tracking.h
#ifndef TRACKING_H
#define TRACKING_H
#include <mutex>
#include "../include/Frame.h"
#include "../include/Capturing.h"
namespace FDSST
{
class Capturing;
class Tracking {
public:
Tracking();
~Tracking();
void Run();
void InsertImages(Frame* frame);
int FramesInQueue() {
std::unique_lock<std::mutex> lock(mMutexNewFrame);
return mlNewFrames.size();
}
void SetCapturer(Capturing* pCapturer);
protected:
//定義互斥鎖變數,確保在TrackThread執行緒中,在list中取影象資料時,CaptureThread停止插入操作
std::mutex mMutexNewFrame;
//定義list變數,儲存從CaptureThread中傳遞的影象資料。
std::list<Frame*> mlNewFrames;
Frame* mCurrentFrame;
Capturing* mpCapturer;
void ProcessNewFrames();
bool CheckNewFrames();
};
}
#endif
Tracking.cpp
#include <iostream>
#include <windows.h>
#include <mutex>
#include "../include/Capturing.h"
#include "../include/Tracking.h"
namespace FDSST
{
Tracking::Tracking(){}
Tracking::~Tracking(){}
void Tracking::Run()
{
while (1)
{
if (CheckNewFrames())
{
ProcessNewFrames();
}
}
}
void Tracking::InsertFrames(Frame* frame)
{
std::unique_lock<std::mutex> lock(mMutexNewFrame);
mlNewFrames.push_back(frame);
}
void Tracking::ProcessNewFrames()
{
std::unique_lock<std::mutex> lock(mMutexNewFrame);
mCurrentFrame = mlNewFrames.front();
cv::Mat img = mCurrentFrame->mImage;
mlNewFrames.pop_front();
cv::imshow("test", img);
cv::waitKey(1);
}
bool Tracking::CheckNewFrames()
{
std::unique_lock<std::mutex> lock(mMutexNewFrame);
return (!mlNewFrames.empty());
}
}
Frame.h
#ifndef FRAME_H
#define FRAME_H
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core.hpp>
namespace FDSST
{
class Frame
{
public:
Frame();
Frame(const cv::Mat& image, const double &timeStamp);
private:
cv::Mat mImage;
double mTimeStamp;
};
}
#endif
我寫完上面的程式碼進行測試,發現我的mpTracker為空,無法操作。
於是我就在Capturing類的建構函式new了一個Tracking指標物件。mpTracker = new Tracking;
然後繼續編譯,發現程式碼可以通過了,但是我從Tracking類中檢視mlNewFrames變數長度,發現一隻是0,也就是影象沒有被插進來,我就很納悶,然後我就又回來研究ORB-SLAM 程式碼,對比一下,我哪個地方沒有做好,找來找去,終於發現了,原來是忘了下面這裡的設定。
於是我在Captureing類中添加了下面函式
void Capturing::SetTracker(Tracking* pTracker)
{
mpTracker = pTracker;
}
在Tracking類中新增下面函式
void Tracking::SetCapturer(Capturing* pCapturer)
{
mpCapturer = pCapturer;
}
在System的建構函式中新增下面語句
System::System()
{
//建立相機獲取影象執行緒
mpCapturer = new Capturing();
mptCaptureThread = new std::thread(&FDSST::Capturing::Run, mpCapturer);
std::cout << "Capture thread has been created" << std::endl;
//建立目標跟蹤執行緒
mpTracker = new Tracking();
mptTrackThread = new std::thread(&FDSST::Tracking::Run, mpTracker);
std::cout << "Tracking thread has been created" << std::endl;
//Set pointers between threads
mpTracker->SetCapturer(mpCapturer);
mpCapturer->SetTracker(mpTracker);
}
經過以上改進CpatureThread中的Frame可以傳送到TrackThread中來了。
感覺好像是他們在互相交換資訊。但是我還是不太清楚這樣操作的目的,經過Set函式之後,將mpTracker就不是空了,而是將mpTracker賦值給他,而mpTracker = new Tracking(),這不很像我剛才的補救措施mpTracker = new Tracking;嘛,我的操作不夠專業。
現在終於把這個實現兩個執行緒之間資料傳輸的程式碼實現了,雖然簡單,但對我來說是一次進步,我不需要跟別人比較,只要我今天跟昨天相比有進步,我就感覺這一天過的有意義,我很開心。