Bootstrap

基于现代 C++17 的模块化视频质量诊断处理流程设计

0. 引言

在整理视频质量诊断程序模块代码时发现,处理流程通常是固定的,各种检测功能以函数形式顺序调用。这种设计缺乏灵活性,不易于扩展和维护。本文将使用 C++17 的 std::functionstd::any,重新设计视频质量诊断处理流程,实现模块化、可灵活扩展的处理管道。

视频质量诊断涉及多个检测模块,如信号检测、冻结检测、遮挡检测等。
详细见:

本文将采用面向接口的设计,使用现代 C++ 特性构建一个可动态配置的处理管道。

1. 原始设计分析

原始代码中,各个检测功能以独立的函数实现,处理流程固定:

signalDetect();
freezeframeDetect();
occlusionDetect();
brightnessDetect();
snownoiseDetect();
stripeDetect();
cameramoveDetect();
sharpnessDetect();
colorDetect();
jitterDetect();

这种设计存在以下问题:

  • 缺乏灵活性:处理流程固定,无法根据需求动态调整检测模块的顺序或添加新的检测功能。
  • 可扩展性差:添加新的检测功能需要修改主流程代码,增加了维护成本。
  • 模块耦合度高:各个检测函数之间可能存在隐式依赖,不利于模块化管理。

2. 新的设计思路

流程图

开始
初始化阈值和标志位
构建检测管道
遍历检测管道
执行检测模块
结束

2.1 定义通用的检测接口

借鉴 COM 的接口设计思想,定义一个通用的检测接口 IVideoQualityDetector

class IVideoQualityDetector {
public:
    virtual int Detect(const cv::Mat& img, const cv::Mat& img_jitterRef, const cv::Mat& img_moveRef,
                       const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
                       DetectData_t* detectData) = 0;
    virtual ~IVideoQualityDetector() = default;
};

每个具体的检测模块都实现该接口,实现其特定的检测逻辑。

2.2 使用 std::functionstd::any 管理检测模块

  • std::function:用于存储各个检测模块的调用方法,便于在管道中按顺序执行。
  • std::any:用于存储不同类型的检测模块实例,实现类型擦除,支持在运行时动态管理模块。

2.3 构建可动态配置的检测管道

将各个检测模块实例化后,添加到一个检测管道(如 std::vector<std::function<int(...)>>)中。这样可以根据需求动态调整检测模块的顺序、添加或移除检测模块。

3. 示例实现

3.1 定义检测接口和模块

3.1.1 检测接口
class IVideoQualityDetector {
public:
    virtual int Detect(const cv::Mat& img, const cv::Mat& img_jitterRef, const cv::Mat& img_moveRef,
                       const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
                       DetectData_t* detectData) = 0;
    virtual ~IVideoQualityDetector() = default;
};
3.1.2 信号检测模块
class SignalDetector : public IVideoQualityDetector {
public:
    int Detect(const cv::Mat& img, const cv::Mat&, const cv::Mat&,
               const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
               DetectData_t* detectData) override {
        // 实现信号检测逻辑
        if (img.empty()) return NO_DETECT;

        detectData->signalVal = signalDetect(img);
        if (detectData->signalVal >= threshold.signalThreshold) {
            detectDescribe->nosignalDescribe = NO_SIGNAL;
            return SIGNAL_DETECT;
        } else {
            detectDescribe->nosignalDescribe = SIGNAL_NORMAL;
        }
        return NO_DETECT;
    }
};
3.1.3 冻结检测模块
class FreezeFrameDetector : public IVideoQualityDetector {
public:
    int Detect(const cv::Mat& img, const cv::Mat& img_jitterRef, const cv::Mat&,
               const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
               DetectData_t* detectData) override {
        // 实现冻结检测逻辑
        if (img.empty() || img_jitterRef.empty()) return NO_DETECT;

        detectData->freezeFrameVal = 1 - freezeFrameDetect(img, img_jitterRef);
        if (detectData->freezeFrameVal >= threshold.freezeFrameThreshold) {
            detectDescribe->freezeFrameDescribe = FREEZEFRAME_YES;
            return FREEZEFRAME_DETECT;
        } else {
            detectDescribe->freezeFrameDescribe = FREEZEFRAME_NO;
        }
        return NO_DETECT;
    }
};
3.1.4 其他检测模块

按照上述方式,实现其他检测模块(遮挡检测、亮度检测、雪花噪点检测等),每个模块继承 IVideoQualityDetector 接口,实现其特定的 Detect 方法。

3.2 构建检测管道

