Skip to content

Commit

Permalink
Merge pull request #28 from niaid/JSONoutputstr
Browse files Browse the repository at this point in the history
JSON output as string
  • Loading branch information
blowekamp authored Aug 19, 2022
2 parents b32c1ec + 3f60b07 commit 8fd02cc
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 23 deletions.
28 changes: 19 additions & 9 deletions pytools/ng/build_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import SimpleITK as sitk
from pytools.utils import MutuallyExclusiveOption
from pytools import __version__
from math import floor, ceil


def weighted_quantile(values, quantiles, sample_weight=None, values_sorted=False, old_style=False):
Expand Down Expand Up @@ -66,7 +67,7 @@ def stream_build_histogram(filename: str, histogram_bin_edges, extract_axis=1, d
Read image slice by slice, and build a histogram. The image file must be readable by SimpleITK.
The SimpleITK is expected to support streaming the file format.
The np.histogram function is run on each image slice with the provided hitogram_bin_edges, and
The np.histogram function is run on each image slice with the provided histogram_bin_edges, and
accumulated for the results.
:param filename: The path to the image file to read. MRC file type is recommend.
Expand Down Expand Up @@ -166,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),
Expand All @@ -174,12 +180,12 @@ 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 am estimated minimum and maximum range to be used for visualization of the
data set.
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.
The optional OUTPUT_JSON filename will be created with the following data elements with a double numeric value:
The optional OUTPUT_JSON filename will be created with the following data elements with integer values as strings:
"neuroglancerPrecomputedMin"
"neuroglancerPrecomputedMax"
"neuroglancerPrecomputedFloor"
Expand Down Expand Up @@ -246,12 +252,16 @@ 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": float(min_max[0]),
"neuroglancerPrecomputedMax": float(min_max[1]),
"neuroglancerPrecomputedFloor": float(floor_limit[0]),
"neuroglancerPrecomputedLimit": float(floor_limit[1]),
"neuroglancerPrecomputedMin": str(floor(min_max[0])),
"neuroglancerPrecomputedMax": str(ceil(min_max[1])),
"neuroglancerPrecomputedFloor": str(floor(floor_limit[0])),
"neuroglancerPrecomputedLimit": str(ceil(floor_limit[1])),
}

logger.debug(f"output: {output}")
if output_json:
import json
Expand Down
12 changes: 11 additions & 1 deletion test/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand All @@ -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)}")
Expand Down
35 changes: 22 additions & 13 deletions test/test_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.5, 57343.5, 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)
Expand All @@ -132,7 +137,11 @@ def test_build_histogram_main(image_mrc, expected_min, expected_max, expected_fl
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
assert float(res["neuroglancerPrecomputedMin"]) == expected_min
assert float(res["neuroglancerPrecomputedMax"]) == expected_max
assert float(res["neuroglancerPrecomputedFloor"]) == expected_floor
assert float(res["neuroglancerPrecomputedLimit"]) == expected_limit
assert type(res["neuroglancerPrecomputedMin"]) == str
assert type(res["neuroglancerPrecomputedMax"]) == str
assert type(res["neuroglancerPrecomputedFloor"]) == str
assert type(res["neuroglancerPrecomputedLimit"]) == str
4 changes: 4 additions & 0 deletions test/test_mrc2ngpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@ def test_mrc2ngpc(image_mrc, expected_pixel_type):
assert "neuroglancerPrecomputedMin" in mm
assert "neuroglancerPrecomputedFloor" in mm
assert "neuroglancerPrecomputedLimit" in mm
assert type(mm["neuroglancerPrecomputedMin"]) == str
assert type(mm["neuroglancerPrecomputedMax"]) == str
assert type(mm["neuroglancerPrecomputedFloor"]) == str
assert type(mm["neuroglancerPrecomputedLimit"]) == str

0 comments on commit 8fd02cc

Please sign in to comment.