PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
connected_components.cc
Go to the documentation of this file.
1 /* Copyright (C) 2013, 2014, 2016, 2021, 2023 PISM Authors
2  *
3  * This file is part of PISM.
4  *
5  * PISM is free software; you can redistribute it and/or modify it under the
6  * terms of the GNU General Public License as published by the Free Software
7  * Foundation; either version 3 of the License, or (at your option) any later
8  * version.
9  *
10  * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with PISM; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <vector>
21 #include <algorithm> // min, max, abs
22 
23 int resolve_label(const std::vector<int> &labels, int provisional_label) {
24  int final_label = provisional_label;
25 
26  while (final_label != labels[final_label]) {
27  final_label = labels[final_label];
28  }
29 
30  return final_label;
31 }
32 
33 struct Run {
34  int row, col, length, label;
35 };
36 
37 /*!
38  * Label connected components. Modifies `image` in place.
39  */
40 void label_connected_components(double *image, int nrows, int ncols, bool identify_icebergs, int mask_grounded, int first_label) {
41 
42  // storage for labels assigned to pixels in the row above the current one
43  std::vector<int> row_above(ncols, 0);
44 
45  // Labels: the "grounded" label should be the smallest one.
46  int grounded_label = 1;
47  int provisional_label = 2;
48  // This array encodes equivalences between labels: label k is
49  // equivalent to labels[k], which is equivalent to
50  // labels[labels[k]], etc. The smallest label from a set of
51  // equivalent ones is used as a "representative" label for the set.
52  // By design labels[k] <= k.
53  std::vector<int> labels = {0, grounded_label};
54 
55  std::vector<Run> runs;
56 
57  for (int r = 0; r < nrows; ++r) {
58  const double *row = &image[static_cast<std::ptrdiff_t>(r) * ncols];
59 
60  int c = 0;
61  while (c < ncols) {
62  if (row[c] > 0) {
63  // we're looking at a foreground pixel that must be the
64  // beginning of a run
65  int L = provisional_label;
66  int c_start = c;
67 
68  runs.push_back({r, c_start, 0, L});
69  Run &current_run = runs.back();
70 
71  // Iterate over all the pixels in this run
72  while (c < ncols and row[c] > 0) {
73 
74  if (identify_icebergs and static_cast<int>(row[c]) == mask_grounded) {
75  // looking at a "grounded" pixel
76  if (L != provisional_label) {
77  labels[L] = grounded_label;
78  }
79  L = grounded_label;
80  }
81 
82  int T = row_above[c];
83  c += 1;
84 
85  if (T > 0) {
86  // foreground pixel in the row above
87  if (T < L) {
88  if (L != provisional_label) {
89  labels[L] = T;
90  }
91  L = T;
92  } else if (T > L) {
93  labels[T] = L;
94  }
95  }
96  } // end of the loop over pixels in a run
97 
98  if (L == provisional_label) {
99  // Failed to assign a label by looking at the row above: we
100  // need to add this label to the array encoding
101  // equivalences.
102  labels.push_back(L);
103  provisional_label += 1;
104  }
105 
106  // Done with a run: record the length and the label.
107  current_run.length = c - c_start;
108  current_run.label = L;
109 
110  // Record pixel labels in the row above.
111  for (int n = 0; n < current_run.length; ++n) {
112  row_above[c_start + n] = L;
113  }
114  } else {
115  // background pixel
116  row_above[c] = 0;
117  c += 1;
118  }
119  } // end of the loop over columns
120  } // end of the loop over rows
121 
122  auto N_labels = static_cast<int>(labels.size());
123 
124  // Flatten the table of equivalences
125  {
126  for (int k = 0; k < N_labels; ++k) {
127  labels[k] = resolve_label(labels, k);
128  }
129  }
130 
131  // Rename labels to make them consecutive
132  {
133  int L = first_label;
134  for (int k = 1; k < N_labels; ++k) {
135  if (labels[k] == k) {
136  labels[k] = L;
137  L += 1;
138  } else {
139  labels[k] = labels[labels[k]];
140  }
141  }
142  }
143 
144  if (identify_icebergs) {
145  // Blobs connected to grounded areas have the label "1", icebergs
146  // have labels 2 and greater.
147  for (int k = 1; k < N_labels; ++k) {
148  labels[k] = labels[k] > 1 ? 1 : 0;
149  }
150  } else {
151  // Here we subtract 1 because provisional labels start at 2 (1 is
152  // a special label used to identify blobs connected to "grounded"
153  // areas).
154  for (int k = 1; k < N_labels; ++k) {
155  labels[k] -= 1;
156  }
157  }
158 
159  // Second scan: assign labels
160  for (int k = 0; k < (int)runs.size(); ++k) {
161  auto &r = runs[k];
162  int run_start = r.row * ncols + r.col;
163  for (int n = 0; n < r.length; ++n) {
164  image[run_start + n] = labels[r.label];
165  }
166  }
167 }
void label_connected_components(double *image, int nrows, int ncols, bool identify_icebergs, int mask_grounded, int first_label)
int resolve_label(const std::vector< int > &labels, int provisional_label)
static const double L
Definition: exactTestL.cc:40
#define n
Definition: exactTestM.c:37
static Vector3 row(const double A[3][3], size_t k)
Definition: Element.cc:46
static const double k
Definition: exactTestP.cc:42