행복한 하루

[도서 실습] Qt 5 and OpenCV 4 Computer Vision (Literacy – EAST detector와 tesseract과 이용한 text 추출 + 스크린 캡처) with Raspberry Pi 본문

Programming/Qt

[도서 실습] Qt 5 and OpenCV 4 Computer Vision (Literacy – EAST detector와 tesseract과 이용한 text 추출 + 스크린 캡처) with Raspberry Pi

변화의 물결 2022. 7. 16. 00:03

 

 

안녕하세요. 

 

  문자열 추출하는 구현은 기본적으로 끝이 났습니다. 그렇지만 성능을 조금 향상하기 위한 기능을 추가해보도록 하겠습니다. 컴퓨터에 desktop 화면을 드래그 선택해서 캡쳐 이미지에서 문자를 추출하는 기능을 추가해 볼 예정입니다.

이전 내용 처럼 frozen_east_text_detection.pb 파일은 디버그 디렉터리에 있어야 합니다.


1. ScreenCapturer 클래스 생성

  - QtCreator에서 LiteracyW 프로젝트를 불러온 후 프로젝트 파일에서 오른쪽 버튼을 눌러 “Add New”를 눌러 C++ Class를 생성합니다.

 

  - 클래스 이름을 ScreenCapturer로 하고 base class를 QWidget으로 선택해서 생성합니다. 그러면 header 파일과 Source 파일이 생성된 것을 확인할 수 있습니다.

2. ScreenCapturer.h 작성하기

  - MainWindow *window는 캡처한 영역을 이미지로 지정해서 MainWindow의 showImage로 전달하기 위해서 포인터 선언

 - QPixmap screen 은 화면을 저장하기 위한 변수

 - QPoint p1, p2은 선택한 영역의 오른쪽 상단, 왼쪽 하단의 지점

 - mouseDown는 마우스 클릭하여 드래그를 확인하기 위한 플래그 변수

 - ScreenCapturer.h 에  #include "mainwindow.h" 추가해주어야 MainWindow 에러가 발생하지 않음  

class ScreenCapturer : public QWidget {
        Q_OBJECT

    public:
        explicit ScreenCapturer(MainWindow *w);
        ~ScreenCapturer();

    protected:
        void paintEvent(QPaintEvent *event) override;
        void mouseMoveEvent(QMouseEvent *event) override;
        void mousePressEvent(QMouseEvent *event) override;
        void mouseReleaseEvent(QMouseEvent *event) override;

    private slots:
        void closeMe();
        void confirmCapture();

    private:
        void initShortcuts();
        QPixmap captureDesktop();

    private:
        MainWindow *window;
        QPixmap screen;
        QPoint p1, p2;
        bool mouseDown;
};

3. ScreenCapturer.cpp 작성하기

1) 생성자

 - widget의 테두리가 없고, 가장 상단에 나타날 수 있는 옵션 선택한 후 생성합니다. 그리고 각종 버튼을 제거하고 전체 화면으로 사이즈를 변경하고 단축키(hotkey)를 설정합니다. 

ScreenCapturer::ScreenCapturer(MainWindow *w):
        QWidget(nullptr), window(w)
{
    setWindowFlags(
        Qt::BypassWindowManagerHint
        | Qt::WindowStaysOnTopHint
        | Qt::FramelessWindowHint
        | Qt::Tool
    );

    setAttribute(Qt::WA_DeleteOnClose);
    screen = captureDesktop();
    resize(screen.size());
    initShortcuts();
}

  

2) 화면 조정

  - 한대의 컴퓨터에 여러 화면으로 사용할 수 있고 QGuiApplication::screens()로 모든화면을 얻어 연결합니다.

그리고 QApplication::desktop()->winId()를 사용하여 데스크톱 위젯(루트 창)의 ID를 얻고 데스크탑 루트 창을 QPixmap의 인스턴스로 가져옵니다.

  - 선택한 사각형의 위치와 크기를 grabWIndow 함수에 전달하기 때문에 모든 화면을 포함한 전체 데스크톱이 잡힙니다. 마지막으로 이미지의 픽셀 비율을 로컬 장치에 맞게 적절한 비율로 설정한 전달 합니다. 

QPixmap ScreenCapturer::captureDesktop() {
    QRect geometry;
    for (QScreen *const screen : QGuiApplication::screens()) {
        geometry = geometry.united(screen->geometry());
    }

    QPixmap pixmap(QApplication::primaryScreen()->grabWindow(
                  QApplication::desktop()->winId(),
                  geometry.x(),
                  geometry.y(),
                  geometry.width(),
                  geometry.height()
        ));
    pixmap.setDevicePixelRatio(QApplication::desktop()->devicePixelRatio());
    return pixmap;
}

 

3) 마우스 이벤트

  -  캡처가 시작되고 마우스가 클릭되었을 때 좌표를 저장하고 화면의 변화를 갱신하기 update() 호출합니다. 그리고 마우스 드래그 상태를 Flag 변수로 확인하고 마우스 클릭 후 좌표를 저장합니다. 

