ocr-stream 2.3.2__tar.gz → 2.4.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ocr_stream
3
- Version: 2.3.2
3
+ Version: 2.4.1
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.2
15
+ Requires-Dist: convert_stream>=2.4.1
16
16
  Requires-Dist: soup_files>=1.4
17
17
 
18
18
  # ocrlib
@@ -0,0 +1,322 @@
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 (
8
+ RecognizeImage, RecognizePdf, VoidPbar, ProgressVoid
9
+ )
10
+ import convert_stream as cs
11
+ import pandas as pd
12
+ import soup_files as sp
13
+
14
+ __version__ = '2.4'
15
+
16
+
17
+ def read_image(img: cs.ImageObject, rec: RecognizeImage = RecognizeImage()) -> TextRecognized:
18
+ _txt = rec.image_recognize(img)
19
+ _txt.metadata = img.metadata
20
+ return _txt
21
+
22
+
23
+ def read_images(
24
+ images: list[cs.ImageObject], *,
25
+ rec: RecognizeImage = RecognizeImage(),
26
+ pbar: sp.ProgressBarAdapter = sp.CreatePbar().get()
27
+ ) -> cs.DocumentPdf:
28
+ pbar.start()
29
+ print()
30
+ max_num = len(images)
31
+ pages = []
32
+
33
+ for idx, image in enumerate(images):
34
+ pbar.update(
35
+ ((idx + 1) / max_num) * 100,
36
+ f'[OCR IMAGENS] {idx + 1} / {max_num}',
37
+ )
38
+ out = read_image(image, rec=rec).to_page_pdf()
39
+ pages.append(out)
40
+ tmp_doc = cs.DocumentPdf.create_from_pages(pages)
41
+ print()
42
+ pbar.stop()
43
+ return tmp_doc
44
+
45
+
46
+ def read_document(
47
+ document: cs.DocumentPdf, *,
48
+ dpi: int = 300,
49
+ pbar: sp.ProgressBarAdapter = sp.CreatePbar().get()
50
+ ) -> cs.DocumentPdf:
51
+ pbar.start()
52
+ print()
53
+ _metadata: cs.MetaDataFile = document.metadata
54
+ recognized_items: list[TextRecognized] = []
55
+ pages_pdf: list[cs.PageDocumentPdf] = document.to_pages()
56
+ total = len(pages_pdf)
57
+ void_pbar = ProgressVoid()
58
+ for num, page in enumerate(pages_pdf):
59
+ pbar.update(
60
+ ((num + 1) / total) * 100,
61
+ f'[OCR PDF] {num + 1}/{total} {page.metadata.name}',
62
+ )
63
+ convert: cs.ConvertPdfToImages = cs.ConvertPdfToImages(cs.DocumentPdf.create_from_pages([page]))
64
+ convert.set_pbar(void_pbar)
65
+ images: list[cs.ImageObject] = convert.to_images(dpi=dpi)
66
+ for im in images:
67
+ recognized_items.append(read_image(im))
68
+ print()
69
+ final_pages: list[cs.PageDocumentPdf] = [x.to_page_pdf() for x in recognized_items]
70
+ final_doc: cs.DocumentPdf = cs.DocumentPdf.create_from_pages(final_pages)
71
+ final_doc.metadata = _metadata
72
+ return final_doc
73
+
74
+
75
+ class SearchableTextImage(object):
76
+ """
77
+
78
+ return {
79
+ cs.ColumnsTable.KEY.value: cs.ColumnBody(cs.ColumnsTable.KEY.value, ListString([])),
80
+ cs.ColumnsTable.NUM_PAGE.value: cs.ColumnBody(cs.ColumnsTable.NUM_PAGE.value, ListString([])),
81
+ cs.ColumnsTable.NUM_LINE.value: cs.ColumnBody(cs.ColumnsTable.NUM_LINE.value, ListString([])),
82
+ cs.ColumnsTable.TEXT.value: cs.ColumnBody(cs.ColumnsTable.TEXT.value, ListString([])),
83
+ cs.ColumnsTable.FILE_NAME.value: cs.ColumnBody(cs.ColumnsTable.FILE_NAME.value, ListString([])),
84
+ cs.ColumnsTable.FILETYPE.value: cs.ColumnBody(cs.ColumnsTable.FILETYPE.value, ListString([])),
85
+ cs.ColumnsTable.FILE_PATH.value: cs.ColumnBody(cs.ColumnsTable.FILE_PATH.value, ListString([])),
86
+ cs.ColumnsTable.DIR.value: cs.ColumnBody(cs.ColumnsTable.DIR.value, ListString([])),
87
+ }
88
+ """
89
+ def __init__(self, silent: bool = False):
90
+ self.silent = silent
91
+ self.elements: cs.DictTextTable = cs.DictTextTable.create_void_dict()
92
+
93
+ def __repr__(self):
94
+ return f'SearchableTextPdf: {self.elements}'
95
+
96
+ @property
97
+ def length(self) -> int:
98
+ return len(self.elements[cs.ColumnsTable.TEXT.value])
99
+
100
+ def is_empty(self) -> bool:
101
+ return len(self.elements[cs.ColumnsTable.TEXT.value]) == 0
102
+
103
+ @property
104
+ def columns(self) -> cs.HeadValues:
105
+ return cs.HeadValues([cs.HeadCell(x) for x in list(self.elements.keys())])
106
+
107
+ @property
108
+ def first(self) -> dict[str, str]:
109
+ if self.is_empty():
110
+ return {}
111
+ cols = self.columns
112
+ _first = {}
113
+ for c in cols:
114
+ _first[c] = self.elements[c][0]
115
+ return _first
116
+
117
+ @property
118
+ def last(self) -> dict[str, str]:
119
+ if self.is_empty():
120
+ return {}
121
+ if self.is_empty():
122
+ return {}
123
+ cols = self.columns
124
+ _last = {}
125
+ for c in cols:
126
+ _last[c] = self.elements[c][-1]
127
+ return _last
128
+
129
+ def get_item(self, idx: int) -> dict[str, str]:
130
+ _current_item = {}
131
+ cols = self.columns
132
+ try:
133
+ for col in cols:
134
+ _current_item[col] = self.elements[col][idx]
135
+ except Exception as err:
136
+ if not self.silent:
137
+ print(err)
138
+ return _current_item
139
+ else:
140
+ return _current_item
141
+
142
+ def add_line(self, line: dict[str, str]) -> None:
143
+ cols: cs.HeadValues = self.columns
144
+ cols_line: cs.HeadValues = cs.HeadValues([cs.HeadCell(y) for y in list(line.keys())])
145
+ for col in cols:
146
+ if cols_line.contains(col, case=True, iqual=True):
147
+ self.elements[col].append(line[col])
148
+
149
+ def clear(self) -> None:
150
+ for _k in self.elements.keys():
151
+ self.elements[_k].clear()
152
+
153
+ def to_string(self) -> str:
154
+ """
155
+ Retorna o texto da coluna TEXT em formato de string
156
+ ou 'nas' em caso de erro nas = Not a String
157
+ """
158
+ try:
159
+ return ' '.join(self.elements[cs.ColumnsTable.TEXT.value])
160
+ except Exception as e:
161
+ print(e)
162
+ return 'nas'
163
+
164
+ def to_data_frame(self) -> pd.DataFrame:
165
+ return pd.DataFrame.from_dict(self.elements)
166
+
167
+ def to_file_json(self, file: sp.File):
168
+ """Exporta os dados da busca para um arquivo JSON"""
169
+ dt = sp.JsonConvert.from_dict(self.elements).to_json_data()
170
+ dt.to_file(file)
171
+
172
+ def to_file_excel(self, file: sp.File):
173
+ """Exporta os dados da busca para um arquivo .XLSX"""
174
+ self.to_data_frame().to_excel(file.absolute(), index=False)
175
+
176
+
177
+ class ImageFinder(object):
178
+ """
179
+ Classe para Filtrar texto em Imagens
180
+ """
181
+
182
+ def __init__(self):
183
+ self.docs_collection: dict[str, cs.ImageObject] = {}
184
+ self.recognize: RecognizeImage = RecognizeImage()
185
+ self.pbar: sp.ProgressBarAdapter = sp.ProgressBarAdapter()
186
+
187
+ def is_empty(self) -> bool:
188
+ return len(self.docs_collection) == 0
189
+
190
+ def clear(self) -> None:
191
+ self.docs_collection.clear()
192
+
193
+ def add_image(self, img: sp.File | bytes | BytesIO | str | cs.ImageObject) -> None:
194
+ if isinstance(img, sp.File):
195
+ img = cs.ImageObject(img)
196
+ elif isinstance(img, cs.ImageObject):
197
+ pass
198
+ elif isinstance(img, bytes):
199
+ img = cs.ImageObject(img)
200
+ elif isinstance(img, str):
201
+ img = cs.ImageObject(img)
202
+ elif isinstance(img, BytesIO):
203
+ img = cs.ImageObject(img)
204
+
205
+ if not img.metadata.file_path.is_empty:
206
+ self.docs_collection[img.metadata.file_path] = img
207
+ else:
208
+ self.docs_collection[img.metadata.name] = img
209
+
210
+ def add_images(self, images: list[sp.File] | list[cs.ImageObject] | list[bytes] | list[BytesIO]) -> None:
211
+ for f in images:
212
+ self.add_image(f)
213
+
214
+ def add_directory_images(self, dir_pdf: sp.Directory) -> None:
215
+ files = sp.InputFiles(dir_pdf).get_files(file_type=sp.LibraryDocs.IMAGE)
216
+ if len(files) > 0:
217
+ self.add_images(files)
218
+
219
+ def find(
220
+ self, text: str,
221
+ separator: str = '\n',
222
+ iqual: bool = False,
223
+ case: bool = False,
224
+ silent: bool = False,
225
+ ) -> SearchableTextImage:
226
+ """
227
+ Filtrar texto retornando a primeira ocorrência do Documento PDF.
228
+ """
229
+ _searchable = SearchableTextImage(silent)
230
+ if self.is_empty():
231
+ return _searchable
232
+
233
+ total_num: int = len(self.docs_collection.keys())
234
+ for n, file_key in enumerate(self.docs_collection.keys()):
235
+ self.pbar.update(
236
+ ((n + 1) / total_num) * 100,
237
+ f'[PESQUISANDO]: {n+1}/{total_num}',
238
+ )
239
+
240
+ current_img: cs.ImageObject = self.docs_collection[file_key]
241
+ txt_in_img = self.recognize.image_to_string(current_img)
242
+ if (txt_in_img == '') or (txt_in_img is None):
243
+ self.pbar.update_text(f'ERRO: {n+1} {current_img.metadata.name}')
244
+ continue
245
+ try:
246
+ fd = cs.text.FindText(txt_in_img, separator=separator)
247
+ idx = fd.find_index(text, iqual=iqual, case=case)
248
+ if idx is None:
249
+ continue
250
+ math_text: str | None = fd.get_index(idx)
251
+ except Exception as err:
252
+ print(f'{__class__.__name__} {err}')
253
+ else:
254
+ new_line: dict[str, str] = {
255
+ cs.ColumnsTable.KEY.value: f'{idx}',
256
+ cs.ColumnsTable.NUM_PAGE.value: 'nan',
257
+ cs.ColumnsTable.NUM_LINE.value: f'{idx + 1}',
258
+ cs.ColumnsTable.TEXT.value: math_text,
259
+ cs.ColumnsTable.FILE_NAME.value: current_img.metadata.file_path,
260
+ cs.ColumnsTable.FILETYPE.value: current_img.metadata.extension,
261
+ cs.ColumnsTable.FILE_PATH.value: current_img.metadata.dir_path,
262
+ cs.ColumnsTable.DIR.value: current_img.metadata.dir_path,
263
+ }
264
+ _searchable.add_line(new_line)
265
+ return _searchable
266
+ return _searchable
267
+
268
+ def find_all(
269
+ self, text: str,
270
+ separator: str = '\n',
271
+ iqual: bool = False,
272
+ case: bool = False,
273
+ silent: bool = False,
274
+ ) -> SearchableTextImage:
275
+ """
276
+ Filtrar texto em documento PDF e retorna todas as ocorrências do texto
277
+ encontradas no documento, incluindo o número da linha, página e nome do arquivo
278
+ em cada ocorrência.
279
+ """
280
+ _searchable = SearchableTextImage(silent)
281
+ if self.is_empty():
282
+ return _searchable
283
+
284
+ total_num: int = len(self.docs_collection.keys())
285
+ for n, file_key in enumerate(self.docs_collection.keys()):
286
+ self.pbar.update(
287
+ ((n + 1) / total_num) * 100,
288
+ f'[PESQUISANDO]: {n + 1}/{total_num}',
289
+ )
290
+
291
+ current_img: cs.ImageObject = self.docs_collection[file_key]
292
+ txt_in_img = self.recognize.image_to_string(current_img)
293
+ if (txt_in_img == '') or (txt_in_img is None):
294
+ self.pbar.update_text(f'ERRO: {n + 1} {current_img.metadata.name}')
295
+ continue
296
+ try:
297
+ fd = cs.text.FindText(txt_in_img, separator=separator)
298
+ idx = fd.find_index(text, iqual=iqual, case=case)
299
+ if idx is None:
300
+ continue
301
+ math_text: str | None = fd.get_index(idx)
302
+ except Exception as err:
303
+ print(f'{__class__.__name__} {err}')
304
+ else:
305
+ new_line: dict[str, str] = {
306
+ cs.ColumnsTable.KEY.value: f'{idx}',
307
+ cs.ColumnsTable.NUM_PAGE.value: 'nan',
308
+ cs.ColumnsTable.NUM_LINE.value: f'{idx + 1}',
309
+ cs.ColumnsTable.TEXT.value: math_text,
310
+ cs.ColumnsTable.FILE_NAME.value: current_img.metadata.file_path,
311
+ cs.ColumnsTable.FILETYPE.value: current_img.metadata.extension,
312
+ cs.ColumnsTable.FILE_PATH.value: current_img.metadata.dir_path,
313
+ cs.ColumnsTable.DIR.value: current_img.metadata.dir_path,
314
+ }
315
+ _searchable.add_line(new_line)
316
+ return _searchable
317
+
318
+
319
+ __all__ = [
320
+ 'ImageFinder', 'SearchableTextImage', 'read_images',
321
+ 'read_document', 'RecognizeImage', 'RecognizePdf', 'VoidPbar', 'ProgressVoid'
322
+ ]
@@ -93,3 +93,6 @@ class BinTesseract(object):
93
93
  if self.__path_tesseract is None:
