A handwritten character recognition web application built with:
- TensorFlow (CNN trained on EMNIST)
- FastAPI backend
- React 18 + Vite + Tailwind 4 frontend
The app allows users to upload images and get real-time character predictions.
The repository also includes train_model.py, the script I wrote to train the CNN model on the EMNIST dataset.
π Live Demo: https://emnist-char-classifier-1.onrender.com
- CNN model trained on EMNIST dataset (digits + letters)
- FastAPI backend serving predictions
- React 18 frontend with Tailwind 4 styling
- Image upload with preview before prediction
- Returns predicted character and confidence score
- CORS enabled for easy frontend-backend communication
mnist-digit-classifier/
ββ backend/
β ββ app.py # FastAPI backend
β ββ model/
β β ββ emnist_cnn.h5 # Pre-trained CNN model
β ββ train_model.py # Script to train the CNN model
β ββ venv/ # Python virtual environment
ββ frontend/
β ββ src/
β β ββ components/
β β β ββ ImageUploader.jsx
β β β ββ PredictionResult.jsx
β β ββ services/
β β β ββ api.js # Axios requests to backend
β β ββ App.jsx
β ββ package.json
β ββ vite.config.js # Vite + Tailwind 4 configuration
ββ README.md
cd backend
python -m venv venv
venv\Scripts\activate # Windows
# OR source venv/bin/activate # Linux / macOSpip install -r requirements.txtrequirements.txt:
tensorflow
numpy
matplotlib
fastapi
uvicorn
pillow
python-multipart
opencv-python
tensorflow_datasets
uvicorn app:app --reload- Runs at
http://127.0.0.1:8000 /predictendpoint accepts uploaded images
cd frontend
npm installnpm run dev- Runs at
http://localhost:5173(Vite default) - Upload images to get predictions from the backend
- Frontend is component-based:
ImageUploader.jsxβ handles file upload & predictionPredictionResult.jsxβ shows predicted character and confidence
- Tailwind 4 provides styling via Vite configuration (
vite.config.js)
- Upload image (
PNG,JPEG,BMP)
Example using Python:
import requests
url = "http://127.0.0.1:8000/predict"
files = {"file": open("digit_or_letter.png", "rb")}
response = requests.post(url, files=files)
print(response.json())Response:
{
"prediction": "A",
"confidence": 0.9987
}- Model is trained on EMNIST (digits + letters); real-world images may require data augmentation
- Backend preprocessing handles resizing, normalization, rotation, and optional color inversion