void ScreenCapturer::mousePressEvent(QMouseEvent *event)
{
    mouseDown = true;
    p1 = event->pos();
    p2 = event->pos();
    update();
}

void ScreenCapturer::mouseMoveEvent(QMouseEvent *event)
{
    if(!mouseDown) return;
    p2 = event->pos();
    update();
}

void ScreenCapturer::mouseReleaseEvent(QMouseEvent *event)
{
    mouseDown = false;
    p2 = event->pos();
    update();
}

 

4) paintEvent override

  - update() 함수가 실행될 때 혹은 widget이 크기 등 변경이 일어나면 실행됩니다.

  - 캡처된 이미지(desktop 배경)를 위젯에 그려주고, 캡처된 의미로 약간의 회색이 생기게 하고, 마우스로 선택된 영역은 원래 색으로 만들어줍니다. 

void ScreenCapturer::paintEvent(QPaintEvent*) {
    QPainter painter(this);
    painter.drawPixmap(0, 0, screen);

    QRegion grey(rect());
    if(p1.x() != p2.x() && p1.y() != p2.y()) {
        painter.setPen(QColor(200, 100, 50, 255));
        painter.drawRect(QRect(p1, p2));
        grey = grey.subtracted(QRect(p1, p2));
    }

    painter.setClipRegion(grey);
    QColor overlayColor(20, 20, 20, 50);
    painter.fillRect(rect(), overlayColor);
    painter.setClipRect(rect());
}

 

5) 이미지 저장과 종료

  - 마우스로 선택한 영역의 그림을 복사해서 메인 윈도우(Widget)에 전달해주는 함수와 캡처하는 위젯을 종료하면서 메인 위젯을 활성화시키는 함수

void ScreenCapturer::confirmCapture()
{
    QPixmap image = screen.copy(QRect(p1, p2));
    window->showImage(image);
    closeMe();
}

void ScreenCapturer::closeMe()
{
    this->close();
    window->showNormal();
    window->activateWindow();
}

 

 6) 단축키 설정

  - ENTER키와 ESC키를 이미지 저장과 캡처 위젯 종료 함수와 연결합니다.

void ScreenCapturer::initShortcuts() {
        new QShortcut(Qt::Key_Escape, this, SLOT(closeMe()));
        new QShortcut(Qt::Key_Return, this, SLOT(confirmCapture()));
}

4. MainWindow.h 수정하기

  - ToolBar에 기능을 추가하기 위해, 변수를 만들고 Pixmap 이미지를 불러오기 showImage 함수 등을 선언합니다.

class MainWindow : public QMainWindow
{
        // ...
    public:
        // ...
    void showImage(QPixmap);
        // ...
    private slots:
        // ...
        void captureScreen();
        void startCapture();

    private:
        // ..
        QAction *captureAction;
        // ...
};

5. MainWindow.cpp 수정하기

  - createActions() 함수 내에 액션을 하나 추가하고 캡처 함수와 연결해줍니다.   

    captureAction = new QAction("Capture Screen", this);
    fileToolBar->addAction(captureAction);
    // ...
    connect(captureAction, SIGNAL(triggered(bool)), this, SLOT(captureScreen()));

  

  - 캡처하는 함수는 메인 윈도우를 최소화시키고, 타이머와 캡처 위젯을 생성하는 함수와 연결합니다.

  - 500ms 후에 한 번만 실행되도록 합니다. 

void MainWindow::captureScreen()
{
    this->setWindowState(this->windowState() | Qt::WindowMinimized);
    QTimer::singleShot(500, this, SLOT(startCapture()));
}

 

  - 캡처하는 위젯을 생성하고 활성화시켜 캡처 로직이 실행되게 합니다.

void MainWindow::startCapture()
{
    ScreenCapturer *cap = new ScreenCapturer(this);
    cap->show();
    cap->activateWindow();
}

6. LiteracyW.pro 확인

  - QtCreator를 통해서 ScreenCapturer 클래스를 생성했다면 자동으로 헤더 파일과 소스파일 이름이 추가되어 있지만, 수동으로 생성했다면 추가해주어야 합니다.  

   HEADERS += mainwindow.h screencapturer.h
   SOURCES += main.cpp mainwindow.cpp screencapturer.cpp

7. 실행 결과

  - Capture Screen 버턴을 누르면 캡처 위젯이 실행되어 화면 색이 살짝 회색으로 변경되고, 마우스로 드래그하면 선택된 영역의 색상이 변화되는 것을 확인할 수 있습니다.

  - 영역을 선택 후 OCR 버튼을 눌러 글자가 추출되는 것을 확인할 수 있습니다.

  - 많은 글자를 추출하고 있지만 이상한 기호로 인식하는 것들도 있기 때문에 좀 더 최신의 OCR 라이브러리 혹은 학습 데이터가 필요해 보입니다.

 

  

감사합니다.

  

 

 

<참고 사이트>

1. Qt 5 and OpenCV 4 Computer Vision github

https://github.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects

2. 멤버 함수 문서

https://runebook.dev/ko/docs/qt/qscreen?page=2#grabWindow 

LiteracyW_day5.zip
0.01MB

 

 

Comments