94
94
  return False
95
95
  return self.__path_tesseract.exists()
96
+
97
+
98
+ __all__ = ['BinTesseract', 'get_path_tesseract_sys']
@@ -29,7 +29,7 @@ try:
29
29
  except Exception as err:
30
30
  print(err)
31
31
  pyocr = object
32
- #pyocr.tesseract = object
32
+
33
33
 
34
34
  if MOD_PYTESSERACT and MOD_PYOCR:
35
35
  ModuleOcr: TypeAlias = Union[pyocr, pytesseract]
@@ -43,3 +43,6 @@ elif MOD_PYOCR:
43
43
  else:
44
44
  ModuleOcr: TypeAlias = None
45
45
  DEFAULT_LIB_OCR = LibOcr.NOT_IMPLEMENTED
46
+
47
+
48
+ __all__ = ['LibOcr', 'DEFAULT_LIB_OCR']
@@ -7,10 +7,10 @@ from typing import Union
7
7
  from io import BytesIO
8
8
  from pandas import DataFrame
9
9
  from soup_files import File
10
- from convert_stream import (
11
- ImageObject, DocumentPdf, PageDocumentPdf, DEFAULT_LIB_IMAGE, LibImage
12
- )
13
- from convert_stream.mod_types import get_void_metadata
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 MetaDataItem, MetaDataFile
14
14
  from convert_stream.mod_types.table_types import ColumnBody
