AIOmarRehan commited on
Commit
630d6ba
·
verified ·
1 Parent(s): a624552

Upload 9 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ Results/InceptionV3_Sports_Balls_Classification.mp4 filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Python 3.10 image
2
+ FROM python:3.10-slim
3
+
4
+ # Set working directory in container
5
+ WORKDIR /app
6
+
7
+ # Copy requirements first (for caching)
8
+ COPY requirements.txt .
9
+
10
+ # Upgrade pip and install dependencies
11
+ RUN python -m pip install --upgrade pip
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ # Copy app folder and saved model
15
+ COPY app ./app
16
+ COPY saved_model ./saved_model
17
+
18
+ # Expose FastAPI default port
19
+ EXPOSE 8000
20
+
21
+ # Set default command to run FastAPI via uvicorn
22
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
Notebook/Sports_Balls_Classification.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
Results/InceptionV3_Sports_Balls_Classification.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3dcd3ca0715d16ced619bead572e4ce1a919c062aa0c9b337d3763af28929694
3
+ size 8705300
app.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import tensorflow as tf
3
+ import numpy as np
4
+ from PIL import Image
5
+ import os
6
+ from datasets import load_dataset
7
+ import random
8
+
9
+ # Load model
10
+ try:
11
+ model = tf.keras.models.load_model("saved_model/Sports_Balls_Classification.h5")
12
+ except:
13
+ # Fallback if model path is different in HF Spaces
14
+ model = tf.keras.models.load_model("./saved_model/Sports_Balls_Classification.h5")
15
+
16
+ # Class names
17
+ CLASS_NAMES = [
18
+ "american_football", "baseball", "basketball", "billiard_ball",
19
+ "bowling_ball", "cricket_ball", "football", "golf_ball",
20
+ "hockey_ball", "hockey_puck", "rugby_ball", "shuttlecock",
21
+ "table_tennis_ball", "tennis_ball", "volleyball"
22
+ ]
23
+
24
+ def preprocess_image(img, target_size=(225, 225)):
25
+ """Preprocess image for model prediction"""
26
+ if isinstance(img, str):
27
+ img = Image.open(img)
28
+
29
+ img = img.convert("RGB")
30
+ img = img.resize(target_size)
31
+ img_array = np.array(img).astype("float32") / 255.0
32
+ img_array = np.expand_dims(img_array, axis=0)
33
+ return img_array
34
+
35
+ def classify_sports_ball(image):
36
+ """Classify sports ball in image"""
37
+ try:
38
+ # Preprocess
39
+ input_tensor = preprocess_image(image)
40
+
41
+ # Predict
42
+ predictions = model.predict(input_tensor, verbose=0)
43
+ probs = predictions[0]
44
+
45
+ # Get top prediction
46
+ class_idx = int(np.argmax(probs))
47
+ confidence = float(np.max(probs))
48
+
49
+ # Create prediction dictionary
50
+ pred_dict = {CLASS_NAMES[i]: float(probs[i]) for i in range(len(CLASS_NAMES))}
51
+
52
+ # Sort by confidence
53
+ pred_dict = dict(sorted(pred_dict.items(), key=lambda x: x[1], reverse=True))
54
+
55
+ return pred_dict
56
+
57
+ except Exception as e:
58
+ return {"error": str(e)}
59
+
60
+ def load_random_dataset_image():
61
+ """Load a random image from HuggingFace dataset"""
62
+ try:
63
+ dataset = load_dataset("Omarinooooo/test", split="train", trust_remote_code=True)
64
+ random_idx = random.randint(0, len(dataset) - 1)
65
+ sample = dataset[random_idx]
66
+
67
+ # Handle different possible image column names
68
+ image = None
69
+ for col in ["image", "img", "photo", "picture"]:
70
+ if col in sample:
71
+ image = sample[col]
72
+ break
73
+
74
+ if image is None:
75
+ # Try first column that might be an image
76
+ for col, val in sample.items():
77
+ if isinstance(val, Image.Image):
78
+ image = val
79
+ break
80
+
81
+ if image is None:
82
+ return None
83
+
84
+ if not isinstance(image, Image.Image):
85
+ image = Image.open(image)
86
+
87
+ return image
88
+
89
+ except Exception as e:
90
+ print(f"Error loading dataset: {e}")
91
+ return None
92
+
93
+ # Create Gradio interface
94
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
95
+ gr.Markdown(
96
+ """
97
+ # Sports Ball Classifier
98
+
99
+ Upload an image of a sports ball to classify it. The model uses InceptionV3 transfer learning
100
+ to identify 15 different types of sports balls.
101
+
102
+ **Supported Sports Balls:**
103
+ American Football, Baseball, Basketball, Billiard Ball, Bowling Ball, Cricket Ball, Football,
104
+ Golf Ball, Hockey Ball, Hockey Puck, Rugby Ball, Shuttlecock, Table Tennis Ball, Tennis Ball, Volleyball
105
+ """
106
+ )
107
+
108
+ with gr.Row():
109
+ with gr.Column():
110
+ image_input = gr.Image(
111
+ type="pil",
112
+ label="Upload Sports Ball Image",
113
+ scale=1
114
+ )
115
+ with gr.Row():
116
+ submit_button = gr.Button("Classify", variant="primary", scale=2)
117
+ random_button = gr.Button("Random Dataset", variant="secondary", scale=1)
118
+
119
+ with gr.Column():
120
+ output = gr.Label(label="Prediction Confidence", num_top_classes=5)
121
+
122
+ with gr.Row():
123
+ gr.Markdown(
124
+ """
125
+ ### How to Use:
126
+ 1. Upload or drag-and-drop an image containing a sports ball
127
+ 2. Click the 'Classify' button
128
+ 3. View the prediction results with confidence scores
129
+
130
+ ### Model Details:
131
+ - Architecture: InceptionV3 (transfer learning from ImageNet)
132
+ - Training: Two-stage training (feature extraction + fine-tuning)
133
+ - Accuracy: High performance across all 15 sports ball classes
134
+ - Preprocessing: Automatic image resizing, normalization, and enhancement
135
+ """
136
+ )
137
+
138
+ with gr.Row():
139
+ gr.Examples(
140
+ examples=[],
141
+ inputs=image_input,
142
+ label="Example Images (if available)",
143
+ run_on_click=False
144
+ )
145
+
146
+ # Connect button to function
147
+ submit_button.click(fn=classify_sports_ball, inputs=image_input, outputs=output)
148
+ random_button.click(fn=load_random_dataset_image, outputs=image_input).then(
149
+ fn=classify_sports_ball, inputs=image_input, outputs=output
150
+ )
151
+
152
+ # Also allow pressing Enter on image upload
153
+ image_input.change(fn=classify_sports_ball, inputs=image_input, outputs=output)
154
+
155
+ if __name__ == "__main__":
156
+ demo.launch(
157
+ server_name="0.0.0.0",
158
+ server_port=7860,
159
+ share=False
160
+ )
app/__pycache__/model.cpython-313.pyc ADDED
Binary file (2.12 kB). View file
 
