文章目录
0. 引言
在整理视频质量诊断程序模块代码时发现,处理流程通常是固定的,各种检测功能以函数形式顺序调用。这种设计缺乏灵活性,不易于扩展和维护。本文将使用 C++17 的 std::function
和 std::any
,重新设计视频质量诊断处理流程,实现模块化、可灵活扩展的处理管道。
视频质量诊断涉及多个检测模块,如信号检测、冻结检测、遮挡检测等。
详细见:
- C++基于opencv4的视频质量检测
- C++基于opencv的视频质量检测–图像清晰度检测
- C++基于opencv的视频质量检测–画面冻结检测
- C++基于opencv的视频质量检测–图像抖动检测
- C++基于opencv的视频质量检测–遮挡检测
本文将采用面向接口的设计,使用现代 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::function
和 std::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
:实现类型擦除,支持在运行时管理不同类型的检测模块实例。