引言
项目中需要用到多线程对四幅视频图像进行拼接,但是目前还未采集用于拼接图像数据,目前先使用同一幅图像(四个图像文件,视频的内容相同),从四幅视频中分别截取一块区域,直接将截取到的图像拼接在一起。
设计原理
线程划分
分为三个线程,每个线程的具体工作如下所示
线程1:
- 读取video1和video2的视频图像
- 获取视频图像的宽度和高度
- 取video1每一帧的左边四分之一图像
- 取video2中上图所示的四分之一图像区域
- 将获取的两幅图像直接拼接在一起
线程2:
线程2对video3和video4进行操作,流程和线程1类似。线程1和线程2属于并行线程互不干扰。不需要加锁
线程3:
- 获取线程1中拼接好的一帧图像
- 获取线程2中拼接好的一帧图像(与线程1获得的图像是同一帧)
- 将获取的两幅图像进行拼接
- 显示拼接结果
线程之间的逻辑
线程1与线程2属于并行线程,互相独立,没有共有的数据;线程3会用到线程1和线程2得到的数据,所以在设计时线程1和线程2之间不用考虑加锁等防止同时操作同一块内存的方法;反之,线程3需要用到前两个线程得到的数据,需要通过合理的设计防止同时操作同一内存位置的数据
线程实现
线程1
获取video1中每一帧的最左边的四分之一图像,获取video2中左边第二个四分之一区域的图像,然后将两幅图像拼接,涉及的操作如下:
- 获取两个视频中每一帧的感兴趣区域
- 将获取的两幅感兴趣区域的图像进行拼接
- 将拼接的结果存放到全局变量vector<Mat> dst12中
- 将存入的图像数放在全局变量num12中
对应的代码如下所示:
void stitch12(const string & video1, const string & video2) {
VideoCapture cap1(video1);
VideoCapture cap2(video2);
if (!cap1.isOpened() || !cap2.isOpened()) {
cout << "Failed to open the videos!" << endl;
return;
}
//计算想要获取的感兴趣区域
int W = cap1.get(CAP_PROP_FRAME_WIDTH);
int H = cap2.get(CAP_PROP_FRAME_HEIGHT);
Rect roi1(0, 0, W/4, H);
Rect roi2(W / 4, 0, W / 4, H);
while (true) {
Mat fram1, fram2;
cap1 >> fram1;
cap2 >> fram2;
if (fram1.empty() || fram2.empty()) {
break;
}
Mat result;
Mat r1 = fram1(roi1).clone();
Mat r2 = fram2(roi2).clone();
hconcat(r1, r2, result);
dst12.push_back(result);
num12++;
imshow("dst12", result);
waitKey(50);
}
}
线程2
获取video3中每一帧的最右边的四分之一图像,获取video3中右边边第二个四分之一区域的图像,然后将两幅图像拼接,涉及的操作如下:
- 获取两个视频中每一帧的感兴趣区域
- 将获取的两幅感兴趣区域的图像进行拼接
- 将拼接的结果存放到全局变量vector<Mat> dst34中
- 将存入的图像数放在全局变量num34中
线程3
此线程要获取前两个线程得到的拼接图像,并将两幅图像拼接,涉及到对共享内存的读写操作
逻辑分析
- 只能读取已经存好的数据对应的内存位置,正在存储的内存位置不能读取;(不能同时对一个内存区域进行读写操作)
- 由于要读取要两幅对应帧的图像,对图像进行拼接,所以读取的图像要一一对应
代码实现
总上所述,对于上图的情况,只能对前三幅图像进行读取并拼接,所以线程3的代码如下所示:
void stitch() {
int i = 0;//存储图像的容器对应的小标
Mat result;
while (true) {
//判断图像是否可以读取
if ((i+1) <= min(num12, num34)){
Mat img1 = dst12[i];
Mat img2 = dst34[i];
if (img1.empty() || img2.empty()) {
break;
}
hconcat(img1, img2, result);
imshow("result", result);
waitKey(50);
i++;
}
//cout << "Wait data write ..." << endl;
if (waitKey(1) == 27) { // 27是ESC的ASCII码
break;
}
}
}
完整代码
#include <iostream>
#include <thread>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
vector<Mat> dst12; //储存前两幅图像的拼接结果
vector<Mat> dst34; //存储后两幅图像的拼接结果
vector<Mat> dst; //存储最终拼接结果
//存入的图像数量
int num12 = 0;
int num34 = 0;
void stitch12(const string & video1, const string & video2) {
VideoCapture cap1(video1);
VideoCapture cap2(video2);
if (!cap1.isOpened() || !cap2.isOpened()) {
cout << "Failed to open the videos!" << endl;
return;
}
//计算想要获取的感兴趣区域
int W = cap1.get(CAP_PROP_FRAME_WIDTH);
int H = cap2.get(CAP_PROP_FRAME_HEIGHT);
Rect roi1(0, 0, W/4, H);
Rect roi2(W / 4, 0, W / 4, H);
while (true) {
Mat fram1, fram2;
cap1 >> fram1;
cap2 >> fram2;
if (fram1.empty() || fram2.empty()) {
break;
}
Mat result;
Mat r1 = fram1(roi1).clone();
Mat r2 = fram2(roi2).clone();
hconcat(r1, r2, result);
dst12.push_back(result);
num12++;
imshow("dst12", result);
waitKey(50);
}
}
void stitch34(const string & video3, const string & video4) {
VideoCapture cap3(video3);
VideoCapture cap4(video4);
if (!cap3.isOpened() || !cap4.isOpened()) {
cout << "Failed to open the videos!" << endl;
return;
}
//计算想要获取的感兴趣区域
int W = cap3.get(CAP_PROP_FRAME_WIDTH);
int H = cap4.get(CAP_PROP_FRAME_HEIGHT);
Rect roi3(W / 2, 0, W / 4, H);
Rect roi4(3*W/4, 0, W / 4, H);
while (true) {
Mat fram3, fram4;
cap3 >> fram3;
cap4 >> fram4;
if (fram3.empty() || fram4.empty()) {
break;
}
Mat result;
Mat r3 = fram3(roi3).clone();
Mat r4 = fram4(roi4).clone();
hconcat(r3, r4, result);
dst34.push_back(result);
num34++;
imshow("dst34", result);
waitKey(50);
}
}
void stitch() {
int i = 0;
Mat result;
while (true) {
if ((i+1) <= min(num12, num34)){
Mat img1 = dst12[i];
Mat img2 = dst34[i];
if (img1.empty() || img2.empty()) {
break;
}
hconcat(img1, img2, result);
imshow("result", result);
waitKey(50);
i++;
}
//cout << "Wait data write ..." << endl;
if (waitKey(1) == 27) { // 27是ESC的ASCII码
break;
}
}
}
int main() {
std::string video1 = "E:/imgMoasic/mulThreadImgSticth/vedio/video1.mp4";
std::string video2 = "E:/imgMoasic/mulThreadImgSticth/vedio/video2.mp4";
std::string video3 = "E:/imgMoasic/mulThreadImgSticth/vedio/video3.mp4";
std::string video4 = "E:/imgMoasic/mulThreadImgSticth/vedio/video4.mp4";
thread t1(stitch12, video1, video2);
thread t2(stitch34, video3, video4);
thread t3(stitch);
t1.join();
t2.join();
t3.join();
destroyAllWindows();
system("pause");
return 0;
}