|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| """Additional numcodecs implemented using imagecodecs.""" |
|
|
| __version__ = '2022.9.26' |
|
|
| __all__ = ('register_codecs',) |
|
|
| import numpy |
| from numcodecs.abc import Codec |
| from numcodecs.registry import register_codec, get_codec |
|
|
| import imagecodecs |
|
|
|
|
| def protective_squeeze(x: numpy.ndarray): |
| """ |
| Squeeze dim only if it's not the last dim. |
| Image dim expected to be *, H, W, C |
| """ |
| img_shape = x.shape[-3:] |
| if len(x.shape) > 3: |
| n_imgs = numpy.prod(x.shape[:-3]) |
| if n_imgs > 1: |
| img_shape = (-1,) + img_shape |
| return x.reshape(img_shape) |
|
|
| def get_default_image_compressor(**kwargs): |
| if imagecodecs.JPEGXL: |
| |
| this_kwargs = { |
| 'effort': 3, |
| 'distance': 0.3, |
| |
| |
| 'decodingspeed': 1 |
| } |
| this_kwargs.update(kwargs) |
| return JpegXl(**this_kwargs) |
| else: |
| this_kwargs = { |
| 'level': 50 |
| } |
| this_kwargs.update(kwargs) |
| return Jpeg2k(**this_kwargs) |
|
|
| class Aec(Codec): |
| """AEC codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_aec' |
|
|
| def __init__( |
| self, bitspersample=None, flags=None, blocksize=None, rsi=None |
| ): |
| self.bitspersample = bitspersample |
| self.flags = flags |
| self.blocksize = blocksize |
| self.rsi = rsi |
|
|
| def encode(self, buf): |
| return imagecodecs.aec_encode( |
| buf, |
| bitspersample=self.bitspersample, |
| flags=self.flags, |
| blocksize=self.blocksize, |
| rsi=self.rsi, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.aec_decode( |
| buf, |
| bitspersample=self.bitspersample, |
| flags=self.flags, |
| blocksize=self.blocksize, |
| rsi=self.rsi, |
| out=_flat(out), |
| ) |
|
|
|
|
| class Apng(Codec): |
| """APNG codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_apng' |
|
|
| def __init__(self, level=None, photometric=None, delay=None): |
| self.level = level |
| self.photometric = photometric |
| self.delay = delay |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.apng_encode( |
| buf, |
| level=self.level, |
| photometric=self.photometric, |
| delay=self.delay, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.apng_decode(buf, out=out) |
|
|
|
|
| class Avif(Codec): |
| """AVIF codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_avif' |
|
|
| def __init__( |
| self, |
| level=None, |
| speed=None, |
| tilelog2=None, |
| bitspersample=None, |
| pixelformat=None, |
| numthreads=None, |
| index=None, |
| ): |
| self.level = level |
| self.speed = speed |
| self.tilelog2 = tilelog2 |
| self.bitspersample = bitspersample |
| self.pixelformat = pixelformat |
| self.numthreads = numthreads |
| self.index = index |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.avif_encode( |
| buf, |
| level=self.level, |
| speed=self.speed, |
| tilelog2=self.tilelog2, |
| bitspersample=self.bitspersample, |
| pixelformat=self.pixelformat, |
| numthreads=self.numthreads, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.avif_decode( |
| buf, index=self.index, numthreads=self.numthreads, out=out |
| ) |
|
|
|
|
| class Bitorder(Codec): |
| """Bitorder codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_bitorder' |
|
|
| def encode(self, buf): |
| return imagecodecs.bitorder_encode(buf) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.bitorder_decode(buf, out=_flat(out)) |
|
|
|
|
| class Bitshuffle(Codec): |
| """Bitshuffle codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_bitshuffle' |
|
|
| def __init__(self, itemsize=1, blocksize=0): |
| self.itemsize = itemsize |
| self.blocksize = blocksize |
|
|
| def encode(self, buf): |
| return imagecodecs.bitshuffle_encode( |
| buf, itemsize=self.itemsize, blocksize=self.blocksize |
| ).tobytes() |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.bitshuffle_decode( |
| buf, |
| itemsize=self.itemsize, |
| blocksize=self.blocksize, |
| out=_flat(out), |
| ) |
|
|
|
|
| class Blosc(Codec): |
| """Blosc codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_blosc' |
|
|
| def __init__( |
| self, |
| level=None, |
| compressor=None, |
| typesize=None, |
| blocksize=None, |
| shuffle=None, |
| numthreads=None, |
| ): |
| self.level = level |
| self.compressor = compressor |
| self.typesize = typesize |
| self.blocksize = blocksize |
| self.shuffle = shuffle |
| self.numthreads = numthreads |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.blosc_encode( |
| buf, |
| level=self.level, |
| compressor=self.compressor, |
| typesize=self.typesize, |
| blocksize=self.blocksize, |
| shuffle=self.shuffle, |
| numthreads=self.numthreads, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.blosc_decode( |
| buf, numthreads=self.numthreads, out=_flat(out) |
| ) |
|
|
|
|
| class Blosc2(Codec): |
| """Blosc2 codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_blosc2' |
|
|
| def __init__( |
| self, |
| level=None, |
| compressor=None, |
| typesize=None, |
| blocksize=None, |
| shuffle=None, |
| numthreads=None, |
| ): |
| self.level = level |
| self.compressor = compressor |
| self.typesize = typesize |
| self.blocksize = blocksize |
| self.shuffle = shuffle |
| self.numthreads = numthreads |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.blosc2_encode( |
| buf, |
| level=self.level, |
| compressor=self.compressor, |
| typesize=self.typesize, |
| blocksize=self.blocksize, |
| shuffle=self.shuffle, |
| numthreads=self.numthreads, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.blosc2_decode( |
| buf, numthreads=self.numthreads, out=_flat(out) |
| ) |
|
|
|
|
| class Brotli(Codec): |
| """Brotli codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_brotli' |
|
|
| def __init__(self, level=None, mode=None, lgwin=None): |
| self.level = level |
| self.mode = mode |
| self.lgwin = lgwin |
|
|
| def encode(self, buf): |
| return imagecodecs.brotli_encode( |
| buf, level=self.level, mode=self.mode, lgwin=self.lgwin |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.brotli_decode(buf, out=_flat(out)) |
|
|
|
|
| class ByteShuffle(Codec): |
| """ByteShuffle codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_byteshuffle' |
|
|
| def __init__( |
| self, shape, dtype, axis=-1, dist=1, delta=False, reorder=False |
| ): |
| self.shape = tuple(shape) |
| self.dtype = numpy.dtype(dtype).str |
| self.axis = axis |
| self.dist = dist |
| self.delta = bool(delta) |
| self.reorder = bool(reorder) |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| assert buf.shape == self.shape |
| assert buf.dtype == self.dtype |
| return imagecodecs.byteshuffle_encode( |
| buf, |
| axis=self.axis, |
| dist=self.dist, |
| delta=self.delta, |
| reorder=self.reorder, |
| ).tobytes() |
|
|
| def decode(self, buf, out=None): |
| if not isinstance(buf, numpy.ndarray): |
| buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape) |
| return imagecodecs.byteshuffle_decode( |
| buf, |
| axis=self.axis, |
| dist=self.dist, |
| delta=self.delta, |
| reorder=self.reorder, |
| out=out, |
| ) |
|
|
|
|
| class Bz2(Codec): |
| """Bz2 codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_bz2' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| return imagecodecs.bz2_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.bz2_decode(buf, out=_flat(out)) |
|
|
|
|
| class Cms(Codec): |
| """CMS codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_cms' |
|
|
| def __init__(self, *args, **kwargs): |
| pass |
|
|
| def encode(self, buf, out=None): |
| |
| raise NotImplementedError |
|
|
| def decode(self, buf, out=None): |
| |
| raise NotImplementedError |
|
|
|
|
| class Deflate(Codec): |
| """Deflate codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_deflate' |
|
|
| def __init__(self, level=None, raw=False): |
| self.level = level |
| self.raw = bool(raw) |
|
|
| def encode(self, buf): |
| return imagecodecs.deflate_encode(buf, level=self.level, raw=self.raw) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.deflate_decode(buf, out=_flat(out), raw=self.raw) |
|
|
|
|
| class Delta(Codec): |
| """Delta codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_delta' |
|
|
| def __init__(self, shape=None, dtype=None, axis=-1, dist=1): |
| self.shape = None if shape is None else tuple(shape) |
| self.dtype = None if dtype is None else numpy.dtype(dtype).str |
| self.axis = axis |
| self.dist = dist |
|
|
| def encode(self, buf): |
| if self.shape is not None or self.dtype is not None: |
| buf = protective_squeeze(numpy.asarray(buf)) |
| assert buf.shape == self.shape |
| assert buf.dtype == self.dtype |
| return imagecodecs.delta_encode( |
| buf, axis=self.axis, dist=self.dist |
| ).tobytes() |
|
|
| def decode(self, buf, out=None): |
| if self.shape is not None or self.dtype is not None: |
| buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape) |
| return imagecodecs.delta_decode( |
| buf, axis=self.axis, dist=self.dist, out=out |
| ) |
|
|
|
|
| class Float24(Codec): |
| """Float24 codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_float24' |
|
|
| def __init__(self, byteorder=None, rounding=None): |
| self.byteorder = byteorder |
| self.rounding = rounding |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.float24_encode( |
| buf, byteorder=self.byteorder, rounding=self.rounding |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.float24_decode( |
| buf, byteorder=self.byteorder, out=out |
| ) |
|
|
|
|
| class FloatPred(Codec): |
| """Floating Point Predictor codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_floatpred' |
|
|
| def __init__(self, shape, dtype, axis=-1, dist=1): |
| self.shape = tuple(shape) |
| self.dtype = numpy.dtype(dtype).str |
| self.axis = axis |
| self.dist = dist |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| assert buf.shape == self.shape |
| assert buf.dtype == self.dtype |
| return imagecodecs.floatpred_encode( |
| buf, axis=self.axis, dist=self.dist |
| ).tobytes() |
|
|
| def decode(self, buf, out=None): |
| if not isinstance(buf, numpy.ndarray): |
| buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape) |
| return imagecodecs.floatpred_decode( |
| buf, axis=self.axis, dist=self.dist, out=out |
| ) |
|
|
|
|
| class Gif(Codec): |
| """GIF codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_gif' |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.gif_encode(buf) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.gif_decode(buf, asrgb=False, out=out) |
|
|
|
|
| class Heif(Codec): |
| """HEIF codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_heif' |
|
|
| def __init__( |
| self, |
| level=None, |
| bitspersample=None, |
| photometric=None, |
| compression=None, |
| numthreads=None, |
| index=None, |
| ): |
| self.level = level |
| self.bitspersample = bitspersample |
| self.photometric = photometric |
| self.compression = compression |
| self.numthreads = numthreads |
| self.index = index |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.heif_encode( |
| buf, |
| level=self.level, |
| bitspersample=self.bitspersample, |
| photometric=self.photometric, |
| compression=self.compression, |
| numthreads=self.numthreads, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.heif_decode( |
| buf, |
| index=self.index, |
| photometric=self.photometric, |
| numthreads=self.numthreads, |
| out=out, |
| ) |
|
|
|
|
| class Jetraw(Codec): |
| """Jetraw codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_jetraw' |
|
|
| def __init__( |
| self, |
| shape, |
| identifier, |
| parameters=None, |
| verbosity=None, |
| errorbound=None, |
| ): |
| self.shape = shape |
| self.identifier = identifier |
| self.errorbound = errorbound |
| imagecodecs.jetraw_init(parameters, verbosity) |
|
|
| def encode(self, buf): |
| return imagecodecs.jetraw_encode( |
| buf, identifier=self.identifier, errorbound=self.errorbound |
| ) |
|
|
| def decode(self, buf, out=None): |
| if out is None: |
| out = numpy.empty(self.shape, numpy.uint16) |
| return imagecodecs.jetraw_decode(buf, out=out) |
|
|
|
|
| class Jpeg(Codec): |
| """JPEG codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_jpeg' |
|
|
| def __init__( |
| self, |
| bitspersample=None, |
| tables=None, |
| header=None, |
| colorspace_data=None, |
| colorspace_jpeg=None, |
| level=None, |
| subsampling=None, |
| optimize=None, |
| smoothing=None, |
| ): |
| self.tables = tables |
| self.header = header |
| self.bitspersample = bitspersample |
| self.colorspace_data = colorspace_data |
| self.colorspace_jpeg = colorspace_jpeg |
| self.level = level |
| self.subsampling = subsampling |
| self.optimize = optimize |
| self.smoothing = smoothing |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.jpeg_encode( |
| buf, |
| level=self.level, |
| colorspace=self.colorspace_data, |
| outcolorspace=self.colorspace_jpeg, |
| subsampling=self.subsampling, |
| optimize=self.optimize, |
| smoothing=self.smoothing, |
| ) |
|
|
| def decode(self, buf, out=None): |
| out_shape = None |
| if out is not None: |
| out_shape = out.shape |
| out = protective_squeeze(out) |
| img = imagecodecs.jpeg_decode( |
| buf, |
| bitspersample=self.bitspersample, |
| tables=self.tables, |
| header=self.header, |
| colorspace=self.colorspace_jpeg, |
| outcolorspace=self.colorspace_data, |
| out=out, |
| ) |
| if out_shape is not None: |
| img = img.reshape(out_shape) |
| return img |
|
|
| def get_config(self): |
| """Return dictionary holding configuration parameters.""" |
| config = dict(id=self.codec_id) |
| for key in self.__dict__: |
| if not key.startswith('_'): |
| value = getattr(self, key) |
| if value is not None and key in ('header', 'tables'): |
| import base64 |
|
|
| value = base64.b64encode(value).decode() |
| config[key] = value |
| return config |
|
|
| @classmethod |
| def from_config(cls, config): |
| """Instantiate codec from configuration object.""" |
| for key in ('header', 'tables'): |
| value = config.get(key, None) |
| if value is not None and isinstance(value, str): |
| import base64 |
|
|
| config[key] = base64.b64decode(value.encode()) |
| return cls(**config) |
|
|
|
|
| class Jpeg2k(Codec): |
| """JPEG 2000 codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_jpeg2k' |
|
|
| def __init__( |
| self, |
| level=None, |
| codecformat=None, |
| colorspace=None, |
| tile=None, |
| reversible=None, |
| bitspersample=None, |
| resolutions=None, |
| numthreads=None, |
| verbose=0, |
| ): |
| self.level = level |
| self.codecformat = codecformat |
| self.colorspace = colorspace |
| self.tile = None if tile is None else tuple(tile) |
| self.reversible = reversible |
| self.bitspersample = bitspersample |
| self.resolutions = resolutions |
| self.numthreads = numthreads |
| self.verbose = verbose |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.jpeg2k_encode( |
| buf, |
| level=self.level, |
| codecformat=self.codecformat, |
| colorspace=self.colorspace, |
| tile=self.tile, |
| reversible=self.reversible, |
| bitspersample=self.bitspersample, |
| resolutions=self.resolutions, |
| numthreads=self.numthreads, |
| verbose=self.verbose, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.jpeg2k_decode( |
| buf, verbose=self.verbose, numthreads=self.numthreads, out=out |
| ) |
|
|
|
|
| class JpegLs(Codec): |
| """JPEG LS codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_jpegls' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.jpegls_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.jpegls_decode(buf, out=out) |
|
|
|
|
| class JpegXl(Codec): |
| """JPEG XL codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_jpegxl' |
|
|
| def __init__( |
| self, |
| |
| level=None, |
| effort=None, |
| distance=None, |
| lossless=None, |
| decodingspeed=None, |
| photometric=None, |
| planar=None, |
| usecontainer=None, |
| |
| index=None, |
| keeporientation=None, |
| |
| numthreads=None, |
| ): |
| """ |
| Return JPEG XL image from numpy array. |
| Float must be in nominal range 0..1. |
| |
| Currently L, LA, RGB, RGBA images are supported in contig mode. |
| Extra channels are only supported for grayscale images in planar mode. |
| |
| Parameters |
| ---------- |
| level : Default to None, i.e. not overwriting lossess and decodingspeed options. |
| When < 0: Use lossless compression |
| When in [0,1,2,3,4]: Sets the decoding speed tier for the provided options. |
| Minimum is 0 (slowest to decode, best quality/density), and maximum |
| is 4 (fastest to decode, at the cost of some quality/density). |
| effort : Default to 3. |
| Sets encoder effort/speed level without affecting decoding speed. |
| Valid values are, from faster to slower speed: 1:lightning 2:thunder |
| 3:falcon 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise. |
| Speed: lightning, thunder, falcon, cheetah, hare, wombat, squirrel, kitten, tortoise |
| control the encoder effort in ascending order. |
| This also affects memory usage: using lower effort will typically reduce memory |
| consumption during encoding. |
| lightning and thunder are fast modes useful for lossless mode (modular). |
| falcon disables all of the following tools. |
| cheetah enables coefficient reordering, context clustering, and heuristics for selecting DCT sizes and quantization steps. |
| hare enables Gaborish filtering, chroma from luma, and an initial estimate of quantization steps. |
| wombat enables error diffusion quantization and full DCT size selection heuristics. |
| squirrel (default) enables dots, patches, and spline detection, and full context clustering. |
| kitten optimizes the adaptive quantization for a psychovisual metric. |
| tortoise enables a more thorough adaptive quantization search. |
| distance : Default to 1.0 |
| Sets the distance level for lossy compression: target max butteraugli distance, |
| lower = higher quality. Range: 0 .. 15. 0.0 = mathematically lossless |
| (however, use JxlEncoderSetFrameLossless instead to use true lossless, |
| as setting distance to 0 alone is not the only requirement). |
| 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. |
| lossess : Default to False. |
| Use lossess encoding. |
| decodingspeed : Default to 0. |
| Duplicate to level. [0,4] |
| photometric : Return JxlColorSpace value. |
| Default logic is quite complicated but works most of the time. |
| Accepted value: |
| int: [-1,3] |
| str: ['RGB', |
| 'WHITEISZERO', 'MINISWHITE', |
| 'BLACKISZERO', 'MINISBLACK', 'GRAY', |
| 'XYB', 'KNOWN'] |
| planar : Enable multi-channel mode. |
| Default to false. |
| usecontainer : |
| Forces the encoder to use the box-based container format (BMFF) |
| even when not necessary. |
| When using JxlEncoderUseBoxes, JxlEncoderStoreJPEGMetadata or |
| JxlEncoderSetCodestreamLevel with level 10, the encoder will |
| automatically also use the container format, it is not necessary |
| to use JxlEncoderUseContainer for those use cases. |
| By default this setting is disabled. |
| index : Selectively decode frames for animation. |
| Default to 0, decode all frames. |
| When set to > 0, decode that frame index only. |
| keeporientation : |
| Enables or disables preserving of as-in-bitstream pixeldata orientation. |
| Some images are encoded with an Orientation tag indicating that the |
| decoder must perform a rotation and/or mirroring to the encoded image data. |
| |
| If skip_reorientation is JXL_FALSE (the default): the decoder will apply |
| the transformation from the orientation setting, hence rendering the image |
| according to its specified intent. When producing a JxlBasicInfo, the decoder |
| will always set the orientation field to JXL_ORIENT_IDENTITY (matching the |
| returned pixel data) and also align xsize and ysize so that they correspond |
| to the width and the height of the returned pixel data. |
| |
| If skip_reorientation is JXL_TRUE: the decoder will skip applying the |
| transformation from the orientation setting, returning the image in |
| the as-in-bitstream pixeldata orientation. This may be faster to decode |
| since the decoder doesnt have to apply the transformation, but can |
| cause wrong display of the image if the orientation tag is not correctly |
| taken into account by the user. |
| |
| By default, this option is disabled, and the returned pixel data is |
| re-oriented according to the images Orientation setting. |
| threads : Default to 1. |
| If <= 0, use all cores. |
| If > 32, clipped to 32. |
| """ |
|
|
| self.level = level |
| self.effort = effort |
| self.distance = distance |
| self.lossless = bool(lossless) |
| self.decodingspeed = decodingspeed |
| self.photometric = photometric |
| self.planar = planar |
| self.usecontainer = usecontainer |
| self.index = index |
| self.keeporientation = keeporientation |
| self.numthreads = numthreads |
|
|
| def encode(self, buf): |
| |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.jpegxl_encode( |
| buf, |
| level=self.level, |
| effort=self.effort, |
| distance=self.distance, |
| lossless=self.lossless, |
| decodingspeed=self.decodingspeed, |
| photometric=self.photometric, |
| planar=self.planar, |
| usecontainer=self.usecontainer, |
| numthreads=self.numthreads, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.jpegxl_decode( |
| buf, |
| index=self.index, |
| keeporientation=self.keeporientation, |
| numthreads=self.numthreads, |
| out=out, |
| ) |
|
|
|
|
| class JpegXr(Codec): |
| """JPEG XR codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_jpegxr' |
|
|
| def __init__( |
| self, |
| level=None, |
| photometric=None, |
| hasalpha=None, |
| resolution=None, |
| fp2int=None, |
| ): |
| self.level = level |
| self.photometric = photometric |
| self.hasalpha = hasalpha |
| self.resolution = resolution |
| self.fp2int = fp2int |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.jpegxr_encode( |
| buf, |
| level=self.level, |
| photometric=self.photometric, |
| hasalpha=self.hasalpha, |
| resolution=self.resolution, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.jpegxr_decode(buf, fp2int=self.fp2int, out=out) |
|
|
|
|
| class Lerc(Codec): |
| """LERC codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_lerc' |
|
|
| def __init__(self, level=None, version=None, planar=None): |
| self.level = level |
| self.version = version |
| self.planar = bool(planar) |
| |
| |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.lerc_encode( |
| buf, |
| level=self.level, |
| version=self.version, |
| planar=self.planar, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.lerc_decode(buf, out=out) |
|
|
|
|
| class Ljpeg(Codec): |
| """LJPEG codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_ljpeg' |
|
|
| def __init__(self, bitspersample=None): |
| self.bitspersample = bitspersample |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.ljpeg_encode(buf, bitspersample=self.bitspersample) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.ljpeg_decode(buf, out=out) |
|
|
|
|
| class Lz4(Codec): |
| """LZ4 codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_lz4' |
|
|
| def __init__(self, level=None, hc=False, header=True): |
| self.level = level |
| self.hc = hc |
| self.header = bool(header) |
|
|
| def encode(self, buf): |
| return imagecodecs.lz4_encode( |
| buf, level=self.level, hc=self.hc, header=self.header |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.lz4_decode(buf, header=self.header, out=_flat(out)) |
|
|
|
|
| class Lz4f(Codec): |
| """LZ4F codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_lz4f' |
|
|
| def __init__( |
| self, |
| level=None, |
| blocksizeid=False, |
| contentchecksum=None, |
| blockchecksum=None, |
| ): |
| self.level = level |
| self.blocksizeid = blocksizeid |
| self.contentchecksum = contentchecksum |
| self.blockchecksum = blockchecksum |
|
|
| def encode(self, buf): |
| return imagecodecs.lz4f_encode( |
| buf, |
| level=self.level, |
| blocksizeid=self.blocksizeid, |
| contentchecksum=self.contentchecksum, |
| blockchecksum=self.blockchecksum, |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.lz4f_decode(buf, out=_flat(out)) |
|
|
|
|
| class Lzf(Codec): |
| """LZF codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_lzf' |
|
|
| def __init__(self, header=True): |
| self.header = bool(header) |
|
|
| def encode(self, buf): |
| return imagecodecs.lzf_encode(buf, header=self.header) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.lzf_decode(buf, header=self.header, out=_flat(out)) |
|
|
|
|
| class Lzma(Codec): |
| """LZMA codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_lzma' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| return imagecodecs.lzma_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.lzma_decode(buf, out=_flat(out)) |
|
|
|
|
| class Lzw(Codec): |
| """LZW codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_lzw' |
|
|
| def encode(self, buf): |
| return imagecodecs.lzw_encode(buf) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.lzw_decode(buf, out=_flat(out)) |
|
|
|
|
| class PackBits(Codec): |
| """PackBits codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_packbits' |
|
|
| def __init__(self, axis=None): |
| self.axis = axis |
|
|
| def encode(self, buf): |
| if not isinstance(buf, (bytes, bytearray)): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.packbits_encode(buf, axis=self.axis) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.packbits_decode(buf, out=_flat(out)) |
|
|
|
|
| class Pglz(Codec): |
| """PGLZ codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_pglz' |
|
|
| def __init__(self, header=True, strategy=None): |
| self.header = bool(header) |
| self.strategy = strategy |
|
|
| def encode(self, buf): |
| return imagecodecs.pglz_encode( |
| buf, strategy=self.strategy, header=self.header |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.pglz_decode(buf, header=self.header, out=_flat(out)) |
|
|
|
|
| class Png(Codec): |
| """PNG codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_png' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.png_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.png_decode(buf, out=out) |
|
|
|
|
| class Qoi(Codec): |
| """QOI codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_qoi' |
|
|
| def __init__(self): |
| pass |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.qoi_encode(buf) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.qoi_decode(buf, out=out) |
|
|
|
|
| class Rgbe(Codec): |
| """RGBE codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_rgbe' |
|
|
| def __init__(self, header=False, shape=None, rle=None): |
| if not header and shape is None: |
| raise ValueError('must specify data shape if no header') |
| if shape and shape[-1] != 3: |
| raise ValueError('invalid shape') |
| self.shape = shape |
| self.header = bool(header) |
| self.rle = None if rle is None else bool(rle) |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.rgbe_encode(buf, header=self.header, rle=self.rle) |
|
|
| def decode(self, buf, out=None): |
| if out is None and not self.header: |
| out = numpy.empty(self.shape, numpy.float32) |
| return imagecodecs.rgbe_decode( |
| buf, header=self.header, rle=self.rle, out=out |
| ) |
|
|
|
|
| class Rcomp(Codec): |
| """Rcomp codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_rcomp' |
|
|
| def __init__(self, shape, dtype, nblock=None): |
| self.shape = tuple(shape) |
| self.dtype = numpy.dtype(dtype).str |
| self.nblock = nblock |
|
|
| def encode(self, buf): |
| return imagecodecs.rcomp_encode(buf, nblock=self.nblock) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.rcomp_decode( |
| buf, |
| shape=self.shape, |
| dtype=self.dtype, |
| nblock=self.nblock, |
| out=out, |
| ) |
|
|
|
|
| class Snappy(Codec): |
| """Snappy codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_snappy' |
|
|
| def encode(self, buf): |
| return imagecodecs.snappy_encode(buf) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.snappy_decode(buf, out=_flat(out)) |
|
|
|
|
| class Spng(Codec): |
| """SPNG codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_spng' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.spng_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.spng_decode(buf, out=out) |
|
|
|
|
| class Tiff(Codec): |
| """TIFF codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_tiff' |
|
|
| def __init__(self, index=None, asrgb=None, verbose=0): |
| self.index = index |
| self.asrgb = bool(asrgb) |
| self.verbose = verbose |
|
|
| def encode(self, buf): |
| |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.tiff_encode(buf) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.tiff_decode( |
| buf, |
| index=self.index, |
| asrgb=self.asrgb, |
| verbose=self.verbose, |
| out=out, |
| ) |
|
|
|
|
| class Webp(Codec): |
| """WebP codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_webp' |
|
|
| def __init__(self, level=None, lossless=None, method=None, hasalpha=None): |
| self.level = level |
| self.hasalpha = bool(hasalpha) |
| self.method = method |
| self.lossless = lossless |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| return imagecodecs.webp_encode( |
| buf, level=self.level, lossless=self.lossless, method=self.method |
| ) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.webp_decode(buf, hasalpha=self.hasalpha, out=out) |
|
|
|
|
| class Xor(Codec): |
| """XOR codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_xor' |
|
|
| def __init__(self, shape=None, dtype=None, axis=-1): |
| self.shape = None if shape is None else tuple(shape) |
| self.dtype = None if dtype is None else numpy.dtype(dtype).str |
| self.axis = axis |
|
|
| def encode(self, buf): |
| if self.shape is not None or self.dtype is not None: |
| buf = protective_squeeze(numpy.asarray(buf)) |
| assert buf.shape == self.shape |
| assert buf.dtype == self.dtype |
| return imagecodecs.xor_encode(buf, axis=self.axis).tobytes() |
|
|
| def decode(self, buf, out=None): |
| if self.shape is not None or self.dtype is not None: |
| buf = numpy.frombuffer(buf, dtype=self.dtype).reshape(*self.shape) |
| return imagecodecs.xor_decode(buf, axis=self.axis, out=_flat(out)) |
|
|
|
|
| class Zfp(Codec): |
| """ZFP codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_zfp' |
|
|
| def __init__( |
| self, |
| shape=None, |
| dtype=None, |
| strides=None, |
| level=None, |
| mode=None, |
| execution=None, |
| numthreads=None, |
| chunksize=None, |
| header=True, |
| ): |
| if header: |
| self.shape = None |
| self.dtype = None |
| self.strides = None |
| elif shape is None or dtype is None: |
| raise ValueError('invalid shape or dtype') |
| else: |
| self.shape = tuple(shape) |
| self.dtype = numpy.dtype(dtype).str |
| self.strides = None if strides is None else tuple(strides) |
| self.level = level |
| self.mode = mode |
| self.execution = execution |
| self.numthreads = numthreads |
| self.chunksize = chunksize |
| self.header = bool(header) |
|
|
| def encode(self, buf): |
| buf = protective_squeeze(numpy.asarray(buf)) |
| if not self.header: |
| assert buf.shape == self.shape |
| assert buf.dtype == self.dtype |
| return imagecodecs.zfp_encode( |
| buf, |
| level=self.level, |
| mode=self.mode, |
| execution=self.execution, |
| header=self.header, |
| numthreads=self.numthreads, |
| chunksize=self.chunksize, |
| ) |
|
|
| def decode(self, buf, out=None): |
| if self.header: |
| return imagecodecs.zfp_decode(buf, out=out) |
| return imagecodecs.zfp_decode( |
| buf, |
| shape=self.shape, |
| dtype=numpy.dtype(self.dtype), |
| strides=self.strides, |
| numthreads=self.numthreads, |
| out=out, |
| ) |
|
|
|
|
| class Zlib(Codec): |
| """Zlib codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_zlib' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| return imagecodecs.zlib_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.zlib_decode(buf, out=_flat(out)) |
|
|
|
|
| class Zlibng(Codec): |
| """Zlibng codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_zlibng' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| return imagecodecs.zlibng_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.zlibng_decode(buf, out=_flat(out)) |
|
|
|
|
| class Zopfli(Codec): |
| """Zopfli codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_zopfli' |
|
|
| def encode(self, buf): |
| return imagecodecs.zopfli_encode(buf) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.zopfli_decode(buf, out=_flat(out)) |
|
|
|
|
| class Zstd(Codec): |
| """ZStandard codec for numcodecs.""" |
|
|
| codec_id = 'imagecodecs_zstd' |
|
|
| def __init__(self, level=None): |
| self.level = level |
|
|
| def encode(self, buf): |
| return imagecodecs.zstd_encode(buf, level=self.level) |
|
|
| def decode(self, buf, out=None): |
| return imagecodecs.zstd_decode(buf, out=_flat(out)) |
|
|
|
|
| def _flat(out): |
| """Return numpy array as contiguous view of bytes if possible.""" |
| if out is None: |
| return None |
| view = memoryview(out) |
| if view.readonly or not view.contiguous: |
| return None |
| return view.cast('B') |
|
|
|
|
| def register_codecs(codecs=None, force=False, verbose=True): |
| """Register codecs in this module with numcodecs.""" |
| for name, cls in globals().items(): |
| if not hasattr(cls, 'codec_id') or name == 'Codec': |
| continue |
| if codecs is not None and cls.codec_id not in codecs: |
| continue |
| try: |
| try: |
| get_codec({'id': cls.codec_id}) |
| except TypeError: |
| |
| pass |
| except ValueError: |
| |
| pass |
| else: |
| if not force: |
| if verbose: |
| log_warning( |
| f'numcodec {cls.codec_id!r} already registered' |
| ) |
| continue |
| if verbose: |
| log_warning(f'replacing registered numcodec {cls.codec_id!r}') |
| register_codec(cls) |
|
|
|
|
| def log_warning(msg, *args, **kwargs): |
| """Log message with level WARNING.""" |
| import logging |
|
|
| logging.getLogger(__name__).warning(msg, *args, **kwargs) |
|
|