Skip to content

Commit

Permalink
Merge pull request #26 from niaid/ChangeJSONOutput
Browse files Browse the repository at this point in the history
Change json output include floor and limit. Add prefix to field names.
  • Loading branch information
blowekamp authored Aug 16, 2022
2 parents dea84e8 + 86b83ce commit b32c1ec
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 27 deletions.
4 changes: 2 additions & 2 deletions docs/commandline.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Command Line Interface
======================

The Pytools packages contain a command line executables for varous tasks. They can be invoke directly as an executable:
The Pytools packages contain a command line executables for various tasks. They can be invoke directly as an executable:

.. code-block :: bash
Expand All @@ -13,7 +13,7 @@ Or the preferred way using the `python` executable to execute the module entry p
python -m mrc_visual_min_max --help
With either method of invoking the command line interface, the following sections descripts the sub-command available
With either method of invoking the command line interface, the following sections describes the sub-commands available
and the command line options available.

.. click:: pytools.ng.mrc2nifti:main
Expand Down
2 changes: 1 addition & 1 deletion docs/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ line:

.. code:: bash
python -m pytest tes
python -m pytest test
Test Configuration
Expand Down
26 changes: 18 additions & 8 deletions pytools/ng/build_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,17 +169,21 @@ def histogram_stats(hist, bin_edges):
@click.option(
"--output-json",
type=click.Path(exists=False, dir_okay=False, resolve_path=True),
help='The output filename produced in JSON format with "min" and "max" data elements of a number '
"value (double).",
help='The output filename produced in JSON format with "neuroglancerPrecomputedMin", '
'"neuroglancerPrecomputedMax", "neuroglancerPrecomputedFloor" and "neuroglancerPrecomputedLimit" data '
"elements of a double numeric value.",
)
@click.version_option(__version__)
def main(input_image, mad, sigma, percentile, output_json):
"""
Reads the INPUT_IMAGE to compute a estimated minimum and maximum range to be used for visualization of the
Reads the INPUT_IMAGE to compute am estimated minimum and maximum range to be used for visualization of the
data set.
The optional OUTPUT_JSON filename will be created and contain "min" and "max" data elements with a number value
(double).
The optional OUTPUT_JSON filename will be created with the following data elements with a double numeric value:
"neuroglancerPrecomputedMin"
"neuroglancerPrecomputedMax"
"neuroglancerPrecomputedFloor"
"neuroglancerPrecomputedLimit"
"""

logger = logging.getLogger()
Expand Down Expand Up @@ -213,6 +217,7 @@ def main(input_image, mad, sigma, percentile, output_json):

logger.info(f'Building histogram for "{reader.GetFileName()}"...')
h, bins = stream_build_histogram(input_image, list(bin_edges))
mids = 0.5 * (bins[1:] + bins[:-1])

logger.info("Computing statistics...")
if mad:
Expand All @@ -225,8 +230,6 @@ def main(input_image, mad, sigma, percentile, output_json):
min_max = (stats["mean"] - stats["sigma"] * sigma, stats["mean"] + stats["sigma"] * sigma)
elif percentile:

mids = 0.5 * (bins[1:] + bins[:-1])

lower_quantile = (0.5 * (100 - percentile)) / 100.0
upper_quantile = percentile / 100.0 + lower_quantile
logger.debug(f"quantiles: {lower_quantile} {upper_quantile}")
Expand All @@ -241,8 +244,15 @@ def main(input_image, mad, sigma, percentile, output_json):
else:
raise RuntimeError("Missing expected argument")

output = {"min": float(min_max[0]), "max": float(min_max[1])}
floor_limit = weighted_quantile(mids, quantiles=[0.0, 1.0], sample_weight=h, values_sorted=True)

output = {
"neuroglancerPrecomputedMin": float(min_max[0]),
"neuroglancerPrecomputedMax": float(min_max[1]),
"neuroglancerPrecomputedFloor": float(floor_limit[0]),
"neuroglancerPrecomputedLimit": float(floor_limit[1]),
}
logger.debug(f"output: {output}")
if output_json:
import json

Expand Down
23 changes: 17 additions & 6 deletions test/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,29 @@
#
import pytest
import SimpleITK as sitk
import numpy as np


@pytest.fixture(
scope="session",
params=[sitk.sitkUInt8, sitk.sitkInt16, sitk.sitkUInt16, sitk.sitkFloat32],
params=[sitk.sitkUInt8, sitk.sitkInt16, sitk.sitkUInt16, sitk.sitkFloat32, "uint16_uniform"],
)
def image_mrc(request, tmp_path_factory):
pixel_type = request.param
print(f"Calling image_mrc with {sitk.GetPixelIDValueAsString(pixel_type)}")
fn = f"image_mrc_{sitk.GetPixelIDValueAsString(pixel_type).replace(' ', '_')}.mrc"
img = sitk.Image([10, 9, 8], pixel_type)
img.SetSpacing([1.1, 1.2, 1.3])
if isinstance(request.param, str) and request.param == "uint16_uniform":

print(f"Calling image_mrc with {request.param}")
fn = f"image_mrc_{request.param.replace(' ', '_')}.mrc"

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])
else:
pixel_type = request.param
print(f"Calling image_mrc with {sitk.GetPixelIDValueAsString(pixel_type)}")
fn = f"image_mrc_{sitk.GetPixelIDValueAsString(pixel_type).replace(' ', '_')}.mrc"
img = sitk.Image([10, 9, 8], pixel_type)
img.SetSpacing([1.1, 1.2, 1.3])

fn = tmp_path_factory.mktemp("data").joinpath(fn)
sitk.WriteImage(img, str(fn))
return str(fn)
25 changes: 17 additions & 8 deletions test/test_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,22 +108,31 @@ def test_histogram_mai_help(cli_args):


@pytest.mark.parametrize(
"image_mrc,expected_min, expected_max",
[(sitk.sitkUInt8, 0, 0), (sitk.sitkInt16, 0, 0), (sitk.sitkUInt16, 0, 0)],
"image_mrc,expected_min, expected_max, expected_floor, expected_limit",
[
(sitk.sitkUInt8, 0, 0, 0, 0),
(sitk.sitkInt16, 0, 0, 0, 0),
(sitk.sitkUInt16, 0, 0, 0, 0),
("uint16_uniform", 8191.5, 57343.5, 0, 65535),
],
indirect=["image_mrc"],
)
def test_build_histogram_main(image_mrc, expected_min, expected_max):
def test_build_histogram_main(image_mrc, expected_min, expected_max, expected_floor, expected_limit):
runner = CliRunner()
output_filename = "out.json"
with runner.isolated_filesystem():
result = runner.invoke(
pytools.ng.build_histogram.main, [image_mrc, "--mad", "5", "--output-json", output_filename]
pytools.ng.build_histogram.main, [image_mrc, "--mad", "1.5", "--output-json", output_filename]
)
assert not result.exception
with open(output_filename) as fp:
res = json.load(fp)

assert "min" in res
assert "max" in res
assert res["min"] == expected_min
assert res["max"] == expected_max
assert "neuroglancerPrecomputedMin" in res
assert "neuroglancerPrecomputedMax" in res
assert "neuroglancerPrecomputedFloor" in res
assert "neuroglancerPrecomputedLimit" in res
assert res["neuroglancerPrecomputedMin"] == expected_min
assert res["neuroglancerPrecomputedMax"] == expected_max
assert res["neuroglancerPrecomputedFloor"] == expected_floor
assert res["neuroglancerPrecomputedLimit"] == expected_limit
6 changes: 4 additions & 2 deletions test/test_mrc2ngpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ def test_mrc2ngpc(image_mrc, expected_pixel_type):

with open("mrc2ngpc-output.json") as fp:
mm = json.load(fp)
assert "min" in mm
assert "max" in mm
assert "neuroglancerPrecomputedMin" in mm
assert "neuroglancerPrecomputedMin" in mm
assert "neuroglancerPrecomputedFloor" in mm
assert "neuroglancerPrecomputedLimit" in mm

0 comments on commit b32c1ec

Please sign in to comment.