hubflow reclassify "unclassified" class name

Issue #203 resolved
Benjamin Jakimow created an issue

hubflow.core.Classification.reclassify() does not allow to use another name for unclassified.

Let pathSrc be a classification raster with 3 classes (values from 0 to 2), then this should allow to reclassify the raster to 2 classes (values 0 or 1, class names "No class" or "Class B").

        import hubflow.core
        classification = hubflow.core.Classification(pathSrc)
        # do not add the unclassified class
        newNames = ['No Class', 'Class B']
        newDef = hubflow.core.ClassDefinition(names=newNames)

        classification.reclassify(filename=pathDst,
                                  classDefinition=newDef,
                                  mapping={0:0,1:1,2:1})
        ds = gdal.Open(pathDst)
        band = ds.GetRasterBand(1)
        classNames = band.GetCategoryNames()
        self.assertEqual(newNames, classNames)

List classNames will return ['unclassified', 'No Class', 'Class B'] instead of ['unclassified', 'Class B']. Using unclassified implicitly breaks with GDAL API standard that allows to specify the class name corresponding to class value = 0.

Comments (4)

  1. Benjamin Jakimow reporter

    Related to this a test case:

    import unittest
    from unittest import TestCase
    from enmapbox.gui.utils import *
    APP = initQgisApplication()
    
    class BugTests(TestCase):
    
    
        def test_reclassify(self):
            import hubflow
            import hubflow.core
            classA = TestObjects.inMemoryClassification()
            self.assertIsInstance(classA, gdal.Dataset)
            print('old names: {}'.format(classA.GetRasterBand(1).GetCategoryNames()))
            pathSrc = classA.GetFileList()[0]
            pathDst = '/vsimem/testoutput.tif'
            classification = hubflow.core.Classification(pathSrc)
            newNames = ['Not specified', 'Class X']
            lookup = {0:0,1:1,2:1}
            # do not add the unclassified class
            newDef = hubflow.core.ClassDefinition(names=newNames)
            print(newDef)
            classification.reclassify(filename=pathDst,
                                      classDefinition=newDef,
                                      mapping=lookup)
    
            dsNew = gdal.Open(pathDst)
            self.assertIsInstance(dsNew, gdal.Dataset)
            writtenNames = dsNew.GetRasterBand(1).GetCategoryNames()
            self.assertIsInstance(writtenNames, list)
            self.assertListEqual(writtenNames, newNames, msg='Names not equal: \nshould be:{}\nwritten: {}'.format(newNames, writtenNames))
    
    if __name__ == "__main__":
    
        unittest.main()
    
  2. Andreas Janz

    In the ClassDefinition, the no data class (label=0) is not considered as a normal class. I added a special setter for the no data name and color and changed the test case accordingly:

            classification = Classification.fromArray(array=[[[0, 1, 2]]], filename='/vsimem/c.bsq')
            newNames = ['Class X']
            mapping = {0: 0, 1: 1, 2: 1}
            newDef = ClassDefinition(names=newNames)
            newDef.setNoDataNameAndColor(name='Not specified', color=Color(128, 128, 128))
            print(classification.classDefinition())
            print(newDef)
            reclassified = classification.reclassify(
                filename='/vsimem/c2.bsq', classDefinition=newDef, mapping=mapping)
    
            print(reclassified.dataset().band(0).categoryNames())
            print(reclassified.dataset().band(0).categoryColors())
    

    Prints:

    #!
    
    ['Not specified', 'Class X']
    [(128, 128, 128, 255), (186, 71, 78, 255)]
    
  3. Log in to comment