Skip to content

Commit 4fa6067

Browse files
committed
Working landmark_2_point and point_2_landmark test
1 parent 268ef31 commit 4fa6067

File tree

9 files changed

+116
-67
lines changed

9 files changed

+116
-67
lines changed

scripts/python/landmark_tools/landmark.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ def save_legacy_little_endian(self, lmk_file, lat=0.0, lon=0.0, radius=0.0):
218218
fp.write(file_data)
219219

220220

221-
222221
def __eq__(self, other):
223222
if isinstance(other, Landmark):
224223
return self.BODY == other.BODY and \
@@ -233,7 +232,22 @@ def __eq__(self, other):
233232
np.allclose(self.srm, other.srm) and \
234233
np.allclose(self.ele, other.ele)
235234
return NotImplemented
236-
235+
236+
def approx_equal(self, other, elevation_tolerance):
237+
if isinstance(other, Landmark):
238+
return self.BODY == other.BODY and \
239+
self.lmk_id == other.lmk_id and \
240+
self.num_cols == other.num_cols and \
241+
self.num_rows == other.num_rows and \
242+
self.anchor_col == other.anchor_col and \
243+
self.anchor_row == other.anchor_row and \
244+
self.resolution == other.resolution and \
245+
np.allclose(self.anchor_point, other.anchor_point) and \
246+
np.allclose(self.mapRworld, other.mapRworld, rtol=0, atol=1e-4) and \
247+
np.allclose(self.srm, other.srm) and \
248+
np.allclose(self.ele, other.ele, rtol=0, atol=elevation_tolerance)
249+
return NotImplemented
250+
237251
def assess_equality(self, other):
238252
if self == other :
239253
print("Objects are equal")

src/landmark_tools/landmark_util/point_cloud2grid.c

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,27 @@ static double *pts_array = 0;
3131
static uint8_t *bv_array = 0;
3232

3333

34-
bool point2lmk( double *pts, uint8_t *bv, size_t num_pts, LMK *lmk, enum PointFrame frame)
34+
bool point2lmk( double *pts, uint8_t *bv, size_t num_pts, LMK *lmk, enum PointFrame frame, bool smooth)
3535
{
3636
float* weight_map = (float *)malloc(sizeof(float)*lmk->num_cols*lmk->num_rows);
3737
float* srm1 = (float *)malloc(sizeof(float)*lmk->num_cols*lmk->num_rows);
3838
float* ele1 = (float *)malloc(sizeof(float)*lmk->num_cols*lmk->num_rows);
39+
float* nearest = (float *)malloc(sizeof(float)*lmk->num_cols*lmk->num_rows);
3940

40-
if(weight_map == NULL || srm1 == NULL || ele1 == NULL){
41+
if(weight_map == NULL || srm1 == NULL || ele1 == NULL || nearest == NULL){
4142
if(weight_map != NULL) free(weight_map);
4243
if(srm1 != NULL) free(srm1);
4344
if(ele1 != NULL) free(ele1);
45+
if(nearest != NULL) free(nearest);
4446
return false;
4547
}
4648

4749
memset(weight_map, 0, sizeof(float)*lmk->num_cols*lmk->num_rows);
4850
memset(srm1, 0, sizeof(float)*lmk->num_cols*lmk->num_rows);
4951
memset(ele1, 0, sizeof(float)*lmk->num_cols*lmk->num_rows);
52+
for(int32_t i = 0; i < num_pts; ++i){
53+
nearest[i] = MAXFLOAT;
54+
}
5055

5156
for(int32_t i = 0; i < num_pts; ++i)
5257
{
@@ -73,24 +78,34 @@ bool point2lmk( double *pts, uint8_t *bv, size_t num_pts, LMK *lmk, enum PointFr
7378

7479
if(ix >= 0 && iy >= 0 && ix < lmk->num_cols && iy < lmk->num_rows)
7580
{
76-
int32_t cmin = ix - 4;
77-
int32_t cmax = ix + 4;
78-
if(cmin < 0) cmin = 0;
79-
if(cmax >= lmk->num_cols) cmax =lmk->num_cols-1;
80-
81-
int32_t rmin = iy - 4;
82-
int32_t rmax = iy + 4;
83-
if(rmin < 0) rmin = 0;
84-
if(rmax >= lmk->num_rows) rmax = lmk->num_rows-1;
85-
for(int32_t m = rmin; m <= rmax; ++m)
86-
{
87-
for(int32_t n = cmin; n <= cmax; ++n)
81+
if(smooth){
82+
int32_t cmin = ix - 4;
83+
int32_t cmax = ix + 4;
84+
if(cmin < 0) cmin = 0;
85+
if(cmax >= lmk->num_cols) cmax =lmk->num_cols-1;
86+
87+
int32_t rmin = iy - 4;
88+
int32_t rmax = iy + 4;
89+
if(rmin < 0) rmin = 0;
90+
if(rmax >= lmk->num_rows) rmax = lmk->num_rows-1;
91+
for(int32_t m = rmin; m <= rmax; ++m)
8892
{
89-
double d = 2.0*sqrt((m- y)*(m-y) + (n- x)*(n-x));
90-
double wt = exp(-d);
91-
ele1[m*lmk->num_cols + n] += wt*ele;
92-
srm1[m*lmk->num_cols + n] +=wt*(double)bb;
93-
weight_map[m*lmk->num_cols + n] +=wt;
93+
for(int32_t n = cmin; n <= cmax; ++n)
94+
{
95+
double d = 2.0*sqrt((m- y)*(m-y) + (n- x)*(n-x));
96+
double wt = exp(-d);
97+
ele1[m*lmk->num_cols + n] += wt*ele;
98+
srm1[m*lmk->num_cols + n] +=wt*(double)bb;
99+
weight_map[m*lmk->num_cols + n] +=wt;
100+
}
101+
}
102+
}else{
103+
double d = 2.0*sqrt((iy- y)*(iy-y) + (ix- x)*(ix-x));
104+
if(nearest[iy*lmk->num_cols + ix] > d){
105+
ele1[iy*lmk->num_cols + ix] = ele;
106+
srm1[iy*lmk->num_cols + ix] = bb;
107+
weight_map[iy*lmk->num_cols + ix] = 1;
108+
nearest[iy*lmk->num_cols + ix] = d;
94109
}
95110
}
96111

@@ -118,6 +133,7 @@ bool point2lmk( double *pts, uint8_t *bv, size_t num_pts, LMK *lmk, enum PointFr
118133
if(weight_map != NULL) free(weight_map);
119134
if(srm1 != NULL) free(srm1);
120135
if(ele1 != NULL) free(ele1);
136+
if(nearest != NULL) free(nearest);
121137
return true;
122138
}
123139

@@ -399,9 +415,9 @@ bool Write_LMK_PLY_Facet_Window(const char *filename, LMK *lmk, int32_t x0, int3
399415
}
400416

401417
if (!ply_add_element(oply, "vertex", numpts)) return false;
402-
if (!ply_add_scalar_property(oply, "x", PLY_FLOAT)) return false;
403-
if (!ply_add_scalar_property(oply, "y", PLY_FLOAT)) return false;
404-
if (!ply_add_scalar_property(oply, "z", PLY_FLOAT)) return false;
418+
if (!ply_add_scalar_property(oply, "x", PLY_DOUBLE)) return false;
419+
if (!ply_add_scalar_property(oply, "y", PLY_DOUBLE)) return false;
420+
if (!ply_add_scalar_property(oply, "z", PLY_DOUBLE)) return false;
405421
if (!ply_add_scalar_property(oply, "intensity", PLY_UINT8)) return false;
406422
if (!ply_add_element(oply, "face", num_faces)) return false;
407423
if (!ply_add_list_property(oply, "vertex_indices", PLY_INT32, PLY_INT32)) return false;
@@ -488,9 +504,9 @@ bool Write_LMK_PLY_Points(const char *filename, LMK *lmk, enum e_ply_storage_mod
488504
}
489505

490506
if (!ply_add_element(oply, "vertex", numpts)) return false;
491-
if (!ply_add_scalar_property(oply, "x", PLY_FLOAT)) return false;
492-
if (!ply_add_scalar_property(oply, "y", PLY_FLOAT)) return false;
493-
if (!ply_add_scalar_property(oply, "z", PLY_FLOAT)) return false;
507+
if (!ply_add_scalar_property(oply, "x", PLY_DOUBLE)) return false;
508+
if (!ply_add_scalar_property(oply, "y", PLY_DOUBLE)) return false;
509+
if (!ply_add_scalar_property(oply, "z", PLY_DOUBLE)) return false;
494510
if (!ply_add_scalar_property(oply, "intensity", PLY_UINT8)) return false;
495511
if (!ply_write_header(oply)) return false;
496512

src/landmark_tools/landmark_util/point_cloud2grid.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ enum PointFrame{WORLD, LOCAL, RASTER};
5050
\param[in] bv array of point intensities for each point in `pts`
5151
\param[in] num_pts number of points in pts
5252
\param[in,out] lmk should have complete header at the time of input
53+
\param[in] smooth if true, use inverse distance weighting to calculate elevations on a grid. if false, assign point to nearest raster neighbor.
5354
*/
54-
bool point2lmk( double *pts, uint8_t *bv, size_t num_pts, LMK *lmk, enum PointFrame frame);
55+
bool point2lmk( double *pts, uint8_t *bv, size_t num_pts, LMK *lmk, enum PointFrame frame, bool smooth);
5556

5657
/** \brief Open a .ply file and read the points into an array
5758
* \param[in] plyname filename

src/main/landmark_2_point_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ void show_usage_and_exit()
3939
printf(" -landmark <filename> - input lmkfile\n");
4040
printf(" -ply <filename> - output PLY filepath\n");
4141
printf(" Optional arguments:\n");
42-
printf(" -filetype <PLY_ASCII|PLY_LITTLE_ENDIAN|PLY_BIG_ENDIAN> - (default arch endian)\n");
42+
printf(" -filetype <PLY_ASCII|PLY_LITTLE_ENDIAN|PLY_BIG_ENDIAN> - (default arch endian) warning: ascii is lossy\n");
4343
printf(" -structure <POINTCLOUD|MESH> - (default MESH)\n");
4444
printf(" -frame <WORLD|LOCAL|RASTER> - (default WORLD)\n");
4545
exit(EXIT_FAILURE);

src/main/point_2_landmark_main.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
#include "landmark_tools/landmark_util/point_cloud2grid.h"
3131
#include "landmark_tools/utils/parse_args.h" // for m_getarg
3232
#include "landmark_tools/utils/safe_string.h"
33-
3433
void show_usage_and_exit()
3534
{
3635
printf("Convert point cloud to landmark format.\n");
@@ -45,9 +44,10 @@ void show_usage_and_exit()
4544
printf(" -ele <float> - elevation of center anchor point in meters\n");
4645
printf(" -s <float> - map width in meters\n");
4746
printf(" -sy <float> - map height in meters\n");
48-
printf(" -planet <Moon|Earth|Mars> - (default Moon)\n");
49-
printf(" -filetype <POINT|PLY> - file format of input file (default POINT)\n");
47+
printf(" -planet <Moon|Earth|Mars> \n");
48+
printf(" -filetype <POINT|PLY> - file format of input file. Use PLY if file was created by landmark_2_point. POINT is a legacy format.\n");
5049
printf(" -frame <WORLD|LOCAL|RASTER> - reference frame of the input pointcloud (default WORLD)\n");
50+
printf(" -smooth <true|false> - if true, use inverse distance weighting to calculate elevations on a grid. if false, assign point to nearest raster neighbor. (default true)\n");
5151
exit(EXIT_FAILURE);
5252
}
5353

@@ -59,6 +59,7 @@ int32_t main(int32_t argc, char **argv)
5959
char *filetype_str = NULL;
6060
char *planet_str = NULL;
6161
char *frame_str = NULL;
62+
char *smooth_str = NULL;
6263
float res;
6364
float lat;
6465
float lg;
@@ -85,7 +86,8 @@ int32_t main(int32_t argc, char **argv)
8586
(m_getarg(argv, "-sy", &sizey, CFO_FLOAT)!=1) &&
8687
(m_getarg(argv, "-planet", &planet_str, CFO_STRING)!=1) &&
8788
(m_getarg(argv, "-filetype", &filetype_str, CFO_STRING)!=1) &&
88-
(m_getarg(argv, "-frame", &frame_str, CFO_STRING)!=1))
89+
(m_getarg(argv, "-frame", &frame_str, CFO_STRING)!=1) &&
90+
(m_getarg(argv, "-smooth", &smooth_str, CFO_STRING)!=1))
8991
show_usage_and_exit();
9092

9193
argc-=2;
@@ -121,6 +123,11 @@ int32_t main(int32_t argc, char **argv)
121123
SAFE_PRINTF(256, "Unable to read %s\n", pointfile);
122124
return EXIT_FAILURE;
123125
}
126+
127+
bool smooth = true;
128+
if(smooth_str != NULL && strncmp(smooth_str, "false", 5) == 0){
129+
smooth = false;
130+
}
124131

125132
// Generate lmk header
126133
LMK lmk = {0};
@@ -150,7 +157,7 @@ int32_t main(int32_t argc, char **argv)
150157
calculateDerivedValuesVectors(&lmk);
151158

152159
// Transform points to landmark coordinate frame
153-
bool success = point2lmk(pts, bv, num_pts, &lmk, frame);
160+
bool success = point2lmk(pts, bv, num_pts, &lmk, frame, smooth);
154161
free(pts);
155162
free(bv);
156163
if(!success){

tests/cpp/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ target_link_libraries(landmark_tests
3434
${PNG_LIBRARIES}
3535
${yaml_LIBRARIES}
3636
m
37-
z
3837
)
3938

4039
# Add test to CTest

tests/test_image_comparison.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
def test_image_comparison_regression(tmp_path):
1212
"""Compare the disparity map output of the current code to an archival copy
1313
14-
Using the gold standard image files to compare with so that we are only comparing differences in the comparison code,
15-
not accumulated differences from the image creation, rendering, etc.
14+
TODO Currently testing a tolerance of 5m disparity. Check whether tighter bounds are desired
1615
"""
1716

1817
# Run executables

tests/test_landmark_comparison.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ def test_landmark_comparison_regression(tmp_path):
3434
# Check changes
3535
for key,I1 in displacement_maps1.items():
3636
I2 = displacement_maps2[key]
37-
np.testing.assert_allclose(I1, I2, rtol=0, atol=1)
37+
mask = np.logical_not(np.logical_or(np.isnan(I1), np.isnan(I2)))
38+
np.testing.assert_allclose(I1[mask], I2[mask], rtol=0, atol=1)
3839

3940

4041
def test_landmark_comparison_self(tmp_path):
@@ -62,4 +63,4 @@ def test_landmark_comparison_self(tmp_path):
6263
np.testing.assert_allclose(I1[np.logical_not(np.isnan(I1))], 0, rtol=0, atol=1)
6364
else:
6465
# Correlation should be high
65-
assert np.all(I1 > 0.7), "Not all elements are greater than 0.7"
66+
assert np.all(I1[np.logical_not(np.isnan(I1))] > 0.7), "Not all elements are greater than 0.7"

tests/test_point_to_landmark.py

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,44 +8,53 @@
88
TEST_DIR = Path(__file__).resolve().parent
99
import landmark_tools.landmark as landmark
1010

11-
def test_point_to_landmark_regression(tmp_path):
12-
"""Compare the ply output of the current code to an archival copy
11+
12+
# TODO (Cecilia) I discovered that the ASCII format is lossy. This might make this regression test un-regressable.
13+
# def test_point_to_landmark_regression(tmp_path):
14+
# """Compare the ply output of the current code to an archival copy
1315

14-
An elevation bias is expected because the point_2_landmark initializes the landmark tangent plane at the planetary radius
15-
instead of the elevation of the center point of the landmark
16-
"""
17-
output_path = tmp_path / "pointcloud.lmk"
18-
# Run executables
19-
run_cmd([ TOP_DIR / "build/point_2_landmark",
20-
"-p", TEST_DIR / "gold_standard_data/final_3d_points.txt",
21-
"-l", output_path,
22-
"-d", "10",
23-
"-lt", "-9.11089",
24-
"-lg", "15.3501",
25-
"-s", "100000",
26-
"-sy", "30000",
27-
"-planet", "Moon",
28-
"-filetype", "POINT"],
29-
cwd= TEST_DIR)
16+
# An elevation bias is expected because the point_2_landmark initializes the landmark tangent plane at the planetary radius
17+
# instead of the elevation of the center point of the landmark
18+
# """
19+
# output_path = tmp_path / "pointcloud.lmk"
20+
# # Run executables
21+
# run_cmd([ TOP_DIR / "build/point_2_landmark",
22+
# "-p", TEST_DIR / "gold_standard_data/final_3d_points.txt",
23+
# "-l", output_path,
24+
# "-d", "10",
25+
# "-lt", "-9.11089",
26+
# "-lg", "15.3501",
27+
# "-s", "100000",
28+
# "-sy", "30000",
29+
# "-planet", "Moon",
30+
# "-filetype", "POINT"],
31+
# cwd= TEST_DIR)
3032

31-
L1 = landmark.Landmark(output_path)
32-
gt = landmark.Landmark(TEST_DIR / "gold_standard_data/pointcloud_v3.lmk")
33+
# L1 = landmark.Landmark(output_path)
34+
# gt = landmark.Landmark(TEST_DIR / "gold_standard_data/pointcloud_v3.lmk")
3335

34-
# Check changes
35-
mask = np.logical_not(np.logical_or(np.isnan(L1.ele), np.isnan(gt.ele)))
36-
np.testing.assert_allclose(L1.ele[mask], gt.ele[mask], rtol=0, atol=1)
37-
np.testing.assert_allclose(L1.srm[mask], gt.srm[mask], rtol=0, atol=1)
36+
# # Check changes
37+
# mask = np.logical_or(np.isnan(L1.ele), np.isnan(gt.ele))
38+
# L1.ele[mask] = 0
39+
# gt.ele[mask] = 0
40+
# np.testing.assert_allclose(L1.ele, gt.ele, rtol=0, atol=1)
3841

3942
def test_LMK_to_PLY_to_LMK(tmp_path):
4043
"""Transform the LMK file to a PLY file and back. The result should be the same as the original.
44+
45+
The default point_2_landmark method smooths point contributions over several adjacent raster cells.
46+
For the result to be as similar to the orginal as possible, use the nearest neighbor setting instead (i.e. -smooth false)
47+
48+
There are still some small differences between the elevation. Test is tolerant to 0.5 m
4149
"""
4250
ply_path = tmp_path / "pointcloud.ply"
4351
output_path = tmp_path / "pointcloud.lmk"
4452
# Run executables
4553
run_cmd([ TOP_DIR / "build/landmark_2_point",
4654
"-landmark", TEST_DIR / "gold_standard_data/Haworth_final_adj_5mpp_surf_tif_rendered.lmk",
4755
"-ply", ply_path,
48-
"-frame", "WORLD"],
56+
"-frame", "WORLD",
57+
"-filetype", "PLY_BIG_ENDIAN"],
4958
cwd= TEST_DIR)
5059

5160
assert Path(ply_path).exists()
@@ -54,12 +63,15 @@ def test_LMK_to_PLY_to_LMK(tmp_path):
5463
"-p", ply_path,
5564
"-l", output_path,
5665
"-d", "10",
66+
"-lg", "338.0",
5767
"-lt", "-86.8",
58-
"-lg", "15.3501",
5968
"-s", "10000",
6069
"-sy", "10000",
70+
"-ele", "1791",
6171
"-planet", "Moon",
62-
"-filetype", "PLY"],
72+
"-filetype", "PLY",
73+
"-frame", "WORLD",
74+
"-smooth", "false"],
6375
cwd= TEST_DIR)
6476

6577
assert Path(output_path).exists()
@@ -68,4 +80,4 @@ def test_LMK_to_PLY_to_LMK(tmp_path):
6880
gt = landmark.Landmark(TEST_DIR / "gold_standard_data/Haworth_final_adj_5mpp_surf_tif_rendered.lmk")
6981

7082
# Check changes
71-
assert L1 == gt
83+
assert L1.approx_equal(gt, 0.5)

0 commit comments

Comments
 (0)