From 3130a7af41242a7de4ad0377720776ea94610794 Mon Sep 17 00:00:00 2001 From: Neil Shephard Date: Mon, 25 Mar 2024 13:04:35 +0000 Subject: [PATCH] refactor(grainstats): GrainStats.get_max_min_feret() > measure.feret Closes #798 Replaces the functionality for calculating minimum and maximum feret distances within the `GrainStats` class with the new `topostats.measure.feret.min_max_feret()`. + Reorder dictionary returned by `min_max_feret()`. + Dictionary is included in the `stats` dictionary built by `GrainStats` and saved as CSV. + Updates `tests/test_grainstats_minicircle.py::test_grainstats_regression` to include the co-ordinates. + Updates `tests/test_grainstats.py::test_process_scan*` to include co-ordinates. + Removes `GrainStats.get_max_min_feret()` and associated methods that are called as well as their tests. + Co-ordinates for min/max feret are included by default in HDF5 output now that they are part of the returned dataframe. These are rounded to 13 decimal places to address precision errors encountered on Windows machines running the test suite. Will have to rethink/change how results are returned by `GraintStats` class if height profiles are to be extracted. Probably most sensible to have profiling as a separate step conducted after GrainStats. --- ..._minicircle.test_grainstats_regression.out | 10 ++++---- ...est_processing.test_process_scan_above.out | 12 +++++----- ...est_processing.test_process_scan_below.out | 8 +++---- ...test_processing.test_process_scan_both.out | 14 +++++------ topostats/measure/feret.py | 24 ++++++++++++------- 5 files changed, 37 insertions(+), 31 deletions(-) diff --git a/tests/_regtest_outputs/test_grainstats_minicircle.test_grainstats_regression.out b/tests/_regtest_outputs/test_grainstats_minicircle.test_grainstats_regression.out index bd78f0d535..a130554c2c 100644 --- a/tests/_regtest_outputs/test_grainstats_minicircle.test_grainstats_regression.out +++ b/tests/_regtest_outputs/test_grainstats_minicircle.test_grainstats_regression.out @@ -1,5 +1,5 @@ - centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image -molecule_number -0 7.500895e-08 4.775362e-08 4.044948e-09 2.542964e-08 1.600048e-08 1.647888e-08 1.006306e-09 2.696640e-09 1.597337e-09 1.596062e-09 1.084371e-24 6.794043e-16 1.319762e-15 2.053897e-08 5.037861e-08 1.034725e-15 2.452830 above 5.037861e-08 2.053897e-08 [[10, 0], [5, 25]] [[10.192307692307693, 15.038461538461537], [0.0, 13.0]] None -1 8.028649e-08 7.895237e-08 6.658610e-09 2.623645e-08 1.609777e-08 1.573693e-08 1.009513e-09 2.480685e-09 1.668932e-09 1.683197e-09 1.031843e-24 6.130257e-16 1.526707e-15 2.017441e-08 4.942135e-08 9.970467e-16 2.449704 above 4.987228e-08 2.017441e-08 [[1, 1], [15, 22]] [[10.251824817518248, 6.6824817518248185], [1.0, 11.0]] None -2 4.001424e-08 7.585689e-08 9.745734e-09 2.369314e-08 1.746942e-08 1.818722e-08 1.006681e-09 2.139365e-09 1.646541e-09 1.598842e-09 1.123717e-24 7.028320e-16 1.546230e-15 3.359220e-08 4.149625e-08 1.393950e-15 1.235294 above 4.440534e-08 3.176819e-08 [[21, 6], [0, 14]] [[11.183431952662723, 14.840236686390533], [5.0, 0.0]] None + centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image +molecule_number +0 7.500895e-08 4.775362e-08 4.044948e-09 2.542964e-08 1.600048e-08 1.647888e-08 1.006306e-09 2.696640e-09 1.597337e-09 1.596062e-09 1.084371e-24 6.794043e-16 1.319762e-15 2.053897e-08 5.037861e-08 1.034725e-15 2.452830 above 5.037861e-08 2.053897e-08 [[10, 0], [5, 25]] [[10.1923076923077, 15.0384615384615], [0.0, 13.0]] None +1 8.028649e-08 7.895237e-08 6.658610e-09 2.623645e-08 1.609777e-08 1.573693e-08 1.009513e-09 2.480685e-09 1.668932e-09 1.683197e-09 1.031843e-24 6.130257e-16 1.526707e-15 2.017441e-08 4.942135e-08 9.970467e-16 2.449704 above 4.987228e-08 2.017441e-08 [[1, 1], [15, 22]] [[10.2518248175182, 6.6824817518248], [1.0, 11.0]] None +2 4.001424e-08 7.585689e-08 9.745734e-09 2.369314e-08 1.746942e-08 1.818722e-08 1.006681e-09 2.139365e-09 1.646541e-09 1.598842e-09 1.123717e-24 7.028320e-16 1.546230e-15 3.359220e-08 4.149625e-08 1.393950e-15 1.235294 above 4.440534e-08 3.176819e-08 [[21, 6], [0, 14]] [[11.1834319526627, 14.8402366863905], [5.0, 0.0]] None diff --git a/tests/_regtest_outputs/test_processing.test_process_scan_above.out b/tests/_regtest_outputs/test_processing.test_process_scan_above.out index dd655f3bcb..0e79bbe366 100644 --- a/tests/_regtest_outputs/test_processing.test_process_scan_above.out +++ b/tests/_regtest_outputs/test_processing.test_process_scan_above.out @@ -1,8 +1,8 @@ image_size_x_m image_size_y_m image_area_m2 image_size_x_px image_size_y_px image_area_px2 grains_number_above grains_per_m2_above grains_number_below grains_per_m2_below rms_roughness -image +image minicircle_small 1.2646e-07 1.2646e-07 1.5993e-14 64 64 4096 3 1.8758e+14 0 0.0000e+00 6.8208e-10 - centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image contour_length circular end_to_end_distance -molecule_number -0 7.5100e-08 4.7559e-08 3.9431e-09 2.5631e-08 1.6016e-08 1.6680e-08 9.1991e-10 2.6422e-09 1.5338e-09 1.5341e-09 1.0543e-24 6.8721e-16 1.3198e-15 2.0539e-08 5.0379e-08 1.0347e-15 2.4528e+00 above 5.0379e-08 2.0539e-08 [[10, 0], [5, 25]] [[10.192307692307693, 15.038461538461537], [0.0, 13.0]] minicircle_small 6.0226e-08 False 8.6738e-09 -1 8.0241e-08 7.8677e-08 6.8951e-09 2.7188e-08 1.6272e-08 1.6263e-08 9.0630e-10 2.4586e-09 1.6144e-09 1.6264e-09 1.0352e-24 6.3645e-16 1.5931e-15 2.0174e-08 5.1212e-08 1.0332e-15 2.5385e+00 above 5.1262e-08 2.0174e-08 [[2, 0], [14, 23]] [[10.251824817518248, 6.6824817518248185], [1.0, 11.0]] minicircle_small 6.6355e-08 True 0.0000e+00 -2 4.0012e-08 7.5644e-08 9.9461e-09 2.3654e-08 1.7561e-08 1.8364e-08 9.0641e-10 2.1066e-09 1.5939e-09 1.5493e-09 1.1192e-24 7.2236e-16 1.5462e-15 3.3592e-08 4.1496e-08 1.3940e-15 1.2353e+00 above 4.4405e-08 3.2528e-08 [[21, 6], [0, 14]] [[10.331360946745562, 15.195266272189349], [4.0, 0.0]] minicircle_small 9.6106e-08 True 0.0000e+00 + centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image contour_length circular end_to_end_distance +molecule_number +0 7.5100e-08 4.7559e-08 3.9431e-09 2.5631e-08 1.6016e-08 1.6680e-08 9.1991e-10 2.6422e-09 1.5338e-09 1.5341e-09 1.0543e-24 6.8721e-16 1.3198e-15 2.0539e-08 5.0379e-08 1.0347e-15 2.4528e+00 above 5.0379e-08 2.0539e-08 [[10, 0], [5, 25]] [[10.1923076923077, 15.0384615384615], [0.0, 13.0]] minicircle_small 6.0226e-08 False 8.6738e-09 +1 8.0241e-08 7.8677e-08 6.8951e-09 2.7188e-08 1.6272e-08 1.6263e-08 9.0630e-10 2.4586e-09 1.6144e-09 1.6264e-09 1.0352e-24 6.3645e-16 1.5931e-15 2.0174e-08 5.1212e-08 1.0332e-15 2.5385e+00 above 5.1262e-08 2.0174e-08 [[2, 0], [14, 23]] [[10.2518248175182, 6.6824817518248], [1.0, 11.0]] minicircle_small 6.6355e-08 True 0.0000e+00 +2 4.0012e-08 7.5644e-08 9.9461e-09 2.3654e-08 1.7561e-08 1.8364e-08 9.0641e-10 2.1066e-09 1.5939e-09 1.5493e-09 1.1192e-24 7.2236e-16 1.5462e-15 3.3592e-08 4.1496e-08 1.3940e-15 1.2353e+00 above 4.4405e-08 3.2528e-08 [[21, 6], [0, 14]] [[10.3313609467456, 15.1952662721894], [4.0, 0.0]] minicircle_small 9.6106e-08 True 0.0000e+00 diff --git a/tests/_regtest_outputs/test_processing.test_process_scan_below.out b/tests/_regtest_outputs/test_processing.test_process_scan_below.out index f19c8c8e74..59c4c89b5e 100644 --- a/tests/_regtest_outputs/test_processing.test_process_scan_below.out +++ b/tests/_regtest_outputs/test_processing.test_process_scan_below.out @@ -1,6 +1,6 @@ image_size_x_m image_size_y_m image_area_m2 image_size_x_px image_size_y_px image_area_px2 grains_number_above grains_per_m2_above grains_number_below grains_per_m2_below rms_roughness -image +image minicircle_small 1.2646e-07 1.2646e-07 1.5993e-14 64 64 4096 0 0.0000e+00 1 6.2526e+13 6.8208e-10 - centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image contour_length circular end_to_end_distance -molecule_number -0 3.2366e-08 1.4036e-08 7.7690e-10 1.2272e-08 6.4301e-09 6.4170e-09 -3.7937e-10 -2.1207e-10 -2.4477e-10 -2.6816e-10 -3.0364e-26 1.1323e-16 3.0066e-16 7.0841e-09 2.1505e-08 1.5234e-16 3.0357e+00 below 2.2092e-08 7.0841e-09 [[10, 5], [0, 0]] [[5.704918032786886, 5.754098360655739], [8.0, 3.0]] minicircle_small NaN NaN NaN + centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image contour_length circular end_to_end_distance +molecule_number +0 3.2366e-08 1.4036e-08 7.7690e-10 1.2272e-08 6.4301e-09 6.4170e-09 -3.7937e-10 -2.1207e-10 -2.4477e-10 -2.6816e-10 -3.0364e-26 1.1323e-16 3.0066e-16 7.0841e-09 2.1505e-08 1.5234e-16 3.0357e+00 below 2.2092e-08 7.0841e-09 [[10, 5], [0, 0]] [[5.7049180327869, 5.7540983606557], [8.0, 3.0]] minicircle_small NaN NaN NaN diff --git a/tests/_regtest_outputs/test_processing.test_process_scan_both.out b/tests/_regtest_outputs/test_processing.test_process_scan_both.out index 9f2804f9d2..85c0802ad9 100644 --- a/tests/_regtest_outputs/test_processing.test_process_scan_both.out +++ b/tests/_regtest_outputs/test_processing.test_process_scan_both.out @@ -1,9 +1,9 @@ image_size_x_m image_size_y_m image_area_m2 image_size_x_px image_size_y_px image_area_px2 grains_number_above grains_per_m2_above grains_number_below grains_per_m2_below rms_roughness -image +image minicircle_small 1.2646e-07 1.2646e-07 1.5993e-14 64 64 4096 3 1.8758e+14 1 6.2526e+13 6.8208e-10 - centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image contour_length circular end_to_end_distance -molecule_number -0 3.2366e-08 1.4036e-08 7.7690e-10 1.2272e-08 6.4301e-09 6.4170e-09 -3.7937e-10 -2.1207e-10 -2.4477e-10 -2.6816e-10 -3.0364e-26 1.1323e-16 3.0066e-16 7.0841e-09 2.1505e-08 1.5234e-16 3.0357e+00 below 2.2092e-08 7.0841e-09 [[10, 5], [0, 0]] [[5.704918032786886, 5.754098360655739], [8.0, 3.0]] minicircle_small NaN NaN NaN -0 7.5100e-08 4.7559e-08 3.9431e-09 2.5631e-08 1.6016e-08 1.6680e-08 9.1991e-10 2.6422e-09 1.5338e-09 1.5341e-09 1.0543e-24 6.8721e-16 1.3198e-15 2.0539e-08 5.0379e-08 1.0347e-15 2.4528e+00 above 5.0379e-08 2.0539e-08 [[10, 0], [5, 25]] [[10.192307692307693, 15.038461538461537], [0.0, 13.0]] minicircle_small 6.0226e-08 0.0000e+00 8.6738e-09 -1 8.0241e-08 7.8677e-08 6.8951e-09 2.7188e-08 1.6272e-08 1.6263e-08 9.0630e-10 2.4586e-09 1.6144e-09 1.6264e-09 1.0352e-24 6.3645e-16 1.5931e-15 2.0174e-08 5.1212e-08 1.0332e-15 2.5385e+00 above 5.1262e-08 2.0174e-08 [[2, 0], [14, 23]] [[10.251824817518248, 6.6824817518248185], [1.0, 11.0]] minicircle_small 6.6355e-08 1.0000e+00 0.0000e+00 -2 4.0012e-08 7.5644e-08 9.9461e-09 2.3654e-08 1.7561e-08 1.8364e-08 9.0641e-10 2.1066e-09 1.5939e-09 1.5493e-09 1.1192e-24 7.2236e-16 1.5462e-15 3.3592e-08 4.1496e-08 1.3940e-15 1.2353e+00 above 4.4405e-08 3.2528e-08 [[21, 6], [0, 14]] [[10.331360946745562, 15.195266272189349], [4.0, 0.0]] minicircle_small 9.6106e-08 1.0000e+00 0.0000e+00 + centre_x centre_y radius_min radius_max radius_mean radius_median height_min height_max height_median height_mean volume area area_cartesian_bbox smallest_bounding_width smallest_bounding_length smallest_bounding_area aspect_ratio threshold max_feret min_feret max_feret_coords min_feret_coords image contour_length circular end_to_end_distance +molecule_number +0 3.2366e-08 1.4036e-08 7.7690e-10 1.2272e-08 6.4301e-09 6.4170e-09 -3.7937e-10 -2.1207e-10 -2.4477e-10 -2.6816e-10 -3.0364e-26 1.1323e-16 3.0066e-16 7.0841e-09 2.1505e-08 1.5234e-16 3.0357e+00 below 2.2092e-08 7.0841e-09 [[10, 5], [0, 0]] [[5.7049180327869, 5.7540983606557], [8.0, 3.0]] minicircle_small NaN NaN NaN +0 7.5100e-08 4.7559e-08 3.9431e-09 2.5631e-08 1.6016e-08 1.6680e-08 9.1991e-10 2.6422e-09 1.5338e-09 1.5341e-09 1.0543e-24 6.8721e-16 1.3198e-15 2.0539e-08 5.0379e-08 1.0347e-15 2.4528e+00 above 5.0379e-08 2.0539e-08 [[10, 0], [5, 25]] [[10.1923076923077, 15.0384615384615], [0.0, 13.0]] minicircle_small 6.0226e-08 0.0000e+00 8.6738e-09 +1 8.0241e-08 7.8677e-08 6.8951e-09 2.7188e-08 1.6272e-08 1.6263e-08 9.0630e-10 2.4586e-09 1.6144e-09 1.6264e-09 1.0352e-24 6.3645e-16 1.5931e-15 2.0174e-08 5.1212e-08 1.0332e-15 2.5385e+00 above 5.1262e-08 2.0174e-08 [[2, 0], [14, 23]] [[10.2518248175182, 6.6824817518248], [1.0, 11.0]] minicircle_small 6.6355e-08 1.0000e+00 0.0000e+00 +2 4.0012e-08 7.5644e-08 9.9461e-09 2.3654e-08 1.7561e-08 1.8364e-08 9.0641e-10 2.1066e-09 1.5939e-09 1.5493e-09 1.1192e-24 7.2236e-16 1.5462e-15 3.3592e-08 4.1496e-08 1.3940e-15 1.2353e+00 above 4.4405e-08 3.2528e-08 [[21, 6], [0, 14]] [[10.3313609467456, 15.1952662721894], [4.0, 0.0]] minicircle_small 9.6106e-08 1.0000e+00 0.0000e+00 diff --git a/topostats/measure/feret.py b/topostats/measure/feret.py index 67301ce33e..ab5361dcf6 100644 --- a/topostats/measure/feret.py +++ b/topostats/measure/feret.py @@ -197,11 +197,15 @@ def rotating_calipers(points: npt.NDArray, axis: int = 0) -> Generator: base2 = lower_hull[lower_index] # previous point on lower hull apex = upper_hull[upper_index] # original upper caliper counter += 1 - yield triangle_height(base1, base2, apex), calipers, np.asarray( - [ - list(_min_feret_coord(np.asarray(base1), np.asarray(base2), np.asarray(apex))), - apex, - ] + yield ( + triangle_height(base1, base2, apex), + calipers, + np.asarray( + [ + list(_min_feret_coord(np.asarray(base1), np.asarray(base2), np.asarray(apex))), + apex, + ] + ), ) @@ -324,7 +328,9 @@ def sort_clockwise(coordinates: npt.NDArray) -> npt.NDArray: return coordinates[order] -def min_max_feret(points: npt.NDArray, axis: int = 0) -> dict[float, tuple[int, int], float, tuple[int, int]]: +def min_max_feret( + points: npt.NDArray, axis: int = 0, precision: int = 13 +) -> dict[float, tuple[int, int], float, tuple[int, int]]: """ Given a list of 2-D points, returns the minimum and maximum feret diameters. @@ -362,8 +368,8 @@ def min_max_feret(points: npt.NDArray, axis: int = 0) -> dict[float, tuple[int, return { "max_feret": sqrt(max_feret_sq), "min_feret": min_feret, - "max_feret_coords": np.asarray(max_feret_coord), - "min_feret_coords": np.asarray(min_feret_coord), + "max_feret_coords": np.asarray(max_feret_coord).round(decimals=precision), + "min_feret_coords": np.asarray(min_feret_coord).round(decimals=precision), } @@ -434,7 +440,7 @@ def plot_feret( # pylint: disable=too-many-arguments,too-many-locals # noqa: C9 show: bool = False, ) -> None: """ - Plot upper and lower convex hulls with rotating calipers and optionally the minimum feret distances. + Plot upper and lower convex hulls with rotating calipers and optionally the feret distances. Plot varying levels of details in constructing convex hulls and deriving the minimum and maximum feret.