Commit 01b74f2e authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Revised SAM_Classifier.

parent 6f7ae30f
Pipeline #3230 failed with stage
in 19 minutes and 13 seconds
......@@ -3,6 +3,8 @@
Algorithms for multispectral image classification.
"""
import warnings
import numpy as np
from typing import Union, List # noqa F401 # flake8 issue
from multiprocessing import Pool
......@@ -115,53 +117,48 @@ class SAM_Classifier_OLD(_ImageClassifier):
class SAM_Classifier(_ImageClassifier):
def __init__(self, train_spectra, threshold=0.1, CPUs=1):
# type: (np.ndarray, Union[np.ndarray, List[int]], Union[int, None]) -> None
super(SAM_Classifier, self).__init__(train_spectra, np.array(range(train_spectra.shape[0])), CPUs=CPUs)
def __init__(self, train_spectra, CPUs=1):
# type: (np.ndarray, Union[int, None]) -> None
if np.percentile(train_spectra, 80) > 1.:
warnings.warn('SAM assumes the input spectra to be scaled between 0 and 1. Received data with an '
'80% percentile above 1. This might lead to invalid output values.')
self.clf = SAM()
self.threshold = threshold
super(SAM_Classifier, self).__init__(train_spectra, np.array(range(train_spectra.shape[0])), CPUs=CPUs)
def _predict(self, tilepos, tileimdata):
if not tileimdata.shape[2] == self.train_spectra.shape[1]:
raise RuntimeError('Matrix dimensions are not aligned. Input image has %d bands but input spectra '
'have %d.' % (tileimdata.shape[2], self.train_spectra.shape[1]))
angles = np.zeros((tileimdata.shape[0], tileimdata.shape[1], self.n_samples), np.float)
# if np.std(tileimdata) == 0:
tileimdata_norm = self._normalize(tileimdata, axis=2)
# if np.std(tileimdata) == 0: # skip tiles that only contain the same value
for n_sample in range(self.n_samples):
train_spectrum = self.train_spectra[n_sample, :].reshape(1, 1, self.n_features)
angles[:, :, n_sample] = self._calc_sam(tileimdata_norm, train_spectrum, axis=2, s1_normed=True)
angles[:, :, n_sample] = self._calc_sam(tileimdata.astype(np.float),
train_spectrum.astype(np.float),
axis=2)
cmap = np.argmin(angles, axis=2)
return tilepos, cmap
def _calc_sam(self, s1, s2, axis=0, s1_normed=False, s2_normed=False):
"""Compute the spectral angle mapper between two vectors or images (in radians)."""
norm_s1 = s1 if s1_normed else self._normalize(s1, axis=axis)
norm_s2 = s2 if s2_normed else self._normalize(s2, axis=axis)
@staticmethod
def _calc_sam(s1, s2, axis=0):
"""Compute spectral angle between two vectors or images (in radians)."""
upper = np.sum(s1 * s2, axis=axis)
lower = np.sqrt(np.sum(s1 * s1, axis=axis)) * np.sqrt(np.sum(s2 * s2, axis=axis))
upper = np.sum(norm_s1 * norm_s2, axis=axis)
lower = np.sqrt(np.sum(norm_s1 * norm_s1, axis=axis)) * np.sqrt(np.sum(norm_s2 * norm_s2, axis=axis))
if lower.ndim > 1:
lower[lower == 0] = 1e-10
else:
lower = lower or 1e-10
return np.arccos(upper / lower)
@staticmethod
def _normalize(x, axis=0):
if x.ndim > 2:
upper = (x - np.min(x, axis=axis)[:, :, np.newaxis]).astype(np.float)
lower = (np.max(x, axis=axis) - np.min(x, axis=axis)).astype(np.float)[:, :, np.newaxis]
lower[lower == 0] = 1e-10
else:
upper = (x - np.min(x, axis=axis)).astype(np.float)
lower = (np.max(x, axis=axis) - np.min(x, axis=axis)).astype(np.float)
lower = lower or 1e-10
quotient = upper / lower
quotient[np.isclose(quotient, 1)] = 1 # in case of pixels that are equal to the endmember
return upper / lower
return np.arccos(quotient)
def classify_image(image, train_spectra, train_labels, classif_alg,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment