Handwritten digit recognition, powered by deep learning.
Upload a photo or snap one with your camera, and a CNN trained on MNIST tells you which digit it sees, with a confidence score and a full look at how the image was processed.
- About the Project
- Features
- How It Works
- Model Architecture
- Model Performance
- Tech Stack
- Getting Started
- Usage Tips
- Project Structure
- Deployment
- Limitations
- License
- Author
MNIST models usually stop at a notebook: great accuracy on clean 28Γ28 test images, and no idea what happens with a real photo. DigitVision closes that gap. It pairs a compact, regularized CNN with a computer-vision preprocessing pipeline that turns messy, real-world photos of handwritten digits into MNIST-style input, and wraps everything in a clean Streamlit web app.
You can try it right now: digitvision-app.streamlit.app
- Two input modes: upload an image (PNG/JPG) or take a photo directly with your camera.
- Smart digit extraction: detects red ink via HSV color masking, with an automatic Otsu-threshold fallback for dark pen on light paper.
- MNIST-style normalization: crops, scales to 20 px, pads to 28Γ28 and centers the digit by its center of mass, the same way MNIST was built.
- Transparent results: predicted digit, confidence level (high / medium / low), top-3 predictions and a probability chart for all ten classes.
- "How DigitVision saw your image": inspect the detection box, binary mask and the exact 28Γ28 model input.
- Model dashboard: test accuracy, parameter count, epochs and per-digit accuracy in the sidebar.
- Prediction history: the last 10 predictions of your session.
- Reproducible training: fixed seeds and a single script (
train.py) that also writesmetrics.json.
Photo / Upload
β
βΌ
Resize (max 800 px)
β
βΌ
Enough red ink found? ββ yes βββΆ HSV red-ink mask βββββββ
β no β
βΌ β
Grayscale β Gaussian blur β Otsu threshold ββββββββββββββ€
(auto-inverted if the background is dark) β
βΌ
Morphological close + open
β
βΌ
Contour detection β bounding box
β
βΌ
Scale to 20 px β pad to 28Γ28 β center of mass
β
βΌ
CNN β softmax (10 classes)
β
βΌ
Digit + confidence + charts
- Segmentation: the image is searched for red ink first; if there isn't enough, it falls back to grayscale + Gaussian blur + Otsu thresholding (auto-inverted when the background is dark).
- Cleanup: morphological closing and opening remove noise and fill small gaps.
- Localization: contours with at least 10 % of the largest contour's area are merged into one bounding box.
- Normalization: the digit is resized so its longest side is 20 px, placed on a 28Γ28 canvas and shifted so its center of mass sits at the center.
- Classification: the CNN outputs a probability for each digit 0β9.
A lightweight VGG-style CNN with built-in data augmentation, 289,066 parameters in total.
| Stage | Layers |
|---|---|
| Augmentation (training only) | RandomRotation(0.08) β RandomTranslation(0.1, 0.1) β RandomZoom(0.12) |
| Block 1 | 2 Γ [Conv2D(32, 3Γ3) β BatchNorm β ReLU] β MaxPool β Dropout(0.25) |
| Block 2 | 2 Γ [Conv2D(64, 3Γ3) β BatchNorm β ReLU] β MaxPool β Dropout(0.25) |
| Block 3 | 1 Γ [Conv2D(128, 3Γ3) β BatchNorm β ReLU] β MaxPool β Dropout(0.25) |
| Classifier | Flatten β Dense(128) β BatchNorm β ReLU β Dropout(0.4) β Dense(10, softmax) |
Training setup
| Setting | Value |
|---|---|
| Optimizer | Adam (lr = 1e-3) |
| Loss | Sparse categorical cross-entropy |
| Batch size | 128 |
| Max epochs | 30 |
| Validation split | 6,000 samples from the training set |
| Callbacks | EarlyStopping (monitor val_accuracy, patience 5, restore best weights), ReduceLROnPlateau (factor 0.5, patience 2, min lr 1e-5) |
| Random seed | 42 |
Evaluated on the official 10,000-image MNIST test set. Early stopping ended training after 15 epochs.
| Metric | Value |
|---|---|
| Test accuracy | 99.32 % |
| Test loss | 0.0205 |
| Parameters | 289,066 |
| Epochs run | 15 (of 30 max) |
| Training time | ~23 min (1,376.7 s on the author's machine) |
Per-digit accuracy
| Digit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Accuracy | 99.69 % | 99.82 % | 99.32 % | 99.80 % | 99.59 % | 98.77 % | 98.12 % | 99.51 % | 99.38 % | 99.01 % |
All numbers come from metrics.json, which train.py generates automatically.
| Area | Tools |
|---|---|
| Deep learning | TensorFlow / Keras |
| Computer vision | OpenCV (headless) |
| Web app | Streamlit |
| Data handling | NumPy, pandas |
| Language | Python |
- Python 3.10 or newer
pip(and optionallyvenv)
# 1. Clone the repository
git clone https://github.com/MahdiKordian/DigitVision.git
cd DigitVision
# 2. (Recommended) create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txtThe repository already includes the trained model (mnist_model.keras), so you can start right away:
streamlit run app.pyThen open the local URL shown in your terminal (usually http://localhost:8501).
python train.pyThis downloads MNIST (if needed), trains the network, and saves:
mnist_model.keras: the trained modelmetrics.json: accuracy, loss, parameters, epochs, training time and per-digit accuracy
The app reads both files automatically.
- Write one digit, large and clear, in the frame.
- A red marker on white paper gives the most reliable detection.
- A dark pen on light paper works too.
- Avoid shadows, glare and busy backgrounds.
- If the result card turns orange or red, the model is unsure: retake the photo with better light or a bigger digit.
DigitVision/
βββ app.py # Streamlit web app (UI, preprocessing, inference)
βββ train.py # Model definition, training and evaluation
βββ config.py # App name, version, tagline and file paths
βββ requirements.txt # Python dependencies
βββ mnist_model.keras # Trained model
βββ metrics.json # Evaluation results shown in the app
βββ LICENSE # MIT License
βββ .gitignore
βββ README.md
The live demo is hosted on Streamlit Community Cloud: digitvision-app.streamlit.app.
To deploy your own copy:
- Fork this repository.
- Go to share.streamlit.io and create a new app from your fork.
- Set the main file to
app.pyand deploy.
Make sure
mnist_model.kerasandmetrics.jsonare committed. The app needs them at startup.
- DigitVision recognizes a single digit per image. Multi-digit numbers are not supported.
- The model is trained on MNIST only. The 99.32 % figure is measured on MNIST's own test set; accuracy on real photos depends on lighting, pen thickness and image quality, which is why the preprocessing pipeline and confidence indicator exist.
- Unusual writing styles (for example a crossed 7 or a looped 1) may reduce confidence.
Distributed under the MIT License. See LICENSE for details.
Mahdi Kordian, AI Engineer working with Machine Learning, Deep Learning, Generative AI and LLMs.
If you find this project useful, a β on the repository is much appreciated.