Download app.py from ChandraPrakashBathula/Intel-ImageClassification: direct link, hf CLI and curl.
- Browser
- Download file 5.17 kB
-
https://huggingface.co/spaces/ChandraPrakashBathula/Intel-ImageClassification/resolve/main/app.py
- Command line
-
hf download hf://spaces/ChandraPrakashBathula/Intel-ImageClassification/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ChandraPrakashBathula/Intel-ImageClassification/resolve/main/app.py
5.17 kB
| import json | |
| from pathlib import Path | |
| import spaces | |
| import numpy as np | |
| import torch | |
| import torchvision.transforms as T | |
| import gradio as gr | |
| from PIL import Image | |
| from matplotlib import colormaps | |
| from model import SceneCNN, grad_cam | |
| ROOT = Path(__file__).parent | |
| # ---------- load model ---------- | |
| meta = json.loads((ROOT / "cnn_intel_meta.json").read_text()) | |
| CLASSES = meta["class_names"] | |
| IMG_SIZE = meta["img_size"] | |
| model = SceneCNN(base=meta["base"], num_classes=len(CLASSES)) | |
| model.load_state_dict(torch.load(ROOT / "cnn_intel.pth", map_location="cpu")) | |
| model.eval() | |
| preprocess = T.Compose([ | |
| T.Resize((IMG_SIZE, IMG_SIZE)), | |
| T.ToTensor(), | |
| T.Normalize(meta["mean"], meta["std"]), | |
| ]) | |
| # ---------- prediction ---------- | |
| def predict(img): | |
| if img is None: | |
| return None, None | |
| img = img.convert("RGB") | |
| cam, probs = grad_cam(model, preprocess(img)) | |
| show = 384 | |
| base = np.asarray(img.resize((show, show))) / 255.0 | |
| cam_big = np.asarray(Image.fromarray((cam * 255).astype(np.uint8)).resize((show, show), Image.BILINEAR)) / 255.0 | |
| heat = colormaps["jet"](cam_big)[..., :3] | |
| overlay = (0.55 * base + 0.45 * heat).clip(0, 1) | |
| label = {c: float(p) for c, p in zip(CLASSES, probs)} | |
| return label, (overlay * 255).astype(np.uint8) | |
| # ---------- metrics ---------- | |
| def metrics_markdown(): | |
| p = ROOT / "metrics.json" | |
| if not p.exists(): | |
| return "_metrics.json not found._" | |
| m = json.loads(p.read_text()) | |
| cm = np.array(m["confusion_matrix"]) | |
| tp = cm.diagonal() | |
| precision = tp / cm.sum(axis=0).clip(min=1) | |
| recall = tp / cm.sum(axis=1).clip(min=1) | |
| f1 = 2 * precision * recall / (precision + recall + 1e-12) | |
| head = [f"**Test accuracy:** {m['test_accuracy']:.2%}", | |
| f"**Macro F1:** {m['macro_f1']:.3f}"] | |
| if m.get("best_val_accuracy"): | |
| head.append(f"**Best val accuracy:** {m['best_val_accuracy']:.2%}") | |
| if m.get("params"): | |
| head.append(f"**Parameters:** {m['params']:,}") | |
| out = [" Β· ".join(head), "", | |
| "| Class | Precision | Recall | F1 | Test images |", | |
| "|---|---|---|---|---|"] | |
| for i, c in enumerate(CLASSES): | |
| out.append(f"| {c} | {precision[i]:.3f} | {recall[i]:.3f} | {f1[i]:.3f} | {cm[i].sum()} |") | |
| # top confusions | |
| off = [(cm[i, j], CLASSES[i], CLASSES[j]) for i in range(len(CLASSES)) | |
| for j in range(len(CLASSES)) if i != j] | |
| out += ["", "**Most confused (actual β predicted):** " + | |
| ", ".join(f"{a} β {b} ({n})" for n, a, b in sorted(off, reverse=True)[:4])] | |
| return "\n".join(out) | |
| PLOTS = [ | |
| ("10_curves.png", "Training curves"), | |
| ("11_confusion_matrix.png", "Confusion matrix"), | |
| ("12_per_class_accuracy.png", "Per-class accuracy"), | |
| ("13_confidence.png", "Confidence: correct vs wrong"), | |
| ("14_mistakes.png", "Most confident mistakes"), | |
| ("15_gradcam.png", "Grad-CAM per class"), | |
| ("16_gradcam_mistakes.png", "Grad-CAM on mistakes"), | |
| ("08_feature_maps_after.png", "Feature maps after training"), | |
| ("09_filters_after.png", "First-layer filters after training"), | |
| ("04_augmentation.png", "Augmentation"), | |
| ("01_samples.png", "Dataset samples"), | |
| ("02_class_distribution.png", "Class distribution"), | |
| ] | |
| gallery_items = [(str(ROOT / "images" / f), cap) for f, cap in PLOTS if (ROOT / "images" / f).exists()] | |
| examples = [[str(p)] for p in sorted((ROOT / "examples").glob("*.jpg"))] if (ROOT / "examples").exists() else [] | |
| # ---------- UI ---------- | |
| with gr.Blocks(title="Intel Scene CNN", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown( | |
| "# Intel Scene Classifier: a CNN built from scratch\n" | |
| "Upload a photo of a **building, forest, glacier, mountain, sea or street**. " | |
| "The CNN predicts the scene, and Grad-CAM shows which pixels drove the decision. " | |
| "No pretrained backbone: 4 conv blocks trained from scratch on ~14k images." | |
| ) | |
| with gr.Row(): | |
| inp = gr.Image(type="pil", label="Upload a scene", height=384) | |
| with gr.Column(): | |
| out_label = gr.Label(num_top_classes=6, label="Prediction") | |
| out_cam = gr.Image(label="Grad-CAM (red = most important)", height=384) | |
| inp.change(predict, inputs=inp, outputs=[out_label, out_cam]) | |
| if examples: | |
| gr.Examples(examples=examples, inputs=inp, cache_examples=False) | |
| gr.Markdown("> Note: the model always picks one of the 6 classes, even for a photo of a cat.") | |
| gr.Markdown("## Test-set metrics (seg_test, 3,000 held-out images)") | |
| gr.Markdown(metrics_markdown()) | |
| if gallery_items: | |
| gr.Markdown("## Training and evaluation plots") | |
| gr.Gallery(value=gallery_items, columns=2, height="auto", object_fit="contain") | |
| gr.Markdown( | |
| "## Architecture\n" | |
| "`[Conv3x3-BN-ReLU] x2 β MaxPool β Dropout2d` Γ 4 blocks " | |
| f"(channels {meta['base']}β{meta['base']*2}β{meta['base']*4}β{meta['base']*8}) " | |
| "β Global Avg Pool β Dropout β FC 128 β ReLU β FC 6" | |
| ) | |
| demo.launch(show_api=False, ssr_mode=False) |