안녕하세요.
Gazer Application을 만들어 보기 전에 기능과 화면 구성에 대해서 이전 내용에서 확인해 보았습니다. 이제는 실제 Qt로 개발하는 것을 진행해 보겠습니다. 우선 이전 내용에서 말한 것처럼 기존 소스가 잘 작동하는 것인지 확인하고 시작하겠습니다.
1. 원본 소스 프로그램 실행
- 몇 가지 내용 수정하고 소스를 컴파일해서 실행 시면 문제없이 카메라 영상을 볼 수 있습니다. 그리고 모션 변화가 있을 때도 감지되는 것을 확인할 수 있습니다. 그래서 책 내용처럼 추가해서 만들어 보도록 하겠습니다. 그러나 원본 소스를 실행하려면 몇 가지 수정 후 실행해야 합니다.
1) 수정해야 할 부분
- 첫 번째로 Gazer.pro 파일에 OpenCV 경로를 지정해 주고 필요한 라이브러리도 함께 입력해주어야 합니다. 이전 내용처럼 OpenCV 경로만 추가했다면 아래와 같이 에러가 발생할 수 있습니다.
opencv error: undefined reference to `cv::VideoCapture::VideoCapture()' 에러가 발생합니다.
win32 {
INCLUDEPATH += D:/opencv/release/install/include
LIBS += -Ld:/opencv/release/install/x64/mingw/bin \
-lopencv_core453 \
-lopencv_imgproc453 \
-lopencv_imgcodecs453 \
-lopencv_video453 \
-lopencv_videoio453
}
- 두 번째로 소스상에 카메라 아이디가 2로 되어 있어서 사용자가 사용하는 카메라가 나타나지 않을 수 있습니다. 컴퓨터에 부착된 카메라마다 인덱스(ID)가 부여되기 때문에 기본적인 웹캠이나 USB 카메라이면 ‘0’으로 camID 번호를 변경하면 됩니다.
void MainWindow::openCamera()
{
if(capturer != nullptr) {
// if a thread is already running, stop it
capturer->setRunning(false);
disconnect(capturer, &CaptureThread::frameCaptured, this, &MainWindow::updateFrame);
disconnect(capturer, &CaptureThread::fpsChanged, this, &MainWindow::updateFPS);
disconnect(capturer, &CaptureThread::videoSaved, this, &MainWindow::appendSavedVideo);
connect(capturer, &CaptureThread::finished, capturer, &CaptureThread::deleteLater);
}
// I am using my second camera whose Index is 2. Usually, the
// Index of the first camera is 0.
int camID = 0;
2) 실행결과
- “File->Open Camera”를 선택하면 이상 없이 카메라 영상이 나오는 것을 확인할 수 있습니다.
2. GazerW UI 설계
- Project 이름은 GazerW로 해서 새로운 프로젝트를 생성합니다. 기본 클래스는 MainWindow로 설정하고, Generate form 체크를 해제합니다. 이유는 동적으로 컴포넌트를 생성해 주기 위해서입니다.
- 선언은 mainwindow.h파일에 하고 구현은 mainwindow.cpp에 하면 됩니다.
1) 변수 생성
- mainwindow.h 파일에 메뉴 관련된 변수를 선언합니다.
private:
QMenu *fileMenu;
QAction *cameraInfoAction;
QAction *openCameraAction;
QAction *exitAction;
- 영상(이미지 프레임)을 보여줄 영상 영역을 위한 변수를 만들어 줍니다.
QGraphicsScene *imageScene;
QGraphicsView *imageView;
- 감지된 이미지와 상태 등 하단부 컴포넌트를 선언해 줍니다.
QCheckBox *monitorCheckBox;
QPushButton *recordButton;
QListView *saved_list;
QStatusBar *mainStatusBar;
QLabel *mainStatusLabel;
- private 영역에 UI를 초기화할 함수를 선언해 줍니다.
private:
void initUI();
void createActions();
2) UI 화면 구현
- 화면에 필요한 내용을 initUI() 함수에서 구현합니다. 예를 들면, 화면 크기를 조정 메뉴, 각 부분에 필요한 하위 widget 생성 및 추가되도록 합니다.
this->resize(1000, 800);
// setup menubar
fileMenu = menuBar()->addMenu("&File");
- 영상 영역 컴포넌트 생성
QGridLayout *main_layout = new QGridLayout();
imageScene = new QGraphicsScene(this);
imageView = new QGraphicsView(imageScene);
main_layout->addWidget(imageView, 0, 0, 12, 1);
- 녹화와 영상 감지 영역 컴포넌트 생성
QGridLayout *tools_layout = new QGridLayout();
main_layout->addLayout(tools_layout, 12, 0, 1, 1);
monitorCheckBox = new QCheckBox(this);
monitorCheckBox->setText("Monitor On/Off");
tools_layout->addWidget(monitorCheckBox, 0, 0);
recordButton = new QPushButton(this);
recordButton->setText("Record");
tools_layout->addWidget(recordButton, 0, 1, Qt::AlignHCenter);
tools_layout->addWidget(new QLabel(this), 0, 2);
- 캡처된 이미지 리스트 표시 영역 생성
// list of saved videos
saved_list = new QListView(this);
main_layout->addWidget(saved_list, 13, 0, 4, 1);
- 생성된 컨테이너 컴포넌트인 main_layout을 화면에 배치하고 상태바 생성과 메뉴 생성하는 것으로 화면 구성을 마무리합니다.
QWidget *widget = new QWidget();
widget->setLayout(main_layout);
setCentralWidget(widget);
// setup status bar
mainStatusBar = statusBar();
mainStatusLabel = new QLabel(mainStatusBar);
mainStatusBar->addPermanentWidget(mainStatusLabel);
mainStatusLabel->setText("GazerW is Ready");
createActions();
- File 메뉴를 추가해 주기 위해서 createActions()를 구현해 줍니다. 단순한 내용이라서 책 내용에는 이전 내용 참고하라고 나와 있어서 소스 내용 추가해 보았습니다. 메뉴 추가와 메뉴와 함수가 연동될 수 있도록 signals와 slots를 connect 함수도 추가되어야 하지만 현재 화면 확인만 할 것이라 fps 부분과 같이 추가하지 않았습니다.
// create actions, add them to menus
cameraInfoAction = new QAction("Camera &Information", this);
fileMenu->addAction(cameraInfoAction);
openCameraAction = new QAction("&Open Camera", this);
fileMenu->addAction(openCameraAction);
exitAction = new QAction("E&xit", this);
fileMenu->addAction(exitAction);
3) 추가사항
- 컴파일시키려고 하면 이런저런 에러가 코드가 발생합니다. 이유는 사용하는 라이브러리가 추가되지 않아서입니다. 그래서 앞으로 필요한 라이브러리를 Mainwindow.h에 포함시켜 줍니다.
#include <QMainWindow>
#include <QMenuBar>
#include <QAction>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QStatusBar>
#include <QLabel>
#include <QListView>
#include <QCheckBox>
#include <QPushButton>
#include <QGraphicsPixmapItem>
#include <QMutex>
#include <QStandardItemModel>
- Mainwindow.cpp 파일에는 QGridLayout를 사용하고 있어서 QGridLayout 라이브러리를 추가해 줍니다.
#include <QGridLayout>
- 메인 메뉴 생성자 함수에 설계한 UI 함수를 추가해 줍니다.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
, fileMenu(nullptr)
{
initUI();
}
4) 참고 함수
- addWidget(), addLayout() 함수를 많이 사용하고 있는데 인수를 보면 아래와 같습니다.
void addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment = Qt::Alignment())
void addLayout(QLayout *layout, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment = Qt::Alignment())
void addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment = Qt::Alignment())
void addWidget(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment = Qt::Alignment())
3. 실행 결과
- 위의 소스코드를 잘 입력해서 실행해 보면 기능은 동작하지 않지만 화면은 UI를 설계한 것 같이 나타날 것입니다. 화면은 필요에 따라 추가나 수정해서 사용할 수 있습니다. 참고로, 위와 같이 컴포넌트들을 동적으로 생성하는 것이 아니라, 정적으로 UI를 만들어서 (.ui) 파일을 만들어서 사용해도 됩니다.
감사합니다.
<참고사이트>
1. opencv error: undefined reference to `cv::VideoCapture::VideoCapture()'
https://forum.qt.io/topic/92848/opencv-error-undefined-reference-to-cv-videocapture-videocapture
2. [BOOK] Qt-5-and-OpenCV-4-Computer-Vision-Projects
3. Qt-5-and-OpenCV-4-Computer-Vision-Projects
4. QGridLayout Class
https://doc.qt.io/qt-5/qgridlayout.html