15
15
  from ocr_stream.modules import (
16
16
  LibOcr, DEFAULT_LIB_OCR, pytesseract, Output, pyocr,
@@ -31,8 +31,9 @@ class TextRecognized(object):
31
31
  """
32
32
 
33
33
  def __init__(self, bytes_recognized: bytes):
34
- self.metadata: dict[str, str | None] = get_void_metadata()
34
+ self.metadata: MetaDataFile = MetaDataFile()
35
35
  self.bytes_recognized: bytes = bytes_recognized
36
+
36
37
  self.list_bad_char: list[str] = [
37
38
  ':', ',', ';', '$', '=',
38
39
  '!', '}', '{', '(', ')',
@@ -51,7 +52,10 @@ class TextRecognized(object):
51
52
  return doc.to_pages()[0]
52
53
 
53
54
  def to_document(self) -> DocumentPdf:
54
- return DocumentPdf(BytesIO(self.bytes_recognized))
55
+ tmp_doc = DocumentPdf(BytesIO(self.bytes_recognized))
56
+ tmp_doc.metadata.file_path = self.metadata.file_path
57
+ tmp_doc.metadata.dir_path = self.metadata.dir_path
58
+ return tmp_doc
55
59
 
56
60
  def to_dict(self, separator: str = '\n') -> dict[str, ColumnBody]:
57
61
  return self.to_page_pdf().to_dict(separator=separator)
@@ -205,3 +209,6 @@ class TesseractOcr(object):
205
209
  else:
206
210
  raise ValueError(f'{lib_ocr} is not supported')
207
211
  return cls(md)
212
+
213
+
214
+ __all__ = ['TesseractOcr', 'TextRecognized']
@@ -1,11 +1,40 @@
1
1
  #!/usr/bin/env python3
2
2
  from __future__ import annotations
3
- from convert_stream import ImageObject, DocumentPdf, PageDocumentPdf, ConvertPdfToImages
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
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
- from soup_files import File, ProgressBarAdapter, CreatePbar
10
+ import soup_files as sp
11
+
12
+
13
+ class VoidPbar(sp.ABCProgressBar):
14
+
15
+ def __init__(self):
16
+ super().__init__()
17
+
18
+ def set_percent(self, percent: float):
19
+ pass
20
+
21
+ def set_text(self, text: str):
22
+ pass
23
+
24
+
25
+ class ProgressVoid(sp.ProgressBarAdapter):
26
+
27
+ def __init__(self, progress_bar: sp.ABCProgressBar = VoidPbar()):
28
+ super().__init__(progress_bar)
29
+
30
+ def update(self, percent: float, status: str = "-"):
31
+ pass
32
+
33
+ def update_percent(self, percent: float = 0):
34
+ pass
35
+
36
+ def update_text(self, text: str = "-"):
37
+ pass
9
38
 
10
39
 
11
40
  class RecognizeImage(object):
@@ -30,7 +59,7 @@ class RecognizeImage(object):
30
59
  def bin_tesseract(self) -> BinTesseract:
31
60
  return self.module_ocr.mod_ocr.bin_tess
32
61
 
33
- def image_content_data(self, img: ImageObject | File) -> DataFrame:
62
+ def image_content_data(self, img: ImageObject | sp.File) -> DataFrame:
34
63
  """
35
64
  Retorna uma tabela estruturada com os dados reconhecidos da imagem.
36
65
 
@@ -39,35 +68,35 @@ class RecognizeImage(object):
39
68
  nível do OCR e confiabilidade, entre outras.
40
69
 
41
70
  Parâmetros:
42
- img (ImageObject | File): A imagem a ser processada, podendo ser um objeto
43
- `ImageObject` ou um arquivo `File`.
71
+ img (ImageObject | sp.File): A imagem a ser processada, podendo ser um objeto
72
+ `ImageObject` ou um arquivo `sp.File`.
44
73
 
45
74
  Retorna:
46
75
  DataFrame: Uma tabela contendo os dados reconhecidos na imagem.
47
76
 
48
77
  Lança:
49
- ValueError: Se o tipo do parâmetro `img` não for `ImageObject` ou `File`.
78
+ ValueError: Se o tipo do parâmetro `img` não for `ImageObject` ou `sp.File`.
50
79
 
51
80
  @param img: Imagem a ser processada, podendo ser uma instância de `ImageObject`
52
- ou um arquivo `File`.
53
- @type img: ImageObject | File
81
+ ou um arquivo `sp.File`.
82
+ @type img: ImageObject | sp.File
54
83
 
55
84
  @rtype: pandas.DataFrame
56
85
  @return: Um DataFrame com os dados reconhecidos na imagem.
57
86
 
58
- @raise ValueError: Caso o tipo de `img` não seja `ImageObject` nem `File`.
87
+ @raise ValueError: Caso o tipo de `img` não seja `ImageObject` nem `sp.File`.
59
88
  """
60
89
  return self.image_recognize(img).to_dataframe()
61
90
 
62
- def image_recognize(self, img: ImageObject | File, *, land_scape: bool = False) -> TextRecognized:
63
- if isinstance(img, File):
91
+ def image_recognize(self, img: ImageObject | sp.File, *, land_scape: bool = False) -> TextRecognized:
92
+ if isinstance(img, sp.File):
64
93
  img = ImageObject(img)
65
94
  if land_scape:
66
95
  img.set_landscape()
67
96
  rec: TextRecognized = self.module_ocr.to_reconize(img)
68
97
  return rec
69
98
 
70
- def image_to_string(self, img: ImageObject | File) -> str:
99
+ def image_to_string(self, img: ImageObject | sp.File) -> str:
71
100
  return self.module_ocr.to_string(img)
72
101
 
73
102
 
@@ -79,9 +108,9 @@ class RecognizePdf(object):
79
108
  lib_ocr: LibOcr = DEFAULT_LIB_OCR,
80
109
  ):
81
110
  self.recognize_image: RecognizeImage = RecognizeImage(bin_tess, lib_ocr=lib_ocr)
82
- self.pbar: ProgressBarAdapter = CreatePbar().get()
111
+ self.pbar: sp.ProgressBarAdapter = sp.CreatePbar().get()
83
112
 
84
- def set_pbar(self, p: ProgressBarAdapter):
113
+ def set_pbar(self, p: sp.ProgressBarAdapter):
85
114
  self.pbar = p
86
115
 
87
116
  def recognize_page_pdf(self, page: PageDocumentPdf, *, dpi: int = 300) -> DocumentPdf:
@@ -91,34 +120,38 @@ class RecognizePdf(object):
91
120
  """
92
121
  _doc = DocumentPdf.create_from_pages([page])
93
122
  _conv = ConvertPdfToImages.create(_doc)
123
+ _conv.set_pbar(self.pbar)
94
124
  img: ImageObject = _conv.to_images(dpi=dpi)[0]
95
125
  text_recognized: TextRecognized = self.recognize_image.image_recognize(img)
96
126
  del _doc
97
127
  del _conv
98
128
  del img
129
+ text_recognized.metadata.file_path = page.metadata.file_path
130
+ text_recognized.metadata.dir_path = page.metadata.dir_path
99
131
  return text_recognized.to_document()
100
132
 
101
133
  def recognize_document(self, doc: DocumentPdf, *, dpi: int = 300) -> DocumentPdf:
102
134
  _pages: list[PageDocumentPdf] = []
103
135
  _pages.clear()
104
136
  _conv = ConvertPdfToImages.create(doc)
137
+ _conv.set_pbar(self.pbar)
105
138
  images: list[ImageObject] = _conv.to_images(dpi=dpi)
106
139
  max_num: int = len(images)
107
140
  for num, img in enumerate(images):
108
141
  self.pbar.update(
109
142
  ((num + 1) / max_num) * 100,
110
- f'Aplicando OCR [{num+1} de {max_num}]'
143
+ f'OCR Documento [{num+1} de {max_num}]'
111
144
  )
112
145
  text_recognized = self.recognize_image.image_recognize(img)
113
146
  _pages.append(text_recognized.to_page_pdf())
114
147
  print()
115
148
  self.pbar.stop()
116
149
  new_doc = DocumentPdf.create_from_pages(_pages)
150
+ new_doc.metadata = doc.metadata
117
151
  return new_doc
118
152
 
119
- def recognize_file_pdf(self, file: File, *, dpi: int = 300) -> DocumentPdf:
153
+ def recognize_file_pdf(self, file: sp.File, *, dpi: int = 300) -> DocumentPdf:
120
154
  file_doc = DocumentPdf.create_from_file(file)
121
155
  recognized_doc = self.recognize_document(file_doc, dpi=dpi)
122
156
  del file_doc
123
157
  return recognized_doc
124
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ocr_stream
3
- Version: 2.3.2
3
+ Version: 2.4.1
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.2
15
+ Requires-Dist: convert_stream>=2.4.1
16
16
  Requires-Dist: soup_files>=1.4
17
17
 
18
18
  # ocrlib
@@ -5,5 +5,5 @@ pandas
5
5
  requests>=2.32.4
6
6
  opencv-python
7
7
  pytesseract
8
- convert_stream>=2.3.2
8
+ convert_stream>=2.4.1
9
9
  soup_files>=1.4
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ocr_stream"
7
- version = "2.3.2"
7
+ version = "2.4.1"
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.2",
24
+ "convert_stream>=2.4.1",
25
25
  "soup_files>=1.4",
26
26
  ]
