How to pass output memory to a filter (avoid a copy to external memory)?

We use ITK together with a lot of older code that has its own memory management. It was quite easy to wrap our memory containers into an itk::Image using itk::ImportImageFilter. It was also very straightforward to apply filters and other processing to the wrapped memory. Awesome!

Now I fail to see if we can do the same for the output (result) image. Instead of getting a new memory block from GetOutput(), we would prefer to pass a wrapped itk::Image (i.e. again from itk::ImportImageFilter) to the last step of the pipeline.

I could not see something like SetOutput(). Does that exist, and if not, are there hints how to implement it?

Note that our goal is to avoid the second memory copy, mostly to save RAM, but also for performance reasons. So I’m not looking for a convenient way to copy the memory. Rather, I want to avoid the second copy alltogether, and have the last filter step write directly to our memory.

Anyone? Is this certainly not possible?

Is the GraftOutput() method suitable to pass a correctly sized output image to an itk::ImageSource? I want to use an itk::Image that has been created with itk::ImportImageFilter.

It seems this works, but I’m not really sure what it will do internally…

Yes, using GraftOutput() should do what you want. It discards the automatically created output image in favor of the one you provide. And if you put there an image obtained via ImportImageFilter, the pixel buffer can go into arbitrary memory location.

1 Like

Thanks so much @dzenanz , this is very relevant for us!

To better understand the details: When you say “It discards the automatically created output image”, does that mean that an output image may have been automatically created? Or when I set a valid size output image with GraftOutput(), the automatic creation will not even take place?

Basically I would like to avoid any new/delete, because our memory is quite large!

automatically created output image has metadata, but no allocated pixel buffer until filter’s Update() method is called. I believe that no big automatic allocations will occur if you GraftOutput() before Update().

Awesome, thanks a lot! This is exactly what I was hoping for!

I would say that the output grafting may work.This has not been a tested or supported feature. It will likely work with many “basic” filters, but filters which are implemented as composite pipelines, may do some internal grafting that may not preserve the output buffer. Also running a filter with the “InPlace” option enabled will not work with the usage either.

2 Likes

I would say that the output grafting may work.This has not been a tested or supported feature. It will likely work with many “basic” filters, but filters which are implemented as composite pipelines, may do some internal grafting that may not preserve the output buffer.

Ok, this is very good to know! Its not a big impediment for us to test this on a filter-by-filter basis, and its great that at least a number of filters should support grafting!

But can we isolate the expected behavior a bit better?

  • If grafting works, and is set up correctly for a specific filter, is it likely that this same filter may reject the grafted output later with different inputs? I do not mean ill-configured grafted outputs (like wrong size or type), only correctly configured grafted outputs.
  • If the filter decides to reject the grafted output, how can we know? Would the GetBufferPointer() change on the Image we pass as grafted output? Or do we need to call GetOutput() on the filter, and compare the GetBufferPointer() of that to the GetBufferPointer() of the Image we passed as grafted output?

Grafting working or not has to do with type of filter, not its inputs. Different input should not affect that.

If it doesn’t work you will get an exception of some sort, hopefully with a useful error message.

Ok that sounds very good then! We will typically test this in a demo environment on a filter-by-filter basis. If that works, I understand that we can be positive that it will usually work in the general case. Thanks a lot!