mnemo_ocr / src /pipeline.py
Hayk Arutyunyan
Add demo OCR version and Streamlit app
d249b39
Raw
History Blame Contribute Delete
5 kB
"""
Главный pipeline проекта
"""
import cv2
import numpy as np
import pandas as pd
from pathlib import Path
from docx import Document
from src.config_loader import load_config
from src.ocr_utils_demo import ocr_title, ocr_sensors
# ---------------------------------------------------------
# Итератор входных данных (СЦЕНАРИЙ A)
# ---------------------------------------------------------
def iter_input_images(input_dir: Path):
"""
Универсальный вход:
- читаем ВСЕ png/jpg/bmp
- читаем ВСЕ изображения внутри DOCX
"""
exts = {".png", ".jpg", ".jpeg", ".bmp"}
image_files = []
docx_files = []
for p in input_dir.rglob("*"):
if not p.is_file():
continue
suf = p.suffix.lower()
if suf in exts:
image_files.append(p)
elif suf == ".docx":
docx_files.append(p)
# ---------- PNG/JPG ----------
for path in sorted(image_files):
img = cv2.imread(str(path), cv2.IMREAD_COLOR)
if img is not None:
yield path.stem, img
# ---------- DOCX ----------
for docx_path in sorted(docx_files):
doc = Document(docx_path)
idx = 0
for rel in doc.part._rels.values():
if "image" not in rel.target_ref:
continue
idx += 1
blob = rel.target_part.blob
arr = np.frombuffer(blob, np.uint8)
img = cv2.imdecode(arr, cv2.IMREAD_COLOR)
if img is not None:
yield f"{docx_path.stem}_img{idx}", img
# Если не нашли ничего
if not image_files and not docx_files:
print("⚠ Папка не содержит ни изображений, ни DOCX.")
# ---------------------------------------------------------
# Извлечение текста титула + датчиков
# ---------------------------------------------------------
def extract_text(img: np.ndarray, color_ranges: dict) -> tuple[str, list[dict]]:
"""
1. Обрезаем титул (верхняя область)
2. OCR титула
3. Извлекаем сенсоры по цветовым маскам
4. OCR сенсоров
"""
# ---------- 1. титул ----------
h, w = img.shape[:2]
title_roi = img[:45, :int(w / 2.4)]
title_text = ocr_title(title_roi)
# ---------- 2. сенсоры ----------
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = None
for (lo, hi) in color_ranges.values():
lo_np = np.array(lo, dtype=np.uint8)
hi_np = np.array(hi, dtype=np.uint8)
cur = cv2.inRange(hsv, lo_np, hi_np)
mask = cur if mask is None else cv2.bitwise_or(mask, cur)
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rois, positions = [], []
for cnt in contours:
x, y, ww, hh = cv2.boundingRect(cnt)
if ww < 90 or hh < 17:
continue
hh = min(hh, 17)
rois.append(img[y:y + hh, x:x + ww])
positions.append((x, y, ww, hh))
# ---------- 3. OCR сенсоров ----------
sensors = []
if rois:
ocr_results = ocr_sensors(rois)
for (x, y, ww, hh), r in zip(positions, ocr_results):
sensors.append({
"text": r["text"],
"score": r["score"],
"x": x,
"y": y,
"w": ww,
"h": hh
})
return title_text, sensors
# ---------------------------------------------------------
# Основной pipeline → Excel
# ---------------------------------------------------------
def process_all_images_to_excel(cfg: dict):
input_dir = Path(cfg["paths"]["input"])
output_dir = Path(cfg["paths"]["output"])
excel_name = cfg["export"]["excel_filename"]
output_dir.mkdir(parents=True, exist_ok=True)
excel_path = output_dir / excel_name
# Цветовые диапазоны сенсоров
color_ranges = cfg["colors"]
results = []
for name, img in iter_input_images(input_dir):
title_text, sensors = extract_text(img, color_ranges)
for sen in sensors:
results.append({
"filename": name,
"title": title_text,
"sensor_name": sen["text"],
"score": sen["score"]
})
if not results:
print("⚠ Нет данных для записи.")
return
df = pd.DataFrame(results)
pd.DataFrame.to_excel(df, excel_path, index=False, engine="openpyxl")
print(f"✅ Готово! Excel сохранён: {excel_path.resolve()}")
# ---------------------------------------------------------
# Запуск
# ---------------------------------------------------------
if __name__ == "__main__":
cfg = load_config("config.yaml")
process_all_images_to_excel(cfg)