File size: 2,387 Bytes
186b436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""Отрисовка bbox и текста распознанного номера на изображении."""
from pathlib import Path

import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont


def _find_font(size: int) -> ImageFont.FreeTypeFont:
    """Ищет шрифт с поддержкой кириллицы. Fallback на default."""
    # Стандартные пути на Windows
    candidates = [
        "C:/Windows/Fonts/arial.ttf",
        "C:/Windows/Fonts/arialbd.ttf",
        # Linux (для Docker)
        "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
    ]
    for path in candidates:
        if Path(path).exists():
            return ImageFont.truetype(path, size)
    return ImageFont.load_default()


def annotate_image(
    image_bgr: np.ndarray,
    detections: list[dict],
    output_path: str | Path,
) -> None:
    """
    Рисует bbox + текст номера для каждой детекции и сохраняет результат.
    detections: список dict от pipeline.process()
    """
    # Конверт BGR (OpenCV) -> RGB (PIL)
    image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
    pil_img = Image.fromarray(image_rgb)
    draw = ImageDraw.Draw(pil_img)

    # Размер шрифта пропорционально картинке
    h = pil_img.height
    font_size = max(16, h // 30)
    font = _find_font(font_size)

    for d in detections:
        if d.get("is_fallback"):
            continue  # для fallback рисовать bbox по всему изображению не имеет смысла

        x1, y1, x2, y2 = d["bbox"]
        color = (0, 200, 0) if d.get("is_valid_gost") else (255, 140, 0)

        # Рамка
        draw.rectangle([x1, y1, x2, y2], outline=color, width=3)

        # Подпись над рамкой
        label = d["plate_text"] or "?"
        bbox_text = draw.textbbox((0, 0), label, font=font)
        text_w = bbox_text[2] - bbox_text[0]
        text_h = bbox_text[3] - bbox_text[1]

        # Фон под текст
        ty = max(0, y1 - text_h - 6)
        draw.rectangle([x1, ty, x1 + text_w + 8, ty + text_h + 6], fill=color)
        draw.text((x1 + 4, ty + 2), label, fill="white", font=font)

    pil_img.save(str(output_path), "JPEG", quality=90)