[CCPPETMR/SIRF] STIRImageData Constructor from ImageData& (#306)

0 views
Skip to first unread message

Johannes Mayer

unread,
Feb 11, 2019, 5:16:55 AM2/11/19
to CCPPETMR/SIRF, Subscribed

@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.

Johannes Mayer

unread,
Feb 11, 2019, 10:04:29 AM2/11/19
to CCPPETMR/SIRF, Subscribed

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 );
}

Richard Brown

unread,
Feb 11, 2019, 11:15:45 AM2/11/19
to CCPPETMR/SIRF, Subscribed

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.

Ashley Gillman

unread,
Feb 24, 2019, 11:50:11 PM2/24/19
to CCPPETMR/SIRF, Subscribed

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.

Ashley Gillman

unread,
Apr 5, 2019, 12:14:19 AM4/5/19
to CCPPETMR/SIRF, Subscribed

It looks like @rijobro made some progress here:

rijobro@ec8f804

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

https://github.com/CCPPETMR/SIRF/blob/c64e3d0658096097c6d205be85d80e3f528d046c/src/xSTIR/cSTIR/stir_data_containers.cpp#L550-L557

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).

Ashley Gillman

unread,
Apr 5, 2019, 12:22:17 AM4/5/19
to CCPPETMR/SIRF, Subscribed

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.

Richard Brown

unread,
Apr 5, 2019, 5:29:42 AM4/5/19
to CCPPETMR/SIRF, Subscribed

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?

Ashley Gillman

unread,
Apr 8, 2019, 4:09:32 AM4/8/19
to CCPPETMR/SIRF, Subscribed

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.

Richard Brown

unread,
Apr 8, 2019, 5:15:20 AM4/8/19
to CCPPETMR/SIRF, Subscribed

Agreed.

Kris Thielemans

unread,
Jan 24, 2020, 6:22:21 AM1/24/20
to CCPPETMR/SIRF, Subscribed

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.

Kris Thielemans

unread,
Jan 24, 2020, 6:22:22 AM1/24/20
to CCPPETMR/SIRF, Subscribed

Closed #306.


You are receiving this because you are subscribed to this thread.

Reply to this email directly, view it on GitHub, or unsubscribe.

Reply all
Reply to author
Forward
0 new messages