@rijobro
The definition of the constructor only contains a ToDo, and is not actually implemented
STIRImageData::STIRImageData(const ImageData& id)
{
only contains throw std::runtime_error("TODO - create STIRImageData from general SIRFImageData.");
}
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.
I tried to write it myself, as otherwise I was stuck, and to simply hard copy the geometrical information from the one image dataset to the other as in the code below.
But when passing the image on, the geometrical info is different from the one in id
.
Is the geometrical information after being set in the constructor overwritten at some point by set_up_geom_info()?
STIRImageData::STIRImageData(const ImageData& id)
{
// throw std::runtime_error("TODO - create STIRImageData from general SIRFImageData.");
std::shared_ptr<const VoxelisedGeometricalInfo3D > sptr_geo_info = id.get_geom_info_sptr();
VoxelisedGeometricalInfo3D::Offset curr_offset = sptr_geo_info->get_offset();
VoxelisedGeometricalInfo3D::Spacing curr_spacing = sptr_geo_info->get_spacing();
VoxelisedGeometricalInfo3D::Size curr_size = sptr_geo_info->get_size();
Voxels3DF voxels(
stir::IndexRange3D( 0, curr_size[2] - 1, 0, curr_size[1] - 1, 0, curr_size[0] - 1 ),
Coord3DF(curr_offset[2], curr_offset[1], curr_offset[0]),
Coord3DF(curr_spacing[2], curr_spacing[1], curr_spacing[0]));
_data.reset(voxels.clone());
copy(id.begin(), begin(), end());
// hard copy the geometrical information
VoxelisedGeometricalInfo3D vox_geo( *sptr_geo_info );
this->_geom_info_sptr = std::make_shared<VoxelisedGeometricalInfo3D >( vox_geo );
}
I gave it a go here: rijobro@e0cfe93.
The relevant bit of code looks like this:
// Get range from number of voxels
const VoxelisedGeometricalInfo3D::Size &sz =
id.get_geom_info_sptr()->get_size();
stir::IndexRange3D range(0, sz[2] - 1,
-(sz[1] / 2), -(sz[1] / 2) + sz[1] - 1,
-(sz[0] / 2), -(sz[0] / 2) + sz[0] - 1);
// Spacing (STIR is z,y,x)
const VoxelisedGeometricalInfo3D::Spacing &sp =
id.get_geom_info_sptr()->get_spacing();
Coord3DF spacing(sp[2], sp[1], sp[0]);
// Offset
const VoxelisedGeometricalInfo3D::Offset &of =
id.get_geom_info_sptr()->get_offset();
Coord3DF offset(of[2], of[1], of[0]);
// Construct image
this->_data = std::make_shared<Voxels3DF>(range,offset,spacing);
// Copy voxel values
this->ImageData::fill(id);
// Set up the geom info
this->set_up_geom_info();
I'm pretty sure that the range
and spacing
are correct, but the offset needs to be changed. It should be doable with the information in STIR's ITK reader (line 133 here), but I haven't got around to it yet. @ashgillman might be able to give you a hand.
Hmm, it will be a little more tricky than this. STIR images are only allowable in one specific orientation. We had some discussions at a few points about how the best way to deal with this would be. It could be
a) force the user to do the resampling manually, e.g., with Niftyreg. Then this function would just check the orientation is correct
b) automatically do the resampling internally (not the best idea in my opinion, as then we have a dependency on whichever resampling is used.
It looks like @rijobro made some progress here:
One key issue at the moment, with both solutions at the moment, it that STIR doesn't accept multiple orientations, rather the orientation is tied to the patient position.
In STIR "physical coordinates", x goes to the right of the gantry, y goes up, z goes in (best not to quote me here, I might have flipped one).
i.e., it is gantry-based, not patient based. This means the patient-based orientation varies with the patient orientation within the scanner. But apart from that, it is fixed. You can't, in STIR, encode that actually the y axis goes right, like you can in other image formats. It also means that you can't have a STIR image that lies at an angle.
This means, that any time you are converting a SIRF image to a STIR image, you will have to perform some resampling. However, this can come second, first step is to be able to convert a STIR image to SIRF image and back again, without resampling, and can throw an error if the sampling is incorrect.
rijobro@ec8f804#diff-ccbbca06226184a74b79c716b573aa4fR270
^ The confusion as to what to do here comes from the fact that it looks like maybe in SIRF we don't actually store the patient orientation? I'm not sure how we overlooked that, woops! It probably needs to be added to VoxelisedGeometricalInfo
rijobro@ec8f804#diff-ccbbca06226184a74b79c716b573aa4fR276
^ This line is also a bit tricky. Essentially, the following has to be inverted
So you need to work out which voxel will have index(0, 0, 0) in STIR (Somewhere approximately at the middle of the first/last axial slice, see STIR convention)
Then you will have to do some sort of inversion of the logic in get_LPS_coordinates_for_indices()
, which will also need the patient orientation as above.
I think I already did that somewhere at some stage, you basically make a temporary image with incorrect origin/offset/whatever-you-want-to-call-it, then use it with get_phyxical_coordinates_for_LPS_coordinates()
. Offset needs to be set to the "physical coordinates" of voxel index(0, 0, 0).
NB:
Remember that DirectionMatrix only describes the voxel layout direction w.r.t. the patient (LPS).
Patient orientation is essentially just a means of encoding the LPS direction w.r.t. the gantry.
It is often omitted from image formats (DICOM, NIfTI, etc.) because you don't need it usually in analysis. You only need it if you are simulating or reconstruction raw data, and need to know about the gantry.
I agree that in the first instance, we can check that the x-, y- and z-directions of the transformation matrix are aligned with the gantry. In that case (which is probably 99% of the time), we won't have to worry about resampling. Right?
Yep. And it is not clear that resampling should ever be done automatically. I'd suggest that is the user's responsibility, and STIRImageData::STIRImageData
only ever check and throws an error.
Agreed.
Any SIRF image can now be created from any other (probably...).
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
Closed #306.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.