
Leaffliction
AI Plant Pathology & Computer Vision CNN
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
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.
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.
Computer Vision Transformations
Developed Transformation.py utilizing CIELAB color decomposition, Gaussian blur, binary mask generation, ROI contour extraction, and leaf boundary detection.
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
Botanical datasets often have vast disparities between common diseases and rare anomalies, leading neural networks to exhibit majority-class bias.
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
Leaves photographed against soil, fingers, or wooden backgrounds caused standard edge detectors and thresholding to produce chaotic segmentations.
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
Deep CNNs with dense fully-connected classification heads tend to memorize small agricultural training subsets rather than learning generalized disease features.
Employed global adaptive average pooling (AdaptiveAvgPool2d) directly prior to the final linear layer, slashing trainable parameters to 1.2M and maintaining high validation generalization.
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)