Initial: Tesseract Web GUI with Flask + start.bat

This commit is contained in:
Sivarius
2026-07-03 16:59:26 +08:00
commit 330b79288b
6 changed files with 534 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
"""
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/<path:filename>")
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)