Explorar el Código

Make band references optional to simplify generation of signatures (#1866)

Bandrefs: make band references optional to simplify generation of signature files. This brings back pre GRASS 8 like functionality by allowing to classify only the same imagery group if band references are missing.
Māris Nartišs hace 3 años
padre
commit
e41c3e9279

+ 11 - 19
imagery/i.cluster/i.cluster.html

@@ -72,12 +72,15 @@ discriminated; any parameter values left unspecified are
 set to their default values.
 
 <p>
-All raster maps used to generate signature file must have band reference
-set. Use <em><a href="r.support.html">r.support</a></em> to set
+For all raster maps used to generate signature file it is recommended
+to have band reference set.
+Use <em><a href="r.support.html">r.support</a></em> to set
 band references of each member of the imagery group.
 Signatures generated for one scene are suitable for classification
 of other scenes as long as they consist of same raster bands
-(band references match).
+(band references match). If band references are not set, it will be
+possible to use obtained signature file to classify only the same
+imagery group used for generating signatures.
 
 <h3>Parameters:</h3>
 
@@ -263,22 +266,11 @@ Preparing the statistics for unsupervised classification of
 a LANDSAT subscene in North Carolina:
 
 <div class="code"><pre>
-## Prepare imagery for classification
-# Skip this step if your rasters already have band references defined
-# Define band references for all LANDSAT bands in mapset PERMANENT
-g.mapset mapset=PERMANENT
-r.support map=lsat7_2002_10 bandref=TM7_1
-r.support map=lsat7_2002_20 bandref=TM7_2
-r.support map=lsat7_2002_30 bandref=TM7_3
-r.support map=lsat7_2002_40 bandref=TM7_4
-r.support map=lsat7_2002_50 bandref=TM7_5
-r.support map=lsat7_2002_61 bandref=TM7_61
-r.support map=lsat7_2002_62 bandref=TM7_62
-r.support map=lsat7_2002_70 bandref=TM7_7
-r.support map=lsat7_2002_80 bandref=TM7_8
-g.mapset mapset=user1  # replace user1 with your mapset name
-
-## Real work starts here
+# To use signature file for classification of a different group, set
+# band references for each group member. In this example we'll create
+# signature file suitable only for the same group and thus band
+# references are not mandatory.
+
 # Set computational region to match the scene
 g.region raster=lsat7_2002_10 -p
 

+ 1 - 4
imagery/i.cluster/open_files.c

@@ -25,10 +25,7 @@ int open_files(void)
 	    G_warning(_("Raster map <%s> do not exists in subgroup <%s>"),
 		      G_fully_qualified_name(name, mapset), subgroup);
         }
-        bandref = Rast_read_bandref(ref.file[n].name, ref.file[n].mapset);
-        if (!bandref)
-            G_fatal_error(_("Raster map <%s@%s> lacks band reference"),
-                            ref.file[n].name, ref.file[n].mapset);
+        bandref = Rast_get_bandref_or_name(ref.file[n].name, ref.file[n].mapset);
         bandrefs[n] = G_store(bandref);
     }
     if (missing)

+ 1 - 4
imagery/i.gensig/openfiles.c

@@ -42,10 +42,7 @@ int openfiles(struct parms *parms, struct files *files, struct Signature *S)
 	files->band_fd[n] =
 	    Rast_open_old(Ref.file[n].name, Ref.file[n].mapset);
 	files->band_cell[n] = Rast_allocate_d_buf();
-        bandref = Rast_read_bandref(Ref.file[n].name, Ref.file[n].mapset);
-        if (!bandref)
-            G_fatal_error(_("Raster map <%s@%s> lacks band reference"),
-                            Ref.file[n].name, Ref.file[n].mapset);
+        bandref = Rast_get_bandref_or_name(Ref.file[n].name, Ref.file[n].mapset);
         S->bandrefs[n] = G_store(bandref);
     }
 

+ 6 - 3
imagery/i.gensigset/i.gensigset.html

@@ -19,12 +19,15 @@ The user would then execute the GRASS program <em>
 final classified map.
 
 <p>
