The SCP classifier suite — air-gapped, on the analyst's desk.

The classic Semi-Automatic Classification Plugin algorithm set — Spectral Angle Mapping, Minimum Distance, Maximum Likelihood (Mahalanobis via Cholesky), Parallelepiped, and unsupervised K-means — implemented in pure C#, plus Random Forest / SVM / XGBoost via an optional ML sidecar. Train on ROI polygons or GPS points, render through the existing ImageServer as just another raster function, and trust the result with a confusion matrix + Overall Accuracy + Kappa. No Python required for the core classifiers.

SAM · MinDist · MaxLikParallelepiped · K-meansRF · SVM · XGBoost Accuracy + KappaChange detectionOBIATOA / DOS1 calibration
Just another raster function

No new layer type, no new route

Classification isn't a separate application — it's a renderingRule on the existing ImageServer. Swap the rule and the ImageryTileLayer re-renders classified. exportImage, tile caching, and every SDK client serve it unchanged.

  • Train by ROI or by points — draw polygons, or drop GPS ground-truth points; the service rasterizes, samples, and accumulates numerically-stable (Welford) mean + covariance per class.
  • Signatures by ID — covariance matrices across 10 bands × N classes are too large for a URL, so the rule references a signature-set id; the in-memory cache makes per-tile classification I/O-free.
  • Discrete palette — class ID → color, not a continuous ramp. Unknown / NoData is transparent (honest degrade, never a blank tile).
renderingRule = Classify
# classify with a trained signature set
?renderingRule={
  "rasterFunction": "Classify",
  "rasterFunctionArguments": {
    "signatureSetId": "set-7",
    "smoothRadius": 2,
    "calibrate": "dos1"
  }
}

# inline K-means preview (no training, per-tile)
?renderingRule={
  "rasterFunction": "KMeans",
  "rasterFunctionArguments": {"k":5}
}
The algorithm set

From classic supervised to modern ensemble

The SCP-parity supervised + unsupervised classifiers are pure C# (air-gapped). RF/SVM/XGBoost add a small ML sidecar when installed; the engine dispatches on the algorithm string and the surface is identical.

📐

Spectral Angle Mapping

θ = acos(p·m / ‖p‖‖m‖) — scale-invariant, works on raw DN or reflectance. Optional max-angle threshold → unclassified.

📏

Minimum Distance

Euclidean distance to the class mean in band space. The fast baseline.

🎯

Maximum Likelihood

Mahalanobis distance via Cholesky (Σ = LLᵀ) with a ridge guard that rescues near-singular and single-sample covariances.

📦

Parallelepiped

Box test per band (mean ± k·stddev). The quick reject filter.

🔮

K-means (unsupervised)

k-means++ seeding + Lloyd iteration. Produces centroids you can persist as a named signature set for coherent cross-tile classification.

🌲

RF · SVM · XGBoost

Ensemble classifiers via an optional :8006 scikit-learn sidecar. The trained tree ensemble runs in-process via a C# tree-walker — no Python at inference time.

Trust, then decide

Accuracy assessment, calibration, and change detection

Confusion matrix + OA + Kappa

Holds out a test fraction (seeded Fisher-Yates, default seed 7 to match ml4qgis), classifies each held-out vector, and reports Overall Accuracy, per-class Producer's/User's accuracy, and Cohen's Kappa. A Plotly heatmap renders in-map.

☀️

TOA + DOS1 calibration

Convert raw DN to reflectance before classifying. Reads Landsat .MTL + Sentinel-2 MTD_MSIL*.xml sidecars (which GDAL doesn't auto-parse). MaxLik and the ensemble benefit; SAM is scale-invariant so it's a harmless no-op.

🔄

Post-classification change detection

Two signature sets over the same raster → a change map + a from→to transition tally. Deforestation, urban growth, flood extent — the classic remote-sensing workflow.

🧩

OBIA — object-based

Classify SEGMENTS (not pixels) to eliminate salt-and-pepper noise by construction. Spectral mean/std per band + shape metrics + GLCM texture per segment.

📈

Spectral signature plot

Per-class mean ± 1σ band curves so you can judge separability BEFORE classifying — well-separated curves = good classes; overlapping bands = pick different bands or merge.

🧹

Majority filter

Optional post-classification smoothing — each pixel becomes the majority class in its window, gated by a threshold so thin linear features (roads, rivers) survive.

Classify imagery — without a Python install or a cloud round-trip.

The SCP algorithm set, the modern ensemble stack, and the credibility reporting — all air-gapped. Pair with IMINT exploitation and annotated reporting.

Get Started