opex792 commited on
Commit
2495559
·
verified ·
1 Parent(s): f318822

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -174
app.py CHANGED
@@ -7,6 +7,7 @@ import numpy as np
7
  import pytesseract
8
  from flask import Flask, render_template, jsonify
9
  from threading import Lock
 
10
 
11
  app = Flask(__name__)
12
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
@@ -19,179 +20,46 @@ HEADERS = {
19
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
20
  }
21
 
22
- def order_points(pts):
23
  """
24
- Упорядочивает 4 точки в последовательности: верх-лево, верх-право, низ-право, низ-лево.
25
  """
26
- rect = np.zeros((4, 2), dtype="float32")
27
- s = pts.sum(axis=1)
28
- rect[0] = pts[np.argmin(s)] # верх-лево
29
- rect[2] = pts[np.argmax(s)] # низ-право
30
- diff = np.diff(pts, axis=1)
31
- rect[1] = pts[np.argmin(diff)] # верх-право
32
- rect[3] = pts[np.argmax(diff)] # низ-лево
33
- return rect
34
-
35
- def find_text_region_contour(image):
36
- """
37
- Находит контур текстовой области более надежным способом.
38
- """
39
- # Создаем несколько версий для поиска контуров
40
-
41
- # Метод 1: Морфологические операции для объединения символов
42
- kernel_horizontal = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 1))
43
- kernel_vertical = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 10))
44
-
45
- # Расширяем по горизонтали чтобы соединить буквы
46
- dilated_h = cv2.dilate(image, kernel_horizontal, iterations=2)
47
- # Небольшое расширение по вертикали
48
- dilated = cv2.dilate(dilated_h, kernel_vertical, iterations=1)
49
-
50
- # Закрываем промежутки
51
- kernel_close = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 8))
52
- closed = cv2.morphologyEx(dilated, cv2.MORPH_CLOSE, kernel_close, iterations=1)
53
 
54
- return closed
 
 
 
 
55
 
56
- def correct_perspective_improved(image):
57
- """
58
- Улучшенная коррекция перспективы с более надежным поиском текстовой области.
59
- """
60
- logging.info("Запуск улучшенной коррекции перспективы...")
61
-
62
- if len(image.shape) == 3:
63
- gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
64
  else:
65
- gray = image.copy()
66
-
67
- # Бинаризация если еще не сделана
68
- if len(np.unique(gray)) > 2:
69
- _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
70
- else:
71
- binary = gray.copy()
72
-
73
- # Инвертируем если нужно (текст должен быть белым)
74
- if np.sum(binary == 255) < np.sum(binary == 0):
75
- binary = cv2.bitwise_not(binary)
76
-
77
- # Находим область текста
78
- text_region = find_text_region_contour(binary)
79
-
80
- # Находим контуры объединенной текстовой области
81
- contours, _ = cv2.findContours(text_region, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
82
-
83
- if not contours:
84
- logging.warning("Контуры текстовой области не найдены.")
85
- return image
86
-
87
- # Берем самый большой контур
88
- largest_contour = max(contours, key=cv2.contourArea)
89
-
90
- # Проверяем минимальную площадь
91
- if cv2.contourArea(largest_contour) < 1000:
92
- logging.warning("Найденная текстовая область слишком мала.")
93
  return image
94
-
95
- # Аппроксимируем контур до прямоугольника
96
- epsilon = 0.02 * cv2.arcLength(largest_contour, True)
97
- approx = cv2.approxPolyDP(largest_contour, epsilon, True)
98
-
99
- # Если не получили 4 точки, используем минимальный ограничивающий прямоугольник
100
- if len(approx) != 4:
101
- rect = cv2.minAreaRect(largest_contour)
102
- box = cv2.boxPoints(rect)
103
- approx = np.int0(box)
104
-
105
- # Преобразуем к нужному формату
106
- if approx.shape[1] == 1:
107
- approx = approx.squeeze(1)
108
-
109
- # Упорядочиваем точки
110
- ordered_points = order_points(approx.astype("float32"))
111
-
112
- # Вычисляем размеры выходного изображения
113
- (tl, tr, br, bl) = ordered_points
114
-
115
- # Ширина: максимум из верхней и нижней стороны
116
- width_top = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
117
- width_bottom = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
118
- max_width = max(int(width_top), int(width_bottom))
119
-
120
- # Высота: максимум из левой и правой стороны
121
- height_left = np.sqrt(((bl[0] - tl[0]) ** 2) + ((bl[1] - tl[1]) ** 2))
122
- height_right = np.sqrt(((br[0] - tr[0]) ** 2) + ((br[1] - tr[1]) ** 2))
123
- max_height = max(int(height_left), int(height_right))
124
-
125
- # Проверяем адекватность размеров
126
- if max_width < 50 or max_height < 20:
127
- logging.warning(f"Неадекватные размеры области: {max_width}x{max_height}")
128
  return image
129
-
130
- # Добавляем небольшие отступы для лучшего распознавания
131
- padding = 10
132
- max_width += padding * 2
133
- max_height += padding * 2
134
-
135
- # Целевые точки (прямоугольник)
136
- dst_points = np.array([
137
- [padding, padding], # верх-лево
138
- [max_width - padding, padding], # верх-право
139
- [max_width - padding, max_height - padding], # низ-право
140
- [padding, max_height - padding] # низ-лево
141
- ], dtype="float32")
142
-
143
- # Вычисляем матрицу трансформации и применяем
144
- transform_matrix = cv2.getPerspectiveTransform(ordered_points, dst_points)
145
- corrected = cv2.warpPerspective(
146
- image,
147
- transform_matrix,
148
- (max_width, max_height),
149
- flags=cv2.INTER_LINEAR,
150
- borderMode=cv2.BORDER_CONSTANT,
151
- borderValue=(255, 255, 255)
152
- )
153
-
154
- logging.info(f"Коррекция перспективы применена. Размер: {max_width}x{max_height}")
155
- return corrected
156
 
157
- def enhance_for_ocr(image):
158
- """
159
- Дополнительная обработка для улучшения OCR.
160
- """
161
- # Преобразуем в градации серого если нужно
162
- if len(image.shape) == 3:
163
- gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
164
- else:
165
- gray = image.copy()
166
 
167
- # Увеличиваем контраст
168
- clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
169
- enhanced = clahe.apply(gray)
170
 
171
- # Бинаризация с адаптивным порогом
172
- binary = cv2.adaptiveThreshold(
173
- enhanced, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
174
- cv2.THRESH_BINARY, 11, 2
175
- )
176
 
177
- # Морфологическая очистка
178
- kernel = np.ones((2,2), np.uint8)
179
- cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=1)
180
- cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_OPEN, kernel, iterations=1)
181
-
182
- return cleaned
183
 
