Upload 3 files
Browse files- .gitattributes +1 -0
- app.py +80 -0
- digit_recognizer_model.keras +3 -0
- requirements.txt +3 -0
.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 |
+
digit_recognizer_model.keras filter=lfs diff=lfs merge=lfs -text
|
app.py
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import tensorflow as tf
|
3 |
+
import numpy as np
|
4 |
+
from PIL import Image, ImageOps
|
5 |
+
|
6 |
+
# --- 1. Load the Saved Model ---
|
7 |
+
try:
|
8 |
+
model = tf.keras.models.load_model('digit_recognizer_model.keras')
|
9 |
+
except Exception as e:
|
10 |
+
print(f"Error loading model: {e}")
|
11 |
+
model = None
|
12 |
+
|
13 |
+
# --- 2. Create the Prediction Function ---
|
14 |
+
def predict_digit(sketchpad_data):
|
15 |
+
if model is None or sketchpad_data is None:
|
16 |
+
return "Model not loaded or no drawing provided."
|
17 |
+
|
18 |
+
# Use the 'composite' key to get the final image with the drawing
|
19 |
+
composite_image = sketchpad_data['composite']
|
20 |
+
|
21 |
+
if composite_image is None:
|
22 |
+
return "Please draw a digit."
|
23 |
+
|
24 |
+
# --- ADVANCED PREPROCESSING ---
|
25 |
+
|
26 |
+
# 1. Convert the NumPy array to a Pillow Image object, letting Pillow detect the mode
|
27 |
+
pil_image = Image.fromarray(composite_image.astype('uint8')).convert('L')
|
28 |
+
|
29 |
+
# 2. Invert colors (so digit is white, background is black)
|
30 |
+
inverted_image = ImageOps.invert(pil_image)
|
31 |
+
|
32 |
+
# 3. Find the bounding box of the digit
|
33 |
+
bbox = inverted_image.getbbox()
|
34 |
+
if bbox is None:
|
35 |
+
return "Please draw a digit." # Handle blank image
|
36 |
+
|
37 |
+
# 4. Crop the image to the bounding box
|
38 |
+
cropped_image = inverted_image.crop(bbox)
|
39 |
+
|
40 |
+
# 5. Resize the digit to fit within a 20x20 box to add padding, preserving aspect ratio
|
41 |
+
cropped_image.thumbnail((20, 20), Image.Resampling.LANCZOS)
|
42 |
+
|
43 |
+
# 6. Create a new 28x28 black canvas and paste the digit in the center
|
44 |
+
new_canvas = Image.new('L', (28, 28), 0) # Black background
|
45 |
+
paste_x = (28 - cropped_image.width) // 2
|
46 |
+
paste_y = (28 - cropped_image.height) // 2
|
47 |
+
new_canvas.paste(cropped_image, (paste_x, paste_y))
|
48 |
+
|
49 |
+
# 7. Convert the final canvas to a NumPy array for the model
|
50 |
+
image = np.array(new_canvas)
|
51 |
+
|
52 |
+
# 8. Reshape for the model and normalize.
|
53 |
+
image = image.reshape(1, 28, 28).astype('float32') / 255.0
|
54 |
+
|
55 |
+
# --- End of Preprocessing ---
|
56 |
+
|
57 |
+
# Make a prediction
|
58 |
+
prediction = model.predict(image)
|
59 |
+
|
60 |
+
# Format the prediction into a dictionary of confidences
|
61 |
+
confidences = {str(i): float(prediction[0][i]) for i in range(10)}
|
62 |
+
|
63 |
+
return confidences
|
64 |
+
|
65 |
+
# --- 3. Create and Launch the Gradio Interface ---
|
66 |
+
inputs = gr.Sketchpad(image_mode='L')
|
67 |
+
outputs = gr.Label(num_top_classes=1, label="Prediction")
|
68 |
+
|
69 |
+
iface = gr.Interface(
|
70 |
+
fn=predict_digit,
|
71 |
+
inputs=inputs,
|
72 |
+
outputs=outputs,
|
73 |
+
title="Handwritten Digit Recognizer ✍️",
|
74 |
+
description="Draw a digit from 0 to 9 on the canvas, and the model will predict what it is. For best results, draw a single, clear digit.",
|
75 |
+
live=True
|
76 |
+
)
|
77 |
+
|
78 |
+
# Launch the app!
|
79 |
+
print("Launching the Gradio app...")
|
80 |
+
iface.launch()
|
digit_recognizer_model.keras
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a302a3844d78f2f7d90c6621668677ef6e8cc3ad766db750fafd6dc179335d32
|
3 |
+
size 8359737
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
tensorflow
|
2 |
+
gradio
|
3 |
+
Pillow
|