diff --git a/tools/concat_channels/test-data/input1_uint8.png b/tools/concat_channels/test-data/input1_uint8.png index 2283a3b6..26452e17 100644 Binary files a/tools/concat_channels/test-data/input1_uint8.png and b/tools/concat_channels/test-data/input1_uint8.png differ diff --git a/tools/concat_channels/test-data/input2_float.tiff b/tools/concat_channels/test-data/input2_float.tiff index 8aee4180..4809551e 100644 Binary files a/tools/concat_channels/test-data/input2_float.tiff and b/tools/concat_channels/test-data/input2_float.tiff differ diff --git a/tools/concat_channels/test-data/res_preserve_brightness.tiff b/tools/concat_channels/test-data/res_preserve_brightness.tiff index 8996b403..a47e8a56 100644 Binary files a/tools/concat_channels/test-data/res_preserve_brightness.tiff and b/tools/concat_channels/test-data/res_preserve_brightness.tiff differ diff --git a/tools/concat_channels/test-data/res_preserve_values.tiff b/tools/concat_channels/test-data/res_preserve_values.tiff index bb93d3f7..7adb27b7 100644 Binary files a/tools/concat_channels/test-data/res_preserve_values.tiff and b/tools/concat_channels/test-data/res_preserve_values.tiff differ diff --git a/util/.gitignore b/util/.gitignore new file mode 100644 index 00000000..6f71c9bc --- /dev/null +++ b/util/.gitignore @@ -0,0 +1 @@ +.*.venv diff --git a/util/README.md b/util/README.md new file mode 100644 index 00000000..a1677148 --- /dev/null +++ b/util/README.md @@ -0,0 +1,31 @@ +# Utility scripts + +## Use case: An input TIFF file is too large + +Assuming that the TIFF file is an RGB file: + +```bash +../../util/shrink_tiff.sh test-data/input2_float.tiff --channel_axis 2 +``` + +The script preserves the brightness and range of values of the image. + +## Use case: An output TIFF file is too large + +Assuming that the TIFF file is an RGB file: + +```bash +../../util/shrink_tiff.sh test-data/res_preserve_values.tiff --channel_axis 2 +``` + +This shrinks the file, but the *input files* also need to be shrinked accordingly. +The output of the above command tells the exact scaling factor that was used. +Denote this factor, and then run: + +```bash +../../util/scale_image.sh test-data/input2_float.tiff --channel_axis 2 --scale SCALE +``` + +where you replace `SCALE` by the denoted factor. This also works for PNG files. + +The script preserves the brightness and range of values of the image. diff --git a/util/scale_image.py b/util/scale_image.py new file mode 100644 index 00000000..2603d5ad --- /dev/null +++ b/util/scale_image.py @@ -0,0 +1,40 @@ +import argparse + +import numpy as np +import skimage.transform +import skimage.io + + +def scale_image(filepath, **kwargs): + im = skimage.io.imread(filepath) + res = skimage.transform.rescale(im, preserve_range=True, **kwargs) + + # Preserve the `dtype` so that both brightness and range of values is preserved + if res.dtype != im.dtype: + if np.issubdtype(im.dtype, np.integer): + res = res.round() + res = res.astype(im.dtype) + + skimage.io.imsave(filepath, res) + return res + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument('filepath', type=str, help='Image filepath') + parser.add_argument('--scale', type=float, help='Scaling factor', required=True) + parser.add_argument('--anti_aliasing', default=False, action='store_true', help='Use anti-aliasing') + parser.add_argument('--order', type=int, default=0, help='Order of interpolation (0: nearest-neighbor, 1: bi-linear)') + parser.add_argument('--channel_axis', type=int, default=None, help='Given axis will not be rescaled') + args = parser.parse_args() + + res = scale_image( + filepath=args.filepath, + scale=args.scale, + anti_aliasing=args.anti_aliasing, + order=args.order, + channel_axis=args.channel_axis, + ) + + print(f'File {args.filepath} has been scaled to a resolution of {str(res.shape)} pixels.') diff --git a/util/scale_image.sh b/util/scale_image.sh new file mode 100755 index 00000000..d673d1c2 --- /dev/null +++ b/util/scale_image.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +cwd=$( pwd ) +cd "$script_path" + +if [ ! -d ".scale_image.venv" ] +then + python -m venv ".scale_image.venv" + source ".scale_image.venv/bin/activate" + pip install "scikit-image>=0.19" "tifffile" "pillow" +else + source ".scale_image.venv/bin/activate" +fi + +cd "$cwd" +python "$script_path/scale_image.py" $* diff --git a/util/shrink_tiff.py b/util/shrink_tiff.py new file mode 100644 index 00000000..13b2c50f --- /dev/null +++ b/util/shrink_tiff.py @@ -0,0 +1,85 @@ +import argparse +import itertools +import math +import os +import tempfile +import shutil + +import humanize +import skimage.transform +import tifffile + + +DEFAULT_MAX_SIZE = 1024 ** 2 + + +def shrink_tiff(filepath, anti_aliasing, order, channel_axis, max_size=DEFAULT_MAX_SIZE, dry=True): + im_full = tifffile.imread(filepath) + + for step in itertools.count(1): + scale = math.sqrt(1 / step) + im = skimage.transform.rescale( + im_full, + scale=scale, + anti_aliasing=anti_aliasing, + order=order, + preserve_range=True, + channel_axis=channel_axis, + ) + + with tempfile.NamedTemporaryFile(suffix='.tiff', mode='wb') as fp: + tifffile.imwrite(fp.name, im) + byte_size = os.path.getsize(fp.name) + + if byte_size <= max_size: + if not dry: + shutil.copy(fp.name, filepath) + return im, scale, byte_size + + +if __name__ == '__main__': + + exec_name = 'shrink_tiff.sh' + long_help = f""" + + Example for single-channel binary images and label maps: + + {exec_name} filepath.tiff + + Example for multi-channel binary images: + + {exec_name} filepath.tiff --channel_axis 2 + + Example for single-channel intensity images: + + {exec_name} filepath.tiff --anti_aliasing --order 1 + + """ + + parser = argparse.ArgumentParser() + parser.add_argument('filepath', type=str, help='TIFF filepath') + parser.add_argument('--max_size', type=int, default=DEFAULT_MAX_SIZE, help='Maximum size in bytes') + parser.add_argument('--anti_aliasing', default=False, action='store_true', help='Use anti-aliasing') + parser.add_argument('--order', type=int, default=0, help='Order of interpolation (0: nearest-neighbor, 1: bi-linear)') + parser.add_argument('--channel_axis', type=int, default=None, help='Given axis will not be rescaled') + parser.add_argument('--run', default=False, action='store_true', help='Update the TIFF file (default is dry run)') + args = parser.parse_args() + + if not args.run: + print(long_help) + + result, scale, result_size = shrink_tiff( + filepath=args.filepath, + max_size=args.max_size, + anti_aliasing=args.anti_aliasing, + order=args.order, + channel_axis=args.channel_axis, + dry=not args.run, + ) + + result_size_str = humanize.naturalsize(result_size, binary=True) + if args.run: + print(f'File {args.filepath} has been shrinked to {result_size_str} and a resolution of {str(result.shape)} pixels by a factor of {scale}.') + else: + print(f'File {args.filepath} will be shrinked to {result_size_str} and a resolution of {str(result.shape)} pixels by a factor of {scale}.') + print('\nUse the switch "--run" to update the file.') diff --git a/util/shrink_tiff.sh b/util/shrink_tiff.sh new file mode 100755 index 00000000..ad4ff4ab --- /dev/null +++ b/util/shrink_tiff.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) +cwd=$( pwd ) +cd "$script_path" + +if [ ! -d ".shrink_tiff.venv" ] +then + python -m venv ".shrink_tiff.venv" + source ".shrink_tiff.venv/bin/activate" + pip install "scikit-image>=0.19" "humanize" "tifffile" +else + source ".shrink_tiff.venv/bin/activate" +fi + +cd "$cwd" +python "$script_path/shrink_tiff.py" $*