diff --git a/CHANGELOG.md b/CHANGELOG.md index 5787285..64b8c94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.2.0] - 2023-11-29 + +### Added + +- Methods for getting rgb and grayscale (8, 12, and 16 bits) Pillow images. + +### Changed + +- Scale samples to number of bits in output. +- RLE segment are even number of bytes. +- MCT used for jpeg2000 lossless YBR. + ## [0.1.0] - 2023-11-22 ### Added diff --git a/pyproject.toml b/pyproject.toml index 08fd101..dfb3680 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wsidicom-data" -version = "0.1.0" +version = "0.2.0" description = "Test data for wsidicom" authors = ["Erik O Gabrielsson "] license = "Apache-2.0" diff --git a/wsidicom_data/create_encoded_test_files.py b/wsidicom_data/create_encoded_test_files.py index 6b24e3d..8b2c8d1 100644 --- a/wsidicom_data/create_encoded_test_files.py +++ b/wsidicom_data/create_encoded_test_files.py @@ -16,21 +16,14 @@ import hashlib -from PIL import Image -from wsidicom.codec import ( - Channels, - Encoder, -) +from wsidicom.codec import Encoder from wsidicom_data.test_data import EncodedTestData, TestData, defined_encoder_settings def create_encoded_test_files(): for settings in defined_encoder_settings: - test_tile_path = TestData.get_test_tile_path() - image = Image.open(test_tile_path) - if settings.channels == Channels.GRAYSCALE: - image = image.convert("L") + image = TestData.image(settings.bits, settings.samples_per_pixel) encoder = Encoder.create(settings) encoded = encoder.encode(image) output_path = EncodedTestData.get_filepath_for_encoder_settings(settings) diff --git a/wsidicom_data/data/encoded/Explicit_VR_Big_Endian-GRAYSCALE-16.raw b/wsidicom_data/data/encoded/Explicit_VR_Big_Endian-GRAYSCALE-16.raw index 4c50a30..3266ca8 100644 Binary files a/wsidicom_data/data/encoded/Explicit_VR_Big_Endian-GRAYSCALE-16.raw and b/wsidicom_data/data/encoded/Explicit_VR_Big_Endian-GRAYSCALE-16.raw differ diff --git a/wsidicom_data/data/encoded/Explicit_VR_Little_Endian-GRAYSCALE-16.raw b/wsidicom_data/data/encoded/Explicit_VR_Little_Endian-GRAYSCALE-16.raw index 3266ca8..4c50a30 100644 Binary files a/wsidicom_data/data/encoded/Explicit_VR_Little_Endian-GRAYSCALE-16.raw and b/wsidicom_data/data/encoded/Explicit_VR_Little_Endian-GRAYSCALE-16.raw differ diff --git a/wsidicom_data/data/encoded/Implicit_VR_Little_Endian-GRAYSCALE-16.raw b/wsidicom_data/data/encoded/Implicit_VR_Little_Endian-GRAYSCALE-16.raw index 3266ca8..4c50a30 100644 Binary files a/wsidicom_data/data/encoded/Implicit_VR_Little_Endian-GRAYSCALE-16.raw and b/wsidicom_data/data/encoded/Implicit_VR_Little_Endian-GRAYSCALE-16.raw differ diff --git a/wsidicom_data/data/encoded/JPEG-LS_Lossless_Image_Compression-GRAYSCALE-16.jls b/wsidicom_data/data/encoded/JPEG-LS_Lossless_Image_Compression-GRAYSCALE-16.jls index f6c29d2..83c48b9 100644 Binary files a/wsidicom_data/data/encoded/JPEG-LS_Lossless_Image_Compression-GRAYSCALE-16.jls and b/wsidicom_data/data/encoded/JPEG-LS_Lossless_Image_Compression-GRAYSCALE-16.jls differ diff --git a/wsidicom_data/data/encoded/JPEG-LS_Lossy_(Near-Lossless)_Image_Compression-GRAYSCALE-16.jls b/wsidicom_data/data/encoded/JPEG-LS_Lossy_(Near-Lossless)_Image_Compression-GRAYSCALE-16.jls index e4f5795..0849e52 100644 Binary files a/wsidicom_data/data/encoded/JPEG-LS_Lossy_(Near-Lossless)_Image_Compression-GRAYSCALE-16.jls and b/wsidicom_data/data/encoded/JPEG-LS_Lossy_(Near-Lossless)_Image_Compression-GRAYSCALE-16.jls differ diff --git a/wsidicom_data/data/encoded/JPEG_2000_Image_Compression-GRAYSCALE-16.jp2 b/wsidicom_data/data/encoded/JPEG_2000_Image_Compression-GRAYSCALE-16.jp2 index c52a162..23e00cb 100644 Binary files a/wsidicom_data/data/encoded/JPEG_2000_Image_Compression-GRAYSCALE-16.jp2 and b/wsidicom_data/data/encoded/JPEG_2000_Image_Compression-GRAYSCALE-16.jp2 differ diff --git a/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-GRAYSCALE-16.jp2 b/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-GRAYSCALE-16.jp2 index 67b579d..3823709 100644 Binary files a/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-GRAYSCALE-16.jp2 and b/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-GRAYSCALE-16.jp2 differ diff --git a/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-YBR-8.jp2 b/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-YBR-8.jp2 index f41582d..7c66753 100644 Binary files a/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-YBR-8.jp2 and b/wsidicom_data/data/encoded/JPEG_2000_Image_Compression_(Lossless_Only)-YBR-8.jp2 differ diff --git a/wsidicom_data/data/encoded/JPEG_Extended_(Process_2_and_4)-GRAYSCALE-12.jpg b/wsidicom_data/data/encoded/JPEG_Extended_(Process_2_and_4)-GRAYSCALE-12.jpg index 6375b0d..2459066 100644 Binary files a/wsidicom_data/data/encoded/JPEG_Extended_(Process_2_and_4)-GRAYSCALE-12.jpg and b/wsidicom_data/data/encoded/JPEG_Extended_(Process_2_and_4)-GRAYSCALE-12.jpg differ diff --git a/wsidicom_data/data/encoded/JPEG_Lossless,_Non-Hierarchical_(Process_14)-GRAYSCALE-16.jpg b/wsidicom_data/data/encoded/JPEG_Lossless,_Non-Hierarchical_(Process_14)-GRAYSCALE-16.jpg index 7b28252..9bbd8e7 100644 Binary files a/wsidicom_data/data/encoded/JPEG_Lossless,_Non-Hierarchical_(Process_14)-GRAYSCALE-16.jpg and b/wsidicom_data/data/encoded/JPEG_Lossless,_Non-Hierarchical_(Process_14)-GRAYSCALE-16.jpg differ diff --git a/wsidicom_data/data/encoded/RLE_Lossless-GRAYSCALE-16.rle b/wsidicom_data/data/encoded/RLE_Lossless-GRAYSCALE-16.rle index cb09688..34afbfe 100644 Binary files a/wsidicom_data/data/encoded/RLE_Lossless-GRAYSCALE-16.rle and b/wsidicom_data/data/encoded/RLE_Lossless-GRAYSCALE-16.rle differ diff --git a/wsidicom_data/test_data.py b/wsidicom_data/test_data.py index 1d6737c..8b4296f 100644 --- a/wsidicom_data/test_data.py +++ b/wsidicom_data/test_data.py @@ -28,15 +28,56 @@ Settings, Subsampling, ) +from PIL.Image import Image as PILImage +from PIL import Image class TestData: test_data_path = Path(__file__).parent.joinpath("data") @classmethod - def get_test_tile_path(cls): + def get_test_tile_path(cls) -> Path: return cls.test_data_path.joinpath("test_tile.png") + @classmethod + def rgb(cls) -> PILImage: + return Image.open(cls.get_test_tile_path()) + + @classmethod + def grayscale_8(cls) -> PILImage: + return cls.rgb().convert("L") + + @classmethod + def grayscale_12(cls) -> PILImage: + return cls._scale_bits(cls._grayscale_32(), 8, 12).convert("I;16L") + + @classmethod + def grayscale_16(cls) -> PILImage: + return cls._scale_bits(cls._grayscale_32(), 8, 16).convert("I;16L") + + @classmethod + def image(cls, bits: int, samples_per_pixel: int) -> PILImage: + if samples_per_pixel == 1: + if bits == 8: + return cls.grayscale_8() + elif bits == 12: + return cls.grayscale_12() + elif bits == 16: + return cls.grayscale_16() + elif samples_per_pixel == 3 and bits == 8: + return cls.rgb() + raise ValueError( + f"Unsupported bit depth {bits} or samples_per_pixel {samples_per_pixel}." + ) + + @classmethod + def _grayscale_32(cls) -> PILImage: + return cls.rgb().convert("I") + + @classmethod + def _scale_bits(cls, image: PILImage, from_bits: int, to_bits: int) -> PILImage: + return image.point(lambda x: 2 ** (to_bits - from_bits) * x) + class EncodedTestData: test_data_path = Path(__file__).parent.joinpath("data", "encoded")