[Insight-developers] Confused about persistance of filter outputs

Hans Johnson hans-johnson@uiowa.edu
Mon, 28 Apr 2003 13:12:41 -0500


Luis,

Thanks for your comments, but I believe that the comment regarding the 
processing of 100 images is incorrect.  I have tried to set up a 
pipeline where a list of N identically sized ::Pointers to images are 
processed by a single pipeline, and put into a list of N new ::Pointers 
to output images.  The problem is that all the output images end up 
pointing to the same image!

Here is some psuedo code that help me determine this:

std::list<itk::Image<>::Pointer> InputImages;
std::list<std::string> InputImageNames;
InputImageNames.push_back("Image1.hdr");
InputImageNames.push_back("Image2.hdr");
InputImageNames.push_back("Image3.hdr");

itk::StatisticalImageFilter<itk::Image<> > mySummer=::New();

itk::ImageFileReader<itk::Image<> > 
myReader=itk::ImageFileReader<itk::Image<> >::New()
int imagenumber=0;
for( i in InputImageNames)
{
   myReader->SetInput(*i);
   myReader->Update();
   InputImages.push_back(myReader->GetOutput());

   mySummer->SetInput(*i)
   mySummer->Update();
   std::cout << imagenumber << " Sum is" << mySummer->GetSum() << std::endl
   imagenumber++;
}

std::cout << "Recheck sums" << std::endl;
imagenumber=0;
for( currImage in InputImages)
{
   mySummer->SetInput(*currImage)
   mySummer->Update();
   std::cout << imagenumber << " Sum is" << mySummer->GetSum() << std::endl;
    imagenumber++;
}

The resulting output was something like:

0 Sum is 12321321
1 Sum is 62345234
2 Sum is 99999999
Recheck sums
0 Sum is 99999999
1 Sum is 99999999
2 Sum is 99999999

To solve this problem, I have begun reinstantiating the filters every 
time I use them (i.e. by putting them inside the for loop).

Regards,
Hans


