diff --git a/isyntax2raw/__init__.py b/isyntax2raw/__init__.py
index a7113eb..6fb8b52 100644
--- a/isyntax2raw/__init__.py
+++ b/isyntax2raw/__init__.py
@@ -19,15 +19,8 @@
import softwarerenderbackend
import zarr
-from numcodecs.abc import Codec
-from numcodecs.compat import \
- ensure_bytes, \
- ensure_contiguous_ndarray, \
- ensure_ndarray, \
- ndarray_copy
-from numcodecs.registry import register_codec
-import glymur
-import tempfile
+from zarr_jpeg2k import jpeg2k
+from numcodecs import Blosc
from datetime import datetime
from concurrent.futures import ALL_COMPLETED, ThreadPoolExecutor, wait
@@ -91,7 +84,8 @@ class WriteTiles(object):
def __init__(
self, tile_width, tile_height, resolutions, max_workers,
- batch_size, fill_color, nested, input_path, output_path, psnr
+ batch_size, fill_color, nested, input_path, output_path,
+ compression, level
):
self.tile_width = tile_width
self.tile_height = tile_height
@@ -102,7 +96,8 @@ def __init__(
self.nested = nested
self.input_path = input_path
self.slide_directory = output_path
- self.psnr = psnr
+ self.level = level
+ self.compression = compression
render_context = softwarerendercontext.SoftwareRenderContext()
render_backend = softwarerenderbackend.SoftwareRenderBackend()
@@ -514,9 +509,9 @@ def write_image_type(self, image_type, series):
tile = self.zarr_group["%d/0" % series]
tile.attrs['image type'] = image_type
for channel in range(0, 3):
- band = np.array(img.getdata(band=channel))
- band.shape = (height, width)
- tile[0, channel, 0] = band
+ data = np.array(img.getdata())
+ data.shape = (height, width, 3)
+ tile[0, 0, :] = data
self.write_image_metadata(range(1), series)
log.info("wrote %s image" % image_type)
@@ -525,6 +520,14 @@ def create_tile_directory(self, series, resolution, width, height):
dimension_separator = '/'
if not self.nested:
dimension_separator = '.'
+ if self.compression == 'blosc':
+ compressor = Blosc(cname='lz4', clevel=5, shuffle=Blosc.SHUFFLE)
+ if self.compression == 'zlib':
+ compressor = zarr.Zlib(level=1)
+ if self.compression == 'jpeg2k':
+ compressor = jpeg2k(self.level)
+ if self.compression == 'raw':
+ compressor = None
self.zarr_store = FSStore(
self.slide_directory,
dimension_separator=dimension_separator,
@@ -536,22 +539,15 @@ def create_tile_directory(self, series, resolution, width, height):
# important to explicitly set the chunk size to 1 for non-XY dims
# setting to None may cause all planes to be chunked together
- # ordering is TCZYX and hard-coded since Z and T are not present
+ # ordering is TZYXC (interleaved) and hard-coded since Z and T
+ # are not present
self.zarr_group.create_dataset(
"%s/%s" % (str(series), str(resolution)),
- shape=(1, 3, 1, height, width),
- chunks=(1, 1, 1, self.tile_height, self.tile_width), dtype='B',
- compressor=j2k(self.psnr)
+ shape=(1, 1, height, width, 3),
+ chunks=(1, 1, self.tile_height, self.tile_width, 3), dtype='B',
+ compressor=compressor
)
- def make_planar(self, pixels, tile_width, tile_height):
- r = pixels[0::3]
- g = pixels[1::3]
- b = pixels[2::3]
- for v in (r, g, b):
- v.shape = (tile_height, tile_width)
- return np.array([r, g, b])
-
def write_pyramid(self):
'''write the slide's pyramid as a set of tiles'''
pe_in = self.pixel_engine["in"]
@@ -572,11 +568,9 @@ def write_tile(
x_end = x_start + tile_width
y_end = y_start + tile_height
try:
- # Zarr has a single n-dimensional array representation on
- # disk (not interleaved RGB)
- pixels = self.make_planar(pixels, tile_width, tile_height)
+ pixels.shape = (tile_height, tile_width, 3)
z = self.zarr_group["0/%d" % resolution]
- z[0, :, 0, y_start:y_end, x_start:x_end] = pixels
+ z[0, 0, y_start:y_end, x_start:x_end, :] = pixels
except Exception:
log.error(
"Failed to write tile [:, %d:%d, %d:%d]" % (
@@ -731,55 +725,3 @@ def create_patch_list(
# order to identify the patches returned asynchronously
patch_ids.append((x, y))
return patches, patch_ids
-
-
-class j2k(Codec):
- """Codec providing j2k compression via Glymur.
- Parameters
- ----------
- psnr : int
- Compression peak signal noise ratio.
- """
-
- codec_id = "j2k"
-
- def __init__(self, psnr=50):
- self.psnr = psnr
- assert (self.psnr > 0 and self.psnr <= 100
- and isinstance(self.psnr, int))
- super().__init__()
-
- def encode(self, buf):
- buf = ensure_ndarray(np.squeeze(buf))
- fd, fname = tempfile.mkstemp()
- try:
- buff = glymur.Jp2k(
- fname, psnr=[self.psnr], shape=buf.shape, numres=1
- )
- buff._write(buf)
- with open(fname, 'rb') as f:
- array = f.read()
- finally:
- os.close(fd)
- os.remove(fname)
- return array
-
- def decode(self, buf, out=None):
- fd, fname = tempfile.mkstemp()
- try:
- with open(fname, 'wb') as f:
- f.write(buf)
- buf = ensure_bytes(buf)
- jp2 = glymur.Jp2k(fname)
- if out is not None:
- out_view = ensure_contiguous_ndarray(out)
- ndarray_copy(jp2[:], out_view)
- else:
- out = jp2[:]
- return out
- finally:
- os.close(fd)
- os.remove(fname)
-
-
-register_codec(j2k)
diff --git a/isyntax2raw/cli/isyntax2raw.py b/isyntax2raw/cli/isyntax2raw.py
index dc215ca..713bc7c 100644
--- a/isyntax2raw/cli/isyntax2raw.py
+++ b/isyntax2raw/cli/isyntax2raw.py
@@ -65,19 +65,27 @@ def cli():
help="enable debugging",
)
@click.option(
- "--psnr", default=50, show_default=True,
- help="JPEG-2000 compression PSNR"
+ "--level", default=50, type=int, show_default=True,
+ help="JPEG-2000 compression level. Higher is better compression. Only \
+ works when compression is set to jpeg2k"
+)
+@click.option(
+ "--compression", default='blosc', show_default=True,
+ type=click.Choice(['blosc', 'zlib', 'jpeg2k', 'raw'],
+ case_sensitive=False),
+ help="Chunk compression type"
)
@click.argument("input_path")
@click.argument("output_path")
def write_tiles(
tile_width, tile_height, resolutions, max_workers, batch_size,
- fill_color, nested, debug, input_path, output_path, psnr
+ fill_color, nested, debug, input_path, output_path, compression, level
):
setup_logging(debug)
with WriteTiles(
tile_width, tile_height, resolutions, max_workers,
- batch_size, fill_color, nested, input_path, output_path, psnr
+ batch_size, fill_color, nested, input_path, output_path,
+ compression, level
) as wt:
wt.write_metadata()
wt.write_label_image()
@@ -96,7 +104,7 @@ def write_metadata(debug, input_path, output_file):
setup_logging(debug)
with WriteTiles(
None, None, None, None,
- None, None, None, input_path, None
+ None, None, None, input_path, None, None, None
) as wt:
wt.write_metadata_json(output_file)
diff --git a/isyntax2raw/resources/ome_template.xml b/isyntax2raw/resources/ome_template.xml
index 53a84c0..5df7793 100644
--- a/isyntax2raw/resources/ome_template.xml
+++ b/isyntax2raw/resources/ome_template.xml
@@ -8,7 +8,7 @@
@@ -42,7 +42,7 @@
=0.9.0',
+ 'zarr-jpeg2k'
],
tests_require=[
'flake8',