Commit 87c56346 authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Fixed issue #1 (kNN_SAM_Classifier.label_unclassified_pixels(...,...

Fixed issue #1

 (kNN_SAM_Classifier.label_unclassified_pixels(..., threshold=...) only flags pixels if threshold is exceeded by FIRST spectral neighbour). Replaced random value test image by image containing more realistic spectra. Added tests for kNN_SAM_Classifier to ensure the output makes sense.
Signed-off-by: Daniel Scheffler's avatarDaniel Scheffler <danschef@gfz-potsdam.de>
parent d01b5358
......@@ -217,7 +217,8 @@ class _kNN_ImageClassifier(_ImageClassifier):
cmap = np.argpartition(dists, k, axis=2)[:, :, :k].astype(np.int16)
dists_min_k = np.partition(dists, k, axis=2)[:, :, :k].astype(np.float32)
else:
cmap = np.tile(np.arange(dists.shape[2]).reshape(1, 1, -1), (*dists.shape[:2], 1))
cmap = np.tile(np.arange(dists.shape[2]).reshape((1, 1, -1)),
(*dists.shape[:2], 1))
dists_min_k = dists
# sort cmap by ascending spectral distances
......
......@@ -79,9 +79,10 @@ class SAM_Classifier(_ImageClassifier):
def label_unclassified_pixels(self, label_unclassified, threshold):
# type: (int, Union[str, int, float]) -> GeoArray
return self._label_unclassified_pixels(
self.cmap, label_unclassified, threshold, self.angles_deg
)
return self._label_unclassified_pixels(cmap=self.cmap,
label_unclassified=label_unclassified,
threshold=threshold,
distances=self.angles_deg)
def show_angles_histogram(self, figsize=(10, 5), bins=100, normed=False):
self._show_distances_histogram(self.angles_deg, self.cmap, figsize=figsize, bins=bins, normed=normed)
......@@ -105,15 +106,3 @@ class kNN_SAM_Classifier(SAM_Classifier, _kNN_ImageClassifier):
cmap = self.overwrite_cmap_at_nodata_positions(cmap, imdata)
return cmap.astype(np.int16), angles_min_k
def label_unclassified_pixels(self, label_unclassified, threshold):
# type: (int, Union[str, int, float]) -> GeoArray
cmap_labelled0 = self._label_unclassified_pixels(
GeoArray(self.cmap[:, :, 0], nodata=self.cmap.nodata),
label_unclassified, threshold, self.angles_deg[:, :, 0]
)
cmap = self.cmap
if label_unclassified in cmap_labelled0[:]:
cmap[cmap_labelled0[:] == label_unclassified] = label_unclassified
return cmap
......@@ -47,9 +47,6 @@ path_testclassifiers = os.path.join(__path__[0], '..', 'tests', 'data', 'classif
path_classifier_zip = os.path.join(path_testclassifiers, 'LR_classifiers.zip')
fName_cls = 'LR_clust50__Landsat-7__ETM+.dill'
test_gA = GeoArray(np.random.RandomState(0).randint(0, 10000, (1010, 1010, 6), np.int16)) # 6 Landsat-5 bands
test_gA[:5, 0, :] = -9999
test_gA[:5, 1, 3] = -9999
# get cluster centers
with zipfile.ZipFile(path_classifier_zip, "r") as zf, tempfile.TemporaryDirectory() as td:
......@@ -65,6 +62,31 @@ test_gA_pure_endmembers = np.zeros((1, cluster_centers.shape[0], cluster_centers
test_gA_pure_endmembers[:, :, :] = cluster_centers
# compute test image
def _get_testIm(shape, nodataVal):
# get array full of nodata values
spec = np.full(shape, nodataVal).reshape(shape[0] * shape[1], shape[2])
# fill array with random spectra taken from the cluster centers
for i in range(cluster_centers.shape[0]):
spec[np.random.randint(spec.shape[0],
size=spec.shape[0]//10), :] = \
cluster_centers[i, :]
# add 10% noise to each spectrum
spec = spec * np.random.normal(1, .1, spec.size).reshape(spec.shape)
# return in desired image dimensions
return spec.reshape(shape)
test_gA = GeoArray(_get_testIm(shape=(1010, 1010, 6),
nodataVal=-9999).astype(np.int16),
nodata=-9999) # 6 Landsat-5 bands
test_gA[:20, 0, :] = -9999 # set 20 pixels to -9999 in all bands
test_gA[:20, 1, 3] = -9999 # set 20 pixels to -9999 in fourth band only
class Test_MinimumDistance_Classifier(unittest.TestCase):
def test_classify(self):
t0 = time()
......@@ -123,6 +145,9 @@ class Test_kNN_MinimumDistance_Classifier(unittest.TestCase):
MDC.classify(test_gA, in_nodataVal=-9999, cmap_nodataVal=-9999, tiledims=(400, 200))
MDC.label_unclassified_pixels(label_unclassified=-1, threshold=6000)
# check that there are no unlabelled spectral neighbors with Dist above 6000 (given threshold) in the output
self.assertTrue(np.max(MDC.euclidian_distance[MDC.cmap[:] > 0]) <= 6000)
def test_label_unclassified_pixels_relative_th(self):
MDC = kNN_MinimumDistance_Classifier(cluster_centers, cluster_labels, self.n_neighbors, CPUs=None)
MDC.classify(test_gA, in_nodataVal=-9999, cmap_nodataVal=-9999, tiledims=(400, 200))
......@@ -178,6 +203,7 @@ class Test_SAM_Classifier(unittest.TestCase):
class Test_kNN_SAM_Classifier(unittest.TestCase):
# TODO: apply these tests to all classifiers
def setUp(self) -> None:
self.n_neighbors = 5
......@@ -198,16 +224,70 @@ class Test_kNN_SAM_Classifier(unittest.TestCase):
cmap_mp = SC.classify(test_gA_pure_endmembers, in_nodataVal=-9999, cmap_nodataVal=-9999)
self.assertTrue(np.array_equal(cmap_mp[:, :, 0].flatten(), cluster_labels))
def test_classification_outputs(self):
SC = kNN_SAM_Classifier(cluster_centers, n_neighbors=self.n_neighbors, CPUs=1)
cmap = SC.classify(test_gA, in_nodataVal=None, cmap_nodataVal=None, tiledims=(400, 200))
# check if first 20 pixels in col 0 contain the same cmap value (input image is nodata in all bands there)
vals = np.unique(cmap[:20, 0, :])
self.assertEqual(len(vals), self.n_neighbors) # n differnt neighbors
self.assertTrue(0 <= min(vals) < cluster_centers.shape[0]) # cmap values min between 0 and 49
self.assertTrue(0 <= max(vals) < cluster_centers.shape[0]) # cmap values max between 0 and 49
# check if first 20 pixels in col 1 contain the different cmap value (input image has values in some bands)
vals = np.unique(cmap[:20, 1, :])
self.assertGreater(len(vals), self.n_neighbors) # n different neighbors
self.assertTrue(0 <= min(vals) < cluster_centers.shape[0]) # cmap values min between 0 and 49
self.assertTrue(0 <= max(vals) < cluster_centers.shape[0]) # cmap values max between 0 and 49
# check if the cmap contains only values between 0 and 49 in the rest of the image
vals = np.unique(cmap[:, 2:, :])
self.assertTrue(0 <= min(vals) < cluster_centers.shape[0]) # cmap values min between 0 and 49
self.assertTrue(0 <= max(vals) < cluster_centers.shape[0]) # cmap values max between 0 and 49
def test_label_unclassified_pixels_absolute_th(self):
SC = kNN_SAM_Classifier(cluster_centers, n_neighbors=self.n_neighbors, CPUs=None)
SC.classify(test_gA, in_nodataVal=-9999, cmap_nodataVal=-9999, tiledims=(400, 200))
SC.label_unclassified_pixels(label_unclassified=-1, threshold=10)
# check that there are no unlabelled spectral neighbors with SAM above 10 degrees in the output
self.assertTrue(np.max(SC.angles_deg[SC.cmap[:] > 0]) <= 10)
def test_label_unclassified_pixels_relative_th(self):
SC = kNN_SAM_Classifier(cluster_centers, self.n_neighbors, CPUs=None)
SC.classify(test_gA, in_nodataVal=-9999, cmap_nodataVal=-9999, tiledims=(400, 200))
SC.label_unclassified_pixels(label_unclassified=-1, threshold='10%')
def test_label_unclassified_pixels_cmapNodataVal_given(self):
SC = kNN_SAM_Classifier(cluster_centers, n_neighbors=self.n_neighbors, CPUs=None)
SC.classify(test_gA, in_nodataVal=-9999, cmap_nodataVal=-999, tiledims=(400, 200))
# now label all pixels with SAM above 10 degrees with -1
cmap = SC.label_unclassified_pixels(label_unclassified=-1, threshold=10)
# check if first 20 pixels in cols 0 and 1 contain only -999
# (nodata pixels in the input image should appear as nodata in the cmap (-999) if cmap_nodataVal=-999)
vals = np.unique(cmap[:20, :2, :])
self.assertEqual(len(vals), 1)
self.assertEqual(vals[0], -999)
# check if there are pixels in the rest of the image that are labelled as unclassified
vals = np.unique(cmap[:, 2:, :])
self.assertTrue(-1 in vals)
def test_label_unclassified_pixels_cmapNodataVal_not_given(self):
SC = kNN_SAM_Classifier(cluster_centers, n_neighbors=self.n_neighbors, CPUs=None)
SC.classify(test_gA, in_nodataVal=-9999, cmap_nodataVal=None, tiledims=(400, 200))
# now label all pixels with SAM above 10 degrees with -1
cmap = SC.label_unclassified_pixels(label_unclassified=-1, threshold=10)
# check if first 20 pixels in cols 0 and 1 contain only -1
# (nodata pixels in the input image should appear as unclassified in the cmap (-1) if cmap_nodataVal=None)
vals = np.unique(cmap[:20, :2, :])
self.assertEqual(len(vals), 1)
self.assertEqual(vals[0], -1)
class Test_FEDSA_Classifier(unittest.TestCase):
def test_classify(self):
......
Supports Markdown
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