-All raster maps used to generate signature file must have band reference
-set. Use <em><a href="r.support.html">r.support</a></em> to set
+For all raster maps used to generate signature file it is recommended
+to have band reference set.
+Use <em><a href="r.support.html">r.support</a></em> to set
 band references of each member of the imagery group.
 Signatures generated for one scene are suitable for classification
 of other scenes as long as they consist of same raster bands
-(band references match).
+(band references match). If band references are not set, it will be
+possible to use obtained signature file to classify only the same
+imagery group used for generating signatures.
 
 <p>
 An usage example can be found in <a href="i.smap.html">i.smap</a>

+ 1 - 4
imagery/i.gensigset/openfiles.c

@@ -41,10 +41,7 @@ int openfiles(struct parms *parms, struct files *files, struct SigSet *S)
 	files->band_fd[n] =
 	    Rast_open_old(Ref.file[n].name, Ref.file[n].mapset);
 	files->band_cell[n] = Rast_allocate_d_buf();
-        bandref = Rast_read_bandref(Ref.file[n].name, Ref.file[n].mapset);
-        if (!bandref)
-            G_fatal_error(_("Raster map <%s@%s> lacks band reference"),
-                            Ref.file[n].name, Ref.file[n].mapset);
+        bandref = Rast_get_bandref_or_name(Ref.file[n].name, Ref.file[n].mapset);
         S->bandrefs[n] = G_store(bandref);
     }
 

+ 5 - 16
imagery/i.smap/i.smap.html

@@ -141,22 +141,11 @@ r.mapcalc "MASKed_map = classification_results"
 Supervised classification of LANDSAT scene (complete NC location)
 
 <div class="code"><pre>
-## Prepare imagery for classification
-# Skip this step if your rasters already have band references defined
-# Define band references for all LANDSAT bands in mapset PERMANENT
-g.mapset mapset=PERMANENT
-r.support map=lsat7_2002_10 bandref=TM7_1
-r.support map=lsat7_2002_20 bandref=TM7_2
-r.support map=lsat7_2002_30 bandref=TM7_3
-r.support map=lsat7_2002_40 bandref=TM7_4
-r.support map=lsat7_2002_50 bandref=TM7_5
-r.support map=lsat7_2002_61 bandref=TM7_61
-r.support map=lsat7_2002_62 bandref=TM7_62
-r.support map=lsat7_2002_70 bandref=TM7_7
-r.support map=lsat7_2002_80 bandref=TM7_8
-g.mapset mapset=user1  # replace user1 with your mapset name
-
-## Real work starts here
+# To use signature file for classification of a different group, set
+# band references for each group member. In this example we'll create
+# signature file suitable only for the same group and thus band
+# references are not mandatory.
+
 # Align computation region to the scene
 g.region raster=lsat7_2002_10 -p
 

+ 1 - 0
include/grass/defs/raster.h

@@ -545,6 +545,7 @@ DCELL Rast_get_d_value(const void *, RASTER_MAP_TYPE);
 char *Rast_read_units(const char *, const char *);
 char *Rast_read_vdatum(const char *, const char *);
 char *Rast_read_bandref(const char *, const char *);
+char *Rast_get_bandref_or_name(const char *, const char *);
 void Rast_write_units(const char *, const char *);
 void Rast_write_vdatum(const char *, const char *);
 void Rast_write_bandref(const char *, const char *);

+ 1 - 6
lib/imagery/iclass_signatures.c

@@ -50,12 +50,7 @@ int I_iclass_init_signatures(struct Signature *sigs, struct Ref *refer)
 
     I_init_signatures(sigs, refer->nfiles);
     for (unsigned int i = refer->nfiles; i--;) {
-        sigs->bandrefs[i] = Rast_read_bandref(refer->file[i].name, refer->file[i].mapset);
-        if (!sigs->bandrefs[i]) {
-            G_warning(_("Raster map <%s@%s> lacks band reference"),
-                refer->file[i].name, refer->file[i].mapset);
-            return 0;
-        }
+        sigs->bandrefs[i] = Rast_get_bandref_or_name(refer->file[i].name, refer->file[i].mapset);
     }
 
     return 1;

+ 1 - 1
lib/imagery/sig.c

