0. 核心内容

// ************ 写入Pro文件 ************ //
// 有UI:  QT += core gui widgets concurrent
// 静态库:    QT += core concurrent


// ************ 头文件引入 ************ //
#include <QCoreApplication>  
#include <QtConcurrent/QtConcurrent>  // 核心需求
#include <QElapsedTimer>  // 耗时监控


// ************ 核心代码 ************ //
// 单形参,有返回值的处理函数
cv::Mat processFunc(const cv::Mat& mat)
{
    cv::Mat resMat = mat.clone();
    resMat .setTo(255, mat>0);
    return resMat;
}
// 待并行处理的数据
vector<cv::Mat> datalist
// 并行处理
QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processFunc);
future.waitForFinished();
vector<cv::Mat> res = future.results().toVector().toStdVector();

1. 基础:单形参函数并行处理 vector<cv::Mat> 

1.1 要求

  1. 处理cv::Mat的函数必须是以cv::Mat作为唯一形参传入,且函数返回值也是cv::Mat的,比如:
    cv::Mat processMat(const cv::Mat& mat) {
        cv::Mat result;
        cv::GaussianBlur(mat, result, cv::Size(5,5), 1.5); // 高斯模糊示例
        return result;
    }
  2. 处理的对象是 vector<cv::Mat>
  3. 对cv::Mat的处理必须彼此独立互不干扰

1.2 目的

        对vector<cv::Mat>由for循环的串行处理升级为Qt下的并行处理,提升算法速度。

1.3 代码

        在pro文件中进行如下配置

// 涉及UI或widget文件
QT += core gui widgets concurrent
// 涉及UI或widget文件
QT += core concurrent

        在源文件中这样构造

#include <QCoreApplication>  
#include <QtConcurrent/QtConcurrent>  // 核心需求
#include <QElapsedTimer>  // 耗时监控

// 签名:输入const cv::Mat&,返回cv::Mat → 所有符合该签名的函数都可传入
using FuncMat2Mat = std::function<cv::Mat(const cv::Mat&)>;

// cv::Mat 处理函数
cv::Mat processMat(const cv::Mat& mat) {
    cv::Mat result;
    cv::GaussianBlur(mat, result, cv::Size(5,5), 1.5); // 高斯模糊示例
    return result;
}
// 串行处理方案
vector<cv::Mat> processSerial(const vector<cv::Mat>& datalist, FuncMat2Mat processFunc) {
    vector<cv::Mat> reslist;
    reslist.reserve(datalist.size());
    for (const auto& mat : datalist) {
        reslist.push_back(processFunc(mat));
    }
    return reslist;
}
// 并行处理方案
vector<cv::Mat> processParallel(const vector<cv::Mat>& datalist, FuncMat2Mat processFunc) {
    // QtConcurrent::mapped 自动将datalist的每个元素并行传入processMat
    QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processFunc);
    future.waitForFinished(); // 等待所有并行任务完成
    return future.results().toVector().toStdVector();
}
// 测试函数
void testFunction()
{

    // 1. 构造测试数据:生成10个随机图像
    vector<cv::Mat> datalist;
    for (int i = 0; i < 10; ++i) {
        cv::Mat mat(800, 800, CV_8UC3, cv::Scalar(rand()%255, rand()%255, rand()%255));
        datalist.push_back(mat);
    }

    // 2. 测试串行耗时
    QElapsedTimer timer;
    timer.start();
    vector<cv::Mat> resSerial = processSerial(datalist, processMat);
    qDebug() << "Serial Times=" << timer.elapsed() << "ms";

    // 3. 测试并行耗时
    timer.restart();
    vector<cv::Mat> resParallel = processParallel(datalist, processMat);
    qDebug() << "Parallel Times=" << timer.elapsed() << "ms";

    // 验证结果数量一致(确保并行处理无遗漏)
    qDebug() << "Serial num=" << resSerial.size() << ", Parallel num=" << resParallel.size();

}

2. 进阶:多形参函数并行处理 vector<cv::Mat>

2.1 说明

        QtConcurrent::mapped 函数前后两个输入分别是:

  • sequence:例如vector,里面的数据类型不限(QList,QVector等亦可)
  • map:一个函数名,该函数只能有一个输入形参,且数据类型和构成vector的一致

2.2 目的

        由于QtConcurrent::mapped函数只能调用有一个输入形参的函数,因此当某个函数需要多个输入形参时,例如:

cv::Mat processParaMat(const cv::Mat& mat, int size, int scale)
{
    cv::Mat result;
    cv::GaussianBlur(paraMat.mat, result, cv::Size(size, size), scale);
    return result;
}

此时,为了还能调用QtConcurrent::mapped函数并行处理,需要通过构造结构体改造一下processParaMat函数,如下:

struct ParaMat
{
    cv::Mat mat;
    int para1;
    int para2;
};
cv::Mat processParaMat(const ParaMat& paraMat)
{
    cv::Mat result;
    cv::GaussianBlur(paraMat.mat, result, cv::Size(paraMat.para1, paraMat.para1), paraMat.para2);
    return result;
}

