开发环境
vs2017+QT5.9+海康威视SDK+opencv
代码
该博主代码略加修改即可运行,存在问题:cpu占用率达50%;拖动窗口明显延迟
我的改动
SDK的导入由环境变量改为相对路径,即< >改为" "
Format_BGR888改为Format_RGB888,qt5不支持Format_BGR888
// 13. 生成 QImage(Qt5兼容写法)
// 先将BGR转为RGB,再用Format_RGB888构造
cv::Mat rgbMat;
cv::cvtColor(bgrMat, rgbMat, cv::COLOR_BGR2RGB);
QImage img(rgbMat.data, rgbMat.cols, rgbMat.rows, rgbMat.step, QImage::Format_RGB888);
gpt给的改进方案
一、项目数据流与线程分析
目前视频流处理流程是:
HK SDK → RealDataCallback (SDK 内线程)
↓
PlayM4 解码 → DecodeCallback
↓
YUV → BGR → RGB → QImage
↓
emit frameUpdated (HikCamera 对象)
↓
CameraThread 捕获 newFrame
↓
emit newFrame → VideoWidget.updateFrame()
↓
paintEvent 绘制
特点:
-
多线程:
-
SDK 内部线程(RealDataCallback) → PlayM4 解码 → DecodeCallback
-
CameraThread 继承 QThread,接收 frameUpdated 并发 newFrame 信号
-
VideoWidget 在 GUI 线程绘制
-
-
瓶颈点:
-
DecodeCallback每帧都进行 YUV→BGR→RGB → QImage → copy() -
VideoWidget::updateFrame()对 QImage 做了 scaled() + QPixmap::fromImage() -
paintEvent 每次
update()都重新绘制 QPixmap -
线程间通过信号传递每帧,Qt 信号默认是 QueuedConnection,每帧都进入事件队列
-
对于高帧率(25~30fps)视频,可能导致 事件队列堆积 → CPU 占用高、UI 卡顿
-
二、主要性能问题分析
-
CPU 占用高(50%)原因:
-
每帧都做 两次颜色转换:
cv::cvtColor(yuvMat, bgrMat, cv::COLOR_YUV2BGR_YV12); cv::cvtColor(bgrMat, rgbMat, cv::COLOR_BGR2RGB); -
QImage img(rgbMat.data, ...)+img.copy()→ 内存拷贝 -
VideoWidget::updateFrame调用scaled()→ CPU 缩放 -
paintEvent 每次都重绘整个控件
-
-
拖动窗口卡顿原因:
-
paintEvent 重绘与 updateFrame 信号频率高
-
QWidget 使用
Qt::WA_OpaquePaintEvent,每次重绘全屏消耗 CPU -
图像缩放是同步的,阻塞 GUI 线程
-
-
线程安全与锁:
- 每帧使用
QMutexLocker对 m_currentFrame 上锁 → 如果锁等待时间过长,也可能拖慢 UI
- 每帧使用
三、优化方案
1️⃣ 减少颜色转换开销
-
如果后续 YOLO 使用 OpenCV,可以保持 BGR 格式,不必转换为 RGB 再 QImage。
-
Qt 支持使用
QImage::Format_BGR888(Qt 5.9 支持较有限)或者直接绘制 OpenCV Mat:cv::Mat bgrMat; // 已解码的 BGR
QImage img(bgrMat.data, bgrMat.cols, bgrMat.rows, bgrMat.step, QImage::Format_RGB888); // 不做 copy
emit frameUpdated(img); // 去掉 copy() -
避免重复 cvtColor,如果不必显示 RGB,可直接用 BGR Mat 做后续处理(YOLO)。
2️⃣ 优化 VideoWidget 渲染
-
避免每帧做
scaled(),只在 resizeEvent 时做一次缩放:void VideoWidget::updateFrame(const QImage& frame)
{
if (frame.isNull()) return;
QMutexLocker locker(&m_mutex);
m_currentFrame = QPixmap::fromImage(frame); // 不缩放
update();
}void VideoWidget::resizeEvent(QResizeEvent* event)
{
QMutexLocker locker(&m_mutex);
if (!m_currentFrame.isNull())
m_currentFrame = m_currentFrame.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
QWidget::resizeEvent(event);
} -
paintEvent 中直接绘制 m_currentFrame,无需每帧缩放。
-
或者使用 QLabel + setPixmap,Qt 内部优化比手动 scaled 快。
3️⃣ 降低信号队列压力
-
使用
Qt::DirectConnection或在 DecodeCallback 内部 限制帧率:static QElapsedTimer timer;
static int fps = 30;
if (timer.elapsed() < 1000 / fps) return;
timer.restart();
emit camera->frameUpdated(img); -
或者在 CameraThread 内做 帧丢弃策略,保证 GUI 线程只处理 20~30 fps 即可。
4️⃣ 异步 YOLO 推理
-
不要在 GUI 线程做 YOLO 推理
-
可以在 CameraThread 或单独的推理线程做:
// CameraThread 收到 frameUpdated
if (!m_inferThread->isRunning())
m_inferThread->enqueueFrame(frame); // 推理线程队列 -
推理线程输出结果 → GUI 线程绘制标注 → 显示
5️⃣ 锁优化
-
DecodeCallback和VideoWidget::updateFrame使用的锁可以考虑:-
使用 QAtomicPointer / std::atomic 替代 QMutex,避免每帧锁等待
-
或者使用 双缓冲,一帧在后台解码,一帧在前台绘制
-
6️⃣ 其他小优化
-
QImage::copy()→ 只在必须时做 -
paintEvent 填充背景可以省略
fillRect,如果WA_OpaquePaintEvent与 QPixmap 覆盖 -
HikCamera 初始化阶段尽量复用 PlayM4 端口,不必频繁获取释放
四、总体优化建议
| 优化点 | 方法 | 预期效果 |
|---|---|---|
| YUV → RGB → QImage | 避免多余转换,保持 BGR | CPU 占用降低 10~20% |
| QImage → QPixmap 缩放 | 只在 resize 时缩放 | UI 拖动卡顿明显改善 |
| 信号队列 | 限制帧率 20~30 fps | CPU 占用下降,避免 GUI 阻塞 |
| Mutex | 双缓冲或原子指针 | 降低锁开销 |
| YOLO 推理 | 单独线程异步处理 | 不阻塞 UI,保持视频流顺滑 |
| paintEvent | 避免每帧 full repaint | UI 响应更流畅 |