import base64 import logging import re import requests import cv2 import numpy as np import pytesseract from flask import Flask, render_template, jsonify from threading import Lock import math app = Flask(__name__) logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') solved_captchas = [] lock = Lock() CAPTCHA_URL = 'https://checkege.rustest.ru/api/captcha' HEADERS = { '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' } def deskew(image): """ Вычисляет угол наклона и поворачивает изображение, но только если угол адекватен. """ gray = cv2.bitwise_not(image) coords = np.column_stack(np.where(gray > 0)) if len(coords) < 1: logging.warning("Нет контента для выпрямления, пропуск deskew.") return image angle = cv2.minAreaRect(coords)[-1] if angle < -45: correction_angle = -(90 + angle) else: correction_angle = -angle # --- КЛЮЧЕВОЕ ИЗМЕНЕНИЕ: ПРОВЕРКА НА АДЕКВАТНОСТЬ --- # Если вычисленный угол слишком большой, это почти наверняка ошибка. # Безопаснее пропустить поворот, чем повернуть на 90 градусов. if abs(correction_angle) > 45: logging.warning(f"Вычислен неадекватный угол {correction_angle:.2f}. Пропуск коррекции наклона.") return image # Пропускаем, если наклон незначителен if abs(correction_angle) < 1: logging.info("Угол наклона незначителен, коррекция не требуется.") return image logging.info(f"Обнаружен адекватный угол наклона: {correction_angle:.2f} градусов. Применяется коррекция.") (h, w) = image.shape[:2] center = (w // 2, h // 2) M = cv2.getRotationMatrix2D(center, correction_angle, 1.0) rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT, borderValue=(255,255,255)) return rotated def fetch_and_solve_captcha(): try: logging.info("Получение новой капчи...") response = requests.get(CAPTCHA_URL, headers=HEADERS) response.raise_for_status() data = response.json() base64_image_data = data.get("Image") if not base64_image_data: return None image_bytes = base64.b64decode(base64_image_data) nparr = np.frombuffer(image_bytes, np.uint8) original_image = cv2.imdecode(nparr, cv2.IMREAD_COLOR) scale_factor = 2 width = int(original_image.shape[1] * scale_factor) height = int(original_image.shape[0] * scale_factor) upscaled_image = cv2.resize(original_image, (width, height), interpolation=cv2.INTER_CUBIC) hsv = cv2.cvtColor(upscaled_image, cv2.COLOR_BGR2HSV) lower_blue = np.array([90, 50, 50]) upper_blue = np.array([130, 255, 255]) mask = cv2.inRange(hsv, lower_blue, upper_blue) kernel = np.ones((2, 2), np.uint8) cleaned_mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2) inverted_mask = cv2.bitwise_not(cleaned_mask) deskewed_image = deskew(inverted_mask) processed_image = deskewed_image tesseract_config = r'--oem 3 --psm 6 -c tessedit_char_whitelist=0123456789' text = pytesseract.image_to_string(processed_image, config=tesseract_config) recognized_text = re.sub(r'\s+', '', text).strip() or "Не распознано" logging.info(f"Распознано: {recognized_text}") _, buffer_orig = cv2.imencode('.png', original_image) original_b64 = base64.b64encode(buffer_orig).decode('utf-8') _, buffer_proc = cv2.imencode('.png', processed_image) processed_b64 = base64.b64encode(buffer_proc).decode('utf-8') return { "text": recognized_text, "original_b64": original_b64, "processed_b64": processed_b64 } except Exception as e: logging.error(f"Произошла ошибка при решении капчи: {e}", exc_info=True) return None @app.route('/') def index(): with lock: return render_template('index.html', captchas=list(solved_captchas)) @app.route('/solve', methods=['POST']) def solve_new_captcha(): new_captcha = fetch_and_solve_captcha() if new_captcha: with lock: solved_captchas.insert(0, new_captcha) return jsonify(new_captcha) return jsonify({"error": "Не удалось решить капчу"}), 500 if __name__ == '__main__': logging.info("Запуск приложения...") initial_captcha = fetch_and_solve_captcha() if initial_captcha: solved_captchas.append(initial_captcha) app.run(host='0.0.0.0', port=7860, debug=False)