def read_multiple(self, path: str) -> List[Image]: """ Allows to read in multiple images at once in case they are mixed within a single directory. """ self._logger.info("Reading dicom images/series from: " + path) file_paths_by_series_uid = self._build_file_paths_by_series_uid_map( path) self._logger.info("Reading in {} images with series UIDs: {}".format( len(file_paths_by_series_uid), ", ".join([uid for uid in file_paths_by_series_uid.keys()]))) images: List[Image] = list() for uid, file_path_list in file_paths_by_series_uid.items(): self._logger.debug("Reading in series {}".format(uid)) # read in all slices slices = [ pydicom.dcmread(file_path) for file_path in file_path_list ] # filter non-image slices # todo: validate criterion slices = [ slice for slice in slices if "ImagePositionPatient" in slice ] if len(slices) == 0: continue # load image volume and meta-data volume = self._build_volume(slices) image_meta_data, slice_meta_data_by_index = self._get_meta_data( slices) image = Image(image_data=volume) image.get_meta_data().update( {DICOM_META_DATA_KEY: image_meta_data}) # attach slice-specific meta-data for index, slice_meta_data in slice_meta_data_by_index.items(): image.get_or_add_slice(index).get_meta_data().update( {DICOM_META_DATA_KEY: slice_meta_data}) # deduce image information from dicom meta-data image.set_voxel_size(self._get_voxel_size(image)) image.set_voxel_spacing(self._get_voxel_spacing(image)) images.append(image) return images
def _build_manifest(self, image: Image, segment_slugs: dict) -> dict: manifest = { "image": { "precision_bytes": image.get_image_data().dtype.itemsize, "size": list(image.get_image_data().shape ), # the image volume byte sequence does not contain this "voxel_size": list(image.get_voxel_size()) if image.get_voxel_size() else None, "voxel_spacing": list(image.get_voxel_spacing()) if image.get_voxel_spacing() else None, }, "meta_data": image.get_meta_data(), "slices": [ self._build_image_slice_manifest(image_slice) for image_slice in image.get_slices() ], "segments": [ self._build_image_segment_manifest( image_segment, segment_slugs[image_segment.get_identifier()]) for image_segment in image.get_segments() ], } # make sure the result will actually be readable self._validate_manifest(manifest) return manifest
def _get_pixel_spacing(self, image: Image) -> Optional[Tuple[float, float]]: try: # "Pixel Spacing" (0028,0030) pixel_spacing = image.get_meta_data()[DICOM_META_DATA_KEY][str( 0x00280030)] except KeyError: return None assert isinstance(pixel_spacing, list) assert len(pixel_spacing) == 2 assert all(isinstance(value, float) for value in pixel_spacing) return (pixel_spacing[0], pixel_spacing[1])
def _get_voxel_size(self, image: Image) -> Optional[Vector3]: """Deduces the voxel size based on slice thickness and pixel spacing. Relies on the assumption of non-overlap and non-sparseness in the xy-plane. """ pixel_spacing = self._get_pixel_spacing(image) if pixel_spacing is None: return None try: slice_thickness = image.get_meta_data()["Slice Thickness"] assert isinstance(slice_thickness, float) assert slice_thickness > 0 except KeyError: return None return ( slice_thickness, pixel_spacing[1], pixel_spacing[0], )
def _get_voxel_spacing(self, image: Image) -> Optional[Vector3]: """Deduces the voxel spacing based on slice increment and pixel spacing.""" pixel_spacing = self._get_pixel_spacing(image) if pixel_spacing is None: return None explicit_slice_increment: Optional[float] = None # Read explicit value from "Spacing Between Slices" (0018,0088) try: explicit_slice_increment = image.get_meta_data( )[DICOM_META_DATA_KEY][str(0x00180088)] assert isinstance(explicit_slice_increment, float) explicit_slice_increment = abs(explicit_slice_increment) except KeyError: pass implicit_slice_increment = self._calculate_z_spacing(image) if explicit_slice_increment is None and implicit_slice_increment is None: # Cannot do anything return None elif explicit_slice_increment is not None and implicit_slice_increment is not None: # Assert consistency assert explicit_slice_increment == implicit_slice_increment,\ "Derived slice increment differs from defined value" slice_increment = explicit_slice_increment if explicit_slice_increment is not None else implicit_slice_increment return ( slice_increment, pixel_spacing[1], pixel_spacing[0], )