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

Add anisotropic coefficient of variation #143

Open
wants to merge 59 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
41e799a
Add filters
Teschl Sep 9, 2024
668d658
Add acv
Teschl Oct 16, 2024
558ddc2
Format acv into one for loop
Teschl Oct 18, 2024
49bb6c2
Add omp support
Teschl Oct 18, 2024
369568a
Add author
Teschl Oct 22, 2024
f40499d
Complete acv documentation
Teschl Oct 22, 2024
b4bae4f
Add acv to compile list
Teschl Oct 23, 2024
34d35c8
Add debug statements
Teschl Oct 23, 2024
c1333f7
Add more debug statements
Teschl Oct 23, 2024
7c389e9
Change debug statements
Teschl Oct 23, 2024
cbd8fda
Modify kernel loop
Teschl Oct 23, 2024
6b12d88
Adjust kernel loop
Teschl Oct 23, 2024
8319266
Update kernel
Teschl Oct 23, 2024
1870534
Update
Teschl Oct 23, 2024
15fb2e5
Fis missing sum reset
Teschl Oct 23, 2024
2a97b43
Add test filter
Teschl Oct 29, 2024
3c1d013
Fix acv parameters
Teschl Oct 29, 2024
986b790
Add debug statements
Teschl Oct 29, 2024
1ec6a33
Change debug
Teschl Oct 29, 2024
8e69844
Remove debug statements
Teschl Oct 29, 2024
f4dae54
Add use_mp to docs
Teschl Oct 29, 2024
ca6e422
Add missing test ;
Teschl Oct 29, 2024
170059d
Fix clang format
Teschl Oct 29, 2024
9703019
Fix powf() usage
Teschl Oct 29, 2024
1cfb6fa
Rework acv to use col major order
Teschl Nov 13, 2024
cabd8b5
Fix comment
Teschl Nov 13, 2024
e0106b7
First boundary solution try
Teschl Nov 14, 2024
8c0471d
Merge branch 'TopoToolbox:main' into acv
Teschl Nov 14, 2024
36bc6bb
Merge branch 'TopoToolbox:main' into acv
Teschl Nov 19, 2024
dcdf93c
switch to two for loops
Teschl Nov 19, 2024
9bee792
Move acv out of graphflood section
Teschl Nov 19, 2024
5178790
Add stdio.h for testing
Teschl Nov 19, 2024
6847ae4
Fix unused variables
Teschl Nov 19, 2024
05b5bd8
Fix wrong iterator in for loop
Teschl Nov 19, 2024
37a0e11
Change testing
Teschl Nov 19, 2024
26e493a
Fix wrong continue condition
Teschl Nov 19, 2024
4da2138
Change test printf's
Teschl Nov 19, 2024
cbfde38
Revert kernel application
Teschl Nov 19, 2024
405ce1c
Change debug comments
Teschl Nov 19, 2024
e10c8a1
Change debug prints
Teschl Nov 19, 2024
e0c8b13
Fix k_index computation
Teschl Nov 20, 2024
809c35e
Fix k5_row usage
Teschl Nov 20, 2024
1aa5244
Add test printf's
Teschl Nov 20, 2024
b495098
Remove omp while using breaks
Teschl Nov 20, 2024
cb4a6e4
Add new printf's
Teschl Nov 20, 2024
e2d97af
Fix last element of for loops
Teschl Nov 20, 2024
49322b6
Acv in working condition
Teschl Nov 20, 2024
3b6e816
Remove unused code
Teschl Nov 20, 2024
e03e702
Fix dimension of last filter
Teschl Nov 20, 2024
f02a413
Fix wrong number of filters issue
Teschl Nov 20, 2024
06e05e8
Merge branch 'TopoToolbox:main' into acv
Teschl Nov 20, 2024
c2f6ae4
Add isnan test for filter 1
Teschl Nov 21, 2024
7744123
Add isnan test to all filters
Teschl Nov 21, 2024
85319a6
Merge branch 'TopoToolbox:main' into acv
Teschl Nov 26, 2024
7767846
Fix true_index order
Teschl Nov 26, 2024
622b165
Add snapshot acv test
Teschl Nov 26, 2024
f0cb3aa
bump snapshot version
Teschl Nov 26, 2024
1a7cb83
Change do while to simple while loop
Teschl Nov 27, 2024
c647cb9
Fix skip if filter is zero
Teschl Nov 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
debug-build:
runs-on: ubuntu-latest
env:
snapshot_version: v1.3.0
snapshot_version: v1.4.0
steps:
- uses: actions/checkout@v4
with:
Expand Down
27 changes: 27 additions & 0 deletions include/topotoolbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,32 @@ void streamquad_trapz_f64(double *integral, double *integrand,
ptrdiff_t *source, ptrdiff_t *target, float *weight,
ptrdiff_t edge_count);

