Skip to content

Commit

Permalink
Fix downsamplers so they take into account number of bytes per element (
Browse files Browse the repository at this point in the history
#242)

* Make sure size calculation in downsampling takes into account number of bytes per element

* fix issues with extent in hdf5 readers
  • Loading branch information
lauramurgatroyd authored May 23, 2022
1 parent 8eb758a commit f9f8e62
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## v22.x.x
* When a new image is loaded on the 3D viewer, reset camera position, slice and volume orientation and visibility, and clipping planes.
* Fix downsampling of resample readers when working with datatypes other than uint8. Previously resulting size of image was multiple times greater than requested target size as uint8 type was always assumed.

## v22.1.1
* Changes released in 22.0.1, but also requires vtk version >= 9.0.3
Expand Down
19 changes: 14 additions & 5 deletions Wrappers/Python/ccpi/viewer/utils/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,7 @@ def RequestData(self, request, inInfo, outInfo):
else:
shape = list(readshape)[::-1]

total_size = shape[0] * shape[1] * shape[2]
total_size = shape[0] * shape[1] * shape[2] * self.GetBytesPerElement()

max_size = self.GetTargetSize()

Expand Down Expand Up @@ -1334,10 +1334,8 @@ def _GetInternalChunkReader(self):
# Here we read just the chunk from the hdf5 file:
cropped_reader = HDF5SubsetReader()
cropped_reader.SetInputConnection(reader.GetOutputPort())
dims = self.GetStoredArrayShape()
# Set default extent to full extent:
cropped_reader.SetUpdateExtent(
(0, dims[0]-1, 0, dims[1]-1, 0, dims[2]-1))
cropped_reader.SetUpdateExtent((0, -1, 0, -1, 0, -1))
self._ChunkReader = cropped_reader
return cropped_reader

Expand All @@ -1346,6 +1344,9 @@ def UpdateChunkToRead(self, start_slice):
start_slice in the z direction'''
num_slices_per_chunk = self._GetNumSlicesPerChunk()
end_slice = start_slice + num_slices_per_chunk - 1
end_z_value = self.GetStoredArrayShape()[2]-1
if end_slice > end_z_value:
end_slice = end_z_value
if start_slice < 0:
raise ValueError('{} ERROR: Start slice cannot be negative.'
.format(self.__class__.__name__))
Expand Down Expand Up @@ -1654,6 +1655,8 @@ def RequestData(self, request, inInfo, outInfo):
read_data = reader.GetOutput()
outData.ShallowCopy(read_data)

return 1


# ------------ RESAMPLE FROM MEMORY: ------------------------------------------------------------------------------

Expand Down Expand Up @@ -1701,6 +1704,11 @@ def GetTargetSize(self):
''' Get the total target size to downsample image to, in bytes.'''
return self._TargetSize

def GetBytesPerElement(self):
''' Get number of bytes per element'''
if hasattr(self, '_BytesPerElement'):
return self._BytesPerElement

def GetOutput(self):
return self.GetOutputDataObject(0)

Expand All @@ -1709,6 +1717,7 @@ def ReadDataSetInfo(self, inData):
self._Origin = inData.GetOrigin()
self._Extent = inData.GetExtent()
self._StoredArrayShape = (self._Extent[1]+1, (self._Extent[3]+1), (self._Extent[5]+1))
self._BytesPerElement = Converter.vtkType_to_bytes[inData.GetScalarType()]

def GetElementSpacing(self):
''' Returns the spacing of the input dataset as a tuple'''
Expand Down Expand Up @@ -1736,7 +1745,7 @@ def RequestData(self, request, inInfo, outInfo):
extent = self.GetExtent()
shape = self.GetStoredArrayShape()

total_size = shape[0] * shape[1] * shape[2]
total_size = shape[0] * shape[1] * shape[2] * self.GetBytesPerElement()

max_size = self.GetTargetSize()

Expand Down
9 changes: 5 additions & 4 deletions Wrappers/Python/ccpi/viewer/utils/hdf5_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,20 +219,21 @@ def RequestUpdateExtent(self, request, inInfo, outInfo):
vtk.vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT())
set_extent = list(info.Get(
vtk.vtkStreamingDemandDrivenPipeline.UPDATE_EXTENT()))

for i, value in enumerate(set_extent):
if value == -1:
set_extent[i] = whole_extent[i]
else:
if i % 2 == 0:
if value < whole_extent[i]:
raise ValueError("Requested extent {}\
is outside of original image extent {}".format(
set_extent, whole_extent))
is outside of original image extent {} as {}<{}".format(
set_extent, whole_extent, value, whole_extent[i]))
else:
if value > whole_extent[i]:
raise ValueError("Requested extent {}\
is outside of original image extent {}".format(
set_extent, whole_extent))
is outside of original image extent {} as {}>{}".format(
set_extent, whole_extent, value, whole_extent[i]))

self.SetUpdateExtent(set_extent)

Expand Down
9 changes: 6 additions & 3 deletions Wrappers/Python/test/test_hdf5.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def test_read_hdf5_channel(self):
np.testing.assert_array_equal(
self.input_4D_array[channel_index], read_array)

def test_read_cropped_hdf5(self):
def test_hdf5_subset_reader(self):
# With the subset reader: -----------------------------
# Test cropping the extent of a dataset
cropped_array = self.input_3D_array[1:3, 3:6, 0:3]
Expand All @@ -81,7 +81,10 @@ def test_read_cropped_hdf5(self):
array_image_data = cropped_reader.GetOutputDataObject(0)
read_cropped_array = Converter.vtk2numpy(array_image_data)
np.testing.assert_array_equal(cropped_array, read_cropped_array)
# With the Cropped reader: -----------------------------

def test_read_cropped_hdf5_reader(self):
# # With the Cropped reader: -----------------------------
cropped_array = self.input_3D_array[1:3, 3:6, 0:3]
reader = cilHDF5CroppedReader()
reader.SetFileName(self.hdf5_filename_3D)
reader.SetDatasetName("ImageData")
Expand Down Expand Up @@ -154,7 +157,7 @@ def test_hdf5_resample_reader(self):
resulting_shape = (extent[1]+1, (extent[3]+1), (extent[5]+1))
og_shape = np.shape(self.input_3D_array)
og_shape = (og_shape[2], og_shape[1], og_shape[0])
og_size = og_shape[0]*og_shape[1]*og_shape[2]
og_size = og_shape[0]*og_shape[1]*og_shape[2]*readerhdf5.GetBytesPerElement()
expected_shape = calculate_target_downsample_shape(
target_size, og_size, og_shape)
self.assertEqual(resulting_shape, expected_shape)
Expand Down
16 changes: 9 additions & 7 deletions Wrappers/Python/test/test_resample_readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,17 @@ class TestResampleReaders(unittest.TestCase):
def setUp(self):
# Generate random 3D array and write to HDF5:
np.random.seed(1)
self.input_3D_array = np.random.randint(10, size=(5, 10, 6), dtype=np.uint8)
bits = 16
self.bytes_per_element = int(bits/8)
self.input_3D_array = np.random.randint(10, size=(5, 10, 6), dtype=eval(f"np.uint{bits}"))
bytes_3D_array = bytes(self.input_3D_array)
self.raw_filename_3D = 'test_3D_data.raw'
with open(self.raw_filename_3D, 'wb') as f:
f.write(bytes_3D_array)

# write header to go with raw file
self.mhd_filename_3D = 'raw_header.mhd'
typecode = 'uint8'
typecode = f'uint{bits}'
big_endian = False
header_length = 0
shape = np.shape(self.input_3D_array)
Expand Down Expand Up @@ -77,7 +79,7 @@ def test_raw_resample_reader(self):
extent = image.GetExtent()
resulting_shape = (extent[1]+1, (extent[3]+1), (extent[5]+1))
og_shape = (og_shape[2], og_shape[1], og_shape[0])
og_size = og_shape[0]*og_shape[1]*og_shape[2]
og_size = og_shape[0]*og_shape[1]*og_shape[2]*self.bytes_per_element
expected_shape = calculate_target_downsample_shape(
target_size, og_size, og_shape)
self.assertEqual(resulting_shape, expected_shape)
Expand Down Expand Up @@ -105,10 +107,10 @@ def test_raw_resample_reader(self):
reader.Update()
image = reader.GetOutput()
extent = image.GetExtent()
shape_not_acquisition = calculate_target_downsample_shape(
shape_acquisition = calculate_target_downsample_shape(
target_size, og_size, og_shape, acq=True)
expected_size = shape_not_acquisition[0] * \
shape_not_acquisition[1]*shape_not_acquisition[2]
expected_size = shape_acquisition[0] * \
shape_acquisition[1]*shape_acquisition[2]
resulting_shape = (extent[1]+1, (extent[3]+1), (extent[5]+1))
resulting_size = resulting_shape[0] * \
resulting_shape[1]*resulting_shape[2]
Expand Down Expand Up @@ -140,7 +142,7 @@ def test_meta_and_numpy_resample_readers(self):
extent = image.GetExtent()
resulting_shape = (extent[1]+1, (extent[3]+1), (extent[5]+1))
og_shape = (og_shape[2], og_shape[1], og_shape[0])
og_size = og_shape[0]*og_shape[1]*og_shape[2]
og_size = og_shape[0]*og_shape[1]*og_shape[2]*self.bytes_per_element
expected_shape = calculate_target_downsample_shape(
target_size, og_size, og_shape)
self.assertEqual(resulting_shape, expected_shape)
Expand Down
6 changes: 4 additions & 2 deletions Wrappers/Python/test/test_vtk_image_resampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ class TestVTKImageResampler(unittest.TestCase):
def setUp(self):
# Generate random 3D array and convert to VTK Image Data:
np.random.seed(1)
self.input_3D_array = np.random.randint(10, size=(50, 10, 60), dtype=np.uint8)
bits = 16
self.bytes_per_element = int(bits/8)
self.input_3D_array = np.random.randint(10, size=(50, 10, 60), dtype=eval(f"np.uint{bits}"))
self.input_vtk_image = Converter.numpy2vtkImage(self.input_3D_array)


Expand All @@ -44,7 +46,7 @@ def test_vtk_resample_reader(self):
og_shape = np.shape(self.input_3D_array)
resulting_shape = (extent[1]+1, (extent[3]+1), (extent[5]+1))
og_shape = (og_shape[2], og_shape[1], og_shape[0])
og_size = og_shape[0]*og_shape[1]*og_shape[2]
og_size = og_shape[0]*og_shape[1]*og_shape[2]*self.bytes_per_element
expected_shape = calculate_target_downsample_shape(
target_size, og_size, og_shape)
self.assertEqual(resulting_shape, expected_shape)
Expand Down

0 comments on commit f9f8e62

Please sign in to comment.