Spaces:
Sleeping
Sleeping
Upload 9 files
Browse files- .python-version +1 -0
- ACCURACY_GUIDE.md +296 -0
- COMPLETE_PROJECT_SUMMARY.txt +666 -0
- Mnist_analysis.ipynb +209 -0
- app.py +140 -0
- digit_model.h5 +3 -0
- requirements.txt +7 -0
- runtime.txt +1 -0
- templates/index.html +589 -0
.python-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
3.10.13
|
ACCURACY_GUIDE.md
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Improving Digit Recognition Accuracy
|
| 2 |
+
|
| 3 |
+
The updated version includes several improvements to increase recognition accuracy. Here's what changed and how to get the best results:
|
| 4 |
+
|
| 5 |
+
## 🔧 Model Improvements
|
| 6 |
+
|
| 7 |
+
### Better Neural Network Architecture
|
| 8 |
+
- **CNN (Convolutional Neural Network)** instead of simple Dense layers
|
| 9 |
+
- Better at learning spatial patterns in images
|
| 10 |
+
- Multiple convolutional blocks with max pooling
|
| 11 |
+
|
| 12 |
+
- **More training epochs**: 20 epochs instead of 10
|
| 13 |
+
- Longer training time but better accuracy
|
| 14 |
+
|
| 15 |
+
- **Data augmentation**:
|
| 16 |
+
- Rotations (±20°)
|
| 17 |
+
- Shifts (±10%)
|
| 18 |
+
- Shear transformations
|
| 19 |
+
- Zoom variations
|
| 20 |
+
- Helps model generalize to different handwriting styles
|
| 21 |
+
|
| 22 |
+
- **Batch normalization**: Stabilizes training
|
| 23 |
+
- **Higher dropout rates**: Better regularization
|
| 24 |
+
|
| 25 |
+
### Expected Model Accuracy
|
| 26 |
+
- **On MNIST test set**: ~99%
|
| 27 |
+
- **On real handwriting**: 70-95% (varies by writing style)
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
## 📸 Image Processing Improvements
|
| 32 |
+
|
| 33 |
+
The backend now applies advanced preprocessing:
|
| 34 |
+
|
| 35 |
+
1. **Gaussian Blur**: Reduces noise in the drawing
|
| 36 |
+
2. **Adaptive Thresholding**: Better binarization (black and white conversion)
|
| 37 |
+
3. **Contour Detection**: Finds and isolates the digit
|
| 38 |
+
4. **Automatic Centering**: Centers the digit in the image
|
| 39 |
+
5. **High-quality Resizing**: LANCZOS interpolation for sharp 28×28 image
|
| 40 |
+
|
| 41 |
+
This matches how MNIST dataset was prepared!
|
| 42 |
+
|
| 43 |
+
---
|
| 44 |
+
|
| 45 |
+
## ✏️ Drawing Tips for Best Results
|
| 46 |
+
|
| 47 |
+
### 1. **Draw Large & Bold**
|
| 48 |
+
- Fill as much of the canvas as possible
|
| 49 |
+
- Use thick, confident strokes
|
| 50 |
+
- The model expects digits that fill most of the space
|
| 51 |
+
|
| 52 |
+
### 2. **Keep It Simple**
|
| 53 |
+
- Draw one digit at a time
|
| 54 |
+
- Don't add decorations or flourishes
|
| 55 |
+
- Stick to standard digit shapes
|
| 56 |
+
|
| 57 |
+
### 3. **Clean Strokes**
|
| 58 |
+
- Use smooth, continuous lines
|
| 59 |
+
- Avoid shaky or jerky movements
|
| 60 |
+
- Keep thickness consistent
|
| 61 |
+
|
| 62 |
+
### 4. **Centered Position**
|
| 63 |
+
- Keep the digit roughly centered
|
| 64 |
+
- Leave some margin around edges
|
| 65 |
+
- The model will auto-center anyway
|
| 66 |
+
|
| 67 |
+
### 5. **Standard Style**
|
| 68 |
+
- Write digits as you normally would
|
| 69 |
+
- Not too slanted or rotated
|
| 70 |
+
- Avoid unusual personal styles
|
| 71 |
+
|
| 72 |
+
### 6. **Proper Timing**
|
| 73 |
+
- Draw at a normal, comfortable pace
|
| 74 |
+
- Not too fast or slow
|
| 75 |
+
- Smooth pressure on mouse
|
| 76 |
+
|
| 77 |
+
---
|
| 78 |
+
|
| 79 |
+
## 🎯 Digit-Specific Tips
|
| 80 |
+
|
| 81 |
+
### Easy to Recognize
|
| 82 |
+
- **0, 1, 4, 7**: Simple, distinct shapes
|
| 83 |
+
- **6, 9**: Distinct loop patterns
|
| 84 |
+
- **2, 5**: Unique curves
|
| 85 |
+
|
| 86 |
+
### Harder to Recognize
|
| 87 |
+
- **3, 8**: Can look similar to each other
|
| 88 |
+
- **5, 6**: Similar curves and loops
|
| 89 |
+
- **3, 5**: Similar bends
|
| 90 |
+
|
| 91 |
+
**Tip**: Make distinctions clear:
|
| 92 |
+
- **3 vs 5**: Make 3's curves more open
|
| 93 |
+
- **8 vs 6**: Make 8's loops clearly separated
|
| 94 |
+
- **6 vs 9**: Clear which way the loop faces
|
| 95 |
+
|
| 96 |
+
---
|
| 97 |
+
|
| 98 |
+
## 🧪 Testing & Troubleshooting
|
| 99 |
+
|
| 100 |
+
### If accuracy is still low:
|
| 101 |
+
|
| 102 |
+
**1. Check Model Training**
|
| 103 |
+
```bash
|
| 104 |
+
# Delete old model to retrain
|
| 105 |
+
rm digit_model.h5
|
| 106 |
+
python app.py
|
| 107 |
+
```
|
| 108 |
+
The model will retrain and might perform better.
|
| 109 |
+
|
| 110 |
+
**2. Verify Preprocessing**
|
| 111 |
+
- Check that the digit is white on a black background
|
| 112 |
+
- Canvas should now be pure black (was blue)
|
| 113 |
+
- White strokes are critical
|
| 114 |
+
|
| 115 |
+
**3. Clear Drawing Issues**
|
| 116 |
+
- If canvas looks faded, refresh the page
|
| 117 |
+
- Ensure drawing is crisp and clean
|
| 118 |
+
- Use a mouse, not a touchpad if possible
|
| 119 |
+
|
| 120 |
+
**4. Compare Different Digits**
|
| 121 |
+
- Try drawing multiple digits
|
| 122 |
+
- See which ones are recognized well
|
| 123 |
+
- This helps identify writing style issues
|
| 124 |
+
|
| 125 |
+
---
|
| 126 |
+
|
| 127 |
+
## 📊 Model Architecture (Improved)
|
| 128 |
+
|
| 129 |
+
```
|
| 130 |
+
Input (28×28×1)
|
| 131 |
+
↓
|
| 132 |
+
Conv2D 32 filters + BatchNorm + ReLU
|
| 133 |
+
↓
|
| 134 |
+
Conv2D 32 filters + BatchNorm + ReLU
|
| 135 |
+
↓
|
| 136 |
+
MaxPool (2×2) + Dropout(0.25)
|
| 137 |
+
↓
|
| 138 |
+
Conv2D 64 filters + BatchNorm + ReLU
|
| 139 |
+
↓
|
| 140 |
+
Conv2D 64 filters + BatchNorm + ReLU
|
| 141 |
+
↓
|
| 142 |
+
MaxPool (2×2) + Dropout(0.25)
|
| 143 |
+
↓
|
| 144 |
+
Conv2D 128 filters + BatchNorm + ReLU
|
| 145 |
+
↓
|
| 146 |
+
MaxPool (2×2) + Dropout(0.25)
|
| 147 |
+
↓
|
| 148 |
+
Flatten
|
| 149 |
+
↓
|
| 150 |
+
Dense 256 + BatchNorm + ReLU + Dropout(0.5)
|
| 151 |
+
↓
|
| 152 |
+
Dense 128 + BatchNorm + ReLU + Dropout(0.5)
|
| 153 |
+
↓
|
| 154 |
+
Dense 10 (Softmax output for 0-9)
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
**Total Parameters**: ~650K
|
| 158 |
+
**Training Time**: 5-10 minutes first run
|
| 159 |
+
|
| 160 |
+
---
|
| 161 |
+
|
| 162 |
+
## 🔄 If You Want Even Better Accuracy
|
| 163 |
+
|
| 164 |
+
### Option 1: Train on Custom Data
|
| 165 |
+
Collect samples of your own handwriting and retrain:
|
| 166 |
+
|
| 167 |
+
```python
|
| 168 |
+
# Add your custom training data
|
| 169 |
+
custom_digits = [...] # Your digit images
|
| 170 |
+
custom_labels = [...] # Their labels
|
| 171 |
+
|
| 172 |
+
# Combine with MNIST
|
| 173 |
+
x_combined = np.concatenate([x_train, custom_digits])
|
| 174 |
+
y_combined = np.concatenate([y_train, custom_labels])
|
| 175 |
+
|
| 176 |
+
# Retrain model
|
| 177 |
+
model.fit(x_combined, y_combined, epochs=5, ...)
|
| 178 |
+
```
|
| 179 |
+
|
| 180 |
+
### Option 2: Use Pre-trained Model
|
| 181 |
+
Download a pre-trained model from TensorFlow Hub or Keras Applications:
|
| 182 |
+
|
| 183 |
+
```python
|
| 184 |
+
from tensorflow.keras.applications import MobileNetV2
|
| 185 |
+
# Use transfer learning for better performance
|
| 186 |
+
```
|
| 187 |
+
|
| 188 |
+
### Option 3: Ensemble Predictions
|
| 189 |
+
Train multiple models and average their predictions:
|
| 190 |
+
|
| 191 |
+
```python
|
| 192 |
+
model1 = train_model()
|
| 193 |
+
model2 = train_model_different_seed()
|
| 194 |
+
model3 = train_model_different_seed()
|
| 195 |
+
|
| 196 |
+
# Average predictions
|
| 197 |
+
avg_prediction = (
|
| 198 |
+
model1.predict(x) +
|
| 199 |
+
model2.predict(x) +
|
| 200 |
+
model3.predict(x)
|
| 201 |
+
) / 3
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
---
|
| 205 |
+
|
| 206 |
+
## 📈 Performance Expectations
|
| 207 |
+
|
| 208 |
+
| Drawing Style | Expected Accuracy |
|
| 209 |
+
|---|---|
|
| 210 |
+
| Clean, printed-like | 95-99% |
|
| 211 |
+
| Normal handwriting | 80-90% |
|
| 212 |
+
| Cursive/decorative | 60-75% |
|
| 213 |
+
| Very sloppy | 40-60% |
|
| 214 |
+
|
| 215 |
+
**Remember**: The model is trained on the MNIST dataset, which has specific digit styles. The more your handwriting matches those styles, the better the accuracy.
|
| 216 |
+
|
| 217 |
+
---
|
| 218 |
+
|
| 219 |
+
## 🐛 Common Issues & Solutions
|
| 220 |
+
|
| 221 |
+
### Issue: Low accuracy on certain digits
|
| 222 |
+
|
| 223 |
+
**Solution**:
|
| 224 |
+
- Make that digit more distinct
|
| 225 |
+
- Use clearer shapes
|
| 226 |
+
- Avoid mixing with other digits
|
| 227 |
+
|
| 228 |
+
### Issue: Model says wrong digit with high confidence
|
| 229 |
+
|
| 230 |
+
**Solution**:
|
| 231 |
+
- Your drawing might not match training style
|
| 232 |
+
- Try different writing style
|
| 233 |
+
- Retrain model with your handwriting samples
|
| 234 |
+
|
| 235 |
+
### Issue: Confidence is always low
|
| 236 |
+
|
| 237 |
+
**Solution**:
|
| 238 |
+
- Drawing might be too faint
|
| 239 |
+
- Use bolder, darker strokes
|
| 240 |
+
- Fill more of the canvas
|
| 241 |
+
- Check canvas background is pure black
|
| 242 |
+
|
| 243 |
+
### Issue: Same drawing predicts different digits
|
| 244 |
+
|
| 245 |
+
**Solution**:
|
| 246 |
+
- Drawing might be ambiguous
|
| 247 |
+
- Make it clearer and more distinct
|
| 248 |
+
- Avoid overlapping strokes
|
| 249 |
+
- Draw more centered
|
| 250 |
+
|
| 251 |
+
---
|
| 252 |
+
|
| 253 |
+
## ✅ Checklist for Best Results
|
| 254 |
+
|
| 255 |
+
- [ ] Canvas background is pure black
|
| 256 |
+
- [ ] Drawing strokes are bright white
|
| 257 |
+
- [ ] Digit fills most of the canvas
|
| 258 |
+
- [ ] Strokes are smooth and continuous
|
| 259 |
+
- [ ] Digit is roughly centered
|
| 260 |
+
- [ ] No extra marks or decorations
|
| 261 |
+
- [ ] Using standard digit shapes
|
| 262 |
+
- [ ] Not too slanted or rotated
|
| 263 |
+
- [ ] Drawing at normal pace
|
| 264 |
+
- [ ] Model has been trained (first run takes 5-10 min)
|
| 265 |
+
|
| 266 |
+
---
|
| 267 |
+
|
| 268 |
+
## 🚀 Quick Start with Improvements
|
| 269 |
+
|
| 270 |
+
```bash
|
| 271 |
+
# 1. Install dependencies (including opencv-python)
|
| 272 |
+
pip install --default-timeout=1000 -r requirements.txt
|
| 273 |
+
|
| 274 |
+
# 2. Run the app (will train improved model)
|
| 275 |
+
python app.py
|
| 276 |
+
|
| 277 |
+
# 3. Open http://localhost:5000
|
| 278 |
+
|
| 279 |
+
# 4. Draw with white on black background
|
| 280 |
+
# 5. Click predict and get results!
|
| 281 |
+
```
|
| 282 |
+
|
| 283 |
+
---
|
| 284 |
+
|
| 285 |
+
## 📚 Technical References
|
| 286 |
+
|
| 287 |
+
- **CNN for digit recognition**: https://keras.io/examples/vision/mnist_convnet/
|
| 288 |
+
- **MNIST dataset**: http://yann.lecun.com/exdb/mnist/
|
| 289 |
+
- **Image preprocessing**: https://en.wikipedia.org/wiki/Thresholding_(image_processing)
|
| 290 |
+
- **Data augmentation**: https://www.tensorflow.org/guide/keras/preprocessing_layers
|
| 291 |
+
|
| 292 |
+
---
|
| 293 |
+
|
| 294 |
+
**Most Important**: The model works best when you write naturally but clearly. Don't overthink it! The improvements in this version should handle most handwriting styles well.
|
| 295 |
+
|
| 296 |
+
Good luck! 🎯
|
COMPLETE_PROJECT_SUMMARY.txt
ADDED
|
@@ -0,0 +1,666 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
╔════════════════════════════════════════════════════════════════════════════╗
|
| 2 |
+
║ ║
|
| 3 |
+
║ HANDWRITTEN DIGIT RECOGNITION - COMPLETE PROJECT ║
|
| 4 |
+
║ Docker-Ready, Production-Grade ║
|
| 5 |
+
║ ║
|
| 6 |
+
╚════════════════════════════════════════════════════════════════════════════╝
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
📦 COMPLETE PROJECT CONTENTS (20+ Files, 226 KB)
|
| 10 |
+
════════════════════════════════════════════════════════════════════════════
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
🎯 APPLICATION FILES (3 core files)
|
| 14 |
+
────────────────────────────────────────────────────────────────────────────
|
| 15 |
+
✓ app.py (Updated for Docker)
|
| 16 |
+
└─ Flask backend with TensorFlow model
|
| 17 |
+
└─ Advanced image preprocessing
|
| 18 |
+
└─ REST API endpoint (/predict)
|
| 19 |
+
└─ Docker environment variable support
|
| 20 |
+
|
| 21 |
+
✓ requirements.txt
|
| 22 |
+
└─ Flask 2.3.3
|
| 23 |
+
└─ TensorFlow 2.12.0
|
| 24 |
+
└─ OpenCV 4.8.0
|
| 25 |
+
└─ Pillow, NumPy, Werkzeug
|
| 26 |
+
|
| 27 |
+
✓ templates/index.html
|
| 28 |
+
└─ Beautiful dark-themed UI
|
| 29 |
+
└─ HTML5 Canvas drawing
|
| 30 |
+
└─ Real-time predictions
|
| 31 |
+
└─ Confidence visualization
|
| 32 |
+
└─ Responsive design
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
🐳 DOCKER CONFIGURATION (11 files)
|
| 36 |
+
────────────────────────────────────────────────────────────────────────────
|
| 37 |
+
Core Docker Files:
|
| 38 |
+
✓ Dockerfile
|
| 39 |
+
✓ docker-compose.yml
|
| 40 |
+
✓ docker-compose.dev.yml
|
| 41 |
+
✓ docker-compose.prod.yml
|
| 42 |
+
✓ docker-compose.nginx.yml
|
| 43 |
+
|
| 44 |
+
Configuration Files:
|
| 45 |
+
✓ nginx.conf (Nginx reverse proxy)
|
| 46 |
+
✓ .dockerignore (Optimize image size)
|
| 47 |
+
✓ .env.example (Configuration template)
|
| 48 |
+
|
| 49 |
+
Utility:
|
| 50 |
+
✓ quickstart.sh (Interactive deployment script)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
📚 DOCUMENTATION (9 files)
|
| 54 |
+
────────────────────────────────────────────────────────────────────────────
|
| 55 |
+
Quick Reference:
|
| 56 |
+
✓ DOCKER_FILES_MANIFEST.txt (File listing & overview)
|
| 57 |
+
✓ DOCKER_README.md (5-minute quick start)
|
| 58 |
+
✓ DOCKER_SETUP_SUMMARY.md (10-minute overview)
|
| 59 |
+
|
| 60 |
+
Comprehensive Guides:
|
| 61 |
+
✓ DOCKER_DEPLOYMENT_GUIDE.md (30+ minute detailed guide)
|
| 62 |
+
✓ DEPLOYMENT_CHECKLIST.md (Step-by-step procedures)
|
| 63 |
+
|
| 64 |
+
Project Documentation:
|
| 65 |
+
✓ README.md (Installation & usage)
|
| 66 |
+
✓ ACCURACY_GUIDE.md (Improving recognition accuracy)
|
| 67 |
+
✓ INSTALLATION_GUIDE.md (Troubleshooting installation)
|
| 68 |
+
✓ DETAILED_PROJECT_INTRODUCTION.md (In-depth technical overview)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
🎓 PRESENTATION & REFERENCE (2 files)
|
| 72 |
+
────────────────────────────────────────────────────────────────────────────
|
| 73 |
+
✓ Handwritten_Digit_Recognition_Presentation.pptx (16 slides)
|
| 74 |
+
✓ COMPLETE_PROJECT_SUMMARY.txt (This file)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
════════════════════════════════════════════════════════════════════════════
|
| 78 |
+
|
| 79 |
+
🚀 QUICK START - 3 WAYS
|
| 80 |
+
════════════════════════════════════════════════════════════════════════════
|
| 81 |
+
|
| 82 |
+
WAY 1: INTERACTIVE SCRIPT (Easiest) ⭐ RECOMMENDED
|
| 83 |
+
────────────────────────────────────────────────────────────────────────────
|
| 84 |
+
$ chmod +x quickstart.sh
|
| 85 |
+
$ ./quickstart.sh
|
| 86 |
+
|
| 87 |
+
Choose from menu:
|
| 88 |
+
1) Development (live editing)
|
| 89 |
+
2) Production (single instance)
|
| 90 |
+
3) Production + Nginx (scalable)
|
| 91 |
+
4) View logs
|
| 92 |
+
5) Cleanup
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
WAY 2: SINGLE COMMAND
|
| 96 |
+
────────────────────────────────────────────────────────────────────────────
|
| 97 |
+
$ docker-compose -f docker-compose.prod.yml up -d
|
| 98 |
+
$ curl http://localhost:5000
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
WAY 3: MANUAL SETUP
|
| 102 |
+
────────────────────────────────────────────────────────────────────────────
|
| 103 |
+
$ cp .env.example .env
|
| 104 |
+
$ docker-compose build
|
| 105 |
+
$ docker-compose up -d
|
| 106 |
+
$ docker logs -f digit-app
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
════════════════════════════════════════════════════════════════════════════
|
| 110 |
+
|
| 111 |
+
✨ KEY FEATURES
|
| 112 |
+
════════════════════════════════════════════════════════════════════════════
|
| 113 |
+
|
| 114 |
+
🤖 MACHINE LEARNING
|
| 115 |
+
✓ 99% accuracy on MNIST dataset
|
| 116 |
+
✓ 70-95% accuracy on real handwriting
|
| 117 |
+
✓ Advanced CNN architecture (650K parameters)
|
| 118 |
+
✓ Data augmentation for robustness
|
| 119 |
+
✓ Fast predictions (100-200ms)
|
| 120 |
+
|
| 121 |
+
🎨 USER INTERFACE
|
| 122 |
+
✓ Beautiful dark theme with cyan accents
|
| 123 |
+
✓ Real-time canvas drawing
|
| 124 |
+
✓ Confidence visualization
|
| 125 |
+
✓ Probability distribution display
|
| 126 |
+
✓ Fully responsive design
|
| 127 |
+
✓ Smooth animations
|
| 128 |
+
|
| 129 |
+
🐳 DOCKER DEPLOYMENT
|
| 130 |
+
✓ 3 deployment modes (dev/prod/nginx)
|
| 131 |
+
✓ Production-ready configuration
|
| 132 |
+
✓ Health checks & auto-restart
|
| 133 |
+
✓ Resource limits & monitoring
|
| 134 |
+
✓ Scalable architecture
|
| 135 |
+
✓ SSL/TLS support ready
|
| 136 |
+
|
| 137 |
+
📚 DOCUMENTATION
|
| 138 |
+
✓ 9 comprehensive guides
|
| 139 |
+
✓ Quick start options
|
| 140 |
+
✓ Troubleshooting guides
|
| 141 |
+
✓ Deployment checklists
|
| 142 |
+
✓ Architecture diagrams
|
| 143 |
+
✓ Security best practices
|
| 144 |
+
|
| 145 |
+
💻 TECHNOLOGY
|
| 146 |
+
✓ Flask + TensorFlow + OpenCV
|
| 147 |
+
✓ HTML5 Canvas + CSS3 + JavaScript
|
| 148 |
+
✓ Docker + Docker Compose + Nginx
|
| 149 |
+
✓ Python 3.10 slim image
|
| 150 |
+
✓ ~800MB container image
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
════════════════════════════════════════════════════════════════════════════
|
| 154 |
+
|
| 155 |
+
📊 DEPLOYMENT COMPARISON TABLE
|
| 156 |
+
════════════════════════════════════════════════════════════════════════════
|
| 157 |
+
|
| 158 |
+
Feature │ Development │ Production │ Production+Nginx
|
| 159 |
+
────────────────────────┼──────────────┼──────────────┼─────────────────
|
| 160 |
+
Live Code Editing │ ✅ │ ❌ │ ❌
|
| 161 |
+
Debug Mode │ ✅ │ ❌ │ ❌
|
| 162 |
+
Auto-Restart │ ⚠️ │ ✅ │ ✅
|
| 163 |
+
Resource Limits │ ❌ │ ✅ │ ✅
|
| 164 |
+
Health Checks │ ⚠️ │ ✅ │ ✅
|
| 165 |
+
Load Balancing │ ❌ │ ❌ │ ✅
|
| 166 |
+
SSL/TLS Ready │ ❌ │ ❌ │ ✅
|
| 167 |
+
Multiple Instances │ ❌ │ ❌ │ ✅
|
| 168 |
+
Production Ready │ ❌ │ ✅ │ ✅✅
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
════════════════════════════════════════════════════════════════════════════
|
| 172 |
+
|
| 173 |
+
📈 PERFORMANCE METRICS
|
| 174 |
+
════════════════════════════════════════════════════════════════════════════
|
| 175 |
+
|
| 176 |
+
Model Accuracy
|
| 177 |
+
• MNIST Test Set: 99%+
|
| 178 |
+
• Real Handwriting: 70-95%
|
| 179 |
+
• Average: ~85%
|
| 180 |
+
|
| 181 |
+
Speed Performance
|
| 182 |
+
• Image Preprocessing: 10-30ms
|
| 183 |
+
• Model Inference: 50-100ms
|
| 184 |
+
• Total Backend: 100-150ms
|
| 185 |
+
• Network Roundtrip: 50-100ms (varies)
|
| 186 |
+
• Total E2E: 150-250ms
|
| 187 |
+
|
| 188 |
+
Resource Usage
|
| 189 |
+
• Image Size: ~800MB
|
| 190 |
+
• Memory (idle): 500MB
|
| 191 |
+
• Memory (running): 1-2GB
|
| 192 |
+
• CPU (idle): <10% of 1 core
|
| 193 |
+
• CPU (predicting): 80-100% of 1 core
|
| 194 |
+
|
| 195 |
+
Scalability
|
| 196 |
+
• Single Instance: 5-10 predictions/sec
|
| 197 |
+
• Multi-Instance: Linear scaling
|
| 198 |
+
• Max Throughput: 100+ predictions/sec (10 instances)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
════════════════════════════════════════════════════════════════════════════
|
| 202 |
+
|
| 203 |
+
🌐 DEPLOYMENT PLATFORMS
|
| 204 |
+
════════════════════════════════════════════════════════════════════════════
|
| 205 |
+
|
| 206 |
+
✓ Local Development
|
| 207 |
+
• MacOS, Linux, Windows (WSL2)
|
| 208 |
+
• Perfect for testing
|
| 209 |
+
|
| 210 |
+
✓ Production Servers
|
| 211 |
+
• Linux servers (Ubuntu, Debian, CentOS)
|
| 212 |
+
• Self-hosted infrastructure
|
| 213 |
+
|
| 214 |
+
✓ Cloud Platforms
|
| 215 |
+
• AWS EC2 (recommended, lowest cost)
|
| 216 |
+
• Google Cloud Run (serverless)
|
| 217 |
+
• Azure Container Instances
|
| 218 |
+
• DigitalOcean App Platform
|
| 219 |
+
• Heroku (with modifications)
|
| 220 |
+
|
| 221 |
+
✓ Container Orchestration
|
| 222 |
+
• Kubernetes (K8s) - advanced
|
| 223 |
+
• Docker Swarm - simpler
|
| 224 |
+
• AWS ECS - AWS-specific
|
| 225 |
+
|
| 226 |
+
✓ Container Registries
|
| 227 |
+
• Docker Hub (public/private)
|
| 228 |
+
• AWS ECR
|
| 229 |
+
• Google Container Registry
|
| 230 |
+
• Azure Container Registry
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
════════════════════════════════════════════════════════════════════════════
|
| 234 |
+
|
| 235 |
+
📋 FILES CHECKLIST - VERIFY YOU HAVE EVERYTHING
|
| 236 |
+
════════════════════════════════════════════════════════════════════════════
|
| 237 |
+
|
| 238 |
+
Application Files:
|
| 239 |
+
☑ app.py
|
| 240 |
+
☑ requirements.txt
|
| 241 |
+
☑ templates/index.html
|
| 242 |
+
|
| 243 |
+
Docker Configuration (Core):
|
| 244 |
+
☑ Dockerfile
|
| 245 |
+
☑ docker-compose.yml
|
| 246 |
+
☑ docker-compose.dev.yml
|
| 247 |
+
☑ docker-compose.prod.yml
|
| 248 |
+
☑ docker-compose.nginx.yml
|
| 249 |
+
☑ nginx.conf
|
| 250 |
+
|
| 251 |
+
Docker Configuration (Other):
|
| 252 |
+
☑ .dockerignore
|
| 253 |
+
☑ .env.example
|
| 254 |
+
☑ quickstart.sh (executable)
|
| 255 |
+
|
| 256 |
+
Documentation:
|
| 257 |
+
☑ DOCKER_FILES_MANIFEST.txt
|
| 258 |
+
☑ DOCKER_README.md
|
| 259 |
+
☑ DOCKER_SETUP_SUMMARY.md
|
| 260 |
+
☑ DOCKER_DEPLOYMENT_GUIDE.md
|
| 261 |
+
☑ DEPLOYMENT_CHECKLIST.md
|
| 262 |
+
☑ README.md
|
| 263 |
+
☑ ACCURACY_GUIDE.md
|
| 264 |
+
☑ INSTALLATION_GUIDE.md
|
| 265 |
+
☑ DETAILED_PROJECT_INTRODUCTION.md
|
| 266 |
+
|
| 267 |
+
Presentation:
|
| 268 |
+
☑ Handwritten_Digit_Recognition_Presentation.pptx
|
| 269 |
+
|
| 270 |
+
Summary:
|
| 271 |
+
☑ COMPLETE_PROJECT_SUMMARY.txt (this file)
|
| 272 |
+
|
| 273 |
+
TOTAL: 20+ files, 226 KB
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
════════════════════════════════════════════════════════════════════════════
|
| 277 |
+
|
| 278 |
+
🎯 GETTING STARTED CHECKLIST
|
| 279 |
+
════════════════════════════════════════════════════════════════════════════
|
| 280 |
+
|
| 281 |
+
Prerequisites:
|
| 282 |
+
☑ Docker installed (docker --version)
|
| 283 |
+
☑ Docker Compose installed (docker-compose --version)
|
| 284 |
+
☑ All files present (verify from checklist above)
|
| 285 |
+
|
| 286 |
+
Setup:
|
| 287 |
+
☑ Navigate to project directory
|
| 288 |
+
☑ (Optional) Edit .env for custom settings
|
| 289 |
+
☑ Make quickstart.sh executable: chmod +x quickstart.sh
|
| 290 |
+
|
| 291 |
+
Deployment:
|
| 292 |
+
☑ Run: ./quickstart.sh
|
| 293 |
+
☑ Select deployment mode
|
| 294 |
+
☑ Wait 30-40 seconds for startup
|
| 295 |
+
☑ Open http://localhost:5000
|
| 296 |
+
|
| 297 |
+
Testing:
|
| 298 |
+
☑ Draw a digit on canvas
|
| 299 |
+
☑ Click "Predict" button
|
| 300 |
+
☑ Verify prediction works
|
| 301 |
+
☑ Check confidence score
|
| 302 |
+
|
| 303 |
+
Documentation:
|
| 304 |
+
☑ Read DOCKER_README.md (5 min) for quick reference
|
| 305 |
+
☑ Read DEPLOYMENT_CHECKLIST.md (15 min) for procedures
|
| 306 |
+
☑ Read DOCKER_DEPLOYMENT_GUIDE.md (30 min) for deep dive
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
════════════════════════════════════════════════════════════════════════════
|
| 310 |
+
|
| 311 |
+
💡 QUICK COMMAND REFERENCE
|
| 312 |
+
════════════════════════════════════════════════════════════════════════════
|
| 313 |
+
|
| 314 |
+
Container Management:
|
| 315 |
+
docker ps # List running containers
|
| 316 |
+
docker logs -f <name> # View container logs
|
| 317 |
+
docker stats # Monitor resource usage
|
| 318 |
+
docker exec -it <name> bash # Access container shell
|
| 319 |
+
|
| 320 |
+
Development:
|
| 321 |
+
docker-compose -f docker-compose.dev.yml up
|
| 322 |
+
# Edit app.py, changes appear instantly
|
| 323 |
+
# Full debug output
|
| 324 |
+
|
| 325 |
+
Production:
|
| 326 |
+
docker-compose -f docker-compose.prod.yml up -d
|
| 327 |
+
docker-compose -f docker-compose.prod.yml down
|
| 328 |
+
docker-compose logs -f
|
| 329 |
+
|
| 330 |
+
Scaling (Nginx):
|
| 331 |
+
docker-compose -f docker-compose.nginx.yml up -d --scale digit-app=3
|
| 332 |
+
# Nginx automatically load-balances across instances
|
| 333 |
+
|
| 334 |
+
Cleanup:
|
| 335 |
+
docker-compose down # Stop containers
|
| 336 |
+
docker system prune -a # Remove unused resources
|
| 337 |
+
rm -rf ./models # (if needed) Reset models
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
════════════════════════════════════════════════════════════════════════════
|
| 341 |
+
|
| 342 |
+
🔐 SECURITY CONSIDERATIONS
|
| 343 |
+
═══════════════════════════════════════════════════════════════════════════���
|
| 344 |
+
|
| 345 |
+
✓ Built-in Security Features
|
| 346 |
+
• Resource limits prevent resource exhaustion
|
| 347 |
+
• Health checks enable automatic recovery
|
| 348 |
+
• Isolated container networking
|
| 349 |
+
• Environment variables for secrets (not hardcoded)
|
| 350 |
+
• Input validation and error handling
|
| 351 |
+
|
| 352 |
+
✓ Production Security Hardening
|
| 353 |
+
• Change SECRET_KEY in .env
|
| 354 |
+
• Set FLASK_ENV=production
|
| 355 |
+
• Disable FLASK_DEBUG
|
| 356 |
+
• Enable HTTPS/SSL (nginx.conf has template)
|
| 357 |
+
• Configure CORS policies
|
| 358 |
+
• Set up rate limiting
|
| 359 |
+
• Monitor logs for attacks
|
| 360 |
+
|
| 361 |
+
✓ Deployment Security
|
| 362 |
+
• Use private container registry (not public Docker Hub)
|
| 363 |
+
• Sign container images
|
| 364 |
+
• Scan images for vulnerabilities
|
| 365 |
+
• Use network policies
|
| 366 |
+
• Enable audit logging
|
| 367 |
+
• Regular security updates
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
════════════════════════════════════════════════════════════════════════════
|
| 371 |
+
|
| 372 |
+
📚 DOCUMENTATION READING GUIDE
|
| 373 |
+
════════════════════════════════════════════════════════════════════════════
|
| 374 |
+
|
| 375 |
+
If you have 5 minutes:
|
| 376 |
+
→ Read: DOCKER_README.md
|
| 377 |
+
→ Learn: Basic commands and quick start
|
| 378 |
+
|
| 379 |
+
If you have 15 minutes:
|
| 380 |
+
→ Read: DOCKER_SETUP_SUMMARY.md
|
| 381 |
+
→ Read: DEPLOYMENT_CHECKLIST.md
|
| 382 |
+
→ Learn: Deployment process and validation
|
| 383 |
+
|
| 384 |
+
If you have 30+ minutes:
|
| 385 |
+
→ Read: DOCKER_DEPLOYMENT_GUIDE.md
|
| 386 |
+
→ Learn: Architecture, advanced configs, troubleshooting
|
| 387 |
+
|
| 388 |
+
If you want technical deep dive:
|
| 389 |
+
→ Read: DETAILED_PROJECT_INTRODUCTION.md
|
| 390 |
+
→ Learn: ML model, preprocessing, real-world applications
|
| 391 |
+
|
| 392 |
+
For presentations:
|
| 393 |
+
→ Use: Handwritten_Digit_Recognition_Presentation.pptx
|
| 394 |
+
→ 16 professional slides with all key information
|
| 395 |
+
|
| 396 |
+
For reference:
|
| 397 |
+
→ Keep: DOCKER_FILES_MANIFEST.txt handy
|
| 398 |
+
→ Quick lookup of all files and their purposes
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
════════════════════════════════════════════════════════════════════════════
|
| 402 |
+
|
| 403 |
+
🆘 TROUBLESHOOTING QUICK LINKS
|
| 404 |
+
════════════════════════════════════════════════════════════════════════════
|
| 405 |
+
|
| 406 |
+
Issue │ See Documentation
|
| 407 |
+
───────────────────────────────┼────────────────────────────────
|
| 408 |
+
Docker won't install │ INSTALLATION_GUIDE.md
|
| 409 |
+
Container won't start │ DOCKER_DEPLOYMENT_GUIDE.md
|
| 410 |
+
Slow predictions │ DOCKER_DEPLOYMENT_GUIDE.md
|
| 411 |
+
Model not training │ ACCURACY_GUIDE.md
|
| 412 |
+
Out of memory errors │ DOCKER_DEPLOYMENT_GUIDE.md
|
| 413 |
+
Can't access application │ DOCKER_DEPLOYMENT_GUIDE.md
|
| 414 |
+
Low accuracy on real writing │ ACCURACY_GUIDE.md
|
| 415 |
+
Need to scale to multiple VMs │ DOCKER_DEPLOYMENT_GUIDE.md
|
| 416 |
+
Want SSL/HTTPS │ DOCKER_DEPLOYMENT_GUIDE.md
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
════════════════════════════════════════════════════════════════════════════
|
| 420 |
+
|
| 421 |
+
🎓 LEARNING PATH
|
| 422 |
+
════════════════════════════════════════════════════════════════════════════
|
| 423 |
+
|
| 424 |
+
Beginner Path (If you're new to Docker):
|
| 425 |
+
1. Read: DOCKER_README.md (understand basic concepts)
|
| 426 |
+
2. Run: ./quickstart.sh (get hands-on experience)
|
| 427 |
+
3. Try: All 3 deployment modes
|
| 428 |
+
4. Read: DOCKER_SETUP_SUMMARY.md (understand what happened)
|
| 429 |
+
|
| 430 |
+
Intermediate Path (If you're familiar with Docker):
|
| 431 |
+
1. Read: DOCKER_DEPLOYMENT_GUIDE.md (architecture section)
|
| 432 |
+
2. Deploy: Production mode with Nginx
|
| 433 |
+
3. Read: DEPLOYMENT_CHECKLIST.md (production validation)
|
| 434 |
+
4. Configure: Custom .env for your environment
|
| 435 |
+
|
| 436 |
+
Advanced Path (If you're an expert):
|
| 437 |
+
1. Read: DOCKER_DEPLOYMENT_GUIDE.md (full guide)
|
| 438 |
+
2. Implement: Multi-instance with load balancing
|
| 439 |
+
3. Setup: SSL/TLS with certificates
|
| 440 |
+
4. Deploy: To your cloud platform (AWS/GCP/Azure)
|
| 441 |
+
5. Configure: Kubernetes manifests (optional)
|
| 442 |
+
|
| 443 |
+
Data Scientist Path (If you focus on ML):
|
| 444 |
+
1. Read: DETAILED_PROJECT_INTRODUCTION.md (ML details)
|
| 445 |
+
2. Read: ACCURACY_GUIDE.md (improve accuracy)
|
| 446 |
+
3. Experiment: Drawing different styles
|
| 447 |
+
4. Optimize: Adjust model parameters
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
════════════════════════════════════════════════════════════════════════════
|
| 451 |
+
|
| 452 |
+
✨ PROJECT HIGHLIGHTS
|
| 453 |
+
════════════════════════════════════════════════════════════════════════════
|
| 454 |
+
|
| 455 |
+
What Makes This Project Stand Out:
|
| 456 |
+
|
| 457 |
+
✓ COMPLETE SOLUTION
|
| 458 |
+
• Code + Configuration + Documentation + Presentation
|
| 459 |
+
• Everything you need to deploy
|
| 460 |
+
|
| 461 |
+
✓ PRODUCTION-GRADE
|
| 462 |
+
• Health checks and auto-restart
|
| 463 |
+
• Resource limits and monitoring
|
| 464 |
+
• Scalable architecture
|
| 465 |
+
• Security best practices
|
| 466 |
+
|
| 467 |
+
✓ WELL-DOCUMENTED
|
| 468 |
+
• 9 comprehensive guides
|
| 469 |
+
• 20+ files organized logically
|
| 470 |
+
• Quick reference guides
|
| 471 |
+
• Step-by-step checklists
|
| 472 |
+
|
| 473 |
+
✓ BEGINNER-FRIENDLY
|
| 474 |
+
• Interactive quickstart script
|
| 475 |
+
• Multiple deployment options
|
| 476 |
+
• Clear error messages
|
| 477 |
+
• Troubleshooting guides
|
| 478 |
+
|
| 479 |
+
✓ EXPERT-FRIENDLY
|
| 480 |
+
• Advanced configurations
|
| 481 |
+
• Multi-instance setup
|
| 482 |
+
• Cloud deployment options
|
| 483 |
+
• Security hardening guides
|
| 484 |
+
|
| 485 |
+
✓ REAL-WORLD READY
|
| 486 |
+
• Actual accuracy on real handwriting
|
| 487 |
+
• Image preprocessing for robustness
|
| 488 |
+
• Fast inference (100-200ms)
|
| 489 |
+
• Scalable from 1 to 1000s of predictions/sec
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
════════════════════════════════════════════════════════════════════════════
|
| 493 |
+
|
| 494 |
+
🚀 DEPLOYMENT WORKFLOW
|
| 495 |
+
════════════════════════════════════════════════════════════════════════════
|
| 496 |
+
|
| 497 |
+
Local Development:
|
| 498 |
+
1. docker-compose -f docker-compose.dev.yml up
|
| 499 |
+
2. Edit code in app.py or templates/
|
| 500 |
+
3. Changes appear instantly
|
| 501 |
+
4. See full debug logs
|
| 502 |
+
|
| 503 |
+
Production Testing:
|
| 504 |
+
1. docker-compose -f docker-compose.prod.yml up -d
|
| 505 |
+
2. Verify with curl http://localhost:5000
|
| 506 |
+
3. Run acceptance tests
|
| 507 |
+
4. Check resource usage with docker stats
|
| 508 |
+
|
| 509 |
+
Production Deployment:
|
| 510 |
+
1. docker-compose -f docker-compose.prod.yml build
|
| 511 |
+
2. Tag and push to registry (Docker Hub/ECR)
|
| 512 |
+
3. Deploy to production server
|
| 513 |
+
4. Setup monitoring and alerts
|
| 514 |
+
5. Configure SSL/TLS if needed
|
| 515 |
+
|
| 516 |
+
Scaling:
|
| 517 |
+
1. docker-compose -f docker-compose.nginx.yml up -d --scale digit-app=5
|
| 518 |
+
2. Nginx automatically load-balances
|
| 519 |
+
3. Monitor with docker stats
|
| 520 |
+
4. Add/remove instances as needed
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
════════════════════════════════════════════════════════════════════════════
|
| 524 |
+
|
| 525 |
+
🎉 YOU'RE ALL SET!
|
| 526 |
+
════════════════════════════════════════════════════════════════════════════
|
| 527 |
+
|
| 528 |
+
Your Handwritten Digit Recognition application is now:
|
| 529 |
+
|
| 530 |
+
✅ FULLY CONTAINERIZED
|
| 531 |
+
Ready to deploy anywhere Docker is available
|
| 532 |
+
|
| 533 |
+
✅ PRODUCTION-READY
|
| 534 |
+
Optimized, secure, and reliable
|
| 535 |
+
|
| 536 |
+
✅ SCALABLE
|
| 537 |
+
From single instance to enterprise-scale
|
| 538 |
+
|
| 539 |
+
✅ WELL-DOCUMENTED
|
| 540 |
+
Comprehensive guides for every need
|
| 541 |
+
|
| 542 |
+
✅ EASY TO USE
|
| 543 |
+
One command or interactive script
|
| 544 |
+
|
| 545 |
+
✅ PROFESSIONALLY PRESENTED
|
| 546 |
+
16-slide PowerPoint deck included
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
════════════════════════════════════════════════════════════════════════════
|
| 550 |
+
|
| 551 |
+
📞 NEXT STEPS
|
| 552 |
+
════════════════════════════════════════════════════════════════════════════
|
| 553 |
+
|
| 554 |
+
IMMEDIATE (Next 5 minutes):
|
| 555 |
+
1. Verify Docker installed
|
| 556 |
+
2. Run ./quickstart.sh
|
| 557 |
+
3. Try all 3 deployment modes
|
| 558 |
+
4. Draw and test predictions
|
| 559 |
+
|
| 560 |
+
SHORT-TERM (Next hour):
|
| 561 |
+
1. Read DOCKER_README.md
|
| 562 |
+
2. Understand deployment process
|
| 563 |
+
3. Try production mode
|
| 564 |
+
4. Check resource usage
|
| 565 |
+
|
| 566 |
+
MEDIUM-TERM (Today):
|
| 567 |
+
1. Read DOCKER_SETUP_SUMMARY.md
|
| 568 |
+
2. Study DEPLOYMENT_CHECKLIST.md
|
| 569 |
+
3. Plan production deployment
|
| 570 |
+
4. Configure custom .env
|
| 571 |
+
|
| 572 |
+
LONG-TERM (This week):
|
| 573 |
+
1. Read DOCKER_DEPLOYMENT_GUIDE.md
|
| 574 |
+
2. Deploy to production
|
| 575 |
+
3. Setup monitoring
|
| 576 |
+
4. Plan scaling strategy
|
| 577 |
+
|
| 578 |
+
|
| 579 |
+
════════════════════════════════════════════════════════════════════════════
|
| 580 |
+
|
| 581 |
+
📞 SUPPORT RESOURCES
|
| 582 |
+
════════════════════════════════════════════════════════════════════════════
|
| 583 |
+
|
| 584 |
+
Official Documentation:
|
| 585 |
+
• Docker: https://docs.docker.com/
|
| 586 |
+
• Docker Compose: https://docs.docker.com/compose/
|
| 587 |
+
• Nginx: https://nginx.org/en/docs/
|
| 588 |
+
• TensorFlow: https://www.tensorflow.org/
|
| 589 |
+
• Flask: https://flask.palletsprojects.com/
|
| 590 |
+
|
| 591 |
+
Community:
|
| 592 |
+
• Docker Community: https://www.docker.com/community
|
| 593 |
+
• Stack Overflow: Tag "docker"
|
| 594 |
+
• GitHub Issues: Check similar projects
|
| 595 |
+
|
| 596 |
+
Your Documentation:
|
| 597 |
+
• All 9 guides are included in your project
|
| 598 |
+
• Comprehensive troubleshooting sections
|
| 599 |
+
• Step-by-step procedures
|
| 600 |
+
• Architecture explanations
|
| 601 |
+
|
| 602 |
+
|
| 603 |
+
════════════════════════════════════════════════════════════════════════════
|
| 604 |
+
|
| 605 |
+
📊 FINAL SUMMARY
|
| 606 |
+
════════════════════════════════════════════════════════════════════════════
|
| 607 |
+
|
| 608 |
+
Project Name: Handwritten Digit Recognition
|
| 609 |
+
Version: 2.0 (Docker-Ready)
|
| 610 |
+
Status: Production-Grade, Ready for Deployment
|
| 611 |
+
Files: 20+ files (226 KB)
|
| 612 |
+
Documentation: 9 comprehensive guides
|
| 613 |
+
Presentation: 16-slide PowerPoint deck
|
| 614 |
+
|
| 615 |
+
Technology Stack:
|
| 616 |
+
• Backend: Flask + TensorFlow + OpenCV
|
| 617 |
+
• Frontend: HTML5 + CSS3 + Vanilla JavaScript
|
| 618 |
+
• Container: Docker + Docker Compose + Nginx
|
| 619 |
+
• Python: 3.10 slim (lightweight)
|
| 620 |
+
|
| 621 |
+
Key Metrics:
|
| 622 |
+
• Model Accuracy: 99% (MNIST), 70-95% (Real)
|
| 623 |
+
• Prediction Speed: 100-200ms per digit
|
| 624 |
+
• Image Size: ~800MB
|
| 625 |
+
• Memory Usage: 500MB-2GB
|
| 626 |
+
• Scalability: Linear (1→1000s predictions/sec)
|
| 627 |
+
|
| 628 |
+
Deployment Options:
|
| 629 |
+
• Development: with live code editing
|
| 630 |
+
• Production: single instance, optimized
|
| 631 |
+
• Enterprise: Nginx with load balancing
|
| 632 |
+
• Cloud: AWS, Google Cloud, Azure, etc.
|
| 633 |
+
|
| 634 |
+
Documentation Quality:
|
| 635 |
+
• Beginner-friendly guides
|
| 636 |
+
• Expert-level configuration
|
| 637 |
+
• Step-by-step checklists
|
| 638 |
+
• Comprehensive troubleshooting
|
| 639 |
+
• Security best practices
|
| 640 |
+
|
| 641 |
+
Ready For:
|
| 642 |
+
• ✓ Local development and testing
|
| 643 |
+
• ✓ Production deployment
|
| 644 |
+
• ✓ Cloud deployment (AWS/GCP/Azure)
|
| 645 |
+
• ✓ Kubernetes orchestration
|
| 646 |
+
• ✓ Enterprise environments
|
| 647 |
+
• ✓ Academic presentations
|
| 648 |
+
• ✓ Business demos
|
| 649 |
+
|
| 650 |
+
|
| 651 |
+
════════════════════════════════════════════════════════════════════════════
|
| 652 |
+
|
| 653 |
+
🎊 CONGRATULATIONS! 🎊
|
| 654 |
+
|
| 655 |
+
Your Handwritten Digit Recognition application is fully containerized,
|
| 656 |
+
comprehensively documented, and production-ready!
|
| 657 |
+
|
| 658 |
+
Everything you need for successful deployment is included.
|
| 659 |
+
|
| 660 |
+
Start with: ./quickstart.sh
|
| 661 |
+
|
| 662 |
+
Happy deploying! 🚀
|
| 663 |
+
|
| 664 |
+
════════════════════════════════════════════════════════════════════════════
|
| 665 |
+
Generated: March 2026 | Version: 2.0 | Status: Production-Ready ✨
|
| 666 |
+
════════════════════════════════════════════════════════════════════════════
|
Mnist_analysis.ipynb
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 2,
|
| 6 |
+
"id": "07b58a38",
|
| 7 |
+
"metadata": {},
|
| 8 |
+
"outputs": [],
|
| 9 |
+
"source": [
|
| 10 |
+
"import numpy as np\n",
|
| 11 |
+
"import pandas as pd\n",
|
| 12 |
+
"import matplotlib.pyplot as plt\n",
|
| 13 |
+
"import seaborn as sns\n",
|
| 14 |
+
"from tensorflow.keras.datasets import mnist"
|
| 15 |
+
]
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"cell_type": "code",
|
| 19 |
+
"execution_count": 3,
|
| 20 |
+
"id": "360d7624",
|
| 21 |
+
"metadata": {},
|
| 22 |
+
"outputs": [
|
| 23 |
+
{
|
| 24 |
+
"name": "stdout",
|
| 25 |
+
"output_type": "stream",
|
| 26 |
+
"text": [
|
| 27 |
+
"Training Shape: (60000, 28, 28)\n",
|
| 28 |
+
"Testing Shape: (10000, 28, 28)\n"
|
| 29 |
+
]
|
| 30 |
+
}
|
| 31 |
+
],
|
| 32 |
+
"source": [
|
| 33 |
+
"(X_train, y_train), (X_test, y_test) = mnist.load_data()\n",
|
| 34 |
+
"print(\"Training Shape: \", X_train.shape)\n",
|
| 35 |
+
"print(\"Testing Shape: \", X_test.shape)"
|
| 36 |
+
]
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"cell_type": "code",
|
| 40 |
+
"execution_count": 6,
|
| 41 |
+
"id": "cb496dd6",
|
| 42 |
+
"metadata": {},
|
| 43 |
+
"outputs": [
|
| 44 |
+
{
|
| 45 |
+
"data": {
|
| 46 |
+
"image/png": "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wQ8AAEAJgh8AAIASBD8AAAAlCH4AAABKcJxLgNSoUcM6tnLlSp/dZ8aMGdax5ORkn90Huo0ZM8Y6Zju25fLly9aezz77zFi3vdG7iMjFixetYzaRkZHGeocOHaw91atXN9ZdLpe1x3Z00tq1az3MDvCNX3/91TqWlJTkv4n4yN133x3oKQQ1nvgBAAAoQfADAABQguAHAACgBMEPAABACYIfAACAEuzqDZCOHTtaxxo1auT19f75z38a69OmTfP6WoBNuXLljPWnn37a2uM4jrFu27krIpKYmOjNtDyqVauWdWzZsmXGepMmTby+z4oVK6xjb731ltfXA4LNc889Zx0rWbKkz+7TsGFDr3u+++4769imTZuuZzpBhyd+AAAAShD8AAAAlCD4AQAAKEHwAwAAUILgBwAAoATBDwAAQAmXYztr4b9f6OENyGFnO5Zi0aJF1h7btndP29F79eplrB87dszaA/tRI4FUlNdapUqVjHVPbwJvExsbax27dOmSsf7YY49Ze7p162asN2jQwNpTqlQpY93T14Vt7MEHH7T2fPjhh9YxLVhrRVOJEiWM9fr161t7xo4da6x37tzZ6/sXK2Z//uR2u72+nu3Povj4eGvPnj17vL5PUZbXWuOJHwAAgBIEPwAAACUIfgAAAEoQ/AAAAJQg+AEAAChRPNATCAU1atSwjq1cudJn9/nll1+sY+zehT9cvnzZWM/IyLD2REdHG+t79+619vhyB6inHcdnz5411m+66SZrz4kTJ4x1du4i0MLDw431O++809pj+x7laQ1cvHjRWPe01jZt2mSsd+zY0dpj23HsSfHi5ljjadf9tGnTjHXbn3fBjid+AAAAShD8AAAAlCD4AQAAKEHwAwAAUILgBwAAoATBDwAAQAmOc/GBF1980TpWkDeZtnnzzTd9di2gIDIzM431xMREa89HH31krFeoUMHaY3vT9LVr11p7Fi1aZKyfOnXK2rN8+XJj3dNRFrYewB9uuOEG65jtaJRVq1Z5fZ/XXnvNOrZhwwZj/dtvv7X22Na77VoiIg0aNLCO2diOj5owYYK158CBA8b6mjVrrD3Z2dlezaso4YkfAACAEgQ/AAAAJQh+AAAAShD8AAAAlCD4AQAAKMGuXi80btzYWO/QoYNP72PbufjTTz/59D6Ar6SkpFjHbLvs/KV169bWsTZt2hjr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",
|
| 47 |
+
"text/plain": [
|
| 48 |
+
"<Figure size 800x800 with 9 Axes>"
|
| 49 |
+
]
|
| 50 |
+
},
|
| 51 |
+
"metadata": {},
|
| 52 |
+
"output_type": "display_data"
|
| 53 |
+
}
|
| 54 |
+
],
|
| 55 |
+
"source": [
|
| 56 |
+
"plt.figure(figsize=(8,8))\n",
|
| 57 |
+
"for i in range(9):\n",
|
| 58 |
+
" plt.subplot(3,3,i+1)\n",
|
| 59 |
+
" plt.imshow(X_train[i], cmap='gray')\n",
|
| 60 |
+
" plt.title(f\"Label: {y_train[i]}\")\n",
|
| 61 |
+
" plt.axis('off')\n",
|
| 62 |
+
"plt.show()"
|
| 63 |
+
]
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"cell_type": "code",
|
| 67 |
+
"execution_count": 7,
|
| 68 |
+
"id": "2d7df7d7",
|
| 69 |
+
"metadata": {},
|
| 70 |
+
"outputs": [
|
| 71 |
+
{
|
| 72 |
+
"data": {
|
| 73 |
+
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAkQAAAGzCAYAAADOnwhmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAQaBJREFUeJzt3XlcFWX///H3AeWAIqAmWyKhdiuau6Uny5Uko+66s83bilzyzrBSSr1tUdPUskytXFrFLO/S7rTUEnHPwo2k26VMy9RvCrQIxxUQ5vdHD+bnERdAdJB5PR+PedS55jozn+ucQd7MuWaOwzAMQwAAADbmZXUBAAAAViMQAQAA2yMQAQAA2yMQAQAA2yMQAQAA2yMQAQAA2yMQAQAA2yMQAQAA2yMQAQAA2yMQwXZGjx4th8NxSfbVuXNnde7c2Xy8evVqORwOffLJJ5dk/w899JCuuuqqS7Kvsjpy5Ij69++v0NBQORwODR48uFy373A4NHr06DI996qrrtJDDz1UrvWc7vRj5HJwIcfVpfz5A0qDQITLWlJSkhwOh7n4+voqPDxcsbGxeu2113T48OFy2c+BAwc0evRopaenl8v2ylNFrq0kxo8fr6SkJA0cOFBz5szRAw88cNa+V111lflee3l5KSgoSM2aNdOAAQO0YcOGi17rjh07NHr0aP3yyy8XfV9lcerPwrmW1atXW12qZRYtWqROnTopODhY1apVU/369XXPPfdo6dKlZdre+PHjtXDhwvItEpZw8F1muJwlJSWpT58+GjNmjKKiopSfn6+MjAytXr1aKSkpqlevnj7//HM1b97cfM7Jkyd18uRJ+fr6lng/mzdv1rXXXqtZs2aV6oxBXl6eJMnHx0fSX2eIunTpovnz5+uuu+4q8XbKWlt+fr4KCwvldDrLZV8XQ/v27VWlShWtW7fuvH2vuuoq1axZU08++aQk6fDhw/r+++81f/58ZWRkaMiQIXr11Vc9nnPixAlVqVJFVapUKXVtubm58vLyUtWqVSVJn3zyie6++26tWrWq3M7qnH6MXIgPPvjA4/H777+vlJQUzZkzx6P9pptuUkhISJn3cyHHVVl+/srLK6+8oqFDh6pTp066/fbbVa1aNe3evVvLly9XixYtlJSUVOpt+vv766677irTc1GxlP5fCKAC6tGjh9q2bWs+HjFihFauXKlbb71Vf//73/X999/Lz89Pksr8y7E0jh07pmrVqpXLL7kLUfSLvCLLyspSkyZNStz/yiuv1P333+/R9tJLL+mf//ynJk+erKuvvloDBw40113IL95LESTL8xg5/XVZv369UlJSirWfruh4LakLOa4uxc/fmZw8eVJjx47VTTfdpGXLlhVbn5WVdclrQsXCR2aotLp27arnnntOe/fu9fjL+UxzGFJSUnTDDTcoKChI/v7+atSokZ5++mlJf53VufbaayVJffr0MT92KPqLsHPnzrrmmmuUlpamjh07qlq1auZzzzY/pKCgQE8//bRCQ0NVvXp1/f3vf9f+/fs9+pxt/sqp2zxfbWea63H06FE9+eSTioiIkNPpVKNGjfTKK6/o9JPFDodDgwYN0sKFC3XNNdfI6XSqadOmJf5oISsrS/369VNISIh8fX3VokULzZ4921xfNJ9qz549WrJkiVl7WT6O8vPz05w5c1SrVi2NGzfOYyxnmkO0evVqtW3bVr6+vmrQoIHefPPNMx4Xp74HSUlJuvvuuyVJXbp0Kfbx0+bNmxUbG6srrrhCfn5+ioqKUt++fc9b+9nmmc2bN0/jxo1T3bp15evrq27dumn37t2lfm3OtL+zHa+fffaZ4uLiFB4eLqfTqQYNGmjs2LEqKCjw2Mbpx9Uvv/wih8OhV155RW+99ZYaNGggp9Opa6+9Vps2bfJ47ple59IcayV97073+++/y+12q0OHDmdcHxwc7PE4NzdXo0aNUsOGDeV0OhUREaFhw4YpNzfXo+6jR49q9uzZ5vFwseec4eLhDBEqtQceeEBPP/20li1bpocffviMfbZv365bb71VzZs315gxY+R0OrV79259/fXXkqTo6GiNGTNGI0eO1IABA3TjjTdKkq6//npzG3/88Yd69Oih++67T/fff/95P44YN26cHA6Hhg8frqysLE2ZMkUxMTFKT083z2SVRElqO5VhGPr73/+uVatWqV+/fmrZsqWSk5M1dOhQ/frrr5o8ebJH/3Xr1unTTz/Vo48+qho1aui1115Tz549tW/fPtWuXfusdR0/flydO3fW7t27NWjQIEVFRWn+/Pl66KGHlJ2drSeeeELR0dGaM2eOhgwZorp165ofg9WpU6fE4z+Vv7+//vGPf+jdd9/Vjh071LRp0zP227Jli26++WaFhYXp+eefV0FBgcaMGXPe/Xbs2FGPP/64XnvtNT399NOKjo6W9Nd7kJWVpe7du6tOnTr697//raCgIP3yyy/69NNPyzQWSXrxxRfl5eWlp556Sjk5OZo4caJ69+5dLnOlzna8JiUlyd/fX4mJifL399fKlSs1cuRIud1uvfzyy+fd7ty5c3X48GH961//ksPh0MSJE3XnnXfq559/Pu9ZpZIca2V976S/Ao+fn58WLVqkxx57TLVq1Tpr38LCQv3973/XunXrNGDAAEVHR2vr1q2aPHmyfvzxR3PO0Jw5c9S/f39dd911GjBggCSpQYMG560FFZQBXMZmzZplSDI2bdp01j6BgYFGq1atzMejRo0yTj30J0+ebEgyfvvtt7NuY9OmTYYkY9asWcXWderUyZBkzJw584zrOnXqZD5etWqVIcm48sorDbfbbbbPmzfPkGRMnTrVbIuMjDTi4+PPu81z1RYfH29ERkaajxcuXGhIMl544QWPfnfddZfhcDiM3bt3m22SDB8fH4+27777zpBkvP7668X2daopU6YYkowPPvjAbMvLyzNcLpfh7+/vMfbIyEgjLi7unNsrad+i9/Kzzz7zGMeoUaPMx7fddptRrVo149dffzXbdu3aZVSpUsU4/Z/E09+D+fPnG5KMVatWefRbsGDBeY/DsznbMRIdHW3k5uaa7VOnTjUkGVu3bi3xthMSEoqN6VzH67Fjx4q1/etf/zKqVatmnDhxwmw7/bjas2ePIcmoXbu28eeff5rtn332mSHJWLRokdl2+s+fYZT8WCvNe3cmI0eONCQZ1atXN3r06GGMGzfOSEtLK9Zvzpw5hpeXl/HVV195tM+cOdOQZHz99ddmW/Xq1c/4c4rLDx+ZodLz9/c/59VmQUFBkv76uKCwsLBM+3A6nerTp0+J+z/44IOqUaOG+fiuu+5SWFiYvvjiizLtv6S++OILeXt76/HHH/dof/LJJ2UYhr788kuP9piYGI+/eJs3b66AgAD9/PPP591PaGioevXqZbZVrVpVjz/+uI4cOaI1a9aUw2iK8/f3l6Szvt8FBQVavny57rjjDoWHh5vtDRs2VI8ePcq836JjaPHixcrPzy/zdk7Vp08fj/lFRWf/zvfal8TZjtdTz04ePnxYv//+u2688UYdO3ZMP/zww3m3e++996pmzZplqvl8x1p5vHfPP/+85s6dq1atWik5OVnPPPOM2rRpo9atW+v77783+82fP1/R0dFq3Lixfv/9d3Pp2rWrJGnVqlUl2h8uLwQiVHpHjhzxCB+nu/fee9WhQwf1799fISEhuu+++zRv3rxShaMrr7yyVJNjr776ao/HDodDDRs2vOiXc+/du1fh4eHFXo+ij3/27t3r0V6vXr1i26hZs6YOHTp03v1cffXV8vLy/CfmbPspL0eOHJGks77fWVlZOn78uBo2bFhs3ZnaSqpTp07q2bOnnn/+eV1xxRW6/fbbNWvWLI/5JqV1+mtfFDTO99qXxNmO1+3bt+sf//iHAgMDFRAQoDp16pgTsnNyci5qzec71srrvevVq5e++uorHTp0SMuWLdM///lPbdmyRbfddptOnDghSdq1a5e2b9+uOnXqeCx/+9vfzFpQ+TCHCJXa//3f/yknJ+ec/2D6+flp7dq1WrVqlZYsWaKlS5fq448/VteuXbVs2TJ5e3ufdz+lmfdTUmebJFpQUFCimsrD2fZjVNC7dWzbtk3ShYWbsii62eb69eu1aNEiJScnq2/fvpo0aZLWr19vnrkqjYv52p/peM3OzlanTp0UEBCgMWPGqEGDBvL19dW3336r4cOHl+gPhAup+VIfawEBAbrpppt00003qWrVqpo9e7Y2bNigTp06qbCwUM2aNSt2C4ciERERF6UmWItAhEqt6P4rsbGx5+zn5eWlbt26qVu3bnr11Vc1fvx4PfPMM1q1apViYmLK/c66u3bt8nhsGIZ2797tcb+kmjVrKjs7u9hz9+7dq/r165uPS1NbZGSkli9frsOHD3ucRSn6OCQyMrLE2zrffv73v/+psLDQ4yxRee/nVEeOHNGCBQsUERFhnok6XXBwsHx9fc94tVZJruA632vdvn17tW/fXuPGjdPcuXPVu3dvffTRR+rfv3/JBmGh1atX648//tCnn36qjh07mu179uyxsKr/70Lfu3Np27atZs+erYMHD0r6a2L0d999p27dup33Peeu25UHH5mh0lq5cqXGjh2rqKgo9e7d+6z9/vzzz2JtLVu2lCTzI4/q1atL0hkDSlm8//77HvNcPvnkEx08eNBjLkSDBg20fv1688Z90l9zVE6/PL80td1yyy0qKCjQG2+84dE+efJkORyOC5pHc/p+MjIy9PHHH5ttJ0+e1Ouvvy5/f3916tSpXPZT5Pjx43rggQf0559/6plnnjnrLylvb2/FxMRo4cKFOnDggNm+e/fuYvOnzuRsr/WhQ4eKnck4/Riq6IrO0Jw6jry8PE2fPt2qkjxc6Ht37NgxpaamnnFd0fMbNWokSbrnnnv066+/6u233y7W9/jx4zp69Kj5uHr16uX27wKsxRkiVApffvmlfvjhB508eVKZmZlauXKlUlJSFBkZqc8///ycN+cbM2aM1q5dq7i4OEVGRiorK0vTp09X3bp1dcMNN0j6K5wEBQVp5syZqlGjhqpXr6527dopKiqqTPXWqlVLN9xwg/r06aPMzExNmTJFDRs29Lg1QP/+/fXJJ5/o5ptv1j333KOffvpJH3zwQbHLektT22233aYuXbromWee0S+//KIWLVpo2bJl+uyzzzR48OByu2R4wIABevPNN/XQQw8pLS1NV111lT755BN9/fXXmjJlyjnndJ3Pr7/+at5X6siRI9qxY4d5p+onn3xS//rXv875/NGjR2vZsmXq0KGDBg4caAbEa6655rxff9KyZUt5e3vrpZdeUk5OjpxOp7p27aq5c+dq+vTp+sc//qEGDRro8OHDevvttxUQEKBbbrmlzGO9lK6//nrVrFlT8fHxevzxx+VwODRnzpwK9fHohbx3x44d0/XXX6/27dvr5ptvVkREhLKzs7Vw4UJ99dVXuuOOO9SqVStJf92uY968eXrkkUe0atUqdejQQQUFBfrhhx80b948JScnmzeCbdOmjZYvX65XX31V4eHhioqKUrt27S72S4GLwbLr24ByUHTZfdHi4+NjhIaGGjfddJMxdepUj8u7i5x+2e+KFSuM22+/3QgPDzd8fHyM8PBwo1evXsaPP/7o8bzPPvvMaNKkiXmJb9Fl7p06dTKaNm16xvrOdkn1f/7zH2PEiBFGcHCw4efnZ8TFxRl79+4t9vxJkyYZV155peF0Oo0OHToYmzdvLrbNc9V2+uXRhmEYhw8fNoYMGWKEh4cbVatWNa6++mrj5ZdfNgoLCz36STISEhKK1XS22wGcLjMz0+jTp49xxRVXGD4+PkazZs3OeGuA0l52X/ReOxwOIyAgwGjatKnx8MMPGxs2bDjjc3TaZfeG8dd73qpVK8PHx8do0KCB8c477xhPPvmk4evre96xvv3220b9+vUNb29v8xL8b7/91ujVq5dRr149w+l0GsHBwcatt95qbN68+bxjOtsxMn/+fI9+RZe2n+k1PJuzXXZ/tuP166+/Ntq3b2/4+fkZ4eHhxrBhw4zk5ORitxo422X3L7/8crFtnv76n+2y+5IeayV9706Xn59vvP3228Ydd9xhREZGGk6n06hWrZrRqlUr4+WXX/a4xYFh/HWbiJdeeslo2rSp4XQ6jZo1axpt2rQxnn/+eSMnJ8fs98MPPxgdO3Y0/Pz8DElcgn8Z47vMAEDSHXfcoe3btxeb34WKj/cO5YE5RABs5/jx4x6Pd+3apS+++KLcvrAVFw/vHS4WzhABsJ2wsDA99NBDql+/vvbu3asZM2YoNzdXW7ZsKXaPKFQsvHe4WJhUDcB2br75Zv3nP/9RRkaGnE6nXC6Xxo8fzy/UywDvHS4WzhABAADbYw4RAACwPQIRAACwPeYQlUBhYaEOHDigGjVqcJt2AAAuE4Zh6PDhwwoPDy/2ZdNn6myZU2+ydury6KOPGoZhGMePHzceffRRo1atWkb16tWNO++808jIyPDYxt69e41bbrnF8PPzM+rUqWM89dRTRn5+vkefVatWedzIqzQ3NjMMw9i/f/8Z62RhYWFhYWGp+Mv+/fvP+7ve0jNEmzZtUkFBgfl427Ztuummm3T33XdLkoYMGaIlS5Zo/vz5CgwM1KBBg3TnnXfq66+/lvTXt37HxcUpNDRU33zzjQ4ePKgHH3xQVatW1fjx4yX99cWEcXFxeuSRR/Thhx9qxYoV6t+/v8LCws77hZ9Fir5mYP/+/QoICCjPlwAAAFwkbrdbERERJfq6oAp1ldngwYO1ePFi7dq1S263W3Xq1NHcuXN11113Sfrrm7Kjo6OVmpqq9u3b68svv9Stt96qAwcOKCQkRJI0c+ZMDR8+XL/99pt8fHw0fPhwLVmyRNu2bTP3c9999yk7O1tLly4tUV1ut1uBgYHKyckhEAEAcJkoze/vCjOpOi8vTx988IH69u0rh8OhtLQ05efnKyYmxuzTuHFj1atXz/zG4tTUVDVr1swMQ5IUGxsrt9ut7du3m31O3UZRn7N967H017dTu91ujwUAAFReFSYQLVy4UNnZ2XrooYckSRkZGfLx8VFQUJBHv5CQEGVkZJh9Tg1DReuL1p2rj9vtLnYL+CITJkxQYGCguURERFzo8AAAQAVWYQLRu+++qx49eig8PNzqUjRixAjl5OSYy/79+60uCQAAXEQV4rL7vXv3avny5fr000/NttDQUOXl5Sk7O9vjLFFmZqZCQ0PNPhs3bvTYVmZmprmu6L9Fbaf2CQgIkJ+f3xnrcTqdcjqdFzwuAABweagQZ4hmzZql4OBgxcXFmW1t2rRR1apVtWLFCrNt586d2rdvn1wulyTJ5XJp69atysrKMvukpKQoICBATZo0Mfucuo2iPkXbAAAAsDwQFRYWatasWYqPj1eVKv//hFVgYKD69eunxMRErVq1SmlpaerTp49cLpfat28vSerevbuaNGmiBx54QN99952Sk5P17LPPKiEhwTzD88gjj+jnn3/WsGHD9MMPP2j69OmaN2+ehgwZYsl4AQBAxWP5R2bLly/Xvn371Ldv32LrJk+eLC8vL/Xs2VO5ubmKjY3V9OnTzfXe3t5avHixBg4cKJfLperVqys+Pl5jxowx+0RFRWnJkiUaMmSIpk6dqrp16+qdd94p8T2IAABA5Veh7kNUUXEfIgAALj+X5X2IAAAArEIgAgAAtkcgAgAAtkcgAgAAtkcgAgAAtkcgAgAAtmf5fYhgvTZD37e6hHNKe/lBq0sAAFRynCECAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2RyACAAC2V8XqAgAAOJ82Q9+3uoSzSnv5QatLQDngDBEAALA9AhEAALA9AhEAALA9AhEAALA9AhEAALA9AhEAALA9LrsHKpCKfGmxxOXFACovzhABAADbIxABAADbszwQ/frrr7r//vtVu3Zt+fn5qVmzZtq8ebO53jAMjRw5UmFhYfLz81NMTIx27drlsY0///xTvXv3VkBAgIKCgtSvXz8dOXLEo8///vc/3XjjjfL19VVERIQmTpx4ScYHAAAqPksD0aFDh9ShQwdVrVpVX375pXbs2KFJkyapZs2aZp+JEyfqtdde08yZM7VhwwZVr15dsbGxOnHihNmnd+/e2r59u1JSUrR48WKtXbtWAwYMMNe73W51795dkZGRSktL08svv6zRo0frrbfeuqTjBQAAFZOlk6pfeuklRURE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3+gf//iHvL291atXLwUGBqpfv35KTEzUqlWrlJaWpj59+sjlcql9+/aSpO7du6tJkyZ64IEH9N133yk5OVnPPvusEhIS5HQ6JUmPPPKIfv75Zw0bNkw//PCDpk+frnnz5mnIkCFWDh0AAFQgls4h+r//+z/16tVLf/zxh+rUqaMbbrhB69evV506dSRJkydPlpeXl3r27Knc3FzFxsZq+vTp5vO9vb21ePFiDRw4UC6XS9WrV1d8fLzGjBlj9omKitKSJUs0ZMgQTZ06VXXr1tU777zDJfcAAMBkaSD66KOPzrne19dX06ZN07Rp087aJzIyUl988cU5t9O5c2dt2bKlTDUCAIDKr0LNIQIAALACgQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANhehQlEL774ohwOhwYPHmy2nThxQgkJCapdu7b8/f3Vs2dPZWZmejxv3759iouLU7Vq1RQcHKyhQ4fq5MmTHn1Wr16t1q1by+l0qmHDhkpKSroEIwIAAJeLChGINm3apDfffFPNmzf3aB8yZIgWLVqk+fPna82aNTpw4IDuvPNOc31BQYHi4uKUl5enb775RrNnz1ZSUpJGjhxp9tmzZ4/i4uLUpUsXpaena/Dgwerfv7+Sk5Mv2fgAAEDFZnkgOnLkiHr37q23335bNWvWNNtzcnL07rvv6tVXX1XXrl3Vpk0bzZo1S998843Wr18vSVq2bJl27NihDz74QC1btlSPHj00duxYTZs2TXl5eZKkmTNnKioqSpMmTVJ0dLQGDRqku+66S5MnTz5rTbm5uXK73R4LAACovCwPRAkJCYqLi1NMTIxHe1pamvLz8z3aGzdurHr16ik1NVWSlJqaqmbNmikkJMTsExsbK7fbre3bt5t9Tt92bGysuY0zmTBh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EREXPiAAAFBhVdhAZKURI0YoJyfHXPbv3291SQAA4CKqsIEoNDRUkpSZmenRnpmZaa4LDQ1VVlaWx/qTJ0/qzz//9Ohzpm2cuo/TOZ1OBQQEeCwAAKDyqrCBKCoqSqGhoVqxYoXZ5na7tWHDBrlcLkmSy+VSdna20tLSzD4rV65UYWGh2rVrZ/ZZu3at8vPzzT4pKSlq1KiRataseYlGAwAAKjJLA9GRI0eUnp6u9PR0SX9NpE5PT9e+ffvkcDg0ePBgvfDCC/r888+1detWPfjggwoPD9cdd9whSYqOjtbNN9+shx9+WBs3btTXX3+tQYMG6b777lN4eLgk6Z///Kd8fHzUr18/bd++XR9//LGmTp2qxMREi0YNAAAqGksvu9+8ebO6dOliPi4KKfHx8UpKStKwYcN09OhRDRgwQNnZ2brhhhu0dOlS+fr6ms/58MMPNWjQIHXr1k1eXl7q2bOnXnvtNXN9YGCgli1bpoSEBLVp00ZXXHGFRo4cySX3AADAZGkg6ty5swzDOOt6h8OhMWPGaMyYMWftU6tWLc2dO/ec+2nevLm++uqrMtcJAAAqtwo7hwgAAOBSIRABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbIxABAADbs1UgmjZtmq666ir5+vqqXbt22rhxo9UlAQCACsA2gejjjz9WYmKiRo0apW+//VYtWrRQbGyssrKyrC4NAABYzDaB6NVXX9XDDz+sPn36qEmTJpo5c6aqVaum9957z+rSAACAxapYXcClkJeXp7S0NI0YMcJs8/LyUkxMjFJTU4v1z83NVW5urvk4JydHkuR2uz36FeQev0gVl4/T6z0bxnHxVYYxSJVjHJVhDBLjqEgqwxikyjGO08dQ9NgwjPM/2bCBX3/91ZBkfPPNNx7tQ4cONa677rpi/UeNGmVIYmFhYWFhYakEy/79+8+bFWxxhqi0RowYocTERPNxYWGh/vzzT9WuXVsOh+Oi7NPtdisiIkL79+9XQEDARdnHpVAZxlEZxiAxjoqkMoxBqhzjqAxjkBhHSRmGocOHDys8PPy8fW0RiK644gp5e3srMzPToz0zM1OhoaHF+judTjmdTo+2oKCgi1miKSAg4LI+uItUhnFUhjFIjKMiqQxjkCrHOCrDGCTGURKBgYEl6meLSdU+Pj5q06aNVqxYYbYVFhZqxYoVcrlcFlYGAAAqAlucIZKkxMRExcfHq23btrruuus0ZcoUHT16VH369LG6NAAAYDHbBKJ7771Xv/32m0aOHKmMjAy1bNlSS5cuVUhIiNWlSfrrY7pRo0YV+6juclMZxlEZxiAxjoqkMoxBqhzjqAxjkBjHxeAwjJJciwYAAFB52WIOEQAAwLkQiAAAgO0RiAAAgO0RiAAAgO0RiAAAgO0RiCqIadOm6aqrrpKvr6/atWunjRs3Wl1Sqaxdu1a33XabwsPD5XA4tHDhQqtLKrUJEybo2muvVY0aNRQcHKw77rhDO3futLqsUpsxY4aaN29u3vnV5XLpyy+/tLqsC/Liiy/K4XBo8ODBVpdSKqNHj5bD4fBYGjdubHVZpfbrr7/q/vvvV+3ateXn56dmzZpp8+bNVpdVKldddVWx98LhcCghIcHq0kqloKBAzz33nKKiouTn56cGDRpo7NixJfvy0grk8OHDGjx4sCIjI+Xn56frr79emzZtsrQmAlEF8PHHHysxMVGjRo3St99+qxYtWig2NlZZWVlWl1ZiR48eVYsWLTRt2jSrSymzNWvWKCEhQevXr1dKSory8/PVvXt3HT161OrSSqVu3bp68cUXlZaWps2bN6tr1666/fbbtX37dqtLK5NNmzbpzTffVPPmza0upUyaNm2qgwcPmsu6deusLqlUDh06pA4dOqhq1ar68ssvtWPHDk2aNEk1a9a0urRS2bRpk8f7kJKSIkm6++67La6sdF566SXNmDFDb7zxhr7//nu99NJLmjhxol5//XWrSyuV/v37KyUlRXPmzNHWrVvVvXt3xcTE6Ndff7WuqHL5OnlckOuuu85ISEgwHxcUFBjh4eHGhAkTLKyq7CQZCxYssLqMC5aVlWVIMtasWWN1KResZs2axjvvvGN1GaV2+PBh4+qrrzZSUlKMTp06GU888YTVJZXKqFGjjBYtWlhdxgUZPny4ccMNN1hdRrl74oknjAYNGhiFhYVWl1IqcXFxRt++fT3a7rzzTqN3794WVVR6x44dM7y9vY3Fixd7tLdu3dp45plnLKrKMDhDZLG8vDylpaUpJibGbPPy8lJMTIxSU1MtrAw5OTmSpFq1allcSdkVFBToo48+0tGjRy/L7+1LSEhQXFycx8/H5WbXrl0KDw9X/fr11bt3b+3bt8/qkkrl888/V9u2bXX33XcrODhYrVq10ttvv211WRckLy9PH3zwgfr27SuHw2F1OaVy/fXXa8WKFfrxxx8lSd99953WrVunHj16WFxZyZ08eVIFBQXy9fX1aPfz87P0DKptvrqjovr9999VUFBQ7CtEQkJC9MMPP1hUFQoLCzV48GB16NBB11xzjdXllNrWrVvlcrl04sQJ+fv7a8GCBWrSpInVZZXKRx99pG+//dbyeQUXol27dkpKSlKjRo108OBBPf/887rxxhu1bds21ahRw+rySuTnn3/WjBkzlJiYqKefflqbNm3S448/Lh8fH8XHx1tdXpksXLhQ2dnZeuihh6wupdT+/e9/y+12q3HjxvL29lZBQYHGjRun3r17W11aidWoUUMul0tjx45VdHS0QkJC9J///Eepqalq2LChZXURiIAzSEhI0LZt2y67+R5FGjVqpPT0dOXk5OiTTz5RfHy81qxZc9mEov379+uJJ55QSkpKsb8iLyen/tXevHlztWvXTpGRkZo3b5769etnYWUlV1hYqLZt22r8+PGSpFatWmnbtm2aOXPmZRuI3n33XfXo0UPh4eFWl1Jq8+bN04cffqi5c+eqadOmSk9P1+DBgxUeHn5ZvR9z5sxR3759deWVV8rb21utW7dWr169lJaWZllNBCKLXXHFFfL29lZmZqZHe2ZmpkJDQy2qyt4GDRqkxYsXa+3atapbt67V5ZSJj4+P+ZdWmzZttGnTJk2dOlVvvvmmxZWVTFpamrKystS6dWuzraCgQGvXrtUbb7yh3NxceXt7W1hh2QQFBelvf/ubdu/ebXUpJRYWFlYsSEdHR+u///2vRRVdmL1792r58uX69NNPrS6lTIYOHap///vfuu+++yRJzZo10969ezVhwoTLKhA1aNBAa9as0dGjR+V2uxUWFqZ7771X9evXt6wm5hBZzMfHR23atNGKFSvMtsLCQq1YseKynPNxOTMMQ4MGDdKCBQu0cuVKRUVFWV1SuSksLFRubq7VZZRYt27dtHXrVqWnp5tL27Zt1bt3b6Wnp1+WYUiSjhw5op9++klhYWFWl1JiHTp0KHb7iR9//FGRkZEWVXRhZs2apeDgYMXFxVldSpkcO3ZMXl6ev7q9vb1VWFhoUUUXpnr16goLC9OhQ4eUnJys22+/3bJaOENUASQmJio+Pl5t27bVddddpylTpujo0aPq06eP1aWV2JEjRzz+6t2zZ4/S09NVq1Yt1atXz8LKSi4hIUFz587VZ599pho1aigjI0OSFBgYKD8/P4urK7kRI0aoR48eqlevng4fPqy5c+dq9erVSk5Otrq0EqtRo0axuVvVq1dX7dq1L6s5XU899ZRuu+02RUZG6sCBAxo1apS8vb3Vq1cvq0srsSFDhuj666/X+PHjdc8992jjxo1666239NZbb1ldWqkVFhZq1qxZio+PV5Uql+evv9tuu03jxo1TvXr11LRpU23ZskWvvvqq+vbta3VppZKcnCzDMNSoUSPt3r1bQ4cOVePGja39vWfZ9W3w8Prrrxv16tUzfHx8jOuuu85Yv3691SWVyqpVqwxJxZb4+HirSyuxM9UvyZg1a5bVpZVK3759jcjISMPHx8eoU6eO0a1bN2PZsmVWl3XBLsfL7u+9914jLCzM8PHxMa688krj3nvvNXbv3m11WaW2aNEi45prrjGcTqfRuHFj46233rK6pDJJTk42JBk7d+60upQyc7vdxhNPPGHUq1fP8PX1NerXr28888wzRm5urtWllcrHH39s1K9f3/Dx8TFCQ0ONhIQEIzs729KaHIZxmd3eEgAAoJwxhwgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANgegQgAANje/wOyHrauPHCFQQAAAABJRU5ErkJggg==",
|
| 74 |
+
"text/plain": [
|
| 75 |
+
"<Figure size 640x480 with 1 Axes>"
|
| 76 |
+
]
|
| 77 |
+
},
|
| 78 |
+
"metadata": {},
|
| 79 |
+
"output_type": "display_data"
|
| 80 |
+
}
|
| 81 |
+
],
|
| 82 |
+
"source": [
|
| 83 |
+
"sns.countplot(x=y_train)\n",
|
| 84 |
+
"plt.title(\"Distribution of Digits in Training Set\")\n",
|
| 85 |
+
"plt.show()"
|
| 86 |
+
]
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"cell_type": "code",
|
| 90 |
+
"execution_count": 8,
|
| 91 |
+
"id": "aee3504a",
|
| 92 |
+
"metadata": {},
|
| 93 |
+
"outputs": [
|
| 94 |
+
{
|
| 95 |
+
"data": {
|
| 96 |
+
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGzCAYAAAD9pBdvAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAM5JJREFUeJzt3Xl8VOW9x/HvRMiwzkSWrIQQZZE1QRAIVlGJhjTlktpWRNoABSwI94JYb0WtKLYNygW1LYKUCrZKg9gCV8QlhO0iEdnSsigVBYKQhD0DEYYlz/3DF6Nj1gkJDwmf9+t1Xi/nnOc553eejDNfzjxzxmGMMQIAALAkyHYBAADg2kYYAQAAVhFGAACAVYQRAABgFWEEAABYRRgBAABWEUYAAIBVhBEAAGAVYQQAAFhFGME1a/jw4WrTpk2N7X/BggVyOBzat29fjR3jarNv3z45HA4tWLDAdimlcjgcevrpp2v8OGvWrJHD4dCaNWt86+644w516dKlxo8tXf1/B+C7CCOocy6FgEtLgwYN1L59e40fP14FBQW2yyvh6aeflsPh0NGjRwPue+jQIT399NPKycmp/sKqyYoVK2okALRp08b3Nw4KClJISIi6du2qBx98UBs3bqy24yxcuFAvvvhite2vOl3NtQGBqGe7AKCmTJ06VbGxsTp79qzWr1+v2bNna8WKFdqxY4caNWqkP/3pTyouLrZd5mU5dOiQnnnmGbVp00bx8fG2y1FMTIzOnDmj+vXr+9atWLFCs2bNqpFAEh8fr0ceeUSSdOrUKX3yySdavHix/vSnP+nhhx/WzJkz/dqfOXNG9eoF9rK3cOFC7dixQxMnTqx0n9tvv11nzpxRcHBwQMcKVFm1lfZ3AK5mhBHUWcnJyerZs6ckadSoUWrevLlmzpypZcuWaciQIbxQ14BLV6KulKioKP30pz/1W/fcc8/pgQce0AsvvKB27dpp7Nixvm01XdvZs2cVHBysoKCgKzoO33Wl/w7A5eJjGlwz7rrrLknS3r17JZWcMzJlyhQFBQUpKyvLr9+DDz6o4OBg/fOf//St27hxowYMGCC3261GjRqpX79++vDDD6ut1kvzC3bt2qU777xTjRo1UlRUlJ5//nlfmzVr1uiWW26RJI0YMcL3kcW35wlUps5LHxPt2bNHw4cPV0hIiNxut0aMGKGvvvrKr21mZqa+973vKSQkRE2aNFGHDh30+OOP+7Z/d67C8OHDNWvWLEny++jMGKM2bdpo0KBBJc797Nmzcrvd+sUvflGlsWvYsKH++te/qlmzZvrtb3+rb/8w+XfnjJw6dUoTJ05UmzZt5HQ6FRoaqrvvvltbt26V9PXf4Z133tH+/ft9tV96zlyaF5KRkaEnn3xSUVFRatSokTweT6lzRi7ZsmWL+vbtq4YNGyo2NlZz5szx217WXKPv7rO82sqaM7Jq1Srddtttaty4sUJCQjRo0CB98sknfm0CeT4A1YUrI7hmfP7555Kk5s2bl7r9ySef1Ntvv62RI0dq+/btatq0qd5//3396U9/0rPPPqu4uDhJX7+gJycnq0ePHr4AM3/+fN111136v//7P/Xq1ata6j1x4oQGDBige++9V/fdd5/eeust/epXv1LXrl2VnJysjh07aurUqXrqqaf04IMP6rbbbpMk9e3bt0p13nfffYqNjVV6erq2bt2qefPmKTQ0VM8995wkaefOnfrBD36gbt26aerUqXI6ndqzZ0+5IewXv/iFDh06pMzMTP31r3/1rXc4HPrpT3+q559/XsePH1ezZs18295++215PJ4SVzwC0aRJE/3whz/Un//8Z+3atUudO3cutd2YMWP01ltvafz48erUqZOOHTum9evX65NPPtHNN9+sJ554QoWFhfryyy/1wgsv+Pb9bc8++6yCg4P1y1/+Ul6vt9yPZk6cOKHvf//7uu+++zRkyBC9+eabGjt2rIKDg/Xzn/88oHOsTG3ftnLlSiUnJ+uGG27Q008/rTNnzugPf/iDbr31Vm3durXEZO6Kng9AtTJAHTN//nwjyaxcudIcOXLEHDhwwGRkZJjmzZubhg0bmi+//NIYY8ywYcNMTEyMX9/t27eb4OBgM2rUKHPixAkTFRVlevbsac6fP2+MMaa4uNi0a9fOJCUlmeLiYl+/r776ysTGxpq77767RB179+4tt94pU6YYSebIkSO+df369TOSzF/+8hffOq/Xa8LDw82PfvQj37pNmzYZSWb+/Pl++wykzkvH//nPf+63jx/+8IemefPmvscvvPBCiTq/a+/evSXqGTdunCntpWb37t1Gkpk9e7bf+v/4j/8wbdq08au7NDExMSYlJaXM7ZfqXbZsmW+dJDNlyhTfY7fbbcaNG1fucVJSUko8T4wxZvXq1UaSueGGG8xXX31V6rbVq1f71l36m86YMcO3zuv1mvj4eBMaGmrOnTtnjCn7eVPaPsuqrbS/w6XjHDt2zLfun//8pwkKCjJpaWm+dZV9PgDViY9pUGclJiaqZcuWio6O1v33368mTZpoyZIlioqKKrNPly5d9Mwzz2jevHlKSkrS0aNH9dprr/kmPebk5Oizzz7TAw88oGPHjuno0aM6evSoioqK1L9/f61bt67aJsU2adLE7+pAcHCwevXqpS+++KLCvlWpc8yYMX6Pb7vtNh07dkwej0eSFBISIklatmxZtZxj+/bt1bt3b73xxhu+dcePH9e7776roUOHyuFwXNb+L10lOHXqVJltQkJCtHHjRh06dKjKxxk2bJgaNmxYqbb16tXz+/gpODhYv/jFL3T48GFt2bKlyjVUJC8vTzk5ORo+fLjfVahu3brp7rvv1ooVK0r0qej5AFSnWhVG1q1bp4EDByoyMlIOh0NLly4NqP+lz0K/uzRu3LhmCoZVs2bNUmZmplavXq1du3bpiy++UFJSUoX9Hn30UcXFxenjjz/WlClT1KlTJ9+2zz77TNLXb0AtW7b0W+bNmyev16vCwsJqqb9Vq1Yl3pCvv/56nThxosK+VamzdevWJY4lyXe8wYMH69Zbb9WoUaMUFham+++/X2+++eZlBZO0tDR9+OGH2r9/vyRp8eLFOn/+vH72s59VeZ+XnD59WpLUtGnTMts8//zz2rFjh6Kjo9WrVy89/fTTlQp73xYbG1vptpGRkSVeb9q3by9JNXo/mkvj26FDhxLbOnbs6Auq31bR8wGoTrVqzkhRUZHi4uL085//XPfee2/A/X/5y1+WSPv9+/f3TQJE3dKrVy/ft2kC8cUXX/jezLdv3+637dIb7/Tp08v8Km15n9sH4rrrrit1vfnWhMyyVKXOio7XsGFDrVu3TqtXr9Y777yj9957T4sWLdJdd92lDz74oMz+5bn//vv18MMP64033tDjjz+u119/XT179iz1TTNQO3bskCS1bdu2zDb33XefbrvtNi1ZskQffPCBpk+frueee07/+Mc/lJycXKnjVPaqSGWVdUXo4sWL1XqcilzO8w8IVK0KI8nJyeW+QHi9Xj3xxBP629/+ppMnT6pLly567rnndMcdd0j6+sX32y/A//znP7Vr164Ss9lx7SouLtbw4cPlcrk0ceJE/e53v9OPf/xjX/i98cYbJUkul0uJiYk2S5VU9htXTdUZFBSk/v37q3///po5c6Z+97vf6YknntDq1avLPE55H7c0a9ZMKSkpeuONNzR06FB9+OGH1XITr9OnT2vJkiWKjo5Wx44dy20bERGhhx56SA899JAOHz6sm2++Wb/97W99rzWX+3HRtx06dEhFRUV+V0f+/e9/S5JvAumlKxAnT57063vp6sa3Vba2mJgYSdLu3btLbPv000/VokULrhDDqlr1MU1Fxo8fr+zsbGVkZOhf//qXfvKTn2jAgAG+f+V+17x589S+fXvftxCAmTNnasOGDZo7d66effZZ9e3bV2PHjvXdHbVHjx668cYb9T//8z++jwG+7ciRI1e03ktvIN9946qJOo8fP15i3aWrLl6vN+AaL/nZz36mXbt26dFHH9V1112n+++/P+Davu3MmTP62c9+puPHj+uJJ54o90rDdz+qCg0NVWRkpN/5NG7cuNo+ertw4YJeeeUV3+Nz587plVdeUcuWLdWjRw9J3wTJdevW+dU6d+7cEvurbG0RERGKj4/Xa6+95vd32LFjhz744AN9//vfr+opAdWiVl0ZKU9ubq7mz5+v3NxcRUZGSvr6Y5n33ntP8+fP1+9+9zu/9mfPntUbb7yhxx57zEa5uAp98skn+vWvf63hw4dr4MCBkr6+50N8fLweeughvfnmmwoKCtK8efOUnJyszp07a8SIEYqKitLBgwe1evVquVwuvf3221es5htvvFEhISGaM2eOmjZtqsaNG6t3796KjY2t9jqnTp2qdevWKSUlRTExMTp8+LBefvlltWrVSt/73vfK7HfpTfa//uu/lJSUVCJwpKSkqHnz5lq8eLGSk5MVGhpa6ZoOHjyo119/XdLXV0N27dqlxYsXKz8/X4888ki59yo5deqUWrVqpR//+MeKi4tTkyZNtHLlSm3atEkzZszwq3/RokWaNGmSbrnlFjVp0sT3/AhUZGSknnvuOe3bt0/t27fXokWLlJOTo7lz5/puwte5c2f16dNHkydP9n3tOSMjQxcuXCixv0Bqmz59upKTk5WQkKCRI0f6vtrrdruvyO/1AOWy/G2eKpNklixZ4nu8fPlyI8k0btzYb6lXr5657777SvRfuHChqVevnsnPz7+CVeNKuPTVyE2bNpXb7ttf7b1w4YK5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lTL7TJ48WYWFhb7lwIEDNV0mAACwpF4gjVu0aKHrrrtOBQUFfusLCgoUHh5eqX3Ur19f3bt31549e8ps43Q65XQ6AykNAADUUgFdGQkODlaPHj2UlZXlW1dcXKysrCwlJCRUah8XL17U9u3bFREREVilAACgTgroyogkTZo0ScOGDVPPnj3Vq1cvvfjiiyoqKtKIESMkSWlpaYqKilJ6erokaerUqerTp4/atm2rkydPavr06dq/f79GjRpVvWcCAABqpYDDyODBg3XkyBE99dRTys/PV3x8vN577z3fpNbc3FwFBX1zweXEiRMaPXq08vPzdf3116tHjx7asGGDOnXqVH1nAQAAai2HMcbYLqIiHo9HbrdbhYWFcrlc1brvNo+9U2GbfdNSqvWYAABcCyr7/s1v0wAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwKuCbngEAgNqjNtxPiysjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwijACAACsqlIYmTVrltq0aaMGDRqod+/e+vjjj8ttv3jxYt10001q0KCBunbtqhUrVlSpWAAAUPcEHEYWLVqkSZMmacqUKdq6davi4uKUlJSkw4cPl9p+w4YNGjJkiEaOHKlt27YpNTVVqamp2rFjx2UXDwAAar+Aw8jMmTM1evRojRgxQp06ddKcOXPUqFEjvfrqq6W2f+mllzRgwAA9+uij6tixo5599lndfPPN+uMf/3jZxQMAgNqvXiCNz507py1btmjy5Mm+dUFBQUpMTFR2dnapfbKzszVp0iS/dUlJSVq6dGmZx/F6vfJ6vb7HhYWFkiSPxxNIuZVS7P2qwjatH15c7ceFfTueSaqW/XSZ8n61HOtK7qe61MYxrKsYH1yOmnh//fZ+jTHltgsojBw9elQXL15UWFiY3/qwsDB9+umnpfbJz88vtX1+fn6Zx0lPT9czzzxTYn10dHQg5QLlcr9Y+451JWuujNo4hnUV44PLUdPPn1OnTsntdpe5PaAwcqVM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",
|
| 97 |
+
"text/plain": [
|
| 98 |
+
"<Figure size 640x480 with 1 Axes>"
|
| 99 |
+
]
|
| 100 |
+
},
|
| 101 |
+
"metadata": {},
|
| 102 |
+
"output_type": "display_data"
|
| 103 |
+
}
|
| 104 |
+
],
|
| 105 |
+
"source": [
|
| 106 |
+
"plt.hist(X_train.flatten(), bins=50)\n",
|
| 107 |
+
"plt.title(\"Pixel Intensity Distribution\")\n",
|
| 108 |
+
"plt.show()"
|
| 109 |
+
]
|
| 110 |
+
},
|
| 111 |
+
{
|
| 112 |
+
"cell_type": "code",
|
| 113 |
+
"execution_count": 9,
|
| 114 |
+
"id": "c9cf1313",
|
| 115 |
+
"metadata": {},
|
| 116 |
+
"outputs": [
|
| 117 |
+
{
|
| 118 |
+
"data": {
|
| 119 |
+
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAHpCAYAAAB0jeQXAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVZdJREFUeJzt3XlwXeV9//GPvGizZFu2vMiWLcuWbcDEQEwoaQhbQ5gJE6CBTCmETEhTmmVCYNKQtpAfkKWBhBISAnSYtDAxyQSmNImbGVNcKJA0zbAEKNjgfbclL/ImeTf39wdjg/D5fJGO9Vj3Su/XTP7Ic/3VPffc5znnPMj+fsoKhUJBAAAAAAAgiUF9fQAAAAAAAPRnbLwBAAAAAEiIjTcAAAAAAAmx8QYAAAAAICE23gAAAAAAJMTGGwAAAACAhNh4AwAAAACQEBtvAAAAAAASYuMNAAAAAEBCbLwTu/XWW1VWVpar9qGHHlJZWZlWrVrVuwcF9BHWA3A01gXQFWsC6Io10T+w8e6BwxP38P8qKys1YcIEXXjhhfrRj36kXbt2JT+G++67Tw899FCPaubNm6f3v//9qqys1OTJk3XLLbfo4MGDaQ4QA0YprodHHnlEn/rUpzR9+nSVlZXp3HPPTXZsGJhKbV1s3bpV3//+93X22WdrzJgxGjlypM4880w98sgjaQ8SA0aprQlJuuGGG/T+979fo0aNUnV1tU488UTdeuut6ujoSHeQGDBKcU280/Lly1VZWamysjK98MILvXtg/VxZoVAo9PVBlIqHHnpI11xzjb75zW+qublZBw4cUGtrq55++mktWLBAkydP1rx58zR79uwjNQcPHtTBgwdVWVnZ4/c7dOiQDhw4oIqKiiP/levkk09WfX29nn766W79jPnz5+uiiy7Sueeeq7/8y7/Uq6++qnvvvVfXXnut7r///h4fE3BYKa6Hc889Vy+++KI+8IEP6OWXX9bs2bO7XQt0R6mti9/85jf6xCc+oY997GM677zzNGTIED322GP67//+b/2///f/dNttt/X4mIB3KrU1IUlnnXWW5syZo5aWFlVWVuqll17Sv/7rv+r000/Xs88+q0GD+L0V8ivFNfFOF198sZ566il1dnbq+eef1+mnn97jnzFgFdBtDz74YEFS4fnnnz/qtSeffLJQVVVVaGpqKuzevTvZMcyaNatwzjnndPvPn3TSSYVTTjmlcODAgSNjN910U6GsrKzw+uuvJzhCDBSluB7WrFlTOHToUK5aoDtKbV2sWLGisGrVqi5jb775ZuH8888vVFRUFDo6OhIcIQaSUlsTzp133lmQVPjf//3f3jkoDFilvCYef/zxQnl5eeHmm2+2nwEe/8mul5x//vn6xje+odWrV+vhhx8+Mp71bzL27Nmj6667TvX19aqtrdXFF1+s9evXq6ysTLfeeuuRP/fuf5MxZcoULVy4UM8888yRv54S/VXZRYsWadGiRbr22ms1ZMiQI+Nf/OIXVSgU9G//9m+98tmBdyvG9SBJkyZN4jcV6DPFuC6am5vV1NTUZaysrEyXXnqp9u3bpxUrVhzz5wacYlwTzpQpUyRJ27dv73Et0F3FvCYOHDigr3zlK/rKV76iadOm9cbHHXB4Au1FV199tSTpiSeeCP/cZz7zGd1zzz362Mc+pjvuuENVVVW66KKL3vPn33333WpsbNQJJ5yguXPnau7cubrpppvsn3/ppZck6ai/AjJhwgQ1NjYeeR1IodjWA1AMSmVdtLa2SpLq6+t7XAv0RLGuiYMHD2rLli3asGGDnnjiCd18882qra3VGWec0b0PBuRUrGvi7rvv1rZt23TzzTd374PgKEPe+4+guxobGzVixAgtX77c/pk//vGPevTRR3X99dfrBz/4gaS3fgN9zTXX6JVXXgl//qWXXqqbb75Z9fX1+tSnPvWex7Nx40ZJUkNDw1GvNTQ0aMOGDe/5M4C8im09AMWgFNZFe3u7fvKTn+jDH/5w5v0D6E3FuiZeeOEFffCDHzzy/2fOnKl58+Zp1KhR3f4ZQB7FuCZaW1v1rW99S3feeaeGDx/e/Q+DLviNdy+rqakJuxE+/vjjkt5aHO/05S9/udePZc+ePZKkioqKo16rrKw88jqQSjGtB6BYFPO6ePPNN3XVVVdp+/btuueee5K/HyAV55o46aSTtGDBAv3qV7/SjTfeqGHDhtHVHMdNsa2Jr3/965o6dao+97nPJfn5AwW/8e5lHR0dGjt2rH199erVGjRokJqbm7uMt7S09PqxVFVVSZL27dt31Gt79+498jqQSjGtB6BYFPO6+PKXv6zHH39cP/3pT3XKKackfz9AKs41MXz4cH3kIx+RJF1yySX6+c9/rksuuUR//OMfWRtIrpjWxB/+8AfNnTtXTz75JH1yjhFnrxetW7dOO3bsKJpNw+G/Inj4r5y/08aNGzVhwoTjfUgYQIptPQDFoJjXxW233ab77rtPt99++5F/YwikVsxr4p0+8YlPSJJ+8Ytf9PGRoL8rtjVx44036sMf/rCam5u1atUqrVq1Slu2bJH01n5izZo1fXyEpYONdy+aO3euJOnCCy+0f6apqUlvvvmmVq5c2WV82bJl3XqPd3c0jJx66qmSdFS4/YYNG7Ru3bojrwMpFNt6AIpBsa6Le++9V7feequuv/56ff3rX+9xPZBXsa6Jd9u3b5/efPNN7dix45h/FhAptjWxZs0aPfvss2pubj7yv6997WuS3sr0fmfeOGJsvHvJU089pW9961tqbm7WVVddZf/c4UV03333dRnv7r+lGzZsWLejLGbNmqUTTjhBDzzwgA4dOnRk/P7771dZWZkuv/zybv0coKeKcT0Afa1Y18Ujjzyi6667TldddZXuuuuubtcBx6oY18T27dt14MCBo8Z/8pOfSDo6KQboTcW4Jh544AH98pe/7PK/w/+W/M4779TPfvazbv0c8G+8c5k/f77eeOMNHTx4UG1tbXrqqae0YMECNTU1ad68eaqsrLS1c+bM0WWXXaa7775bW7du1ZlnnqlnnnlGS5YskfTe/wVqzpw5uv/++/Xtb39bLS0tGjt2rM4//3z757///e/r4osv1kc/+lFdccUVeu211/TjH/9Yn/vc53TiiSfmOwHAO5TSenj22Wf17LPPSpI2b96szs5Offvb35YknX322Tr77LN7+vGBTKWyLp577jl9+tOf1ujRo/Vnf/ZnRz1A/emf/qmmTp3aw08PHK1U1sTTTz+t6667TpdffrmmT5+u/fv367e//a3+/d//XaeffjopGug1pbImPvrRjx41dnjTfs455/Afo3qigG578MEHC5KO/K+8vLwwfvz4wgUXXFD44Q9/WNi5c+dRNbfcckvh3ae5s7Oz8KUvfakwatSoQk1NTeHSSy8tLF68uCCpcPvttx/1fitXrjwy1traWrjooosKtbW1BUmFc8455z2P+5e//GXh1FNPLVRUVBQaGxsLN998c2H//v25zwNQKJTmejj8/ln/u+WWW47ldACFQqH01sW7j/fd/3vwwQeP9ZRggCu1NbFs2bLCpz/96cLUqVMLVVVVhcrKysKsWbMKt9xyS6Gjo+OYzwdQamsi+gzPP/98j+oGurJCoVDo9d08euzll1/Waaedpocffjj8qyXAQMB6AI7GugC6Yk0AXbEmihv/xrsPZOVn33333Ro0aBB/1RUDDusBOBrrAuiKNQF0xZooPfwb7z7wve99Ty+++KLOO+88DRkyRPPnz9f8+fN17bXXatKkSX19eMBxxXoAjsa6ALpiTQBdsSZKD3/VvA8sWLBAt912mxYtWqSOjg5NnjxZV199tW666SYNGcJ/C8HAwnoAjsa6ALpiTQBdsSZKDxtvAAAAAAAS4t94AwAAAACQEBtvAAAAAAASYuMNAAAAAEBC3f6X92VlZSmPA+gTx9LigDWB/og1ARwt77pgTaA/4j4BHK0764LfeAMAAAAAkBAbbwAAAAAAEmLjDQAAAABAQmy8AQAAAABIiI03AAAAAAAJsfEGAAAAACAhNt4AAAAAACTExhsAAAAAgITYeAMAAAAAkBAbbwAAAAAAEmLjDQAAAABAQkP6+gAAACg1ZWVlPRrv7Zq8CoVCj8Yl6c033+xxDQAA6IrfeAMAAAAAkBAbbwAAAAAAEmLjDQAAAABAQmy8AQAAAABIiI03AAAAAAAJsfEGAAAAACAh4sR6wfGKj8kTKxPFveSJgskTRUPkDLojz/yOanpz3jGHS180VwYNyv5v0EOHDrU1VVVVPRqXpOrq6szxYcOG9fh9hgzxt+9ovu7bty9zvKOjw9bs3Lkzc3z37t22Zs+ePZnjBw8etDWHDh2yr6G45Vlfbjz6eXmenfI8n/R2vF6e9wH6Sp611NvRlz1VKmuJ33gDAAAAAJAQG28AAAAAABJi4w0AAAAAQEJsvAEAAAAASIiNNwAAAAAACdHVHADQb7hOyVEXcNc5fMSIEbZm7NixmeMTJkywNZMnT84cnzRpUo/fp6amxtZE3cFdh/I1a9bYmuXLl/e4Zv369Znj27ZtszWdnZ2Z41EndMSiTsODBw/OHM/TzT/qzO/W0ciRI22Nm9+VlZW2xn3WaP64bv7RPHVraNeuXbbGze39+/fbGreOS6V780BRDMlGeeZEbycR9GZNdA9zr+VJzHAJBSkN2I23m3DuRiT5m1F0I3A3nFGjRvW4JnoIdA+VLjpG8jePrVu32hp3M3I/S5L27t2bOR4tEm4spcGtozybnOHDh9sa9xBWXl5ua9wFNbrQuoegaB25SCUXpxS9T3SzYU0AAACULv6qOQAAAAAACbHxBgAAAAAgITbeAAAAAAAkxMYbAAAAAICE2HgDAAAAAJBQv+5qHrWwd12XXcdlyXcVb2hosDXNzc2Z4zNmzOhxzbhx42yN66zuojIkad26dZnjS5cutTWvv/565viqVatsTVtbW+Z4FL1BfEzxyBM5EcXR1NbWZo5HMUzjx4/PHI86oTuuy77ku/O3t7fbGtfpP+pCnifagq7mb4vmpLu2V1dX25rRo0dnjjc2NtqaqVOnZo5Pnz7d1rjXmpqabI2b+1GcWHR+8sSJvfbaa5njr7zyiq2JrgGOWxcuOUDqmziYYuSuxRUVFbbGRYBFMV9uPk6ZMsXWuGeaqMbdD6JEGJdKEyVMbNq0KXPcRehJ0pIlS3pc4+L1Nm/ebGvc8xuJMMfOXSOjfYObX1EakkteiZJf3M+Lrut5nimin+euG9F91O1DovPjjjuK2XPPcC6yL3otSqxJtZb4jTcAAAAAAAmx8QYAAAAAICE23gAAAAAAJMTGGwAAAACAhNh4AwAAAACQUL/uag4A6H/ydDWPuoCPGTMmczzqtD9p0qTM8YkTJ9qa+vr6zPEoTcN1fY2680cdxV23WncOJN99eseOHbbGvea6qku+i3P0WQdSV/No3rvuyXm6+bu5LUktLS2Z4zNnzrQ106ZNyxyPUgNc93KXiiH5cxBxcy5a++68uS7xkl+Tbn1L0oEDB3pcQ1fzt/V2IoubX9H1283XaF26Y4i+W3eNjBIhIm4uu7kv+TSEqKu5O+4o9Wj79u2Z49H5cd3Lo+7pqdZSv9h4uwUUtet3C8U9GEnS5MmTM8dPOOEEW3PyySf3uMa9TzTh3UKNYidcjEb04Ogi1aLIEvdg5G4qkm/9z02luOSJ5HBzNc8DYrQmnOiC7kQxFe4cRHOVeQwAADCw8FfNAQAAAABIiI03AAAAAAAJsfEGAAAAACAhNt4AAAAAACTExhsAAAAAgIRKpqt5nliAqNN2XV1d5rjrKC5Js2bNyhyfPXu2rXERG1Hn8OHDh2eOR+fAdQ6POruPHTs2czzquJwn2sZ1hHYxHtHPi7q04/hzcyVP1+4oCsZFcrjImegYoiiWrVu3Zo5HHfjdXI1q3Hql23n35IkTq6ystDUu5SK6h7jvKuqA39bWljm+bds2W+Pub1FES3TcLiYmT4yOu49KPnHA3d8k/z1EEWQDSZQWkWfeu7kQxWK5uRBdV938jq6R69atyxyP5qmbP9Gcc2slenZysUnumUrya9+NS1J7e3vmeHT9w9ui8+Sun3nSkKLroEtKcglBkp/jLhJL8mkt0TUjep52z1zRHHeRlNE5zRMNlicirZierfiNNwAAAAAACbHxBgAAAAAgITbeAAAAAAAkxMYbAAAAAICE2HgDAAAAAJBQyXQ1BwBAypdyEXV3dd2Vo+SFTZs2ZY5HnWfXr1+fOe663Eu+W3T0eaIuzg0NDZnjEyZMsDWuA7Dr8hu9FqUXuPehi/N7c+comlv79+/PHHedhiU/76LO81Fndccdd9TN33WKjlJkJk2alDkedXZ3HZKj7s155jbz/tjkuU/k6ZrvutxL0vjx4zPHo0QWN1fydPOO7mFRp2+3ZqPjdveW6H1cZ3XX0T+qie697j7aF93OS2bjHS0gt1BcO3zJP2RMnz7d1px00kk9rskT2eXiJaKYGvfgGF1E3MNZFEXj4taiG/XmzZszxzdu3GhrXPwIcWKlIZrf7rXogcrNVRdfIfmYryi6ydVED5XuZugeaiXixAAAAAYa/qo5AAAAAAAJsfEGAAAAACAhNt4AAAAAACTExhsAAAAAgITYeAMAAAAAkFDJdDWPolNc2/uo43FTU1Pm+MyZM23NtGnTMsdHjx5ta1wXbhdFI0nr1q3LHHfdziVp165dmeNRh3IXseE+pyTV1NRkjkdRNK4T+rJly2zN2rVrM8f37Nlja1A88nQ1j+aqW2MuqkOStmzZkjkeRU647uVufUl+TkYd+KOIHxwbd26j791FrrS2ttoaNyei6CQ396MO+C4GJYouitaFSw+oq6uzNS4hJErNiO7Z6H15InZ27NiROe5SUiSfChHNhTyxWG6eumcQSWpsbMwcj+a2u15Ex+zWZBT35K4x0TON+05Jvzh27vuNrqsuYi6aX+5aXF9fb2vc9xvtG/LcW6LnkDz7qnHjxmWOR3PcpR5F1y23zlwqjeSvaX2xlrgzAgAAAACQEBtvAAAAAAASYuMNAAAAAEBCbLwBAAAAAEiIjTcAAAAAAAmVTFdzAACkuBur6wQcdVZ1nU1dJ2LJd+GPuiG794m6SDvDhw+3r7ku5JI/d1EXcvda1BHWfQ/RZ6WLcyzP+c7TNTvqzu26jbtxSSovL88cr6qqsjUuyWLs2LG2pqWlJXN8xowZtsYlskTdoN11Ieo67bo3uySN6BhYD90TXYvdfHVzVfLXVdfNW/LpQSNHjrQ1bk5ESSnbt2/PHI8SWaL15z5rlJjhOp5HiUyue3l03O61qBN6MSXJlMzGO4qqGDFiROZ4FHHlIrOmTp1qa6I2+s7GjRszxxcvXmxrXMzWmjVrbE1nZ2fmeLSwos/quLi16CGwoaEhczyKU4iipVD88jwYRHPVzSE3Lknt7e2Z4y5CR5K2bt2aOR7dBNzDUTFd6AEAANC3+KvmAAAAAAAkxMYbAAAAAICE2HgDAAAAAJAQG28AAAAAABJi4w0AAAAAQEJF19Xctf+Puly72InJkyfbGtfRO+qS7DqrR63yFy5cmDn+f//3f7Zm+fLlmeNRVIXrrFxTU2Nr3LmOzoGLRoi6mruu83V1dbamsrLSvobil6eruZsnktTc3Jw5PmrUKFvjYnSi9eq6mu/du9fW0L38+MsTqxRFJLnrZxSRNGRIz2+fedaFuxZG19xhw4bZ19w9IbrH5olBc+c7+h5cHAzxSW+JzsOhQ4d6rSb6Xt1zUDTnXDTRlClTbM0JJ5yQOX7yySf3uCZ6pnHnZ+XKlbbGPYtt2LDB1mzZsiVzPIosdN8D66GrKDbMcdf2KF3FPbO65+LotSipadu2bZnjbg5Fr0VxgtEzl4tIa2xstDVuna9fv97WuOi0KH3GPY+565lUXGuG33gDAAAAAJAQG28AAAAAABJi4w0AAAAAQEJsvAEAAAAASIiNNwAAAAAACRVdV3MAAPJynU1d53LJd0KPOpe7mkGD/H/Pdp10o47irnv5+PHjbU30mksCKC8vtzWuu7LrSCtJu3btyhzv7S7OrqNxMXWx7S15PlOe1IWom7+bq64LsiTNmjUrc3zOnDm25rTTTsscnzlzpq1xxxCtSdehPOrs3tnZmTkedZB2HftLpRNzKYq6nbs5HiUBuetqlKA0YcKEzPGoa7e7rrrUFcnf36Iu7dGabWlpyRyPupq7buPRunD3g95Okimm+0TRbbzdBTJaDC4qwkUQSX7yRJEYboIsW7bM1rjYMBczJvlIiiiGxU0e93Ao+YXvogwkf8OJvh8Xm1BdXW1roodAFA8376KLmVvj7gYlSU1NTZnj0cORu0nlibaI3oeHIwAAALwX/qo5AAAAAAAJsfEGAAAAACAhNt4AAAAAACTExhsAAAAAgITYeAMAAAAAkFDRdTV3Lf5HjhxpayZOnJg5HrX4Hz16dOZ41KZ+3bp1meOLFi2yNa+//nrm+Nq1a22Ni2GJOpS7TtGVlZW2xsVYRPEW7hii8+a6PkcRHy7GJ4qHoLt0aXDJAdOnT7c1br1G62jNmjWZ45s3b7Y1LvKFuVVcou8jz3flrkVRnJiLVYriW1z6w4gRI2yNS+2YOnWqrYleGzNmTOZ49Fm3b9+eOd7e3m5rXKqAS8aQ/L0nuu677y7PPamU9eZniuLEamtrM8fdPJWkGTNmZI67mLGoxs1fyc+FKM7IxTC5NBbJR/LV1dXZGnffi2Kl3LHliVMaiKJnTHf9jr5Dl4aUZ68RzUl3vXNrT5ImTZqUOR4lNc2ePdu+9r73vS9zPIqqdM9c0d7Fie5H7rWoxl0fo/1OKvzGGwAAAACAhNh4AwAAAACQEBtvAAAAAAASYuMNAAAAAEBCbLwBAAAAAEio6LqaAwAQibpcu47MroutlK/buEvacF1sJd+Redy4cbbGddJ1aR7v9fOqq6szxzs6OmyNS9qIupq77uVRF1n3vUbdavN0pXVdoftjt/NorbjXoq7m7rsoLy+3Ne687ty509asWrUqc7ytrc3WOFFXZXds0Tlway9KE3BrJeryn6erOR3P35anq3l0/R47dmzmeNRp391Doq7mLS0tmePuPiX5OT58+HBbc+KJJ9rXTj755Mxxd/+QpNbW1szx6N7ruq5Hn3X37t2Z49G9wL3WF+kXRbfxdhf1aDG4i2DU9t5NnihqaOXKlZnjS5cutTUbNmzIHHcPMpK/2EaTIM8NNE8cjvt+ognvIprcuNQ/H4AGkuhB2a3L6KHF/bzowX/FihWZ49Ha46EFAAAAKfBXzQEAAAAASIiNNwAAAAAACbHxBgAAAAAgITbeAAAAAAAkxMYbAAAAAICEiq6rueu0XV9fb2tcl+QoCsZ1L87T1Xz9+vW2xsVluM7lku8QHkUjDB06NHM8ihJw53TUqFG2prKyMnM86lDuukhH8TXR+UHxcHMymneue3k071xUhouckaSNGzdmjh84cMDW0E2/NERpDS6VIZpf7h4SRXZNmDChR+PRz4vep6GhIXM8ur9F58ddd3fs2GFr3DU8ikJy91h3r5L8/SW697n1HK3l/phe4JJN8sSJRedn7969meNRzNfixYszx6M552KGos/j5ombV5J/DopScWprazPHJ0+ebGtc1FL0zOnWanQP649z+73kmfturxFFX7l0lTznvK6uzr7morxczFjEzVUpvu+46DS3/qV8kV3uO4qu+e7+Fn3fxYTfeAMAAAAAkBAbbwAAAAAAEmLjDQAAAABAQmy8AQAAAABIiI03AAAAAAAJsfEGAAAAACChoosTKy8vzxyPWu+PHDkyczyKkHDxV1G0g4uDcJFhko99iNrruzb6UcyBOwdRtE1TU1PmuIuvkfz3E8WCbNq0KXO8vb3d1kSRBTi+oogGt8bGjRtnayZNmpQ57qI6JGnr1q2Z40uXLrU1LoqFyLDSkeda6GLDmpubbY2LuJsyZYqtcdfPKIbIvRZdc921PVove/bssa+5a6uLgolei46hpqYmczyKdXNRY9Hn2b17d+Z4tM7d5yn2a0MUseNei6LlXE10Htz5ds9Hko+ji67f7tiic5Dn+dGt/ei8ueeqKEbTzfsoFtA9j0Yxfi56s9jn9rFwny3PNSA6txs2bMgcd9c6yT/nRrGKeb4rN/eje2V0DG6P5J7nJR+t7M6b5M+Pu85Ex+bmvpRvjqTCb7wBAAAAAEiIjTcAAAAAAAmx8QYAAAAAICE23gAAAAAAJMTGGwAAAACAhIquq7nrwFddXW1rXGflqPul64oXdTR0nVWjbrCuI3TUTdB1J3TdbSXfYffEE0+0NTNmzMgcr6+vtzWuG3vUDd51NGxra7M17lz3586cfS3PXHUdWRsbG22Nm19uTUq+k+a6detsjZurUZd29xrzrm+4zsJRF1nXOdx1L5akmTNnZo5PnjzZ1owZMyZzvLcTONw5iJIxXJqGJO3fvz9zPJrjVVVVmePRvcKtZ3d/k6Tt27dnjkepGW7NRueg2FMz3GeKOm2763TUeT5P53A3T1zncsknTETynAO3jqLv263J6FnQza3oXLt5H619951G52AgcnMyejZ332/0TOHeJ+ra7e5V0XXQzaPoWSxPmkd0jXRzbNWqVbbm1VdfzRxfvny5rdm4cWPm+LZt22yN++7cvU2K75fHG7/xBgAAAAAgITbeAAAAAAAkxMYbAAAAAICE2HgDAAAAAJAQG28AAAAAABJi4w0AAAAAQEJFFyfmYiyiqJM8UT+uVX7U4t/FAgwfPtzWuGOL4jrcz5s0aZKtOeGEEzLH3/e+99maKVOmZI5H8Rau9f/q1attzdq1azPHt2zZYmuiaCnkF0VpuTURRfm5OKGxY8faGhcZ2N7ebmvcHNq5c6etcZ81WnvR+ekpIsiOnYtVieLE3NybMGGCrWloaMgcdxEtkr9OR/eD6P7iuIiUKDolikI6ePBg5nh0bO48RHE0eb47F1sWxSe5e0UUQVaqorgqd+7yxFVFXCxPNB/dnIvintz7RHPBzeHoc7r56M5ndAxRZFGeeEv3PtxbuieaXy7+bv369bbGxR1Ga8yt2Wgeu2ckF98q+ef5aH5Fa9bNsZUrV9qapUuXZo5HcWsuNmz37t22xt138lxP+gK/8QYAAAAAICE23gAAAAAAJMTGGwAAAACAhNh4AwAAAACQEBtvAAAAAAASKrqu5q7zXNThbs+ePT36WZLvZBl1Dp85c2bmeNQ1sKOjI3M86iA7bty4zHHXtVCSpk+fnjk+ceJEW+OOoa2tzdYsX768R+OS70gddZ2NuuXiveXp6O06c0adNF1X86jGdZ5sbW21Na6bftT9Pk83Udd9N0KH2XTcdxV1kR02bFjmeDQnR48enTnursWSn/tR1+48Xc337t2bOe668kpxt39XF819d77r6upsTR7us7puwpJff9H9v9jXrLtO5+lqPnLkSFvj1kr0Pu767b676LWoxn1/0Rpya3Lq1Km2xr0WrX3XJT36PO45NbqH5eneXOxz+3iKrgHuvOfphB49V7nXok77tbW1mePRXsPtkVzX8PfiOp67ZzHJJ9NE+zf3Pnk6lJfK3Oc33gAAAAAAJMTGGwAAAACAhNh4AwAAAACQEBtvAAAAAAASYuMNAAAAAEBCbLwBAAAAAEio6OLEXIv/zZs325pNmzZljrsoL0maMGFC5visWbNszfDhwzPHXcxYdAxRJMaoUaMyx6N4C3dsUXv9DRs2ZI4vXrzY1ixatChzfOnSpbbGxUR1dnbamigGAu8tT4RFdXV15ngUR+Nei+a3+96j+AgXiRHVuM8aReW4SKU88S1R9EepxF4Uq+jc5okacXMimvsuuiia+24eRfFf7t7n7ntSHCGTJ0bHne8o9tGtpSg+ycXORPcK9/P6YyRlFFtUUVGROd7bcZDufaLz7b6/KH7LieL6GhsbM8dbWlpsjYsTc5FOko+327p1q61xr0VrNc+9Em+Lrvl57vV5YlrdvSVPBFmeOMq8z9nuuhrFALv1TPxdV/zGGwAAAACAhNh4AwAAAACQEBtvAAAAAAASYuMNAAAAAEBCbLwBAAAAAEio6Lqau66m69evtzWuo3ZDQ4OtcZ3DJ06caGsmTZqUOb5nzx5b416LuvwNHjw4czzq/tfe3p45vnr1aluzcOHCzPHXXnvN1rzxxhuZ4+vWrbM1rgui6yopDcxOhz0VdcV0cyjqium6mkddZCsrKzPHo26ZroNzlELgXovex3UTjbqaR92ye4o5fOzcdTKaK+5a2NbWZmvca3V1dbYmT7faXbt29fjYXPpEVBN1nnXX3TxddqNruDuG6LhdAsaWLVtsjZsLpXx/cccXXe/cWomuaS4NxT3rSNL48eMzx939IzqGPNdvd8yS79I+evToHr9PNOfWrl2bOb58+XJb457Fok7o7nmYrubHzq2x6NoQXSOd3nwWc4kCkj/u6F65f/9++5rrau7mpJRvXuY5p+56UipJMvzGGwAAAACAhNh4AwAAAACQEBtvAAAAAAASYuMNAAAAAEBCbLwBAAAAAEiIjTcAAAAAAAkVXZzY3r17M8ddpIokvfrqq5njUbt+J4q3aGxszBwfOXKkrRkxYkTmuPucko+XcBEWkrR48eLMcRcZJkmvv/565viqVatsjYt7iSILDhw4kDkenWu8tyg6wUU0RFFaLqoiqnHfYWdnp62JYn4cF8uX52fliZwopiiKgcRdO6K4LHedjGJL3PzauHGjrXFRY3nixKLook2bNmWOb9++3dZEEZduzUbH7daM+34kf49z50Dynyn6vt21ppQjl9z1prfPd3TfdtwzTXNzs60ZM2ZM5viwYcNsjYuqzHM/is6Bi/mKnp1eeeWVHte469K2bdtsjYt74n5UXKJrp5uvUTSY27tEzy4u/iuqieLE3GvR3qU3n8d6M9q12PAbbwAAAAAAEmLjDQAAAABAQmy8AQAAAABIiI03AAAAAAAJsfEGAAAAACChoutq7rritbe325pFixZlju/cudPWrF+/PnM86ko5derUzHHXsVOSBg8enDkedWl1x7ZixQpb416LOqG7brnReXOdE6MOsnTgTCM6r+61qJO8+26jzreuA//u3bttTdQB1HHzK7ouuOOOOnm685PnXOPYue896lLsOj9H3YOXL1+eOT58+HBb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c87p9p9vamoqXHTRRcmOBwNbKa6JL3zhC4WRI0cWVqxYkeyYMHCV4prI0tLSUpg+fXrvHBAGtFJbE+vWrStIKvzt3/5tl/GnnnqqIKlw1113JThCDCSltiYeffTRgqTCv/zLv3QZv+yyywqVlZWFtra2BEfYP/FXzXvJ+eefr2984xtavXq1Hn744SPjWf8mY8+ePbruuutUX1+v2tpaXXzxxVq/fr3Kysp06623Hvlz7/43GVOmTNHChQv1zDPPHPnrKeeee263jm///v3q7Ow81o8JdFsxront27frwQcf1LXXXqvm5mbt379f+/bt682PDVjFuCayPPfcc1q2bJmuuuqqvB8V6JZiXBOH/5rvuHHjuow3NDRIkqqqqo7hEwOxYlwTv/3tbyVJV1xxRZfxK664Qnv37tWvf/3rY/vQAwgb71509dVXS5KeeOKJ8M995jOf0T333KOPfexjuuOOO1RVVaWLLrroPX/+3XffrcbGRp1wwgmaO3eu5s6dq5tuuuk965566ilVV1erpqZGU6ZM0Q9/+MPufSDgGBXbmvjd736nvXv3qqWlRZdffrmqq6tVVVWlD33oQ3r55Zd79NmAPIptTWT52c9+JklsvHFcFNuamDZtmhobG/VP//RP+o//+A+tW7dOzz33nD7/+c+rubn5qM0H0NuKbU3s27dPgwcPVnl5eZfx6upqSdKLL774nu+Jtwzp6wPoTxobGzVixAgtX77c/pk//vGPevTRR3X99dfrBz/4gSTpi1/8oq655hq98sor4c+/9NJLdfPNN6u+vl6f+tSnunVMs2fP1llnnaWZM2dq69ateuihh3T99ddrw4YNuuOOO7r/4YAcim1NLF26VJL093//95o2bZp++tOfaseOHbrtttt0/vnna+HChUd+qwGkUGxr4t0OHTqkRx55RGeccYZaWlp6XA/0VLGtiaFDh+qxxx7TlVdeqYsvvvjI+Jw5c/T73/9eI0eO7N4HA3IqtjUxc+ZMHTp0SH/4wx901llnHRk//Jvw9evXd+djQfzGu9fV1NSE3Qgff/xxSW8tjnf68pe/nOR45s2bpxtvvFGXXHKJPvvZz+qZZ57RhRdeqLvuukvr1q1L8p7AOxXTmujo6JAklZWV6cknn9SVV16pL3zhC/rVr36lbdu26d577+319wTerZjWxLs9+eSTamtr47fd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",
|
| 120 |
+
"text/plain": [
|
| 121 |
+
"<Figure size 1000x600 with 10 Axes>"
|
| 122 |
+
]
|
| 123 |
+
},
|
| 124 |
+
"metadata": {},
|
| 125 |
+
"output_type": "display_data"
|
| 126 |
+
}
|
| 127 |
+
],
|
| 128 |
+
"source": [
|
| 129 |
+
"plt.figure(figsize=(10,6))\n",
|
| 130 |
+
"\n",
|
| 131 |
+
"for digit in range(10):\n",
|
| 132 |
+
" mean_image = np.mean(X_train[y_train == digit], axis=0)\n",
|
| 133 |
+
" plt.subplot(2,5,digit+1)\n",
|
| 134 |
+
" plt.imshow(mean_image, cmap='gray')\n",
|
| 135 |
+
" plt.title(f\"Digit {digit}\")\n",
|
| 136 |
+
" plt.axis('off')\n",
|
| 137 |
+
"\n",
|
| 138 |
+
"plt.tight_layout()\n",
|
| 139 |
+
"plt.show()"
|
| 140 |
+
]
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"cell_type": "code",
|
| 144 |
+
"execution_count": 10,
|
| 145 |
+
"id": "c6a476c9",
|
| 146 |
+
"metadata": {},
|
| 147 |
+
"outputs": [
|
| 148 |
+
{
|
| 149 |
+
"name": "stdout",
|
| 150 |
+
"output_type": "stream",
|
| 151 |
+
"text": [
|
| 152 |
+
"Min Pixel Value: 0\n",
|
| 153 |
+
"Max Pixel Value: 255\n",
|
| 154 |
+
"Mean Pixel Value: 33.318421449829934\n",
|
| 155 |
+
"Standard Deviation: 78.56748998339798\n"
|
| 156 |
+
]
|
| 157 |
+
}
|
| 158 |
+
],
|
| 159 |
+
"source": [
|
| 160 |
+
"print(f\"Min Pixel Value: {X_train.min()}\")\n",
|
| 161 |
+
"print(f\"Max Pixel Value: {X_train.max()}\")\n",
|
| 162 |
+
"print(f\"Mean Pixel Value: {X_train.mean()}\")\n",
|
| 163 |
+
"print(f\"Standard Deviation: {X_train.std()}\")"
|
| 164 |
+
]
|
| 165 |
+
},
|
| 166 |
+
{
|
| 167 |
+
"cell_type": "code",
|
| 168 |
+
"execution_count": 12,
|
| 169 |
+
"id": "16d8b0fd",
|
| 170 |
+
"metadata": {},
|
| 171 |
+
"outputs": [
|
| 172 |
+
{
|
| 173 |
+
"name": "stdout",
|
| 174 |
+
"output_type": "stream",
|
| 175 |
+
"text": [
|
| 176 |
+
"New Max Value: 1.0\n",
|
| 177 |
+
"New min Value: 0.0\n"
|
| 178 |
+
]
|
| 179 |
+
}
|
| 180 |
+
],
|
| 181 |
+
"source": [
|
| 182 |
+
"X_train_norm = X_train / 255.0\n",
|
| 183 |
+
"print(\"New Max Value: \", X_train_norm.max())\n",
|
| 184 |
+
"print(\"New min Value: \", X_train_norm.min())"
|
| 185 |
+
]
|
| 186 |
+
}
|
| 187 |
+
],
|
| 188 |
+
"metadata": {
|
| 189 |
+
"kernelspec": {
|
| 190 |
+
"display_name": "tf_env",
|
| 191 |
+
"language": "python",
|
| 192 |
+
"name": "python3"
|
| 193 |
+
},
|
| 194 |
+
"language_info": {
|
| 195 |
+
"codemirror_mode": {
|
| 196 |
+
"name": "ipython",
|
| 197 |
+
"version": 3
|
| 198 |
+
},
|
| 199 |
+
"file_extension": ".py",
|
| 200 |
+
"mimetype": "text/x-python",
|
| 201 |
+
"name": "python",
|
| 202 |
+
"nbconvert_exporter": "python",
|
| 203 |
+
"pygments_lexer": "ipython3",
|
| 204 |
+
"version": "3.10.2"
|
| 205 |
+
}
|
| 206 |
+
},
|
| 207 |
+
"nbformat": 4,
|
| 208 |
+
"nbformat_minor": 5
|
| 209 |
+
}
|
app.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import tensorflow as tf
|
| 3 |
+
from flask import Flask, render_template, request, jsonify
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import io
|
| 6 |
+
import base64
|
| 7 |
+
import os
|
| 8 |
+
|
| 9 |
+
app = Flask(__name__)
|
| 10 |
+
|
| 11 |
+
# Load the pre-trained MNIST model
|
| 12 |
+
print("Loading MNIST model...")
|
| 13 |
+
model_path = 'digit_model.h5'
|
| 14 |
+
if not os.path.exists(model_path):
|
| 15 |
+
print("Training improved model... this may take a few minutes")
|
| 16 |
+
|
| 17 |
+
# Load MNIST dataset
|
| 18 |
+
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
|
| 19 |
+
|
| 20 |
+
# Normalize data
|
| 21 |
+
x_train = x_train.astype('float32') / 255.0
|
| 22 |
+
x_test = x_test.astype('float32') / 255.0
|
| 23 |
+
|
| 24 |
+
# Reshape for CNN
|
| 25 |
+
x_train = x_train.reshape(-1, 28, 28, 1)
|
| 26 |
+
x_test = x_test.reshape(-1, 28, 28, 1)
|
| 27 |
+
|
| 28 |
+
# Data augmentation
|
| 29 |
+
from tensorflow.keras.preprocessing.image import ImageDataGenerator
|
| 30 |
+
|
| 31 |
+
datagen = ImageDataGenerator(
|
| 32 |
+
rotation_range=20,
|
| 33 |
+
width_shift_range=0.1,
|
| 34 |
+
height_shift_range=0.1,
|
| 35 |
+
shear_range=0.2,
|
| 36 |
+
zoom_range=0.1,
|
| 37 |
+
fill_mode='nearest'
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
model = tf.keras.Sequential([
|
| 42 |
+
# First convolutional block
|
| 43 |
+
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1), padding='same'),
|
| 44 |
+
tf.keras.layers.BatchNormalization(),
|
| 45 |
+
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', padding='same'),
|
| 46 |
+
tf.keras.layers.BatchNormalization(),
|
| 47 |
+
tf.keras.layers.MaxPooling2D((2, 2)),
|
| 48 |
+
tf.keras.layers.Dropout(0.25),
|
| 49 |
+
|
| 50 |
+
# Second convolutional block
|
| 51 |
+
tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
|
| 52 |
+
tf.keras.layers.BatchNormalization(),
|
| 53 |
+
tf.keras.layers.Conv2D(64, (3, 3), activation='relu', padding='same'),
|
| 54 |
+
tf.keras.layers.BatchNormalization(),
|
| 55 |
+
tf.keras.layers.MaxPooling2D((2, 2)),
|
| 56 |
+
tf.keras.layers.Dropout(0.25),
|
| 57 |
+
|
| 58 |
+
# Third convolutional block
|
| 59 |
+
tf.keras.layers.Conv2D(128, (3, 3), activation='relu', padding='same'),
|
| 60 |
+
tf.keras.layers.BatchNormalization(),
|
| 61 |
+
tf.keras.layers.MaxPooling2D((2, 2)),
|
| 62 |
+
tf.keras.layers.Dropout(0.25),
|
| 63 |
+
|
| 64 |
+
# Flatten and dense layers
|
| 65 |
+
tf.keras.layers.Flatten(),
|
| 66 |
+
tf.keras.layers.Dense(256, activation='relu'),
|
| 67 |
+
tf.keras.layers.BatchNormalization(),
|
| 68 |
+
tf.keras.layers.Dropout(0.5),
|
| 69 |
+
tf.keras.layers.Dense(128, activation='relu'),
|
| 70 |
+
tf.keras.layers.BatchNormalization(),
|
| 71 |
+
tf.keras.layers.Dropout(0.5),
|
| 72 |
+
tf.keras.layers.Dense(10, activation='softmax')
|
| 73 |
+
])
|
| 74 |
+
|
| 75 |
+
model.compile(
|
| 76 |
+
optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
|
| 77 |
+
loss='sparse_categorical_crossentropy',
|
| 78 |
+
metrics=['accuracy']
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
print("Training with data augmentation...")
|
| 82 |
+
model.fit(
|
| 83 |
+
datagen.flow(x_train, y_train, batch_size=64),
|
| 84 |
+
epochs=20,
|
| 85 |
+
validation_data=(x_test, y_test),
|
| 86 |
+
verbose=1
|
| 87 |
+
)
|
| 88 |
+
model.save(model_path)
|
| 89 |
+
print("Model trained and saved!")
|
| 90 |
+
else:
|
| 91 |
+
print("Loading saved model...")
|
| 92 |
+
model = tf.keras.models.load_model(model_path)
|
| 93 |
+
|
| 94 |
+
@app.route('/')
|
| 95 |
+
def index():
|
| 96 |
+
return render_template('index.html')
|
| 97 |
+
|
| 98 |
+
@app.route('/predict', methods=['POST'])
|
| 99 |
+
def predict():
|
| 100 |
+
try:
|
| 101 |
+
# Get image data from request
|
| 102 |
+
data = request.json
|
| 103 |
+
image_data = data.get('image')
|
| 104 |
+
|
| 105 |
+
if not image_data:
|
| 106 |
+
return jsonify({'error': 'No image provided'}), 400
|
| 107 |
+
|
| 108 |
+
# Remove data URI prefix
|
| 109 |
+
if 'base64,' in image_data:
|
| 110 |
+
image_data = image_data.split('base64,')[1]
|
| 111 |
+
|
| 112 |
+
# Decode image
|
| 113 |
+
image_bytes = base64.b64decode(image_data)
|
| 114 |
+
image = Image.open(io.BytesIO(image_bytes)).convert('L')
|
| 115 |
+
|
| 116 |
+
# Resize to 28x28
|
| 117 |
+
image = image.resize((28, 28), Image.Resampling.LANCZOS)
|
| 118 |
+
|
| 119 |
+
# Convert to numpy array and normalize
|
| 120 |
+
image_array = np.array(image) / 255.0
|
| 121 |
+
|
| 122 |
+
# Make prediction
|
| 123 |
+
prediction = model.predict(np.array([image_array]), verbose=0)
|
| 124 |
+
predicted_digit = int(np.argmax(prediction[0]))
|
| 125 |
+
confidence = float(np.max(prediction[0])) * 100
|
| 126 |
+
|
| 127 |
+
# Get all predictions for visualization
|
| 128 |
+
all_predictions = {str(i): float(prediction[0][i]) * 100 for i in range(10)}
|
| 129 |
+
|
| 130 |
+
return jsonify({
|
| 131 |
+
'digit': predicted_digit,
|
| 132 |
+
'confidence': round(confidence, 2),
|
| 133 |
+
'all_predictions': all_predictions
|
| 134 |
+
})
|
| 135 |
+
|
| 136 |
+
except Exception as e:
|
| 137 |
+
return jsonify({'error': str(e)}), 500
|
| 138 |
+
|
| 139 |
+
if __name__ == '__main__':
|
| 140 |
+
app.run(debug=True, port=5000)
|
digit_model.h5
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a4c73accfb964a10e31e8533ec7cc00d151bcc738be8b2aa1e1d19e23b22eb2f
|
| 3 |
+
size 5776080
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask==2.3.3
|
| 2 |
+
tensorflow==2.13.0
|
| 3 |
+
Pillow==9.5.0
|
| 4 |
+
numpy==1.23.5
|
| 5 |
+
Werkzeug==2.3.7
|
| 6 |
+
opencv-python==4.8.0.74
|
| 7 |
+
gunicorn
|
runtime.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
3.10.13
|
templates/index.html
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Digit Recognition</title>
|
| 7 |
+
<style>
|
| 8 |
+
* {
|
| 9 |
+
margin: 0;
|
| 10 |
+
padding: 0;
|
| 11 |
+
box-sizing: border-box;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
:root {
|
| 15 |
+
--primary: #1a1a2e;
|
| 16 |
+
--secondary: #0f3460;
|
| 17 |
+
--accent: #00d4ff;
|
| 18 |
+
--accent-warm: #ff6b6b;
|
| 19 |
+
--success: #00ff88;
|
| 20 |
+
--bg-dark: #0a0e27;
|
| 21 |
+
--surface: #16213e;
|
| 22 |
+
--text-primary: #e0e0e0;
|
| 23 |
+
--text-secondary: #a0a0a0;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
body {
|
| 27 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| 28 |
+
background: linear-gradient(135deg, var(--bg-dark) 0%, var(--primary) 50%, var(--secondary) 100%);
|
| 29 |
+
min-height: 100vh;
|
| 30 |
+
color: var(--text-primary);
|
| 31 |
+
overflow-x: hidden;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/* Animated background elements */
|
| 35 |
+
body::before {
|
| 36 |
+
content: '';
|
| 37 |
+
position: fixed;
|
| 38 |
+
top: 0;
|
| 39 |
+
left: 0;
|
| 40 |
+
right: 0;
|
| 41 |
+
bottom: 0;
|
| 42 |
+
background:
|
| 43 |
+
radial-gradient(circle at 20% 50%, rgba(0, 212, 255, 0.05) 0%, transparent 50%),
|
| 44 |
+
radial-gradient(circle at 80% 80%, rgba(255, 107, 107, 0.05) 0%, transparent 50%);
|
| 45 |
+
pointer-events: none;
|
| 46 |
+
z-index: -1;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
.container {
|
| 50 |
+
max-width: 1200px;
|
| 51 |
+
margin: 0 auto;
|
| 52 |
+
padding: 40px 20px;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
header {
|
| 56 |
+
text-align: center;
|
| 57 |
+
margin-bottom: 60px;
|
| 58 |
+
animation: slideDown 0.8s ease-out;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.logo {
|
| 62 |
+
font-size: 48px;
|
| 63 |
+
font-weight: 700;
|
| 64 |
+
margin-bottom: 10px;
|
| 65 |
+
background: linear-gradient(135deg, var(--accent) 0%, #00ffff 50%, var(--accent-warm) 100%);
|
| 66 |
+
background-clip: text;
|
| 67 |
+
-webkit-background-clip: text;
|
| 68 |
+
-webkit-text-fill-color: transparent;
|
| 69 |
+
letter-spacing: -1px;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.tagline {
|
| 73 |
+
color: var(--text-secondary);
|
| 74 |
+
font-size: 16px;
|
| 75 |
+
font-weight: 300;
|
| 76 |
+
letter-spacing: 2px;
|
| 77 |
+
text-transform: uppercase;
|
| 78 |
+
text-align: center;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.main-content {
|
| 82 |
+
display: grid;
|
| 83 |
+
grid-template-columns: 1fr 1fr;
|
| 84 |
+
gap: 40px;
|
| 85 |
+
margin-bottom: 40px;
|
| 86 |
+
animation: fadeIn 1s ease-out 0.2s both;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
@media (max-width: 968px) {
|
| 90 |
+
.main-content {
|
| 91 |
+
grid-template-columns: 1fr;
|
| 92 |
+
}
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
.canvas-section {
|
| 96 |
+
background: var(--surface);
|
| 97 |
+
border-radius: 16px;
|
| 98 |
+
padding: 30px;
|
| 99 |
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
| 100 |
+
border: 1px solid rgba(0, 212, 255, 0.1);
|
| 101 |
+
transition: all 0.3s ease;
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
.canvas-section:hover {
|
| 105 |
+
border-color: rgba(0, 212, 255, 0.3);
|
| 106 |
+
box-shadow: 0 20px 80px rgba(0, 212, 255, 0.1);
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
.section-title {
|
| 110 |
+
font-size: 20px;
|
| 111 |
+
font-weight: 600;
|
| 112 |
+
margin-bottom: 20px;
|
| 113 |
+
color: var(--accent);
|
| 114 |
+
text-transform: uppercase;
|
| 115 |
+
letter-spacing: 1px;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
canvas {
|
| 119 |
+
width: 100%;
|
| 120 |
+
height: 350px;
|
| 121 |
+
background: #000000;
|
| 122 |
+
border: 2px solid var(--accent);
|
| 123 |
+
border-radius: 12px;
|
| 124 |
+
cursor: crosshair;
|
| 125 |
+
display: block;
|
| 126 |
+
margin-bottom: 20px;
|
| 127 |
+
box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.5);
|
| 128 |
+
transition: all 0.3s ease;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
canvas:hover {
|
| 132 |
+
border-color: #00ffff;
|
| 133 |
+
box-shadow: inset 0 0 30px rgba(0, 212, 255, 0.1), 0 0 20px rgba(0, 212, 255, 0.1);
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
.button-group {
|
| 137 |
+
display: flex;
|
| 138 |
+
gap: 12px;
|
| 139 |
+
margin-bottom: 20px;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
button {
|
| 143 |
+
flex: 1;
|
| 144 |
+
padding: 14px 24px;
|
| 145 |
+
border: none;
|
| 146 |
+
border-radius: 8px;
|
| 147 |
+
font-size: 14px;
|
| 148 |
+
font-weight: 600;
|
| 149 |
+
cursor: pointer;
|
| 150 |
+
text-transform: uppercase;
|
| 151 |
+
letter-spacing: 1px;
|
| 152 |
+
transition: all 0.3s ease;
|
| 153 |
+
position: relative;
|
| 154 |
+
overflow: hidden;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
.btn-primary {
|
| 158 |
+
background: linear-gradient(135deg, var(--accent) 0%, #00ffff 100%);
|
| 159 |
+
color: var(--primary);
|
| 160 |
+
box-shadow: 0 8px 24px rgba(0, 212, 255, 0.3);
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
.btn-primary:hover {
|
| 164 |
+
transform: translateY(-2px);
|
| 165 |
+
box-shadow: 0 12px 32px rgba(0, 212, 255, 0.5);
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
.btn-primary:active {
|
| 169 |
+
transform: translateY(0);
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
.btn-secondary {
|
| 173 |
+
background: var(--surface);
|
| 174 |
+
color: var(--text-primary);
|
| 175 |
+
border: 1px solid var(--accent-warm);
|
| 176 |
+
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.2);
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.btn-secondary:hover {
|
| 180 |
+
background: rgba(255, 107, 107, 0.1);
|
| 181 |
+
transform: translateY(-2px);
|
| 182 |
+
box-shadow: 0 8px 20px rgba(255, 107, 107, 0.3);
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
.btn-secondary:active {
|
| 186 |
+
transform: translateY(0);
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
.result-section {
|
| 190 |
+
background: var(--surface);
|
| 191 |
+
border-radius: 16px;
|
| 192 |
+
padding: 30px;
|
| 193 |
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
| 194 |
+
border: 1px solid rgba(0, 212, 255, 0.1);
|
| 195 |
+
display: flex;
|
| 196 |
+
flex-direction: column;
|
| 197 |
+
justify-content: space-between;
|
| 198 |
+
min-height: 500px;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
.result-content {
|
| 202 |
+
text-align: center;
|
| 203 |
+
flex: 1;
|
| 204 |
+
display: flex;
|
| 205 |
+
flex-direction: column;
|
| 206 |
+
justify-content: center;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.result-empty {
|
| 210 |
+
color: var(--text-secondary);
|
| 211 |
+
font-size: 16px;
|
| 212 |
+
opacity: 0.6;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
.result-digit {
|
| 216 |
+
font-size: 120px;
|
| 217 |
+
font-weight: 700;
|
| 218 |
+
background: linear-gradient(135deg, var(--accent) 0%, var(--success) 100%);
|
| 219 |
+
background-clip: text;
|
| 220 |
+
-webkit-background-clip: text;
|
| 221 |
+
-webkit-text-fill-color: transparent;
|
| 222 |
+
margin: 20px 0;
|
| 223 |
+
animation: scaleIn 0.5s ease-out;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
.result-confidence {
|
| 227 |
+
font-size: 28px;
|
| 228 |
+
color: var(--accent);
|
| 229 |
+
font-weight: 600;
|
| 230 |
+
margin-bottom: 10px;
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
.confidence-label {
|
| 234 |
+
color: var(--text-secondary);
|
| 235 |
+
font-size: 14px;
|
| 236 |
+
text-transform: uppercase;
|
| 237 |
+
letter-spacing: 1px;
|
| 238 |
+
margin-bottom: 20px;
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
.confidence-bar {
|
| 242 |
+
width: 100%;
|
| 243 |
+
height: 8px;
|
| 244 |
+
background: rgba(255, 255, 255, 0.1);
|
| 245 |
+
border-radius: 4px;
|
| 246 |
+
overflow: hidden;
|
| 247 |
+
margin-bottom: 30px;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
.confidence-fill {
|
| 251 |
+
height: 100%;
|
| 252 |
+
background: linear-gradient(90deg, var(--accent) 0%, var(--success) 100%);
|
| 253 |
+
border-radius: 4px;
|
| 254 |
+
transition: width 0.6s ease-out;
|
| 255 |
+
animation: slideRight 0.6s ease-out;
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
.predictions-list {
|
| 259 |
+
background: rgba(0, 0, 0, 0.2);
|
| 260 |
+
border-radius: 12px;
|
| 261 |
+
padding: 20px;
|
| 262 |
+
margin-top: 20px;
|
| 263 |
+
max-height: 250px;
|
| 264 |
+
overflow-y: auto;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
.prediction-item {
|
| 268 |
+
display: flex;
|
| 269 |
+
justify-content: space-between;
|
| 270 |
+
align-items: center;
|
| 271 |
+
padding: 10px 0;
|
| 272 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
| 273 |
+
font-size: 14px;
|
| 274 |
+
animation: slideIn 0.4s ease-out;
|
| 275 |
+
animation-fill-mode: both;
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
.prediction-item:last-child {
|
| 279 |
+
border-bottom: none;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
.prediction-digit {
|
| 283 |
+
font-weight: 600;
|
| 284 |
+
color: var(--accent);
|
| 285 |
+
min-width: 30px;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
.prediction-bar {
|
| 289 |
+
flex: 1;
|
| 290 |
+
height: 6px;
|
| 291 |
+
background: rgba(255, 255, 255, 0.1);
|
| 292 |
+
border-radius: 3px;
|
| 293 |
+
margin: 0 15px;
|
| 294 |
+
overflow: hidden;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
.prediction-fill {
|
| 298 |
+
height: 100%;
|
| 299 |
+
background: linear-gradient(90deg, var(--accent) 0%, var(--success) 100%);
|
| 300 |
+
border-radius: 3px;
|
| 301 |
+
animation: slideRight 0.6s ease-out;
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
.prediction-percent {
|
| 305 |
+
color: var(--text-secondary);
|
| 306 |
+
min-width: 45px;
|
| 307 |
+
text-align: right;
|
| 308 |
+
font-size: 13px;
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
.loading {
|
| 312 |
+
text-align: center;
|
| 313 |
+
color: var(--accent);
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
.spinner {
|
| 317 |
+
display: inline-block;
|
| 318 |
+
width: 40px;
|
| 319 |
+
height: 40px;
|
| 320 |
+
border: 3px solid rgba(0, 212, 255, 0.2);
|
| 321 |
+
border-top-color: var(--accent);
|
| 322 |
+
border-radius: 50%;
|
| 323 |
+
animation: spin 0.8s linear infinite;
|
| 324 |
+
margin-bottom: 15px;
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
@keyframes spin {
|
| 328 |
+
to { transform: rotate(360deg); }
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
@keyframes slideDown {
|
| 332 |
+
from {
|
| 333 |
+
opacity: 0;
|
| 334 |
+
transform: translateY(-30px);
|
| 335 |
+
}
|
| 336 |
+
to {
|
| 337 |
+
opacity: 1;
|
| 338 |
+
transform: translateY(0);
|
| 339 |
+
}
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
@keyframes fadeIn {
|
| 343 |
+
from { opacity: 0; }
|
| 344 |
+
to { opacity: 1; }
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
@keyframes scaleIn {
|
| 348 |
+
from {
|
| 349 |
+
opacity: 0;
|
| 350 |
+
transform: scale(0.8);
|
| 351 |
+
}
|
| 352 |
+
to {
|
| 353 |
+
opacity: 1;
|
| 354 |
+
transform: scale(1);
|
| 355 |
+
}
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
@keyframes slideRight {
|
| 359 |
+
from { width: 0; }
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
@keyframes slideIn {
|
| 363 |
+
from {
|
| 364 |
+
opacity: 0;
|
| 365 |
+
transform: translateX(-10px);
|
| 366 |
+
}
|
| 367 |
+
to {
|
| 368 |
+
opacity: 1;
|
| 369 |
+
transform: translateX(0);
|
| 370 |
+
}
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
.info-section {
|
| 374 |
+
text-align: center;
|
| 375 |
+
color: var(--text-secondary);
|
| 376 |
+
font-size: 14px;
|
| 377 |
+
padding: 20px;
|
| 378 |
+
background: rgba(0, 212, 255, 0.05);
|
| 379 |
+
border-radius: 12px;
|
| 380 |
+
border: 1px solid rgba(0, 212, 255, 0.1);
|
| 381 |
+
animation: slideUp 0.8s ease-out 0.4s both;
|
| 382 |
+
}
|
| 383 |
+
|
| 384 |
+
@keyframes slideUp {
|
| 385 |
+
from {
|
| 386 |
+
opacity: 0;
|
| 387 |
+
transform: translateY(20px);
|
| 388 |
+
}
|
| 389 |
+
to {
|
| 390 |
+
opacity: 1;
|
| 391 |
+
transform: translateY(0);
|
| 392 |
+
}
|
| 393 |
+
}
|
| 394 |
+
|
| 395 |
+
/* Scrollbar styling */
|
| 396 |
+
.predictions-list::-webkit-scrollbar {
|
| 397 |
+
width: 6px;
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
.predictions-list::-webkit-scrollbar-track {
|
| 401 |
+
background: rgba(255, 255, 255, 0.05);
|
| 402 |
+
border-radius: 3px;
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
.predictions-list::-webkit-scrollbar-thumb {
|
| 406 |
+
background: var(--accent);
|
| 407 |
+
border-radius: 3px;
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
.predictions-list::-webkit-scrollbar-thumb:hover {
|
| 411 |
+
background: #00ffff;
|
| 412 |
+
}
|
| 413 |
+
</style>
|
| 414 |
+
</head>
|
| 415 |
+
<body>
|
| 416 |
+
<div class="container">
|
| 417 |
+
<header>
|
| 418 |
+
<div class="logo">✦ DIGIT READER</div>
|
| 419 |
+
<div class="tagline">Handwritten Digit Recognition By Nausad</div>
|
| 420 |
+
</header>
|
| 421 |
+
|
| 422 |
+
<div class="main-content">
|
| 423 |
+
<!-- Canvas Section -->
|
| 424 |
+
<div class="canvas-section">
|
| 425 |
+
<div class="section-title">Draw Here</div>
|
| 426 |
+
<canvas id="drawingCanvas"></canvas>
|
| 427 |
+
<div class="button-group">
|
| 428 |
+
<button class="btn-primary" id="predictBtn">Predict</button>
|
| 429 |
+
<button class="btn-secondary" id="clearBtn">Clear</button>
|
| 430 |
+
</div>
|
| 431 |
+
<div class="info-section">
|
| 432 |
+
✨ Draw a digit (0-9) using your mouse. Click "Predict" to identify it.
|
| 433 |
+
</div>
|
| 434 |
+
</div>
|
| 435 |
+
|
| 436 |
+
<!-- Result Section -->
|
| 437 |
+
<div class="result-section">
|
| 438 |
+
<div class="section-title">Result</div>
|
| 439 |
+
<div class="result-content" id="resultContent">
|
| 440 |
+
<div class="result-empty">
|
| 441 |
+
<div style="font-size: 48px; margin-bottom: 10px; opacity: 0.3;">✦</div>
|
| 442 |
+
<div>Draw a digit and click predict to see the result</div>
|
| 443 |
+
</div>
|
| 444 |
+
</div>
|
| 445 |
+
</div>
|
| 446 |
+
</div>
|
| 447 |
+
</div>
|
| 448 |
+
|
| 449 |
+
<script>
|
| 450 |
+
const canvas = document.getElementById('drawingCanvas');
|
| 451 |
+
const ctx = canvas.getContext('2d');
|
| 452 |
+
const predictBtn = document.getElementById('predictBtn');
|
| 453 |
+
const clearBtn = document.getElementById('clearBtn');
|
| 454 |
+
const resultContent = document.getElementById('resultContent');
|
| 455 |
+
|
| 456 |
+
let isDrawing = false;
|
| 457 |
+
|
| 458 |
+
// Set canvas size
|
| 459 |
+
function resizeCanvas() {
|
| 460 |
+
const rect = canvas.parentElement.getBoundingClientRect();
|
| 461 |
+
canvas.width = rect.width - 60; // Account for padding
|
| 462 |
+
canvas.height = 350;
|
| 463 |
+
ctx.lineCap = 'round';
|
| 464 |
+
ctx.lineJoin = 'round';
|
| 465 |
+
ctx.lineWidth = 12; // Increased from 8 for better strokes
|
| 466 |
+
ctx.strokeStyle = '#FFFFFF'; // White on black for MNIST compatibility
|
| 467 |
+
ctx.fillStyle = '#000000';
|
| 468 |
+
ctx.fillRect(0, 0, canvas.width, canvas.height); // Black background
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
resizeCanvas();
|
| 472 |
+
window.addEventListener('resize', resizeCanvas);
|
| 473 |
+
|
| 474 |
+
// Drawing functions
|
| 475 |
+
canvas.addEventListener('mousedown', (e) => {
|
| 476 |
+
isDrawing = true;
|
| 477 |
+
const rect = canvas.getBoundingClientRect();
|
| 478 |
+
const x = e.clientX - rect.left;
|
| 479 |
+
const y = e.clientY - rect.top;
|
| 480 |
+
ctx.beginPath();
|
| 481 |
+
ctx.moveTo(x, y);
|
| 482 |
+
});
|
| 483 |
+
|
| 484 |
+
canvas.addEventListener('mousemove', (e) => {
|
| 485 |
+
if (!isDrawing) return;
|
| 486 |
+
const rect = canvas.getBoundingClientRect();
|
| 487 |
+
const x = e.clientX - rect.left;
|
| 488 |
+
const y = e.clientY - rect.top;
|
| 489 |
+
ctx.lineTo(x, y);
|
| 490 |
+
ctx.stroke();
|
| 491 |
+
});
|
| 492 |
+
|
| 493 |
+
canvas.addEventListener('mouseup', () => {
|
| 494 |
+
isDrawing = false;
|
| 495 |
+
});
|
| 496 |
+
|
| 497 |
+
canvas.addEventListener('mouseleave', () => {
|
| 498 |
+
isDrawing = false;
|
| 499 |
+
});
|
| 500 |
+
|
| 501 |
+
// Clear canvas
|
| 502 |
+
clearBtn.addEventListener('click', () => {
|
| 503 |
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 504 |
+
resultContent.innerHTML = `
|
| 505 |
+
<div class="result-empty">
|
| 506 |
+
<div style="font-size: 48px; margin-bottom: 10px; opacity: 0.3;">✦</div>
|
| 507 |
+
<div>Draw a digit and click predict to see the result</div>
|
| 508 |
+
</div>
|
| 509 |
+
`;
|
| 510 |
+
});
|
| 511 |
+
|
| 512 |
+
// Predict
|
| 513 |
+
predictBtn.addEventListener('click', async () => {
|
| 514 |
+
const imageData = canvas.toDataURL('image/png');
|
| 515 |
+
|
| 516 |
+
resultContent.innerHTML = `
|
| 517 |
+
<div class="loading">
|
| 518 |
+
<div class="spinner"></div>
|
| 519 |
+
<div>Analyzing...</div>
|
| 520 |
+
</div>
|
| 521 |
+
`;
|
| 522 |
+
|
| 523 |
+
try {
|
| 524 |
+
const response = await fetch('/predict', {
|
| 525 |
+
method: 'POST',
|
| 526 |
+
headers: {
|
| 527 |
+
'Content-Type': 'application/json',
|
| 528 |
+
},
|
| 529 |
+
body: JSON.stringify({ image: imageData })
|
| 530 |
+
});
|
| 531 |
+
|
| 532 |
+
if (!response.ok) {
|
| 533 |
+
throw new Error('Prediction failed');
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
const data = await response.json();
|
| 537 |
+
displayResult(data);
|
| 538 |
+
} catch (error) {
|
| 539 |
+
resultContent.innerHTML = `
|
| 540 |
+
<div class="result-empty">
|
| 541 |
+
<div style="color: var(--accent-warm); font-size: 20px;">⚠</div>
|
| 542 |
+
<div>Error: ${error.message}</div>
|
| 543 |
+
</div>
|
| 544 |
+
`;
|
| 545 |
+
}
|
| 546 |
+
});
|
| 547 |
+
|
| 548 |
+
function displayResult(data) {
|
| 549 |
+
const confidence = data.confidence;
|
| 550 |
+
const digit = data.digit;
|
| 551 |
+
const predictions = data.all_predictions;
|
| 552 |
+
|
| 553 |
+
// Sort predictions
|
| 554 |
+
const sortedPredictions = Object.entries(predictions)
|
| 555 |
+
.sort((a, b) => b[1] - a[1]);
|
| 556 |
+
|
| 557 |
+
let predictionHtml = '';
|
| 558 |
+
sortedPredictions.forEach((pred, index) => {
|
| 559 |
+
const num = pred[0];
|
| 560 |
+
const percent = pred[1].toFixed(1);
|
| 561 |
+
const delay = index * 0.05;
|
| 562 |
+
predictionHtml += `
|
| 563 |
+
<div class="prediction-item" style="animation-delay: ${delay}s;">
|
| 564 |
+
<div class="prediction-digit">${num}</div>
|
| 565 |
+
<div class="prediction-bar">
|
| 566 |
+
<div class="prediction-fill" style="width: ${percent}%;"></div>
|
| 567 |
+
</div>
|
| 568 |
+
<div class="prediction-percent">${percent}%</div>
|
| 569 |
+
</div>
|
| 570 |
+
`;
|
| 571 |
+
});
|
| 572 |
+
|
| 573 |
+
resultContent.innerHTML = `
|
| 574 |
+
<div>
|
| 575 |
+
<div class="result-digit">${digit}</div>
|
| 576 |
+
<div class="result-confidence">${confidence.toFixed(1)}%</div>
|
| 577 |
+
<div class="confidence-label">Confidence</div>
|
| 578 |
+
<div class="confidence-bar">
|
| 579 |
+
<div class="confidence-fill" style="width: ${confidence}%;"></div>
|
| 580 |
+
</div>
|
| 581 |
+
<div class="predictions-list">
|
| 582 |
+
${predictionHtml}
|
| 583 |
+
</div>
|
| 584 |
+
</div>
|
| 585 |
+
`;
|
| 586 |
+
}
|
| 587 |
+
</script>
|
| 588 |
+
</body>
|
| 589 |
+
</html>
|