/**
@brief The anisotropic coefficient of variation (ACV) describes the general
geometry of the local land surface and can be used to destinguish elongated
from oval land forms.

@param[out] output: The computed anisotropic of variation (ACV)
@parblock
A pointer to a `float` array of size `dims[0]` x `dims[1]`
@endparblock

@param[in] dem: The input digital elevation model
@parblock
A pointer to a `float` array of size `dims[0]` x `dims[1]`
@endparblock

@param[in] use_mp: If 1 then multiprocessing will be used to compute result

@param[in] dims: The horizontal dimensions of the arrays
@parblock
A pointer to a `ptrdiff_t` array of size 2
@endparblock

*/
TOPOTOOLBOX_API
void acv(float *output, float *dem, int use_mp, ptrdiff_t dims[2]);

/*
Graphflood
*/
Expand Down Expand Up @@ -1028,4 +1054,5 @@ void graphflood_full(GF_FLOAT *Z, GF_FLOAT *hw, uint8_t *BCs,
GF_FLOAT *Precipitations, GF_FLOAT *manning, GF_UINT *dim,
GF_FLOAT dt, GF_FLOAT dx, bool SFD, bool D8,
GF_UINT N_iterations, GF_FLOAT step);

#endif // TOPOTOOLBOX_H
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_library(topotoolbox
identifyflats.c
gwdt.c
gradient8.c
acv.c
morphology/reconstruct.c
priority_queue.c
excesstopography.c
Expand Down
265 changes: 265 additions & 0 deletions src/acv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
#define TOPOTOOLBOX_BUILD

#include <math.h>
#include <stddef.h>
#include <stdint.h>

#if TOPOTOOLBOX_OPENMP_VERSION > 0
#include <omp.h>
#endif

#include "topotoolbox.h"

/*
The anisotropic coefficient of variation (ACV) describes the general
geometry of the local land surface and can be used to destinguish elongated
from oval land forms.

output: Empty (or filled with zeros), same shape as dem
dem: DEM matrix, passed from python in order='C'
use_mp: If 1 then multiprocessing will be used to compute result
dims[2]: dimensions of DEM

References:
Olaya, V. 2009: Basic land-surface parameters. In: Geomorphometry.
Concepts, Software, Applications, Hengl, T. & Reuter, H. I. (Eds.),
Elsevier, 33, 141-169.

Author:
Theophil Bringezu (theophil.bringezu[at]uni-potsdam.de)

Original MATLAB version by:
Wolfgang Schwanghart (w.schwanghart[at]geo.uni-potsdam.de)

*/

TOPOTOOLBOX_API
void acv(float *output, float *dem, int use_mp, ptrdiff_t dims[2]) {
/*
filter_1 :
{{ 1, 0, 1, 0, 1},
{ 0, 0, 0, 0, 0},
{ 1, 0, 0, 0, -1},
{ 0, 0, 0, 0, 0},
{-1, 0, -1, 0, -1}}
*/
float filter_1[25] = {1, 0, 1, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0,
0, -1, 0, 0, 0, 0, 0, 1, 0, -1, 0, -1};
/* filter_2
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{1, 0, 0, 0, -1},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}},

{{1, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, -1}},

{{0, 0, -1, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, -1, 0, 0}},

{{0, 0, 0, 0, -1},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{1, 0, 0, 0, 0}}}
*/
float filter_2[4][25] = {{
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0,
},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1},
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0,
0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0}};
/*
filter_3
{{{ 0, 0, 0},
{ 1, 0, -1},
{ 0, 0, 0}},

{{ 1, 0, 0},
{ 0, 0, 0},
{ 0, 0, -1}},

{{ 0, 1, 0},
{ 0, 0, 0},
{ 0, -1, 0}},

{{ 0, 0, 1},
{ 0, 0, 0},
{-1, 0, 0}}};
*/
float filter_3[4][9] = {{0, 1, 0, 0, 0, 0, 0, -1, 0},
{1, 0, 0, 0, 0, 0, 0, 0, -1},
{0, 0, 0, 1, 0, -1, 0, 0, 0},
{0, 0, -1, 0, 0, 0, 1, 0, 0}};

/*{{21, 15, 10, 17, 23},
{13, 5, 2, 7, 19},
{ 9, 1, 0, 4, 12},
{14, 6, 3, 8, 20},
{22, 16, 11, 18, 24}};
search_order = {{row_shift, col_shift}, ...}
*/
ptrdiff_t search_order[25][2] = {
{0, 0}, {0, -1}, {-1, 0}, {1, 0}, {0, 1}, // 0 to 4
{-1, -1}, {1, -1}, {-1, 1}, {1, 1}, // 5 to 8
{0, -2}, {-2, 0}, {2, 0}, {0, 2}, // 9 to 12
{-1, -2}, {1, -2}, {-2, -1}, {2, -1}, // 13 to 16
{-2, 1}, {2, 1}, {-1, 2}, {1, 2}, // 17 to 20
{-2, -2}, {2, -2}, {-2, 2}, {2, 2} // 21 to 24
};

