opex792 commited on
Commit
5985dd0
·
verified ·
1 Parent(s): 9b133e8

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -36
app.py CHANGED
@@ -7,7 +7,6 @@ import numpy as np
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,45 +19,80 @@ HEADERS = {
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():
@@ -69,8 +103,7 @@ def fetch_and_solve_captcha():
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)
@@ -89,12 +122,14 @@ def fetch_and_solve_captcha():
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}")
 
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
  '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 correct_perspective(image):
36
+ """
37
+ Находит текстовый блок и исправляет искажение перспективы.
38
+ """
39
+ logging.info("Запуск коррекции перспективы...")
40
+ # Создаем копию, чтобы не изменять оригинал
41
+ img_for_transform = image.copy()
42
 
43
+ # Инвертируем изображение для поиска контуров (белый текст на черном фоне)
44
+ inverted = cv2.bitwise_not(img_for_transform)
45
+
46
+ # Находим контуры. RETR_EXTERNAL находит только внешние контуры.
47
+ contours, _ = cv2.findContours(inverted, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
48
 
49
+ if not contours:
50
+ logging.warning("Контуры не найдены. Пропуск коррекции перспективы.")
 
 
 
 
 
 
 
 
51
  return image
52
 
53
+ # Находим самый большой контур по площади
54
+ largest_contour = max(contours, key=cv2.contourArea)
55
+
56
+ # Находим минимальный ограничивающий прямоугольник (может быть повернут)
57
+ rect = cv2.minAreaRect(largest_contour)
58
+ box = cv2.boxPoints(rect)
59
+
60
+ # Проверка на адекватность: если найденный бокс слишком мал, это шум
61
+ if cv2.contourArea(box) < 500: # Пороговое значение, можно подбирать
62
+ logging.warning("Найденный контур слишком мал. Пропуск коррекции.")
63
  return image
64
 
65
+ # Упорядочиваем 4 угла
66
+ ordered_box = order_points(box)
67
+ (tl, tr, br, bl) = ordered_box
68
+
69
+ # Вычисляем ширину и высоту целевого прямоугольника
70
+ widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
71
+ widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
72
+ maxWidth = max(int(widthA), int(widthB))
73
+
74
+ heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
75
+ heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
76
+ maxHeight = max(int(heightA), int(heightB))
77
 
78
+ # Проверка на адекватность соотношения сторон
79
+ if maxHeight == 0 or maxWidth / maxHeight < 1.5:
80
+ logging.warning(f"Неадекватное соотношение сторон ({maxWidth}/{maxHeight}). Пропуск коррекции.")
81
+ return image
82
+
83
+ # Задаем точки назначения (идеальный прямоугольник)
84
+ dst = np.array([
85
+ [0, 0],
86
+ [maxWidth - 1, 0],
87
+ [maxWidth - 1, maxHeight - 1],
88
+ [0, maxHeight - 1]], dtype="float32")
89
+
90
+ # Вычисляем матрицу преобразования перспективы и применяем ее
91
+ M = cv2.getPerspectiveTransform(ordered_box, dst)
92
+ warped = cv2.warpPerspective(img_for_transform, M, (maxWidth, maxHeight), flags=cv2.INTER_LINEAR, borderValue=(255,255,255))
93
 
94
+ logging.info("Коррекция перспективы успешно применена.")
95
+ return warped
96
 
97
 
98
  def fetch_and_solve_captcha():
 
103
 
104
  data = response.json()
105
  base64_image_data = data.get("Image")
106
+ if not base64_image_data: return None
 
107
 
108
  image_bytes = base64.b64decode(base64_image_data)
109
  nparr = np.frombuffer(image_bytes, np.uint8)
 
122
  kernel = np.ones((2, 2), np.uint8)
123
  cleaned_mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2)
124
 
125
+ inverted_for_processing = cv2.bitwise_not(cleaned_mask)
126
+
127
+ # ПРИМЕНЯЕМ НОВЫЙ АЛГОРИТМ КОРРЕКЦИИ
128
+ corrected_image = correct_perspective(inverted_for_processing)
129
 
130
+ processed_image = corrected_image
131
 
132
+ tesseract_config = r'--oem 3 --psm 7 -c tessedit_char_whitelist=0123456789'
133
  text = pytesseract.image_to_string(processed_image, config=tesseract_config)
134
  recognized_text = re.sub(r'\s+', '', text).strip() or "Не распознано"
135
  logging.info(f"Распознано: {recognized_text}")