Skip to content

About

AI-powered hybrid image quality assessment and multi-label defect detection system with classical CV metrics, MobileNetV2 fusion, Grad-CAM visual explainability, and containerized FastAPI backend.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

AI-Powered Image Quality & Defect Detection System

An end-to-end full-stack computer vision application designed to evaluate visual image quality, detect degradation artifacts (blur, over/underexposure, noise, spatial corruption), and produce explainable quality assessments with continuous scoring ($0-100$).


1. Architectural Strategy: Hybrid AI + Classical CV

A pure deep learning approach lacks deterministic explainability on basic statistical degradations, while a purely classical CV rule engine fails to generalize to complex spatial defects. This system implements a Hybrid Fusion Architecture:

  1. Deterministic CV Feature Extractor:
    • Sharpness: Variance of the Laplacian kernel ($\sigma^2_{\nabla^2}$) detecting high-frequency spatial gradients.
    • Exposure: Mean pixel intensity ($\mu_I$) combined with extreme clipping ratios ($I \le 5$ and $I \ge 250$).
    • Noise Estimation: Mean absolute residual between original grayscale image and a $3\times3$ spatial median filter.
    • Contrast: Root Mean Square (RMS) contrast across intensity distribution.
  2. Deep Learning Feature Extractor:
    • Pretrained MobileNetV2 backbone extracting 1,280-dimensional spatial visual embeddings.
  3. Multi-Modal Feature Fusion:
    • Handcrafted CV metrics are standard-scaled (StandardScaler) and passed through a dedicated projection layer (Linear(6, 32) $\to$ BatchNorm $\to$ ReLU).
    • Classical embeddings and deep visual embeddings are concatenated (1280 + 32 = 1312) into a dense multi-label classification head trained with Binary Cross-Entropy with Logits (BCEWithLogitsLoss).

2. Dataset Synthesis & Zero-Leakage Protocol

To ensure strict evaluation rigor, synthetic degradations were generated following a zero-leakage protocol:

  • Source Partitioning: 100 clean high-resolution baseline images from DIV2K were partitioned at the source image level (70% Train / 15% Validation / 15% Test) prior to synthetic degradation. The test split contains zero underlying scene content from the training set.
  • Degradation Generation:
    • Blur: Gaussian smoothing filters with varying kernel sizes ($k \in [3, 15]$).
    • Over/Underexposure: Linear scale factors ($[0.2, 2.5]$) with pixel intensity clipping.
    • Noise: Additive zero-mean Gaussian noise ($\sigma \in [10, 50]$).
    • Corruption: Spatial block-region replacement simulating localized packet loss and sensor dropout.
  • Target Distribution: 1,050 training samples, 225 validation samples, and 225 test samples across clean (25%), single-defect (45%), and multi-defect (30%) permutations.

3. Evaluation, Metric Discussion & Failure Cases

Test Set Performance (225 Unseen Source Scenes, Deployed Threshold = 0.40)

The table below reflects inference at the same confidence threshold (> 0.40) used by the live /analyze endpoint in main.py, not the conventional 0.50 default — this is the honest representation of production behavior rather than an idealized number.

               precision    recall  f1-score   support

        blur       0.84      0.98      0.91        54
overexposure       0.75      0.80      0.77        51
underexposure       0.73      0.93      0.82        58
       noise       0.78      0.93      0.85        42
  corruption       1.00      0.96      0.98        48

   micro avg       0.81      0.92      0.86       253
   macro avg       0.82      0.92      0.86       253
weighted avg       0.82      0.92      0.86       253
 samples avg       0.69      0.72      0.69       253

Note: a secondary evaluation using the conventional 0.50 threshold is included as artifacts/evaluation_report.txt (weighted F1 0.87) — we report at 0.40 above since that is the threshold the deployed API actually uses. Lowering the decision threshold trades a small amount of precision for higher recall, which we judged the better trade-off for a quality-flagging tool where missing a real defect is costlier than an extra false positive.

