Page MenuHomePhabricator | MITK

Casting of images to and from itkImage modifies image
Open, HighPublic

Description

When casting an mitk::Image to an itk::Image and back using the "CastToItkImage" and "CastToMitkImage" macros, the image seems to be modified even if the itk::Image is not touched.

I have created a unit-test that loads an image (input), casts it to an itk::Image and back to an mitk::Image (output) and compares the images.
The 'input' and 'output' images are not identical!

Event Timeline

engelm added a subscriber: engelm.Jul 17 2013, 10:12 AM

I attach the unittest that fails due to the error. It has a Qt dependency!

The first parameter is the input image, the second parameter is the path where intermediate images will be written to.

I have opened Pic3D both pre and post Cast-Test and created screenshots. The changes are obvious!

It looks like the type of the image has changed. Could you compare the header of the pre- and postcast Pic3D.nrrd file?

I have compared the ::Print() output of both input and output images and found out that they differ in pixeltype.

input : short
output: unsigned char

That explains the error on Pic3D. However, when I execute the test with a binary segmentation, the casting actually sets some voxels that are 0 in the original to some different values(various values between 5 and 208).

Don't think that should happen when you change the pixeltype.

Hm... first it shouldn't change the pixel type at all. And the values should remain the same for the binary-segmentation case.

I will take a look on it.

@Markus: it is actually clear that the pixel type changed, you have to know the correct output type when using the cast macro, so the 'problem' with the Pic3D is no bug as you cast it to the SegmentationImageType ( uchar, 3D ).

That's true.

However, the problem with the segmentation remains. Both input and output images are "unsigned char", so there should be no messing around here.

I attached the binary segmentation that Ive been testing with.
The created output.nrrd has pixel values != 0 at
0,0,0
1,0,0
2,0,0
3,0,0

8,0,0
9,0,0
10,0,0
11,0,0

The rest seems to be fine. I cannot see a pattern here...

Status report:

The problem is the in-place casting ( but still need to find out why? ). When passing another mitk::Image to hold the results of the CastToMitkImage, the data is transferred correctly.

The design of CastToMitkImage does not correctly cover the case of in-place usage. The method calls an InitializeByItk() method which then consequently calls the

mitk::Image( const PixelType&, uint, const uint*, uint )

which then resets ALL(!) of its associated ImageDataItemPointers stored in the vectors m_Slices, m_Volumes, m_Channels and m_CompleteData to a NULL pointer. One of the pointers is the one passed to the ItkImage while casting the input to MITK. So the initialize deletes the data that should be copied in.

The CastToMitkImage calls

mitkoutputimage->InitializeByItk(itkimage);
mitkoutputimage->SetChannel(itkimage->GetBufferPointer());

In the (rather simple) test-case Marcus posted, omitting the

mitkoutputimage->InitializeByItk(itkimage);

call if the mitkoutputimage already has a valid channel works perfectly.

But only checking for IsValidChannel() is somehow not sufficient, especially if one of the dimensions or the pixel type of the mitk image has changed.

Proposed Solution:

Check for in-place usage ( the mitk image has already the to be imported pointer either as slice or volume or channel or complete data ) and

(1) throw an exception if the dimension, size or pixel type changed

(2) make a temporary copy of the pointer to be imported before calling InitializeByItk and use this only in the SetChannel call

In all other cases the original data of the image can be released ( not needed if none of the geometry and pixeltype changed ) and the data copied-in through the SetChannel call.

maleike added a subscriber: maleike.Aug 7 2013, 5:55 PM

(In reply to Jan Hering from comment #10)

Proposed Solution:


Check for in-place usage ( the mitk image has already the to be imported
pointer either as slice or volume or channel or complete data ) and

(1) throw an exception if the dimension, size or pixel type changed

(2) make a temporary copy of the pointer to be imported before calling

InitializeByItk and use this only in the SetChannel call

We shortly talked on the phone to get the same idea of a solution. This first suggestion would actually not fix the issue but only detect errors. The second one would work around the current problem and produce correct results.

I would favor a solution that detects both sides of the case macro point to the same memory area, and if this is the case, then the "casting" would actually not have to do much.

Current release is finished. Reseting target milestone...

hering removed hering as the assignee of this task.Sep 2 2016, 4:00 PM
hering added a subscriber: hering.