184
- def correct_perspective(image):
185
- """
186
- Основная функция коррекции перспективы.
187
- """
188
- # Применяем улучшенную коррекцию перспективы
189
- corrected = correct_perspective_improved(image)
190
-
191
- # Дополнительная обработка для OCR
192
- enhanced = enhance_for_ocr(corrected)
193
-
194
- return enhanced
195
 
196
  def fetch_and_solve_captcha():
197
  try:
@@ -201,42 +69,40 @@ def fetch_and_solve_captcha():
201
 
202
  data = response.json()
203
  base64_image_data = data.get("Image")
204
- if not base64_image_data: return None
 
205
 
206
  image_bytes = base64.b64decode(base64_image_data)
207
  nparr = np.frombuffer(image_bytes, np.uint8)
208
  original_image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
209
 
210
- # Увеличиваем масштаб для лучшего качества
211
- scale_factor = 3 # Увеличили с 2 до 3
212
  width = int(original_image.shape[1] * scale_factor)
213
  height = int(original_image.shape[0] * scale_factor)
214
  upscaled_image = cv2.resize(original_image, (width, height), interpolation=cv2.INTER_CUBIC)
215
 
216
- # Выделение синего текста
217
  hsv = cv2.cvtColor(upscaled_image, cv2.COLOR_BGR2HSV)
218
  lower_blue = np.array([90, 50, 50])
219
  upper_blue = np.array([130, 255, 255])
220
  mask = cv2.inRange(hsv, lower_blue, upper_blue)
221
 
222
- # Очистка маски
223
  kernel = np.ones((2, 2), np.uint8)
224
- cleaned_mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
 
 
 
225
 
226
- # ПРИМЕНЯЕМ УЛУЧШЕННУЮ КОРРЕКЦИЮ ПЕРСПЕКТИВЫ
227
- corrected_image = correct_perspective(cleaned_mask)
228
 
229
- # Настройки для Tesseract - более точные для цифр
230
- tesseract_config = r'--oem 3 --psm 8 -c tessedit_char_whitelist=0123456789'
231
- text = pytesseract.image_to_string(corrected_image, config=tesseract_config)
232
  recognized_text = re.sub(r'\s+', '', text).strip() or "Не распознано"
233
  logging.info(f"Распознано: {recognized_text}")
234
 
235
- # Кодируем изображения для отображения
236
  _, buffer_orig = cv2.imencode('.png', original_image)
237
  original_b64 = base64.b64encode(buffer_orig).decode('utf-8')
238
 
239
- _, buffer_proc = cv2.imencode('.png', corrected_image)
240
  processed_b64 = base64.b64encode(buffer_proc).decode('utf-8')
241
 
242
  return {
@@ -269,3 +135,5 @@ if __name__ == '__main__':
269
  solved_captchas.append(initial_captcha)
270
 
271
  app.run(host='0.0.0.0', port=7860, debug=False)
 
 
 
7
  import pytesseract
8
  from flask import Flask, render_template, jsonify
9
  from threading import Lock
10
+ import math
11
 
12
  app = Flask(__name__)
13
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
20
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
21
  }
22
 
23
+ def deskew(image):
24
  """
25
+ Вычисляет угол наклона и поворачивает изображение, но только если угол адекватен.
26
  """
27
+ gray = cv2.bitwise_not(image)
28
+ coords = np.column_stack(np.where(gray > 0))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ if len(coords) < 1:
31
+ logging.warning("Нет контента для выпрямления, пропуск deskew.")
32
+ return image
33
+
34
+ angle = cv2.minAreaRect(coords)[-1]
35
 
36
+ if angle < -45:
37
+ correction_angle = -(90 + angle)
 
 
 
 
 
 
38
  else:
39
+ correction_angle = -angle
40
+
41
+ # --- КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: ПРОВЕРКА НА АДЕКВАТНОСТЬ ---
42
+ # Если вычисленный угол слишком большой, это почти наверняка ошибка.
43
+ # Безопаснее пропустить поворот, чем повернуть на 90 градусов.
44
+ if abs(correction_angle) > 45:
45
+ logging.warning(f"Вычислен неадекватный угол {correction_angle:.2f}. Пропуск коррекции наклона.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  return image
47
+
48
+ # Пропускаем, если наклон незначителен
49
+ if abs(correction_angle) < 1:
50
+ logging.info("Угол наклона незначителен, коррекция не требуется.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  return image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ logging.info(f"Обнаружен адекватный угол наклона: {correction_angle:.2f} градусов. Применяется коррекция.")
 
 
 
 
 
 
 
 
54
 
55
+ (h, w) = image.shape[:2]
56
+ center = (w // 2, h // 2)
57
+ M = cv2.getRotationMatrix2D(center, correction_angle, 1.0)
58
 
59
+ rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT, borderValue=(255,255,255))
 
 
 
 
60
 
61
+ return rotated
 
 
 
 
 
62
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  def fetch_and_solve_captcha():
65
  try:
 
69
 
70
  data = response.json()
71
  base64_image_data = data.get("Image")
72
+ if not base64_image_data:
73
+ return None
74
 
75
  image_bytes = base64.b64decode(base64_image_data)
76
  nparr = np.frombuffer(image_bytes, np.uint8)
77
  original_image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
78
 
79
+ scale_factor = 2
 
80
  width = int(original_image.shape[1] * scale_factor)
81
  height = int(original_image.shape[0] * scale_factor)
82
  upscaled_image = cv2.resize(original_image, (width, height), interpolation=cv2.INTER_CUBIC)
83
 
 
84
  hsv = cv2.cvtColor(upscaled_image, cv2.COLOR_BGR2HSV)
85
  lower_blue = np.array([90, 50, 50])
86
  upper_blue = np.array([130, 255, 255])
87
  mask = cv2.inRange(hsv, lower_blue, upper_blue)
88
 
 
89
  kernel = np.ones((2, 2), np.uint8)
90
+ cleaned_mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2)
91
+
92
+ inverted_mask = cv2.bitwise_not(cleaned_mask)
93
+ deskewed_image = deskew(inverted_mask)
94
 
95
+ processed_image = deskewed_image
 
96
 
97
+ tesseract_config = r'--oem 3 --psm 6 -c tessedit_char_whitelist=0123456789'
98
+ text = pytesseract.image_to_string(processed_image, config=tesseract_config)
 
99
  recognized_text = re.sub(r'\s+', '', text).strip() or "Не распознано"
100
  logging.info(f"Распознано: {recognized_text}")
101
 
 
102
  _, buffer_orig = cv2.imencode('.png', original_image)
103
  original_b64 = base64.b64encode(buffer_orig).decode('utf-8')
104
 
105
+ _, buffer_proc = cv2.imencode('.png', processed_image)
106
  processed_b64 = base64.b64encode(buffer_proc).decode('utf-8')
107
 
108
  return {
 
135
  solved_captchas.append(initial_captcha)
136
 
137
  app.run(host='0.0.0.0', port=7860, debug=False)
138
+
139
+