From dd0a56477b2c167e264f0cf68101a25c6ec4fe52 Mon Sep 17 00:00:00 2001 From: Erik O Gabrielsson <83275777+erikogabrielsson@users.noreply.github.com> Date: Wed, 9 Aug 2023 08:18:16 +0200 Subject: [PATCH 1/2] Run codespell (#107) * Spelling fixes * Fix error in read_thumbnail usage --- CHANGELOG.md | 13 ++- README.md | 14 +-- poetry.lock | 24 ++++- pyproject.toml | 1 + tests/download_test_images.py | 92 +++++++++---------- tests/test_save.py | 2 +- wsidicom/conceptcode.py | 3 +- wsidicom/errors.py | 6 +- wsidicom/file/wsidicom_file.py | 6 +- wsidicom/file/wsidicom_file_image_data.py | 2 +- wsidicom/file/wsidicom_file_source.py | 2 +- wsidicom/file/wsidicom_file_writer.py | 2 +- wsidicom/graphical_annotations.py | 14 +-- wsidicom/group/group.py | 10 +- wsidicom/group/level.py | 14 +-- wsidicom/instance/dataset.py | 5 +- wsidicom/instance/image_coordinate_system.py | 2 +- wsidicom/instance/image_data.py | 4 +- wsidicom/instance/instance.py | 2 +- .../instance/tile_index/full_tile_index.py | 4 +- .../instance/tile_index/sparse_tile_index.py | 4 +- wsidicom/instance/wsidicom_image_data.py | 4 +- wsidicom/optical.py | 10 +- wsidicom/series/series.py | 2 +- wsidicom/source.py | 4 +- wsidicom/stringprinting.py | 10 +- wsidicom/target.py | 4 +- wsidicom/uid.py | 2 +- wsidicom/wsidicom.py | 8 +- 29 files changed, 146 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8e0f7d0..9a701e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Error in documentation of read_overview() in readme. +- Spellings. + ## [0.10.0] - 2023-06-01 ### Added @@ -31,11 +36,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support for opening DICOM WSI using DICOMWeb. - save() now takes the optional parameter add_missing_levels, that enables adding missing pyramid levels up to the single-tile level. -- read_region(), read_region_mm(), and read_region_mpp() takes an optional parameter threads, that allows multiple threads to be used for stitching togheter the region. +- read_region(), read_region_mm(), and read_region_mpp() takes an optional parameter threads, that allows multiple threads to be used for stitching together the region. ### Changed -- WsiDicom is now initalized using a Source, that is responsible for provides the instances to view. +- WsiDicom is now initialized using a Source, that is responsible for provides the instances to view. - Saving a WsiDicom is now handled by WsiDicomFileTarget. - Refactoring due to adding support for DICOMWeb and opening instances using Source and saving using Target. - Frame positions and tiling for levels are now parsed lazily, e.g. on first tile access. @@ -81,7 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Focal planes are considered equal if configurable withing threshold distance, see config.focal_plane_distance_threshold +- Focal planes are considered equal if configurable within threshold distance, see config.focal_plane_distance_threshold - Option to read region defined in slide coordinate system for get_region_mm(). ### Changed @@ -109,7 +114,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Order of paramaters for ConceptCode matches pydicom Code. +- Order of parameters for ConceptCode matches pydicom Code. ## [0.3.0] - 2022-04-20 diff --git a/README.md b/README.md index 552aa6b5..571c96bd 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ region_mpp = slide.read_region_mpp((0, 0), 0.01, (3, 3)) ***Read a thumbnail of the whole slide with maximum dimensions 200x200 px.*** ```python -thumbnail = slide.read_thumbnail(200, 200) +thumbnail = slide.read_thumbnail((200, 200)) ``` ***Read an overview image (if available).*** @@ -146,7 +146,7 @@ slide.close() An opened WsiDicom instance can be saved to a new path using the save()-method. The produced files will be: - Fully tiled. Any sparse tiles will be replaced with a blank tile with color depending on the photometric interpretation. -- Have a basic offset table (or optionally an exteded offset table or no offset table). +- Have a basic offset table (or optionally an extended offset table or no offset table). - Not be concatenated. The frames are copied as-is, i.e. without re-compression. @@ -156,7 +156,7 @@ with WsiDicom.open(path_to_folder) as slide: slide.save(path_to_output) ``` -The output folder must already exists. Be careful to specify a unique folder folder to avoid mixing files from diferent images. +The output folder must already exists. Be careful to specify a unique folder folder to avoid mixing files from different images. ## Settings @@ -175,7 +175,7 @@ Annotations are structured in a hierarchy: - AnnotationInstance Represents a collection of AnnotationGroups. All the groups have the same frame of reference, i.e. annotations are from the same wsi stack. - AnnotationGroup - Represents a group of annotations. All annotations in the group are of the same type (e.g. PointAnnotation), have the same label, description and category and type. The category and type are codes that are used to define the annotated feature. A good resource for working with codes is avaiable [here](https://qiicr.gitbook.io/dcmqi-guide/opening/coding_schemes). + Represents a group of annotations. All annotations in the group are of the same type (e.g. PointAnnotation), have the same label, description and category and type. The category and type are codes that are used to define the annotated feature. A good resource for working with codes is available [here](https://qiicr.gitbook.io/dcmqi-guide/opening/coding_schemes). - Annotation Represents a annotation. An Annotation has a geometry (currently Point, Polyline, Polygon) and an optional list of Measurements. - Measurement @@ -257,7 +257,7 @@ To watch unit tests use: poetry run pytest-watch -- -m unittest ``` -The integration tests uses test images from nema.org thats needs to be downloaded. The location of the test images can be changed from the default tests\testdata\slides using the enviroment variable WSIDICOM_TESTDIR. Download the images using the supplied script: +The integration tests uses test images from nema.org that's needs to be downloaded. The location of the test images can be changed from the default tests\testdata\slides using the environment variable WSIDICOM_TESTDIR. Download the images using the supplied script: ```console python .\tests\download_test_images.py @@ -285,7 +285,7 @@ A WSI DICOM pyramid is in *wsidicom* represented by a hierarchy of objects of di Labels and overviews are structured similarly to levels, but with somewhat different properties and restrictions. For DICOMWeb the WsiDicomFile\* classes are replaced with WsiDicomWeb\* classes. -A Source is used to create WsiInstances, either from files (*WsiDicomFileSource*) or DICOMWeb (*WsiDicomWebSource*), and can be used to to initate a *WsiDicom* object. A source is easiest created with the open() and open_web() helper functions, e.g.: +A Source is used to create WsiInstances, either from files (*WsiDicomFileSource*) or DICOMWeb (*WsiDicomWebSource*), and can be used to to Initiate a *WsiDicom* object. A source is easiest created with the open() and open_web() helper functions, e.g.: ```python slide = WsiDicom.open(path_to_folder) @@ -342,4 +342,4 @@ Our aim is to provide constructive and positive code reviews for all submissions *wsidicom*: Copyright 2021 Sectra AB, licensed under Apache 2.0. -This project is part of a project that has received funding from the Innovative Medicines Initiative 2 Joint Undertaking under grant agreement No 945358. This Joint Undertaking receives support from the European Union’s Horizon 2020 research and innovation programme and EFPIA. IMI website: www.imi.europa.eu +This project is part of a project that has received funding from the Innovative Medicines Initiative 2 Joint Undertaking under grant agreement No 945358. This Joint Undertaking receives support from the European Union’s Horizon 2020 research and innovation programme and EFPIA. IMI website: diff --git a/poetry.lock b/poetry.lock index 84f4f9f6..f0b43a22 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. [[package]] name = "attrs" @@ -181,6 +181,24 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "codespell" +version = "2.2.5" +description = "Codespell" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "codespell-2.2.5-py3-none-any.whl", hash = "sha256:efa037f54b73c84f7bd14ce8e853d5f822cdd6386ef0ff32e957a3919435b9ec"}, + {file = "codespell-2.2.5.tar.gz", hash = "sha256:6d9faddf6eedb692bf80c9a94ec13ab4f5fb585aabae5f3750727148d7b5be56"}, +] + +[package.extras] +dev = ["Pygments", "build", "chardet", "pytest", "pytest-cov", "pytest-dependency", "ruff", "tomli"] +hard-encoding-detection = ["chardet"] +toml = ["tomli"] +types = ["chardet (>=5.1.0)", "mypy", "pytest", "pytest-cov", "pytest-dependency"] + [[package]] name = "colorama" version = "0.4.6" @@ -787,5 +805,5 @@ files = [ [metadata] lock-version = "2.0" -python-versions = ">=3.8,<3.12" -content-hash = "ae8e70616e5a119feadb0ffab5af1619cf3a68369228d68f430319b879a822cf" +python-versions = "^3.8" +content-hash = "264f7d4363a7398ef5c95db0a9946e5b7d3f177b291c914818935d0988ee72c9" diff --git a/pyproject.toml b/pyproject.toml index 136981d5..5ecbb5f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ pycodestyle = "^2.8.0" black = "^23.1.0" flake8 = "^4.0.1" parameterized = "^0.8.1" +codespell = "^2.2.5" [build-system] requires = ["poetry-core>=1.2.0"] diff --git a/tests/download_test_images.py b/tests/download_test_images.py index 6d1520ad..8ba6ee5d 100644 --- a/tests/download_test_images.py +++ b/tests/download_test_images.py @@ -19,62 +19,62 @@ from typing import Any, Dict -FILESERVER = 'medical.nema.org' -FILESERVER_SLIDE_PATH = Path('MEDICAL/Dicom/DataSets/WG26') +FILESERVER = "medical.nema.org" +FILESERVER_SLIDE_PATH = Path("MEDICAL/Dicom/DataSets/WG26") SLIDES: Dict[str, Dict[str, Any]] = { - 'FULL_WITH_BOT': { - 'name': r'Histech^Samantha [1229631]', - 'parentpath': r'WG26Demo2020_PV', - 'subpath': r'20190104 140000 [Case S - Colon polyps]/Series 000 [SM]', - 'files': { - '2.25.173648596820938096199028939965251554503.dcm': '865538d55fce37ae6d91d85aebe29029', # NOQA - '2.25.181487944453580109633363498147571426374.dcm': '7ff4acf3c71572236ce968e06e79d8db', # NOQA - '2.25.191907898033754830752233761154920949936.dcm': '596183ec0444fedaba981de2f94652cb', # NOQA - '2.25.209236321826383427842899333369775338594.dcm': 'f7fe907553f036ec6d2a68443f006fd4', # NOQA - '2.25.222943316513786317622687980099326639180.dcm': 'ec1db6ca69c7d6fe8c4dacb04051ff11', # NOQA - '2.25.251277550657103455222613143487830679046.dcm': '03399b8332c967a9a97e67bffdd70fb9', # NOQA - '2.25.253973508129488054885063915385651983009.dcm': 'ac85a6f618ed0ca2bdf921be429fa185', # NOQA - '2.25.259312801889857550164526960213815274816.dcm': 'b7c64d1ed975b42f2b1b4d917c4ba8c0', # NOQA - '2.25.264278491200307498225194538752823371217.dcm': '0cc906dbeb22e10bff65f9b6d8961fa7', # NOQA - '2.25.290884199110265475119989552423006928136.dcm': 'b8faf60dc44e4cb9aa5e7ab92a706b88', # NOQA - '2.25.315427219625170954090644500893748466389.dcm': '84cca002af2e6263f25913b2896e36db', # NOQA - '2.25.339652625381363485545547336547695948130.dcm': '52ea8ec41f3f86368d50542c9ed41975', # NOQA - '2.25.98447601926716844575918590187306802549.dcm': '382e1b6780404efb7f44c65444694b05' # NOQA - } + "FULL_WITH_BOT": { + "name": r"Histech^Samantha [1229631]", + "parentpath": r"WG26Demo2020_PV", + "subpath": r"20190104 140000 [Case S - Colon polyps]/Series 000 [SM]", + "files": { + "2.25.173648596820938096199028939965251554503.dcm": "865538d55fce37ae6d91d85aebe29029", # NOQA + "2.25.181487944453580109633363498147571426374.dcm": "7ff4acf3c71572236ce968e06e79d8db", # NOQA + "2.25.191907898033754830752233761154920949936.dcm": "596183ec0444fedaba981de2f94652cb", # NOQA + "2.25.209236321826383427842899333369775338594.dcm": "f7fe907553f036ec6d2a68443f006fd4", # NOQA + "2.25.222943316513786317622687980099326639180.dcm": "ec1db6ca69c7d6fe8c4dacb04051ff11", # NOQA + "2.25.251277550657103455222613143487830679046.dcm": "03399b8332c967a9a97e67bffdd70fb9", # NOQA + "2.25.253973508129488054885063915385651983009.dcm": "ac85a6f618ed0ca2bdf921be429fa185", # NOQA + "2.25.259312801889857550164526960213815274816.dcm": "b7c64d1ed975b42f2b1b4d917c4ba8c0", # NOQA + "2.25.264278491200307498225194538752823371217.dcm": "0cc906dbeb22e10bff65f9b6d8961fa7", # NOQA + "2.25.290884199110265475119989552423006928136.dcm": "b8faf60dc44e4cb9aa5e7ab92a706b88", # NOQA + "2.25.315427219625170954090644500893748466389.dcm": "84cca002af2e6263f25913b2896e36db", # NOQA + "2.25.339652625381363485545547336547695948130.dcm": "52ea8ec41f3f86368d50542c9ed41975", # NOQA + "2.25.98447601926716844575918590187306802549.dcm": "382e1b6780404efb7f44c65444694b05", # NOQA + }, + }, + "SPARSE_NO_BOT": { + "name": r"MoticWangjie^Professer [100001]", + "parentpath": r"WG26Demo2019_PV", + "subpath": r"20190903 102029 [200001]\Series 000 [SM]", + "files": { + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753444.778.dcm": "4463e99bea080b14591f0665a6e84559", # NOQA + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753447.783.dcm": "0b48b76e46c754fc75b13d6fbf682a19", # NOQA + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753450.788.dcm": "75452d5f811f5c8c8f4475fbac6665bb", # NOQA + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.793.dcm": "3bf4fee344571bda7005d9735b5bd699", # NOQA + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.798.dcm": "84f2b3ba111ec09beb8846d17ddcd9e5", # NOQA + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.803.dcm": "d2e565d600a573685ca1be48d96a0ef4", # NOQA + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.808.dcm": "aedbe1f7ba9f15754078e3c8a9077fee", # NOQA + "1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.813.dcm": "3533d30ef5e9abe3b8ee6e1c7bc0fb17", # NOQA + }, }, - 'SPARSE_NO_BOT': { - 'name': r'MoticWangjie^Professer [100001]', - 'parentpath': r'WG26Demo2019_PV', - 'subpath': r'20190903 102029 [200001]\Series 000 [SM]', - 'files': { - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753444.778.dcm': '4463e99bea080b14591f0665a6e84559', # NOQA - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753447.783.dcm': '0b48b76e46c754fc75b13d6fbf682a19', # NOQA - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753450.788.dcm': '75452d5f811f5c8c8f4475fbac6665bb', # NOQA - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.793.dcm': '3bf4fee344571bda7005d9735b5bd699', # NOQA - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.798.dcm': '84f2b3ba111ec09beb8846d17ddcd9e5', # NOQA - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.803.dcm': 'd2e565d600a573685ca1be48d96a0ef4', # NOQA - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.808.dcm': 'aedbe1f7ba9f15754078e3c8a9077fee', # NOQA - '1.2.276.0.7230010.3.1.4.1145362585.2096.1567753451.813.dcm': '3533d30ef5e9abe3b8ee6e1c7bc0fb17' # NOQA - } - } } def cwd_to_folder(ftp, folder: Path): - ftp.cwd('/') + ftp.cwd("/") for subfolder in folder.parts: ftp.cwd(str(subfolder)) def download_file(ftp: FTP, file: str, filename: Path): - with open(filename, 'wb') as fp: - ftp.retrbinary(f'RETR {file}', fp.write) + with open(filename, "wb") as fp: + ftp.retrbinary(f"RETR {file}", fp.write) def get_slide_dir() -> Path: - DEFAULT_DIR = 'tests/testdata' - SLIDE_DIR = 'slides' + DEFAULT_DIR = "tests/testdata" + SLIDE_DIR = "slides" test_data_path = os.environ.get("DICOM_TESTDIR") if test_data_path is None: test_data_dir = Path(DEFAULT_DIR) @@ -89,15 +89,13 @@ def get_slide_dir() -> Path: def get_or_check_slide(slide_dir: Path, slide: Dict[str, Any], ftp: FTP): - path = slide_dir.joinpath(slide['name'], slide['subpath']) + path = slide_dir.joinpath(slide["name"], slide["subpath"]) ftp_path = FILESERVER_SLIDE_PATH.joinpath( - slide['parentpath'], - slide['name'], - slide['subpath'] + slide["parentpath"], slide["name"], slide["subpath"] ) os.makedirs(path, exist_ok=True) cwd_to_folder(ftp, ftp_path) - for file, checksum in slide['files'].items(): + for file, checksum in slide["files"].items(): file_path = path.joinpath(file) if not file_path.exists(): print( @@ -111,7 +109,7 @@ def get_or_check_slide(slide_dir: Path, slide: Dict[str, Any], ftp: FTP): def check_checksum(file_path: Path, checksum: str): - with open(file_path, 'rb') as saved_file: + with open(file_path, "rb") as saved_file: data = saved_file.read() file_checksum = md5(data).hexdigest() if checksum != file_checksum: diff --git a/tests/test_save.py b/tests/test_save.py index 3558b30b..bac9c661 100644 --- a/tests/test_save.py +++ b/tests/test_save.py @@ -177,7 +177,7 @@ def _get_relative_path(slide_path: Path) -> Path: def create_test_dataset(frame_count: int, image_data: WsiDicomTestImageData): dataset = Dataset() dataset.SOPClassUID = WSI_SOP_CLASS_UID - dataset.ImageType = ["ORGINAL", "PRIMARY", "VOLUME", "NONE"] + dataset.ImageType = ["ORIGINAL", "PRIMARY", "VOLUME", "NONE"] dataset.NumberOfFrames = frame_count dataset.SOPInstanceUID = generate_uid() dataset.StudyInstanceUID = generate_uid() diff --git a/wsidicom/conceptcode.py b/wsidicom/conceptcode.py index dc413ca8..8d0452d1 100644 --- a/wsidicom/conceptcode.py +++ b/wsidicom/conceptcode.py @@ -228,7 +228,7 @@ def _from_cid(cls, meaning: str) -> Code: for code in cls.cid.values(): if code.meaning == meaning: return code - raise ValueError("Unsupported code") + raise ValueError(f"Unsupported code with meaning {meaning}.") @classmethod def list(cls) -> List[str]: @@ -254,6 +254,7 @@ def from_code_value( code.meaning, code.value, code.scheme_designator, code.scheme_version ) + class UnitCode(SingleConceptCode): """Code for concepts representing units according to UCUM scheme""" diff --git a/wsidicom/errors.py b/wsidicom/errors.py index 498f35da..63a37908 100644 --- a/wsidicom/errors.py +++ b/wsidicom/errors.py @@ -38,14 +38,14 @@ def __str__(self): class WsiDicomMatchError(Exception): - """Raised if item in group that should match doesnt match.""" + """Raised if item in group that should match does not match.""" def __init__(self, item: str, group: str): self.item = item self.group = group def __str__(self): - return f"{self.item} doesnt match {self.group}" + return f"{self.item} does not match {self.group}" class WsiDicomUidDuplicateError(Exception): @@ -89,7 +89,7 @@ class WsiDicomRequirementError(Exception): """Raised if required attribute is missing.""" -class WsiDicomNoResultionError(Exception): +class WsiDicomNoResolutionError(Exception): """Raised if method is not possible as resolution is missing in image data.""" diff --git a/wsidicom/file/wsidicom_file.py b/wsidicom/file/wsidicom_file.py index e609ff6e..f7dbfc65 100644 --- a/wsidicom/file/wsidicom_file.py +++ b/wsidicom/file/wsidicom_file.py @@ -366,7 +366,7 @@ def _read_sequence_delimiter(self): TAG_BYTES = 4 self._file.seek(-TAG_BYTES, 1) if self._file.read_tag() != SequenceDelimiterTag: - raise WsiDicomFileError(self._file, "No sequence delimeter tag") + raise WsiDicomFileError(self._file, "No sequence delimiter tag") def read_frame(self, frame_index: int) -> bytes: """Return frame data from pixel data by frame index. @@ -411,11 +411,11 @@ def _parse_pixel_data(self) -> Tuple[List[Tuple[int, int]], OffsetTableType]: then not be empty. A BOT most always be the first item in the Pixel data, but can be empty (zero length). If EOT is used BOT should be empty. - First seach to pixel data position, which is either EOT tag or PixelData tag. + First search to pixel data position, which is either EOT tag or PixelData tag. If EOT read the EOT. For all cases validate that the filepointer now is at the PixelData tag. If BOT read the BOT, otherwise skip the BOT. If EOT nor BOT has been read, parse frame positions from pixel data. Otherwise parse frame - positions from EOT or BOT. Finaly check that the number of read frames equals + positions from EOT or BOT. Finally check that the number of read frames equals the specified number of frames, otherwise frames are fragmented which we dont support. diff --git a/wsidicom/file/wsidicom_file_image_data.py b/wsidicom/file/wsidicom_file_image_data.py index 9f529034..f0d823bd 100644 --- a/wsidicom/file/wsidicom_file_image_data.py +++ b/wsidicom/file/wsidicom_file_image_data.py @@ -68,7 +68,7 @@ def transfer_syntax(self) -> UID: def _get_file(self, frame_index: int) -> WsiDicomFile: """ - Return file contaning frame index. + Return file containing frame index. Raises WsiDicomNotFoundError if frame is not found. diff --git a/wsidicom/file/wsidicom_file_source.py b/wsidicom/file/wsidicom_file_source.py index d2ce39f4..d4236d29 100644 --- a/wsidicom/file/wsidicom_file_source.py +++ b/wsidicom/file/wsidicom_file_source.py @@ -137,7 +137,7 @@ def image_files(self) -> List[WsiDicomFile]: @property def is_ready_for_viewing(self) -> Optional[bool]: """ - Returns True if files in source are formated for fast viewing. + Returns True if files in source are formatted for fast viewing. Returns None if no files are in source. """ diff --git a/wsidicom/file/wsidicom_file_writer.py b/wsidicom/file/wsidicom_file_writer.py index 230e6ede..f83f6050 100644 --- a/wsidicom/file/wsidicom_file_writer.py +++ b/wsidicom/file/wsidicom_file_writer.py @@ -94,7 +94,7 @@ def write( transfer_syntax: UID. Transfer syntax for file dataset: Dataset - Dataset to write (exluding pixel data). + Dataset to write (excluding pixel data). data: Dict[Tuple[str, float], ImageData] Pixel data to write. workers: int diff --git a/wsidicom/graphical_annotations.py b/wsidicom/graphical_annotations.py index 1479a3e8..5339ea73 100644 --- a/wsidicom/graphical_annotations.py +++ b/wsidicom/graphical_annotations.py @@ -296,7 +296,7 @@ def from_list(cls, list: Sequence[float]) -> "Geometry": @classmethod def list_to_coords(cls, data: Sequence[float]) -> List[Tuple[float, float]]: - """Return cordinates in list of floats as list of tuple of floats + """Return coordinates in list of floats as list of tuple of floats Parameters ---------- @@ -367,7 +367,7 @@ def from_shapely_like(cls, object: Any) -> "Geometry": Parameters ---------- - ojbect + object Object with shapely-like attributes. Returns @@ -572,7 +572,7 @@ def from_list(cls, list: Sequence[float]) -> "Polyline": @dataclass class Polygon(Polyline): - """Geometry consisting of connected lines implicity closed.""" + """Geometry consisting of connected lines implicitly closed.""" name = "POLYGON" @@ -1097,7 +1097,7 @@ def get_measurements( def create_measurement_indices( self, code: MeasurementCode, unit: UnitCode ) -> np.ndarray: - """Return measurement indicies for all measurements of specified type. + """Return measurement indices for all measurements of specified type. Indices are stored starting at index 1. Parameters @@ -1440,7 +1440,7 @@ def _get_geometries_from_ds(cls, ds: Dataset) -> List[Point]: class PolylineAnnotationGroupMeta(AnnotationGroup[GeometryType]): - """Meta class for line annotation goup""" + """Meta class for line annotation group""" @property def point_index_list(self) -> np.ndarray: @@ -1581,7 +1581,7 @@ def __init__( """ self.groups = groups if coordinate_type not in ["image", "volume"]: - raise ValueError("Coordiante type should be 'image' or 'volume'") + raise ValueError("Coordinate type should be 'image' or 'volume'") self.coordinate_type = coordinate_type self.slide_uids = slide_uids self.datetime = datetime.now() @@ -1697,7 +1697,7 @@ def open_dataset(cls, dataset: Dataset) -> "AnnotationInstance": elif dataset.AnnotationCoordinateType == "3D": coordinate_type = "volume" else: - raise ValueError("Unkown coordiante type") + raise ValueError("Unknown coordinate type") if coordinate_type == "volume" and frame_of_reference_uid is None: raise ValueError( "volume annotation corrindate type requires frame of reference" diff --git a/wsidicom/group/group.py b/wsidicom/group/group.py index 8e13b750..c783e008 100644 --- a/wsidicom/group/group.py +++ b/wsidicom/group/group.py @@ -22,7 +22,7 @@ from wsidicom.errors import ( WsiDicomMatchError, - WsiDicomNoResultionError, + WsiDicomNoResolutionError, WsiDicomNotFoundError, WsiDicomOutOfBoundsError, ) @@ -196,7 +196,7 @@ def matches(self, other_group: "Group") -> bool: ) def valid_pixels(self, region: Region) -> bool: - """Check if pixel region is withing the size of the group image size. + """Check if pixel region is within the size of the group image size. Parameters ---------- @@ -215,7 +215,7 @@ def valid_pixels(self, region: Region) -> bool: def get_instance( self, z: Optional[float] = None, path: Optional[str] = None ) -> WsiInstance: - """Search for instance fullfilling the parameters. + """Search for instance fulfilling the parameters. The behavior when z and/or path is none could be made more clear. @@ -347,7 +347,7 @@ def get_region_mm( """ if slide_origin and self.image_coordinate_system is None: raise ValueError( - "Cant map to slide region as image coordinate system is not defined." + "Can't map to slide region as image coordinate system is not defined." ) if slide_origin: @@ -429,7 +429,7 @@ def mm_to_pixel(self, region: RegionMm) -> Region: Region in pixels """ if self.pixel_spacing is None: - raise WsiDicomNoResultionError() + raise WsiDicomNoResolutionError() pixel_region = Region( position=region.position // self.pixel_spacing, size=region.size // self.pixel_spacing, diff --git a/wsidicom/group/level.py b/wsidicom/group/level.py index 1ed45ae6..a0a62ef3 100644 --- a/wsidicom/group/level.py +++ b/wsidicom/group/level.py @@ -18,7 +18,7 @@ from PIL import Image from PIL.Image import Image as PILImage -from wsidicom.errors import WsiDicomNoResultionError, WsiDicomOutOfBoundsError +from wsidicom.errors import WsiDicomNoResolutionError, WsiDicomOutOfBoundsError from wsidicom.geometry import Point, Region, Size, SizeMm from wsidicom.group.group import Group from wsidicom.instance import WsiInstance @@ -32,7 +32,7 @@ class Level(Group): """ def __init__(self, instances: Sequence[WsiInstance], base_pixel_spacing: SizeMm): - """Create a level from list of WsiInstances. Asign the pyramid level + """Create a level from list of WsiInstances. Assign the pyramid level index from pixel spacing of base level. Parameters @@ -84,13 +84,13 @@ def level(self) -> int: @property def mpp(self) -> SizeMm: if self.pixel_spacing is None: - raise WsiDicomNoResultionError() + raise WsiDicomNoResolutionError() return self.pixel_spacing * 1000.0 @property def pixel_spacing(self) -> SizeMm: if self._pixel_spacing is None: - raise WsiDicomNoResultionError() + raise WsiDicomNoResolutionError() return self._pixel_spacing @classmethod @@ -116,7 +116,7 @@ def open( base_group = list(instances_grouped_by_level.values())[0] base_pixel_spacing = base_group[0].pixel_spacing if base_pixel_spacing is None: - raise WsiDicomNoResultionError() + raise WsiDicomNoResolutionError() for level in instances_grouped_by_level.values(): levels.append(cls(level, base_pixel_spacing)) return levels @@ -178,7 +178,7 @@ def get_scaled_tile( ) -> PILImage: """Return tile in another level by scaling a region. If the tile is an edge tile, the resulting tile is croped - to remove part outside of the image (as defiend by level size). + to remove part outside of the image (as defined by level size). Parameters ---------- @@ -260,7 +260,7 @@ def _assign_level(self, base_pixel_spacing: SizeMm) -> int: Parameters ---------- base_pixel_spacing: SizeMm - The pixel spacing of the base lavel + The pixel spacing of the base level Returns ---------- diff --git a/wsidicom/instance/dataset.py b/wsidicom/instance/dataset.py index 00fe49b5..3c3e0dcb 100644 --- a/wsidicom/instance/dataset.py +++ b/wsidicom/instance/dataset.py @@ -433,7 +433,7 @@ def slice_thickness(self) -> Optional[float]: Returns ---------- Optional[float] - Slice thickess or None if unkown. + Slice thickess or None if unknown. """ try: return self._get_dicom_attribute("SliceThickness", self.pixel_measure) @@ -459,7 +459,7 @@ def is_supported_wsi_dicom( ) -> Optional[ImageType]: """Check if dataset is dicom wsi type and that required attributes (for the function of the library) is available. - Warn if attribute listed as requierd in the library or required in the + Warn if attribute listed as required in the library or required in the standard is missing. Parameters @@ -593,7 +593,6 @@ def as_tiled_full( dataset = deepcopy(self) dataset.DimensionOrganizationType = "TILED_FULL" - # Make a new Shared functional group sequence and Pixel measure # sequence if not in dataset, otherwise update the Pixel measure # sequence diff --git a/wsidicom/instance/image_coordinate_system.py b/wsidicom/instance/image_coordinate_system.py index 4fff5442..85febfcb 100644 --- a/wsidicom/instance/image_coordinate_system.py +++ b/wsidicom/instance/image_coordinate_system.py @@ -77,7 +77,7 @@ def image_orientation_slide( @property def rotation(self) -> float: - """The rotation of the image in relation to the slide coordiante system in degrees.""" + """The rotation of the image in relation to the slide coordinate system in degrees.""" return self._orientation.rotation def image_to_slide(self, image: GeometryType) -> GeometryType: diff --git a/wsidicom/instance/image_data.py b/wsidicom/instance/image_data.py index 6dc37514..a37eaf82 100644 --- a/wsidicom/instance/image_data.py +++ b/wsidicom/instance/image_data.py @@ -106,12 +106,12 @@ def image_region(self) -> Region: @property def focal_planes(self) -> List[float]: - """Focal planes avaiable in the image defined in um.""" + """Focal planes available in the image defined in um.""" return [0.0] @property def optical_paths(self) -> List[str]: - """Optical paths avaiable in the image.""" + """Optical paths available in the image.""" return ["0"] @property diff --git a/wsidicom/instance/instance.py b/wsidicom/instance/instance.py index 7dd31cce..f8044b2f 100644 --- a/wsidicom/instance/instance.py +++ b/wsidicom/instance/instance.py @@ -290,7 +290,7 @@ def matches(self, other_instance: "WsiInstance") -> bool: Returns ---------- bool - True if instanes are of same group. + True if instances are of same group. """ return ( diff --git a/wsidicom/instance/tile_index/full_tile_index.py b/wsidicom/instance/tile_index/full_tile_index.py index f0841495..137c949b 100644 --- a/wsidicom/instance/tile_index/full_tile_index.py +++ b/wsidicom/instance/tile_index/full_tile_index.py @@ -23,7 +23,7 @@ class FullTileIndex(TileIndex): """Index for mapping tile position to frame number for datasets containing - full tiles. Pixel data tiles are ordered by colum, row, z and path, thus + full tiles. Pixel data tiles are ordered by column, row, z and path, thus the frame index for a tile can directly be calculated.""" def __init__(self, datasets: Sequence[WsiDataset]): @@ -107,7 +107,7 @@ def _read_focal_planes_from_datasets( slice_spacing = 0.0 else: raise ValueError( - "Slice spacing must be known if multple focal planes." + "Slice spacing must be known if multiple focal planes." ) elif slice_spacing == 0 and number_of_focal_planes != 1: raise ValueError( diff --git a/wsidicom/instance/tile_index/sparse_tile_index.py b/wsidicom/instance/tile_index/sparse_tile_index.py index 54e2d18a..a75a0df9 100644 --- a/wsidicom/instance/tile_index/sparse_tile_index.py +++ b/wsidicom/instance/tile_index/sparse_tile_index.py @@ -86,7 +86,7 @@ class SparseTileIndex(TileIndex): def __init__(self, datasets: Sequence[WsiDataset]): """Create sparse tile index for frames in datasets. Requires equal tile size for all tile planes. Pixel data tiles are identified by the Per - Frame Functional Groups Sequence that contains tile colum, row, z, + Frame Functional Groups Sequence that contains tile column, row, z, path, and frame index. These are stored in a SparseTilePlane (one plane for every combination of z and path). @@ -140,7 +140,7 @@ def get_frame_index(self, tile: Point, z: float, path: str) -> int: return frame_index def _get_focal_planes(self) -> List[float]: - """Return list of focal planes defiend in planes. + """Return list of focal planes defined in planes. Returns ---------- diff --git a/wsidicom/instance/wsidicom_image_data.py b/wsidicom/instance/wsidicom_image_data.py index d49f91ce..18423ed9 100644 --- a/wsidicom/instance/wsidicom_image_data.py +++ b/wsidicom/instance/wsidicom_image_data.py @@ -69,12 +69,12 @@ def tile_size(self) -> Size: @property def focal_planes(self) -> List[float]: - """Focal planes avaiable in the image defined in um.""" + """Focal planes available in the image defined in um.""" return self.tiles.focal_planes @property def optical_paths(self) -> List[str]: - """Optical paths avaiable in the image.""" + """Optical paths available in the image.""" return self.tiles.optical_paths @property diff --git a/wsidicom/optical.py b/wsidicom/optical.py index 5f645064..451ab458 100644 --- a/wsidicom/optical.py +++ b/wsidicom/optical.py @@ -57,7 +57,7 @@ def __init__(self, lut_sequence: DicomSequence): self._type = np.dtype(np.uint8) else: self._type = np.dtype(np.uint16) - self._byte_format = "HHH" # Do we need to set endianess? + self._byte_format = "HHH" # Do we need to set endianness? self.table = self._parse_lut(self._lut_item) def array(self, mode: str) -> np.ndarray: @@ -129,7 +129,7 @@ def _parse_color(self, segmented_lut_data: bytes): elif lut_type == 1: parsed_table = self._add_linear(parsed_table, lut_length, lut_value) else: - raise NotImplementedError("Unkown lut segment type") + raise NotImplementedError("Unknown lut segment type") return parsed_table def _parse_lut(self, lut: Dataset) -> np.ndarray: @@ -153,7 +153,7 @@ def _parse_lut(self, lut: Dataset) -> np.ndarray: @classmethod def _insert(cls, table: np.ndarray, segment: np.ndarray): - """Insert a segement into the lookup table of channel. + """Insert a segment into the lookup table of channel. Parameters ---------- @@ -167,7 +167,7 @@ def _insert(cls, table: np.ndarray, segment: np.ndarray): @classmethod def _add_discret(cls, table: np.ndarray, length: int, value: int): - """Add a discret segement into the lookup table of channel. + """Add a discret segment into the lookup table of channel. Parameters ---------- @@ -184,7 +184,7 @@ def _add_discret(cls, table: np.ndarray, length: int, value: int): @classmethod def _add_linear(cls, table: np.ndarray, length: int, value: int): - """Add a linear segement into the lookup table of channel. + """Add a linear segment into the lookup table of channel. Parameters ---------- diff --git a/wsidicom/series/series.py b/wsidicom/series/series.py index 2b517433..2dea1ac2 100644 --- a/wsidicom/series/series.py +++ b/wsidicom/series/series.py @@ -35,7 +35,7 @@ class Series(metaclass=ABCMeta): """Represents a series of Groups with the same image flavor, e.g. - pyramidal levels, lables, or overviews. + pyramidal levels, labels, or overviews. """ def __init__(self, groups: Iterable[Group]): diff --git a/wsidicom/source.py b/wsidicom/source.py index 9e5f8566..d08c7707 100644 --- a/wsidicom/source.py +++ b/wsidicom/source.py @@ -26,7 +26,7 @@ class to extend support to other DICOM sources or to read other WSI formats. class Source(metaclass=ABCMeta): """A source providing DICOM WSI instances to open. - A source should be initated with a path or similar, and parse the content into + A source should be initiated with a path or similar, and parse the content into instances. """ @@ -68,5 +68,5 @@ def annotation_instances(self) -> Iterable[AnnotationInstance]: @abstractmethod def close(self) -> None: - """Close any opened resouces (such as files).""" + """Close any opened resources (such as files).""" raise NotImplementedError() diff --git a/wsidicom/stringprinting.py b/wsidicom/stringprinting.py index d7c972fb..92496602 100644 --- a/wsidicom/stringprinting.py +++ b/wsidicom/stringprinting.py @@ -16,7 +16,7 @@ def str_indent(indent: int = 0) -> str: - """Returns a string for indention + """Returns a string for indentation Parameters ---------- @@ -50,11 +50,11 @@ def list_pretty_str( items: sequence List of items to pretty-print indent: int - Identation for the string and list + Indentation for the string and list depth: Optional[int] = None Depth to print, if item contains list pre_new_lines: int - Number of new lines at begining of string + Number of new lines at beginning of string list_new_lines: int Number of new lines between list items space: bool @@ -97,11 +97,11 @@ def dict_pretty_str( items: sequence List of items to pretty-print indent: int - Identation for the string and list + Indentation for the string and list depth: Optional[int] = None Depth to print, if item contains list pre_new_lines: int - Number of new lines at begining of string + Number of new lines at beginning of string list_new_lines: int Number of new lines between list items space: bool diff --git a/wsidicom/target.py b/wsidicom/target.py index b538cb11..8a599ac4 100644 --- a/wsidicom/target.py +++ b/wsidicom/target.py @@ -23,7 +23,7 @@ class Target(metaclass=ABCMeta): - """A target should be initated with a path or similar, and enable saving of + """A target should be initiated with a path or similar, and enable saving of instances into that path.""" def __init__( @@ -33,7 +33,7 @@ def __init__( chunk_size: int, add_missing_levels: bool = False, ) -> None: - """Initate a target. + """Initiate a target. Parameters ---------- diff --git a/wsidicom/uid.py b/wsidicom/uid.py index 11221560..c3d92897 100644 --- a/wsidicom/uid.py +++ b/wsidicom/uid.py @@ -87,7 +87,7 @@ class FileUids: @property def identifier(self) -> UID: """Return identifier for the instance the file belongs to. Either its - own intance uid or, if not none, the concnatenation uid. + own instance uid or, if not none, the concnatenation uid. Returns ---------- diff --git a/wsidicom/wsidicom.py b/wsidicom/wsidicom.py index 80a03545..d7d58d41 100644 --- a/wsidicom/wsidicom.py +++ b/wsidicom/wsidicom.py @@ -514,7 +514,7 @@ def read_encoded_tile( def get_instance( self, level: int, z: Optional[float] = None, path: Optional[str] = None ) -> WsiInstance: - """Return instance fullfilling level, z and/or path. + """Return instance fulfilling level, z and/or path. Parameters ---------- @@ -558,7 +558,7 @@ def save( ---------- output_path: Union[str, Path] uid_generator: Callable[..., UID] = pydicom.uid.generate_uid - Function that can gernerate unique identifiers. + Function that can generate unique identifiers. workers: Optional[int] = None Maximum number of thread workers to use. chunk_size: Optional[int] = None @@ -645,7 +645,7 @@ def is_ready_for_viewing( cls, path: Union[str, Iterable[str], Path, Iterable[Path]] ) -> Optional[bool]: """ - Return true if files in path are formated for fast viewing, i.e. + Return true if files in path are formatted for fast viewing, i.e. have TILED_FULL tile arrangement and have an offset table. Parameters @@ -654,7 +654,7 @@ def is_ready_for_viewing( Path to files to test. Returns - True if files in path are formated for fast viewing, None if no DICOM WSI + True if files in path are formatted for fast viewing, None if no DICOM WSI files are in the path. """ source = WsiDicomFileSource(path) From fe91d244455f9f5086f37aeb13d3d180464b63c9 Mon Sep 17 00:00:00 2001 From: Erik O Gabrielsson <83275777+erikogabrielsson@users.noreply.github.com> Date: Wed, 9 Aug 2023 08:19:09 +0200 Subject: [PATCH 2/2] Logging (#109) * Log warnings etc instead of issuing warnings --- CHANGELOG.md | 4 ++++ tests/test_save.py | 1 - tests/test_wsidicom_file_target.py | 1 - tests/test_wsidicom_integration.py | 1 - wsidicom/file/wsidicom_file_source.py | 14 +++++++------- wsidicom/geometry.py | 4 ++-- wsidicom/instance/dataset.py | 12 ++++++------ wsidicom/instance/image_coordinate_system.py | 1 - wsidicom/wsidicom.py | 4 ++-- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a701e18..5bbfba00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Use logger instead of issuing warnings. + ### Fixed - Error in documentation of read_overview() in readme. diff --git a/tests/test_save.py b/tests/test_save.py index bac9c661..7a44a0bb 100644 --- a/tests/test_save.py +++ b/tests/test_save.py @@ -111,7 +111,6 @@ def _get_encoded_tile(self, tile: Point, z: float, path: str) -> bytes: @pytest.mark.save -@pytest.mark.filterwarnings("ignore") class WsiDicomFileSaveTests(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/tests/test_wsidicom_file_target.py b/tests/test_wsidicom_file_target.py index 8b9efb5c..cc6aa048 100644 --- a/tests/test_wsidicom_file_target.py +++ b/tests/test_wsidicom_file_target.py @@ -26,7 +26,6 @@ @pytest.mark.integration -@pytest.mark.filterwarnings("ignore") class WsiDicomFileTargetIntegrationTests(unittest.TestCase): @classmethod def setUpClass(cls): diff --git a/tests/test_wsidicom_integration.py b/tests/test_wsidicom_integration.py index 5dd1f5f9..252c3ff4 100644 --- a/tests/test_wsidicom_integration.py +++ b/tests/test_wsidicom_integration.py @@ -25,7 +25,6 @@ @pytest.mark.integration -@pytest.mark.filterwarnings("ignore") @parameterized_class( [ {"input_type": WsiInputType.FILE}, diff --git a/wsidicom/file/wsidicom_file_source.py b/wsidicom/file/wsidicom_file_source.py index d4236d29..be1b6039 100644 --- a/wsidicom/file/wsidicom_file_source.py +++ b/wsidicom/file/wsidicom_file_source.py @@ -16,9 +16,9 @@ from collections import defaultdict import io +import logging from pathlib import Path from typing import BinaryIO, Dict, Iterable, List, Optional, Tuple, Union -import warnings from pydicom.uid import UID @@ -70,21 +70,21 @@ def __init__( elif wsi_file.image_type == ImageType.OVERVIEW: self._overview_files.append(wsi_file) except WsiDicomNotSupportedError: - warnings.warn(f"Non-supported file {stream.name}.") + logging.debug(f"Non-supported file {stream.name}.") if filepath is not None: stream.close() elif sop_class_uid == ANN_SOP_CLASS_UID: self._annotation_files.append(stream) elif filepath is not None: - warnings.warn( + logging.debug( f"Non-supported SOP class {sop_class_uid} " f"for file {stream.name}." ) # File was opened but not supported SOP class. stream.close() except Exception as exception: - warnings.warn( - f"Failed to open file {file.name} due to " f"exception: {exception}" + logging.error( + f"Failed to open file {file.name} due to exception: {exception}" ) if len(self._level_files) == 0: raise WsiDicomNotFoundError("Level files", str(files)) @@ -236,7 +236,7 @@ def _get_sop_class_uid(stream: BinaryIO) -> Optional[UID]: stream.seek(0) return metadata.MediaStorageSOPClassUID except InvalidDicomError as exception: - warnings.warn( + logging.debug( f"Failed to parse DICOM file metadata for file {stream}, not DICOM? " f"Got exception {exception}" ) @@ -306,7 +306,7 @@ def _filter_files( if file.dataset.matches_series(series_uids, series_tile_size): yield file else: - warnings.warn( + logging.warn( f"{file.filepath} with uids {file.uids.slide} " f"did not match series with {series_uids} " f"and tile size {series_tile_size}" diff --git a/wsidicom/geometry.py b/wsidicom/geometry.py index 0ee54d07..d6e6b2ab 100644 --- a/wsidicom/geometry.py +++ b/wsidicom/geometry.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging import math from dataclasses import dataclass from typing import Callable, Iterable, Iterator, Tuple, Union, Sequence -import warnings @dataclass @@ -507,7 +507,7 @@ def zoom(self, zoom: float) -> "RegionMm": class Orientation: def __init__(self, orientation: Sequence[float]): if orientation[0] != -orientation[4] or orientation[1] != orientation[3]: - warnings.warn( + logging.warn( f"Orientation {orientation} is not " "orthogonal with equal lengths with column rotated 90 deg from row" ) diff --git a/wsidicom/instance/dataset.py b/wsidicom/instance/dataset.py index 3c3e0dcb..64286890 100644 --- a/wsidicom/instance/dataset.py +++ b/wsidicom/instance/dataset.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import warnings +import logging from copy import deepcopy from dataclasses import dataclass from enum import Enum, IntEnum, auto @@ -303,7 +303,7 @@ def pixel_spacing(self) -> Optional[SizeMm]: pixel_spacing_values = getattr(self.pixel_measure, "PixelSpacing", None) if pixel_spacing_values is not None: if any([spacing == 0 for spacing in pixel_spacing_values]): - warnings.warn(f"Pixel spacing is zero, {pixel_spacing_values}") + logging.warn(f"Pixel spacing is zero, {pixel_spacing_values}") return None return SizeMm.from_tuple(pixel_spacing_values) return None @@ -477,23 +477,23 @@ def is_supported_wsi_dicom( sop_class_uid: UID = getattr(dataset, "SOPClassUID") if sop_class_uid != WSI_SOP_CLASS_UID: - warnings.warn(f"Non-wsi image, SOP class {sop_class_uid.name}") + logging.debug(f"Non-wsi image, SOP class {sop_class_uid.name}") return None try: image_type = cls._get_image_type(dataset.ImageType) except ValueError: - warnings.warn(f"Non-supported image type {dataset.ImageType}") + logging.debug(f"Non-supported image type {dataset.ImageType}") return None for name, attribute in WSI_ATTRIBUTES.items(): if name not in dataset and attribute.evaluate(image_type): - warnings.warn(f"Missing required attribute {name}") + logging.debug(f"Missing required attribute {name}") return None syntax_supported = pillow_handler.supports_transfer_syntax(transfer_syntax) if not syntax_supported: - warnings.warn(f"Non-supported transfer syntax {transfer_syntax}") + logging.debug(f"Non-supported transfer syntax {transfer_syntax}") return None return image_type diff --git a/wsidicom/instance/image_coordinate_system.py b/wsidicom/instance/image_coordinate_system.py index 85febfcb..ad1fc476 100644 --- a/wsidicom/instance/image_coordinate_system.py +++ b/wsidicom/instance/image_coordinate_system.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import warnings from typing import List, Optional, TypeVar from pydicom.dataset import Dataset diff --git a/wsidicom/wsidicom.py b/wsidicom/wsidicom.py index d7d58d41..acd36cd4 100644 --- a/wsidicom/wsidicom.py +++ b/wsidicom/wsidicom.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging import os -import warnings from pathlib import Path from typing import ( BinaryIO, @@ -637,7 +637,7 @@ def _validate_collection(self) -> SlideUids: if self.annotations != []: for annotation in self.annotations: if annotation.slide_uids != slide_uids: - warnings.warn("Annotations uids does not match") + logging.warn("Annotations uids does not match.") return slide_uids @classmethod