Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i.gensig: Add support of original class value in sig file (for i.maxlik) #2425

Merged
merged 9 commits into from
Jun 18, 2022
3 changes: 3 additions & 0 deletions imagery/i.gensig/get_train.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ int get_training_classes(struct files *files, struct Signature *S)
/* convert this to an array */
Rast_rewind_cell_stats(&cell_stats);
n = 0;
S->have_oclass = 1;
while (Rast_next_cell_stat(&cat, &count, &cell_stats)) {
if (count > 1) {
I_new_signature(S);
Expand All @@ -48,6 +49,8 @@ int get_training_classes(struct files *files, struct Signature *S)
Rast_get_c_cat(&cat, &files->training_labels),
sizeof(S->sig[n].desc)
);
S->sig[n].desc[255] = '\0'; /* desc is limited to 256 */
S->sig[n].oclass = cat;
n++;
}
else
Expand Down
14 changes: 14 additions & 0 deletions imagery/i.gensig/testsuite/data/b1.ascii
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
north: 5
south: 0
east: 5
west: 0
rows: 5
cols: 5
null: n
type: float

3.1 2.9 8.8 8.9 9.0
2.8 2.7 8.9 9.0 9.1
6.5 6.8 8.7 8.8 8.9
6.9 6.7 2.8 2.9 3.1
7.1 6.8 2.7 3.0 3.1
14 changes: 14 additions & 0 deletions imagery/i.gensig/testsuite/data/b2.ascii
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
north: 5
south: 0
east: 5
west: 0
rows: 5
cols: 5
null: n
type: int

8 8 1 1 2
7 7 2 1 2
9 9 2 2 1
8 9 7 7 6
8 8 8 7 8
14 changes: 14 additions & 0 deletions imagery/i.gensig/testsuite/data/train.ascii
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
north: 5
south: 0
east: 5
west: 0
rows: 5
cols: 5
null: n
type: int

