09 树莓派5 Qt调用CSI摄像头
该安装的在前几节都已经安装好了,这一节就直接从代码开始。
核心代码
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
#include <QPushButton>
#include <QProcess>
#include <QTimer>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void toggleC
mera();
void readCameraOutput();
void updateFrame();
private:
QProcess *cameraProcess = nullptr;
QLabel *cameraView = nullptr;
QPushButton *cameraButton;
QTimer *frameTimer;
QByteArray frameData;
bool cameraRunning = false;
bool frameStarted = false;
int frameCount = 0;
};
#endif // MAINWINDOW_H// mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
#include <QDebug>
#include <QTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// 设置窗口属性
setWindowTitle("树莓派CSI摄像头");
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
resize(800, 600);
// 创建中央部件和布局
QWidget *centralWidget = new QWidget(this);
QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
// 创建摄像头视图区域
QGroupBox *viewGroup = new QGroupBox("摄像头预览", centralWidget);
QVBoxLayout *viewLayout = new QVBoxLayout(viewGroup);
cameraView = new QLabel(viewGroup);
cameraView->setAlignment(Qt::AlignCenter);
cameraView->setMinimumSize(640, 480);
cameraView->setStyleSheet("background-color: black;");
viewLayout->addWidget(cameraView);
// 创建控制区域
QGroupBox *controlGroup = new QGroupBox("控制面板", centralWidget);
QHBoxLayout *controlLayout = new QHBoxLayout(controlGroup);
cameraButton = new QPushButton("启动摄像头", controlGroup);
cameraButton->setFixedSize(150, 40);
cameraButton->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; }");
connect(cameraButton, &QPushButton::clicked, this, &MainWindow::toggleCamera);
controlLayout->addWidget(cameraButton);
// 添加到主布局
mainLayout->addWidget(viewGroup, 4);
mainLayout->addWidget(controlGroup, 1);
setCentralWidget(centralWidget);
// 创建帧定时器
frameTimer = new QTimer(this);
frameTimer->setInterval(33); // 约30fps
connect(frameTimer, &QTimer::timeout, this, &MainWindow::updateFrame);
}
MainWindow::~MainWindow()
{
if (cameraProcess) {
cameraProcess->terminate();
cameraProcess->waitForFinished();
delete cameraProcess;
}
}
void MainWindow::toggleCamera()
{
if (!cameraRunning) {
// 启动libcamera-vid进程
cameraProcess = new QProcess(this);
connect(cameraProcess, &QProcess::readyReadStandardOutput,
this, &MainWindow::readCameraOutput);
// 准备命令行参数
QStringList arguments;
arguments << "--inline" // 输出原始数据
<< "--width" << "640"
<< "--height" << "480"
<< "--nopreview" // 禁用内部预览
<< "--codec" << "mjpeg" // 使用MJPEG格式
<< "--timeout" << "0" // 无限运行
<< "--framerate" << "30" // 设置帧率
<< "--denoise" << "cdn_off" // 禁用降噪以提高性能
<< "-o" << "-"; // 输出到stdout
// 启动摄像头进程
cameraProcess->start("libcamera-vid", arguments);
if (cameraProcess->waitForStarted(3000)) {
cameraButton->setText("停止摄像头");
cameraButton->setStyleSheet("QPushButton { background-color: #F44336; color: white; font-weight: bold; }");
cameraRunning = true;
frameTimer->start();
frameCount = 0;
qDebug() << "摄像头已启动";
} else {
qDebug() << "无法启动摄像头进程:" << cameraProcess->errorString();
delete cameraProcess;
cameraProcess = nullptr;
}
} else {
// 停止摄像头
frameTimer->stop();
if (cameraProcess) {
cameraProcess->terminate();
if (!cameraProcess->waitForFinished(1000)) {
cameraProcess->kill();
}
delete cameraProcess;
cameraProcess = nullptr;
}
cameraButton->setText("启动摄像头");
cameraButton->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; }");
cameraRunning = false;
// 清空视图
cameraView->clear();
cameraView->setStyleSheet("background-color: black;");
qDebug() << "摄像头已停止";
}
}
void MainWindow::readCameraOutput()
{
// 读取所有可用数据
QByteArray newData = cameraProcess->readAllStandardOutput();
frameData.append(newData);
}
void MainWindow::updateFrame()
{
// 如果没有足够数据,直接返回
if (frameData.size() < 100) return;
// 寻找JPEG开始标记 (0xFF 0xD8)
int startIndex = frameData.indexOf("\xFF\xD8");
if (startIndex < 0) {
// 没有找到JPEG开始标记,清空缓冲区
frameData.clear();
return;
}
// 寻找JPEG结束标记 (0xFF 0xD9)
int endIndex = frameData.indexOf("\xFF\xD9", startIndex);
if (endIndex < 0) {
// 没有完整帧,保留可能包含开始标记的数据
if (startIndex > 0) {
frameData = frameData.mid(startIndex);
}
return;
}
// 计算帧长度(包含结束标记)
int frameLength = endIndex - startIndex + 2;
// 提取完整帧数据
QByteArray jpegData = frameData.mid(startIndex, frameLength);
// 移除已处理的数据
frameData = frameData.mid(startIndex + frameLength);
// 更新帧计数器
frameCount++;
// 显示帧率(每30帧更新一次)
if (frameCount % 30 == 0) {
setWindowTitle(QString("树莓派CSI摄像头 - %1 FPS").arg(frameCount / 3));
frameCount = 0;
}
// 创建图像并显示
QPixmap pixmap;
if (pixmap.loadFromData(jpegData, "JPEG")) {
// 缩放图像以适应视图
QPixmap scaledPixmap = pixmap.scaled(cameraView->width(),
cameraView->height(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
cameraView->setPixmap(scaledPixmap);
}
}
// main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置应用样式
QApplication::setStyle("Fusion");
// 创建主窗口
MainWindow w;
w.show();
return a.exec();
}主要流程:

成果演示:

我要赚赏金
