Views
No views yet
google/efficientnet-b0
backbone, trained at a custom, aspect-ratio-preserving 180×320 input resolution (instead of the
standard 224×224 square crop) on the Bingsu/Gameplay_Images
dataset.google/efficientnet-b0AdaptiveAvgPool2d head is
resolution-agnostic.sklearn balanced class
weights applied in the loss (the source dataset is already perfectly balanced at 1,000 images/class)Among Us, Apex Legends, Fortnite, Forza Horizon, Free Fire, Genshin Impact, God of War, Minecraft, Roblox, Terraria.onnx graph loads its weights from the
.onnx.data file alongside it at runtime:efficientnet_b0_gameplay.onnx — the ONNX graphefficientnet_b0_gameplay.onnx.data — the external weights filepip install onnxruntime huggingface_hub pillow numpy1import numpy as np
2import onnxruntime as ort
3from PIL import Image
4from huggingface_hub import hf_hub_download
5
6REPO_ID = "nihal4/Game_Detection"
7IMG_SIZE = (320, 180) # PIL resize takes (width, height)
8IMAGENET_MEAN = np.array([0.485, 0.456, 0.406], dtype=np.float32)
9IMAGENET_STD = np.array([0.229, 0.224, 0.225], dtype=np.float32)
10CLASS_NAMES = ['Among Us', 'Apex Legends', 'Fortnite', 'Forza Horizon', 'Free Fire',
11 'Genshin Impact', 'God of War', 'Minecraft', 'Roblox', 'Terraria']
12
13# Downloads both files into the same local cache folder — required, since the
14# .onnx graph references .onnx.data by relative path at load time.
15onnx_path = hf_hub_download(repo_id=REPO_ID, filename="efficientnet_b0_gameplay.onnx")
16hf_hub_download(repo_id=REPO_ID, filename="efficientnet_b0_gameplay.onnx.data")
17
18session = ort.InferenceSession(onnx_path, providers=["CPUExecutionProvider"])
19input_name = session.get_inputs()[0].name
20output_name = session.get_outputs()[0].name
21
22def preprocess_pil(img: Image.Image) -> np.ndarray:
23 img = img.convert("RGB").resize(IMG_SIZE)
24 arr = np.asarray(img, dtype=np.float32) / 255.0 # HWC, [0,1]
25 arr = (arr - IMAGENET_MEAN) / IMAGENET_STD # normalize, same stats as training
26 return arr.transpose(2, 0, 1) # HWC -> CHW
27
28def softmax(x: np.ndarray) -> np.ndarray:
29 e = np.exp(x - x.max(axis=1, keepdims=True))
30 return e / e.sum(axis=1, keepdims=True)
31
32def predict(image_path: str):
33 image = Image.open(image_path)
34 x = preprocess_pil(image)[np.newaxis, ...].astype(np.float32)
35 logits = session.run([output_name], {input_name: x})[0]
36 probs = softmax(logits)[0]
37 top_idx = int(probs.argmax())
38 return CLASS_NAMES[top_idx], probs
39
40label, probs = predict("path/to/screenshot.jpg")
41print(f"Prediction: {label}")
42for name, p in sorted(zip(CLASS_NAMES, probs), key=lambda t: -t[1]):
43 print(f" {name:<16} {p*100:5.1f}%")1image_paths = ["shot1.jpg", "shot2.jpg", "shot3.jpg"]
2
3batch = np.stack([preprocess_pil(Image.open(p)) for p in image_paths]).astype(np.float32)
4logits = session.run([output_name], {input_name: batch})[0]
5probs = softmax(logits)
6preds = probs.argmax(axis=1)
7
8for path, pred, p in zip(image_paths, preds, probs):
9 print(f"{path}: {CLASS_NAMES[int(pred)]} ({p[int(pred)]*100:.1f}%)")For GPU inference, installonnxruntime-gpuinstead and passproviders=["CUDAExecutionProvider", "CPUExecutionProvider"]when creating the session.
pip install torch torchvision huggingface_hub pillow numpy1import torch
2import torch.nn as nn
3import numpy as np
4from torchvision import models, transforms
5from PIL import Image
6from huggingface_hub import hf_hub_download
7
8REPO_ID = "nihal4/Game_Detection"
9IMG_SIZE = (180, 320) # (H, W) — torchvision transforms convention
10CLASS_NAMES = ['Among Us', 'Apex Legends', 'Fortnite', 'Forza Horizon', 'Free Fire',
11 'Genshin Impact', 'God of War', 'Minecraft', 'Roblox', 'Terraria']
12
13ckpt_path = hf_hub_download(repo_id=REPO_ID, filename="efficientnet_b0_gameplay_final.pth")
14checkpoint = torch.load(ckpt_path, map_location="cpu")
15
16device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
17
18model = models.efficientnet_b0(weights=None)
19in_features = model.classifier[1].in_features
20model.classifier[1] = nn.Linear(in_features, len(CLASS_NAMES))
21model.load_state_dict(checkpoint["model_state_dict"])
22model.to(device).eval()
23
24transform = transforms.Compose([
25 transforms.Resize(IMG_SIZE),
26 transforms.ToTensor(),
27 transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
28])
29
30@torch.no_grad()
31def predict(image_path: str):
32 image = Image.open(image_path).convert("RGB")
33 x = transform(image).unsqueeze(0).to(device)
34 logits = model(x)
35 probs = torch.softmax(logits, dim=1)[0]
36 top_idx = int(probs.argmax())
37 return CLASS_NAMES[top_idx], probs.cpu().numpy()
38
39label, probs = predict("path/to/screenshot.jpg")
40print(f"Prediction: {label}")
41for name, p in sorted(zip(CLASS_NAMES, probs), key=lambda t: -t[1]):
42 print(f" {name:<16} {p*100:5.1f}%")1from torch.utils.data import Dataset, DataLoader
2
3class ImageListDataset(Dataset):
4 def __init__(self, paths, transform):
5 self.paths = paths
6 self.transform = transform
7
8 def __len__(self):
9 return len(self.paths)
10
11 def __getitem__(self, i):
12 img = Image.open(self.paths[i]).convert("RGB")
13 return self.transform(img), self.paths[i]
14
15image_paths = ["shot1.jpg", "shot2.jpg", "shot3.jpg"]
16loader = DataLoader(ImageListDataset(image_paths, transform), batch_size=8)
17
18model.eval()
19with torch.no_grad():
20 for images, paths in loader:
21 images = images.to(device)
22 logits = model(images)
23 probs = torch.softmax(logits, dim=1)
24 preds = probs.argmax(dim=1)
25 for path, pred, p in zip(paths, preds, probs):
26 print(f"{path}: {CLASS_NAMES[int(pred)]} ({p[int(pred)]*100:.1f}%)")Bingsu/Gameplay_Images
dataset — 10,000 gameplay screenshots (1,000 per class) at native 640×360 resolution, PNG format.train split only; the split above was carved out manually, preserving per-class balance)[0.485, 0.456, 0.406], std [0.229, 0.224, 0.225])sklearn
balanced class weights are still computed and applied in the loss as a safeguard
| Class | Precision | Recall | F1-score | Support |
|---|---|---|---|---|
| Among Us | 1.0000 | 1.0000 | 1.0000 | 150 |
| Apex Legends | 1.0000 | 0.9933 | 0.9967 | 150 |
| Fortnite | 1.0000 | 1.0000 | 1.0000 | 150 |
| Forza Horizon | 1.0000 | 1.0000 | 1.0000 | 150 |
| Free Fire | 1.0000 | 1.0000 | 1.0000 | 150 |
| Genshin Impact | 0.9934 | 1.0000 | 0.9967 | 150 |
| God of War | 1.0000 | 1.0000 | 1.0000 | 150 |
| Minecraft | 1.0000 | 1.0000 | 1.0000 | 150 |
| Roblox | 1.0000 | 1.0000 | 1.0000 | 150 |
| Terraria | 1.0000 | 1.0000 | 1.0000 | 150 |
| accuracy | 0.9993 | 1,500 | ||
| macro avg | 0.9993 | 0.9993 | 0.9993 | 1,500 |
| weighted avg | 0.9993 | 0.9993 | 0.9993 | 1,500 |


@misc{game-detection-classifier,
title = {Automated Video Game Recognition and Hashtag Suggestion for Live Streaming Platforms
Using Image Classification},
author = {S. M. Nihal Ahmed and Afrim Hossen Khan},
year = {2026},
note = {Course project, AI Lab (SE334), Daffodil International University}
}