Luis Ibanez wrote:
> 
> Hi Hans,
> 
> When you have a pipeline and want to make use of
> one of the intermediate outputs, the easy way to
> proceed is to connect an Observer to the filter
> whose output you want to use. This Command/Observer
> should look for the "EndEvent" of the filter.
> 
> Whenever the filter is done processing its output,
> it will invoke an "EndEvent" which in turn will
> trigger a call to the "Execute()" method of the
> Command/Observer.
> 
> For this to work correctly, make sure that the
> filters following the filter you are observing
> do not enable their ReleaseDataFlag (they usualy
> don't, unless you make this call explicitly).
> 
> ----
> 
> About filter 20 and filter 19,
> yeap, filter 20 call Update() in its input,
> which in turn call Update in their sources.
> 
> If you keep in mind that Filters are ProcessObjects
> and Images are DataObjects, the default implementation
> of the "Update()" method in ProcessObject and
> DataObject will give you a pretty good idea of
> how the update calls propagate upstream the pipeline.
> 
> void
> ProcessObject
> ::Update()
> {
>   if (this->GetOutput(0))
>     {
>     this->GetOutput(0)->Update();
>     }
> }
> 
> 
> void
> DataObject
> ::Update()
> {
>   this->UpdateOutputInformation();
>   this->PropagateRequestedRegion();
>   this->UpdateOutputData();
> }
> 
> 
> 
> ----
> 
> For processing a 100 images with a pipeline
> the decisive factor will be whether the images
> have the same dimensions (number of pixels along
> each axis).
> 
> If they do, you can save in the allocation of
> intermediate memory by creating an initial pipeline
> and reusing it 100 time, just exchanging the input
> image.
> 
> If, on the other hand, the images have different
> dimensions each time, it will be about the same
> to create the pipeline in the body of a for loop.
> 
> In general the computations involved in setting
> up a pipeline are negligeable compared to the
> computing time required for actually executing
> the pipeline.
> 
> 
> 
> 
>    Luis
> 
> 
> ------------------------
> Hans Johnson wrote:
> 
>> Luis,
>>
>> You are correct, I ment itk::Image<>::Pointer in all the examples 
>> below.    I should know better than to ask for help, and then provide 
>> poor description of what I want help on.
>>
>> I have worked through my problems up to this point, so do not feel 
>> that I need a personal response.  I just feel that these comments may 
>> help other developers, and would be a good addition to the software 
>> guide. Some of this information is in the Software guide already, but 
>> it is of such importance to using ITK that I think it needs a little 
>> more emphasis.
>>
>> I have not run into many problems when designing new applications, 
>> because the are ITK'ized from the start, and all the magic 
>> smartpointer handeling is behind the scenes appropriatly.  My problems 
>> have been in handeling the instatiation and assignement of images 
>> while trying to insert our in-house functions in the middle of an ITK 
>> image processing stream.
>>
>> I gave the example template problems that I have found to be most 
>> difficult to figure out when integrating ITK into current projects. 
>> Because most of the projects that I work on have a "c" programming 
>> mentality (pass images/objects through functions that modify the 
>> images/objects), it is often difficult to flip back to the ITK piple 
>> mentality.
>>
>> For example, how should I set up a pipeline to process 100 images? 
>> Should I set up 100 identical pipelines?  Should I instantiate a new 
>> pipeline for every image?  Is there a simple pipeline call that will 
>> release the output smart pointer, and instantiate a new output pointer 
>> for the next image?
>>
>> Finally, something that I still do not fully understand is how 
>> "messages" get propogated back through the pipeline to initiate 
>> evaluation.  When several filters are chained together, how does the 
>> message get propogated back?
>>
>> ===========================================================
>> itk::ImageFileWriter<> myfio=itk::ImageFileWriter<>::New()
>> {
>> itk::Image::Pointer IntermediateImage;
>> {
>>     itk::Image::Pointer InputImage<>;
>>     Filter1->SetInput(InputImage);
>>     Filter2->SetInput(Filter1->GetOutput());
>>     ...
>>
>>     IntermediateImage=Filter19.GetOutput();
>>     myfio->SetInput(IntermediateImage);
>>     // ERROR:  myfio->Update()
>>     Filter20->SetInput(Filter19->GetOutput());
>>     Filter20->Update();  //Initiate processing of all filters
>>     itk::Image::Pointer OutputImage<>=Filter20->GetOutput();
>>     myfio->Update(); //This is OK Write out the buffer image that was 
>> created as part of the Filter19 exection;
>>
>>     IntermediateImage=Filter20->GetOutput();//Reference count to 
>> Filter19 internal output buffer is decremented by one, and reference 
>> count to internal Filter29 output buffer is incremented by one.
>>     }//All memory and internal buffers from filters 1-19 are deleted. 
>> The interal output buffer of Filter20 will persist because it is still 
>> referenced by IntermediateImage.
>>
>>   myfio->Update(); //Write out the buffer image that was created as 
>> part of the Filter20 execution.
>>   }
>>
>> myfio->Update(); //OK
>> ===========================================================
>>
>> When does IntermediateImage become usable?
>> Does filter 20 interally make a call analogus to 
>> Filter19->GetOutput()->Update()?
>>
>> I just don't have a good understanding of how this works.
>>
>> Thanks,
>> Hans
>>
>> PS:  Thanks Luis and James for your quick and helpful responses!
>>
>> Luis Ibanez wrote:
>>
>>>
>>> Hi Hans,
>>>
>>> About your second set of examples:
>>>
>>> All of them are missing the SmartPointers.
>>>
>>> Is this intentional in the code you want to
>>> illustrate or is it just an omission ?
>>>
>>> For example, and STL list of <itk::Image<>>
>>> will not work. You could only use raw pointers
>>> to images or smart pointers to images.
>>>
>>> Default constructors have been declared protected
>>> in order to prevent images from being declared as
>>> static variables.
>>>
>>> The same goes for filters.
>>>
>>>
>>> Luis
>>>
>>>
>>> -------------------------
>>> Hans Johnson wrote:
>>>
>>>> Luis,
>>>
>>>
>>>
>>>
>>>
>>>> ...
>>>>
>>>> 2) A common pitfalls appendix with examples
>>>>      -) All of the images in MyNewImagesList will have a smart 
>>>> pointer to the same image.
>>>>
>>>>         std::list<itk::Image<>::Pointer> MyImagesList;
>>>>         std::list<itk::Image<>::Pointer> MyNewImagesList;
>>>>         itk::FilterX::Pointer X=itk::FilterX::New()
>>>>         for( currImage in MyImagesList )
>>>>         {
>>>>             X->SetInput(currImage);
>>>>             X->Update();
>>>>             MyNewImagesList.push_back(X->GetOutput);
>>>>          }
>>>>
>>>>       -) This will cause errors because MyNewImage was never allocated.
>>>>
>>>>         itk::Image<>::Pointer MyImage;
>>>>         itk::Image<>::Pointer MyNewImage;
>>>>         {
>>>>            itk::FilterX::Pointer X=itk::FilterX::New()
>>>>            X->SetInput(MyImage);
>>>>            //NOTE: No UpdateCommand
>>>>            MyNewImage=X->GetOutput();
>>>>          }
>>>>          itk::FilterY::Pointer Y=itk::FilterY::New();
>>>>          Y->SetInput(MyNewImage);
>>>>          Y->Update();
>>>>
>>>>
>>>> Both of these issues are closely related to not fully understanding 
>>>> the pipeline, and making improper assumptions about it's behavior.
>>>>
>>>> Again, Thanks for all your help.
>>>>
>>>> Hans
>>>>
>>>>
>>>>
>>>>
>>
> 
> 

-- 
===================================================================
Hans J. Johnson                              W294 GH
hans-johnson@uiowa.edu                       Dept. of Psychiatry
http://www.psychiatry.uiowa.edu/~hjohnson    The University of Iowa
(319) 353-8587                               Iowa City, IA 52242
===================================================================