1 n n n 6
n 1 6 6 n
9 n n n 6
9 n n n 1
n 9 n 1 n
159 changes: 159 additions & 0 deletions imagery/i.gensig/testsuite/test_i_gensig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""
Name: i.gensig general functionality
Purpose: Test ability to generate signature files
Author: Maris Nartiss
Copyright: (C) 2022 by Maris Nartiss and the GRASS Development Team
Licence: This program is free software under the GNU General Public
License (>=v2). Read the file COPYING that comes with GRASS
for details.
"""

import os
import stat
import ctypes
import shutil

from grass.pygrass import utils
from grass.pygrass.gis import Mapset
from grass.script.core import tempname

from grass.gunittest.case import TestCase
from grass.gunittest.main import test

from grass.lib.gis import G_mapset_path
from grass.lib.raster import Rast_write_semantic_label
from grass.lib.imagery import (
Signature,
Ref,
I_init_group_ref,
I_add_file_to_group_ref,
I_put_group_ref,
I_put_subgroup_ref,
I_fopen_signature_file_old,
I_read_signatures,
)


class SuccessTest(TestCase):
"""Test successful generation of a sig file"""

@classmethod
def setUpClass(cls):
"""Ensures expected computational region and generated data"""
cls.use_temp_region()
cls.runModule("g.region", n=5, s=0, e=5, w=0, res=1)
cls.data_dir = "data"
cls.mpath = utils.decode(G_mapset_path())
cls.mapset_name = Mapset().name

# Load imagery bands and training set
cls.b1 = tempname(10)
cls.runModule(
"r.in.ascii",
input=os.path.join(cls.data_dir, "b1.ascii"),
output=cls.b1,
)
cls.b2 = tempname(10)
cls.runModule(
"r.in.ascii",
input=os.path.join(cls.data_dir, "b2.ascii"),
output=cls.b2,
)
cls.train = tempname(10)
cls.runModule(
"r.in.ascii",
input=os.path.join(cls.data_dir, "train.ascii"),
output=cls.train,
)
cls.group = tempname(10)
cls.semantic_label1 = "The_Doors"
cls.semantic_label2 = "The_Who"
Rast_write_semantic_label(cls.b1, cls.semantic_label1)
Rast_write_semantic_label(cls.b2, cls.semantic_label2)
Rg = Ref()
I_init_group_ref(ctypes.byref(Rg))
I_add_file_to_group_ref(cls.b1, cls.mapset_name, ctypes.byref(Rg))
I_add_file_to_group_ref(cls.b2, cls.mapset_name, ctypes.byref(Rg))
I_put_group_ref(cls.group, ctypes.byref(Rg))
Rs = Ref()
I_init_group_ref(ctypes.byref(Rs))
I_add_file_to_group_ref(cls.b1, cls.mapset_name, ctypes.byref(Rs))
I_add_file_to_group_ref(cls.b2, cls.mapset_name, ctypes.byref(Rs))
I_put_subgroup_ref(cls.group, cls.group, ctypes.byref(Rs))

# Location of target signature file
cls.sig_name1 = tempname(10)
cls.sig_dir1 = f"{cls.mpath}/signatures/sig/{cls.sig_name1}"

@classmethod
def tearDownClass(cls):
"""Remove the temporary region and generated data"""
cls.del_temp_region()
shutil.rmtree(cls.sig_dir1, ignore_errors=True)
cls.runModule("g.remove", flags="f", type="raster", name=cls.b1, quiet=True)
cls.runModule("g.remove", flags="f", type="raster", name=cls.b2, quiet=True)
cls.runModule("g.remove", flags="f", type="raster", name=cls.train, quiet=True)

def test_creation(self):
"""Test creating a signature"""
self.assertModule(
"i.gensig",
trainingmap=self.train,
group=self.group,
subgroup=self.group,
signaturefile=self.sig_name1,
quiet=True,
)

# File must be present
sig_stat = os.stat(f"{self.sig_dir1}/sig")
self.assertTrue(stat.S_ISREG(sig_stat.st_mode))

# Compare values within sig file
Sn = Signature()
p_old_sigfile = I_fopen_signature_file_old(self.sig_name1)
ret = I_read_signatures(p_old_sigfile, ctypes.byref(Sn))
self.assertEqual(ret, 1)
self.assertEqual(Sn.nbands, 2)
self.assertEqual(Sn.nsigs, 3)
self.assertEqual(Sn.have_oclass, 1)
semantic_label = utils.decode(
ctypes.cast(Sn.semantic_labels[0], ctypes.c_char_p).value
)
self.assertEqual(semantic_label, self.semantic_label1)
semantic_label = utils.decode(
ctypes.cast(Sn.semantic_labels[1], ctypes.c_char_p).value
)
self.assertEqual(semantic_label, self.semantic_label2)
# 1
self.assertEqual(Sn.sig[0].status, 1)
self.assertEqual(Sn.sig[0].have_color, 0)
self.assertEqual(Sn.sig[0].npoints, 4)
self.assertEqual(Sn.sig[0].oclass, 1)
self.assertTrue(abs(Sn.sig[0].mean[0] - 2.9) < 0.1)
self.assertTrue(abs(Sn.sig[0].mean[1] - 7.0) < 0.1)
self.assertTrue(abs(Sn.sig[0].var[0][0] - 0.03) < 0.01)
self.assertTrue(abs(Sn.sig[0].var[1][1] - 0.6) < 0.1)
# 6
self.assertEqual(Sn.sig[1].status, 1)
self.assertEqual(Sn.sig[1].have_color, 0)
self.assertEqual(Sn.sig[1].npoints, 4)
self.assertEqual(Sn.sig[1].oclass, 6)
self.assertTrue(abs(Sn.sig[1].mean[0] - 8.9) < 0.1)
self.assertTrue(abs(Sn.sig[1].mean[1] - 1.5) < 0.1)
self.assertTrue(abs(Sn.sig[1].var[0][0] - 0.003) < 0.001)
self.assertTrue(abs(Sn.sig[1].var[1][1] - 0.3) < 0.1)
# 9
self.assertEqual(Sn.sig[2].status, 1)
self.assertEqual(Sn.sig[2].have_color, 0)
self.assertEqual(Sn.sig[2].npoints, 3)
self.assertEqual(Sn.sig[2].oclass, 9)
self.assertTrue(abs(Sn.sig[2].mean[0] - 6.7) < 0.1)
self.assertTrue(abs(Sn.sig[2].mean[1] - 8.3) < 0.1)
self.assertTrue(abs(Sn.sig[2].var[0][0] - 0.043) < 0.01)
self.assertTrue(abs(Sn.sig[2].var[1][1] - 0.3) < 0.1)


if __name__ == "__main__":
test()
15 changes: 12 additions & 3 deletions imagery/i.maxlik/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,13 @@ int main(int argc, char *argv[])
Rast_get_d_row(cellfd[band], cell[band], row);

classify(class_cell, reject_cell, ncols);
Rast_put_row(class_fd, class_cell, CELL_TYPE);
if (S.have_oclass) {
for (int col = 0; col < ncols; col++) {
/* Predicted classes start at 1 but signature array is 0 based */
class_cell[col] = S.sig[class_cell[col] - 1].oclass;
}
}
Rast_put_row(class_fd, class_cell, CELL_TYPE);
if (reject_fd > 0)
Rast_put_row(reject_fd, reject_cell, CELL_TYPE);
}
Expand All @@ -133,8 +139,11 @@ int main(int argc, char *argv[])
Rast_init_cats("Maximum Likelihood Classification", &cats);
for (i = 0; i < S.nsigs; i++) {
if (*S.sig[i].desc) {
cat = i + 1;
Rast_set_c_cat(&cat, &cat, S.sig[i].desc, &cats);
if (S.have_oclass)
cat = S.sig[i].oclass;
else
cat = i + 1;
Rast_set_c_cat(&cat, &cat, S.sig[i].desc, &cats);
}
}
Rast_write_cats(class_name, &cats);
Expand Down
Loading