GRASS 8 Programmer's Manual 8.6.0dev(2026)-1d1e47ad9d
Loading...
Searching...
No Matches
sigset.c
Go to the documentation of this file.
1#include <string.h>
2#include <stdlib.h>
3#include <grass/gis.h>
4#include <grass/glocale.h>
5#include <grass/imagery.h>
6
7static int gettag(FILE *, char *);
8static int get_semantic_labels(FILE *, struct SigSet *);
9static int get_title(FILE *, struct SigSet *);
10static int get_class(FILE *, struct SigSet *);
11static int get_classnum(FILE *, struct ClassSig *);
12static int get_classtype(FILE *, struct ClassSig *);
13static int get_classtitle(FILE *, struct ClassSig *);
14static int get_subclass(FILE *, struct SigSet *, struct ClassSig *);
15static int get_subclass_pi(FILE *, struct SubSig *);
16static int get_subclass_means(FILE *, struct SubSig *, int);
17static int get_subclass_covar(FILE *, struct SubSig *, int);
18
19static double **alloc_matrix(int rows, int cols)
20{
21 double **m;
22 int i;
23
24 m = (double **)G_calloc(rows, sizeof(double *));
25 m[0] = (double *)G_calloc(rows * cols, sizeof(double));
26 for (i = 1; i < rows; i++)
27 m[i] = m[i - 1] + cols;
28
29 return m;
30}
31
33{
34 int i, count;
35
36 for (i = 0, count = 0; i < S->nclasses; i++)
37 if (S->ClassSig[i].used)
38 count++;
39
40 return count;
41}
42
43struct ClassData *I_AllocClassData(struct SigSet *S, struct ClassSig *C,
44 int npixels)
45{
46 struct ClassData *Data;
47
48 Data = &(C->ClassData);
49 Data->npixels = npixels;
50 Data->count = 0;
51 Data->x = alloc_matrix(npixels, S->nbands);
52 Data->p = alloc_matrix(npixels, C->nsubclasses);
53 return Data;
54}
55
56/*!
57 * \brief Initialize struct SigSet before use
58 *
59 * No need to call before calling I_ReadSigSet.
60 *
61 * \param S *Signature to initialize
62 * \param nbands band (imagery group member) count
63 */
64int I_InitSigSet(struct SigSet *S, int nbands)
65{
66 S->nbands = nbands;
67 S->semantic_labels = (char **)G_malloc(nbands * sizeof(char **));
68 for (int i = 0; i < nbands; i++)
69 S->semantic_labels[i] = NULL;
70 S->nclasses = 0;
71 S->ClassSig = NULL;
72 S->title = NULL;
73
74 return 0;
75}
76
77struct ClassSig *I_NewClassSig(struct SigSet *S)
78{
79 struct ClassSig *Sp;
80
81 if (S->nclasses == 0)
82 S->ClassSig = (struct ClassSig *)G_malloc(sizeof(struct ClassSig));
83 else
84 S->ClassSig = (struct ClassSig *)G_realloc(
85 (char *)S->ClassSig, sizeof(struct ClassSig) * (S->nclasses + 1));
86
87 Sp = &S->ClassSig[S->nclasses++];
88 Sp->classnum = 0;
89 Sp->nsubclasses = 0;
90 Sp->used = 1;
92 Sp->title = NULL;
93 return Sp;
94}
95
96struct SubSig *I_NewSubSig(struct SigSet *S, struct ClassSig *C)
97{
98 struct SubSig *Sp;
99 int i;
100
101 if (C->nsubclasses == 0)
102 C->SubSig = (struct SubSig *)G_malloc(sizeof(struct SubSig));
103 else
104 C->SubSig = (struct SubSig *)G_realloc(
105 (char *)C->SubSig, sizeof(struct SubSig) * (C->nsubclasses + 1));
106
107 Sp = &C->SubSig[C->nsubclasses++];
108 Sp->used = 1;
109 Sp->R = (double **)G_calloc(S->nbands, sizeof(double *));
110 Sp->R[0] = (double *)G_calloc(S->nbands * S->nbands, sizeof(double));
111 for (i = 1; i < S->nbands; i++)
112 Sp->R[i] = Sp->R[i - 1] + S->nbands;
113 Sp->Rinv = (double **)G_calloc(S->nbands, sizeof(double *));
114 Sp->Rinv[0] = (double *)G_calloc(S->nbands * S->nbands, sizeof(double));
115 for (i = 1; i < S->nbands; i++)
116 Sp->Rinv[i] = Sp->Rinv[i - 1] + S->nbands;
117 Sp->means = (double *)G_calloc(S->nbands, sizeof(double));
118 Sp->N = 0;
119 Sp->pi = 0;
120 Sp->cnst = 0;
121 return Sp;
122}
123
124#define eq(a, b) strcmp(a, b) == 0
125
126/*!
127 * \brief Read sigset signatures from file
128 *
129 * File stream should be opened in advance by call to
130 * I_fopen_sigset_file_old()
131 * It is up to caller to fclose the file stream afterwards.
132 *
133 * There is no need to initialise struct SigSet in advance, as this
134 * function internally calls I_InitSigSet.
135 *
136 * \param fd pointer to FILE*
137 * \param S pointer to struct SigSet *S
138 *
139 * \return 1 on success, -1 on failure
140 */
141int I_ReadSigSet(FILE *fd, struct SigSet *S)
142{
143 char tag[256];
144 unsigned int version;
145
146 if (fscanf(fd, "%u", &version) != 1) {
147 G_warning(_("Invalid signature file"));
148 return -1;
149 }
150 if (version != 1) {
151 G_warning(_("Invalid signature file version"));
152 return -1;
153 }
154
155 I_InitSigSet(S, 0);
156 while (gettag(fd, tag)) {
157 if (eq(tag, "title:"))
158 if (get_title(fd, S) != 0)
159 return -1;
160 if (eq(tag, "semantic_labels:"))
161 if (get_semantic_labels(fd, S) != 0)
162 return -1;
163 if (eq(tag, "class:"))
164 if (get_class(fd, S) != 0)
165 return -1;
166 }
167 return 1; /* for now assume success */
168}
169
170static int gettag(FILE *fd, char *tag)
171{
172 if (fscanf(fd, "%255s", tag) != 1)
173 return 0;
174 G_strip(tag);
175 return 1;
176}
177
178static int get_semantic_labels(FILE *fd, struct SigSet *S)
179{
180 int n, pos;
181 char c, prev;
183
184 /* Read semantic labels and count them to set nbands */
185 n = 0;
186 pos = 0;
187 S->semantic_labels =
188 (char **)G_realloc(S->semantic_labels, (n + 1) * sizeof(char **));
189 while ((c = (char)fgetc(fd)) != EOF) {
190 if (c == '\n') {
191 if (prev != ' ') {
192 semantic_label[pos] = '\0';
193 if (strlen(semantic_label) > 0) {
195 n++;
196 }
197 }
198 S->nbands = n;
199 break;
200 }
201 if (c == ' ') {
202 semantic_label[pos] = '\0';
203 if (strlen(semantic_label) > 0) {
205 n++;
206 /* [n] is 0 based thus: (n + 1) */
207 S->semantic_labels = (char **)G_realloc(
208 S->semantic_labels, (n + 1) * sizeof(char **));
209 }
210 pos = 0;
211 prev = c;
212 continue;
213 }
214 /* Semantic labels are limited to GNAME_MAX - 1 + \0 in length;
215 * n is 0-based */
216 if (pos == (GNAME_MAX - 2)) {
217 G_warning(_("Invalid signature file: semantic label length limit "
218 "exceeded"));
219 return -1;
220 }
221 semantic_label[pos] = c;
222 pos++;
223 prev = c;
224 }
225 if (!(S->nbands > 0)) {
226 G_warning(_("Signature file does not contain bands"));
227 return -1;
228 }
229
230 return 0;
231}
232
233static int get_title(FILE *fd, struct SigSet *S)
234{
235 char title[1024];
236
237 *title = 0;
238 if (fscanf(fd, "%1023[^\n]", title) != 1)
239 return -1;
240 G_strip(title);
241 I_SetSigTitle(S, title);
242
243 return 0;
244}
245
246static int get_class(FILE *fd, struct SigSet *S)
247{
248 char tag[1024];
249 struct ClassSig *C;
250
251 C = I_NewClassSig(S);
252 while (gettag(fd, tag)) {
253 if (eq(tag, "endclass:"))
254 break;
255 if (eq(tag, "classnum:"))
256 if (get_classnum(fd, C) != 0)
257 return -1;
258 if (eq(tag, "classtype:"))
259 if (get_classtype(fd, C) != 0)
260 return -1;
261 if (eq(tag, "classtitle:"))
262 if (get_classtitle(fd, C) != 0)
263 return -1;
264 if (eq(tag, "subclass:"))
265 if (get_subclass(fd, S, C) != 0)
266 return -1;
267 }
268
269 return 0;
270}
271
272static int get_classnum(FILE *fd, struct ClassSig *C)
273{
274 if (fscanf(fd, "%ld", &C->classnum) != 1)
275 return -1;
276
277 return 0;
278}
279
280static int get_classtype(FILE *fd, struct ClassSig *C)
281{
282 if (fscanf(fd, "%d", &C->type) != 1)
283 return -1;
284
285 return 0;
286}
287
288static int get_classtitle(FILE *fd, struct ClassSig *C)
289{
290 char title[1024];
291
292 *title = 0;
293 if (fscanf(fd, "%1023[^\n]", title) != 1)
294 return -1;
295 G_strip(title);
297
298 return 0;
299}
300
301static int get_subclass(FILE *fd, struct SigSet *S, struct ClassSig *C)
302{
303 struct SubSig *Sp;
304 char tag[1024];
305
306 Sp = I_NewSubSig(S, C);
307
308 while (gettag(fd, tag)) {
309 if (eq(tag, "endsubclass:"))
310 break;
311 if (eq(tag, "pi:"))
312 if (get_subclass_pi(fd, Sp) != 0)
313 return -1;
314 if (eq(tag, "means:"))
315 if (get_subclass_means(fd, Sp, S->nbands) != 0)
316 return -1;
317 if (eq(tag, "covar:"))
318 if (get_subclass_covar(fd, Sp, S->nbands) != 0)
319 return -1;
320 }
321
322 return 0;
323}
324
325static int get_subclass_pi(FILE *fd, struct SubSig *Sp)
326{
327 if (fscanf(fd, "%lf", &Sp->pi) != 1)
328 return -1;
329
330 return 0;
331}
332
333static int get_subclass_means(FILE *fd, struct SubSig *Sp, int nbands)
334{
335 int i;
336
337 for (i = 0; i < nbands; i++)
338 if (fscanf(fd, "%lf", &Sp->means[i]) != 1)
339 return -1;
340
341 return 0;
342}
343
344static int get_subclass_covar(FILE *fd, struct SubSig *Sp, int nbands)
345{
346 int i, j;
347
348 for (i = 0; i < nbands; i++)
349 for (j = 0; j < nbands; j++)
350 if (fscanf(fd, "%lf", &Sp->R[i][j]) != 1)
351 return -1;
352
353 return 0;
354}
355
356int I_SetSigTitle(struct SigSet *S, const char *title)
357{
358 if (title == NULL)
359 title = "";
360 if (S->title)
361 free(S->title);
362 S->title = G_store(title);
363
364 return 0;
365}
366
367const char *I_GetSigTitle(const struct SigSet *S)
368{
369 if (S->title)
370 return S->title;
371 else
372 return "";
373}
374
375int I_SetClassTitle(struct ClassSig *C, const char *title)
376{
377 if (title == NULL)
378 title = "";
379 if (C->title)
380 free(C->title);
381 C->title = G_store(title);
382
383 return 0;
384}
385
386const char *I_GetClassTitle(const struct ClassSig *C)
387{
388 if (C->title)
389 return C->title;
390 else
391 return "";
392}
393
394int I_WriteSigSet(FILE *fd, const struct SigSet *S)
395{
396 const struct ClassSig *Cp;
397 const struct SubSig *Sp;
398 int i, j, b1, b2;
399
400 /* This is version 1 sigset file format */
401 fprintf(fd, "1\n");
402 fprintf(fd, "title: %s\n", I_GetSigTitle(S));
403 fprintf(fd, "semantic_labels: ");
404 for (i = 0; i < S->nbands; i++) {
405 fprintf(fd, "%s ", S->semantic_labels[i]);
406 }
407 fprintf(fd, "\n");
408 for (i = 0; i < S->nclasses; i++) {
409 Cp = &S->ClassSig[i];
410 if (!Cp->used)
411 continue;
412 if (Cp->nsubclasses <= 0)
413 continue;
414 fprintf(fd, "class:\n");
415 fprintf(fd, " classnum: %ld\n", Cp->classnum);
416 fprintf(fd, " classtitle: %s\n", I_GetClassTitle(Cp));
417 fprintf(fd, " classtype: %d\n", Cp->type);
418
419 for (j = 0; j < Cp->nsubclasses; j++) {
420 Sp = &Cp->SubSig[j];
421 fprintf(fd, " subclass:\n");
422 fprintf(fd, " pi: %g\n", Sp->pi);
423 fprintf(fd, " means:");
424 for (b1 = 0; b1 < S->nbands; b1++)
425 fprintf(fd, " %g", Sp->means[b1]);
426 fprintf(fd, "\n");
427 fprintf(fd, " covar:\n");
428 for (b1 = 0; b1 < S->nbands; b1++) {
429 fprintf(fd, " ");
430 for (b2 = 0; b2 < S->nbands; b2++)
431 fprintf(fd, " %g", Sp->R[b1][b2]);
432 fprintf(fd, "\n");
433 }
434 fprintf(fd, " endsubclass:\n");
435 }
436 fprintf(fd, "endclass:\n");
437 }
438
439 return 0;
440}
441
442/*!
443 * \brief Reorder struct SigSet to match imagery group member order
444 *
445 * The function will check for semantic label match between sigset struct
446 * and imagery group.
447 *
448 * In the case of a complete semantic label match, values of passed in
449 * struct SigSet are reordered to match the order of imagery group items.
450 * This reordering is done only for items present in the sigset file.
451 * Thus reordering should be done only after calling I_ReadSigSet.
452 *
453 * If all semantic labels are not identical (in
454 * arbitrary order), function will return two dimensional array with
455 * comma separated list of:
456 * - [0] semantic labels present in the signature struct but
457 * absent in the imagery group
458 * - [1] semantic labels present in the imagery group but
459 * absent in the signature struct
460 *
461 * If no mismatch of semantic labels for signatures or imagery group are
462 * detected (== all are present in the other list), a NULL value will be
463 * returned in the particular list of mismatches (not an empty string).
464 * For example:
465 * \code
466 * if (ret && ret[1])
467 * printf("List of imagery group bands without signatures: %s\n, ret[1]);
468 * \endcode
469 *
470 * \param S existing signatures to check & sort
471 * \param R group reference
472 *
473 * \return NULL successfully sorted
474 * \return err_array two comma separated lists of mismatches
475 */
476char **I_SortSigSetBySemanticLabel(struct SigSet *S, const struct Ref *R)
477{
478 unsigned int total, complete;
479 unsigned int *match1, *match2, mc1, mc2, *new_order;
480 double ***new_means, ****new_vars;
482
483 /* Safety measure. Untranslated as this should not happen in production! */
484 if (S->nbands < 1 || R->nfiles < 1)
485 G_fatal_error("Programming error. Invalid length structs passed to "
486 "I_sort_signatures_by_semantic_label(%d, %d);",
487 S->nbands, R->nfiles);
488
489 /* Obtain group semantic labels */
490 group_semantic_labels = (char **)G_malloc(R->nfiles * sizeof(char *));
491 for (unsigned int j = R->nfiles; j--;) {
493 Rast_get_semantic_label_or_name(R->file[j].name, R->file[j].mapset);
494 }
495
496 /* If lengths are not equal, there will be a mismatch */
497 complete = S->nbands == R->nfiles;
498
499 /* Initialize match tracker */
500 new_order = (unsigned int *)G_malloc(S->nbands * sizeof(unsigned int));
501 match1 = (unsigned int *)G_calloc(S->nbands, sizeof(unsigned int));
502 match2 = (unsigned int *)G_calloc(R->nfiles, sizeof(unsigned int));
503
504 /* Allocate memory for temporary storage of sorted values */
505 new_semantic_labels = (char **)G_malloc(S->nbands * sizeof(char *));
506 new_means = (double ***)G_malloc(S->nclasses * sizeof(double **));
507 // new_vars[S.ClassSig[x]][.SubSig[y]][R[band1]][R[band1]]
508 new_vars = (double ****)G_malloc(S->nclasses * sizeof(double ***));
509 for (unsigned int c = S->nclasses; c--;) {
510 new_means[c] =
511 (double **)G_malloc(S->ClassSig[c].nsubclasses * sizeof(double *));
512 new_vars[c] = (double ***)G_malloc(S->ClassSig[c].nsubclasses *
513 sizeof(double **));
514 for (unsigned int s = S->ClassSig[c].nsubclasses; s--;) {
515 new_means[c][s] = (double *)G_malloc(S->nbands * sizeof(double));
516 new_vars[c][s] = (double **)G_malloc(S->nbands * sizeof(double *));
517 for (unsigned int i = S->nbands; i--;)
518 new_vars[c][s][i] =
519 (double *)G_malloc(S->nbands * sizeof(double));
520 }
521 }
522
523 /* Obtain order of matching items */
524 for (unsigned int j = R->nfiles; j--;) {
525 for (unsigned int i = S->nbands; i--;) {
528 if (complete) {
529 /* Reorder pointers to existing strings only */
531 new_order[i] = j;
532 }
533 /* Keep a track of matching items for error reporting */
534 match1[i] = 1;
535 match2[j] = 1;
536 break;
537 }
538 }
539 }
540
541 /* Check for semantic label mismatch */
542 mc1 = mc2 = 0;
543 mismatches = (char **)G_malloc(2 * sizeof(char **));
544 mismatches[0] = NULL;
545 mismatches[1] = NULL;
546 total = 1;
547 for (unsigned int i = 0; i < (unsigned int)S->nbands; i++) {
548 if (!match1[i]) {
549 if (S->semantic_labels[i])
550 total = total + strlen(S->semantic_labels[i]);
551 else
552 total = total + 24;
553 mismatches[0] =
554 (char *)G_realloc(mismatches[0], total * sizeof(char *));
555 if (mc1)
556 strcat(mismatches[0], ",");
557 else
558 mismatches[0][0] = '\0';
559 if (S->semantic_labels[i])
561 else
562 strcat(mismatches[0], "<semantic label missing>");
563 mc1++;
564 total = total + 1;
565 }
566 }
567 total = 1;
568 for (unsigned int j = 0; j < (unsigned int)R->nfiles; j++) {
569 if (!match2[j]) {
571 total = total + strlen(group_semantic_labels[j]);
572 else
573 total = total + 24;
574 mismatches[1] =
575 (char *)G_realloc(mismatches[1], total * sizeof(char *));
576 if (mc2)
577 strcat(mismatches[1], ",");
578 else
579 mismatches[1][0] = '\0';
582 else
583 strcat(mismatches[1], "<semantic label missing>");
584 mc2++;
585 total = total + 1;
586 }
587 }
588
589 /* Swap mean and var matrix values in each of classes */
590 if (!mc1 && !mc2) {
591 for (unsigned int c = S->nclasses; c--;) {
592 for (unsigned int s = S->ClassSig[c].nsubclasses; s--;) {
593 for (unsigned int b1 = 0; b1 < (unsigned int)S->nbands; b1++) {
594 new_means[c][s][new_order[b1]] =
595 S->ClassSig[c].SubSig[s].means[b1];
596 for (unsigned int b2 = 0; b2 < (unsigned int)S->nbands;
597 b2++) {
598 new_vars[c][s][new_order[b1]][new_order[b2]] =
599 S->ClassSig[c].SubSig[s].R[b1][b2];
600 }
601 }
602 }
603 }
604
605 /* Replace values in struct with ordered ones */
607 S->nbands * sizeof(char **));
608 for (unsigned int c = S->nclasses; c--;) {
609 for (unsigned int s = S->ClassSig[c].nsubclasses; s--;) {
610 memcpy(S->ClassSig[c].SubSig[s].means, new_means[c][s],
611 S->nbands * sizeof(double));
612 for (unsigned int i = S->nbands; i--;)
613 memcpy(S->ClassSig[c].SubSig[s].R[i], new_vars[c][s][i],
614 S->nbands * sizeof(double));
615 }
616 }
617 }
618
619 /* Clean up */
620 for (unsigned int j = R->nfiles; j--;)
624 G_free(match1);
625 G_free(match2);
627 for (unsigned int c = S->nclasses; c--;) {
628 for (unsigned int s = S->ClassSig[c].nsubclasses; s--;) {
629 G_free(new_means[c][s]);
630 for (unsigned int i = S->nbands; i--;)
631 G_free(new_vars[c][s][i]);
632 G_free(new_vars[c][s]);
633 }
634 G_free(new_means[c]);
635 G_free(new_vars[c]);
636 }
637
640
641 if (mc1 || mc2) {
642 return mismatches;
643 }
645 return NULL;
646}
#define NULL
Definition ccmath.h:32
AMI_err name(char **stream_name)
Definition ami_stream.h:426
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
#define G_realloc(p, n)
Definition defs/gis.h:141
#define G_calloc(m, n)
Definition defs/gis.h:140
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:139
void G_strip(char *)
Removes all leading and trailing white space from string.
Definition strings.c:300
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:87
char * Rast_get_semantic_label_or_name(const char *, const char *)
Get a raster map semantic label or fall back to its name.
#define GNAME_MAX
Definition gis.h:196
#define _(str)
Definition glocale.h:10
#define SIGNATURE_TYPE_MIXED
Definition imagery.h:199
int count
struct SubSig * I_NewSubSig(struct SigSet *S, struct ClassSig *C)
Definition sigset.c:96
int I_SetSigTitle(struct SigSet *S, const char *title)
Definition sigset.c:356
int I_ReadSigSet(FILE *fd, struct SigSet *S)
Read sigset signatures from file.
Definition sigset.c:141
#define eq(a, b)
Definition sigset.c:124
int I_SigSetNClasses(struct SigSet *S)
Definition sigset.c:32
int I_SetClassTitle(struct ClassSig *C, const char *title)
Definition sigset.c:375
const char * I_GetSigTitle(const struct SigSet *S)
Definition sigset.c:367
char ** I_SortSigSetBySemanticLabel(struct SigSet *S, const struct Ref *R)
Reorder struct SigSet to match imagery group member order.
Definition sigset.c:476
int I_InitSigSet(struct SigSet *S, int nbands)
Initialize struct SigSet before use.
Definition sigset.c:64
struct ClassSig * I_NewClassSig(struct SigSet *S)
Definition sigset.c:77
struct ClassData * I_AllocClassData(struct SigSet *S, struct ClassSig *C, int npixels)
Definition sigset.c:43
const char * I_GetClassTitle(const struct ClassSig *C)
Definition sigset.c:386
int I_WriteSigSet(FILE *fd, const struct SigSet *S)
Definition sigset.c:394
void free(void *)
int npixels
Definition imagery.h:78
double ** p
Definition imagery.h:81
double ** x
Definition imagery.h:80
int count
Definition imagery.h:79
int nsubclasses
Definition imagery.h:89
struct ClassData ClassData
Definition imagery.h:91
long classnum
Definition imagery.h:85
int type
Definition imagery.h:88
char * title
Definition imagery.h:86
struct SubSig * SubSig
Definition imagery.h:90
Definition imagery.h:24
int nbands
Definition imagery.h:95
struct ClassSig * ClassSig
Definition imagery.h:99
char ** semantic_labels
Definition imagery.h:96
int nclasses
Definition imagery.h:97
char * title
Definition imagery.h:98
double ** R
Definition imagery.h:71