[Insight-developers] Neighborhood iterator refactoring request for comment

Joshua Cates cates@sci.utah.edu
Wed, 27 Nov 2002 11:59:14 -0700 (MST)


Hi Folks,

I am refactoring the neighborhood iterators to optimize them with respect
to current use and expected future use.  I expect to change their
implementation slightly (mostly transparent to the user), but also I want
to add some new functionality.

Below is a summary of the functionality that both exists in the iterators
and that I think would be nice to have based on how I've used the
iterators and how I've seen others use them.

Items which are new or represent changes are marked with +.


itk neighborhood iterators

(1)+ A single iterator class handles forward, backward, and random access,
and automatically enables/disables bounds checking at construction time 
by analyzing its iteration region.  This is different from original 
design where increasing functionality was added through subclassing.

(2) The API supports access of neighbor pixel values through
    (a) A scalar index into a linear array.  it.GetPixel(14)
   +(b) An itk::Offset from the center index.  
        it.GetPixel(itk::Offset<3>(1,0,0))
   +(c) A distance and direction along a principle axis. it.GetNext(0, 1)
    (d) Returning/setting entire neighborhood at once. it.GetNeighborhood()

(3) The API supports querying the following information about a neighbor 
pixel.
   +(a) Its image index (itk::Index) location.  
        it.GetIndex(14), it.GetIndex(itk::Offset<3>(1,0,0), etc.
   +(b) Its offset (itk::Offset) from the center of the neighborhood. 
        it.GetOffset(14), etc.
   +(c) What else do people need to know? Perhaps spatial distance from the
        center pixel?

(4) The API supports querying the following information about the 
neighborhood
    (a) Stride lengths along each neighborhood axis.  GetStride(1).
    (b) Extent of the neighborhood (radius)
    (c) Slices (std::slice) along axes.  GetSlice(2)
    (d) Size of the underlying neighborhood container, i.e. how many 
        pixels are the neighborhood.
    (e) Image location (itk::Index) of the center of the neighborhood.

(5)+ A new iterator class extends (1) to allow the creation of arbitrarily
     shaped iterators. Shaped neighborhood iterators have the following
     properties.
   (a) Iterators are optimized for linear array access, versus indexed
       lookups, though both should be possible.  This means I expect more use
       for things like neighborhood searching, summation of values, etc.
   (b) Respond to all access methods in (2).  Attempted access 
       of undefined pixel locations throws an exception.
   (c) Respond to all queries in (3). Querying on an undefined neighbor
       throws an exception.
   (d) Responds to a restricted subset of (4). GetSlice and GetStride  don't
       make sense in this context, for example.
   (e) Active or inactive neighbor locations are specified by offsets from  the
       center of the neighborhood.
   (f) Neighbor locations can be activated or deactivated after construction
       to support reshaping the neighborhood on-the-fly?
   (g) What else?

Attached below are some use case scenarios I put together which exercise
some of the existing and proposed API. Please send me your additional use
cases.

I welcome comments from anyone.  I plan to start working on these changes
next week.

Thanks,

Josh.
 

--------------------------------------------------------------------------------

typedef itk::Image<float, 3> ImageType;
ImageType::Pointer image;
  
  .
  .
  .

//
// In a 3x3 neighborhood mask, create a shaped neighborhood
//
ShapedNeighborhoodIterator<ImageType>::RadiusType radius = {1, 1, 1};
ShapedNeighborhoodIterator<ImageType> it(radius, image, 
image->GetRequestedRegion);

//
// activate the center cell
//
it->ActivateIndex(itk::Offset<3>(0,0,0));

//
// activate a list of cells which represent the 6-neighbors
//
std::list<itk::Offset<3> > active_cells;
active_cells.push_back(itk::Offset<3>(0,-1,0));
active_cells.push_back(itk::Offset<3>(0,1,0));
active_cells.push_back(itk::Offset<3>(-1,0,0));
active_cells.push_back(itk::Offset<3>(1,0,0));
active_cells.push_back(itk::Offset<3>(0,0,1));
active_cells.push_back(itk::Offset<3>(0,0,-1));

it->ActivateIndex(active_cells);


//
// Calculate all z partial derivatives using a non-shaped neighborhood
// 
itk::ConstNeighborhoodIterator<ImageType> it(region, image, 
image->GetRequestedRegion());
itk::NeighborhoodInnerProduct<ImageType> IP;
itk::DerivativeOperator<3> d_op;
d_op->SetOrder(1);
d_op->SetDirection(2);
d_op->CreateDirectional();
d_op->FlipAxes();

std::slice z_slice = it.GetSlice(2);
for (; !it.IsAtEnd(); ++it) { float df_dz = IP(z_slice, it, d_op); }


// 
// Calculate "half" forward derivatives in y direction
//
for (it.GoToBegin(); !it.IsAtEnd(); ++it) { float dy_forward = 
it.GetNext(2) - it.GetCenter(); }


//
// Sum pixel values in neighborhoods
//
for (it.GoToBegin(); !it.IsAtEnd(); ++it)
{
  float sum = 0.0f;
  for (unsigned i = 0; i < it.Size(); ++i) { sum += it.GetPixel(i); }
}

// 
// Search the neighborhood for a value and and find the itk::Offset of that 
// neighbor from the center of the neighborhood.  here used to implement a 
// "flood fill" type iterator.  Can use shaped or non-shaped iterator. 
// 
std::list<itk::Index<3> > indicies_to_visit;
indicies_to_visit.push_back( itk::Index<3>(x_seed,y_seed,z_seed) ); 

while (! indicies_to_visit.empty() ) 
{
  itk::Index idx = indicies_to_visit.front()
  indicies_to_visit.pop_front();
  this->do_some_stuff(idx);
  
  it.SetLocation(idx); // or perhaps: it+=(idx - it.GetIndex());
  for (unsigned i =0; i < it.Size(); ++i)   
    {
      if ( it.GetPixel(i) == it.GetCenterPixel() )
        {
          if (this->already_visited( it.GetIndex(i) ) == false)
            indicies_to_visit.push_back( it.GetIndex(i) );
        }
    }
}



______________________________
 Josh Cates			
 School of Computer Science	
 University of Utah
 Email: cates@sci.utah.edu
 Phone: (801) 587-7697
 URL:   www.cs.utk.edu/~cates