File size: 2,041 Bytes
1c0c94d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
66
67
68
69
70
71
72
73
74
75
76
77
"""Image quality gate. Quantifies blur / exposure / resolution and decides whether the
image is good enough to judge. Abstaining on unusable images is a feature: it prevents
confident-but-wrong outputs under bad conditions. Uses numpy + Pillow only.
"""

from __future__ import annotations

from pydantic import BaseModel


class QualityReport(BaseModel):
    ok: bool
    reason: str
    blur: float
    brightness: float
    min_side: int


def assess(
    image_path: str,
    *,
    min_side: int = 64,
    blur_threshold: float = 12.0,
    dark: float = 15.0,
    bright: float = 245.0,
) -> QualityReport:
    import numpy as np
    from PIL import Image

    gray = Image.open(image_path).convert("L")
    w, h = gray.size
    side = min(w, h)
    arr = np.asarray(gray, dtype="float64")

    brightness = float(arr.mean())
    # variance of a discrete Laplacian = sharpness proxy (low => blurry/featureless)
    lap = (
        -4.0 * arr
        + np.roll(arr, 1, 0)
        + np.roll(arr, -1, 0)
        + np.roll(arr, 1, 1)
        + np.roll(arr, -1, 1)
    )
    blur = float(lap[1:-1, 1:-1].var()) if side > 2 else 0.0

    if side < min_side:
        return QualityReport(
            ok=False,
            reason="resolution too low",
            blur=blur,
            brightness=brightness,
            min_side=side,
        )
    if brightness < dark:
        return QualityReport(
            ok=False, reason="too dark", blur=blur, brightness=brightness, min_side=side
        )
    if brightness > bright:
        return QualityReport(
            ok=False,
            reason="overexposed",
            blur=blur,
            brightness=brightness,
            min_side=side,
        )
    if blur < blur_threshold:
        return QualityReport(
            ok=False,
            reason="too blurry / no detail",
            blur=blur,
            brightness=brightness,
            min_side=side,
        )
    return QualityReport(
        ok=True, reason="ok", blur=blur, brightness=brightness, min_side=side
    )