With SDK versions 2.20 and 2.21 the Algorithm and Image API has undergone an extensive overhaul. Often there is a 1-to-1 mapping between the legacy API and the modern API. However, the new API provides a bunch of new interfaces for increased convenience. This page lists how common usage patters can be mapped to the new API.
Algorithm API
Summary of the changes:
- Algorithm::output(DataList&) was declared deprecated, use the new Algorithm::takeOutput() instead. The default implementation of these two member functions forward to the repsective other to maintain compatibility of existing code.
- Introduce Algorithm::FactoryInfo struct with corresponding getters/setters to store information about how an Algorithm was created by the factory. Naming is explicit to make intentions clear.
- Algorithm::input(), Algorithm::setName(), and Algorithm::name() are deprecated. Use the Algorithm::FactoryInfo struct instead. Algorithm::setInput() has been removed completely because of its highly confusing semantics.
- Removed unused
Algorithm::categories()
interface.
- The AlgorithmListener interface was deprecated and replaced by dedicated signals signalParametersChanged and signalOutputChanged. AlgorithmController now inherits from SignalReceiver and will connect to these signals on construction.
Algorithm::output()
Algorithm::output() has been superseeded with Algorithm::takeOutput() offering clearer ownership semantics:
void Algorithm::output(DataList& dataOut)
{
if (m_imgOut)
dataOut.add(m_imgOut.release());
}
{
return OwningDataList(std::move(m_imgOut));
}
virtual OwningDataList takeOutput()
Return any new Data that was created by the Algorithm during the last call to compute().
Algorithm::setInput() et al.
If you were using Algorithm::setInput()
, Algorithm::input()
, Algorithm::setName()
, or Algorithm::name()
, chances are high that those functions did not actually do what you expect since they are only about tracking information for factories/workspaces and could not be used to dynamically exchange the input data of an algorithm. In case you want to actually access the factory information, this is now available through the FactoryInfo
struct returned by Algorithm::factoryInfo().
Image API
Summary of the changes:
- Usage of the Image base class as image descriptor has been deprecated. Use the new ImageDescriptor struct instead, which avoid ambiguous function overloads taking an Image as polymorphic argument.
- Member functions of Image, SharedImage, and SharedImageSet that use owning raw pointers have been deprecated. The new overloads with std::unique_ptr or std::shared_ptr should be used instead.
- SharedImageSet has been refactored to hold its SharedImages in a std::shared_ptr. This makes the previous container mode (
own
flag) obsolete and deprecated. Copy the std::shared_ptr directly to model 'container' SharedImageSets providing a view onto images. You can access the std::shared_ptrs via SharedImageSet::getShared() and SharedImageSet::images().
- The SharedImageSetIterator has been deprecated because of confusing semantics.
- As a corollary, the interface to clone a SharedImageSet has been overhauled as well:
clone()
and nonOwningClone()
have been deprecated, use the new SharedImageSet::clone2() member function instead (clone2()
will be renamed to clone()
once the old deprecated functions have been removed completely).
- The ImageListener interface has been deprecated. There is no planned replacement. Use the shared ownership models of SharedImage/SharedImageSet if you want to implement shared ownership of image data.
Creating a new MemImage from a template image
We introduced the ImageDescriptor class enabling you to easily copy over the core aspects of an image and optionally adjusting them:
int channelsRef = imageRef->channels();
int width = imageRef->width();
int height = imageRef->height();
int slices = imageRef->slices();
TypedImage<float>* outTypedImage = new TypedImage<float>(Image::FLOAT, width, height, slices, outChannels);
outTypedImage->setSpacing(memImageRef->spacing(), memImageRef->isMetric());
ImageDescriptor imgDesc = imageRef->descriptor();
imgDesc.channels = outChannels;
static std::unique_ptr< TypedImage< T > > create(const ImageDescriptor &descriptor, T *data=nullptr, Ownership own=Ownership::NotOwning)
Factory method to create a new TypedImage from an ImageDescriptor.
Creating a new TypedImage from scratch
The legacy TypedImage constructor asked for the pixel type while at the same time ignoring it since it's already given by the template type:
auto img =
std::make_unique<TypedImage<unsigned char>>(vec3i(width, height, slices));
Creating a SharedImage from an existing Image
We fixed the very odd ownership semantics of the SharedImage constructor:
TypedImage<float>* outMem = new TypedImage<float>(...);
m_imgOut = new SharedImage(*outMem);
m_imgOut = new SharedImage(std::move(outMem));
Cloning of SharedImages
We made cloning a SharedImage and all of its properties easier and more idiomatic:
SharedImage* imgNew = new SharedImage(*m_imgIn[n]->mem(i)->clone());
imgNew->setModality(m_imgIn[n]->get(i)->modality());
imgNew->setMatrix(m_imgIn[n]->matrix(i));
m_imgOut->add(imgNew);
if (m_imgIn[n]->mask(i))
m_imgOut->setMask(m_imgIn[n]->mask(i), m_imgOut->size() - 1);
@ NoDeformation
Do not copy the attached deformation.
Definition SharedImage.h:93
Updating an Image inside a SharedImage
Updating a SharedImage with new image content has become easier and more explicit:
SharedImage* si = ...;
si->mem()->crop(...);
si->setDirtyMem();
SharedImage* si = ...;
std::unique_ptr< TypedImage< T > > createCropped(const TypedImage< T > &input, vec3i newSize, vec3i offset=vec3i(-1, -1, -1))
Create a cropped version of the input image.
Container (non-owning) SharedImageSets
The legacy SharedImageSet API offered a "container" mode where it would not own the SharedImages inside. This was error-prone, highly implicit, and algorithms could easily break stuff when they were not aware of this caveat. The new SharedImageSet API stores its SharedImages in std::shared_ptrs
so that use cases where images need to be shared across multiple SharedImageSets become very natural:
void foo(SharedImageSet* input)
{
SharedImageSet container(false);
for (int i = 0; i < input->size(); ++i)
container.add(input->get(i));
SomeFanyAlgorthm::compute(container);
}
void foo(SharedImageSet* input)
{
SharedImageSet container;
for (int i = 0; i < input->size(); ++i)
container.add(input->getShared(i));
SharedImageSet container;
container.add(input->images());
SomeFanyAlgorthm::compute(container);
}
@ ShallowImageCopy
Clone SharedImages with SharedImage::ShallowImageCopy flag (do not deep copy the underlying Images)
Definition SharedImageSet.h:50
Iterating over the images of a SharedImageSet
The SharedImageSetIterator has been deprecated because of it's unclear semantics. Iterate over SharedImageSet::images() or SharedImageSet::selectedImages() instead:
SharedImageSet* sis = ...
for (SharedImage* si : sis)
{
[...]
}
SharedImageSet* sis = ...
[...]
[...]
@ All
Consider all images being selected.
Definition Selection.h:46
Image Processing
The majority of basic image processing functions in MemImage / TypedImage has been deprecated. Use the new free functions in the ImageProcessing namespace instead. These functions will always create a new Image instance if the image descriptor changes. You can use move assignment or std::swap to mimic an in-place processing operation.
MemImage/TypedImage Processing
The image processing member functions of MemImage/TypedImage have been deprecated. Use the free functions in the ImageProcessing namespace which are more powerful, more explicit and exhibit proper memory ownership semantics:
TypedImage<float>* imageFloat = curImage->mem()->createFloat(true, false, true);
imageFloat->flip();
*curImage->mem(),
{ImageProcessing::Normalization::TypeRange, ImageProcessing::Normalization::ApplyShiftAndScale});
ImageProcessing::flip(*imageFloat);
std::unique_ptr< TypedImage< float > > createFloat(const MemImage &input, Normalization valueMapping)
Create a floating point version of the input image.