Leaffliction
42 Abu Dhabi

Leaffliction

AI Plant Pathology & Computer Vision CNN

3 weeks
Individual Project
PyTorchOpenCVPythonNumPyMatplotlibuv

Leaffliction explores the power of modern deep learning applied to precision agriculture and botanical pathology. The project addresses real-world machine learning challenges: imbalanced agricultural datasets, noisy natural backgrounds, and resource-constrained environments. Through five structured stages, the system analyzes class distributions, balances datasets with geometric and mathematical distortions (affine transforms, perspective warps, sine-wave distortion), isolates foliage using CIELAB a* channel Otsu thresholding and morphological filtering, and trains an efficient 1.2M parameter custom CNN (LeafNet) with adaptive average pooling to deliver high-accuracy multi-class disease classification.

Key Features

Custom LeafNet Architecture

Engineered a 4-block CNN featuring double 3x3 convolutions with BatchNorm, ReLU, and Adaptive Average Pooling, restricting parameters to ~1.2M to prevent overfitting.

Mathematical Data Augmentation

Developed 6 custom augmentation transforms: horizontal flip, 25° rotation, perspective skew, affine shear, center crop, and non-rigid sinusoidal distortion.

CIELAB Leaf Segmentation

Separated green plant tissue from complex backgrounds using the CIELAB a* channel with Otsu thresholding, morphological closing/opening, and connected-component analysis.

Full Inference & Verification Suite

Comprehensive CLI suite with Distribution.py, Augmentation.py, Transformation.py, train.py, and predict.py delivering per-class accuracy and SHA1 signature verification.

Development Journey

Part 1

Dataset Distribution & Analysis

Built Distribution.py to traverse nested image datasets, quantify class representation across disease categories (e.g. Apple Scab, Black Rot, Cedar Rust, Healthy), and generate informative bar and pie charts.

Part 2

Class Balancing & Data Augmentation

Engineered Augmentation.py to balance imbalanced datasets by synthesizing augmented copies using OpenCV matrix transformations until all classes match the majority count.

Part 3

Computer Vision Transformations

Developed Transformation.py utilizing CIELAB color decomposition, Gaussian blur, binary mask generation, ROI contour extraction, and leaf boundary detection.

Part 4 & 5

Deep Learning Model Training & Prediction

Architected LeafNet in PyTorch, executed multi-epoch training with Adam optimizer and validation splits, packaged model checkpoints into signed archives, and built CLI prediction tooling.

Challenges & Solutions

Class Imbalance in Natural Datasets

Problem:

Botanical datasets often have vast disparities between common diseases and rare anomalies, leading neural networks to exhibit majority-class bias.

Solution:

Created automated balancing routines that dynamically sample and apply randomized mathematical augmentations to minority classes until all categories match the maximum count.

Complex Background Interference

Problem:

Leaves photographed against soil, fingers, or wooden backgrounds caused standard edge detectors and thresholding to produce chaotic segmentations.

Solution:

Leveraged the CIELAB color space's a* channel (green-to-red axis) where leaves cleanly detach from neutral backgrounds, followed by morphological closing and largest-connected-component isolation.

Overfitting Prevention with Limited Samples

Problem:

Deep CNNs with dense fully-connected classification heads tend to memorize small agricultural training subsets rather than learning generalized disease features.

Solution:

Employed global adaptive average pooling (AdaptiveAvgPool2d) directly prior to the final linear layer, slashing trainable parameters to 1.2M and maintaining high validation generalization.

leaffliction/model.py
python
def _block(in_channels, out_channels):
    return nn.Sequential(
        nn.Conv2d(in_channels, out_channels, 3, padding=1, bias=False),
        nn.BatchNorm2d(out_channels),
        nn.ReLU(inplace=True),
        nn.Conv2d(out_channels, out_channels, 3, padding=1, bias=False),
        nn.BatchNorm2d(out_channels),
        nn.ReLU(inplace=True),
        nn.MaxPool2d(2),
    )

class LeafNet(nn.Module):
    """Four convolution blocks, global average pooling, one linear layer."""

    def __init__(self, class_count):
        super().__init__()
        self.features = nn.Sequential(
            _block(3, 32),
            _block(32, 64),
            _block(64, 128),
            _block(128, 256),
        )
        self.pool = nn.AdaptiveAvgPool2d(1)
        self.classifier = nn.Sequential(
            nn.Dropout(0.3),
            nn.Linear(256, class_count),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.pool(x).flatten(1)
        return self.classifier(x)