CoolFace
Apppublic

FraRiccio/pattern-recognition-ai

sourceHugging Faceupdated 11mo agoView on Hugging Face
0likes
app.py190 linesDownload Raw Back to root
1import math2import numpy as np3import cv24import gradio as gr5 6# ---------------------- Feature extraction ----------------------7 8def _safe_resize(img, max_side=256):9    h, w = img.shape[:2]10    scale = max_side / max(h, w)11    if scale < 1.0:12        img = cv2.resize(img, (int(w*scale), int(h*scale)), interpolation=cv2.INTER_AREA)13    return img14 15def to_gray_bin(img):16    if img.ndim == 3:17        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)18    else:19        gray = img.copy()20    gray = cv2.GaussianBlur(gray, (3,3), 0)21    bin_im = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,22                                   cv2.THRESH_BINARY, 11, 2)23    if np.mean(bin_im) > 127:24        bin_im = 255 - bin_im25    return gray, bin_im26 27def edge_density(gray):28    edges = cv2.Canny(gray, 50, 150)29    return float(np.mean(edges > 0)), edges30 31def connected_components(bin_im):32    # cv2.connectedComponents richiede uint8 (0/1). Evitiamo bool.33    mask = (bin_im > 0).astype(np.uint8)34    num_labels, labels = cv2.connectedComponents(mask)35    return max(0, num_labels - 1)36 37 38def largest_contour_features(bin_im):39    cnts, _ = cv2.findContours(bin_im, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)40    if not cnts:41        return dict(area=0., ar=0., circ=0., extent=0.)42    c = max(cnts, key=cv2.contourArea)43    area = float(cv2.contourArea(c))44    x,y,w,h = cv2.boundingRect(c)45    rect_area = float(w*h) if w*h>0 else 1.046    ar = float(w)/float(h) if h>0 else 0.047    per = cv2.arcLength(c, True)48    circ = (4*math.pi*area/(per*per)) if per>0 else 0.049    extent = area/rect_area if rect_area>0 else 0.050    return dict(area=area, ar=ar, circ=circ, extent=extent)51 52def hu_moments(bin_im):53    m = cv2.moments(bin_im)54    hu = cv2.HuMoments(m).flatten()55    hu_log = np.sign(hu) * np.log1p(np.abs(hu))56    return hu_log.tolist()57 58def symmetry_score(bin_im):59    im = bin_im.astype(np.float32)/255.060    fh = cv2.flip(im, 1)61    fv = cv2.flip(im, 0)62    def cos_sim(a,b):63        denom = (np.linalg.norm(a)*np.linalg.norm(b) + 1e-8)64        return float(np.dot(a.ravel(), b.ravel())/denom)65    return dict(sym_h=cos_sim(im, fh), sym_v=cos_sim(im, fv))66 67def dominant_orientation(gray):68    gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0, ksize=3)69    gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1, ksize=3)70    mag, ang = cv2.cartToPolar(gx, gy, angleInDegrees=True)71    bins = 1872    hist, edges = np.histogram(ang[mag>0], bins=bins, range=(0,180), weights=mag[mag>0])73    if hist.sum() == 0:74        return 0.075    angle_center = (edges[:-1] + edges[1:]) / 2.076    return float(angle_center[np.argmax(hist)])77 78def corner_count(gray):79    corners = cv2.goodFeaturesToTrack(gray, maxCorners=128, qualityLevel=0.01, minDistance=5)80    return int(0 if corners is None else len(corners))81 82FEATURE_KEYS = [83    "avg_gray","edge_density","components","largest_area","largest_ar","largest_circ","largest_extent",84    "hu1","hu2","hu3","hu4","hu5","hu6","hu7","sym_h","sym_v","dom_angle","corner_count","area_ratio"85]86 87def extract_features(img):88    img = _safe_resize(img)89    gray, bin_im = to_gray_bin(img)90    ed, _ = edge_density(gray)91    comps = connected_components(bin_im)92    lcf = largest_contour_features(bin_im)93    hu = hu_moments(bin_im)94    sym = symmetry_score(bin_im)95    ang = dominant_orientation(gray)96    cor = corner_count(gray)97    area_ratio = float(np.sum(bin_im>0)) / float(bin_im.size)98    feats = {99        "avg_gray": float(np.mean(gray)/255.0),100        "edge_density": ed,101        "components": float(comps),102        "largest_area": float(lcf["area"]),103        "largest_ar": float(lcf["ar"]),104        "largest_circ": float(lcf["circ"]),105        "largest_extent": float(lcf["extent"]),106        "hu1": float(hu[0]), "hu2": float(hu[1]), "hu3": float(hu[2]),107        "hu4": float(hu[3]), "hu5": float(hu[4]), "hu6": float(hu[5]), "hu7": float(hu[6]),108        "sym_h": float(sym["sym_h"]),109        "sym_v": float(sym["sym_v"]),110        "dom_angle": float(ang/180.0),111        "corner_count": float(cor),112        "area_ratio": float(area_ratio),113    }114    return feats115 116def vectorize(feats):117    return np.array([feats[k] for k in FEATURE_KEYS], dtype=np.float32)118 119def analyze_sequence(seq_imgs, option_imgs):120    seq_vecs = [vectorize(extract_features(img)) for img in seq_imgs]121    delta = np.median([seq_vecs[i+1]-seq_vecs[i] for i in range(3)], axis=0)122    v_pred = seq_vecs[-1] + delta123    results = []124    for idx, img in enumerate(option_imgs):125        v = vectorize(extract_features(img))126        score = float(np.linalg.norm(v_pred - v))127        results.append((idx+1, score))128    return sorted(results, key=lambda x: x[1])129 130def run_ui(a1,a2,a3,a4,b1,b2,b3,b4,b5=None,b6=None):131    seq = [a1,a2,a3,a4]132    opts = [x for x in [b1,b2,b3,b4,b5,b6] if x is not None]133    if any(x is None for x in seq):134        return "⚠️ Carica tutte e 4 le immagini A1..A4."135    if len(opts) < 4:136        return "⚠️ Carica almeno 4 opzioni (B1..B4), fino a 6."137    results = analyze_sequence(seq, opts)138    text = "### 🔮 Risultato\nLa scelta consigliata è **B{}** ✅\n\n".format(results[0][0])139    text += "### Classifica (score: più basso è meglio)\n"140    for r in results:141        text += f"- B{r[0]} → {r[1]:.4f}\n"142    return text143 144# ---------------------- PASSWORD PROTECTION ----------------------145PASSWORD = "123456789"  # puoi cambiarla quando vuoi146 147def check_password(password: str) -> bool:148    return (password or "").strip() == PASSWORD149 150# ====================== UI ======================151with gr.Blocks() as demo:152    gr.Markdown("## 🔒 Accesso")153    with gr.Row():154        password_box = gr.Textbox(label="Inserisci password per accedere", type="password")155        access_button = gr.Button("🔓 Entra")156 157    access_msg = gr.Markdown(visible=False)158 159    # App vera e propria (nascosta finché non c'è accesso)160    with gr.Group(visible=False) as app_group:161        gr.Markdown("# 🧠 Pattern Recognition – Trova la figura successiva")162        gr.Markdown("Carica 4 immagini sequenziali e scegli tra 4–6 opzioni quella corretta 🔍")163        with gr.Row():164            with gr.Column():165                a1 = gr.Image(label="A1", type="numpy")166                a2 = gr.Image(label="A2", type="numpy")167                a3 = gr.Image(label="A3", type="numpy")168                a4 = gr.Image(label="A4", type="numpy")169            with gr.Column():170                b1 = gr.Image(label="B1", type="numpy")171                b2 = gr.Image(label="B2", type="numpy")172                b3 = gr.Image(label="B3", type="numpy")173                b4 = gr.Image(label="B4", type="numpy")174                b5 = gr.Image(label="B5 (opzionale)", type="numpy")175                b6 = gr.Image(label="B6 (opzionale)", type="numpy")176        btn = gr.Button("🔎 Analizza Pattern")177        output = gr.Markdown()178        btn.click(run_ui, inputs=[a1,a2,a3,a4,b1,b2,b3,b4,b5,b6], outputs=output)179 180    def grant_access(pwd):181        if check_password(pwd):182            return gr.update(visible=False, value=""), gr.update(visible=True)183        else:184            return gr.update(visible=True, value="❌ **Password errata. Riprova.**"), gr.update(visible=False)185 186    access_button.click(fn=grant_access, inputs=password_box, outputs=[access_msg, app_group])187 188demo.launch()189 190