Spaces:
Sleeping
Sleeping
File size: 4,290 Bytes
b510add | 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | """Prétraitement image : détection de l'écran LCD puis découpage en lignes
de valeurs (prix / volume / prix du litre), avant reconnaissance CRNN.
Cette étape reste indépendante du modèle de reconnaissance : elle correspond
au travail de prétraitement/détection d'écran attendu par le cahier des
charges (§6.1, §8.2/8.3), et reste identique quel que soit le moteur de
lecture des chiffres utilisé en aval.
"""
import cv2
import numpy as np
from typing import List, Tuple
FIELD_NAMES = ["prix", "volume", "prix_litre"]
def detect_screen_region(img: np.ndarray) -> Tuple[np.ndarray, Tuple[int, int, int, int]]:
"""Tente de localiser la zone de l'écran LCD dans l'image.
Retourne (crop, (x, y, w, h)) ou (img, (0,0,W,H)) si rien de convaincant
n'a été trouvé (l'appelant doit alors traiter l'image entière).
"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) if len(img.shape) == 3 else img.copy()
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 30, 100)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 5))
closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
h_img, w_img = img.shape[:2]
best_rect, best_area = None, 0
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
area = w * h
if area > 0.02 * h_img * w_img and 1.0 < (w / max(h, 1)) < 8.0:
if area > best_area:
best_area = area
best_rect = (x, y, w, h)
if best_rect:
x, y, w, h = best_rect
pad = 10
x1 = max(0, x - pad); y1 = max(0, y - pad)
x2 = min(w_img, x + w + pad); y2 = min(h_img, y + h + pad)
return img[y1:y2, x1:x2], (x1, y1, x2 - x1, y2 - y1)
return img, (0, 0, w_img, h_img)
def split_lcd_lines(lcd_crop: np.ndarray,
n_lines: int = 3) -> Tuple[List[np.ndarray], List[Tuple[int, int]]]:
"""Découpe le crop d'écran en `n_lines` bandes horizontales (une par
valeur affichée : prix, volume, prix du litre), par projection
horizontale des pixels sombres, avec repli sur un découpage proportionnel
si la projection ne trouve pas exactement le bon nombre de bandes.
NB : `n_lines=3` par défaut (et non 2) car la majorité des écrans du jeu
d'annotation affichent les trois valeurs (prix, volume, prix/litre) —
un défaut à 2 aurait systématiquement ignoré la troisième ligne.
"""
gray = cv2.cvtColor(lcd_crop, cv2.COLOR_BGR2GRAY)
h, w = gray.shape
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
enh = clahe.apply(gray)
_, bin_img = cv2.threshold(enh, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
if np.mean(bin_img) > 128:
bin_img = cv2.bitwise_not(bin_img)
row_sums = np.sum(bin_img == 255, axis=1).astype(np.float32)
smoothed = cv2.GaussianBlur(
row_sums.reshape(-1, 1), (1, max(3, h // 30) * 2 + 1), 0
).flatten()
threshold = max(smoothed.max() * 0.08, 2)
active = smoothed > threshold
bands: List[Tuple[int, int]] = []
in_band = False
start = 0
for y in range(h):
if active[y] and not in_band:
start, in_band = y, True
elif not active[y] and in_band:
bands.append((start, y))
in_band = False
if in_band:
bands.append((start, h))
min_h = h * 0.06
bands = [(s, e) for s, e in bands if e - s >= min_h]
merged: List[Tuple[int, int]] = []
for band in bands:
if merged and band[0] - merged[-1][1] < h * 0.05:
merged[-1] = (merged[-1][0], band[1])
else:
merged.append(band)
bands = [tuple(band) for band in merged]
if len(bands) != n_lines:
band_h = h / n_lines
bands = [(int(i * band_h), int((i + 1) * band_h)) for i in range(n_lines)]
crops: List[np.ndarray] = []
normalized_bands: List[Tuple[int, int]] = []
for start_y, end_y in bands:
pad = max(2, int(h * 0.02))
y0 = max(0, start_y - pad)
y1 = min(h, end_y + pad)
crops.append(lcd_crop[y0:y1, :])
normalized_bands.append((y0, y1 - y0))
return crops, normalized_bands
|