3.2.1 初始化管道
std::vector<std::function<int(const cv::Mat&, const cv::Mat&, const cv::Mat&,
                              const Threshold_t&, DetectDescribe_t*, DetectData_t*)>> detectPipeline;
3.2.2 创建检测模块实例
std::any signalDetector = std::make_unique<SignalDetector>();
std::any freezeFrameDetector = std::make_unique<FreezeFrameDetector>();
// ... 创建其他检测模块实例
3.2.3 将检测模块添加到管道
detectPipeline.push_back([&](const cv::Mat& img, const cv::Mat& img_jitterRef, const cv::Mat& img_moveRef,
                             const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
                             DetectData_t* detectData) {
    return std::any_cast<SignalDetector&>(signalDetector).Detect(img, img_jitterRef, img_moveRef,
                                                                 threshold, detectDescribe, detectData);
});

detectPipeline.push_back([&](const cv::Mat& img, const cv::Mat& img_jitterRef, const cv::Mat& img_moveRef,
                             const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
                             DetectData_t* detectData) {
    return std::any_cast<FreezeFrameDetector&>(freezeFrameDetector).Detect(img, img_jitterRef, img_moveRef,
                                                                           threshold, detectDescribe, detectData);
});
// ... 将其他检测模块添加到管道

3.3 执行检测管道

for (auto& detectFunc : detectPipeline) {
    detectFunc(img, img_jitterRef, img_moveRef, threshold, detectDescribe, detectData);
}

3.4 完整代码示例

这里仅展示主要结构,完整代码应包括所有检测模块的实现。

#include <iostream>
#include <vector>
#include <functional>
#include <any>
#include <opencv2/opencv.hpp>

// 定义相关的常量、结构体和函数(如 Threshold_t、DetectDescribe_t、DetectData_t、signalDetect 等)

class IVideoQualityDetector {
public:
    virtual int Detect(const cv::Mat& img, const cv::Mat& img_jitterRef, const cv::Mat& img_moveRef,
                       const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
                       DetectData_t* detectData) = 0;
    virtual ~IVideoQualityDetector() = default;
};

class SignalDetector : public IVideoQualityDetector {
public:
    int Detect(const cv::Mat& img, const cv::Mat&, const cv::Mat&,
               const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
               DetectData_t* detectData) override {
        // 信号检测实现
    }
};

// ... 实现其他检测模块

int ROCK_QualityDiagnoseFromMat(const cv::Mat img, cv::Mat img_jitterRef, cv::Mat img_moveRef,
                                Threshold_t threshold, DetectDescribe_t* detectDescribe,
                                DetectData_t* detectData, unsigned int flag) {
    // 初始化阈值和标志位
    init_threshold(threshold);
    init_flag(flag);

    // 构建检测管道
    std::vector<std::function<int(const cv::Mat&, const cv::Mat&, const cv::Mat&,
                                  const Threshold_t&, DetectDescribe_t*, DetectData_t*)>> detectPipeline;

    std::any signalDetector = std::make_unique<SignalDetector>();
    // ... 创建其他检测模块实例

    // 根据标志位添加需要的检测模块
    if (flag & SIGNAL_DETECT) {
        detectPipeline.push_back([&](const cv::Mat& img, const cv::Mat& img_jitterRef, const cv::Mat& img_moveRef,
                                     const Threshold_t& threshold, DetectDescribe_t* detectDescribe,
                                     DetectData_t* detectData) {
            return std::any_cast<SignalDetector&>(signalDetector).Detect(img, img_jitterRef, img_moveRef,
                                                                         threshold, detectDescribe, detectData);
        });
    }

    // ... 根据其他标志位添加检测模块

    // 执行检测管道
    for (auto& detectFunc : detectPipeline) {
        detectFunc(img, img_jitterRef, img_moveRef, threshold, detectDescribe, detectData);
    }

    return 0;
}

4. 设计优势

4.1 灵活性和可扩展性

  • 动态配置检测模块:可以根据需要在运行时添加、移除或重新排列检测模块,无需修改主流程代码。
  • 支持新功能的扩展:添加新检测功能只需实现 IVideoQualityDetector 接口,并在构建管道时添加即可。

4.2 模块化和可维护性

  • 降低模块耦合度:各个检测模块独立实现,遵循统一的接口,模块之间无直接依赖。
  • 易于维护和测试:每个检测模块可以独立开发和测试,提高代码质量。

4.3 利用现代 C++ 特性

  • std::function:方便地管理和调用函数对象,实现检测管道的灵活组织。
  • std::any:实现类型擦除,支持在运行时管理不同类型的检测模块实例。

5. 参考资料

;