
Qt QCamera를 사용하여 웹캠으로 바코드 스캐너를 구현하는 방법
며칠 전에 Qt와 C++를 사용하여 데스크탑 바코드 리더 애플리케이션을 구현하는 방법에 대해 blog post 썼습니다. 응용 프로그램은 정지 이미지 모드만 지원합니다. 이 기사에서는 응용 프로그램이 실시간으로 바코드를 스캔할 수 있도록 웹캠 지원을 추가하려고 합니다.
Qt 애플리케이션에서 웹캠 지원을 추가하려면
따라서 Qt 5.12.11을 설치하려면 Qt 유지 관리 도구를 실행해야 합니다.

그런 다음 PATH 환경 변수를 업데이트하여 빌드 도구를 다운그레이드합니다.

QCamera 헤더 파일은
QCamera에는 웹캠 미리보기를 표시하는 데 사용할 수 있는
내 목표는 바코드 감지를 위해 웹캠에서 미리보기 프레임을 가져오는 것입니다. 따라서
다음으로
미리보기 프레임을 표시하려면 프레임 데이터의 복사본을 만들어야 합니다.
제 경우에는
그런 다음
마지막으로 라벨에 이미지를 표시하고 텍스트 상자에 바코드 인식 결과를 표시합니다.
여기까지
Qt Creator에서

그런 다음 버튼 클릭 이벤트를 해당 기능에 바인딩합니다.
바코드 스캐너를 사용할 준비가 되었습니다. 애플리케이션을 빌드하고 실행해 보겠습니다.

https://github.com/yushulx/Qt-desktop-barcode-reader
Windows에서 QCamera용 CMake 구성
Qt 애플리케이션에서 웹캠 지원을 추가하려면
QCamera
라이브러리에서 QtMultimedia
클래스를 가져와야 합니다. 내 Windows Qt 환경에서는 QtMultimedia
라이브러리가 포함되지 않은 Qt 6.1.2만 설치했습니다. Qt documentation 에 따르면 Qt 5에서 QCamera
클래스를 찾을 수 있습니다.따라서 Qt 5.12.11을 설치하려면 Qt 유지 관리 도구를 실행해야 합니다.

그런 다음 PATH 환경 변수를 업데이트하여 빌드 도구를 다운그레이드합니다.

QCamera 헤더 파일은
Qt/5.12.11/mingw73_64/include/QtMultimedia/qcamera.h
에 있습니다.CMakeLists.txt
파일에서 Qt 버전을 5로 변경하고 QtMultimedia
라이브러리를 연결합니다.cmake_minimum_required(VERSION 3.5)
project(BarcodeReader VERSION 0.1 LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (CMAKE_HOST_WIN32)
link_directories("${PROJECT_SOURCE_DIR}/platform/windows/lib/")
elseif(CMAKE_HOST_UNIX)
link_directories("${PROJECT_SOURCE_DIR}/platform/linux/")
endif()
include_directories("${PROJECT_SOURCE_DIR}/include/")
find_package(Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt5MultimediaWidgets REQUIRED)
set(PROJECT_SOURCES
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui
myvideosurface.h
myvideosurface.cpp
)
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
if (CMAKE_HOST_WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets Qt5::MultimediaWidgets "DBRx64")
elseif(CMAKE_HOST_UNIX)
target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Widgets Qt5::MultimediaWidgets "DynamsoftBarcodeReader")
endif()
if(CMAKE_HOST_WIN32)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${PROJECT_SOURCE_DIR}/platform/windows/bin/"
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
endif()
myvideosurface.h
파일과 myvideosurface.cpp
파일은 웹캠 미리보기를 표시하고 실시간으로 바코드를 인식하는 데 사용됩니다. 다음 섹션에서 구현에 대해 이야기하겠습니다.실시간 바코드 스캐닝
QCamera에는 웹캠 미리보기를 표시하는 데 사용할 수 있는
setViewfinder()
기능이 있습니다. setViewfinder()
의 지원되는 매개변수에는 QVideoWidget
, QGraphicsVideoItem
및 QAbstractVideoSurface
가 포함됩니다.내 목표는 바코드 감지를 위해 웹캠에서 미리보기 프레임을 가져오는 것입니다. 따라서
QAbstractVideoSurface
클래스를 서브클래싱하고 present()
메서드에서 프레임 데이터를 가져와야 합니다.myvideosurface.h
파일을 생성합니다.#ifndef MYVIDEOSURFACE_H
#define MYVIDEOSURFACE_H
#include <QImage>
#include <QPixmap>
#include <QDebug>
#include <QCamera>
#include <QCameraInfo>
#include <QAbstractVideoSurface>
#include <QLabel>
#include <QDateTime>
#include "DynamsoftCommon.h"
#include "DynamsoftBarcodeReader.h"
QT_BEGIN_NAMESPACE
namespace Ui
{
class MainWindow;
}
QT_END_NAMESPACE
class MyVideoSurface : public QAbstractVideoSurface
{
Q_OBJECT
private:
Ui::MainWindow *ui;
void *reader;
bool is_detecting;
public:
MyVideoSurface(QObject *parent, Ui::MainWindow *ui, void *reader);
~MyVideoSurface();
void reset();
QList<QVideoFrame::PixelFormat>
supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const;
bool present(const QVideoFrame &frame);
};
#endif // MYVIDEOSURFACE_H
MyVideoSurface
클래스의 생성자는 QObject
포인터, Ui::MainWindow
포인터 및 void
포인터를 사용합니다. Ui::MainWindow
포인터는 모든 UI 위젯에 액세스하는 데 사용됩니다. void *reader
포인터는 바코드를 감지하는 데 사용됩니다.다음으로
MyVideoSurface.cpp
클래스의 구현을 추가하기 위해 MyVideoSurface
파일을 생성합니다.supportedPixelFormats()
는 웹캠의 지원되는 픽셀 형식을 가져오는 것입니다. 어떤 형식을 사용해야 합니까? 솔직히 잘 모르겠습니다. 처음에는 QVideoFrame::Format_RGB24
를 사용하지만 웹캠에서 QVideoFrame::Format_RGB24
를 지원하지 않는 것 같습니다. 오류 메시지에 따르면 대신 QVideoFrame::Format_ARGB32
를 사용합니다.QList<QVideoFrame::PixelFormat> MyVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
{
return QList<QVideoFrame::PixelFormat>()
<< QVideoFrame::Format_RGB32;
}
present()
함수에서 QVideoFrame
개체에서 프레임 데이터를 가져와 QImage
개체로 변환합니다.bool MyVideoSurface::present(const QVideoFrame &frame)
{
if (frame.isValid() && is_detecting)
{
QVideoFrame cloneFrame(frame);
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
const QImage img(cloneFrame.bits(),
cloneFrame.width(),
cloneFrame.height(),
QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
cloneFrame.unmap();
return true;
}
return false;
}
미리보기 프레임을 표시하려면 프레임 데이터의 복사본을 만들어야 합니다.
QImage cp = img.copy();
제 경우에는
QVideoFrame
개체에서 얻은 이미지가 세로로 반전되었습니다. QImage::mirrored()
기능으로 이미지를 뒤집을 수 있습니다.cp = cp.mirrored(false, true);
그런 다음
DBR_DecodeBuffer()
를 호출하여 프레임에서 바코드를 감지하고 이미지에 경계 상자를 그립니다.int ret = DBR_DecodeBuffer(reader, (unsigned char *)cp.bits(), cp.width(), cp.height(), cp.bytesPerLine(), IPF_ARGB_8888, "");
DBR_GetAllTextResults(reader, &handler);
QPixmap pm = QPixmap::fromImage(cp);
QPainter painter(&pm);
painter.setPen(Qt::red);
QString out = "";
TextResult **results = handler->results;
for (int index = 0; index < handler->resultsCount; index++)
{
LocalizationResult* localizationResult = results[index]->localizationResult;
out += "Index: " + QString::number(index) + ", Elapsed time: " + QString::number(start.msecsTo(end)) + "ms\n";
out += "Barcode format: " + QString(results[index]->barcodeFormatString) + "\n";
out += "Barcode value: " + QString(results[index]->barcodeText) + "\n";
out += "Bounding box: (" + QString::number(localizationResult->x1) + ", " + QString::number(localizationResult->y1) + ") "
+ "(" + QString::number(localizationResult->x2) + ", " + QString::number(localizationResult->y2) + ") "
+ "(" + QString::number(localizationResult->x3) + ", " + QString::number(localizationResult->y3) + ") "
+ "(" + QString::number(localizationResult->x4) + ", " + QString::number(localizationResult->y4) + ")\n";
out += "----------------------------------------------------------------------------------------\n";
painter.drawLine(localizationResult->x1, localizationResult->y1, localizationResult->x2, localizationResult->y2);
painter.drawLine(localizationResult->x2, localizationResult->y2, localizationResult->x3, localizationResult->y3);
painter.drawLine(localizationResult->x3, localizationResult->y3, localizationResult->x4, localizationResult->y4);
painter.drawLine(localizationResult->x4, localizationResult->y4, localizationResult->x1, localizationResult->y1);
}
DBR_FreeTextResults(&handler);
painter.end();
마지막으로 라벨에 이미지를 표시하고 텍스트 상자에 바코드 인식 결과를 표시합니다.
ui->label->setPixmap(pm.scaled(ui->label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
ui->textEdit_results->setText(out);
여기까지
myvideosurface
부분이 끝났습니다. mainwindow.cpp
로 이동하여 카메라 제어 로직에 대한 코드를 추가할 수 있습니다.Qt Creator에서
mainwindow.ui
를 열고 카메라 제어 버튼을 추가합니다.
MainWindow
생성자에서 카메라 상태를 확인하고 카메라 객체와 표면 객체를 초기화합니다.QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
if (cameras.size() > 0)
{
for( int i = 0; i < cameras.count(); ++i )
{
QCameraInfo cameraInfo = cameras.at(i);
qDebug() << cameraInfo.deviceName();
qDebug() << cameraInfo.description();
camera = new QCamera(cameraInfo);
surface = new MyVideoSurface(this, ui, reader);
camera->setViewfinder(surface);
break;
}
}
else {
ui->pushButton_open->setEnabled(false);
ui->pushButton_stop->setEnabled(false);
}
그런 다음 버튼 클릭 이벤트를 해당 기능에 바인딩합니다.
connect(ui->pushButton_open, SIGNAL(clicked()), this, SLOT(startCamera()));
connect(ui->pushButton_stop, SIGNAL(clicked()), this, SLOT(stopCamera()));
void MainWindow::startCamera()
{
surface->reset();
camera->start();
}
void MainWindow::stopCamera()
{
camera->stop();
}
바코드 스캐너를 사용할 준비가 되었습니다. 애플리케이션을 빌드하고 실행해 보겠습니다.
mkdir build
cd build
cmake -G "MinGW Makefiles" ..
cmake --build .
BarcodeReader.exe

소스 코드
https://github.com/yushulx/Qt-desktop-barcode-reader