@@ -311,7 +311,7 @@ char **I_sort_signatures_by_bandref(struct Signature *S, const struct Ref *R) {
     /* Obtain group band references */
     group_bandrefs = (char **)G_malloc(R->nfiles * sizeof(char *));
     for (unsigned int j = R->nfiles; j--;) {
-        group_bandrefs[j] = Rast_read_bandref(R->file[j].name, R->file[j].mapset);
+        group_bandrefs[j] = Rast_get_bandref_or_name(R->file[j].name, R->file[j].mapset);
     }
 
     /* If lengths are not equal, there will be a mismatch */

+ 1 - 2
lib/imagery/sigset.c

@@ -488,8 +488,7 @@ char **I_SortSigSetByBandref(struct SigSet *S, const struct Ref *R)
     /* Obtain group band references */
     group_bandrefs = (char **)G_malloc(R->nfiles * sizeof(char *));
     for (unsigned int j = R->nfiles; j--;) {
-        group_bandrefs[j] =
-            Rast_read_bandref(R->file[j].name, R->file[j].mapset);
+        group_bandrefs[j] = Rast_get_bandref_or_name(R->file[j].name, R->file[j].mapset);
     }
 
     /* If lengths are not equal, there will be a mismatch */

+ 1 - 1
lib/imagery/testsuite/test_imagery_sigfile.py

@@ -376,7 +376,7 @@ class SortSignaturesByBandrefTest(TestCase):
             + "<band reference missing>,<band reference missing>,"
             + "<band reference missing>",
         )
-        self.assertEqual(ref_err, "The_Doors,<band reference missing>")
+        self.assertEqual(ref_err, f"The_Doors,{self.map3}")
 
         # Clean up memory to help track memory leaks when run by valgrind
         S.bandrefs[0] = None

+ 1 - 1
lib/imagery/testsuite/test_imagery_sigsetfile.py

@@ -368,7 +368,7 @@ class SortSigSetByBandrefTest(TestCase):
             + "<band reference missing>,<band reference missing>,"
             + "<band reference missing>",
         )
-        self.assertEqual(ref_err, "The_Doors,<band reference missing>")
+        self.assertEqual(ref_err, f"The_Doors,{self.map3}")
 
         # Clean up memory to help track memory leaks when run by valgrind
         I_free_group_ref(ctypes.byref(R))

+ 17 - 0
lib/raster/raster_metadata.c

@@ -99,6 +99,23 @@ char *Rast_read_bandref(const char *name, const char *mapset)
 }
 
 /*!
+ * \brief Get a raster map band reference or fall back to its name
+ * 
+ * Use this function if a band reference is needed but not mandated.
+ * 
+ * \param name raster map name
+ * \param mapset mapset name
+ *
+ * \return  string representing band reference or map name
+ */
+char *Rast_get_bandref_or_name(const char *name, const char *mapset) {
+    char *buff;
+
+    buff = Rast_read_bandref(name, mapset);
+    return buff ? buff : G_store(name);
+}
+
+/*!
  * \brief Write a string into a raster's band reference metadata file
  *
  * Raster map must exist in the current mapset.

+ 14 - 1
lib/raster/testsuite/test_raster_metadata.py

@@ -19,7 +19,12 @@ from grass.pygrass.gis import Mapset
 from grass.pygrass import utils
 
 from grass.lib.gis import G_remove_misc
-from grass.lib.raster import Rast_legal_bandref, Rast_read_bandref, Rast_write_bandref
+from grass.lib.raster import (
+    Rast_legal_bandref,
+    Rast_read_bandref,
+    Rast_get_bandref_or_name,
+    Rast_write_bandref,
+)
 
 
 class RastLegalBandIdTestCase(TestCase):
@@ -95,6 +100,14 @@ class RastBandReferenceTestCase(TestCase):
         ret = utils.decode(Rast_read_bandref(self.map, self.mapset))
         self.assertEqual(ret, self.bandref)
 
+    def test_get_bandref_or_name(self):
+        G_remove_misc("cell_misc", "bandref", self.map)
+        ret = utils.decode(Rast_get_bandref_or_name(self.map, self.mapset))
+        self.assertEqual(ret, self.map)
+        Rast_write_bandref(self.map, self.bandref)
+        ret = utils.decode(Rast_get_bandref_or_name(self.map, self.mapset))
+        self.assertEqual(ret, self.bandref)
+
 
 if __name__ == "__main__":
     test()