27
27
 
@@ -1,269 +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
8
- from convert_stream import (
9
- ImageObject, DocumentPdf, ConvertPdfToImages, ColumnsTable, CollectionImages, FindText
10
- )
11
- from pandas import DataFrame
12
- from soup_files import JsonConvert, ProgressBarAdapter, CreatePbar
13
-
14
-
15
- def read_images(
16
- images: list[ImageObject], *,
17
- pbar: ProgressBarAdapter = CreatePbar().get()
18
- ) -> DocumentPdf:
19
- pbar.start()
20
- print()
21
- max_num = len(images)
22
- pages = []
23
- rec = RecognizeImage()
24
- for idx, image in enumerate(images):
25
- pbar.update(
26
- ((idx + 1) / max_num) * 100,
27
- f'[OCR] {idx + 1} / {max_num}',
28
- )
29
- out = rec.image_recognize(image).to_page_pdf()
30
- pages.append(out)
31
- tmp_doc = DocumentPdf.create_from_pages(pages)
32
- print()
33
- pbar.stop()
34
- return tmp_doc
35
-
36
-
37
- def read_document(
38
- document: DocumentPdf, *,
39
- dpi: int = 300,
40
- pbar: ProgressBarAdapter = CreatePbar().get()
41
- ) -> DocumentPdf:
42
- pbar.start()
43
- print()
44
- convert = ConvertPdfToImages.create(document)
45
- convert.set_pbar(pbar)
46
- images = convert.to_images(dpi=dpi)
47
- print()
48
- return read_images(images, pbar=pbar)
49
-
50
-
51
- class SearchableTextImage(object):
52
- """
53
- Esta classe serve para armazenar textos filtrados em documentos PDF
54
- guardando o texto buscado e outros elementos como a página em que o texto foi
55
- encontrado, linha e o nome do respetivo arquivo.
56
- """
57
- def __init__(self, silent: bool = False):
58
- self.silent = silent
59
- self.elements: dict[str, list[str]] = {
60
- ColumnsTable.NUM_LINE.value: [],
61
- ColumnsTable.TEXT.value: [],
62
- ColumnsTable.FILE_PATH.value: [],
63
- }
64
-
65
- def __repr__(self):
66
- return f'{__class__.__name__}: {self.elements}'
67
-
68
- def is_empty(self) -> bool:
69
- return len(self.elements[ColumnsTable.TEXT.value]) == 0
70
-
71
- @property
72
- def first(self) -> dict[str, str]:
73
- if self.is_empty():
74
- return {}
75
- return {
76
- ColumnsTable.NUM_LINE.value: self.elements[ColumnsTable.NUM_LINE.value][0],
77
- ColumnsTable.TEXT.value: self.elements[ColumnsTable.TEXT.value][0],
78
- ColumnsTable.FILE_PATH.value: self.elements[ColumnsTable.FILE_PATH.value][0],
79
- }
80
-
81
- @property
82
- def last(self) -> dict[str, str]:
83
- if self.is_empty():
84
- return {}
85
- return {
86
- ColumnsTable.NUM_LINE.value: self.elements[ColumnsTable.NUM_LINE.value][-1],
87
- ColumnsTable.TEXT.value: self.elements[ColumnsTable.TEXT.value][-1],
88
- ColumnsTable.FILE_PATH.value: self.elements[ColumnsTable.FILE_PATH.value][-1],
89
- }
90
-
91
- @property
92
- def length(self) -> int:
93
- return len(self.elements[ColumnsTable.TEXT.value])
94
-
95
- @property
96
- def files(self) -> list[str]:
97
- if self.is_empty():
98
- return []
99
- return self.elements[ColumnsTable.FILE_PATH.value]
100
-
101
- def get_item(self, idx: int) -> dict[str, str]:
102
- try:
103
- return {
104
- ColumnsTable.NUM_LINE.value: self.elements[ColumnsTable.NUM_LINE.value][idx],
105
- ColumnsTable.TEXT.value: self.elements[ColumnsTable.TEXT.value][idx],
106
- ColumnsTable.FILE_PATH.value: self.elements[ColumnsTable.FILE_PATH.value][idx],
107
- }
108
- except Exception as err:
109
- if not self.silent:
110
- print(err)
111
- return {}
112
-
113
- def add_line(self, text, *, num_line: str, file: str) -> None:
114
- if not self.silent:
115
- print('----------------------')
116
- print(f'{file} => {text}')
117
- self.elements[ColumnsTable.NUM_LINE.value].append(num_line)
118
- self.elements[ColumnsTable.TEXT.value].append(text)
119
- self.elements[ColumnsTable.FILE_PATH.value].append(file)
120
-
121
- def clear(self) -> None:
122
- for _k in self.elements.keys():
123
- self.elements[_k].clear()
124
-
125
- def to_string(self) -> str:
126
- """
127
- Retorna o texto da coluna TEXT em formato de string
128
- ou 'nas' em caso de erro nas = Not a String
129
- """
130
- try:
131
- return ' '.join(self.elements[ColumnsTable.TEXT.value])
132
- except Exception as e:
133
- print(e)
134
- return 'nas'
135
-
136
- def to_data_frame(self) -> DataFrame:
137
- return DataFrame.from_dict(self.elements)
138
-
139
- def to_file_json(self, file: File):
140
- """Exporta os dados da busca para arquivo .JSON"""
141
- dt = JsonConvert.from_dict(self.elements).to_json_data()
142
- dt.to_file(file)
143
-
144
- def to_file_excel(self, file: File):
145
- """Exporta os dados da busca para um arquivo .XLSX"""
146
- try:
147
- self.to_data_frame().to_excel(file.absolute())
148
- except Exception as e:
149
- print(e)
150
-
151
-
152
- class FindTextImages(object):
153
-
154
- def __init__(
155
- self, collection_images: CollectionImages,
156
- *,
157
- rec_img: RecognizeImage = RecognizeImage(),
158
- pbar: ProgressBarAdapter = CreatePbar().get(),
159
- ):
160
- self.collection_images: CollectionImages = collection_images
161
- self._rec_img: RecognizeImage = rec_img
162
- self.pbar: ProgressBarAdapter = pbar
163
-
164
- def find(
165
- self, text: str,
166
- separator: str = '\n',
167
- iqual: bool = False,
168
- case: bool = False,
169
- silent: bool = False,
170
- ) -> SearchableTextImage:
171
- """
172
- Filtrar texto retornando a primeira ocorrência do Documento PDF.
173
- """
174
- print()
175
- self.pbar.start()
176
- _searchable = SearchableTextImage(silent)
177
-
178
- max_num: int = self.collection_images.length
179
- for idx, image in enumerate(self.collection_images):
180
- self.pbar.update(
181
- ((idx + 1) / max_num) * 100,
182
- f'[Pesquisando]: {idx + 1} / {max_num} Aguarde...',
183
- )
184
- text_str_in_page: str = self._rec_img.image_to_string(image)
185
-
186
- if (text_str_in_page == 'nas') or (text_str_in_page is None):
187
- continue
188
- try:
189
- fd = FindText(text_str_in_page, separator=separator)
190
- idx = fd.find_index(text, iqual=iqual, case=case)
191
- if idx is None:
192
- continue
193
- math_text = fd.get_index(idx)
194
- except Exception as err:
195
- print(f'{__class__.__name__} {err}')
196
- else:
197
- _searchable.add_line(
198
- math_text,
199
- num_line=str(idx + 1),
200
- file=image.name,
201
- )
202
- return _searchable
203
- self.pbar.stop()
204
- print()
205
- return _searchable
206
-
207
- def find_all(
208
- self, text: str,
209
- separator: str = '\n',
210
- iqual: bool = False,
211
- case: bool = False,
212
- silent: bool = False,
213
- ) -> SearchableTextImage:
214
- """
215
- Filtrar texto em documento PDF e retorna todas as ocorrências do texto
216
- encontradas no documento, incluindo o número da linha, página e nome do arquivo
217
- em cada ocorrência.
218
- """
219
- print()
220
- self.pbar.start()
221
- _searchable = SearchableTextImage(silent)
222
- max_num: int = self.collection_images.length
223
- for idx, image in enumerate(self.collection_images):
224
- self.pbar.update(
225
- ((idx + 1) / max_num) * 100,
226
- f'[Pesquisando]: {idx + 1} / {max_num} Aguarde...',
227
- )
228
-
229
- text_str_in_page = self._rec_img.image_to_string(image)
230
- if (text_str_in_page == 'nas') or (text_str_in_page is None):
231
- continue
232
- try:
233
- _values = text_str_in_page.split(separator)
234
- for num, item in enumerate(_values):
235
- if case:
236
- if iqual:
237
- if text == item:
238
- _searchable.add_line(
239
- item,
240
- num_line=str(num + 1),
241
- file=image.name,
242
- )
243
- else:
244
- if text in item:
245
- _searchable.add_line(
246
- item,
247
- num_line=str(num + 1),
248
- file=image.name,
249
- )
250
- else:
251
- if iqual:
252
- if text.lower() == item.lower():
253
- _searchable.add_line(
254
- item,
255
- num_line=str(num + 1),
256
- file=image.name,
257
- )
258
- else:
259
- if text.lower() in item.lower():
260
- _searchable.add_line(
261
- item,
262
- num_line=str(num + 1),
263
- file=image.name,
264
- )
265
- except Exception as e:
266
- print(f'{__class__.__name__} {e}')
267
- self.pbar.stop()
268
- print()
269
- return _searchable
File without changes
File without changes