// ACV:
#if TOPOTOOLBOX_OPENMP_VERSION < 30
ptrdiff_t col;
#pragma omp parallel for if (use_mp)
for (col = 0; col < dims[1]; col++) {
for (ptrdiff_t row = 0; row < dims[0]; row++) {
#else
ptrdiff_t col, row;
#pragma omp parallel for collapse(2) if (use_mp)
for (col = 0; col < dims[1]; col++) {
for (row = 0; row < dims[0]; row++) {
#endif

// Catch NaN cells and skip them
ptrdiff_t index = col * dims[0] + row;
if (isnan(dem[index])) continue;

float sum = 0.0f;
float dz_avg = 0.0f;
float anisotropic_cov = 0.0f;

// filter_1
for (ptrdiff_t k_col = -2; k_col <= 2; k_col++) {
for (ptrdiff_t k_row = -2; k_row <= 2; k_row++) {
// if filter cell is zero skip this filter cell (sum += 0.0f)
ptrdiff_t k_index = (k_col + 2) * 5 + (k_row + 2);
if (filter_1[k_index] == 0.0f) continue;

ptrdiff_t true_row, true_col, true_index, search_pos;
int out_of_bounds;
// If out of bounds or isnan find nearest replacement value using
// Euclidean distance transform. (search_order[25][2])
search_pos = 0;
while (true) {
true_col = col + k_col + search_order[search_pos][1];
true_row = row + k_row + search_order[search_pos][0];
out_of_bounds = (true_row < 0 || true_row >= dims[0] ||
true_col < 0 || true_col >= dims[1]);
if (out_of_bounds) {
search_pos++;
continue;
}
true_index = true_col * dims[0] + true_row;
if (isnan(dem[true_index])) {
search_pos++;
continue;
}
// valid position found. While(true) loop will terminate because
// cell at `index` is valid.
break;
}
sum += filter_1[k_index] * dem[true_index];
}
}
// dz_AVG = conv2(dem,k,'valid')/4;
dz_avg = sum / 4.0f;

// filter_2
for (ptrdiff_t n = 0; n < 4; n++) {
sum = 0.0f;
for (ptrdiff_t k_col = -2; k_col <= 2; k_col++) {
for (ptrdiff_t k_row = -2; k_row <= 2; k_row++) {
// if filter cell is zero skip this filter cell
ptrdiff_t k_index = (k_col + 2) * 5 + (k_row + 2);
if (filter_2[n][k_index] == 0.0f) continue;

ptrdiff_t true_row, true_col, true_index, search_pos;
int out_of_bounds;
// If out of bounds or isnan find nearest replacement value using
// Euclidean distance transform. (search_order[25][2])
search_pos = 0;
while (true) {
true_col = col + k_col + search_order[search_pos][1];
true_row = row + k_row + search_order[search_pos][0];
out_of_bounds = (true_row < 0 || true_row >= dims[0] ||
true_col < 0 || true_col >= dims[1]);
if (out_of_bounds) {
search_pos++;
continue;
}
true_index = true_col * dims[0] + true_row;
if (isnan(dem[true_index])) {
search_pos++;
continue;
}
// valid position found. While(true) loop will terminate because
// cell at `index` is valid.
break;
}
sum += filter_2[n][k_index] * dem[true_index];
}
}
// ACV = ACV + (conv2(dem,F{r},'valid') - dz_AVG).^2;
anisotropic_cov += powf(sum - dz_avg, 2.0f);
}

// filter_3
for (ptrdiff_t n = 0; n < 4; n++) {
sum = 0.0f;
for (ptrdiff_t k_col = -1; k_col <= 1; k_col++) {
for (ptrdiff_t k_row = -1; k_row <= 1; k_row++) {
// if filter cell is zero skip this filter cell
ptrdiff_t k_index = (k_col + 1) * 3 + (k_row + 1);
if (filter_3[n][k_index] == 0.0f) continue;

ptrdiff_t true_row, true_col, true_index, search_pos;
int out_of_bounds;
// If out of bounds or isnan find nearest replacement value using
// Euclidean distance transform. (search_order[25][2])
search_pos = 0;
while (true) {
true_col = col + k_col + search_order[search_pos][1];
true_row = row + k_row + search_order[search_pos][0];
out_of_bounds = (true_row < 0 || true_row >= dims[0] ||
true_col < 0 || true_col >= dims[1]);
if (out_of_bounds) {
search_pos++;
continue;
}
true_index = true_col * dims[0] + true_row;
if (isnan(dem[true_index])) {
search_pos++;
continue;
}
// valid position found. While(true) loop will terminate because
// cell at `index` is valid.
break;
}
sum += filter_3[n][k_index] * dem[true_index];
}
}
// ACV = ACV + (conv2(dem,F{r},'valid') - dz_AVG).^2;
anisotropic_cov += powf(sum - dz_avg, 2.0f);
}
// dz_AVG = max(abs(dz_AVG),0.001);
dz_avg = fmaxf(0.001f, fabsf(dz_avg));

// C = log(1 + sqrt(ACV./8)./dz_AVG);
output[index] = logf(1.0f + sqrtf(anisotropic_cov / 8.0f) / dz_avg);
}
}
}
4 changes: 4 additions & 0 deletions test/random_dem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ int32_t test_gradient8_mp(float *gradient, float *gradient_mp,
}
return 0;
}
/*

*/
int32_t test_acv() { return 0; }
/*
Flow direction should point downstream or across flats
*/
Expand Down
Loading
Loading