안녕하세요.
아쉽게도 바로 이전 내용에서 윈도용으로 진행했었는데, 윈도우상에서 Qt와 tesseract, OpenCV 조합하기가 어려워져서 라즈베리 파이로 환경을 바꿨습니다. 이후 내용은 리눅스 기반에서 작업하는 것으로 생각하시면 됩니다.
이전 Rasbperry Pi 내용에서 Tesseract 4.1.3을 Raspberry Pi CM4에 설치했다는 것을 전제로 아래 내용을 진행합니다. (하단 링크 참고) 추가로, 현재 최신 버전인 Tesseract 5.1.x를 설치해보려고 했으나 현재 라즈베리 파이 상에서는 설치가 되지 않았습니다. (다른 OS에서는 테스트해보지 않았습니다.)
1. LiteracyW.pro 파일 수정
- 설치된 tesseract의 해더 파일과 라이브러리 경로를 설정해 줍니다. 그리고 학습된 데이터 경로도 지정해 줍니다.
unix: {
INCLUDEPATH += /home/pi/install_lib/tesseract/include
LIBS += -L/home/pi/install_lib/tesseract/lib -ltesseract
}
DEFINES += TESSDATA_PREFIX=\\\"/home/pi/install_lib/tesseract/share/tessdata/\\\"
2. mainwindow.h 파일 수정
- 추가한 tesseract 라이브러리를 사용하기 위한 해더 파일(baseapi)을 추가해 주고 툴바 버튼 변수와 tesseractAPI를 사용할 수 있는 포인터 변수를 선언합니다.
#include "tesseract/baseapi.h"
class MainWindow : public QMainWindow
{
// ...
private slots:
// ...
void extractText();
private:
// ...
QAction *ocrAction;
// ...
tesseract::TessBaseAPI *tesseractAPI;
};
3. mainwindow.cpp 파일 수정
- 생성자 함수에서 TesseractAPI 포인터 변수를 nullptr로 초기화해 줍니다.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
, currentImage(nullptr)
, tesseractAPI(nullptr)
{
initUI();
}
- 툴바에서 OCR 버튼을 함수와 연결합니다. openImage() 등 함수도 연결합니다.
void MainWindow::createActions()
{
// ...
ocrAction = new QAction("OCR", this);
fileToolBar->addAction(ocrAction);
// ...
// connect the signals and slots
connect(exitAction, SIGNAL(triggered(bool)), QApplication::instance(), SLOT(quit()));
connect(openAction, SIGNAL(triggered(bool)), this, SLOT(openImage()));
//connect(saveImageAsAction, SIGNAL(triggered(bool)), this, SLOT(saveImageAs()));
connect(saveTextAsAction, SIGNAL(triggered(bool)), this, SLOT(saveTextAs()));
connect(ocrAction, SIGNAL(triggered(bool)), this, SLOT(extractText()));
// ...
}
- 이미지에서 문자열을 출력하는 함수를 구현합니다. 우선 현재 이미지를 불러왔는지 확인하고 TesseractAPI의 인스턴스를 생성합니다.
- setlocale( 언어 설정 카테고리, 변경하려는 언어 설정 명칭)로 인자가 되어 있고, 변경하려는 언어 설정 명칭에 NULL을 입력하면 현재 언어 상태가 리턴이 되며, 카테고리에 LC_ALL라고 입력하면 시간, 화폐단위 등 전반적으로 영향을 미게 합니다. “C”라고 입력했다면, C의 기본 베이스 언어가 선택이 되며 한글 지원이 되지 않습니다.
- strdup()를 사용해서 생성된 상태 값을 동적으로 Heap 영역에 생성해서 저장하고 있습니다. 그래서 끝부분에서 free() 해제해주고 있습니다.
- tesseractAPI를 사용하기 위해서는 init()를 사용해서 초기화해주어야 합니다. init() 함수는 여러 가지 기능(engine mode, segmentation mode 등)을 설정하기 위해서 overload를 시켜 놓았습니다. 여기서는 가장 단순하게 미리 학습된 데이터를 통해 영어를 분석하는 함수로 초기화해 줍니다. 그리고 초기화가 정상적으로 되었는지 확인해주어야 합니다.
- 불러온 이미지를 RGB888 포맷으로 변환해 주고, SetImage() 함수에 인자로 넣어 줍니다. 여기서 이미지 사이즈 정보와 8bit depth의 3은 채널 값을 인자로 넣어 줍니다.
- GetUTF8Text()를 통해서 이미지에서 인식한 글자를 추출합니다. 여기서 주의할 점은 추출한 글자가 동적으로 할당된 문자열 포인터를 받기 때문에 Text 창에 출력한 후에 free로 해제해주어야 한다는 것입니다.
void MainWindow::extractText()
{
if (currentImage == nullptr) {
QMessageBox::information(this, "Information", "No opened image.");
return;
}
char *old_ctype = strdup(setlocale(LC_ALL, NULL));
setlocale(LC_ALL, "C");
tesseractAPI = new tesseract::TessBaseAPI();
// Initialize tesseract-ocr with English, with specifying tessdata path
if (tesseractAPI->Init(TESSDATA_PREFIX, "eng")) {
QMessageBox::information(this, "Error", "Could not initialize tesseract.");
return;
}
QPixmap pixmap = currentImage->pixmap();
QImage image = pixmap.toImage();
image = image.convertToFormat(QImage::Format_RGB888);
tesseractAPI->SetImage(image.bits(), image.width(), image.height(),
3, image.bytesPerLine());
char *outText = tesseractAPI->GetUTF8Text();
editor->setPlainText(outText);
// Destroy used object and release memory
tesseractAPI->End();
delete tesseractAPI;
tesseractAPI = nullptr;
delete [] outText;
setlocale(LC_ALL, old_ctype);
free(old_ctype);
}
- openImage()와 showImage 구현을 추가로 구현해 줍니다. 다이얼로그 창을 띄워서 이미지를 선택할 수 있도록 하고, 이미지 경로를 받아서 이미지 사이즈 등 정보를 출력해 주는 부분과 이미지를 imageView에 출력해 주는 부분으로 나누어 구현합니다.
void MainWindow::openImage()
{
QFileDialog dialog(this);
dialog.setWindowTitle("Open Image");
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("Images (*.png *.bmp *.jpg)"));
QStringList filePaths;
if (dialog.exec()) {
filePaths = dialog.selectedFiles();
showImage(filePaths.at(0));
}
}
void MainWindow::showImage(QString path)
{
QPixmap image(path);
showImage(image);
currentImagePath = path;
QString status = QString("%1, %2x%3, %4 Bytes").arg(path).arg(image.width())
.arg(image.height()).arg(QFile(path).size());
mainStatusLabel->setText(status);
}
void MainWindow::showImage(QPixmap image)
{
imageScene->clear();
imageView->resetMatrix();
currentImage = imageScene->addPixmap(image);
imageScene->update();
imageView->setSceneRect(image.rect());
}
4. 실행 결과
- 샘플 이미지를 눌러온 후 "OCR" 버튼을 누르면 우측면에 이미지에서 인식한 글자가 나타나는 것을 확인할 수 있습니다. Service 부분의 글자는 인식이 잘 되지 않았습니다.
감사합니다.
<참고 사이트>
1. Qt 5 and OpenCV 4 Computer Vision github
https://github.com/PacktPublishing/Qt-5-and-OpenCV-4-Computer-Vision-Projects
2. c++ 및 api 언어 설정에 대하여 setlocale에 대해 하여 알아보자.
https://m.blog.naver.com/assortrockp/220764067554
3. strdup함수 활용 예제
4. c언어-strdup / _strdup
https://swimminglab.tistory.com/54