Extending the default DICOM import/export with custom functionality.
More...
Extending the default DICOM import/export with custom functionality.
Implementing a Dicom::Extension is the best way if you need to load/store additional metadata, like a custom DataComponent or change certain parameters of the image data. See Extensions for more information. When loading, the Extensions are run sequentially after the full SharedImageSet was loaded by a Dicom::IOD. In the case of saving to DICOM, the Extensions are run sequentially after the corresponding dcmtk datasets have been created by a Dicom::IOD. Each Extension is called for each frame that the Dicom::IOD generated.
Creating a Dicom::Extension
Let's look at an example which reads the tags "XRay Tube Current" and "Convolution Kernel" and stores them in a custom DataComponent of the SharedImageSet:
#include <ImFusion/Base/DataComponentList.h>
#include <ImFusion/Base/SharedImageSet.h>
#include <ImFusion/Dicom/DicomElement.h>
#include <ImFusion/Dicom/DicomIOD.h>
#include <ImFusion/Dicom/Extension.h>
#include <dcmtk/dcmdata/dctk.h>
class ExampleDataComponent :
public DataComponent<ExampleDataComponent>
{
public:
ExampleDataComponent() = default;
ExampleDataComponent(const ExampleDataComponent& other) = default;
ExampleDataComponent& operator=(const ExampleDataComponent& other) = default;
bool operator==(const ExampleDataComponent& other) const
{
return other.xrayTubeCurrent == xrayTubeCurrent && other.convolutionKernel == convolutionKernel;
}
void configure(const Properties* p) override {};
void configuration(Properties* p) const override {};
int xrayTubeCurrent = 0;
std::string convolutionKernel = "";
};
{
public:
std::string name() const override { return "Extension Example"; }
std::vector<std::unique_ptr<Data>>
load(
const Dicom::FrameDescriptor& frame, SharedImageSet& sis)
const override
{
return {};
Dicom::Element<int> xrayTubeCurrent(*frame.
iod, DCM_XRayTubeCurrent);
Dicom::Element<std::string> convolutionKernel(*frame.
iod, DCM_ConvolutionKernel);
if (xrayTubeCurrent.isValid())
dc.xrayTubeCurrent = xrayTubeCurrent.value();
if (convolutionKernel.isValid())
dc.convolutionKernel = convolutionKernel.value();
return {};
}
void save(
const Dicom::FrameDescriptor& frame,
const SharedImageSet& sis)
const override {}
};
Main specialization of DataComponentBase for regular types.
Definition DataComponent.h:109
T & getOrCreate(Args &&... ctorArgs)
Returns the DataComponent exactly matching the given type.
Definition DataComponentList.h:231
Interface for adding custom functionality to DicomLoader and DicomWriter An Extension might be called...
Definition Extension.h:26
const DataComponentList & components() const
Returns the list of DataComponents for this data.
Definition Data.h:179
Mesh * load(const char *buffer, size_t bufferLength, const std::string &sourceFilename="", Progress *progressBar=nullptr, bool log=false)
Loads a mesh from a memory buffer.
void save(const char *filename, const std::vector< mat4 > &matrices, bool rigidPars=false)
Save matrices or its rigid pose parameters to a text file.
Namespace of the ImFusion SDK.
Definition Assert.h:7
IOD * iod
Parent IOD that loaded the frame. Never nullptr and must not be re-assigned.
Definition DicomIOD.h:52
int sharedImageIndex
index of the SharedImage in the SharedImageSet
Definition DicomIOD.h:59
int sliceIndex
slice index of the frame in the volume (or 0 for 2D images)
Definition DicomIOD.h:60
In order to use the extension automatically in DicomLoader and DicomWriter, it has to be registered on the IOD_Registry:
static void registerDefaultExtension(std::unique_ptr< const Extension > extension)
Register a Extension that should be available by default.
A good place for registration is the constructor of your ImFusionPlugin.
If your extension also needs to handle enhanced DICOM features, you can structure your code like this:
#include <ImFusion/Base/SharedImageSet.h>
#include <ImFusion/Dicom/DicomElement.h>
#include <ImFusion/Dicom/DicomIOD.h>
#include <ImFusion/Dicom/Extension.h>
#include <dcmtk/dcmdata/dctk.h>
{
public:
std::string name()
const override {
return "Extension Example2"; }
std::vector<std::unique_ptr<Data>>
load(
const Dicom::FrameDescriptor& frame, SharedImageSet& sis)
const override
{
if (
auto enhanced =
dynamic_cast<Dicom::EnhancedMultiFrameImageIOD*
>(frame.
iod))
{
bool hasShared = false;
if (enhanced->sharedSequence.count() >= 1)
{
auto& shared = enhanced->sharedSequence.item(0);
Windowing windowing(shared.dataset());
if (windowing.exists())
{
windowing.load(sis);
hasShared = true;
}
}
if (!hasShared && frame.
index < enhanced->perFrameSequence.count())
{
auto& perFrame = enhanced->perFrameSequence.item(frame.
index);
Windowing windowing(perFrame.dataset());
if (windowing.exists())
windowing.load(sis);
}
}
else
{
if (windowing.exists())
windowing.load(sis);
}
return {};
}
void save(
const Dicom::FrameDescriptor& frame,
const SharedImageSet& sis)
const override {}
private:
class Windowing
{
public:
Dicom::Element<double> center;
Dicom::Element<double> width;
Windowing(DcmItem& parent)
: center(parent, DCM_WindowCenter, Dicom::ElementBase::Conditional, true)
, width(parent, DCM_WindowWidth, Dicom::ElementBase::Conditional, true)
{
}
void load(SharedImageSet& sis)
{
}
};
};
virtual bool exists() const
Returns true if the elements exists in the dataset.
DcmDataset & dataset() override
Returns the dataset represented by this IOD.
int index
Index of the frame in the iod.
Definition DicomIOD.h:53
The following example shows how the extension mechanism can be used to load and write private DICOM tags:
#include <ImFusion/Base/DataComponent.h>
#include <ImFusion/Base/SharedImageSet.h>
#include <ImFusion/Dicom/DicomElement.h>
#include <ImFusion/Dicom/DicomIOD.h>
#include <ImFusion/Dicom/Extension.h>
#include <dcmtk/dcmdata/dctk.h>
class PrivateTagsDataComponent :
public DataComponent<PrivateTagsDataComponent>
{
public:
PrivateTagsDataComponent() = default;
PrivateTagsDataComponent(const PrivateTagsDataComponent& other) = default;
PrivateTagsDataComponent& operator=(const PrivateTagsDataComponent& other) = default;
bool operator==(const PrivateTagsDataComponent& other) const { return other.someString == someString && other.someInt == someInt; }
void configure(const Properties* p) override {};
void configuration(Properties* p) const override {};
std::string someString = "";
int someInt = 0;
};
{
public:
ExtensionExample3()
{
DcmDataDictionary& dict = dcmDataDict.wrlock();
dict.addEntry(
new DcmDictEntry(someStringTag.getGTag(), someStringTag.getETag(), EVR_LO, "PrivateText", 1, 1, "private", OFTrue, creatorName.c_str()));
dict.addEntry(
new DcmDictEntry(someIntTag.getGTag(), someIntTag.getETag(), EVR_US, "PrivateInteger", 1, 1, "private", OFTrue, creatorName.c_str()));
#if OFFIS_DCMTK_VERSION_NUMBER < 364
dcmDataDict.unlock();
#else
dcmDataDict.wrunlock();
#endif
}
std::string name() const override { return "Extension Example3"; }
std::vector<std::unique_ptr<Data>>
load(
const Dicom::FrameDescriptor& frame, SharedImageSet& sis)
const override
{
return {};
Dicom::Element<std::string> creator(*frame.
iod, creatorTag);
if (creator.isValid() && creator.value() == creatorName)
{
Dicom::Element<std::string> someString(*frame.
iod, someStringTag);
Dicom::Element<int> someInt(*frame.
iod, someIntTag);
if (someString.isValid())
dc.someString = someString.value();
if (someInt.isValid())
dc.someInt = someInt.value();
}
return {};
}
void save(
const Dicom::FrameDescriptor& frame,
const SharedImageSet& sis)
const override
{
{
Dicom::Element<std::string> creator(*frame.
iod, creatorTag);
creator.setValue(creatorName);
Dicom::Element<std::string> someString(*frame.
iod, someStringTag);
someString.setValue(dc->someString);
Dicom::Element<int> someInt(*frame.
iod, someIntTag);
someInt.setValue(dc->someInt);
}
}
private:
const std::string creatorName = "ImFusion";
const DcmTag creatorTag = DcmTag(DcmTagKey(0x0029, 0x0010), EVR_LO);
const DcmTag someStringTag = DcmTag(DcmTagKey(0x0029, 0x1000), EVR_LO);
const DcmTag someIntTag = DcmTag(DcmTagKey(0x0029, 0x1010), EVR_US);
};
T * get()
Returns the DataComponent exactly matching the given type.
Definition DataComponentList.h:210
For details on private DICOM tags please consider this part of the standard and this example for dcmtk on which our example is build.