ocr-stream 2.3__tar.gz → 2.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ocr_stream-2.3 → ocr_stream-2.3.3}/PKG-INFO +2 -2
- ocr_stream-2.3.3/ocr_stream/__init__.py +316 -0
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream/bin_tess/__init__.py +2 -1
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream/modules/__init__.py +8 -2
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream/ocr/__init__.py +16 -23
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream/recognize.py +4 -2
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream.egg-info/PKG-INFO +2 -2
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream.egg-info/requires.txt +1 -1
- {ocr_stream-2.3 → ocr_stream-2.3.3}/pyproject.toml +2 -2
- ocr_stream-2.3/ocr_stream/__init__.py +0 -51
- {ocr_stream-2.3 → ocr_stream-2.3.3}/README.md +0 -0
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream.egg-info/SOURCES.txt +0 -0
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream.egg-info/dependency_links.txt +0 -0
- {ocr_stream-2.3 → ocr_stream-2.3.3}/ocr_stream.egg-info/top_level.txt +0 -0
- {ocr_stream-2.3 → ocr_stream-2.3.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ocr_stream
|
|
3
|
-
Version: 2.3
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: Extrair textos de documentos digitalizados e imagens.
|
|
5
5
|
Author: Bruno Chaves
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -12,7 +12,7 @@ Requires-Dist: pandas
|
|
|
12
12
|
Requires-Dist: requests>=2.32.4
|
|
13
13
|
Requires-Dist: opencv-python
|
|
14
14
|
Requires-Dist: pytesseract
|
|
15
|
-
Requires-Dist: convert_stream>=2.3
|
|
15
|
+
Requires-Dist: convert_stream>=2.3.3
|
|
16
16
|
Requires-Dist: soup_files>=1.4
|
|
17
17
|
|
|
18
18
|
# ocrlib
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from ocr_stream.modules import *
|
|
5
|
+
from ocr_stream.bin_tess import BinTesseract, get_path_tesseract_sys
|
|
6
|
+
from ocr_stream.ocr import TesseractOcr, TextRecognized
|
|
7
|
+
from ocr_stream.recognize import RecognizeImage, RecognizePdf
|
|
8
|
+
from convert_stream import DocumentPdf
|
|
9
|
+
from convert_stream.image import ImageObject
|
|
10
|
+
from convert_stream.mod_types import (
|
|
11
|
+
ColumnsTable, ColumnBody, HeadCell, HeadValues, create_void_table, create_void_map
|
|
12
|
+
)
|
|
13
|
+
from convert_stream.text import FindText
|
|
14
|
+
from convert_stream.pdf import ConvertPdfToImages
|
|
15
|
+
import pandas as pd
|
|
16
|
+
from soup_files import (
|
|
17
|
+
JsonConvert, ProgressBarAdapter, CreatePbar, Directory, File, InputFiles, LibraryDocs
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def read_images(
|
|
22
|
+
images: list[ImageObject], *,
|
|
23
|
+
pbar: ProgressBarAdapter = CreatePbar().get()
|
|
24
|
+
) -> DocumentPdf:
|
|
25
|
+
pbar.start()
|
|
26
|
+
print()
|
|
27
|
+
max_num = len(images)
|
|
28
|
+
pages = []
|
|
29
|
+
rec = RecognizeImage()
|
|
30
|
+
for idx, image in enumerate(images):
|
|
31
|
+
pbar.update(
|
|
32
|
+
((idx + 1) / max_num) * 100,
|
|
33
|
+
f'[OCR] {idx + 1} / {max_num}',
|
|
34
|
+
)
|
|
35
|
+
out = rec.image_recognize(image).to_page_pdf()
|
|
36
|
+
pages.append(out)
|
|
37
|
+
tmp_doc = DocumentPdf.create_from_pages(pages)
|
|
38
|
+
print()
|
|
39
|
+
pbar.stop()
|
|
40
|
+
return tmp_doc
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def read_document(
|
|
44
|
+
document: DocumentPdf, *,
|
|
45
|
+
dpi: int = 300,
|
|
46
|
+
pbar: ProgressBarAdapter = CreatePbar().get()
|
|
47
|
+
) -> DocumentPdf:
|
|
48
|
+
pbar.start()
|
|
49
|
+
print()
|
|
50
|
+
convert = ConvertPdfToImages.create(document)
|
|
51
|
+
convert.set_pbar(pbar)
|
|
52
|
+
images = convert.to_images(dpi=dpi)
|
|
53
|
+
print()
|
|
54
|
+
return read_images(images, pbar=pbar)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SearchableTextImage(object):
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
create_void_map() -> dict[str, ColumnBody]:
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
ColumnsTable.KEY.value: ColumnBody(ColumnsTable.KEY.value, ListString([])),
|
|
64
|
+
ColumnsTable.NUM_PAGE.value: ColumnBody(ColumnsTable.NUM_PAGE.value, ListString([])),
|
|
65
|
+
ColumnsTable.NUM_LINE.value: ColumnBody(ColumnsTable.NUM_LINE.value, ListString([])),
|
|
66
|
+
ColumnsTable.TEXT.value: ColumnBody(ColumnsTable.TEXT.value, ListString([])),
|
|
67
|
+
ColumnsTable.FILE_NAME.value: ColumnBody(ColumnsTable.FILE_NAME.value, ListString([])),
|
|
68
|
+
ColumnsTable.FILETYPE.value: ColumnBody(ColumnsTable.FILETYPE.value, ListString([])),
|
|
69
|
+
ColumnsTable.FILE_PATH.value: ColumnBody(ColumnsTable.FILE_PATH.value, ListString([])),
|
|
70
|
+
ColumnsTable.DIR.value: ColumnBody(ColumnsTable.DIR.value, ListString([])),
|
|
71
|
+
}
|
|
72
|
+
"""
|
|
73
|
+
def __init__(self, silent: bool = False):
|
|
74
|
+
self.silent = silent
|
|
75
|
+
self.elements: dict[str, ColumnBody] = create_void_map()
|
|
76
|
+
|
|
77
|
+
def __repr__(self):
|
|
78
|
+
return f'SearchableTextPdf: {self.elements}'
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def length(self) -> int:
|
|
82
|
+
return len(self.elements[ColumnsTable.TEXT.value])
|
|
83
|
+
|
|
84
|
+
def is_empty(self) -> bool:
|
|
85
|
+
return len(self.elements[ColumnsTable.TEXT.value]) == 0
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def columns(self) -> HeadValues:
|
|
89
|
+
return HeadValues([HeadCell(x) for x in list(self.elements.keys())])
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def first(self) -> dict[str, str]:
|
|
93
|
+
if self.is_empty():
|
|
94
|
+
return {}
|
|
95
|
+
cols = self.columns
|
|
96
|
+
_first = {}
|
|
97
|
+
for c in cols:
|
|
98
|
+
_first[c] = self.elements[c][0]
|
|
99
|
+
return _first
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def last(self) -> dict[str, str]:
|
|
103
|
+
if self.is_empty():
|
|
104
|
+
return {}
|
|
105
|
+
if self.is_empty():
|
|
106
|
+
return {}
|
|
107
|
+
cols = self.columns
|
|
108
|
+
_last = {}
|
|
109
|
+
for c in cols:
|
|
110
|
+
_last[c] = self.elements[c][-1]
|
|
111
|
+
return _last
|
|
112
|
+
|
|
113
|
+
def get_item(self, idx: int) -> dict[str, str]:
|
|
114
|
+
_current_item = {}
|
|
115
|
+
cols = self.columns
|
|
116
|
+
try:
|
|
117
|
+
for col in cols:
|
|
118
|
+
_current_item[col] = self.elements[col][idx]
|
|
119
|
+
except Exception as err:
|
|
120
|
+
if not self.silent:
|
|
121
|
+
print(err)
|
|
122
|
+
return _current_item
|
|
123
|
+
else:
|
|
124
|
+
return _current_item
|
|
125
|
+
|
|
126
|
+
def add_line(self, line: dict[str, str]) -> None:
|
|
127
|
+
cols: HeadValues = self.columns
|
|
128
|
+
cols_line: HeadValues = HeadValues([HeadCell(y) for y in list(line.keys())])
|
|
129
|
+
for col in cols:
|
|
130
|
+
if cols_line.contains(col, case=True, iqual=True):
|
|
131
|
+
self.elements[col].append(line[col])
|
|
132
|
+
|
|
133
|
+
def clear(self) -> None:
|
|
134
|
+
for _k in self.elements.keys():
|
|
135
|
+
self.elements[_k].clear()
|
|
136
|
+
|
|
137
|
+
def to_string(self) -> str:
|
|
138
|
+
"""
|
|
139
|
+
Retorna o texto da coluna TEXT em formato de string
|
|
140
|
+
ou 'nas' em caso de erro nas = Not a String
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
return ' '.join(self.elements[ColumnsTable.TEXT.value])
|
|
144
|
+
except Exception as e:
|
|
145
|
+
print(e)
|
|
146
|
+
return 'nas'
|
|
147
|
+
|
|
148
|
+
def to_data_frame(self) -> pd.DataFrame:
|
|
149
|
+
return pd.DataFrame.from_dict(self.elements)
|
|
150
|
+
|
|
151
|
+
def to_file_json(self, file: File):
|
|
152
|
+
"""Exporta os dados da busca para um arquivo .JSON"""
|
|
153
|
+
dt = JsonConvert.from_dict(self.elements).to_json_data()
|
|
154
|
+
dt.to_file(file)
|
|
155
|
+
|
|
156
|
+
def to_file_excel(self, file: File):
|
|
157
|
+
"""Exporta os dados da busca para um arquivo .XLSX"""
|
|
158
|
+
self.to_data_frame().to_excel(file.absolute(), index=False)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class ImageFinder(object):
|
|
162
|
+
"""
|
|
163
|
+
Classe para Filtrar texto em Imagens
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def __init__(self):
|
|
167
|
+
self.docs_collection: dict[str, ImageObject] = {}
|
|
168
|
+
self.recognize: RecognizeImage = RecognizeImage()
|
|
169
|
+
self.pbar: ProgressBarAdapter = ProgressBarAdapter()
|
|
170
|
+
|
|
171
|
+
def is_empty(self) -> bool:
|
|
172
|
+
return len(self.docs_collection) == 0
|
|
173
|
+
|
|
174
|
+
def clear(self) -> None:
|
|
175
|
+
self.docs_collection.clear()
|
|
176
|
+
|
|
177
|
+
def add_image(self, img: File | bytes | BytesIO | str | ImageObject) -> None:
|
|
178
|
+
if isinstance(img, File):
|
|
179
|
+
img = ImageObject(img)
|
|
180
|
+
elif isinstance(img, ImageObject):
|
|
181
|
+
pass
|
|
182
|
+
elif isinstance(img, bytes):
|
|
183
|
+
img = ImageObject(img)
|
|
184
|
+
elif isinstance(img, str):
|
|
185
|
+
img = ImageObject(img)
|
|
186
|
+
elif isinstance(img, BytesIO):
|
|
187
|
+
img = ImageObject(img)
|
|
188
|
+
|
|
189
|
+
if img.metadata['file_path'] is not None:
|
|
190
|
+
self.docs_collection[img.metadata['file_path']] = img
|
|
191
|
+
else:
|
|
192
|
+
self.docs_collection[img.metadata['name']] = img
|
|
193
|
+
|
|
194
|
+
def add_images(self, images: list[File] | list[ImageObject] | list[bytes] | list[BytesIO]) -> None:
|
|
195
|
+
for f in images:
|
|
196
|
+
self.add_image(f)
|
|
197
|
+
|
|
198
|
+
def add_directory_images(self, dir_pdf: Directory) -> None:
|
|
199
|
+
files = InputFiles(dir_pdf).get_files(file_type=LibraryDocs.IMAGE)
|
|
200
|
+
if len(files) > 0:
|
|
201
|
+
self.add_images(files)
|
|
202
|
+
|
|
203
|
+
def find(
|
|
204
|
+
self, text: str,
|
|
205
|
+
separator: str = '\n',
|
|
206
|
+
iqual: bool = False,
|
|
207
|
+
case: bool = False,
|
|
208
|
+
silent: bool = False,
|
|
209
|
+
) -> SearchableTextImage:
|
|
210
|
+
"""
|
|
211
|
+
Filtrar texto retornando a primeira ocorrência do Documento PDF.
|
|
212
|
+
"""
|
|
213
|
+
_searchable = SearchableTextImage(silent)
|
|
214
|
+
if self.is_empty():
|
|
215
|
+
return _searchable
|
|
216
|
+
|
|
217
|
+
total_num: int = len(self.docs_collection.keys())
|
|
218
|
+
for n, file_key in enumerate(self.docs_collection.keys()):
|
|
219
|
+
self.pbar.update(
|
|
220
|
+
((n + 1) / total_num) * 100,
|
|
221
|
+
f'[PESQUISANDO]: {n+1}/{total_num}',
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
current_img: ImageObject = self.docs_collection[file_key]
|
|
225
|
+
txt_in_img = self.recognize.image_to_string(current_img)
|
|
226
|
+
if (txt_in_img == '') or (txt_in_img is None):
|
|
227
|
+
self.pbar.update_text(f'ERRO: {n+1} {current_img.metadata["name"]}')
|
|
228
|
+
continue
|
|
229
|
+
try:
|
|
230
|
+
fd = FindText(txt_in_img, separator=separator)
|
|
231
|
+
idx = fd.find_index(text, iqual=iqual, case=case)
|
|
232
|
+
if idx is None:
|
|
233
|
+
continue
|
|
234
|
+
math_text: str | None = fd.get_index(idx)
|
|
235
|
+
except Exception as err:
|
|
236
|
+
print(f'{__class__.__name__} {err}')
|
|
237
|
+
else:
|
|
238
|
+
file_path = current_img.metadata["file_path"]
|
|
239
|
+
file_name = current_img.metadata["name"]
|
|
240
|
+
dir_path = current_img.metadata["dir_path"]
|
|
241
|
+
file_type = current_img.metadata["extension"]
|
|
242
|
+
|
|
243
|
+
new_line: dict[str, str] = {
|
|
244
|
+
ColumnsTable.KEY.value: f'{idx}',
|
|
245
|
+
ColumnsTable.NUM_PAGE.value: 'nan',
|
|
246
|
+
ColumnsTable.NUM_LINE.value: f'{idx + 1}',
|
|
247
|
+
ColumnsTable.TEXT.value: math_text,
|
|
248
|
+
ColumnsTable.FILE_NAME.value: file_name if file_path is not None else 'nan',
|
|
249
|
+
ColumnsTable.FILETYPE.value: file_type if file_path is not None else 'nan',
|
|
250
|
+
ColumnsTable.FILE_PATH.value: file_path if file_path is not None else 'nan',
|
|
251
|
+
ColumnsTable.DIR.value: dir_path if file_path is not None else 'nan',
|
|
252
|
+
}
|
|
253
|
+
_searchable.add_line(new_line)
|
|
254
|
+
return _searchable
|
|
255
|
+
return _searchable
|
|
256
|
+
|
|
257
|
+
def find_all(
|
|
258
|
+
self, text: str,
|
|
259
|
+
separator: str = '\n',
|
|
260
|
+
iqual: bool = False,
|
|
261
|
+
case: bool = False,
|
|
262
|
+
silent: bool = False,
|
|
263
|
+
) -> SearchableTextImage:
|
|
264
|
+
"""
|
|
265
|
+
Filtrar texto em documento PDF e retorna todas as ocorrências do texto
|
|
266
|
+
encontradas no documento, incluindo o número da linha, página e nome do arquivo
|
|
267
|
+
em cada ocorrência.
|
|
268
|
+
"""
|
|
269
|
+
_searchable = SearchableTextImage(silent)
|
|
270
|
+
if self.is_empty():
|
|
271
|
+
return _searchable
|
|
272
|
+
|
|
273
|
+
total_num: int = len(self.docs_collection.keys())
|
|
274
|
+
for n, file_key in enumerate(self.docs_collection.keys()):
|
|
275
|
+
self.pbar.update(
|
|
276
|
+
((n + 1) / total_num) * 100,
|
|
277
|
+
f'[PESQUISANDO]: {n + 1}/{total_num}',
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
current_img: ImageObject = self.docs_collection[file_key]
|
|
281
|
+
txt_in_img = self.recognize.image_to_string(current_img)
|
|
282
|
+
if (txt_in_img == '') or (txt_in_img is None):
|
|
283
|
+
self.pbar.update_text(f'ERRO: {n + 1} {current_img.metadata["name"]}')
|
|
284
|
+
continue
|
|
285
|
+
try:
|
|
286
|
+
fd = FindText(txt_in_img, separator=separator)
|
|
287
|
+
idx = fd.find_index(text, iqual=iqual, case=case)
|
|
288
|
+
if idx is None:
|
|
289
|
+
continue
|
|
290
|
+
math_text: str | None = fd.get_index(idx)
|
|
291
|
+
except Exception as err:
|
|
292
|
+
print(f'{__class__.__name__} {err}')
|
|
293
|
+
else:
|
|
294
|
+
file_path = current_img.metadata["file_path"]
|
|
295
|
+
file_name = current_img.metadata["name"]
|
|
296
|
+
dir_path = current_img.metadata["dir_path"]
|
|
297
|
+
file_type = current_img.metadata["extension"]
|
|
298
|
+
|
|
299
|
+
new_line: dict[str, str] = {
|
|
300
|
+
ColumnsTable.KEY.value: f'{idx}',
|
|
301
|
+
ColumnsTable.NUM_PAGE.value: 'nan',
|
|
302
|
+
ColumnsTable.NUM_LINE.value: f'{idx + 1}',
|
|
303
|
+
ColumnsTable.TEXT.value: math_text,
|
|
304
|
+
ColumnsTable.FILE_NAME.value: file_name if file_path is not None else 'nan',
|
|
305
|
+
ColumnsTable.FILETYPE.value: file_type if file_path is not None else 'nan',
|
|
306
|
+
ColumnsTable.FILE_PATH.value: file_path if file_path is not None else 'nan',
|
|
307
|
+
ColumnsTable.DIR.value: dir_path if file_path is not None else 'nan',
|
|
308
|
+
}
|
|
309
|
+
_searchable.add_line(new_line)
|
|
310
|
+
return _searchable
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
__all__ = [
|
|
314
|
+
'ImageFinder', 'SearchableTextImage', 'read_images',
|
|
315
|
+
'read_document', 'RecognizeImage', 'RecognizePdf'
|
|
316
|
+
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import os
|
|
4
4
|
from shutil import which
|
|
5
|
-
from soup_files import File,
|
|
5
|
+
from soup_files import File, KERNEL_TYPE
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def __get_path_tesseract_unix() -> File | None:
|
|
@@ -95,3 +95,4 @@ class BinTesseract(object):
|
|
|
95
95
|
return self.__path_tesseract.exists()
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
__all__ = ['BinTesseract', 'get_path_tesseract_sys']
|
|
@@ -17,7 +17,8 @@ MOD_PYOCR: bool = False
|
|
|
17
17
|
try:
|
|
18
18
|
from pytesseract import pytesseract, Output
|
|
19
19
|
MOD_PYTESSERACT = True
|
|
20
|
-
except:
|
|
20
|
+
except Exception as err:
|
|
21
|
+
print(err)
|
|
21
22
|
pytesseract = None
|
|
22
23
|
Output = None
|
|
23
24
|
|
|
@@ -25,7 +26,8 @@ try:
|
|
|
25
26
|
import pyocr
|
|
26
27
|
import pyocr.tesseract
|
|
27
28
|
MOD_PYOCR = True
|
|
28
|
-
except:
|
|
29
|
+
except Exception as err:
|
|
30
|
+
print(err)
|
|
29
31
|
pyocr = object
|
|
30
32
|
#pyocr.tesseract = object
|
|
31
33
|
|
|
@@ -41,3 +43,7 @@ elif MOD_PYOCR:
|
|
|
41
43
|
else:
|
|
42
44
|
ModuleOcr: TypeAlias = None
|
|
43
45
|
DEFAULT_LIB_OCR = LibOcr.NOT_IMPLEMENTED
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = ['LibOcr', 'DEFAULT_LIB_OCR']
|
|
49
|
+
|
|
@@ -6,20 +6,21 @@ from abc import abstractmethod, ABC
|
|
|
6
6
|
from typing import Union
|
|
7
7
|
from io import BytesIO
|
|
8
8
|
from pandas import DataFrame
|
|
9
|
-
from soup_files import File
|
|
10
|
-
from convert_stream import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
from soup_files import File
|
|
10
|
+
from convert_stream import DocumentPdf, PageDocumentPdf
|
|
11
|
+
from convert_stream.mod_types.modules import DEFAULT_LIB_IMAGE, LibImage
|
|
12
|
+
from convert_stream.image import ImageObject
|
|
13
|
+
from convert_stream.mod_types import get_void_metadata
|
|
14
|
+
from convert_stream.mod_types.table_types import ColumnBody
|
|
14
15
|
from ocr_stream.modules import (
|
|
15
|
-
LibOcr,
|
|
16
|
-
pytesseract, Output, pyocr,
|
|
16
|
+
LibOcr, DEFAULT_LIB_OCR, pytesseract, Output, pyocr,
|
|
17
17
|
)
|
|
18
18
|
from ocr_stream.bin_tess import BinTesseract
|
|
19
19
|
|
|
20
20
|
try:
|
|
21
21
|
import pyocr.tesseract
|
|
22
|
-
except:
|
|
22
|
+
except Exception as err:
|
|
23
|
+
print(err)
|
|
23
24
|
pyocr = None
|
|
24
25
|
|
|
25
26
|
|
|
@@ -30,6 +31,7 @@ class TextRecognized(object):
|
|
|
30
31
|
"""
|
|
31
32
|
|
|
32
33
|
def __init__(self, bytes_recognized: bytes):
|
|
34
|
+
self.metadata: dict[str, str | None] = get_void_metadata()
|
|
33
35
|
self.bytes_recognized: bytes = bytes_recognized
|
|
34
36
|
self.list_bad_char: list[str] = [
|
|
35
37
|
':', ',', ';', '$', '=',
|
|
@@ -51,21 +53,11 @@ class TextRecognized(object):
|
|
|
51
53
|
def to_document(self) -> DocumentPdf:
|
|
52
54
|
return DocumentPdf(BytesIO(self.bytes_recognized))
|
|
53
55
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
if (text is None) or (text == '') or (text == 'nas'):
|
|
57
|
-
return MapText([])
|
|
58
|
-
try:
|
|
59
|
-
return MapText(text.split(separator))
|
|
60
|
-
except Exception as err:
|
|
61
|
-
print(err)
|
|
62
|
-
return MapText([])
|
|
63
|
-
|
|
64
|
-
def to_dict(self, separator: str = '\n') -> dict[str, list[str]]:
|
|
65
|
-
return self.to_map(separator).map_elements
|
|
56
|
+
def to_dict(self, separator: str = '\n') -> dict[str, ColumnBody]:
|
|
57
|
+
return self.to_page_pdf().to_dict(separator=separator)
|
|
66
58
|
|
|
67
59
|
def to_dataframe(self, separator='\n') -> DataFrame:
|
|
68
|
-
return self.
|
|
60
|
+
return DataFrame.from_dict(self.to_dict(separator=separator))
|
|
69
61
|
|
|
70
62
|
|
|
71
63
|
class ABCOcrTesseract(ABC):
|
|
@@ -166,8 +158,6 @@ class IPyOcr(ABCOcrTesseract):
|
|
|
166
158
|
self.lang = lang
|
|
167
159
|
else:
|
|
168
160
|
self.lang = langs[0]
|
|
169
|
-
|
|
170
|
-
# Ex: Will use tool 'libtesseract'
|
|
171
161
|
print(f"Will use tool {self._pyOcr.get_name()}")
|
|
172
162
|
|
|
173
163
|
def to_string(self, img: Union[File, ImageObject]) -> str:
|
|
@@ -215,3 +205,6 @@ class TesseractOcr(object):
|
|
|
215
205
|
else:
|
|
216
206
|
raise ValueError(f'{lib_ocr} is not supported')
|
|
217
207
|
return cls(md)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
__all__ = ['TesseractOcr', 'TextRecognized']
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
from __future__ import annotations
|
|
3
|
-
from convert_stream import
|
|
3
|
+
from convert_stream import DocumentPdf, PageDocumentPdf
|
|
4
|
+
from convert_stream.pdf import ConvertPdfToImages
|
|
5
|
+
from convert_stream.image import ImageObject
|
|
4
6
|
from ocr_stream.bin_tess import BinTesseract
|
|
5
|
-
from ocr_stream.modules import LibOcr, DEFAULT_LIB_OCR
|
|
7
|
+
from ocr_stream.modules import LibOcr, DEFAULT_LIB_OCR
|
|
6
8
|
from ocr_stream.ocr import TesseractOcr, TextRecognized
|
|
7
9
|
from pandas import DataFrame
|
|
8
10
|
from soup_files import File, ProgressBarAdapter, CreatePbar
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ocr_stream
|
|
3
|
-
Version: 2.3
|
|
3
|
+
Version: 2.3.3
|
|
4
4
|
Summary: Extrair textos de documentos digitalizados e imagens.
|
|
5
5
|
Author: Bruno Chaves
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -12,7 +12,7 @@ Requires-Dist: pandas
|
|
|
12
12
|
Requires-Dist: requests>=2.32.4
|
|
13
13
|
Requires-Dist: opencv-python
|
|
14
14
|
Requires-Dist: pytesseract
|
|
15
|
-
Requires-Dist: convert_stream>=2.3
|
|
15
|
+
Requires-Dist: convert_stream>=2.3.3
|
|
16
16
|
Requires-Dist: soup_files>=1.4
|
|
17
17
|
|
|
18
18
|
# ocrlib
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ocr_stream"
|
|
7
|
-
version = "2.3"
|
|
7
|
+
version = "2.3.3"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Bruno Chaves"},
|
|
10
10
|
]
|
|
@@ -21,7 +21,7 @@ dependencies = [
|
|
|
21
21
|
"requests>=2.32.4",
|
|
22
22
|
"opencv-python",
|
|
23
23
|
"pytesseract",
|
|
24
|
-
"convert_stream>=2.3",
|
|
24
|
+
"convert_stream>=2.3.3",
|
|
25
25
|
"soup_files>=1.4",
|
|
26
26
|
]
|
|
27
27
|
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
from ocr_stream.modules import *
|
|
4
|
-
from ocr_stream.bin_tess import BinTesseract, get_path_tesseract_sys
|
|
5
|
-
from ocr_stream.ocr import TesseractOcr, TextRecognized, DEFAULT_LIB_OCR
|
|
6
|
-
from ocr_stream.recognize import RecognizeImage, RecognizePdf
|
|
7
|
-
from soup_files import File, Directory, InputFiles, LibraryDocs, CreatePbar, ProgressBarAdapter
|
|
8
|
-
from convert_stream import ImageObject, PdfStream, DocumentPdf, ConvertPdfToImages
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def read_images(
|
|
12
|
-
images: list[ImageObject], *,
|
|
13
|
-
pbar: ProgressBarAdapter = CreatePbar().get()
|
|
14
|
-
) -> DocumentPdf:
|
|
15
|
-
pbar.start()
|
|
16
|
-
print()
|
|
17
|
-
max_num = len(images)
|
|
18
|
-
pages = []
|
|
19
|
-
rec = RecognizeImage()
|
|
20
|
-
for idx, image in enumerate(images):
|
|
21
|
-
pbar.update(
|
|
22
|
-
((idx + 1) / max_num) * 100,
|
|
23
|
-
f'[OCR] {idx + 1} / {max_num}',
|
|
24
|
-
)
|
|
25
|
-
out = rec.image_recognize(image).to_page_pdf()
|
|
26
|
-
pages.append(out)
|
|
27
|
-
tmp_doc = DocumentPdf.create_from_pages(pages)
|
|
28
|
-
print()
|
|
29
|
-
pbar.stop()
|
|
30
|
-
return tmp_doc
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def read_document(
|
|
34
|
-
document: DocumentPdf, *,
|
|
35
|
-
dpi: int = 300,
|
|
36
|
-
pbar: ProgressBarAdapter = CreatePbar().get()
|
|
37
|
-
) -> DocumentPdf:
|
|
38
|
-
pbar.start()
|
|
39
|
-
print()
|
|
40
|
-
convert = ConvertPdfToImages.create(document)
|
|
41
|
-
convert.set_pbar(pbar)
|
|
42
|
-
images = convert.to_images(dpi=dpi)
|
|
43
|
-
print()
|
|
44
|
-
return read_images(images, pbar=pbar)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|