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(); }
主要流程:
成果演示: