From 3f60b074c41bfa3b2670874f441f1a925b7b21ef Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Fri, 19 Aug 2022 14:56:20 -0400 Subject: [PATCH] Add clamp option Optionally ensure that min and max are withon floor and limit. --- pytools/ng/build_histogram.py | 10 +++++++++- test/fixtures.py | 12 +++++++++++- test/test_histogram.py | 23 ++++++++++++++--------- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/pytools/ng/build_histogram.py b/pytools/ng/build_histogram.py index 063fb74..486040c 100644 --- a/pytools/ng/build_histogram.py +++ b/pytools/ng/build_histogram.py @@ -167,6 +167,11 @@ def histogram_stats(hist, bin_edges): mutually_exclusive=["sigma", "mad"], help="Use INPUT_IMAGE's middle percentile (option's value) of data for minimum and maximum range.", ) +@click.option( + "--clamp/--no-clamp", + default=False, + help="Clamps minimum and maximum range to existing intensity values (floor and limit).", +) @click.option( "--output-json", type=click.Path(exists=False, dir_okay=False, resolve_path=True), @@ -175,7 +180,7 @@ def histogram_stats(hist, bin_edges): "elements of a double numeric value.", ) @click.version_option(__version__) -def main(input_image, mad, sigma, percentile, output_json): +def main(input_image, mad, sigma, percentile, clamp, output_json): """ Reads the INPUT_IMAGE to compute an estimated minimum and maximum range to be used for visualization of the data set. The image is required to have an integer pixel type. @@ -247,6 +252,9 @@ def main(input_image, mad, sigma, percentile, output_json): floor_limit = weighted_quantile(mids, quantiles=[0.0, 1.0], sample_weight=h, values_sorted=True) + if clamp: + min_max = (max(min_max[0], floor_limit[0]), min(min_max[1], floor_limit[1])) + output = { "neuroglancerPrecomputedMin": str(floor(min_max[0])), "neuroglancerPrecomputedMax": str(ceil(min_max[1])), diff --git a/test/fixtures.py b/test/fixtures.py index 4d83f82..97605bf 100644 --- a/test/fixtures.py +++ b/test/fixtures.py @@ -18,7 +18,7 @@ @pytest.fixture( scope="session", - params=[sitk.sitkUInt8, sitk.sitkInt16, sitk.sitkUInt16, sitk.sitkFloat32, "uint16_uniform"], + params=[sitk.sitkUInt8, sitk.sitkInt16, sitk.sitkUInt16, sitk.sitkFloat32, "uint16_uniform", "uint8_bimodal"], ) def image_mrc(request, tmp_path_factory): if isinstance(request.param, str) and request.param == "uint16_uniform": @@ -29,6 +29,16 @@ def image_mrc(request, tmp_path_factory): a = np.linspace(0, 2**16 - 1, num=2**16, dtype="uint16").reshape(16, 64, 64) img = sitk.GetImageFromArray(a) img.SetSpacing([1.23, 1.23, 4.96]) + + elif isinstance(request.param, str) and request.param == "uint8_bimodal": + + print(f"Calling image_mrc with {request.param}") + fn = f"image_mrc_{request.param.replace(' ', '_')}.mrc" + + a = np.zeros([16, 16, 16], np.uint8) + a[len(a) // 2 :] = 255 + img = sitk.GetImageFromArray(a) + img.SetSpacing([12.3, 12.3, 56.7]) else: pixel_type = request.param print(f"Calling image_mrc with {sitk.GetPixelIDValueAsString(pixel_type)}") diff --git a/test/test_histogram.py b/test/test_histogram.py index 574ac47..aea8c6b 100644 --- a/test/test_histogram.py +++ b/test/test_histogram.py @@ -108,22 +108,27 @@ def test_histogram_mai_help(cli_args): @pytest.mark.parametrize( - "image_mrc,expected_min, expected_max, expected_floor, expected_limit", + "image_mrc,expected_min, expected_max, expected_floor, expected_limit, clamp", [ - (sitk.sitkUInt8, 0, 0, 0, 0), - (sitk.sitkInt16, 0, 0, 0, 0), - (sitk.sitkUInt16, 0, 0, 0, 0), - ("uint16_uniform", 8191, 57344, 0, 65535), + (sitk.sitkUInt8, 0, 0, 0, 0, False), + (sitk.sitkInt16, 0, 0, 0, 0, True), + (sitk.sitkUInt16, 0, 0, 0, 0, False), + ("uint16_uniform", 8191, 57344, 0, 65535, True), + ("uint16_uniform", 8191, 57344, 0, 65535, False), + ("uint8_bimodal", 0, 255, 0, 255, True), + ("uint8_bimodal", -64, 319, 0, 255, False), ], indirect=["image_mrc"], ) -def test_build_histogram_main(image_mrc, expected_min, expected_max, expected_floor, expected_limit): +def test_build_histogram_main(image_mrc, expected_min, expected_max, expected_floor, clamp, expected_limit): runner = CliRunner() output_filename = "out.json" + args = [image_mrc, "--mad", "1.5", "--output-json", output_filename] + if clamp: + args.append("--clamp") + print(args) with runner.isolated_filesystem(): - result = runner.invoke( - pytools.ng.build_histogram.main, [image_mrc, "--mad", "1.5", "--output-json", output_filename] - ) + result = runner.invoke(pytools.ng.build_histogram.main, args=args) assert not result.exception with open(output_filename) as fp: res = json.load(fp)