プロジェクトの作成
「ファイル」ー「New Project」をクリックし、「Application(Qt)」ー「Qtウィジェットアプリケーション」を選び、「選択」をクリックする。

プロジェクト名を入力し「次へ」をクリックする。

ビルドシステムを選択し「次へ」をクリックする。

ここは、そのままで「次へ」をクリックする。

言語はとりあえず「Japanese(Japan)」を選択

「Desktop Qt 6.11.0」「Debug」「Release」を選択

「完了」を押して終了です。

以下の画面となります。

コードの実装
今回作成したファイルは以下の通り
OpenCVを使用しています。
必要なライブラリーはインストールしてください

MainWindowには、「Vertual Layout」「Push Button」を配置

各コードは以下の通り
CMakeList.txt
cmake_minimum_required(VERSION 3.19)
project(QtCameraCapture LANGUAGES CXX)
find_package(Qt6 6.5 REQUIRED COMPONENTS Core Widgets LinguistTools)
find_package(OpenCV REQUIRED)
qt_standard_project_setup()
qt_add_executable(QtCameraCapture
WIN32 MACOSX_BUNDLE
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
videowidget.h videowidget.cpp
cameraworker.h cameraworker.cpp
)
qt_add_translations(
TARGETS QtCameraCapture
TS_FILES QtCameraCapture_ja_JP.ts
)
target_link_libraries(QtCameraCapture
PRIVATE
Qt::Core
Qt::Widgets
${OpenCV_LIBS}
)
install(TARGETS QtCameraCapture
BUNDLE DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
qt_generate_deploy_app_script(
TARGET QtCameraCapture
OUTPUT_SCRIPT deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "videowidget.h"
#include "cameraworker.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_btnStart_clicked();
void onFrameReceived(const QImage &img);
private:
Ui::MainWindow *ui;
VideoWidget *videoWidget;
CameraWorker *worker;
bool isRunning = false;
};
#endif
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
// コンストラクタ
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 描画Widget生成
videoWidget = new VideoWidget(this);
// layoutの上に追加
ui->verticalLayout->insertWidget(0, videoWidget, 1);
ui->verticalLayout->addWidget(ui->btnStart, 0, Qt::AlignHCenter);
// カメラワーカー生成
worker = new CameraWorker();
// フレーム受信(UIスレッド)
connect(worker, &CameraWorker::frameReady,
this, &MainWindow::onFrameReceived,
Qt::QueuedConnection);
}
// デストラクタ
MainWindow::~MainWindow()
{
disconnect(worker, nullptr, this, nullptr);
worker->stop();
delete worker;
delete ui;
}
// Start / Stop
void MainWindow::on_btnStart_clicked()
{
if (!isRunning) {
worker->start();
ui->btnStart->setText("Stop");
isRunning = true;
} else {
worker->stop();
videoWidget->setImage(QImage());
ui->btnStart->setText("Start");
isRunning = false;
}
}
// フレーム受信
void MainWindow::onFrameReceived(const QImage &img)
{
videoWidget->setImage(img);
}
videowidget.h
#ifndef VIDEOWIDGET_H
#define VIDEOWIDGET_H
#include <QWidget>
#include <QImage>
// =====================
// 映像表示専用Widget
// =====================
class VideoWidget : public QWidget
{
Q_OBJECT
public:
explicit VideoWidget(QWidget *parent = nullptr);
// 外部から画像をセット
void setImage(const QImage &img);
protected:
// 描画処理
void paintEvent(QPaintEvent *event) override;
private:
QImage currentImage; // 最新フレーム保持
};
#endif
videowidget.cpp
#include "videowidget.h"
#include <QPainter>
// コンストラクタ
VideoWidget::VideoWidget(QWidget *parent)
: QWidget(parent)
{
// 背景を黒に(任意)
setStyleSheet("background-color: black;");
}
// フレーム受信
void VideoWidget::setImage(const QImage &img)
{
currentImage = img;
update(); // 再描画
}
// 描画処理
void VideoWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (currentImage.isNull()) return;
QPainter painter(this);
// ウィジェット全体を描画領域に
QRect rect = this->rect();
// アスペクト比を保って拡大縮小
QImage scaled = currentImage.scaled(
rect.size(),
Qt::KeepAspectRatio,
Qt::FastTransformation
);
// 中央配置
int x = (rect.width() - scaled.width()) / 2;
int y = (rect.height() - scaled.height()) / 2;
painter.drawImage(QPoint(x, y), scaled);
}
cameraworker.h
#ifndef CAMERAWORKER_H
#define CAMERAWORKER_H
#include <QObject>
#include <QImage>
#include <pthread.h>
#include <atomic>
#include <opencv2/opencv.hpp>
// =====================
// POSIXスレッドカメラ
// =====================
class CameraWorker : public QObject
{
Q_OBJECT
public:
CameraWorker();
~CameraWorker();
void start();
void stop();
signals:
void frameReady(const QImage &img);
private:
static void* threadFunc(void *arg);
pthread_t thread;
std::atomic<bool> running{false};
cv::VideoCapture cap;
};
#endif
cameraworker.cpp
#include "cameraworker.h"
#include <unistd.h> // usleep
#include <QDebug>
CameraWorker::CameraWorker() {}
CameraWorker::~CameraWorker()
{
stop();
}
// =====================
// スレッド開始
// =====================
void CameraWorker::start()
{
if (running.load()) return;
running = true;
pthread_create(&thread, nullptr, threadFunc, this);
}
// =====================
// スレッド停止
// =====================
void CameraWorker::stop()
{
if(running){
running = false;
pthread_join(thread, nullptr);
}
if (cap.isOpened()) {
cap.release();
}
}
// =====================
// スレッド本体
// =====================
void* CameraWorker::threadFunc(void *arg)
{
CameraWorker *self = static_cast<CameraWorker*>(arg);
self->cap.open(0, cv::CAP_V4L2);
if (!self->cap.isOpened()) {
qDebug() << "Camera open failed";
return nullptr;
}
self->cap.set(cv::CAP_PROP_BUFFERSIZE, 1);
while (self->running.load()) {
cv::Mat frame;
self->cap >> frame;
if (!frame.empty()) {
cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
QImage img((const unsigned char*)frame.data,
frame.cols,
frame.rows,
frame.step,
QImage::Format_RGB888);
// Qtスレッドへ通知
emit self->frameReady(img.copy());
}
usleep(5000); // 5ms
}
return nullptr;
}
main.cpp
変更なし
実行
以下から、実行する。

以下は、実行結果

以上


コメント