app/main.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File
2
+ from fastapi.responses import JSONResponse
3
+ from app.model import predict
4
+ from PIL import Image
5
+ import io
6
+
7
+ app = FastAPI(title="Sports Balls Image Classifier")
8
+
9
+ @app.post("/predict")
10
+ async def predict_image(file: UploadFile = File(...)):
11
+ try:
12
+ # Read image from uploaded file
13
+ contents = await file.read()
14
+ img = Image.open(io.BytesIO(contents))
15
+
16
+ # Run prediction
17
+ label, confidence, probs = predict(img)
18
+
19
+ return JSONResponse(content={
20
+ "predicted_label": label,
21
+ "confidence": round(confidence, 3),
22
+ "probabilities": {k: round(v, 3) for k, v in probs.items()}
23
+ })
24
+
25
+ except Exception as e:
26
+ return JSONResponse(content={"error": str(e)}, status_code=500)
app/model.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tensorflow as tf
2
+ import numpy as np
3
+ from PIL import Image
4
+
5
+ # Load your trained CNN model
6
+ model = tf.keras.models.load_model("saved_model/Sports_Balls_Classification.h5")
7
+
8
+ # Same label order you used when training (from LabelEncoder)
9
+ CLASS_NAMES = [
10
+ "american_football", "baseball", "basketball", "billiard_ball",
11
+ "bowling_ball", "cricket_ball", "football", "golf_ball",
12
+ "hockey_ball", "hockey_puck", "rugby_ball", "shuttlecock",
13
+ "table_tennis_ball", "tennis_ball", "volleyball"
14
+ ]
15
+
16
+ def preprocess_image(img: Image.Image, target_size=(225, 225)):
17
+ """
18
+ Preprocess a PIL image to match training pipeline:
19
+ - Convert to RGB
20
+ - Resize
21
+ - Convert to float32
22
+ - Normalize to [0,1]
23
+ - Add batch dimension
24
+ """
25
+ img = img.convert("RGB") # ensure 3 channels
26
+ img = img.resize(target_size)
27
+ img = np.array(img).astype("float32") / 255.0 # normalize
28
+ img = np.expand_dims(img, axis=0) # (1, 225, 225, 3)
29
+ return img
30
+
31
+
32
+ def predict(img: Image.Image):
33
+ # Apply preprocessing
34
+ input_tensor = preprocess_image(img)
35
+
36
+ # Model prediction
37
+ preds = model.predict(input_tensor)
38
+ probs = preds[0]
39
+ class_idx = int(np.argmax(probs))
40
+ confidence = float(np.max(probs))
41
+
42
+ # Map all probabilities
43
+ prob_dict = {CLASS_NAMES[i]: float(probs[i]) for i in range(len(CLASS_NAMES))}
44
+
45
+ return CLASS_NAMES[class_idx], confidence, prob_dict
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ tensorflow
4
+ numpy
5
+ python-multipart
6
+ pillow
saved_model/Sports_Balls_Classification.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4c7f12770b686a1d722e5c2343cadd7bdf0a02157d27e1b3b8b980554f3d3220
3
+ size 142156800