MauiKit Image Tools

ocs.cpp
1#include "ocs.h"
2#include <QImage>
3#include <QDebug>
4#include <QtConcurrent>
5#include <QFutureWatcher>
6
7#include <tesseract/baseapi.h>
8#include <leptonica/allheaders.h>
9#include "OCRLanguageModel.h"
10#if TESSERACT_MAJOR_VERSION < 5
11#include <tesseract/strngs.h>
12#include <tesseract/genericvector.h>
13#endif
14
15//static cv::Mat qimageToMatRef(QImage &img, int format)
16// {
17// return cv::Mat(img.height(),
18// img.width(),
19// format,
20// img.bits(),
21// static_cast<size_t>(img.bytesPerLine()));
22// }
23// static cv::Mat qimageToMat(QImage img, int format)
24// {
25// return cv::Mat(img.height(),
26// img.width(),
27// format,
28// img.bits(),
29// static_cast<size_t>(img.bytesPerLine()));
30// }
31// static QImage matToQimageRef(cv::Mat &mat, QImage::Format format)
32// {
33// return QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), format);
34// }
35// static QImage matToQimage(cv::Mat mat, QImage::Format format)
36// {
37// return QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), format);
38// }
39
40OCS::OCS(QObject *parent) : QObject(parent)
41 ,m_tesseract(new tesseract::TessBaseAPI())
42 ,m_languages(new OCRLanguageModel(this))
43 ,m_boxesTypes(BoxType::Word | BoxType::Line | BoxType::Paragraph)
44{
45 std::vector<std::string> availableLanguages;
46#if TESSERACT_MAJOR_VERSION < 5
47 GenericVector<STRING> languageVector;
48 m_tesseract->GetAvailableLanguagesAsVector(&languageVector);
49 for (int i = 0; i < languageVector.size(); i++) {
50 availableLanguages.push_back(languageVector[i].c_str());
51 }
52#else
53 m_tesseract->GetAvailableLanguagesAsVector(&availableLanguages);
54#endif
55
56 m_languages->setLanguages(availableLanguages);
57}
58
59OCS::~OCS()
60{
61 m_tesseract->End();
62}
63
64QString OCS::filePath() const
65{
66 return m_filePath;
67}
68
69QRect OCS::area() const
70{
71 return m_area;
72}
73
74bool OCS::autoRead() const
75{
76 return m_autoRead;
77}
78
79void OCS::setAutoRead(bool value)
80{
81 if(m_autoRead == value)
82 return;
83
84 m_autoRead = value;
85 Q_EMIT autoReadChanged();
86}
87
88void OCS::setBoxesType(OCS::BoxesType types)
89{
90 if(m_boxesTypes == types)
91 return;
92
93
94 m_boxesTypes = types;
95 qDebug() << "Setting the boxes types" << m_boxesTypes << types;
96
97 Q_EMIT boxesTypeChanged();
98}
99
100void OCS::getTextAsync()
101{
102 if(!QUrl::fromUserInput(m_filePath).isLocalFile())
103 {
104 qDebug() << "URL is not local :: OCR";
105 return;
106 }
107 typedef QMap<BoxType, TextBoxes> Res;
108 auto func = [](QUrl url, BoxesType levels) -> Res
109 {
110 Pix *image = pixRead(url.toLocalFile().toStdString().c_str());
111 tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
112 api->Init(NULL, "eng");
113 api->SetImage(image);
114 api->Recognize(0);
115
116 TextBoxes wordBoxes, lineBoxes, paragraphBoxes;
117
118 auto levelFunc = [](tesseract::TessBaseAPI *api, tesseract::PageIteratorLevel level) -> TextBoxes
119 {
120 TextBoxes res;
121 tesseract::ResultIterator* ri = api->GetIterator();
122 if (ri != 0)
123 {
124 qDebug() << "Getting text for level" << level;
125 do
126 {
127 const char* word = ri->GetUTF8Text(level);
128 float conf = ri->Confidence(level);
129 int x1, y1, x2, y2;
130 ri->BoundingBox(level, &x1, &y1, &x2, &y2);
131
132 printf("word: '%s'; \tconf: %.2f; BoundingBox: %d,%d,%d,%d;\n",
133 word, conf, x1, y1, x2, y2);
134
135 if(conf > 50)
136 res << QVariantMap{{"text", QString::fromStdString(word)}, {"rect", QRect{x1, y1, x2-x1, y2-y1}}};
137 delete[] word;
138 } while (ri->Next(level));
139 }
140
141 return res;
142 };
143
144 if(levels.testFlag(Word))
145 wordBoxes = levelFunc(api, tesseract::RIL_WORD);
146
147 if(levels.testFlag(Line))
148 lineBoxes = levelFunc(api, tesseract::RIL_TEXTLINE);
149
150 if(levels.testFlag(Paragraph))
151 paragraphBoxes = levelFunc(api, tesseract::RIL_PARA);
152
153
154 delete api;
155 return Res{{Word, wordBoxes}, {Line, lineBoxes}, {Paragraph, paragraphBoxes}};
156 };
157
158 auto watcher = new QFutureWatcher<Res>;
159 connect(watcher, &QFutureWatcher<Res>::finished, [this, watcher]()
160 {
161 // Q_EMIT textReady(watcher.future().result());
162 m_wordBoxes = watcher->result()[Word];
163 m_lineBoxes = watcher->result()[Line];
164 m_paragraphBoxes = watcher->result()[Paragraph];
165 Q_EMIT wordBoxesChanged();
166 Q_EMIT lineBoxesChanged();
167 Q_EMIT paragraphBoxesChanged();
168 watcher->deleteLater();
169 });
170
171 qDebug() << "GEtting text for boxes " << m_boxesTypes << m_boxesTypes.testFlag(Word);
172 QFuture<Res> future = QtConcurrent::run(func, QUrl::fromUserInput(m_filePath), m_boxesTypes);
173 watcher->setFuture(future);
174}
175
176QString OCS::getText()
177{
178 QUrl url(QUrl::fromUserInput(m_filePath));
179 if(!url.isLocalFile())
180 {
181 qDebug() << "URL is not local :: OCR";
182 return "Error!";
183 }
184
185 if (m_tesseract->Init(nullptr, m_languages->getLanguagesString().c_str()))
186 {
187 qDebug() << "Failed tesseract OCR init";
188 return "Error!";
189 }
190
191 m_tesseract->SetPageSegMode(tesseract::PSM_AUTO);
192
193 QString outText;
194
195 if(!m_area.isEmpty())
196 {
197 QImage img(url.toLocalFile());
198 img = img.copy(m_area);
199 // img = img.convertToFormat(QImage::Format_Grayscale8);
200
201 m_tesseract->SetImage(img.bits(), img.width(), img.height(), 4, img.bytesPerLine());
202
203 }else
204 {
205 Pix* im = pixRead(url.toLocalFile().toStdString().c_str());
206 m_tesseract->SetImage(im);
207 }
208
209 outText = QString::fromStdString(m_tesseract->GetUTF8Text());
210
211 return outText;
212}
213
214void OCS::setFilePath(QString filePath)
215{
216 if (m_filePath == filePath)
217 return;
218
219 m_filePath = filePath;
220 Q_EMIT filePathChanged(m_filePath);
221}
222
223void OCS::setArea(QRect area)
224{
225 if (m_area == area)
226 return;
227
228 m_area = area;
229 Q_EMIT areaChanged(m_area);
230}
231
232
233TextBoxes OCS::wordBoxes() const
234{
235 return m_wordBoxes;
236}
237
238TextBoxes OCS::paragraphBoxes() const
239{
240 return m_paragraphBoxes;
241}
242
243TextBoxes OCS::lineBoxes() const
244{
245 return m_lineBoxes;
246}
247
248OCS::BoxesType OCS::boxesType()
249{
250 return m_boxesTypes;
251}
252
253void OCS::classBegin()
254{
255}
256
257void OCS::componentComplete()
258{
259 qDebug() << "OCS CALSS COMPLETED IN QML";
260 connect(this, &OCS::filePathChanged, [this](QString)
261 {
262 if(m_autoRead)
263 {
264 getTextAsync();
265 }
266 });
267 getTextAsync();
268}
QStringView level(QStringView ifopt)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString fromStdString(const std::string &str)
std::string toStdString() const const
QFuture< T > run(Function function,...)
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
bool isLocalFile() const const
QString toLocalFile() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Mar 7 2025 11:56:33 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.