5#include <QFutureWatcher>
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>
16#include <preprocessimage.hpp>
17#include <convertimage.hpp>
22 ,m_tesseract(new tesseract::TessBaseAPI())
23 ,m_languages(new OCRLanguageModel(this))
24 ,m_boxesTypes(BoxType::Word | BoxType::Line | BoxType::Paragraph)
25 ,m_confidenceThreshold(50)
28 std::vector<std::string> availableLanguages;
29#if TESSERACT_MAJOR_VERSION < 5
30 GenericVector<STRING> languageVector;
31 m_tesseract->GetAvailableLanguagesAsVector(&languageVector);
32 for (
int i = 0; i < languageVector.size(); i++) {
33 availableLanguages.push_back(languageVector[i].c_str());
36 m_tesseract->GetAvailableLanguagesAsVector(&availableLanguages);
39 m_languages->setLanguages(availableLanguages);
52QRect OCS::area()
const
57bool OCS::autoRead()
const
62void OCS::setAutoRead(
bool value)
64 if(m_autoRead == value)
71void OCS::setBoxesType(OCS::BoxesType types)
73 if(m_boxesTypes == types)
78 qDebug() <<
"Setting the boxes types" << m_boxesTypes << types;
83void OCS::setConfidenceThreshold(
float value)
85 if(m_confidenceThreshold == value)
88 m_confidenceThreshold = value;
89 Q_EMIT confidenceThresholdChanged();
92int OCS::wordBoxAt(
const QPoint point)
95 for(
const auto &box : m_wordBoxes)
97 QRect rect = box[
"rect"].toRect();
99 qDebug() <<
"Rect: " << rect <<
"Point: " << point << rect.
contains(point,
true);
114 for(
const auto &box : m_wordBoxes)
116 QRect rect_o = box[
"rect"].toRect();
127void OCS::setWhiteList(
const QString &value)
129 if(value == m_whiteList)
133 Q_EMIT whiteListChanged();
136void OCS::setBlackList(
const QString &value)
138 if(value == m_blackList)
142 Q_EMIT blackListChanged();
145void OCS::setPreprocessImage(
bool value)
147 if(m_preprocessImage == value)
150 m_preprocessImage = value;
152 Q_EMIT preprocessImageChanged();
155void OCS::setPageSegMode(PageSegMode value)
157 if(m_segMode == value)
161 Q_EMIT pageSegModeChanged();
169void OCS::do_preprocessImage(
const QImage &image)
175static tesseract::PageSegMode mapPageSegValue(OCS::PageSegMode value)
180 case OCS::PageSegMode::Auto:
return tesseract::PageSegMode::PSM_AUTO;
181 case OCS::PageSegMode::Auto_OSD:
return tesseract::PageSegMode::PSM_AUTO_OSD;
182 case OCS::PageSegMode::SingleColumn:
return tesseract::PageSegMode::PSM_SINGLE_COLUMN;
183 case OCS::PageSegMode::SingleLine:
return tesseract::PageSegMode::PSM_SINGLE_LINE;
184 case OCS::PageSegMode::SingleBlock:
return tesseract::PageSegMode::PSM_SINGLE_BLOCK;
185 case OCS::PageSegMode::SingleWord:
return tesseract::PageSegMode::PSM_SINGLE_WORD;
189void OCS::getTextAsync()
195 qDebug() <<
"URL is not local :: OCR";
199 typedef QMap<BoxType, TextBoxes> Res;
200 auto func = [ocs =
this](QUrl url, BoxesType levels) -> Res
202 tesseract::TessBaseAPI *api =
new tesseract::TessBaseAPI();
203 api->Init(NULL,
"eng");
205 api->SetVariable(
"tessedit_char_whitelist",
206 ocs->m_whiteList.toStdString().c_str());
207 api->SetVariable(
"tessedit_char_blacklist",
208 ocs->m_blackList.toStdString().c_str());
210 api->SetPageSegMode(mapPageSegValue(ocs->m_segMode));
212 if(ocs->m_preprocessImage)
214 auto var =
new QImage(url.toLocalFile());
215 auto m_imgMat = ConvertImage::qimageToMatRef(*var, CV_8UC4);
218 PreprocessImage::adaptThreshold(m_imgMat,
false, 3, 1);
222 api->SetImage(m_ocrImg.bits(), m_ocrImg.width(), m_ocrImg.height(), 4, m_ocrImg.bytesPerLine());
228 ocs->m_ocrImg =
new QImage(url.toLocalFile());
229 api->SetImage(ocs->m_ocrImg->bits(), ocs->m_ocrImg->width(), ocs->m_ocrImg->height(), 4,
230 ocs->m_ocrImg->bytesPerLine());
233 api->SetSourceResolution(200);
237 TextBoxes wordBoxes, lineBoxes, paragraphBoxes;
239 auto levelFunc = [ocs](tesseract::TessBaseAPI *api, tesseract::PageIteratorLevel
level) -> TextBoxes
242 tesseract::ResultIterator* ri = api->GetIterator();
245 qDebug() <<
"Getting text for level" <<
level;
248 const char* word = ri->GetUTF8Text(level);
249 float conf = ri->Confidence(level);
251 ri->BoundingBox(level, &x1, &y1, &x2, &y2);
253 printf(
"word: '%s'; \tconf: %.2f; BoundingBox: %d,%d,%d,%d;\n",
254 word, conf, x1, y1, x2, y2);
256 if(conf > ocs->m_confidenceThreshold && !isspace(*word))
259 }
while (ri->Next(level));
265 if(levels.testFlag(Word))
266 wordBoxes = levelFunc(api, tesseract::RIL_WORD);
268 if(levels.testFlag(Line))
269 lineBoxes = levelFunc(api, tesseract::RIL_TEXTLINE);
271 if(levels.testFlag(Paragraph))
272 paragraphBoxes = levelFunc(api, tesseract::RIL_PARA);
277 return Res{{Word, wordBoxes}, {Line, lineBoxes}, {Paragraph, paragraphBoxes}};
280 qDebug() <<
"GEtting text for boxes " << m_boxesTypes << m_boxesTypes.testFlag(Word);
282 if(OCRCache.contains(m_filePath))
284 qDebug() <<
"OCR retrieved from cached";
285 auto res = OCRCache[m_filePath];
286 m_wordBoxes = res[Word];
287 m_lineBoxes = res[Line];
288 m_paragraphBoxes = res[Paragraph];
289 Q_EMIT wordBoxesChanged();
290 Q_EMIT lineBoxesChanged();
291 Q_EMIT paragraphBoxesChanged();
296 auto watcher =
new QFutureWatcher<Res>;
300 auto res = watcher->result();
301 m_wordBoxes = res[Word];
302 m_lineBoxes = res[Line];
303 m_paragraphBoxes = res[Paragraph];
304 Q_EMIT wordBoxesChanged();
305 Q_EMIT lineBoxesChanged();
306 Q_EMIT paragraphBoxesChanged();
310 OCRCache.insert(m_filePath, res);
312 watcher->deleteLater();
316 watcher->setFuture(future);
323 if(!url.isLocalFile())
325 qDebug() <<
"URL is not local :: OCR";
329 if (m_tesseract->Init(
nullptr, m_languages->getLanguagesString().c_str()))
331 qDebug() <<
"Failed tesseract OCR init";
335 m_tesseract->SetPageSegMode(tesseract::PSM_AUTO);
339 if(!m_area.isEmpty())
341 QImage img(url.toLocalFile());
342 img = img.copy(m_area);
345 m_tesseract->SetImage(img.bits(), img.width(), img.height(), 4, img.bytesPerLine());
349 Pix* im = pixRead(url.toLocalFile().toStdString().c_str());
350 m_tesseract->SetImage(im);
358void OCS::setFilePath(
QString filePath)
360 if (m_filePath == filePath)
363 m_filePath = filePath;
364 Q_EMIT filePathChanged(m_filePath);
367void OCS::setArea(
QRect area)
373 Q_EMIT areaChanged(m_area);
377TextBoxes OCS::wordBoxes()
const
382TextBoxes OCS::paragraphBoxes()
const
384 return m_paragraphBoxes;
387TextBoxes OCS::lineBoxes()
const
392OCS::BoxesType OCS::boxesType()
397float OCS::confidenceThreshold()
399 return m_confidenceThreshold;
412OCS::PageSegMode OCS::pageSegMode()
const
417bool OCS::preprocessImage()
const
419 return m_preprocessImage;
422void OCS::classBegin()
426void OCS::componentComplete()
428 qDebug() <<
"OCS CALSS COMPLETED IN QML";
429 connect(
this, &OCS::filePathChanged, [
this](QString)
439bool OCS::ready()
const
QStringView level(QStringView ifopt)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool contains(const QPoint &point, bool proper) const const
bool intersects(const QRect &rectangle) const const
QString fromStdString(const std::string &str)
QFuture< T > run(Function function,...)