def test_multi_frame_three_to_one(self): """Test a multi-frame image where each frame is three fragments""" # 2 frames, each 3 fragments long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x0C\x00\x00\x00' \ b'\x00\x00\x00\x00' \ b'\x20\x00\x00\x00' \ b'\x40\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') assert next(frames) == (b'\x02\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') assert next(frames) == (b'\x03\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') pytest.raises(StopIteration, next, frames)
def test_multi_frame_varied_ratio(self): """Test a multi-frame image where each frames is random fragments""" # 3 frames, 1st is 1 fragment, 2nd is 3 fragments, 3rd is 2 fragments bytestream = b'\xFE\xFF\x00\xE0' \ b'\x0C\x00\x00\x00' \ b'\x00\x00\x00\x00' \ b'\x0E\x00\x00\x00' \ b'\x32\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x06\x00\x00\x00\x01\x00\x00\x00\x00\x01' \ b'\xFE\xFF\x00\xE0' \ b'\x02\x00\x00\x00\x02\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x06\x00\x00\x00\x03\x00\x00\x00\x00\x02' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x02\x00\x00\x00\x02\x04' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00\x00\x01', ) assert next(frames) == (b'\x02\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00\x00\x02') assert next(frames) == (b'\x03\x00\x00\x00', b'\x02\x04') pytest.raises(StopIteration, next, frames)
def test_multi_frame_varied_ratio(self): """Test a multi-frame image where each frames is random fragments""" # 3 frames, 1st is 1 fragment, 2nd is 3 fragments, 3rd is 2 fragments bytestream = b'\xFE\xFF\x00\xE0' \ b'\x0C\x00\x00\x00' \ b'\x00\x00\x00\x00' \ b'\x0E\x00\x00\x00' \ b'\x32\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x06\x00\x00\x00\x01\x00\x00\x00\x00\x01' \ b'\xFE\xFF\x00\xE0' \ b'\x02\x00\x00\x00\x02\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x06\x00\x00\x00\x03\x00\x00\x00\x00\x02' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x02\x00\x00\x00\x02\x04' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00\x00\x01', ) assert next(frames) == (b'\x02\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00\x00\x02') assert next(frames) == (b'\x03\x00\x00\x00', b'\x02\x04') pytest.raises(StopIteration, next, frames)
def test_multi_frame_three_to_one(self): """Test a multi-frame image where each frame is three fragments""" # 2 frames, each 3 fragments long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x0C\x00\x00\x00' \ b'\x00\x00\x00\x00' \ b'\x20\x00\x00\x00' \ b'\x40\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x03\x00\x00\x00' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') assert next(frames) == (b'\x02\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') assert next(frames) == (b'\x03\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') pytest.raises(StopIteration, next, frames)
def test_empty_bot_single_fragment(self): """Test a single-frame image where the frame is one fragments""" # 1 frame, 1 fragment long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x00\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x01\x00\x00\x00' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00', ) pytest.raises(StopIteration, next, frames)
def test_empty_bot_single_fragment(self): """Test a single-frame image where the frame is one fragments""" # 1 frame, 1 fragment long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x00\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x01\x00\x00\x00' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00', ) pytest.raises(StopIteration, next, frames)
def test_empty_bot_multi_fragments_per_frame(self): """Test parsing with multiple fragments per frame.""" # 4 frames in 6 fragments with JPEG EOI marker bytestream = (b'\xFE\xFF\x00\xE0\x00\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xD9\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\xFF\xD9' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xD9\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xD9\x00') frames = generate_pixel_data(bytestream, 4) for ii in range(4): next(frames) with pytest.raises(StopIteration): next(frames)
def test_empty_bot_triple_fragment_single_frame(self): """Test a single-frame image where the frame is three fragments""" # 1 frame, 3 fragments long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x00\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x01\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x03\x00\x00\x00' frames = generate_pixel_data(bytestream, 1) assert next(frames) == (b'\x01\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') pytest.raises(StopIteration, next, frames)
def test_empty_bot_no_nr_frames_raises(self): """Test parsing raises if not BOT and no nr_frames.""" # 1 frame, 3 fragments long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x00\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x01\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x03\x00\x00\x00' msg = (r"Unable to determine the frame boundaries for the " r"encapsulated pixel data as the Basic Offset Table is empty " r"and `nr_frames` parameter is None") with pytest.raises(ValueError, match=msg): next(generate_pixel_data(bytestream))
def test_bot_triple_fragment_single_frame(self): """Test a single-frame image where the frame is three fragments""" # 1 frame, 3 fragments long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x00\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x01\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x03\x00\x00\x00' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00', b'\x02\x00\x00\x00', b'\x03\x00\x00\x00') pytest.raises(StopIteration, next, frames)
def test_empty_bot_missing_marker(self): """Test parsing not BOT and missing marker with multi fragments.""" # 4 frames in 6 fragments with JPEG EOI marker (1 missing EOI) bytestream = (b'\xFE\xFF\x00\xE0\x00\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xD9\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xFF\xD9' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xD9\x00') msg = (r"The end of the encapsulated pixel data has been " r"reached but one or more frame boundaries may have " r"been missed; please confirm that the generated frame " r"data is correct") with pytest.warns(UserWarning, match=msg): ii = 0 for frames in generate_pixel_data(bytestream, 4): ii += 1 assert 3 == ii
def test_multi_frame_one_to_one(self): """Test a multi-frame image where each frame is one fragment""" # 3 frames, each 1 fragment long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x0C\x00\x00\x00' \ b'\x00\x00\x00\x00' \ b'\x0C\x00\x00\x00' \ b'\x18\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x01\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x03\x00\x00\x00' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00', ) assert next(frames) == (b'\x02\x00\x00\x00', ) assert next(frames) == (b'\x03\x00\x00\x00', ) pytest.raises(StopIteration, next, frames)
def test_multi_frame_one_to_one(self): """Test a multi-frame image where each frame is one fragment""" # 3 frames, each 1 fragment long bytestream = b'\xFE\xFF\x00\xE0' \ b'\x0C\x00\x00\x00' \ b'\x00\x00\x00\x00' \ b'\x0C\x00\x00\x00' \ b'\x18\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x01\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x02\x00\x00\x00' \ b'\xFE\xFF\x00\xE0' \ b'\x04\x00\x00\x00' \ b'\x03\x00\x00\x00' frames = generate_pixel_data(bytestream) assert next(frames) == (b'\x01\x00\x00\x00', ) assert next(frames) == (b'\x02\x00\x00\x00', ) assert next(frames) == (b'\x03\x00\x00\x00', ) pytest.raises(StopIteration, next, frames)
def test_empty_bot_no_marker(self): """Test parsing not BOT and no final marker with multi fragments.""" # 4 frames in 6 fragments with JPEG EOI marker (1 missing EOI) bytestream = (b'\xFE\xFF\x00\xE0\x00\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xD9\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\x00\x00\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xD9\x00' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\xFF\xD9' b'\xFE\xFF\x00\xE0\x04\x00\x00\x00\x01\xFF\x00\x00') frames = generate_pixel_data(bytestream, 4) for ii in range(3): next(frames) msg = (r"The end of the encapsulated pixel data has been " r"reached but one or more frame boundaries may have " r"been missed; please confirm that the generated frame " r"data is correct") with pytest.warns(UserWarning, match=msg): next(frames) with pytest.raises(StopIteration): next(frames)
def get_pixeldata(ds: "Dataset") -> "numpy.ndarray": """Use the GDCM package to decode *Pixel Data*. Returns ------- numpy.ndarray A correctly sized (but not shaped) array of the entire data volume Raises ------ ImportError If the required packages are not available. TypeError If the image could not be read by GDCM or if the *Pixel Data* type is unsupported. AttributeError If the decoded amount of data does not match the expected amount. """ if not HAVE_GDCM: raise ImportError("The GDCM handler requires both gdcm and numpy") if HAVE_GDCM_IN_MEMORY_SUPPORT: gdcm_data_element = create_data_element(ds) gdcm_image = create_image(ds, gdcm_data_element) else: gdcm_image_reader = create_image_reader(ds) if not gdcm_image_reader.Read(): raise TypeError("GDCM could not read DICOM image") gdcm_image = gdcm_image_reader.GetImage() # GDCM returns char* as type str. Python 3 decodes this to # unicode strings by default. # The SWIG docs mention that they always decode byte streams # as utf-8 strings for Python 3, with the `surrogateescape` # error handler configured. # Therefore, we can encode them back to their original bytearray # representation on Python 3 by using the same parameters. pixel_bytearray = gdcm_image.GetBuffer().encode("utf-8", "surrogateescape") # Here we need to be careful because in some cases, GDCM reads a # buffer that is too large, so we need to make sure we only include # the first n_rows * n_columns * dtype_size bytes. expected_length_bytes = get_expected_length(ds) if ds.PhotometricInterpretation == 'YBR_FULL_422': # GDCM has already resampled the pixel data, see PS3.3 C.7.6.3.1.2 expected_length_bytes = expected_length_bytes // 2 * 3 if len(pixel_bytearray) > expected_length_bytes: # We make sure that all the bytes after are in fact zeros padding = pixel_bytearray[expected_length_bytes:] if numpy.any(numpy.frombuffer(padding, numpy.byte)): pixel_bytearray = pixel_bytearray[:expected_length_bytes] else: # We revert to the old behavior which should then result # in a Numpy error later on. pass numpy_dtype = pixel_dtype(ds) arr = numpy.frombuffer(pixel_bytearray, dtype=numpy_dtype) expected_length_pixels = get_expected_length(ds, 'pixels') if arr.size != expected_length_pixels: raise AttributeError( f"Amount of pixel data {arr.size} does not match the " f"expected data {expected_length_pixels}" ) file_meta: "FileMetaDataset" = ds.file_meta # type: ignore[has-type] tsyntax = cast(UID, file_meta.TransferSyntaxUID) if ( config.APPLY_J2K_CORRECTIONS and tsyntax in [JPEG2000, JPEG2000Lossless] ): nr_frames = getattr(ds, 'NumberOfFrames', 1) codestream = next(generate_pixel_data(ds.PixelData, nr_frames))[0] params = get_j2k_parameters(codestream) j2k_precision = cast( int, params.setdefault("precision", ds.BitsStored) ) j2k_sign = params.setdefault("is_signed", None) if not j2k_sign and ds.PixelRepresentation == 1: # Convert unsigned J2K data to 2's complement shift = cast(int, ds.BitsAllocated) - j2k_precision pixel_module = ds.group_dataset(0x0028) pixel_module.PixelRepresentation = 0 dtype = pixel_dtype(pixel_module) arr = (arr.astype(dtype) << shift).astype(numpy_dtype) >> shift if should_change_PhotometricInterpretation_to_RGB(ds): ds.PhotometricInterpretation = "RGB" return arr.copy()