Detailed Analysis of Metric Discrepancies

  • weighted avg (0.86) vs. samples avg (0.69): strong per-class F1 shows each defect head is well-calibrated in isolation, but samples avg requires an exact label-set match per image. Multi-defect images (2–3 simultaneous issues) are harder to get exactly right than single-defect images, since one borderline confidence on a secondary defect breaks the full-match score even when the primary defect is identified correctly.
  • Precision/recall trade visible per class: recall is consistently higher than precision across most classes at this threshold (e.g. underexposure: 0.73 precision / 0.93 recall), confirming the model is tuned toward catching more true defects at the cost of some false positives — consistent with the threshold choice above.

Real Misclassified Test Cases (from extract_real_failures.py)

Filename Ground Truth Predicted Confidence (all classes) Root Cause
test_00005.jpg blur, overexposure, underexposure blur, underexposure blur 1.00, overexposure 0.16, underexposure 0.95, noise 0.00, corruption 0.01 Overexposure confidence (0.16) fell well below the 0.40 threshold despite being a true label — likely because the co-occurring severe underexposure/blur dominated the visual signal the CNN branch attended to.
test_00006.jpg clean underexposure, noise blur 0.00, overexposure 0.04, underexposure 0.62, noise 0.50, corruption 0.02 False positive on a nominally clean image; CV stats show a high noise residual (14.23) despite no synthetic noise applied — natural DIV2K image texture in low/mid-frequency detail was likely misread as noise by both the CV feature and the CNN branch.
test_00016.jpg clean underexposure blur 0.00, overexposure 0.28, underexposure 0.96, noise 0.00, corruption 0.00 False positive at high confidence (0.96) on a clean image with mean_brightness = 48.21 — a naturally dark scene (not synthetically underexposed) was indistinguishable from true underexposure using brightness-based features alone.
test_00018.jpg overexposure, corruption overexposure blur 0.00, overexposure 0.99, underexposure 0.06, noise 0.00, corruption 0.09 Corruption confidence (0.09) missed the true label; co-occurring severe overexposure (clipping 13% of pixels) likely washed out the localized block artifact the corruption detector relies on.

Inherent Limitations

  1. Naturally dark/textured scenes are conflated with synthetic defects. test_00006.jpg and test_00016.jpg show the model's most consistent failure mode: images that are naturally dark or low-contrast (not synthetically degraded) get flagged as underexposed or noisy. This is a direct consequence of training only on synthetically clean-vs-degraded pairs — the model has not learned to distinguish "naturally moody photography" from "underexposure defect," which real-world deployment would need to address with a broader, more stylistically diverse clean-image set.
  2. Co-occurring defects can mask each other. In 2 of the 4 sampled failures, a dominant defect (severe blur, severe overexposure) suppressed the model's confidence on a secondary, true co-occurring defect. This is a known limitation of independent per-class sigmoid heads on a shared backbone and is a candidate for future work (e.g. defect-specific attention, or a curriculum that oversamples multi-defect combinations).
  3. Uniform synthetic degradations vs. real optical flaws. Synthetic Gaussian blur/noise apply uniformly across the frame, while real-world defects (motion streaks, radial lens distortion, sensor-specific chroma noise) are spatially non-uniform. Real deployment accuracy is expected to be lower than these test numbers until validated against genuinely defective camera images.
  4. Limited scene diversity in the test split. The 70/15/15 split is applied at the source-image level across only 100 DIV2K images, meaning the test set draws from 15 unique underlying scenes. This is sufficient to guarantee zero train/test content leakage, but is a small diversity pool — real-world generalization claims should be treated as preliminary pending evaluation on a larger, independent image set.

4. Setup & Deployment

Prerequisites

  • Docker Desktop (with Docker Compose)
  • ~9 GB free disk space (base Python image + PyTorch/torchvision layers)

Run with Docker Compose (recommended — verified working)

