""" Tesseract Web GUI Локальный веб-сервис для распознавания текста с картинок. Запуск: start.bat """ import os import io import uuid import base64 from pathlib import Path from datetime import datetime from PIL import Image, ImageEnhance, ImageFilter, ImageOps import pytesseract from flask import Flask, render_template, request, jsonify, send_from_directory from werkzeug.utils import secure_filename # ── Конфиг ───────────────────────────────────────────────────────────── BASE_DIR = Path(__file__).resolve().parent UPLOAD_DIR = BASE_DIR / "uploads" UPLOAD_DIR.mkdir(exist_ok=True) # Путь к Tesseract (Windows) TESSERACT_CMD = r"C:\Program Files\Tesseract-OCR\tesseract.exe" if Path(TESSERACT_CMD).exists(): pytesseract.pytesseract.tesseract_cmd = TESSERACT_CMD ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "bmp", "tiff", "webp", "gif"} app = Flask(__name__) app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024 # 16 MB # ── Хелперы ──────────────────────────────────────────────────────────── def allowed_file(filename: str) -> bool: return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS def preprocess_image(img: Image.Image, scale: int, contrast: float, sharpen: bool, binary: bool, binary_thresh: int) -> Image.Image: """Предобработка перед OCR.""" if scale > 1: w, h = img.size img = img.resize((w * scale, h * scale), Image.LANCZOS) img = img.convert("L") if contrast != 1.0: img = ImageEnhance.Contrast(img).enhance(contrast) if sharpen: img = img.filter(ImageFilter.SHARPEN) if binary: img = img.point(lambda x: 0 if x < binary_thresh else 255) return img def do_ocr(image_path: Path, lang: str, psm: int, scale: int, contrast: float, sharpen: bool, binary: bool, binary_thresh: int, whitelist: str = "") -> dict: """Выполняет OCR и возвращает словарь с результатом.""" img = Image.open(image_path) # Для preview сохраняем оригинал как base64 buf = io.BytesIO() img.convert("RGB").save(buf, format="PNG") preview_b64 = base64.b64encode(buf.getvalue()).decode("utf-8") # Предобработка proc = preprocess_image(img, scale, contrast, sharpen, binary, binary_thresh) config = f"--psm {psm}" if whitelist: config += f' -c tessedit_char_whitelist="{whitelist}"' text = pytesseract.image_to_string(proc, lang=lang, config=config) # Сохраняем обработанную картинку proc_id = uuid.uuid4().hex[:8] proc_path = UPLOAD_DIR / f"proc_{proc_id}.png" proc.save(proc_path) return { "text": text, "preview": preview_b64, "proc_path": str(proc_path), "config_used": f"lang={lang}, psm={psm}, scale={scale}, contrast={contrast}, sharpen={sharpen}, binary={binary}, thresh={binary_thresh}, whitelist={whitelist or 'none'}", } # ── Роуты ────────────────────────────────────────────────────────────── @app.route("/") def index(): return render_template("index.html") @app.route("/ocr", methods=["POST"]) def ocr(): if "file" not in request.files: return jsonify({"error": "Файл не загружен"}), 400 file = request.files["file"] if file.filename == "" or not allowed_file(file.filename): return jsonify({"error": "Неверный или пустой файл (разрешены PNG, JPG, JPEG, BMP, TIFF, WEBP, GIF)"}), 400 # Параметры из формы lang = request.form.get("lang", "rus+eng").strip() psm = int(request.form.get("psm", 6)) scale = int(request.form.get("scale", 2)) contrast = float(request.form.get("contrast", 2.0)) sharpen = request.form.get("sharpen", "true").lower() in ("true", "1", "on") binary = request.form.get("binary", "false").lower() in ("true", "1", "on") binary_thresh = int(request.form.get("binary_thresh", 140)) whitelist = request.form.get("whitelist", "").strip() # Сохраняем оригинал ext = secure_filename(file.filename).rsplit(".", 1)[1].lower() uid = uuid.uuid4().hex[:8] original_path = UPLOAD_DIR / f"{uid}_{datetime.now():%Y%m%d_%H%M%S}.{ext}" file.save(original_path) try: result = do_ocr( original_path, lang, psm, scale, contrast, sharpen, binary, binary_thresh, whitelist ) return jsonify(result) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route("/uploads/") def uploads(filename): return send_from_directory(UPLOAD_DIR, filename) # ── Запуск ───────────────────────────────────────────────────────────── if __name__ == "__main__": port = int(os.environ.get("PORT", 5000)) print(f"[Tesseract Web GUI] http://127.0.0.1:{port}/") # debug=False, потому что иначе батник не закроется нормально app.run(host="127.0.0.1", port=port, debug=False)