iamhmh commited on
Commit
397dad3
·
1 Parent(s): a03ff22

Initial model upload

Browse files
Files changed (6) hide show
  1. app.py +17 -0
  2. inference.py +90 -0
  3. labels.json +9 -0
  4. model.pth +3 -0
  5. model.py +91 -0
  6. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from inference import predict
3
+
4
+ def classify(image):
5
+ idx, label, probs = predict(image)
6
+ return {label: float(1.0)}
7
+
8
+ demo = gr.Interface(
9
+ fn=classify,
10
+ inputs=gr.Image(type="filepath", label="Upload a dermatoscopic image"),
11
+ outputs=gr.Label(label="Predicted lesion"),
12
+ title="Derm-CNN HAM10000 – Skin Lesion Classifier",
13
+ description="Upload a dermatoscopic image and get the predicted skin lesion class.",
14
+ allow_flagging="never"
15
+ )
16
+
17
+ demo.launch()
inference.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import json
3
+ from pathlib import Path
4
+
5
+ import numpy as np
6
+ import torch
7
+ from PIL import Image
8
+
9
+ from model import load_model
10
+
11
+
12
+ def load_labels(labels_path: str = "labels.json") -> dict[int, str]:
13
+ labels_file = Path(labels_path)
14
+ if not labels_file.exists():
15
+ raise FileNotFoundError(f"labels.json not found at: {labels_file}")
16
+
17
+ with labels_file.open("r", encoding="utf-8") as f:
18
+ raw = json.load(f)
19
+
20
+ return {int(k): v for k, v in raw.items()}
21
+
22
+
23
+ def preprocess_image(image_path: str) -> torch.Tensor:
24
+ img_file = Path(image_path)
25
+ if not img_file.exists():
26
+ raise FileNotFoundError(f"Image not found at: {img_file}")
27
+
28
+ img = Image.open(img_file).convert("RGB")
29
+ img = img.resize((28, 28))
30
+
31
+ arr = np.array(img).astype("float32") / 255.0 # [H, W, C] in [0,1]
32
+ arr = np.transpose(arr, (2, 0, 1)) # [C, H, W]
33
+ tensor = torch.from_numpy(arr).unsqueeze(0) # [1, 3, 28, 28]
34
+
35
+ return tensor
36
+
37
+
38
+ def predict(
39
+ image_path: str,
40
+ weights_path: str = "model.pth",
41
+ labels_path: str = "labels.json"
42
+ ):
43
+ model, device = load_model(weights_path)
44
+ id2label = load_labels(labels_path)
45
+
46
+ x = preprocess_image(image_path).to(device)
47
+
48
+ with torch.no_grad():
49
+ logits = model(x)
50
+ probs = torch.softmax(logits, dim=1)[0]
51
+
52
+ pred_idx = int(torch.argmax(probs).item())
53
+ pred_label = id2label.get(pred_idx, str(pred_idx))
54
+ probs_list = probs.cpu().tolist()
55
+
56
+ return pred_idx, pred_label, probs_list
57
+
58
+
59
+ def main():
60
+ parser = argparse.ArgumentParser(
61
+ description="Run inference with SkinCNN on a dermatoscopic image."
62
+ )
63
+ parser.add_argument("image", type=str, help="Path to input dermatoscopic image.")
64
+ parser.add_argument(
65
+ "--weights",
66
+ type=str,
67
+ default="model.pth",
68
+ help="Path to model weights (.pth).",
69
+ )
70
+ parser.add_argument(
71
+ "--labels",
72
+ type=str,
73
+ default="labels.json",
74
+ help="Path to labels.json.",
75
+ )
76
+ args = parser.parse_args()
77
+
78
+ idx, label, probs = predict(
79
+ image_path=args.image,
80
+ weights_path=args.weights,
81
+ labels_path=args.labels,
82
+ )
83
+
84
+ print(f"Predicted class index: {idx}")
85
+ print(f"Predicted label : {label}")
86
+ print(f"Probabilities : {probs}")
87
+
88
+
89
+ if __name__ == "__main__":
90
+ main()
labels.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "0": "Actinic keratoses (akiec)",
3
+ "1": "Basal cell carcinoma (bcc)",
4
+ "2": "Benign keratosis (bkl)",
5
+ "3": "Dermatofibroma (df)",
6
+ "4": "Melanoma (mel)",
7
+ "5": "Melanocytic nevi (nv)",
8
+ "6": "Vascular lesions (vasc)"
9
+ }
model.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c720b1a42a5c99ff88a858e71fff5587d0c9c8c21a6c7d6997c6c048f90c7551
3
+ size 5119928
model.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+
5
+ class SkinCNN(nn.Module):
6
+ def __init__(self, num_classes: int = 7):
7
+ super().__init__()
8
+
9
+ # Feature extractor
10
+ self.features = nn.Sequential(
11
+ # Block 1: 3 -> 32
12
+ nn.Conv2d(3, 32, kernel_size=3, padding=1),
13
+ nn.ReLU(),
14
+ nn.MaxPool2d(kernel_size=2), # 28x28 -> 14x14
15
+ nn.BatchNorm2d(32),
16
+
17
+ # Block 2: 32 -> 64 -> 64
18
+ nn.Conv2d(32, 64, kernel_size=3, padding=1),
19
+ nn.ReLU(),
20
+ nn.Conv2d(64, 64, kernel_size=3, padding=1),
21
+ nn.ReLU(),
22
+ nn.MaxPool2d(kernel_size=2), # 14x14 -> 7x7
23
+ nn.BatchNorm2d(64),
24
+
25
+ # Block 3: 64 -> 128 -> 128
26
+ nn.Conv2d(64, 128, kernel_size=3, padding=1),
27
+ nn.ReLU(),
28
+ nn.Conv2d(128, 128, kernel_size=3, padding=1),
29
+ nn.ReLU(),
30
+ nn.MaxPool2d(kernel_size=2), # 7x7 -> 3x3
31
+ nn.BatchNorm2d(128),
32
+
33
+ # Block 4: 128 -> 256 -> 256
34
+ nn.Conv2d(128, 256, kernel_size=3, padding=1),
35
+ nn.ReLU(),
36
+ nn.Conv2d(256, 256, kernel_size=3, padding=1),
37
+ nn.ReLU(),
38
+ nn.MaxPool2d(kernel_size=2) # 3x3 -> 1x1
39
+ )
40
+
41
+ # Classifier: 5 FC layers with BatchNorm + Dropout at input
42
+ self.classifier = nn.Sequential(
43
+ nn.Flatten(),
44
+ nn.Dropout(0.2),
45
+
46
+ # 256*1*1 -> 256
47
+ nn.Linear(256 * 1 * 1, 256),
48
+ nn.ReLU(),
49
+ nn.BatchNorm1d(256),
50
+
51
+ # 256 -> 128
52
+ nn.Linear(256, 128),
53
+ nn.ReLU(),
54
+ nn.BatchNorm1d(128),
55
+
56
+ # 128 -> 64
57
+ nn.Linear(128, 64),
58
+ nn.ReLU(),
59
+ nn.BatchNorm1d(64),
60
+
61
+ # 64 -> 32
62
+ nn.Linear(64, 32),
63
+ nn.ReLU(),
64
+ nn.BatchNorm1d(32),
65
+
66
+ # 32 -> num_classes
67
+ nn.Linear(32, num_classes)
68
+ )
69
+
70
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
71
+ x = self.features(x)
72
+ x = self.classifier(x)
73
+ return x
74
+
75
+
76
+ def load_model(
77
+ weights_path: str = "model.pth",
78
+ device: str | None = None
79
+ ) -> tuple[nn.Module, torch.device]:
80
+ if device is None:
81
+ device = "cuda" if torch.cuda.is_available() else "cpu"
82
+ device = torch.device(device)
83
+
84
+ model = SkinCNN(num_classes=7)
85
+ state_dict = torch.load(weights_path, map_location=device)
86
+ model.load_state_dict(state_dict)
87
+
88
+ model.to(device)
89
+ model.eval()
90
+
91
+ return model, device
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch
2
+ torchvision
3
+ numpy
4
+ pillow
5
+ gradio