[Insight-developers] Image memory model

Karthik Krishnan karthik.krishnan at kitware.com
Sat May 24 20:32:01 EDT 2008


Volken, Dan:

Thanks for the detailed explanation. Comments are inlined.

On Sat, May 24, 2008 at 1:28 AM, Dan Mueller <dan.muel at gmail.com> wrote:
> Thanks for engaging in this discussion with me.
>
> To cut to the chase, my actual problem is politics -- the "not
> invented here syndrome". I am trying to introduce ITK into my project,
> but have encountered resistance from other developers. The memory
> model is their biggest concern; they seem blinded to the plethora of
> advantages ITK offers. As such I am investigating the possibility of
> removing this stumbling block. I guess suggesting a breaking change to
> ITK due to the internal politics at my work is probably not a very
> good idea :P Let me describe the problem further to see if there is
> another way round.
>
> Some background. We have an extensive image processing application
> which uses a slice-contiguous model (based on DICOM). Images are read
> from disk, loaded into memory, image processing operations performed,
> and results rendered to screen or written back to disk. There may be 5
> or more 1GB images open at a time (many situations require less
> memory, some require more). To integrate ITK I am currently 1.
> importing each slice using the Import filter, copying the slices into
> contiguous memory using the Tile filter, applying the desired ITK
> algorithm(s), and converting the contiguous memory back to slices (if
> required). The conversions consume unnecessary time and (more
> importantly) memory, which is not acceptable in various circumstances
> (having to consume an extra 5GB of memory to duplicate image slices is
> not very nice). Some of the ITK operations I want to use in this
> setting include (but are not limited to): registration, level set
> segmentation, intensity-based segmentation, distance transforms, and
> mesh operations (image to mesh, mesh to image).
>
> So, how exactly would the proposed itk::SliceContiguousImage subclass
> work? I guess I would need to override the PixelContainer type (by
> adding a new template argument to itk:Image, which defaults to
> ImportImageContainer) and override the Set/GetPixel(..) methods. Then
> I guess I would need to create a new ImportSlicedImagePixelContainer
> to replace ImportImageContainer. I'm not sure I understand how the new
> image type would interact with linear iterators -- these iterators use
> m_Buffer+m_Offset throughout. Could I somehow override
> ImageBase::ComputeOffsetTable(..) to return the correct offset based
> on the slice memory model?

> -OR-
> 2. attempt to get linear *and* neighbor iterators to work with a slice
> contiguous memory model.
>

Very much possible with the iterators.

We already do this with the itk::VectorImage. More on it here
  http://www.itk.org/Wiki/Proposals:VectorImage

Any access to any pixel via the iterators goes through another class
called the "PixelAccessor" and the "PixelAccessorFunctor".

Each image may define its own PixelAccessor. ie its a trait of the Image.

For instance the itk::VectorImage, which also internally has a
different memory layout from a standard itk::Image, this is done by
defining our own PixelAccessor. Please see
itk::DefaultVectorPixelAccessor

------

If you follow the code of Iterator's Set method:

1. Iterator delegates the actual job of access to the pixel to a
"PixelAccessorFunctor".

   PixelType iterator.Get()   reads
    { return pixelAccessorFunctor.Get(*(m_Buffer+m_Offset)); }


2. PixelAccessorFunctor delegates it to the appropriate
"PixelAccessor" for the image. For instance for the
DefaultVectorPixelAccessorFunctor, the one used for the VectorImage,
this reads:

  inline ExternalPixelType Get( const InternalPixelType &input ) const
    {
    return m_PixelAccessor.Get( input, &input  - m_Begin );
    }

Note that we pass the offset in:  (&input  - m_Begin)


3. The PixelAccessor for a vector image uses the offset to compute the
correct index into its memory layout and return the right pixel on the
fly.

  ExternalType Get( const InternalType & input, const unsigned long
offset ) const
    {
    ExternalType output( (&input)+(offset*m_OffsetMultiplier) ,
m_VectorLength );
    return output;
    }

--------

The same holds true for the iterator.Set(..) methods.

You can see that PixelAccessors allow you to:

(a) Plug in your own way of accessing the image, by computing the
actual index from the iterator's offset (m_Offset).

(b) You don't need this, but FYI. You can construct a different return
type/ input type as well, so as to implement a facade. For the
VectorImage, we not only have a different memory layout, but we wish
to exhibit a facade to the actual pixel type (much like
ImageAdaptors). Pixels retreived/set via Get/Set are VectorPixels,
however the memory organization is that of multi-component Image<
pixels >. (more on the wiki).

Note that all the methods above are inlined for rapid access despite
the delegation.

--------

So the gist is:

  (a) You would write your own Image class: itk::SliceImage.

  (b) You would write your own SliceImagePixelAccessor and
SliceImagePixelAccessorFunctor

  (c) These classes would be traits of your image.

That's it. I also take back statements the statement we might have
made that you would have to write your own iterator. I don't think
this will be needed. All the current iterators in ITK will work fine
on your image as they do for VectorImage.

------

GetBufferPointer() as such is never (I think) used within ITK
directly. However it provides a convinient way of exporting data to
other libraries.

Hope this helps.

--
Karthik Krishnan
R&D Engineer,
Kitware Inc.


More information about the Insight-developers mailing list