afrim3000 commited on
Commit
4c9ee0e
·
verified ·
1 Parent(s): 0294483

Upload README.md

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