← Back to projects

Bachelor’s degree capstone

Maize Disease Classification

A web application that sends maize leaf images to a convolutional neural network and stores predictions alongside editable disease recommendations. It runs locally on current runtimes and its bundled model is measured on the notebook’s held-out test images.

Problem

Classify a Leaf Image Through a Web Interface.

The project classifies maize leaf images through a web interface. It supports image uploads, prediction history and stored recommendation lookups. It is an academic prototype with no validated farm advisory use.

Role and scope

The Capstone, Upgraded and Measured.

What I Built

My bachelor’s capstone combines a Laravel interface, a Flask prediction service and a TensorFlow classifier. I upgraded it to run on supported runtimes and measured its bundled model on the notebook’s held-out test images.

Interface

Laravel 13 provides registration, login, private image uploads, per-user prediction history and administrator-edited disease-to-pesticide recommendations. A local end-to-end run exercises upload, prediction, access control and duplicate handling; password reset and the role screens are not exercised.

Model

The classifier distinguishes Blight, Common Rust, Gray Leaf Spot and Healthy. The four model files are unchanged from the capstone: saved with TensorFlow 2.7.0, they run on TensorFlow 2.21.0 without retraining.

Scope

The application runs on the local machine only, on the loopback interface. No retraining, live deployment or field validation is part of the project. The private system-documentation PDF is not published.

Architecture

Laravel Sends Image Bytes to a Flask Model Service.

Open full-size diagramOpen full-size diagramSwipe or use arrow keys to explore the diagram.
A training notebook uses leaf-image folders and is separate historical evidence. In the local application, the browser uploads a leaf image to Laravel 13, which stores it privately and sends its bytes to Flask; Flask classifies it with the bundled SavedModel (loaded once on TensorFlow 2.21) and returns one class. MySQL stores users, predictions with image hashes and recommendations.A training notebook uses leaf-image folders and is separate historical evidence. In the local application, the browser uploads a leaf image to Laravel 13, which stores it privately and sends its bytes to Flask; Flask classifies it with the bundled SavedModel (loaded once on TensorFlow 2.21) and returns one class. MySQL stores users, predictions with image hashes and recommendations.

The local application. The notebook is separate evidence with no verified link to the bundled model.

Read the diagram as text
  1. The notebook reads leaf images from training and test folders and records CNN training and evaluation outputs.
  2. Laravel stores a submitted image in private storage and sends its bytes to the Flask endpoint as a multipart upload.
  3. Flask loads the bundled TensorFlow SavedModel once at startup and maps its output to one of four class names.
  4. Laravel stores one prediction per user and image hash in MySQL and shows the matching disease recommendation.
  5. Both services listen on 127.0.0.1 only. The diagram shows the local application, not a deployment.

Input

Signed-in users upload a JPEG or PNG leaf image of up to 2 MB through Laravel. The notebook reads an existing image-folder dataset.

  • Laravel
  • Image files

Storage

MySQL holds users, predictions keyed by image hash and recommendations; uploads stay in private storage behind an owner check. No user records or uploads are published.

  • MySQL
  • Local filesystem

Model

Keras defines a convolutional classifier with image resizing and rescaling, convolution, batch normalization, pooling and a four-class output.

  • TensorFlow
  • Keras

Serving

Flask accepts the image bytes, runs the model loaded at startup and returns a class name or a JSON error. Laravel displays the stored result.

  • Flask
  • Laravel
  • HTTP

Key decisions

How I Upgraded and Measured the Capstone.

01

Send Image Bytes, Not a Server Path

The original Laravel posted a filesystem path that Flask read. Flask also reloaded the model for each request. I changed the boundary to a multipart upload and a model loaded once, so the two services share no filesystem.

  • Laravel
  • Flask
02

Upgrade the Runtime, Keep the Model

I moved the interface to Laravel 13 and the service to TensorFlow 2.21 and kept the four model files byte-identical, so any measured result describes the capstone’s own model.

  • Laravel
  • TensorFlow
03

Measure the Bundled Files on the Notebook’s Test Folder

The notebook’s saved result does not identify the exported model, so I evaluated the bundled files on the same held-out images, once with the notebook’s loading pipeline and once through the Flask service.

  • TensorFlow
  • Flask
04

Select a Checkpoint by Validation Accuracy

The original notebook saves the checkpoint with the highest validation accuracy. Its training and validation subsets use different seeds, so overlap weakens that selection criterion.

  • Keras
  • ModelCheckpoint

Evidence

Inspect the Source and the Measurement.

Repository

The repository holds the upgraded application, its setup and its limitations.

Notebook

The training notebook contains the saved dataset counts, architecture and test result.

Results and limits

Measured on 628 Held-Out Test Images.

Per-Class Results on the Test Images

Recall: the share of each class’s test images the model labels correctly.

  • Blight0.3967 of 172 correct
  • Common Rust1.00196 of 196 correct
  • Gray Leaf Spot0.7262 of 86 correct
  • Healthy1.00174 of 174 correct

Of the 172 Blight test images:

  • 67 labelled Blight (correct)
  • 47 labelled Common Rust
  • 45 labelled Gray Leaf Spot
  • 13 labelled Healthy

Source: README per-class table

Measured Test Accuracy

Accuracy 0.7946 (499 of 628) and loss 2.1865 on the notebook’s test folder, with identical results in two runs. Through the Flask service, 501 of 628 predictions are correct. Source: the README model evaluation.

Blight Is the Weak Class

Blight recall is 0.3895: 67 of 172 Blight images are classified correctly and 92 are predicted as Common Rust or Gray Leaf Spot. Common Rust and Healthy recall are 1.0. Source: the per-class table.

Notebook Figure and Limits

The notebook’s saved accuracy of 0.8105 comes from the model after its last training epoch, while its checkpoint callback saves the best validation epoch; the bundled model does not reproduce it. Validation overlaps training. The images are public data; no field, agronomist or recommendation validation is claimed. See the README limitations.

Lessons

What Measuring the Capstone Showed.

Evaluation Needs a Stable Split

The saved split settings show why training and validation must share one partition definition. The test folder stays separate, so it remains a usable held-out set.

Results Need Model Provenance

A notebook metric and an exported model are separate artifacts. Measuring the exported files on the same test images gives a different result from the one the notebook saved.

Accuracy Hides Class Gaps

Overall accuracy hides that most Blight images are misclassified. Per-class recall shows where the model fails and what a recommendation built on it would get wrong.

↑ ↓ to move · Enter to open · Esc to close