# From the project root
docker compose --progress=plain build
docker compose up -d

The first build pulls a python:3.13-slim base image and installs pinned dependencies (scikit-learn==1.9.0, torch==2.13.0, torchvision==0.28.0) — expect several minutes on first run. Subsequent builds are cached and much faster.

The container exposes the app on port 8000. Once running:

  • Web UI: http://localhost:8000
  • Health check: http://localhost:8000/health

To stop the stack:

docker compose down

Run without Docker (local development)

python -m venv venv
venv\Scripts\Activate.ps1        # Windows PowerShell
# source venv/bin/activate       # macOS/Linux

pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000

Database

SQLite is used for persistence (artifacts/results.db), created automatically on first run via SQLAlchemy — no manual setup required. The artifacts/ folder is bind-mounted into the container (see docker-compose.yml) and must contain best_model.pth and scaler.pkl on the host before docker compose up, or the container's startup validation will fail loudly rather than serve a broken model.

Verified Deployment Evidence

The following was run and confirmed working end-to-end against the live Docker container prior to submission:

$ curl.exe http://localhost:8000/health
{"status":"healthy","device":"cpu","model_loaded":true,"scaler_loaded":true}
$ curl.exe -F "file=@sample.png" http://localhost:8000/analyze
{"id":7,"filename":"49229bfc_0840.png","quality_score":80,"quality_label":"ACCEPTABLE",
 "issues":[{"type":"underexposure","severity":"high","confidence":0.98}],
 "metrics":{"sharpness_laplacian":660.15,"mean_brightness":71.59,
 "underexposed_clip_ratio":0.0052,"overexposed_clip_ratio":0.0,
 "noise_residual":2.93,"rms_contrast":45.58},
 "timestamp":"2026-08-29T15:19:34.215558"}
$ curl.exe "http://localhost:8000/gradcam/7?target_class=underexposure" --output heatmap.png
# 1.23 MB PNG heatmap returned and visually confirmed

5. API Documentation

Base URL (local): http://localhost:8000

GET /health

Health/status check. Confirms the service is up and the model + scaler artifacts loaded successfully.

Response

{"status": "healthy", "device": "cpu", "model_loaded": true, "scaler_loaded": true}

POST /analyze

Uploads an image and runs the full hybrid CV + deep learning quality analysis.

Request — multipart/form-data, field name file

curl -F "file=@your_image.jpg" http://localhost:8000/analyze

Response

{
  "id": 7,
  "filename": "49229bfc_0840.png",
  "quality_score": 80,
  "quality_label": "ACCEPTABLE",
  "issues": [
    {"type": "underexposure", "severity": "high", "confidence": 0.98}
  ],
  "metrics": {
    "sharpness_laplacian": 660.15,
    "mean_brightness": 71.59,
    "underexposed_clip_ratio": 0.0052,
    "overexposed_clip_ratio": 0.0,
    "noise_residual": 2.93,
    "rms_contrast": 45.58
  },
  "timestamp": "2026-08-29T15:19:34.215558"
}

Invalid or unreadable files return a 422 with a structured Pydantic validation error rather than a server crash.

GET /results

Returns the full history of past analyses.

curl http://localhost:8000/results

GET /results/{id}

Returns a single stored analysis by its ID.

curl http://localhost:8000/results/7

GET /gradcam/{id}?target_class={class}

Returns a Grad-CAM heatmap PNG explaining the model's attention for a specific detected defect class on a previously analyzed image. target_class must be one of: blur, overexposure, underexposure, noise, corruption.

curl "http://localhost:8000/gradcam/7?target_class=underexposure" --output heatmap.png

Requesting an invalid class returns a 422 explaining the accepted values instead of failing silently.

About

AI-powered hybrid image quality assessment and multi-label defect detection system with classical CV metrics, MobileNetV2 fusion, Grad-CAM visual explainability, and containerized FastAPI backend.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages