Qt QCamera를 사용하여 웹캠으로 바코드 스캐너를 구현하는 방법

31413 단어 cppqrqtwebcam
며칠 전에 Qt와 C++를 사용하여 데스크톱 바코드 판독기 응용 프로그램을 구현하는 방법에 대한 blog post 글을 썼습니다. 응용 프로그램은 정지 이미지 모드만 지원합니다. 이 기사에서는 응용 프로그램이 실시간으로 바코드를 스캔할 수 있도록 웹캠 지원을 추가할 것입니다.

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 , QGraphicsVideoItemQAbstractVideoSurface 가 포함됩니다.

내 목표는 바코드 감지를 위해 웹캠에서 미리보기 프레임을 얻는 것입니다. 따라서 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

좋은 웹페이지 즐겨찾기