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