ocr-stream 1.2__tar.gz → 1.4__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.
Files changed (25) hide show
  1. {ocr_stream-1.2 → ocr_stream-1.4}/PKG-INFO +9 -9
  2. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream/__init__.py +1 -1
  3. ocr_stream-1.4/ocr_stream/_version.py +5 -0
  4. ocr_stream-1.4/ocr_stream/extractors.py +126 -0
  5. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream/models/__init__.py +0 -1
  6. ocr_stream-1.4/ocr_stream/models/m_extractors.py +2 -0
  7. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream/models/m_modules_ocr.py +27 -38
  8. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream/modules_ocr.py +225 -168
  9. ocr_stream-1.4/ocr_stream/utils/__init__.py +16 -0
  10. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream.egg-info/PKG-INFO +9 -9
  11. ocr_stream-1.4/ocr_stream.egg-info/requires.txt +9 -0
  12. {ocr_stream-1.2 → ocr_stream-1.4}/pyproject.toml +10 -9
  13. ocr_stream-1.2/ocr_stream/_version.py +0 -5
  14. ocr_stream-1.2/ocr_stream/extractors.py +0 -108
  15. ocr_stream-1.2/ocr_stream/models/m_extractors.py +0 -39
  16. ocr_stream-1.2/ocr_stream/utils/__init__.py +0 -13
  17. ocr_stream-1.2/ocr_stream.egg-info/requires.txt +0 -9
  18. {ocr_stream-1.2 → ocr_stream-1.4}/README.md +0 -0
  19. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream/__main__.py +0 -0
  20. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream.egg-info/SOURCES.txt +0 -0
  21. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream.egg-info/dependency_links.txt +0 -0
  22. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream.egg-info/not-zip-safe +0 -0
  23. {ocr_stream-1.2 → ocr_stream-1.4}/ocr_stream.egg-info/top_level.txt +0 -0
  24. {ocr_stream-1.2 → ocr_stream-1.4}/setup.cfg +0 -0
  25. {ocr_stream-1.2 → ocr_stream-1.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ocr_stream
3
- Version: 1.2
3
+ Version: 1.4
4
4
  Summary: Extrair textos de documentos digitalizados e imagens.
5
5
  Author: Bruno Chaves
6
6
  Requires: PyPDF2
@@ -17,14 +17,14 @@ Requires: soup_files
17
17
  Requires: convert_stream
18
18
  Requires-Python: >=3.11
19
19
  Description-Content-Type: text/markdown
20
- Requires-Dist: PyPDF2==3.0.1
21
- Requires-Dist: PyMuPDF==1.25.5
22
- Requires-Dist: reportlab==4.3.1
23
- Requires-Dist: openpyxl==3.1.5
24
- Requires-Dist: pandas==2.2.3
25
- Requires-Dist: Pillow==11.2.1
26
- Requires-Dist: opencv-python==4.11.0.86
27
- Requires-Dist: convert_stream
20
+ Requires-Dist: PyMuPDF
21
+ Requires-Dist: reportlab
22
+ Requires-Dist: openpyxl
23
+ Requires-Dist: pandas
24
+ Requires-Dist: Pillow>=11.3.0
25
+ Requires-Dist: requests>=2.32.4
26
+ Requires-Dist: opencv-python
27
+ Requires-Dist: convert_stream>=1.4
28
28
  Requires-Dist: soup_files
29
29
  Dynamic: requires
30
30
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
3
  from .models.m_modules_ocr import LibraryOCR
4
- from .modules_ocr import TextRecognized
4
+ from .modules_ocr import TextRecognized, BinaryTesseract
5
5
  from .utils import File, Directory, InputFiles, UserFileSystem
6
6
  from .extractors import RecognizeImage, RecognizePdf
7
7
  from ._version import __version__, __modify_data__
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+
4
+ __version__ = '1.4'
5
+ __modify_data__ = '2025-07-11'
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+ from typing import List
4
+ from ocr_stream.utils import (
5
+ File, ImageObject,
6
+ DataFrame, PageDocumentPdf, DocumentPdf,
7
+ )
8
+ from ocr_stream.models import LibraryOCR
9
+ from ocr_stream.modules_ocr import ModuleOcr, TextRecognized, BinaryTesseract
10
+ from convert_stream import ConvertPdfToImage, CreatePbar
11
+ from soup_files import ProgressBarAdapter
12
+
13
+
14
+ class RecognizeImage(object):
15
+ """
16
+ Reconhecer textos em imagens com pytesseract|pyocr usando o tesseract
17
+ """
18
+
19
+ def __init__(self, module_ocr):
20
+ super().__init__()
21
+ self.module_ocr: ModuleOcr = module_ocr
22
+
23
+ def image_content_data(self, img: ImageObject | File) -> DataFrame:
24
+ """
25
+ Retorna uma tabela estruturada com os dados reconhecidos da imagem.
26
+
27
+ Este método extrai as informações contidas em uma imagem e as organiza em
28
+ um DataFrame com colunas como o texto detectado, coordenadas da caixa delimitadora,
29
+ nível do OCR e confiabilidade, entre outras.
30
+
31
+ Parâmetros:
32
+ img (ImageObject | File): A imagem a ser processada, podendo ser um objeto
33
+ `ImageObject` ou um arquivo `File`.
34
+
35
+ Retorna:
36
+ DataFrame: Uma tabela contendo os dados reconhecidos na imagem.
37
+
38
+ Lança:
39
+ ValueError: Se o tipo do parâmetro `img` não for `ImageObject` ou `File`.
40
+
41
+ @param img: Imagem a ser processada, podendo ser uma instância de `ImageObject`
42
+ ou um arquivo `File`.
43
+ @type img: ImageObject | File
44
+
45
+ @rtype: pandas.DataFrame
46
+ @return: Um DataFrame com os dados reconhecidos na imagem.
47
+
48
+ @raise ValueError: Caso o tipo de `img` não seja `ImageObject` nem `File`.
49
+ """
50
+ if isinstance(img, File):
51
+ return self.module_ocr.image_content_data(ImageObject.create_from_file(img))
52
+ elif isinstance(img, ImageObject):
53
+ return self.module_ocr.image_content_data(img)
54
+ else:
55
+ raise ValueError(f'{__class__.__name__}\nUse: ImageObject() ou File(), não {type(img)}')
56
+
57
+ def image_recognize(self, img: ImageObject | File) -> TextRecognized:
58
+ if isinstance(img, File):
59
+ return self.module_ocr.image_recognize(ImageObject.create_from_file(img))
60
+ elif isinstance(img, ImageObject):
61
+ return self.module_ocr.image_recognize(img)
62
+ else:
63
+ raise ValueError(f'{__class__.__name__}\nUse: ImageObject() ou File(), não {type(img)}')
64
+
65
+ def image_to_string(self, img: ImageObject | File) -> str:
66
+ if isinstance(img, File):
67
+ return self.module_ocr.imag_to_string(ImageObject.create_from_file(img))
68
+ elif isinstance(img, ImageObject):
69
+ return self.module_ocr.imag_to_string(img)
70
+ else:
71
+ raise ValueError(f'{__class__.__name__}\nUse: ImageObject() ou File(), não {type(img)}')
72
+
73
+ @classmethod
74
+ def create(
75
+ cls,
76
+ library_ocr: LibraryOCR = LibraryOCR.PYTESSERACT,
77
+ *,
78
+ binary_tess: BinaryTesseract = BinaryTesseract(),
79
+ ) -> RecognizeImage:
80
+ #
81
+ module_ocr = ModuleOcr.create(library_ocr, binary_tess=binary_tess)
82
+ return cls(module_ocr)
83
+
84
+
85
+ class RecognizePdf(object):
86
+ def __init__(
87
+ self,
88
+ recognize_image: RecognizeImage, *,
89
+ pbar: ProgressBarAdapter = CreatePbar().get()
90
+ ):
91
+ self.recognize_image: RecognizeImage = recognize_image
92
+ self.pdf_to_image: ConvertPdfToImage = ConvertPdfToImage.create()
93
+ self.pbar: ProgressBarAdapter = pbar
94
+
95
+ def recognize_page_pdf(self, page: PageDocumentPdf) -> PageDocumentPdf:
96
+ """
97
+ Converte a página em Imagem, reconhece o texto com OCR e
98
+ retorna uma nova página com o texto reconhecido.
99
+ """
100
+ img: ImageObject = self.pdf_to_image.from_page_pdf(page)
101
+ text_recognized: TextRecognized = self.recognize_image.image_recognize(img)
102
+ return text_recognized.to_page_pdf()
103
+
104
+ def recognize_document(self, doc: DocumentPdf) -> DocumentPdf:
105
+ new_pages: List[PageDocumentPdf] = []
106
+
107
+ for n, page in enumerate(doc.get_pages()):
108
+ self.pbar.update(
109
+ ((n+1)/doc.get_num_pages()) * 100,
110
+ f'Reconhecendo página PDF [{n+1} de {doc.get_num_pages()}]',
111
+ )
112
+ recognized_page: PageDocumentPdf = self.recognize_page_pdf(page)
113
+ new_pages.append(recognized_page)
114
+ return DocumentPdf(pages=new_pages)
115
+
116
+ @classmethod
117
+ def create(
118
+ cls,
119
+ library_ocr=LibraryOCR.PYTESSERACT,
120
+ *,
121
+ binary_tess: BinaryTesseract = BinaryTesseract(),
122
+ pbar: ProgressBarAdapter = CreatePbar().get(),
123
+ ) -> RecognizePdf:
124
+ #
125
+ recognize_image = RecognizeImage.create(library_ocr, binary_tess=binary_tess)
126
+ return cls(recognize_image, pbar=pbar)
@@ -1,3 +1,2 @@
1
1
  #!/usr/bin/env python3
2
2
  from .m_modules_ocr import LibraryOCR, ABCTextRecognized, ABCModuleOcr
3
- from .m_extractors import ABCRecognizeImage
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env python3
2
+
@@ -8,72 +8,61 @@ from pandas import DataFrame
8
8
  from soup_files import File, Directory
9
9
  from convert_stream import ImageObject, DocumentPdf, PageDocumentPdf, LibraryImage
10
10
 
11
+
11
12
  class LibraryOCR(Enum):
12
13
  PYTESSERACT = 'pytesseract'
13
14
  PYOCR = 'pyocr'
14
-
15
-
15
+
16
+
16
17
  class ABCTextRecognized(ABC):
17
- def __init__(self, bytes_recognized:bytes):
18
+ def __init__(self, bytes_recognized: bytes):
18
19
  super().__init__()
19
- self.bytes_recognized:bytes = bytes_recognized
20
-
20
+ self.bytes_recognized: bytes = bytes_recognized
21
+
21
22
  @abstractmethod
22
23
  def to_string(self) -> str:
23
24
  pass
24
-
25
+
25
26
  @abstractmethod
26
27
  def to_page_pdf(self) -> PageDocumentPdf:
27
28
  pass
28
-
29
+
29
30
  @abstractmethod
30
31
  def to_document(self) -> DocumentPdf:
31
32
  pass
32
-
33
+
33
34
  @abstractmethod
34
- def to_dataframe(self, separator:str='\n', column_text: str = 'TEXT') -> DataFrame:
35
+ def to_dataframe(self, separator: str = '\n', column_text: str = 'TEXT') -> DataFrame:
35
36
  pass
36
-
37
+
37
38
  @abstractmethod
38
- def to_dict(self, separator:str='\n', column_text: str = 'TEXT') -> Dict[str, List[str]]:
39
+ def to_dict(self, separator: str = '\n', column_text: str = 'TEXT') -> Dict[str, List[str]]:
39
40
  pass
40
-
41
-
41
+
42
+
42
43
  #======================================================================#
43
44
  # Abstração para o módulo que extrai o texto das imagens (OCR)
44
45
  # pode ser implementado com pytesseract ou outros.
45
46
  #======================================================================#
47
+
48
+
46
49
  class ABCModuleOcr(ABC):
47
- def __init__(self, *, cmd_executable:File, lang:str=None, tess_data_dir:Directory=None):
48
- self.cmd_executable:File = cmd_executable
49
- self.lang:str = lang
50
- self.tess_data_dir:Directory = tess_data_dir
51
- self.current_library_ocr:LibraryOCR = None
52
-
50
+
51
+ def __init__(self, *, cmd_executable: File, lang: str = None, tess_data_dir: Directory = None):
52
+ self.cmd_executable: File = cmd_executable
53
+ self.lang: str = lang
54
+ self.tess_data_dir: Directory = tess_data_dir
55
+ self.current_library_ocr: LibraryOCR = None
56
+
53
57
  @abstractmethod
54
- def image_recognize(self, img:ImageObject) -> ABCTextRecognized:
58
+ def image_recognize(self, img: ImageObject) -> ABCTextRecognized:
55
59
  pass
56
-
60
+
57
61
  @abstractmethod
58
62
  def image_content_data(self, img: ImageObject) -> DataFrame:
59
63
  pass
60
-
64
+
61
65
  @abstractmethod
62
- def imag_to_string(self, img:ImageObject) -> str:
63
- pass
64
-
65
- @classmethod
66
- def create(
67
- cls,
68
- library_ocr:LibraryOCR=LibraryOCR.PYTESSERACT,
69
- *,
70
- path_tesseract:File,
71
- lang:str=None,
72
- tess_data_dir:Directory=None,
73
- ) -> ABCModuleOcr:
66
+ def imag_to_string(self, img: ImageObject) -> str:
74
67
  pass
75
-
76
-
77
-
78
68
 
79
-
@@ -2,99 +2,93 @@
2
2
  from __future__ import annotations
3
3
  from types import ModuleType
4
4
  from typing import List, Dict
5
- from pathlib import Path
5
+ from shutil import which
6
6
  import re
7
7
  import os
8
8
  from pandas import DataFrame
9
9
  from pytesseract import pytesseract, Output
10
- from ocr_stream.models import (
10
+
11
+ from ocr_stream.models import (
11
12
  ABCModuleOcr, ABCTextRecognized, LibraryOCR,
12
13
  )
14
+
13
15
  from ocr_stream.utils import (
14
16
  PageDocumentPdf, DocumentPdf, LibraryImage, LibraryPDF,
15
- ImageObject, ArrayString, DataString, File, Directory
17
+ ImageObject, DataString, File, Directory,
18
+ ColumnTable, KERNEL_TYPE, ArrayString,
16
19
  )
17
20
 
21
+ try:
22
+ import pyocr
23
+ except Exception as e:
24
+ print(e)
18
25
 
19
-
20
- def get_temp_dir(prefix=Path().home()) -> str:
21
- prefix = Path().home()
22
- sys_temp_dir = os.environ.get('TMP', os.path.join(prefix, 'var', 'temp'))
23
- if not sys_temp_dir:
24
- sys_temp_dir = os.path.join(prefix, 'var', 'temp')
25
- if not os.path.exists(sys_temp_dir):
26
- try:
27
- os.makedirs(sys_temp_dir)
28
- except Exception as e:
29
- print(e)
30
- os.environ["TEMP"] = sys_temp_dir
31
- print('-' * 30)
32
- print(f'Diretório temporário: {sys_temp_dir}')
33
- print('-' * 30)
34
- return sys_temp_dir
35
-
36
-
37
26
  class TextRecognizedToi(object):
38
27
  """
39
28
  Extração/Filtro de texto em documentos do tipo carta TOI.
40
29
  """
41
- def __init__(self, value:str=None, *, separator:str='\n', column_text='TEXT', file:File):
42
- # Todas as linhas que foram reconhecidas em uma imagem/página PDF.
30
+
31
+ def __init__(self, value: str = None, *, separator: str = '\n', column_text='TEXT', file: File):
32
+ # Todas as linhas reconhecidas em imagem/página PDF.
43
33
  if not isinstance(value, str):
44
34
  raise ValueError(f'{__class__.__name__}Use: str, não {type(value)}')
45
- self.text_document:str = DataString(value).to_upper().value if value is not None else None
46
- self.separator:str = separator
47
- self.file:File = file
48
- self.column_text:str = column_text
49
-
35
+ self.text_document: str = DataString(value).to_upper().value if value is not None else None
36
+ self.separator: str = separator
37
+ self.file: File = file
38
+ self.column_text: str = column_text
39
+ self.listText = []
50
40
  self.pattern_line_uc_toi = r'UC:\s*\d+\s*TOI:\s*\d+'
51
41
  self.pattern_uc = r'\b(?:UC:?|UC):?\s*(\d+)'
52
42
  self.pattern_toi = r'\b(?:TOI?|TO):?\s*(\d+)'
53
43
  self.pattern_localidade = r'LOCALIDADE:\s*(.*)'
54
-
44
+
55
45
  self.words_toi = (
56
46
  'TO]!', 'TO]', 'TOl', 'TOI', 'TOI:'
57
47
  )
58
-
48
+
59
49
  self.words_uc = (
60
50
  'UE', 'UE:' 'UC', 'UC:', 'JC;'
61
51
  )
62
-
52
+
63
53
  def __replace_chars(self, text, char, new_char) -> str:
64
54
  return re.sub(r'{}'.format(char), new_char, text)
65
-
66
- def __get_match_groups(self, pattern, text:str) -> object | None:
55
+
56
+ def __get_match_groups(self, pattern, text: str) -> object | None:
67
57
  i = re.search(pattern, text)
68
58
  if i is None:
69
59
  return None
70
60
  return i.groups()
71
-
72
- def __get_match_group(self, pattern, text:str) -> object | None:
61
+
62
+ def __get_match_group(self, pattern, text: str) -> object | None:
73
63
  i = re.search(pattern, text)
74
64
  if i is None:
75
65
  return None
76
66
  return i.group()
77
-
67
+
78
68
  def is_null(self) -> bool:
79
69
  if self.text_document is None:
80
70
  return True
81
71
  if self.text_document == '':
82
72
  return True
83
73
  return False
84
-
74
+
85
75
  def to_string(self) -> str | None:
86
76
  return DataString(self.text_document).to_upper().value if not self.is_null() else None
87
-
77
+
88
78
  def to_list(self) -> List[str]:
89
79
  return self.text_document.split(self.separator)
90
-
80
+
91
81
  def to_array_string(self) -> ArrayString:
92
- values:List[str] = self.to_list()
93
-
82
+ try:
83
+ return ArrayString(self.to_list())
84
+ except Exception as e:
85
+ print(e)
86
+ return ArrayString([])
87
+
94
88
  def to_upper(self) -> TextRecognizedToi:
95
89
  self.text_document = self.text_document.upper()
96
90
  return self
97
-
91
+
98
92
  def to_dataframe(self) -> DataFrame:
99
93
  uc = self.uc()
100
94
  toi = self.toi()
@@ -107,8 +101,9 @@ class TextRecognizedToi(object):
107
101
  toi = 'nan'
108
102
  else:
109
103
  toi = DataString(toi).replace_bad_chars()
110
- toi = toi.replace_all('- ', '-').replace_all('_', ' ').replace_all('-', ' ').replace_all('TOI', '').value.strip()
111
-
104
+ toi = toi.replace_all('- ', '-').replace_all('_', ' ').replace_all('-', ' ').replace_all('TOI',
105
+ '').value.strip()
106
+
112
107
  df = DataFrame()
113
108
  df[self.column_text] = self.to_list()
114
109
  num = len(df)
@@ -118,7 +113,7 @@ class TextRecognizedToi(object):
118
113
  df['ARQUIVO'] = [self.file.absolute()] * num
119
114
  df['MD5'] = [self.file.md5()] * num
120
115
  return df
121
-
116
+
122
117
  def remove_bad_chars(self) -> TextRecognizedToi:
123
118
  values = []
124
119
  for item in self.to_list():
@@ -127,21 +122,22 @@ class TextRecognizedToi(object):
127
122
  )
128
123
  self.listText = values
129
124
  return self
130
-
131
- def uc(self) -> str:
125
+
126
+ def uc(self) -> str | None:
132
127
  """
133
128
  Extrair o número da UC em forma de string, se a operação falhar, será retornado (nan)
134
129
  """
135
130
  line_uc_toi = self.line_uc_and_toi()
136
- uc_value:str = None
131
+
132
+ uc_value: str = None
137
133
  if line_uc_toi is not None:
138
- uc_value = self.__get_match_group(self.pattern_uc, line_uc_toi)
134
+ uc_value: str = self.__get_match_group(self.pattern_uc, line_uc_toi.value)
139
135
  if uc_value is not None:
140
136
  return uc_value.replace('UC', '')
141
-
137
+
142
138
  arr = ArrayString(self.text_document.split(' '))
143
139
  for w in self.words_uc:
144
- out = arr.get_next(w).value
140
+ out: str = arr.get_next(w).value
145
141
  try:
146
142
  int(out)
147
143
  except:
@@ -149,16 +145,19 @@ class TextRecognizedToi(object):
149
145
  else:
150
146
  return out
151
147
  return None
152
-
153
- def toi(self) -> str:
148
+
149
+ def toi(self) -> str | None:
154
150
  line_uc_toi = self.line_uc_and_toi()
155
- toi_value:str = None
151
+ if line_uc_toi is None:
152
+ return None
153
+
154
+ toi_value: str = None
156
155
  if line_uc_toi is not None:
157
- toi_value = self.__get_match_group(self.pattern_toi, line_uc_toi)
156
+ toi_value = self.__get_match_group(self.pattern_toi, line_uc_toi.value)
158
157
  if toi_value is not None:
159
158
  return toi_value.replace('TOI', '')
160
159
  arr = ArrayString(self.text_document.split(' '))
161
-
160
+
162
161
  for w in self.words_toi:
163
162
  out = arr.get_next(w).value
164
163
  try:
@@ -168,7 +167,7 @@ class TextRecognizedToi(object):
168
167
  else:
169
168
  return out
170
169
  return None
171
-
170
+
172
171
  def roteiro(self) -> str:
173
172
  self.remove_bad_chars()
174
173
  pass
@@ -184,10 +183,10 @@ class TextRecognizedToi(object):
184
183
  loc = DataString(loc).replace_all(' ').value
185
184
  # Remover "_" do final
186
185
  return re.sub(r"_$", "", loc)
187
-
186
+
188
187
  def line_postagem(self) -> str:
189
188
  pass
190
-
189
+
191
190
  def line_uc_and_toi(self) -> DataString | None:
192
191
  line_toi = None
193
192
  lines = self.to_list()
@@ -198,9 +197,9 @@ class TextRecognizedToi(object):
198
197
  line_toi = out
199
198
  break
200
199
  return line_toi
201
-
200
+
202
201
  def line_roteiro(self) -> DataString:
203
- return DataString(None)
202
+ return DataString(None)
204
203
 
205
204
 
206
205
  #======================================================================#
@@ -216,66 +215,125 @@ class TextRecognized(ABCTextRecognized):
216
215
  -> str
217
216
  -> PageDocumentPdf()
218
217
  """
218
+
219
219
  def __init__(self, bytes_recognized: bytes):
220
220
  super().__init__(bytes_recognized)
221
-
221
+
222
222
  self.list_bad_char: List[str] = [
223
- ':', ',', ';', '$', '=',
224
- '!', '}', '{', '(', ')',
225
- '|', '\\', '‘', '*'
226
- '¢', '“', '\'', '¢', '"',
227
- '#', '.', '<', '?', '>',
228
- '»', '@', '+', '[', ']',
229
- '%', '~', '¥', '♀',
223
+ ':', ',', ';', '$', '=',
224
+ '!', '}', '{', '(', ')',
225
+ '|', '\\', '‘', '*'
226
+ '¢', '“', '\'', '¢', '"',
227
+ '#', '.', '<', '?', '>',
228
+ '»', '@', '+', '[', ']',
229
+ '%', '~', '¥', '♀',
230
230
  ]
231
-
231
+
232
232
  def to_string(self) -> str | None:
233
233
  return self.to_page_pdf().to_string()
234
-
234
+
235
235
  def to_page_pdf(self) -> PageDocumentPdf:
236
236
  _page = PageDocumentPdf.create_from_page_bytes(
237
- self.bytes_recognized,
238
- library = LibraryPDF.FITZ
237
+ self.bytes_recognized,
238
+ library=LibraryPDF.FITZ
239
239
  )
240
240
  return _page
241
-
241
+
242
242
  def to_document(self) -> DocumentPdf:
243
- doc = DocumentPdf(pages=self.to_page_pdf())
244
- return doc
245
-
246
- def to_dict(self, separator = '\n', column_text = 'TEXT') -> Dict[str, List[str]]:
247
- image_dict:Dict[str, List[str]] = {
248
- column_text: [],
249
- 'NUM_LINHA': [],
243
+ return DocumentPdf(pages=[self.to_page_pdf()])
244
+
245
+ def to_dict(
246
+ self,
247
+ separator='\n',
248
+ column_text: ColumnTable.TEXT = ColumnTable.TEXT
249
+ ) -> Dict[str, List[str]]:
250
+ #
251
+ image_dict: Dict[str, List[str]] = {
252
+ column_text.value: [],
253
+ ColumnTable.NUMLINHA.value: [],
250
254
  }
251
- image_text:str = self.to_string()
255
+ image_text: str = self.to_string()
252
256
  if (image_text is None) or (image_text == ''):
253
257
  return {
254
- column_text: ['nan'],
255
- 'NUM_LINHA': ['nan'],
256
- }
257
-
258
+ column_text.value: ['nan'],
259
+ ColumnTable.NUMLINHA.value: ['nan'],
260
+ }
261
+
258
262
  image_lines: List[str] = image_text.split(separator)
259
263
  for num, line in enumerate(image_lines):
260
- image_dict[column_text].append(line)
261
- image_dict['NUM_LINHA'].append(num+1)
262
- return image_dict
263
-
264
- def to_dataframe(self, separator = '\n', column_text = 'TEXT') -> DataFrame:
264
+ image_dict[column_text.value].append(line)
265
+ image_dict[ColumnTable.NUMLINHA.value].append(f'{num + 1}')
266
+ return image_dict
267
+
268
+ def to_dataframe(self, separator='\n', column_text: ColumnTable.TEXT = ColumnTable.TEXT) -> DataFrame:
265
269
  return DataFrame(self.to_dict(separator, column_text))
266
-
267
-
270
+
271
+
272
+ class BinaryTesseract(object):
273
+ """
274
+ Fornece o caminho absoluto do tesseract instalado no sistema, se
275
+ disponível. Você pode usar um binário alternativo, basta informar
276
+ o caminho do binário desejado no construtor.
277
+ """
278
+ _instance = None # Atributo de classe para armazenar a instância singleton
279
+
280
+ def __new__(cls, *args, **kwargs):
281
+ if cls._instance is None:
282
+ cls._instance = super(BinaryTesseract, cls).__new__(cls)
283
+ return cls._instance
284
+
285
+ def __init__(
286
+ self,
287
+ path: File = None, *,
288
+ lang: str = None,
289
+ tess_data_dir: Directory = None
290
+ ):
291
+ #
292
+ # Garante que __init__ não será executado mais de uma vez
293
+ if hasattr(self, '_initialized') and self._initialized:
294
+ return
295
+ self._initialized = True
296
+
297
+ self.tess_data_dir: Directory = tess_data_dir
298
+ self.lang: str = lang
299
+ self.path_tesseract: File = path
300
+ if self.path_tesseract is None:
301
+ if KERNEL_TYPE == 'Windows':
302
+ name = 'tesseract.exe'
303
+ else:
304
+ name = 'tesseract'
305
+ output = which(name)
306
+ if output is not None:
307
+ if os.path.isfile(output):
308
+ print(f'[binary]: {output}')
309
+ self.path_tesseract = File(output)
310
+
311
+ def exists(self) -> bool:
312
+ """Verifica se o binário tesseract existe"""
313
+ if self.path_tesseract is None:
314
+ return False
315
+ return self.path_tesseract.exists()
316
+
317
+
268
318
  #======================================================================#
269
319
  # Modulo OCR pytesseract
270
320
  #======================================================================#
271
321
 
272
- class IModulePytesseract(ABCModuleOcr):
273
- def __init__(self, *, cmd_executable, lang = None, tess_data_dir = None):
322
+
323
+ class IPytesseract(ABCModuleOcr):
324
+
325
+ def __init__(
326
+ self, *,
327
+ cmd_executable: File,
328
+ lang: str = None,
329
+ tess_data_dir: Directory = None,
330
+ ):
331
+ #
274
332
  super().__init__(cmd_executable=cmd_executable, lang=lang, tess_data_dir=tess_data_dir)
275
- self.current_library:LibraryOCR = LibraryOCR.PYTESSERACT
276
- self._pyTesseract:pytesseract = pytesseract
333
+ self.current_library: LibraryOCR = LibraryOCR.PYTESSERACT
334
+ self._pyTesseract: pytesseract = pytesseract
277
335
  self._pyTesseract.tesseract_cmd = self.cmd_executable.absolute()
278
-
336
+
279
337
  def __get_tess_dir_config(self) -> str | None:
280
338
  """
281
339
  https://github.com/h/pytesseract
@@ -292,143 +350,142 @@ class IModulePytesseract(ABCModuleOcr):
292
350
  if not self.tess_data_dir.path.exists():
293
351
  return ''
294
352
  return r'--tessdata-dir "{}"'.format(self.tess_data_dir.absolute())
295
-
353
+
296
354
  def imag_to_string(self, img) -> str:
297
355
  if img.current_library == LibraryImage.OPENCV:
298
- text_img:str = self._pyTesseract.image_to_string(
356
+ text_img: str = self._pyTesseract.image_to_string(
299
357
  img.to_image_opencv(),
300
358
  lang=self.lang,
301
359
  config=self.__get_tess_dir_config()
302
360
  )
303
361
  else:
304
- text_img:str = self._pyTesseract.image_to_string(
305
- img.to_image_pil(),
306
- lang=self.lang,
307
- config=self.__get_tess_dir_config()
308
- )
362
+ text_img: str = self._pyTesseract.image_to_string(
363
+ img.to_image_pil(),
364
+ lang=self.lang,
365
+ config=self.__get_tess_dir_config()
366
+ )
309
367
  return text_img
310
-
368
+
311
369
  def image_recognize(self, img) -> TextRecognized:
370
+ """
371
+
372
+ @rtype: TextRecognized
373
+ """
312
374
  if img.current_library == LibraryImage.OPENCV:
313
- pdf_bytes:bytes = self._pyTesseract.image_to_pdf_or_hocr(
375
+ pdf_bytes: bytes = self._pyTesseract.image_to_pdf_or_hocr(
314
376
  img.to_image_opencv(),
315
377
  lang=self.lang,
316
378
  config=self.__get_tess_dir_config()
317
379
  )
318
380
  else:
319
- pdf_bytes:bytes = self._pyTesseract.image_to_pdf_or_hocr(
320
- img.to_image_pil(),
321
- lang=self.lang,
322
- config=self.__get_tess_dir_config()
323
- )
381
+ pdf_bytes: bytes = self._pyTesseract.image_to_pdf_or_hocr(
382
+ img.to_image_pil(),
383
+ lang=self.lang,
384
+ config=self.__get_tess_dir_config()
385
+ )
324
386
  return TextRecognized(pdf_bytes)
325
-
326
- def image_content_data(self, img) -> DataFrame:
387
+
388
+ def image_content_data(self, img: ImageObject) -> DataFrame:
389
+ """
390
+ Retorna um dataframe gerado por pytesseract
391
+
392
+ @rtype: DataFrame
393
+ """
327
394
  if img.current_library == LibraryImage.OPENCV:
328
- text_data:DataFrame = self._pyTesseract.image_to_data(
395
+ text_data: DataFrame = self._pyTesseract.image_to_data(
329
396
  img.to_image_opencv(),
330
397
  lang=self.lang,
331
398
  config=self.__get_tess_dir_config(),
332
399
  output_type=Output.DATAFRAME,
333
400
  )
334
401
  else:
335
- text_data:DataFrame = self._pyTesseract.image_to_data(
402
+ text_data: DataFrame = self._pyTesseract.image_to_data(
336
403
  img.to_image_pil(),
337
404
  lang=self.lang,
338
405
  config=self.__get_tess_dir_config(),
339
406
  output_type=Output.DATAFRAME,
340
407
  )
341
408
  return text_data
342
-
343
-
409
+
410
+
344
411
  #======================================================================#
345
412
  # Modulo OCR pyocr
346
413
  #======================================================================#
347
414
 
348
- class IModulePyOcr(ABCModuleOcr):
349
- def __init__(self, *, cmd_executable, lang = None, tess_data_dir = None):
415
+
416
+ class IPyOcr(ABCModuleOcr):
417
+ def __init__(self, *, cmd_executable, lang=None, tess_data_dir=None):
350
418
  super().__init__(cmd_executable=cmd_executable, lang=lang, tess_data_dir=tess_data_dir)
351
- self.current_library:LibraryOCR = LibraryOCR.PYOCR
352
- self.cmd_executable:File = cmd_executable
353
- self.tess_data_dir:Directory = tess_data_dir
419
+ self.current_library: LibraryOCR = LibraryOCR.PYOCR
420
+ self.cmd_executable: File = cmd_executable
421
+ self.tess_data_dir: Directory = tess_data_dir
354
422
  try:
355
423
  import pyocr
356
424
  import pyocr.tesseract
357
425
  except Exception as e:
358
- raise (e)
359
-
360
- pyocr_modules:List[ModuleType] = pyocr.get_available_tools()
426
+ raise e
427
+
428
+ pyocr_modules: List[ModuleType] = pyocr.get_available_tools()
361
429
  if len(pyocr_modules) == 0:
362
430
  raise ValueError(f"{__class__.__name__} No OCR tool found")
363
431
  # The tools are returned in the recommended order of usage
364
- self._pyOcr:pyocr.tesseract = pyocr_modules[0]
365
- langs:List[str] = self._pyOcr.get_available_languages()
432
+ self._pyOcr: pyocr.tesseract = pyocr_modules[0]
433
+ langs: List[str] = self._pyOcr.get_available_languages()
366
434
  if lang in lang:
367
435
  self.lang = lang
368
436
  else:
369
437
  self.lang = langs[0]
370
-
438
+
371
439
  # Ex: Will use tool 'libtesseract'
372
440
  print(f"Will use tool {self._pyOcr.get_name()}")
373
-
441
+
374
442
  def imag_to_string(self, img) -> str:
375
443
  return self._pyOcr.image_to_string(
376
444
  img.to_image_pil(),
377
445
  lang=self.lang,
378
446
  builder=pyocr.builders.TextBuilder()
379
447
  )
380
-
448
+
381
449
  def image_recognize(self, img) -> TextRecognized:
382
450
  raise NotImplementedError(f'{__class__.__name__} método não implementado')
383
-
451
+
384
452
  def image_content_data(self, img) -> DataFrame:
385
453
  raise NotImplementedError(f'{__class__.__name__} método não implementado')
386
-
387
-
388
- class ModuleOcr(ABCModuleOcr):
389
- def __init__(self, module_ocr: ABCModuleOcr):
390
- super().__init__(
391
- cmd_executable=module_ocr.cmd_executable,
392
- lang=module_ocr.lang,
393
- tess_data_dir=module_ocr.tess_data_dir
394
- )
395
- self.module_ocr:ABCModuleOcr = module_ocr
396
-
454
+
455
+
456
+ class ModuleOcr(object):
457
+ def __init__(self, module_ocr: IPytesseract | IPyOcr):
458
+ self.module_ocr: IPytesseract | IPyOcr = module_ocr
459
+
397
460
  def image_content_data(self, img: ImageObject) -> DataFrame:
398
461
  return self.module_ocr.image_content_data(img)
399
-
400
- def image_recognize(self, img:ImageObject) -> TextRecognized:
462
+
463
+ def image_recognize(self, img: ImageObject) -> TextRecognized:
401
464
  return self.module_ocr.image_recognize(img)
402
-
465
+
403
466
  def imag_to_string(self, img) -> str:
404
467
  return self.module_ocr.imag_to_string(img)
405
-
468
+
406
469
  @classmethod
407
470
  def create(
408
- cls,
409
- library_ocr = LibraryOCR.PYTESSERACT,
410
- *,
411
- path_tesseract: File,
412
- lang = None,
413
- tess_data_dir = None
414
- ) -> ModuleOcr:
471
+ cls,
472
+ library_ocr=LibraryOCR.PYTESSERACT,
473
+ *,
474
+ binary_tess: BinaryTesseract = BinaryTesseract(),
475
+ ) -> ModuleOcr:
415
476
  #
416
477
  if library_ocr == LibraryOCR.PYTESSERACT:
417
- _module_ocr = IModulePytesseract(cmd_executable=path_tesseract, lang=lang, tess_data_dir=tess_data_dir)
478
+ _module_ocr = IPytesseract(
479
+ cmd_executable=binary_tess.path_tesseract,
480
+ lang=binary_tess.lang,
481
+ tess_data_dir=binary_tess.tess_data_dir,
482
+ )
418
483
  elif library_ocr == LibraryOCR.PYOCR:
419
- _module_ocr = IModulePyOcr(cmd_executable=path_tesseract, lang=lang, tess_data_dir=tess_data_dir)
484
+ _module_ocr = IPyOcr(
485
+ cmd_executable=binary_tess.path_tesseract,
486
+ lang=binary_tess.lang,
487
+ tess_data_dir=binary_tess.tess_data_dir,
488
+ )
420
489
  else:
421
490
  raise NotImplementedError(f'{__class__.__name__}\nLibraryOcr não implementada: {library_ocr}')
422
491
  return cls(_module_ocr)
423
-
424
-
425
-
426
-
427
-
428
-
429
-
430
-
431
-
432
-
433
-
434
-
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ from pandas import DataFrame
4
+ from soup_files import (
5
+ File, Directory, UserAppDir, UserFileSystem,
6
+ InputFiles, JsonConvert, JsonData, LibraryDocs,
7
+ KERNEL_TYPE,
8
+ )
9
+
10
+ from convert_stream import (
11
+ LibraryDates, LibraryImage, LibraryPDF, LibImageToPDF, LibConvertPDF,
12
+ ImageObject, PdfStream, ImageStream, PageDocumentPdf, DocumentPdf,
13
+ ArrayString, DataString, print_line, print_title, FileToTable,
14
+ )
15
+ from convert_stream.models import ConvertStringDate
16
+ from convert_stream.table_files import ColumnTable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ocr_stream
3
- Version: 1.2
3
+ Version: 1.4
4
4
  Summary: Extrair textos de documentos digitalizados e imagens.
5
5
  Author: Bruno Chaves
6
6
  Requires: PyPDF2
@@ -17,14 +17,14 @@ Requires: soup_files
17
17
  Requires: convert_stream
18
18
  Requires-Python: >=3.11
19
19
  Description-Content-Type: text/markdown
20
- Requires-Dist: PyPDF2==3.0.1
21
- Requires-Dist: PyMuPDF==1.25.5
22
- Requires-Dist: reportlab==4.3.1
23
- Requires-Dist: openpyxl==3.1.5
24
- Requires-Dist: pandas==2.2.3
25
- Requires-Dist: Pillow==11.2.1
26
- Requires-Dist: opencv-python==4.11.0.86
27
- Requires-Dist: convert_stream
20
+ Requires-Dist: PyMuPDF
21
+ Requires-Dist: reportlab
22
+ Requires-Dist: openpyxl
23
+ Requires-Dist: pandas
24
+ Requires-Dist: Pillow>=11.3.0
25
+ Requires-Dist: requests>=2.32.4
26
+ Requires-Dist: opencv-python
27
+ Requires-Dist: convert_stream>=1.4
28
28
  Requires-Dist: soup_files
29
29
  Dynamic: requires
30
30
 
@@ -0,0 +1,9 @@
1
+ PyMuPDF
2
+ reportlab
3
+ openpyxl
4
+ pandas
5
+ Pillow>=11.3.0
6
+ requests>=2.32.4
7
+ opencv-python
8
+ convert_stream>=1.4
9
+ soup_files
@@ -4,23 +4,24 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ocr_stream"
7
- version = "1.2"
7
+ version = "1.4"
8
8
  authors = [
9
9
  { name="Bruno Chaves"},
10
10
  ]
11
+
11
12
  description = "Extrair textos de documentos digitalizados e imagens."
12
13
  readme = "README.md"
13
14
  requires-python = ">=3.11"
14
15
 
15
16
  dependencies = [
16
- "PyPDF2==3.0.1",
17
- "PyMuPDF==1.25.5",
18
- "reportlab==4.3.1",
19
- "openpyxl==3.1.5",
20
- "pandas==2.2.3",
21
- "Pillow==11.2.1",
22
- "opencv-python==4.11.0.86",
23
- "convert_stream",
17
+ "PyMuPDF",
18
+ "reportlab",
19
+ "openpyxl",
20
+ "pandas",
21
+ "Pillow>=11.3.0",
22
+ "requests>=2.32.4",
23
+ "opencv-python",
24
+ "convert_stream>=1.4",
24
25
  "soup_files",
25
26
  ]
26
27
 
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env python3
2
- #
3
-
4
- __version__ = '1.2'
5
- __modify_data__ = '2025-06-22'
@@ -1,108 +0,0 @@
1
- #!/usr/bin/env python3
2
- from __future__ import annotations
3
- from ocr_stream.utils import (
4
- File, Directory, ImageObject,
5
- DataFrame, PageDocumentPdf,
6
- DocumentPdf, PdfStream, LibraryPDF,
7
- )
8
- from ocr_stream.models import ABCRecognizeImage, LibraryOCR
9
- from ocr_stream.modules_ocr import ModuleOcr, TextRecognized
10
-
11
-
12
- class RecognizeImage(ABCRecognizeImage):
13
- def __init__(self, module_ocr):
14
- super().__init__(module_ocr)
15
-
16
- def image_content_data(self, img: ImageObject | File) -> DataFrame:
17
- if isinstance(img, File):
18
- return self.module_ocr.image_content_data(ImageObject.create_from_file(img))
19
- elif isinstance(img, ImageObject):
20
- return self.module_ocr.image_content_data(img)
21
- else:
22
- raise ValueError(f'{__class__.__name__}\nUse: ImageObject() ou File(), não {type(img)}')
23
-
24
- def image_recognize(self, img: ImageObject | File) -> TextRecognized:
25
- if isinstance(img, File):
26
- return self.module_ocr.image_recognize(ImageObject.create_from_file(img))
27
- elif isinstance(img, ImageObject):
28
- return self.module_ocr.image_recognize(img)
29
- else:
30
- raise ValueError(f'{__class__.__name__}\nUse: ImageObject() ou File(), não {type(img)}')
31
-
32
- def image_to_string(self, img: ImageObject | File) -> str:
33
- if isinstance(img, File):
34
- return self.module_ocr.imag_to_string(ImageObject.create_from_file(img))
35
- elif isinstance(img, ImageObject):
36
- return self.module_ocr.imag_to_string(img)
37
- else:
38
- raise ValueError(f'{__class__.__name__}\nUse: ImageObject() ou File(), não {type(img)}')
39
-
40
- @classmethod
41
- def create(
42
- cls,
43
- library_ocr = LibraryOCR.PYTESSERACT,
44
- *,
45
- path_tesseract,
46
- lang=None,
47
- tess_data_dir=None
48
- ) -> RecognizeImage:
49
- #
50
- module_ocr: ModuleOcr = ModuleOcr.create(
51
- library_ocr,
52
- path_tesseract=path_tesseract,
53
- lang=lang,
54
- tess_data_dir=tess_data_dir
55
- )
56
- return cls(module_ocr)
57
-
58
-
59
- class RecognizePdf(object):
60
- def __init__(self, recognize_image: RecognizeImage):
61
- self.recognize_image: RecognizeImage = recognize_image
62
-
63
- def recognize_page_pdf(self, page:PageDocumentPdf) -> PageDocumentPdf:
64
- """
65
- Converte a página em Imagem, reconhece o texto com OCR e
66
- retorna uma nova página com o texto reconhecido.
67
- """
68
- pdf_stream = PdfStream(library_pdf=LibraryPDF.FITZ)
69
- pdf_stream.add_page(page)
70
- img: ImageObject = pdf_stream.to_images()[0]
71
- text_recognized:TextRecognized = self.recognize_image.image_recognize(img)
72
- return text_recognized.to_page_pdf()
73
-
74
- def recognize_document(self, doc:DocumentPdf) -> DocumentPdf:
75
- new: DocumentPdf = DocumentPdf()
76
- for page in doc.pages:
77
- recognized_page: PageDocumentPdf = self.recognize_page_pdf(page)
78
- new.add_page(recognized_page)
79
- return new
80
-
81
- @classmethod
82
- def create(
83
- cls,
84
- library_ocr = LibraryOCR.PYTESSERACT,
85
- *,
86
- path_tesseract: File,
87
- lang:str=None,
88
- tess_data_dir: Directory=None,
89
- ) -> RecognizePdf:
90
- #
91
- #
92
- recognize_image = RecognizeImage.create(
93
- library_ocr,
94
- path_tesseract=path_tesseract,
95
- lang=lang,
96
- tess_data_dir=tess_data_dir
97
- )
98
- return cls(recognize_image)
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env python3
2
- #
3
- from __future__ import annotations
4
- from abc import ABC, abstractmethod
5
-
6
- from pandas import DataFrame
7
- from soup_files import File, Directory
8
- from convert_stream import ImageObject
9
- from ocr_stream.models.m_modules_ocr import ABCModuleOcr, ABCTextRecognized, LibraryOCR
10
-
11
- class ABCRecognizeImage(ABC):
12
- def __init__(self, module_ocr:ABCModuleOcr):
13
- super().__init__()
14
- self.module_ocr:ABCModuleOcr = module_ocr
15
-
16
- @abstractmethod
17
- def image_to_string(self, img:ImageObject | File) -> str:
18
- pass
19
-
20
- @abstractmethod
21
- def image_recognize(self, img:ImageObject | File) -> ABCTextRecognized:
22
- pass
23
-
24
- @abstractmethod
25
- def image_content_data(self, img:ImageObject | File) -> DataFrame:
26
- pass
27
-
28
- @classmethod
29
- def create(
30
- cls,
31
- library_ocr:LibraryOCR = LibraryOCR.PYTESSERACT,
32
- *,
33
- path_tesseract:File,
34
- lang:str=None,
35
- tess_data_dir:Directory=None
36
- ) -> ABCRecognizeImage:
37
- pass
38
-
39
-
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env python3
2
- #
3
- from pandas import DataFrame
4
- from soup_files import (
5
- File, Directory, UserAppDir, UserFileSystem, InputFiles,
6
- JsonConvert, JsonData
7
- )
8
-
9
- from convert_stream import (
10
- LibraryDates, LibraryImage, LibraryPDF, ImageObject,
11
- PdfStream, ImageStream,PageDocumentPdf, DocumentPdf,
12
- ArrayString, DataString, ConvertDate, print_line, print_title
13
- )
@@ -1,9 +0,0 @@
1
- PyPDF2==3.0.1
2
- PyMuPDF==1.25.5
3
- reportlab==4.3.1
4
- openpyxl==3.1.5
5
- pandas==2.2.3
6
- Pillow==11.2.1
7
- opencv-python==4.11.0.86
8
- convert_stream
9
- soup_files
File without changes
File without changes
File without changes