netzel commited on 2024-02-21 22:51:55
Showing 3 changed files, with 385 additions and 0 deletions.
... | ... |
@@ -0,0 +1,28 @@ |
1 |
+ |
|
2 |
+PROG = plforestfragment |
|
3 |
+ |
|
4 |
+CC = gcc |
|
5 |
+ |
|
6 |
+CFLAGS=-Wall -fopenmp -O3 -msse2 -mfpmath=sse -Wl,-rpath,'$$ORIGIN/' -I/usr/include/gdal -lgomp -lgdal -lezgdal -largtable3 -lm |
|
7 |
+ |
|
8 |
+COMMON_C = $(shell ls *.c) |
|
9 |
+COMMON_C_O = $(COMMON_C:%.c=%.o) |
|
10 |
+ |
|
11 |
+ifndef PREFIX |
|
12 |
+ PREFIX = /usr/local |
|
13 |
+endif |
|
14 |
+ |
|
15 |
+ |
|
16 |
+default: $(PROG) |
|
17 |
+ |
|
18 |
+$(PROG): $(COMMON_C_O) $(HEADERS) |
|
19 |
+ $(CC) $(CFLAGS) -o $(PROG) $(COMMON_O) $(COMMON_C_O) |
|
20 |
+ |
|
21 |
+$(COMMON_C_O): $(COMMON_C) $(HEADERS_C) |
|
22 |
+ $(CC) $(CFLAGS) -c $(COMMON_C) |
|
23 |
+ |
|
24 |
+install: |
|
25 |
+ cp -f $(PROG) $(PREFIX)/bin |
|
26 |
+ |
|
27 |
+clean: |
|
28 |
+ rm -f *.o $(PROG) |
... | ... |
@@ -0,0 +1,20 @@ |
1 |
+PROG = plforestfragment.exe |
|
2 |
+ |
|
3 |
+CC = x86_64-w64-mingw32-gcc |
|
4 |
+ |
|
5 |
+CFLAGS= -I../../Win64_cross/gdal/include -I../../Win64_cross/ezgdal -I../../Win64_cross/argtable3 -L../../Win64_cross/argtable3 -L../../Win64_cross/gdal/bin -L../../Win64_cross/ezgdal -Wall -fopenmp -O3 -msse2 -mfpmath=sse -lgomp -lgdal -llibezgdal -llibargtable3 -lm |
|
6 |
+ |
|
7 |
+COMMON_C = $(shell ls *.c) |
|
8 |
+COMMON_C_O = $(COMMON_C:%.c=%.o) |
|
9 |
+HEADERS = $(shell ls *.h) |
|
10 |
+ |
|
11 |
+default: $(PROG) |
|
12 |
+ |
|
13 |
+$(PROG): $(COMMON_C_O) $(HEADERS) |
|
14 |
+ $(CC) $(CFLAGS) -o $(PROG) $(COMMON_C_O) |
|
15 |
+ |
|
16 |
+$(COMMON_C_O): $(COMMON_C) $(HEADERS) |
|
17 |
+ $(CC) $(CFLAGS) -c $(COMMON_C) |
|
18 |
+ |
|
19 |
+clean: |
|
20 |
+ rm -f *.o $(PROG) |
... | ... |
@@ -0,0 +1,337 @@ |
1 |
+/**************************************************************************** |
|
2 |
+ * |
|
3 |
+ * PROGRAM: forestfragment |
|
4 |
+ * |
|
5 |
+ * AUTHOR(S): Pawel Netzel - URK |
|
6 |
+ * |
|
7 |
+ * PURPOSE: Program calculates multiscale fores fragmentation measure |
|
8 |
+ * with using Jensen-Shannon Similarity measuring similarity |
|
9 |
+ * to fully forested areas. |
|
10 |
+ * |
|
11 |
+ * COPYRIGHT: (C) 2023 by Pawel Netzel |
|
12 |
+ * |
|
13 |
+ * This program is a free software under the GNU General Public |
|
14 |
+ * License (>=v2). |
|
15 |
+ * |
|
16 |
+ ***************************************************************************/ |
|
17 |
+ |
|
18 |
+#include <stdlib.h> |
|
19 |
+#include <string.h> |
|
20 |
+#include <omp.h> |
|
21 |
+#include <gdal.h> |
|
22 |
+#include <ezgdal.h> |
|
23 |
+#include <argtable3.h> |
|
24 |
+ |
|
25 |
+/**** |
|
26 |
+ * The procedure displays program usage |
|
27 |
+ * and returns to the system |
|
28 |
+ */ |
|
29 |
+void usage(char *progname, void *argtable) { |
|
30 |
+ |
|
31 |
+ printf("\nplForestFragment calculates forest fragmentation index based on \n"); |
|
32 |
+ printf("similarity approach.\n"); |
|
33 |
+ printf("It can calculate the index for one scale. It can also produce \n"); |
|
34 |
+ printf("the multiscale fragmentation index. \n"); |
|
35 |
+ printf("plForestFragment takes a categorical raster layer as an input.\n"); |
|
36 |
+ printf("A user can point a category that represents the forest class.\n"); |
|
37 |
+ |
|
38 |
+ printf("\nUsage:\n\t%s", progname); |
|
39 |
+ arg_print_syntax(stdout,argtable,"\n"); |
|
40 |
+ printf("\n"); |
|
41 |
+ arg_print_glossary_gnu(stdout,argtable); |
|
42 |
+ printf("\n"); |
|
43 |
+ exit(0); |
|
44 |
+} |
|
45 |
+ |
|
46 |
+/**** |
|
47 |
+ * Comparator to sort radiuses |
|
48 |
+ */ |
|
49 |
+int compare(const void* numA, const void* numB) |
|
50 |
+{ |
|
51 |
+ const int* num1 = (const int*)numA; |
|
52 |
+ const int* num2 = (const int*)numB; |
|
53 |
+ |
|
54 |
+ if (*num1 > *num2) { |
|
55 |
+ return 1; |
|
56 |
+ } |
|
57 |
+ else { |
|
58 |
+ if (*num1 == *num2) |
|
59 |
+ return 0; |
|
60 |
+ else |
|
61 |
+ return -1; |
|
62 |
+ } |
|
63 |
+} |
|
64 |
+ |
|
65 |
+/**** |
|
66 |
+ * Calculating index in the coocurence matrix |
|
67 |
+ * based on values of categories |
|
68 |
+ */ |
|
69 |
+int calc_cooc(double v1, double v2, double forest) { |
|
70 |
+ if(v1==forest && v2==forest) return 0; |
|
71 |
+ else if(v1==forest) return 1; |
|
72 |
+ else if(v2==forest) return 2; |
|
73 |
+ else return 3; |
|
74 |
+} |
|
75 |
+ |
|
76 |
+/**** |
|
77 |
+ * Creating multiscale matrix that will be used as a mask |
|
78 |
+ * to get circular neighbourhood |
|
79 |
+ */ |
|
80 |
+int** create_mask(int* rad, int count) { |
|
81 |
+ int radius, radius_max=rad[count-1]; |
|
82 |
+ int size = 2*radius_max+1; |
|
83 |
+ int i, r, c; |
|
84 |
+ int** mask = (int**)malloc(size*sizeof(int*)); |
|
85 |
+ double x, y; |
|
86 |
+ |
|
87 |
+ for(i=0; i<size; i++) |
|
88 |
+ mask[i] = (int*)calloc(size, sizeof(int)); |
|
89 |
+ for(r=0; r<size; r++) { |
|
90 |
+ y = pow(r - radius_max, 2); |
|
91 |
+ for(c=0; c<size; c++) { |
|
92 |
+ x = pow(c - radius_max, 2); |
|
93 |
+ if(sqrt(x+y)<=radius_max) |
|
94 |
+ mask[r][c] = count-1; |
|
95 |
+ else |
|
96 |
+ mask[r][c] = count+1; |
|
97 |
+ } |
|
98 |
+ } |
|
99 |
+ for(i=count-2; i>-1; i--) { |
|
100 |
+ radius = rad[i]; |
|
101 |
+ for(r=0; r<size; r++) { |
|
102 |
+ y = pow(r - radius_max, 2); |
|
103 |
+ for(c=0; c<size; c++) { |
|
104 |
+ x = pow(c - radius_max, 2); |
|
105 |
+ if(sqrt(x+y)<=radius) |
|
106 |
+ mask[r][c] = i; |
|
107 |
+ } |
|
108 |
+ } |
|
109 |
+ } |
|
110 |
+ return mask; |
|
111 |
+} |
|
112 |
+ |
|
113 |
+void free_mask(int** mask, int size) { |
|
114 |
+ int i; |
|
115 |
+ for(i=0; i<size; i++) free(mask[i]); |
|
116 |
+ free(mask); |
|
117 |
+} |
|
118 |
+ |
|
119 |
+/**** |
|
120 |
+ * The function calculates Jensen-Shannon Similarity - JSS |
|
121 |
+ * between compund coocurence matrix and reference matrix |
|
122 |
+ */ |
|
123 |
+double calc_similarity(int** cooc, int count, double weight, double* reference) { |
|
124 |
+ int i, j, pos=0; |
|
125 |
+ double tsum=0.0, sum, M=0.0, HM=0.0, HA=0.0, HB=0.0; |
|
126 |
+ double* norm =(double*)calloc(count*4,sizeof(double)); |
|
127 |
+ // normalize |
|
128 |
+ for(i=0; i<count; i++) { |
|
129 |
+ sum = 0.0; |
|
130 |
+ for(j=0; j<4; j++) |
|
131 |
+ sum += cooc[i][j]; |
|
132 |
+ tsum += sum; |
|
133 |
+ sum /= weight; |
|
134 |
+ for(j=0; j<4; j++) |
|
135 |
+ if(sum>0) |
|
136 |
+ norm[pos++] = (double)cooc[i][j]/sum; |
|
137 |
+ } |
|
138 |
+ if(tsum==0.0) return 0.0; |
|
139 |
+ for(i=0; i<count*4; i++) { |
|
140 |
+ M = 0.5*(norm[i] + reference[i]); |
|
141 |
+ HM -= (M>0.0)?M*log2(M):0.0; |
|
142 |
+ HA -= (norm[i]>0)?norm[i]*log2(norm[i]):0.0; |
|
143 |
+ HB -= (reference[i]>0.0)?reference[i]*log2(reference[i]):0.0; |
|
144 |
+ } |
|
145 |
+ free(norm); |
|
146 |
+ return 1.0-sqrt(HM-0.5*(HA+HB)); |
|
147 |
+} |
|
148 |
+ |
|
149 |
+/**** |
|
150 |
+ * Main calculation procedure |
|
151 |
+ * It fills output layer with calculated similarities. |
|
152 |
+ * The procedure uses one or more scales defines by |
|
153 |
+ * variables: count and rad |
|
154 |
+ */ |
|
155 |
+void calc_forest_fragmentation(EZGDAL_LAYER* in_fd, double forest, |
|
156 |
+ EZGDAL_LAYER* out_fd, |
|
157 |
+ int* rad, int count) { |
|
158 |
+ EZGDAL_STRIPE* stripe = in_fd->stripe; |
|
159 |
+ int layer_cols = in_fd->cols, layer_rows = in_fd->rows; |
|
160 |
+ double* buffer = out_fd->buffer; |
|
161 |
+ double in_no_data = in_fd->no_data; |
|
162 |
+ double out_no_data = out_fd->no_data; |
|
163 |
+ double weight = 1.0/(double)count; |
|
164 |
+ int i, row, col; |
|
165 |
+ int radius_max = rad[count-1]; |
|
166 |
+ double* reference = (double*)calloc(count*4,sizeof(double)); |
|
167 |
+ int** mask = create_mask(rad, count); |
|
168 |
+ |
|
169 |
+ for(i=0; i<count; i++) |
|
170 |
+ reference[i*4] = weight; |
|
171 |
+ |
|
172 |
+ ezgdal_show_message_nolf(stdout,"Calculations... 0%"); |
|
173 |
+ /**** |
|
174 |
+ * For each row of input raster |
|
175 |
+ */ |
|
176 |
+ for(row=0; row<layer_rows; row++) { |
|
177 |
+ ezgdal_load_stripe_data(stripe, row-radius_max); |
|
178 |
+ ezgdal_show_progress(stderr,row,layer_rows); |
|
179 |
+ /**** |
|
180 |
+ * For each cell int the row - parallel processing |
|
181 |
+ */ |
|
182 |
+#pragma omp parallel for default(shared) schedule(dynamic, 100) |
|
183 |
+ for(col=0; col<layer_cols; col++) { |
|
184 |
+ int i, c, r; |
|
185 |
+ EZGDAL_FRAME* frame = &(stripe->frame[col]); |
|
186 |
+ double** frame_buffer = frame->buffer; |
|
187 |
+ if(frame_buffer[radius_max][radius_max]!=in_no_data) { |
|
188 |
+ int** cooc = (int **)malloc(count*sizeof(int*)); |
|
189 |
+ for(i=0; i<count; i++) |
|
190 |
+ cooc[i] = (int*)calloc(4, sizeof(int)); |
|
191 |
+ /**** |
|
192 |
+ * For each scale |
|
193 |
+ */ |
|
194 |
+ for(i=0; i<count; i++) { |
|
195 |
+ int radius = rad[i]; |
|
196 |
+ int* cooc_scale = cooc[i]; |
|
197 |
+ /**** |
|
198 |
+ * Calculation of the coocurence matrix |
|
199 |
+ */ |
|
200 |
+ for(r=radius_max-radius; r<radius_max+radius; r++) { |
|
201 |
+ for(c=radius_max-radius; c<radius_max+radius; c++) { |
|
202 |
+ if(mask[r][c]<=i) { |
|
203 |
+ double v = frame_buffer[r][c]; |
|
204 |
+ if(v!=in_no_data) { |
|
205 |
+ double v_right = frame_buffer[r][c+1]; |
|
206 |
+ double v_down = frame_buffer[r+1][c]; |
|
207 |
+ if(v_right!=in_no_data) |
|
208 |
+ cooc_scale[calc_cooc(v, v_right, forest)]++; |
|
209 |
+ if(v_down!=in_no_data) |
|
210 |
+ cooc_scale[calc_cooc(v, v_down, forest)]++; |
|
211 |
+ } |
|
212 |
+ } |
|
213 |
+ } |
|
214 |
+ } |
|
215 |
+ } |
|
216 |
+ /**** |
|
217 |
+ * Calculate of similarity based on counpound coocurence matrix |
|
218 |
+ */ |
|
219 |
+ buffer[col] = calc_similarity(cooc, count, weight, reference); |
|
220 |
+ for(i=0; i<count; i++) |
|
221 |
+ free(cooc[i]); |
|
222 |
+ free(cooc); |
|
223 |
+ } else |
|
224 |
+ buffer[col] = out_no_data; |
|
225 |
+ } |
|
226 |
+ ezgdal_write_buffer(out_fd, row); |
|
227 |
+ } |
|
228 |
+ ezgdal_show_progress(stderr,row,layer_rows); |
|
229 |
+ |
|
230 |
+ free(reference); |
|
231 |
+ free_mask(mask, 2*radius_max+1); |
|
232 |
+} |
|
233 |
+ |
|
234 |
+ |
|
235 |
+int main(int argc, char *argv[]) |
|
236 |
+{ |
|
237 |
+ EZGDAL_LAYER *in_fd, *out_fd; |
|
238 |
+ char *OUTPUT; |
|
239 |
+ char *INPUT; |
|
240 |
+ double forest = 1.0; |
|
241 |
+ |
|
242 |
+ struct arg_str *inp = arg_str1("i","input","<file_name>","name of input files (GeoTIFF)"); |
|
243 |
+ struct arg_dbl *frst = arg_dbl0("f","forest-value","<dbl>","value for forest (default: 1.0)"); |
|
244 |
+ struct arg_str *out = arg_str1("o","output","<file_name>","name of output file with forest fragmentation index (JSS) (GeoTIFF)"); |
|
245 |
+ struct arg_int *rad = arg_intn("r","radius","<n>",1,8,"radius(es) in cells defining scale(s)"); |
|
246 |
+ struct arg_int *th = arg_int0("t",NULL,"<n>","number of threads (default: 1)"); |
|
247 |
+ struct arg_lit *help = arg_lit0("h","help","print help and exit"); |
|
248 |
+ struct arg_end *end = arg_end(20); |
|
249 |
+ |
|
250 |
+ void* argtable[] = {inp,frst,out,rad,th,help,end}; |
|
251 |
+ |
|
252 |
+ int nerrors = arg_parse(argc,argv,argtable); |
|
253 |
+ |
|
254 |
+ if (help->count > 0) |
|
255 |
+ usage(argv[0],argtable); |
|
256 |
+ |
|
257 |
+ if (nerrors > 0) { |
|
258 |
+ arg_print_errors(stdout,end,argv[0]); |
|
259 |
+ usage(argv[0],argtable); |
|
260 |
+ } |
|
261 |
+ |
|
262 |
+ /**** |
|
263 |
+ * Setting the number of threads for OpenMP - default 1 |
|
264 |
+ */ |
|
265 |
+ if(th->count > 0) |
|
266 |
+ omp_set_num_threads(th->ival[0]); |
|
267 |
+ else |
|
268 |
+ omp_set_num_threads(1); |
|
269 |
+ |
|
270 |
+ /**** |
|
271 |
+ * Setting the value for forested cells - default 1 |
|
272 |
+ */ |
|
273 |
+ if(frst->count > 0) |
|
274 |
+ forest = frst->dval[0]; |
|
275 |
+ |
|
276 |
+ INPUT = (char *)inp->sval[0]; |
|
277 |
+ OUTPUT = (char *)out->sval[0]; |
|
278 |
+ |
|
279 |
+ /**** |
|
280 |
+ * Sorting radiuses from the smallest to the largest |
|
281 |
+ */ |
|
282 |
+ qsort(rad->ival, rad->count, sizeof(int), compare); |
|
283 |
+ |
|
284 |
+ /**** |
|
285 |
+ * Opening input raster layer |
|
286 |
+ */ |
|
287 |
+ if(!ezgdal_file_exists(INPUT)) { |
|
288 |
+ printf("\nThe file '%s' does not exist!\n\n",INPUT); |
|
289 |
+ exit(0); |
|
290 |
+ } |
|
291 |
+ |
|
292 |
+ ezgdal_show_message_nolf(stdout,"Opening input file..."); |
|
293 |
+ in_fd = ezgdal_open_layer(INPUT); |
|
294 |
+ |
|
295 |
+ if(in_fd==NULL) { |
|
296 |
+ printf("\nThe file '%s' can't be opened!\n\n",INPUT); |
|
297 |
+ exit(0); |
|
298 |
+ } else |
|
299 |
+ ezgdal_show_message(stdout," OK"); |
|
300 |
+ |
|
301 |
+ ezgdal_create_stripe(in_fd, -rad->ival[rad->count-1], 2*rad->ival[rad->count-1]+1); |
|
302 |
+ ezgdal_create_all_frames(in_fd->stripe, -rad->ival[rad->count-1], 1); |
|
303 |
+ |
|
304 |
+ /**** |
|
305 |
+ * Creating output raster layer |
|
306 |
+ */ |
|
307 |
+ int nodata = -9999; |
|
308 |
+ ezgdal_show_message_nolf(stdout,"Creating output file..."); |
|
309 |
+ out_fd = ezgdal_create_layer(OUTPUT, |
|
310 |
+ ezgdal_layer_get_wkt(in_fd), |
|
311 |
+ "Float32", |
|
312 |
+ ezgdal_layer_get_at(in_fd), |
|
313 |
+ in_fd->rows, |
|
314 |
+ in_fd->cols, |
|
315 |
+ &nodata, EZGDAL_COMPRESS_DEFLATE |
|
316 |
+ ); |
|
317 |
+ if(out_fd==NULL) { |
|
318 |
+ printf("\nThe file '%s' can't be created!\n\n",OUTPUT); |
|
319 |
+ exit(0); |
|
320 |
+ } else |
|
321 |
+ ezgdal_show_message(stdout," OK"); |
|
322 |
+ |
|
323 |
+ /**** |
|
324 |
+ * Calculation of the forest fragmentation measure |
|
325 |
+ */ |
|
326 |
+ calc_forest_fragmentation(in_fd, forest, out_fd, rad->ival, rad->count); |
|
327 |
+ |
|
328 |
+ |
|
329 |
+ /**** |
|
330 |
+ * Cleaning up and closing files |
|
331 |
+ */ |
|
332 |
+ ezgdal_close_layer(out_fd); |
|
333 |
+ ezgdal_close_layer(in_fd); |
|
334 |
+ arg_free(argtable); |
|
335 |
+ |
|
336 |
+ exit(EXIT_SUCCESS); |
|
337 |
+} |
|
0 | 338 |