def test_byte_swapping(self): """Test that the endianess of the system is taken into account.""" # The main problem is that our testing environments are probably # all little endian, but we'll try our best dtype = np.dtype('uint16') # < is little, = is native, > is big if byteorder == 'little': out = dtype_corrected_for_endianness(True, dtype) assert out.byteorder in ['<', '='] out = dtype_corrected_for_endianness(False, dtype) assert out.byteorder == '>' elif byteorder == 'big': out = dtype_corrected_for_endianness(True, dtype) assert out.byteorder == '<' out = dtype_corrected_for_endianness(False, dtype) assert out.byteorder in ['>', '=']
def test_byte_swapping(self): """Test that the endianess of the system is taken into account.""" # The main problem is that our testing environments are probably # all little endian, but we'll try our best dtype = np.dtype('uint16') # < is little, = is native, > is big if byteorder == 'little': out = dtype_corrected_for_endianness(True, dtype) assert out.byteorder in ['<', '='] out = dtype_corrected_for_endianness(False, dtype) assert out.byteorder == '>' elif byteorder == 'big': out = dtype_corrected_for_endianness(True, dtype) assert out.byteorder == '<' out = dtype_corrected_for_endianness(False, dtype) assert out.byteorder in ['>', '=']
def get_pixeldata(dicom_dataset): """Use Pillow to decompress compressed Pixel Data. Returns ------- numpy.ndarray The contents of the Pixel Data element (7FE0,0010) as an ndarray. Raises ------ ImportError If PIL is not available. NotImplementedError if the transfer syntax is not supported TypeError if the pixel data type is unsupported """ logger.debug("Trying to use Pillow to read pixel array " "(has pillow = %s)", have_pillow) if not have_pillow: msg = ("The pillow package is required to use pixel_array for " "this transfer syntax {0}, and pillow could not be " "imported.".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise ImportError(msg) if (not have_pillow_jpeg_plugin and dicom_dataset.file_meta.TransferSyntaxUID in PillowJPEGTransferSyntaxes): msg = ("this transfer syntax {0}, can not be read because " "Pillow lacks the jpeg decoder plugin".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise NotImplementedError(msg) if (not have_pillow_jpeg2000_plugin and dicom_dataset.file_meta.TransferSyntaxUID in PillowJPEG2000TransferSyntaxes): msg = ("this transfer syntax {0}, can not be read because " "Pillow lacks the jpeg 2000 decoder plugin".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise NotImplementedError(msg) if (dicom_dataset.file_meta.TransferSyntaxUID not in PillowSupportedTransferSyntaxes): msg = ("this transfer syntax {0}, can not be read because " "Pillow does not support this syntax".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise NotImplementedError(msg) # Make NumPy format code, e.g. "uint16", "int32" etc # from two pieces of info: # dicom_dataset.PixelRepresentation -- 0 for unsigned, 1 for signed; # dicom_dataset.BitsAllocated -- 8, 16, or 32 if dicom_dataset.PixelRepresentation == 0: format_str = 'uint{}'.format(dicom_dataset.BitsAllocated) elif dicom_dataset.PixelRepresentation == 1: format_str = 'int{}'.format(dicom_dataset.BitsAllocated) else: format_str = 'bad_pixel_representation' try: numpy_format = numpy.dtype(format_str) except TypeError: msg = ("Data type not understood by NumPy: " "format='{}', PixelRepresentation={}, " "BitsAllocated={}".format( format_str, dicom_dataset.PixelRepresentation, dicom_dataset.BitsAllocated)) raise TypeError(msg) numpy_format = dtype_corrected_for_endianness( dicom_dataset.is_little_endian, numpy_format) # decompress here if (dicom_dataset.file_meta.TransferSyntaxUID in PillowJPEGTransferSyntaxes): logger.debug("This is a JPEG lossy format") if dicom_dataset.BitsAllocated > 8: raise NotImplementedError("JPEG Lossy only supported if " "Bits Allocated = 8") generic_jpeg_file_header = b'' frame_start_from = 0 elif (dicom_dataset.file_meta.TransferSyntaxUID in PillowJPEG2000TransferSyntaxes): logger.debug("This is a JPEG 2000 format") generic_jpeg_file_header = b'' # generic_jpeg_file_header = b'\x00\x00\x00\x0C\x6A' # b'\x50\x20\x20\x0D\x0A\x87\x0A' frame_start_from = 0 else: logger.debug("This is a another pillow supported format") generic_jpeg_file_header = b'' frame_start_from = 0 try: UncompressedPixelData = bytearray() if ('NumberOfFrames' in dicom_dataset and dicom_dataset.NumberOfFrames > 1): # multiple compressed frames CompressedPixelDataSeq = \ pydicom.encaps.decode_data_sequence( dicom_dataset.PixelData) for frame in CompressedPixelDataSeq: data = generic_jpeg_file_header + \ frame[frame_start_from:] fio = io.BytesIO(data) try: decompressed_image = PILImg.open(fio) except IOError as e: raise NotImplementedError(e.strerror) UncompressedPixelData.extend(decompressed_image.tobytes()) else: # single compressed frame UncompressedPixelData = pydicom.encaps.defragment_data( dicom_dataset.PixelData) UncompressedPixelData = generic_jpeg_file_header + \ UncompressedPixelData[frame_start_from:] try: fio = io.BytesIO(UncompressedPixelData) decompressed_image = PILImg.open(fio) except IOError as e: raise NotImplementedError(e.strerror) UncompressedPixelData = decompressed_image.tobytes() except Exception: raise logger.debug( "Successfully read %s pixel bytes", len(UncompressedPixelData)) pixel_array = numpy.copy( numpy.frombuffer(UncompressedPixelData, numpy_format)) if (dicom_dataset.file_meta.TransferSyntaxUID in PillowJPEG2000TransferSyntaxes and dicom_dataset.BitsStored == 16): # WHY IS THIS EVEN NECESSARY?? pixel_array &= 0x7FFF if should_change_PhotometricInterpretation_to_RGB(dicom_dataset): dicom_dataset.PhotometricInterpretation = "RGB" return pixel_array
def test_no_endian_raises(self): """Test that an unset endianness raises exception.""" with pytest.raises(ValueError, match="attribute 'is_little_endian' has"): dtype_corrected_for_endianness(None, None)
def get_pixeldata(dicom_dataset): """Return the *Pixel Data* as a :class:`numpy.ndarray`. Returns ------- numpy.ndarray A correctly sized (but not shaped) numpy array of the *Pixel Data*. Raises ------ ImportError If the required packages are not available. NotImplementedError If the transfer syntax is not supported. TypeError If the pixel data type is unsupported. """ if (dicom_dataset.file_meta.TransferSyntaxUID not in SUPPORTED_TRANSFER_SYNTAXES): msg = ("The jpeg_ls does not support " "this transfer syntax {0}.".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise NotImplementedError(msg) if not HAVE_JPEGLS: msg = ("The jpeg_ls package is required to use pixel_array " "for this transfer syntax {0}, and jpeg_ls could not " "be imported.".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise ImportError(msg) # Make NumPy format code, e.g. "uint16", "int32" etc # from two pieces of info: # dicom_dataset.PixelRepresentation -- 0 for unsigned, 1 for signed; # dicom_dataset.BitsAllocated -- 8, 16, or 32 if dicom_dataset.PixelRepresentation == 0: format_str = 'uint{}'.format(dicom_dataset.BitsAllocated) elif dicom_dataset.PixelRepresentation == 1: format_str = 'int{}'.format(dicom_dataset.BitsAllocated) else: format_str = 'bad_pixel_representation' try: numpy_format = numpy.dtype(format_str) except TypeError: msg = ("Data type not understood by NumPy: " "format='{}', PixelRepresentation={}, " "BitsAllocated={}".format( format_str, dicom_dataset.PixelRepresentation, dicom_dataset.BitsAllocated)) raise TypeError(msg) numpy_format = dtype_corrected_for_endianness( dicom_dataset.is_little_endian, numpy_format) # decompress here UncompressedPixelData = bytearray() if ('NumberOfFrames' in dicom_dataset and dicom_dataset.NumberOfFrames > 1): # multiple compressed frames CompressedPixelDataSeq = pydicom.encaps.decode_data_sequence( dicom_dataset.PixelData) # print len(CompressedPixelDataSeq) for frame in CompressedPixelDataSeq: decompressed_image = jpeg_ls.decode( numpy.frombuffer(frame, dtype=numpy.uint8)) UncompressedPixelData.extend(decompressed_image.tobytes()) else: # single compressed frame CompressedPixelData = pydicom.encaps.defragment_data( dicom_dataset.PixelData) decompressed_image = jpeg_ls.decode( numpy.frombuffer(CompressedPixelData, dtype=numpy.uint8)) UncompressedPixelData.extend(decompressed_image.tobytes()) pixel_array = numpy.frombuffer(UncompressedPixelData, numpy_format) if should_change_PhotometricInterpretation_to_RGB(dicom_dataset): dicom_dataset.PhotometricInterpretation = "RGB" return pixel_array
def get_pixeldata(dicom_dataset): """ Use the jpeg_ls package to decode the PixelData attribute Returns ------- numpy.ndarray A correctly sized (but not shaped) numpy array of the entire data volume Raises ------ ImportError if the required packages are not available NotImplementedError if the transfer syntax is not supported TypeError if the pixel data type is unsupported """ if (dicom_dataset.file_meta.TransferSyntaxUID not in SUPPORTED_TRANSFER_SYNTAXES): msg = ("The jpeg_ls does not support " "this transfer syntax {0}.".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise NotImplementedError(msg) if not HAVE_JPEGLS: msg = ("The jpeg_ls package is required to use pixel_array " "for this transfer syntax {0}, and jpeg_ls could not " "be imported.".format( dicom_dataset.file_meta.TransferSyntaxUID.name)) raise ImportError(msg) # Make NumPy format code, e.g. "uint16", "int32" etc # from two pieces of info: # dicom_dataset.PixelRepresentation -- 0 for unsigned, 1 for signed; # dicom_dataset.BitsAllocated -- 8, 16, or 32 if dicom_dataset.PixelRepresentation == 0: format_str = 'uint{}'.format(dicom_dataset.BitsAllocated) elif dicom_dataset.PixelRepresentation == 1: format_str = 'int{}'.format(dicom_dataset.BitsAllocated) else: format_str = 'bad_pixel_representation' try: numpy_format = numpy.dtype(format_str) except TypeError: msg = ("Data type not understood by NumPy: " "format='{}', PixelRepresentation={}, " "BitsAllocated={}".format( format_str, dicom_dataset.PixelRepresentation, dicom_dataset.BitsAllocated)) raise TypeError(msg) numpy_format = dtype_corrected_for_endianness( dicom_dataset.is_little_endian, numpy_format) # decompress here UncompressedPixelData = bytearray() if ('NumberOfFrames' in dicom_dataset and dicom_dataset.NumberOfFrames > 1): # multiple compressed frames CompressedPixelDataSeq = pydicom.encaps.decode_data_sequence( dicom_dataset.PixelData) # print len(CompressedPixelDataSeq) for frame in CompressedPixelDataSeq: decompressed_image = jpeg_ls.decode( numpy.frombuffer(frame, dtype=numpy.uint8)) UncompressedPixelData.extend(decompressed_image.tobytes()) else: # single compressed frame CompressedPixelData = pydicom.encaps.defragment_data( dicom_dataset.PixelData) decompressed_image = jpeg_ls.decode( numpy.frombuffer(CompressedPixelData, dtype=numpy.uint8)) UncompressedPixelData.extend(decompressed_image.tobytes()) pixel_array = numpy.frombuffer(UncompressedPixelData, numpy_format) if should_change_PhotometricInterpretation_to_RGB(dicom_dataset): dicom_dataset.PhotometricInterpretation = "RGB" return pixel_array
def get_pixeldata(dicom_dataset): """Use Pillow to decompress compressed Pixel Data. Returns ------- numpy.ndarray The contents of the Pixel Data element (7FE0,0010) as an ndarray. Raises ------ ImportError If PIL is not available. NotImplementedError if the transfer syntax is not supported TypeError if the pixel data type is unsupported """ logger.debug("Trying to use Pillow to read pixel array " "(has pillow = %s)", HAVE_PIL) transfer_syntax = dicom_dataset.file_meta.TransferSyntaxUID if not HAVE_PIL: msg = ("The pillow package is required to use pixel_array for " "this transfer syntax {0}, and pillow could not be " "imported.".format(transfer_syntax.name)) raise ImportError(msg) if not HAVE_JPEG and transfer_syntax in PillowJPEGTransferSyntaxes: msg = ("this transfer syntax {0}, can not be read because " "Pillow lacks the jpeg decoder plugin" .format(transfer_syntax.name)) raise NotImplementedError(msg) if not HAVE_JPEG2K and transfer_syntax in PillowJPEG2000TransferSyntaxes: msg = ("this transfer syntax {0}, can not be read because " "Pillow lacks the jpeg 2000 decoder plugin" .format(transfer_syntax.name)) raise NotImplementedError(msg) if transfer_syntax not in PillowSupportedTransferSyntaxes: msg = ("this transfer syntax {0}, can not be read because " "Pillow does not support this syntax" .format(transfer_syntax.name)) raise NotImplementedError(msg) # Make NumPy format code, e.g. "uint16", "int32" etc # from two pieces of info: # dicom_dataset.PixelRepresentation -- 0 for unsigned, 1 for signed; # dicom_dataset.BitsAllocated -- 8, 16, or 32 if dicom_dataset.PixelRepresentation == 0: format_str = 'uint{}'.format(dicom_dataset.BitsAllocated) elif dicom_dataset.PixelRepresentation == 1: format_str = 'int{}'.format(dicom_dataset.BitsAllocated) else: format_str = 'bad_pixel_representation' try: numpy_format = numpy.dtype(format_str) except TypeError: msg = ("Data type not understood by NumPy: " "format='{}', PixelRepresentation={}, " "BitsAllocated={}".format( format_str, dicom_dataset.PixelRepresentation, dicom_dataset.BitsAllocated)) raise TypeError(msg) numpy_format = dtype_corrected_for_endianness( dicom_dataset.is_little_endian, numpy_format) # decompress here if transfer_syntax in PillowJPEGTransferSyntaxes: logger.debug("This is a JPEG lossy format") if dicom_dataset.BitsAllocated > 8: raise NotImplementedError("JPEG Lossy only supported if " "Bits Allocated = 8") generic_jpeg_file_header = b'' frame_start_from = 0 elif transfer_syntax in PillowJPEG2000TransferSyntaxes: logger.debug("This is a JPEG 2000 format") generic_jpeg_file_header = b'' # generic_jpeg_file_header = b'\x00\x00\x00\x0C\x6A' # b'\x50\x20\x20\x0D\x0A\x87\x0A' frame_start_from = 0 else: logger.debug("This is a another pillow supported format") generic_jpeg_file_header = b'' frame_start_from = 0 try: UncompressedPixelData = bytearray() if ('NumberOfFrames' in dicom_dataset and dicom_dataset.NumberOfFrames > 1): # multiple compressed frames CompressedPixelDataSeq = \ pydicom.encaps.decode_data_sequence( dicom_dataset.PixelData) for frame in CompressedPixelDataSeq: data = generic_jpeg_file_header + \ frame[frame_start_from:] fio = io.BytesIO(data) try: decompressed_image = Image.open(fio) except IOError as e: raise NotImplementedError(e.strerror) UncompressedPixelData.extend(decompressed_image.tobytes()) else: # single compressed frame pixel_data = pydicom.encaps.defragment_data( dicom_dataset.PixelData) pixel_data = generic_jpeg_file_header + \ pixel_data[frame_start_from:] try: fio = io.BytesIO(pixel_data) decompressed_image = Image.open(fio) except IOError as e: raise NotImplementedError(e.strerror) UncompressedPixelData.extend(decompressed_image.tobytes()) except Exception: raise logger.debug( "Successfully read %s pixel bytes", len(UncompressedPixelData) ) pixel_array = numpy.frombuffer(UncompressedPixelData, numpy_format) if (transfer_syntax in PillowJPEG2000TransferSyntaxes and dicom_dataset.BitsStored == 16): # WHY IS THIS EVEN NECESSARY?? pixel_array &= 0x7FFF if should_change_PhotometricInterpretation_to_RGB(dicom_dataset): dicom_dataset.PhotometricInterpretation = "RGB" return pixel_array
def test_no_endian_raises(self): """Test that an unset endianness raises exception.""" with pytest.raises(ValueError, match="attribute 'is_little_endian' has"): dtype_corrected_for_endianness(None, None)