[Insight-users] More on: Can metrics handle nodata/void data
values ?
Luis Ibanez
luis . ibanez at kitware . com
Fri, 03 Oct 2003 14:10:11 -0400
Hi Carolyn,
There are several options for implementing the masking
of pixels during the metric computation.
We could imagine:
A) Non-Slick "if" statement in the interpolator
B) Iterators with attached mask that will skip
non-data values
C) Masked image that answers whether a pixel is
valid or not.
D) Explicitly adding the mask to the Metric
and setting another non-slick if statement.
Option (A) and (B) may not be that bad since the
evaluation of the interpolator (unfortunately)
involve a significant amount of code. An extra
if may not be too much of a disadvantage, at least
as far as performance is concerned.
Option (B) may be quite elegant but also harder
to implement an maintain. The main concern there
is to find a way of not penalizing the normal
use of iterators. There are at the hearth of
computations in all ITK filters. If we go that
way, we should probably duplicate the interpolators
in order to have new interpolator classes supporting
masking.
Option (C) could be implemented using SpatialObjects.
The big difference here is that the registration will
be then performed between two SpatialObjects that
each one encapsulate an image (fixed and moving images
respectively).
I still would be inclined to do the modifications on
the metrics themselves, rather than the iterators.
Making the change in the iterator simply hides the
"if" deeper into the code.
For example, If we look at the code of MeanSquaresImage
Metric (about lines 66-84). The masking could be implemented
by adding the lines marked with "#"
>
> while(!ti.IsAtEnd())
> {
>
> index = ti.GetIndex();
>
> typename Superclass::InputPointType inputPoint;
> fixedImage->TransformIndexToPhysicalPoint( index, inputPoint );
>
# if( !m_FixedSpatialObjectMask->IsInside( inputPoint ) )
# {
# ++ti;
# continue;
# }
> typename Superclass::OutputPointType transformedPoint = m_Transform->TransformPoint( inputPoint );
>
# if( !m_MovingSpatialObjectMask->IsInside( transformedPoint ) )
# {
# ++ti;
# continue;
# }
> if( m_Interpolator->IsInsideBuffer( transformedPoint ) )
> {
> const RealType movingValue = m_Interpolator->Evaluate( transformedPoint );
> const RealType fixedValue = ti.Get();
> m_NumberOfPixelsCounted++;
> const RealType diff = movingValue - fixedValue;
> measure += diff * diff;
> }
>
Similar lines should be inserted in the GetDerivative and
GetValueAndDerivative() methods.
The Fixed and Moving SpatialObject mask could be added at the level
of the ImageToImageMetric class.
This modification has been entered as a "Feature Request" in the
Bug tracker, under ID = 255.
http://www . itk . org/Bug/query . php?op=query
We will be very interested in following your tests if you
implement this feature in the immediate future.
(or in coordinating efforts otherwise).
Luis
---------------------------------
Carolyn Johnston wrote:
> Hi Luis,
>
> apropos of this discussion about handling nodata values, which actually
> took place a little while ago (but I am actually now on the verge of
> making these modifications):
>
> I like the idea of making the interpolator return 'true' to the query of
> whether a nodata pixel is outside the moving data region. That's slick,
> by golly, which is what OO programming is all about: slickness!
>
> But sticking an 'if' statement into the loop that iterates over the
> fixed image's datapoints, to check for nodata values... welllll...
> that's not very slick. :D
>
> But here's what I'm thinking. I'm thinking of using a "Decorator"
> pattern to make the images aware of their own nodata values (lest this
> sound pointlessly tricky, realize that I am also doing multiresolution
> registration and will have to figure out how to handle the filtering of
> nodata values as well). What do you think about modifying the fixed
> image iterator so that it simply skips over image pixels that are nodata
> values as though they weren't there?
>
> If I made these modifications, I don't believe I would have to rewrite
> the metrics at all.
>
> Please let me know what you think of all this and how you would do it
> differently.
>
> :) Thanks, Carolyn
>
>
> Luis Ibanez wrote:
>
>> Hi Carolyn,
>>
>> Thanks for the clarification.
>>
>> That actually raises a good point: We will need two masks
>> instead of one. In that way one mask can be associated to
>> the fixed image space and the other to the moving image
>> space.
>>
>> ---
>>
>> Now that, on the pragmatic side:
>> You probably already have assigned a distinct gray value
>> to the pixels that you know are "void".
>>
>> You can make a "minimally invasive intervention" in the
>> itkNormalizedCorrelation ImageToImageMetric and/or
>> the itkMeanSquaresImageToImageMetric.
>>
>> Simply go to the loop that iterates over the pixels of the
>> fixed image and add an "if()" that test for the "void" pixel
>> value.
>>
>> For the moving image you have two options
>>
>> 1) the easy option
>> 2) the correct option
>> 3) the elegant option
>>
>> The easy option is to use nearestneighbor interpolator,
>> and let the interpolator get the gray value of the moving
>> image for you. Then you test this value against your "void"
>> value and reject or accept the pixel accordingly.
>>
>> The correct option requires you to first map the point to the
>> coordinates of the moving image, then convert Physical point
>> to index, and finally probe the moving image in order to
>> obtain the pixel value.
>>
>> The elegant option involves to built-in this functionality
>> on the interpolator itself. Part of the work of the interpolator
>> is to determine if the requirested position is inside or outside
>> of the image. So, it is natural for the API to also manage
>> holes inside the image. In this option you could take your
>> favorite ITK interpolator, copy it, rename it and modify
>> its code, for the verifiction of being inside or outside.
>> You will add a check for the pixel value against the code
>> you user for "void" pixels.
>>
>>
>>
>> Regards,
>>
>>
>> Luis
>>
>>
>> ---------------------------
>>
>