[Insight-developers] Confused about persistance of filter outputs

Luis Ibanez luis.ibanez@kitware.com
Mon, 28 Apr 2003 13:24:40 -0400


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
>>>
>>>
>>>
>>>
>