afrim3000 commited on
Commit
db5c416
·
verified ·
1 Parent(s): 315d5a3

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +285 -0
README.md CHANGED
@@ -1,3 +1,288 @@
1
  ---
2
  license: mit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: mit
3
+ datasets:
4
+ - Bingsu/Gameplay_Images
5
+ language:
6
+ - en
7
+ metrics:
8
+ - accuracy
9
+ - precision
10
+ - recall
11
+ - f1
12
+ - roc_auc
13
+ - confusion_matrix
14
+ base_model:
15
+ - google/efficientnet-b0
16
+ pipeline_tag: image-classification
17
+ tags:
18
+ - game-detection
19
+ - image-classification
20
+ - efficientnet
21
+ - hashtag-generation
22
+ - computer-vision
23
+ - gaming
24
  ---
25
+ # Game_Detection
26
+ ### Automated Video Game Recognition for Hashtag Suggestion on Live Streaming Platforms
27
+ A 10-class image classifier that identifies which video game is being played from a gameplay
28
+ screenshot. Built on a fine-tuned [`google/efficientnet-b0`](https://huggingface.co/google/efficientnet-b0)
29
+ backbone, trained at a custom, aspect-ratio-preserving **180×320** input resolution (instead of the
30
+ standard 224×224 square crop) on the [`Bingsu/Gameplay_Images`](https://huggingface.co/datasets/Bingsu/Gameplay_Images)
31
+ dataset.
32
+ This model was built as part of a university course project (AI Lab, SE334) — *"Automated Video Game
33
+ Recognition and Hashtag Suggestion for Live Streaming Platforms Using Image Classification"* — and
34
+ powers the [GameSense](https://gamesense-h456.onrender.com/) demo app.
35
+ **Authors:** S. M. Nihal Ahmed, Afrim Hossen Khan
36
+ ## Model Details
37
+ - **Base model:** `google/efficientnet-b0`
38
+ - **Task:** Multi-class image classification (10 classes)
39
+ - **License:** MIT
40
+ - **Architecture:** EfficientNet-B0 backbone (ImageNet-pretrained), fine-tuned end-to-end with the
41
+ final classifier layer replaced for 10 output classes. Trained at a custom **180×320** input
42
+ resolution — half of the source dataset's native 640×360, preserving the true 16:9 aspect ratio —
43
+ made possible without architectural changes since EfficientNet's `AdaptiveAvgPool2d` head is
44
+ resolution-agnostic.
45
+ - **Fine-tuning objective:** Cross-entropy loss with label smoothing (0.1), `sklearn` balanced class
46
+ weights applied in the loss (the source dataset is already perfectly balanced at 1,000 images/class)
47
+ - **Training regime:** Mixed-precision (AMP) training on dual CUDA T4 GPUs, AdamW optimizer with a
48
+ OneCycleLR schedule, up to 25 epochs with early stopping (patience = 6, monitored on validation loss)
49
+ ## Classes
50
+ `Among Us, Apex Legends, Fortnite, Forza Horizon, Free Fire, Genshin Impact, God of War, Minecraft,
51
+ Roblox, Terraria`
52
+ ## Intended Use
53
+ This model is intended for identifying which video game is shown in a gameplay screenshot. Example use
54
+ cases:
55
+ - Auto-generating hashtags/tags for gameplay clips, stream thumbnails, and social posts
56
+ - Categorizing or organizing gameplay footage/screenshots by game on a content platform
57
+ - A component in a larger stream metadata or content-tagging pipeline
58
+ - Research and coursework on multi-class visual classification
59
+ **Out of scope:** This model only recognizes the 10 games listed above — any other game will be forced
60
+ into one of these 10 labels rather than correctly rejected. It has been evaluated on one dataset only,
61
+ and has not been validated against real-world production streaming footage, unusual camera angles,
62
+ menu/loading screens, or extensive in-game cosmetic content (e.g. crossover skins) that may visually
63
+ resemble a different game in the label set.
64
+ ## How to Use
65
+ This model is distributed in two formats — pick whichever fits your stack.
66
+ ### Option A: ONNX (lightweight, CPU-friendly)
67
+ Download both files and keep them in the same folder — the `.onnx` graph loads its weights from the
68
+ `.onnx.data` file alongside it at runtime:
69
+ - [`efficientnet_b0_gameplay.onnx`](https://huggingface.co/nihal4/Game_Detection/resolve/main/efficientnet_b0_gameplay.onnx) — the ONNX graph
70
+ - [`efficientnet_b0_gameplay.onnx.data`](https://huggingface.co/nihal4/Game_Detection/resolve/main/efficientnet_b0_gameplay.onnx.data) — the external weights file
71
+ Install dependencies:
72
+ ```bash
73
+ pip install onnxruntime huggingface_hub pillow numpy
74
+ ```
75
+ #### Single-image prediction
76
+ ```python
77
+ import numpy as np
78
+ import onnxruntime as ort
79
+ from PIL import Image
80
+ from huggingface_hub import hf_hub_download
81
+ REPO_ID = "nihal4/Game_Detection"
82
+ IMG_SIZE = (320, 180) # PIL resize takes (width, height)
83
+ IMAGENET_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
84
+ IMAGENET_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
85
+ CLASS_NAMES = ['Among Us', 'Apex Legends', 'Fortnite', 'Forza Horizon', 'Free Fire',
86
+ 'Genshin Impact', 'God of War', 'Minecraft', 'Roblox', 'Terraria']
87
+ # Downloads both files into the same local cache folder — required, since the
88
+ # .onnx graph references .onnx.data by relative path at load time.
89
+ onnx_path = hf_hub_download(repo_id=REPO_ID, filename="efficientnet_b0_gameplay.onnx")
90
+ hf_hub_download(repo_id=REPO_ID, filename="efficientnet_b0_gameplay.onnx.data")
91
+
92
+ session = ort.InferenceSession(onnx_path, providers=["CPUExecutionProvider"])
93
+ input_name = session.get_inputs()[0].name
94
+ output_name = session.get_outputs()[0].name
95
+ def preprocess_pil(img: Image.Image) -> np.ndarray:
96
+ img = img.convert("RGB").resize(IMG_SIZE)
97
+ arr = np.asarray(img, dtype=np.float32) / 255.0 # HWC, [0,1]
98
+ arr = (arr - IMAGENET_MEAN) / IMAGENET_STD # normalize, same stats as training
99
+ return arr.transpose(2, 0, 1) # HWC -> CHW
100
+ def softmax(x: np.ndarray) -> np.ndarray:
101
+ e = np.exp(x - x.max(axis=1, keepdims=True))
102
+ return e / e.sum(axis=1, keepdims=True)
103
+ def predict(image_path: str):
104
+ image = Image.open(image_path)
105
+ x = preprocess_pil(image)[np.newaxis, ...].astype(np.float32)
106
+ logits = session.run([output_name], {input_name: x})[0]
107
+ probs = softmax(logits)[0]
108
+ top_idx = int(probs.argmax())
109
+ return CLASS_NAMES[top_idx], probs
110
+ label, probs = predict("path/to/screenshot.jpg")
111
+ print(f"Prediction: {label}")
112
+ for name, p in sorted(zip(CLASS_NAMES, probs), key=lambda t: -t[1]):
113
+ print(f" {name:<16} {p*100:5.1f}%")
114
+ ```
115
+ #### Batch prediction
116
+ ```python
117
+ image_paths = ["shot1.jpg", "shot2.jpg", "shot3.jpg"]
118
+
119
+ batch = np.stack([preprocess_pil(Image.open(p)) for p in image_paths]).astype(np.float32)
120
+ logits = session.run([output_name], {input_name: batch})[0]
121
+ probs = softmax(logits)
122
+ preds = probs.argmax(axis=1)
123
+
124
+ for path, pred, p in zip(image_paths, preds, probs):
125
+ print(f"{path}: {CLASS_NAMES[int(pred)]} ({p[int(pred)]*100:.1f}%)")
126
+ ```
127
+ > For GPU inference, install `onnxruntime-gpu` instead and pass
128
+ > `providers=["CUDAExecutionProvider", "CPUExecutionProvider"]` when creating the session.
129
+ ### Option B: PyTorch (.pth checkpoint)
130
+ Download the checkpoint:
131
+ - [`efficientnet_b0_gameplay_final.pth`](https://huggingface.co/nihal4/Game_Detection/resolve/main/efficientnet_b0_gameplay_final.pth)
132
+ Install dependencies:
133
+ ```bash
134
+ pip install torch torchvision huggingface_hub pillow numpy
135
+ ```
136
+ #### Single-image prediction
137
+ ```python
138
+ import torch
139
+ import torch.nn as nn
140
+ import numpy as np
141
+ from torchvision import models, transforms
142
+ from PIL import Image
143
+ from huggingface_hub import hf_hub_download
144
+ REPO_ID = "nihal4/Game_Detection"
145
+ IMG_SIZE = (180, 320) # (H, W) — torchvision transforms convention
146
+ CLASS_NAMES = ['Among Us', 'Apex Legends', 'Fortnite', 'Forza Horizon', 'Free Fire',
147
+ 'Genshin Impact', 'God of War', 'Minecraft', 'Roblox', 'Terraria']
148
+ ckpt_path = hf_hub_download(repo_id=REPO_ID, filename="efficientnet_b0_gameplay_final.pth")
149
+ checkpoint = torch.load(ckpt_path, map_location="cpu")
150
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
151
+ model = models.efficientnet_b0(weights=None)
152
+ in_features = model.classifier[1].in_features
153
+ model.classifier[1] = nn.Linear(in_features, len(CLASS_NAMES))
154
+ model.load_state_dict(checkpoint["model_state_dict"])
155
+ model.to(device).eval()
156
+ transform = transforms.Compose([
157
+ transforms.Resize(IMG_SIZE),
158
+ transforms.ToTensor(),
159
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
160
+ ])
161
+ @torch.no_grad()
162
+ def predict(image_path: str):
163
+ image = Image.open(image_path).convert("RGB")
164
+ x = transform(image).unsqueeze(0).to(device)
165
+ logits = model(x)
166
+ probs = torch.softmax(logits, dim=1)[0]
167
+ top_idx = int(probs.argmax())
168
+ return CLASS_NAMES[top_idx], probs.cpu().numpy()
169
+ label, probs = predict("path/to/screenshot.jpg")
170
+ print(f"Prediction: {label}")
171
+ for name, p in sorted(zip(CLASS_NAMES, probs), key=lambda t: -t[1]):
172
+ print(f" {name:<16} {p*100:5.1f}%")
173
+ ```
174
+ #### Batch prediction
175
+ ```python
176
+ from torch.utils.data import Dataset, DataLoader
177
+
178
+ class ImageListDataset(Dataset):
179
+ def __init__(self, paths, transform):
180
+ self.paths = paths
181
+ self.transform = transform
182
+ def __len__(self):
183
+ return len(self.paths)
184
+ def __getitem__(self, i):
185
+ img = Image.open(self.paths[i]).convert("RGB")
186
+ return self.transform(img), self.paths[i]
187
+ image_paths = ["shot1.jpg", "shot2.jpg", "shot3.jpg"]
188
+ loader = DataLoader(ImageListDataset(image_paths, transform), batch_size=8)
189
+ model.eval()
190
+ with torch.no_grad():
191
+ for images, paths in loader:
192
+ images = images.to(device)
193
+ logits = model(images)
194
+ probs = torch.softmax(logits, dim=1)
195
+ preds = probs.argmax(dim=1)
196
+ for path, pred, p in zip(paths, preds, probs):
197
+ print(f"{path}: {CLASS_NAMES[int(pred)]} ({p[int(pred)]*100:.1f}%)")
198
+ ```
199
+ ## Training Data
200
+
201
+ The model was fine-tuned on the [`Bingsu/Gameplay_Images`](https://huggingface.co/datasets/Bingsu/Gameplay_Images)
202
+ dataset — 10,000 gameplay screenshots (1,000 per class) at native 640×360 resolution, PNG format.
203
+
204
+ - **Labels:** 10 classes (see [Classes](#classes) above)
205
+ - **Splits:** Stratified 70 / 15 / 15 train / validation / test (the source dataset ships a single
206
+ `train` split only; the split above was carved out manually, preserving per-class balance)
207
+ - **Preprocessing:** Resize to 180×320 (custom, aspect-ratio-preserving resolution), ImageNet
208
+ normalization (mean `[0.485, 0.456, 0.406]`, std `[0.229, 0.224, 0.225]`)
209
+ - **Training augmentation:** Random horizontal flip, color jitter, random rotation (±8°), random
210
+ erasing
211
+ - **Class balancing:** The dataset is already perfectly balanced (1,000 images/class); `sklearn`
212
+ balanced class weights are still computed and applied in the loss as a safeguard
213
+
214
+ ## Training Procedure
215
+
216
+ <!-- PLACEHOLDER: training curves (loss/accuracy per epoch) — image to be uploaded -->
217
+
218
+ ![training_curves](https://cdn-uploads.huggingface.co/production/uploads/661d43ec3cf2981df52d0756/lBckExVfa7nctNBbRq36B.png)
219
+
220
+
221
+ - **Framework:** PyTorch
222
+ - **Hardware:** Kaggle free-tier T4 x2 GPUs
223
+ - **Loss:** Cross-entropy with label smoothing (0.1)
224
+ - **Mixed precision:** Enabled (AMP)
225
+
226
+
227
+ ## Evaluation
228
+
229
+ Evaluated on the held-out test split (n = 1,500) at a decision threshold of 0.5.
230
+
231
+ ### Classification Report
232
+
233
+ | Class | Precision | Recall | F1-score | Support |
234
+ |----------------|:---------:|:------:|:--------:|:-------:|
235
+ | Among Us | 1.0000 | 1.0000 | 1.0000 | 150 |
236
+ | Apex Legends | 1.0000 | 0.9933 | 0.9967 | 150 |
237
+ | Fortnite | 1.0000 | 1.0000 | 1.0000 | 150 |
238
+ | Forza Horizon | 1.0000 | 1.0000 | 1.0000 | 150 |
239
+ | Free Fire | 1.0000 | 1.0000 | 1.0000 | 150 |
240
+ | Genshin Impact | 0.9934 | 1.0000 | 0.9967 | 150 |
241
+ | God of War | 1.0000 | 1.0000 | 1.0000 | 150 |
242
+ | Minecraft | 1.0000 | 1.0000 | 1.0000 | 150 |
243
+ | Roblox | 1.0000 | 1.0000 | 1.0000 | 150 |
244
+ | Terraria | 1.0000 | 1.0000 | 1.0000 | 150 |
245
+ | **accuracy** | | | **0.9993** | 1,500 |
246
+ | macro avg | 0.9993 | 0.9993 | 0.9993 | 1,500 |
247
+ | weighted avg | 0.9993 | 0.9993 | 0.9993 | 1,500 |
248
+
249
+ **Test ROC-AUC:** 1.0000 (macro average; per-class AUC is also 1.0000 across all 10 classes)
250
+
251
+ ### Confusion Matrix
252
+
253
+ <!-- PLACEHOLDER: image to be uploaded -->
254
+
255
+
256
+ ![confusion_matrix](https://cdn-uploads.huggingface.co/production/uploads/661d43ec3cf2981df52d0756/GgkCX5ir4A12Iip96hrQy.png)
257
+
258
+ ### ROC Curve
259
+
260
+ <!-- PLACEHOLDER: image to be uploaded -->
261
+
262
+
263
+ ![roc_auc_curves](https://cdn-uploads.huggingface.co/production/uploads/661d43ec3cf2981df52d0756/Q0J0IoHtaHhkq_IaTP-Qf.png)
264
+
265
+ ## Limitations
266
+
267
+ - Performance is reported on a single dataset; generalization to other capture sources, image
268
+ qualities, camera angles, or game versions/UI updates is not guaranteed.
269
+ - The classifier is closed-set — it will always assign one of the 10 trained classes, even to games or
270
+ content it has never seen, rather than rejecting out-of-distribution input.
271
+ - Confidence can be lower on visually ambiguous content, such as games with extensive cosmetic/skin
272
+ systems whose art style can resemble another class in the label set.
273
+ - The model has not been evaluated as a standalone production guardrail; low-confidence predictions
274
+ should be handled with a confidence threshold or human review rather than trusted outright.
275
+
276
+ ## Citation
277
+
278
+ If you use this model, please cite this repository and reference this course project:
279
+
280
+ ```
281
+ @misc{game-detection-classifier,
282
+ title = {Automated Video Game Recognition and Hashtag Suggestion for Live Streaming Platforms
283
+ Using Image Classification},
284
+ author = {S. M. Nihal Ahmed and Afrim Hossen Khan},
285
+ year = {2026},
286
+ note = {Course project, AI Lab (SE334), Daffodil International University}
287
+ }
288
+ ```