这样,就能把参数信息和cv::Mat信息合并到一个结构体中,通过单形参函数处理了。

完整代码:

struct ParaMat
{
    cv::Mat mat;
    int para1;
    int para2;
};

cv::Mat processParaMat(const ParaMat& paraMat)
{
    cv::Mat result;
    cv::GaussianBlur(paraMat.mat, result, cv::Size(paraMat.para1, paraMat.para1), paraMat.para2);
    return result;
}
vector<cv::Mat> processParallel(const vector<ParaMat>& datalist) {
    QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processParaMat);
    future.waitForFinished(); // 等待所有并行任务完成
    return future.results().toVector().toStdVector();
}
void testFunction()
{

    // 1. 构造测试数据
    vector<ParaMat> datalist;
    for (int i = 0; i < 10; ++i) {
        ParaMat data;
        data.mat = cv::Mat(800, 800, CV_8UC3, cv::Scalar(rand()%255, rand()%255, rand()%255));
        data.para1 = 7;
        data.para2 = 7;
        datalist.push_back(data);
    }

    // 2. 测试并行耗时
    QElapsedTimer timer;
    timer.start();
    vector<cv::Mat> res = processParallel(datalist);
    qDebug() << "Parallel Times=" << timer.elapsed() << "ms";

    // 验证结果数量一致(确保并行处理无遗漏)
    qDebug()<<", Parallel num=" << res.size();
    
}

3. 进阶:类似的并行函数

3.1 QtConcurrent::map()

适用于:单形参且无返回值的void函数来处理输入信息(变量原地址赋值)

// 单形参,无返回值的void处理函数
void processFunc(cv::Mat& mat)
{
    mat.setTo(255, mat>0);
}
// 待并行处理的数据
vector<cv::Mat> datalist
// 并行处理
QFuture<cv::Mat> future = QtConcurrent::map(datalist, processFunc);
future.waitForFinished();
vector<cv::Mat> res = future.results().toVector().toStdVector();

3.2 QtConcurrent::mapped()

适用于:单形参且有返回值的函数来处理输入信息(创建新变量返回)

// 单形参,有返回值的处理函数
cv::Mat processFunc(const cv::Mat& mat)
{
    cv::Mat resMat = mat.clone();
    resMat .setTo(255, mat>0);
    return resMat;
}
// 待并行处理的数据
vector<cv::Mat> datalist
// 并行处理
QFuture<cv::Mat> future = QtConcurrent::mapped(datalist, processFunc);
future.waitForFinished();
vector<cv::Mat> res = future.results().toVector().toStdVector();

3.3 QtConcurrent::mappedReduced()

适用于

  • 单形参且有返回值的函数来处理输入信息(创建新变量返回)
  • 处理后的信息按照有序或无序的方式迭代处理,并最终返回
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <vector>
#include <opencv2/opencv.hpp>

// ========== 1. 映射函数:处理Mat(高斯模糊) ==========
cv::Mat processMat(const cv::Mat& mat) {
    cv::Mat result;
    cv::GaussianBlur(mat, result, cv::Size(5,5), 1.5);
    return result;
}

// ========== 2. 归约函数:拼接所有Mat为一个大图 ==========
void concatMats(cv::Mat& finalMat, const cv::Mat& singleMat) {
    if (finalMat.empty()) {
        finalMat = singleMat.clone(); // 第一个Mat直接赋值
    } else {
        // 横向拼接:最终结果 = 原最终结果 + 当前Mat
        cv::hconcat(finalMat, singleMat, finalMat);
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 构造测试数据:2个相同大小的随机Mat
    std::vector<cv::Mat> datalist;
    datalist.emplace_back(400, 400, CV_8UC3, cv::Scalar(0,255,0));  // 绿色
    datalist.emplace_back(400, 400, CV_8UC3, cv::Scalar(255,0,0));  // 红色

    // ========== 3. 并行处理+归约(拼接) ==========
    QFuture<cv::Mat> future = QtConcurrent::mappedReduced(
        datalist,
        processMat,
        concatMats,
        QtConcurrent::OrderedReduce // 按顺序拼接(保证绿色在前,红色在后)
    );
    future.waitForFinished();

    // 保存最终拼接图
    cv::Mat finalMat = future.result();
    cv::imwrite("concat_result.png", finalMat);
    qDebug() << "拼接图尺寸:" << finalMat.rows << "x" << finalMat.cols; // 400x800

    return 0;
}

注意:

  • 最终结果的初始化reduceFunc的第一个参数(最终结果)会被 QtConcurrent 自动初始化为 “默认值”(如int初始为 0,cv::Mat初始为空),无需手动初始化。

  • OrderedReduce vs UnorderedReduce

    • 若聚合结果和顺序无关(如统计总数、求和),用默认的UnorderedReduce(效率更高);
    • 若聚合结果依赖顺序(如拼接图像、按输入顺序汇总),用OrderedReduce
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