ASureevaA commited on
Commit
1af7fc8
·
1 Parent(s): efbc18d
Files changed (1) hide show
  1. app.py +55 -85
app.py CHANGED
@@ -7,7 +7,6 @@ import soundfile as soundfile_module
7
  import torch
8
  import gradio as gradio_module
9
  from PIL import Image
10
- import easyocr
11
  from transformers import (
12
  pipeline,
13
  VitsModel,
@@ -18,83 +17,61 @@ from transformers import (
18
  # 1. Настройки устройства
19
  # ============================
20
 
21
- device_string: str = "cuda" if torch.cuda.is_available() else "cpu"
 
 
 
 
 
 
22
 
23
 
24
  # ============================
25
- # 2. OCR (easyocr, английский)
26
  # ============================
27
 
28
- # TODO_USER: при желании можно добавить другие языки, но тогда конспект и TTS всё равно останутся на английском
29
- ocr_reader = easyocr.Reader(
30
- ["en"], # языки
31
- gpu=(device_string == "cuda"),
 
 
 
32
  )
33
 
34
 
35
  def run_ocr(image_object: Image.Image) -> str:
36
  """
37
- OCR для печатного английского текста.
38
- Используем easyocr, потому что он реально более устойчивый для
39
- произвольных сканов/фото, чем большинство трансформеров, которые мы пробовали.
 
40
  """
41
  if image_object is None:
42
  return ""
43
 
44
  rgb_image_object: Image.Image = image_object.convert("RGB")
45
 
46
- # easyocr работает с numpy-массивом
47
- numpy_image = numpy_module.array(rgb_image_object)
48
-
49
- results = ocr_reader.readtext(
50
- numpy_image,
51
- detail=1, # возвращаем bbox + текст + confidence
52
- paragraph=True, # склеивать текст в параграфы, где это возможно
53
- )
54
-
55
- text_parts = []
56
- for bbox, text_value, confidence_value in results:
57
- if not text_value:
58
- continue
59
- # TODO_USER: при желании можно фильтровать по confidence_value
60
- text_parts.append(text_value)
61
-
62
- recognized_text: str = "\n".join(text_parts).strip()
63
  return recognized_text
64
 
65
 
66
  # ============================
67
- # 3. Трансформер #1: классификация текста
68
- # ============================
69
-
70
- text_classifier_pipeline = pipeline(
71
- task="text-classification",
72
- model="distilbert-base-uncased-finetuned-sst-2-english",
73
- )
74
-
75
-
76
- def run_text_classification(input_text: str) -> str:
77
- """
78
- Пример анализа текста трансформером:
79
- используем sentiment-классификатор как демонстрацию.
80
- Возвращаем строку вида: "label: POSITIVE, score: 0.98".
81
- """
82
- cleaned_text: str = input_text.strip()
83
- if not cleaned_text:
84
- return ""
85
-
86
- result_list = text_classifier_pipeline(cleaned_text)
87
- result = result_list[0]
88
-
89
- label_value: str = str(result.get("label", ""))
90
- score_value: float = float(result.get("score", 0.0))
91
-
92
- classification_text: str = f"{label_value} (score={score_value:.3f})"
93
- return classification_text
94
-
95
-
96
- # ============================
97
- # 4. Трансформер #2: суммаризация (английский)
98
  # ============================
99
 
100
  summary_pipeline = pipeline(
@@ -121,8 +98,8 @@ def run_summarization(
121
  max(32, word_count + 20),
122
  )
123
 
 
124
  if word_count < 8:
125
- # TODO_USER: для очень короткого текста суммаризация сомнительна, возвращаем исходный текст
126
  return cleaned_text
127
 
128
  summary_result_list = summary_pipeline(
@@ -137,7 +114,7 @@ def run_summarization(
137
 
138
 
139
  # ============================
140
- # 5. Трансформер #3: TTS (английский, MMS VITS)
141
  # ============================
142
 
143
  tts_model: VitsModel = VitsModel.from_pretrained("facebook/mms-tts-eng")
@@ -150,7 +127,7 @@ def run_tts(summary_text: str) -> Optional[str]:
150
  Озвучка английского текста конспекта через VitsModel (facebook/mms-tts-eng).
151
 
152
  Если модель внутри упадёт (известный баг на некоторых странных инпутах),
153
- мы просто вернём None и не будем ронять всё приложение.
154
  """
155
  cleaned_text: str = summary_text.strip()
156
  if not cleaned_text:
@@ -195,24 +172,21 @@ def run_tts(summary_text: str) -> Optional[str]:
195
 
196
 
197
  # ============================
198
- # 6. Полный пайплайн
199
  # ============================
200
 
201
  def full_flow(
202
  image_object: Image.Image,
203
  max_summary_tokens: int = 128,
204
- ) -> Tuple[str, str, str, Optional[str]]:
205
  """
206
  Полный пайплайн:
207
- 1) OCR (easyocr): изображение -> исходный текст (английский)
208
- 2) Классификация текста трансформером (sentiment)
209
- 3) Суммаризация: текст -> конспект
210
- 4) TTS: конспект -> .wav файл (или None)
211
  """
212
  recognized_text: str = run_ocr(image_object=image_object)
213
 
214
- classification_text: str = run_text_classification(recognized_text)
215
-
216
  summary_text: str = run_summarization(
217
  input_text=recognized_text,
218
  max_summary_tokens=max_summary_tokens,
@@ -220,11 +194,11 @@ def full_flow(
220
 
221
  audio_file_path: Optional[str] = run_tts(summary_text=summary_text)
222
 
223
- return recognized_text, classification_text, summary_text, audio_file_path
224
 
225
 
226
  # ============================
227
- # 7. Gradio UI (на русском)
228
  # ============================
229
 
230
  gradio_interface = gradio_module.Interface(
@@ -244,29 +218,25 @@ gradio_interface = gradio_module.Interface(
244
  ],
245
  outputs=[
246
  gradio_module.Textbox(
247
- label="Распознанный текст (OCR, easyocr)",
248
- lines=8,
249
- ),
250
- gradio_module.Textbox(
251
- label="Анализ текста (классификация, DistilBERT)",
252
- lines=2,
253
  ),
254
  gradio_module.Textbox(
255
- label="Конспект (английский текст, DistilBART)",
256
  lines=6,
257
  ),
258
  gradio_module.Audio(
259
- label="Озвучка конспекта (английский TTS, VITS)",
260
  type="filepath",
261
  ),
262
  ],
263
- title="Картинка → Текст → Анализ → Конспект → Озвучка",
264
  description=(
265
- "1) easyocr распознаёт печатный английский текст с картинки.\n"
266
- "2) Трансформер-классификатор (DistilBERT) оценивает тон текста.\n"
267
- "3) Трансформер-суммаризатор (DistilBART) делает краткий конспект.\n"
268
- "4) Трансформер TTS (MMS VITS) озвучивает конспект.\n"
269
- "В проекте используются три трансформера с Hugging Face, OCR сделан через easyocr."
270
  ),
271
  )
272
 
 
7
  import torch
8
  import gradio as gradio_module
9
  from PIL import Image
 
10
  from transformers import (
11
  pipeline,
12
  VitsModel,
 
17
  # 1. Настройки устройства
18
  # ============================
19
 
20
+ # TODO_USER: для нормальной работы olmOCR почти наверняка нужен GPU
21
+ if torch.cuda.is_available():
22
+ device_string: str = "cuda"
23
+ pipeline_device_index: int = 0
24
+ else:
25
+ device_string = "cpu"
26
+ pipeline_device_index = -1 # Gradio/transformers: -1 = CPU
27
 
28
 
29
  # ============================
30
+ # 2. OCR на olmOCR-2-7B-1025-FP8
31
  # ============================
32
 
33
+ # Модель: allenai/olmOCR-2-7B-1025-FP8
34
+ # По README это image-to-text трансформер, так что используем стандартный pipeline.
35
+ ocr_pipeline = pipeline(
36
+ task="image-to-text",
37
+ model="allenai/olmOCR-2-7B-1025-FP8",
38
+ device=pipeline_device_index,
39
+ # TODO_USER: при необходимости можно добавить torch_dtype=..., но лучше сначала проверить дефолт
40
  )
41
 
42
 
43
  def run_ocr(image_object: Image.Image) -> str:
44
  """
45
+ OCR для печатного английского текста с помощью olmOCR-2-7B-1025-FP8.
46
+
47
+ Вход: PIL.Image (страница/скриншот).
48
+ Выход: строка текста, которую модель сгенерировала как распознавание.
49
  """
50
  if image_object is None:
51
  return ""
52
 
53
  rgb_image_object: Image.Image = image_object.convert("RGB")
54
 
55
+ # olmOCR поддерживает прямой вызов через pipeline("image-to-text").
56
+ # Ожидаемый формат ответа: список dict вида [{"generated_text": "..."}].
57
+ result = ocr_pipeline(rgb_image_object)
58
+
59
+ if isinstance(result, list) and len(result) > 0:
60
+ first_item = result[0]
61
+ if isinstance(first_item, dict) and "generated_text" in first_item:
62
+ text_value: str = str(first_item["generated_text"])
63
+ else:
64
+ # TODO_USER: непредвиденный формат ответа, логировать при необходимости
65
+ text_value = str(first_item)
66
+ else:
67
+ text_value = str(result)
68
+
69
+ recognized_text: str = text_value.strip()
 
 
70
  return recognized_text
71
 
72
 
73
  # ============================
74
+ # 3. Суммаризация (английский DistilBART)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  # ============================
76
 
77
  summary_pipeline = pipeline(
 
98
  max(32, word_count + 20),
99
  )
100
 
101
+ # Для совсем короткого текста суммаризация мало смысла
102
  if word_count < 8:
 
103
  return cleaned_text
104
 
105
  summary_result_list = summary_pipeline(
 
114
 
115
 
116
  # ============================
117
+ # 4. TTS (английский, MMS VITS)
118
  # ============================
119
 
120
  tts_model: VitsModel = VitsModel.from_pretrained("facebook/mms-tts-eng")
 
127
  Озвучка английского текста конспекта через VitsModel (facebook/mms-tts-eng).
128
 
129
  Если модель внутри упадёт (известный баг на некоторых странных инпутах),
130
+ просто возвращаем None и не роняем всё приложение.
131
  """
132
  cleaned_text: str = summary_text.strip()
133
  if not cleaned_text:
 
172
 
173
 
174
  # ============================
175
+ # 5. Полный пайплайн
176
  # ============================
177
 
178
  def full_flow(
179
  image_object: Image.Image,
180
  max_summary_tokens: int = 128,
181
+ ) -> Tuple[str, str, Optional[str]]:
182
  """
183
  Полный пайплайн:
184
+ 1) OCR: изображение -> исходный английский текст (olmOCR)
185
+ 2) Суммаризация: текст -> конспект (DistilBART)
186
+ 3) TTS: конспект -> .wav файл (или None, если TTS не смог)
 
187
  """
188
  recognized_text: str = run_ocr(image_object=image_object)
189
 
 
 
190
  summary_text: str = run_summarization(
191
  input_text=recognized_text,
192
  max_summary_tokens=max_summary_tokens,
 
194
 
195
  audio_file_path: Optional[str] = run_tts(summary_text=summary_text)
196
 
197
+ return recognized_text, summary_text, audio_file_path
198
 
199
 
200
  # ============================
201
+ # 6. Gradio UI (по-русски)
202
  # ============================
203
 
204
  gradio_interface = gradio_module.Interface(
 
218
  ],
219
  outputs=[
220
  gradio_module.Textbox(
221
+ label="Распознанный текст (olmOCR)",
222
+ lines=10,
 
 
 
 
223
  ),
224
  gradio_module.Textbox(
225
+ label="Конспект (английский текст)",
226
  lines=6,
227
  ),
228
  gradio_module.Audio(
229
+ label="Озвучка конспекта (английский TTS)",
230
  type="filepath",
231
  ),
232
  ],
233
+ title="Картинка → Текст → Конспект → Озвучка (olmOCR + английские модели)",
234
  description=(
235
+ "1) olmOCR-2-7B-1025-FP8 распознаёт текст с документа.\n"
236
+ "2) Английский трансформер суммаризации д��лает краткий пересказ.\n"
237
+ "3) VITS-модель MMS (facebook/mms-tts-eng) озвучивает конспект.\n\n"
238
+ "Если озвучка не сгенерировалась, значит конкретный текст не понравился TTS-модели "
239
+ "и она упала внутри — пайплайн просто пропустит аудио."
240
  ),
241
  )
242