/* * * Copyright (C) 2019-2021, Open Connections GmbH * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation are maintained by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * * * Author: Michael Onken * * Purpose: Class representing a Enhanced CT object * */ #include "dcmtk/dcmect/enhanced_ct.h" #include "dcmtk/config/osconfig.h" #include "dcmtk/dcmdata/dcuid.h" #include "dcmtk/dcmect/types.h" #include "dcmtk/dcmfg/concatenationcreator.h" #include "dcmtk/dcmfg/concatenationloader.h" #include "dcmtk/dcmiod/iodutil.h" #include "dcmtk/dcmiod/modimagepixel.h" struct EctEnhancedCT::SetImagePixelModuleVisitor { SetImagePixelModuleVisitor(const Uint16 r, const Uint16 c) : rows(r) , cols(c) { } template OFCondition operator()(T& t) { if ((rows == 0) || (cols == 0)) { DCMECT_ERROR("Rows/Cols must be non-zero but are : " << rows << "/" << cols); return ECT_InvalidDimensions; } t.setRows(rows); t.setColumns(cols); t.setBitsAllocated(16); t.setBitsStored(16); t.setHighBit(15); t.setSamplesPerPixel(1); t.setPhotometricInterpretation("MONOCHROME2"); t.setPixelRepresentation(OFis_signed::value ? 1 : 0); return EC_Normal; } // Members const Uint16 rows; const Uint16 cols; }; // ---------------------------------------------------------------------------- // Class WriteVisitor: // Class for adding DICOM pixel data bulk element based on type pixel data type // ---------------------------------------------------------------------------- // Generic implementation, template specialization is done in inner // DcmElementOf class struct EctEnhancedCT::WriteVisitor { // Inner class that implements the specializations for different // pixel data types // template class DcmElementOf; // Constructor, sets parameters the visitor works on in operator() WriteVisitor(DcmItem& i, EctEnhancedCT& m) : m_Item(i) , m_CT(m) { // Nothing else to do } // OFCondition operator()(OFmonostate) { DCMECT_ERROR("Could not write pixel data: Invalid pixel data type, only 16 bit integer is handled"); return ECT_NoPixelData; } template OFCondition operator()(ImagePixel& pixel) { // Avoid compiler warning about unused parameter "pixel" (void)pixel; // Input data is checked before Uint16 rows = 0; Uint16 cols = 0; m_CT.getRows(rows); m_CT.getColumns(cols); const size_t numFrames = m_CT.m_Frames.size(); const size_t numBytesFrame = m_CT.m_Frames[0]->length; const size_t numPixelsFrame = rows * cols; // Creates the correct pixel data element, based on the image pixel module used. DcmPixelData* pixData = new DcmPixelData(DCM_PixelData); OFCondition result; if (pixData) { pixData->setVR(EVR_OW); Uint16* ptr = NULL; size_t numBytesTotal = numBytesFrame * numFrames / 2; if (numBytesTotal <= 4294967294UL) { result = pixData->createUint16Array(OFstatic_cast(Uint32, numBytesTotal), ptr); // copy all frames into CT's frame structure if (ptr) { for (size_t f = 0; f < numFrames; ++f) { memcpy(ptr, m_CT.m_Frames[f]->pixData, numBytesFrame); ptr += numPixelsFrame; } return m_Item.insert(pixData); } } else { result = FG_EC_PixelDataTooLarge; delete pixData; } } return result; } // Members, i.e. parameters to operator() DcmItem& m_Item; EctEnhancedCT& m_CT; }; // Generic implementation, template specialization is done in inner // DcmElementOf class struct EctEnhancedCT::WriteVisitorConcatenation { // Inner class that implements the writing to Concatentions via ConcatenationCreator class // Constructor, sets parameters the visitor works on in operator() WriteVisitorConcatenation(EctEnhancedCT& m, Uint8*& pixData, size_t& pixDataLength) : m_CT(m) , m_pixData(pixData) , m_pixDataLength(pixDataLength) { // Nothing else to do } // OFCondition operator()(OFmonostate) { DCMECT_ERROR("Could not write pixel data: Invalid pixel data type, only 16 bit integer is handled"); return ECT_NoPixelData; } template OFCondition operator()(ImagePixel& pixel) { // Avoid compiler warning about unused parameter "pixel" (void)pixel; // Input data is checked before Uint16 rows = 0; Uint16 cols = 0; m_CT.getRows(rows); m_CT.getColumns(cols); const size_t numFrames = m_CT.m_Frames.size(); const size_t numBytesFrame = m_CT.m_Frames[0]->length; // Creates the correct pixel data element, based on the image pixel module used. m_pixDataLength = numBytesFrame * numFrames; m_pixData = new Uint8[m_pixDataLength]; if (m_pixData) { Uint8* ptr = m_pixData; // copy all frames into CT's frame structure if (ptr) { for (size_t f = 0; f < numFrames; ++f) { memcpy(ptr, m_CT.m_Frames[f]->pixData, numBytesFrame); ptr += numBytesFrame; } return EC_Normal; } } m_pixData = OFnullptr; m_pixDataLength = 0; return EC_MemoryExhausted; } // Members, i.e. parameters to operator() EctEnhancedCT& m_CT; Uint8*& m_pixData; size_t& m_pixDataLength; }; struct EctEnhancedCT::ReadVisitor { // Inner class that implements the specializations for different // pixel data types template class DcmElementOf; ReadVisitor(DcmItem& srcItem, EctEnhancedCT& m, const OFBool readPixelData = OFTrue) : m_Item(srcItem) , m_CT(m) , m_readPixelData(readPixelData) { // Nothing to do } OFCondition operator()(OFmonostate) { return ECT_InvalidPixelData; } template OFCondition operator()(ImagePixel& pixel) { m_CT.readGeneric(m_Item); OFCondition result; Uint16 rows, cols; Uint32 numFrames; size_t numBytesFrame = 0; rows = cols = 0; m_CT.getRows(rows); m_CT.getColumns(cols); numFrames = DcmIODUtil::limitMaxFrames( m_CT.getFunctionalGroups().getNumberOfFrames(), "Functional groups implicate more than 2147483647 frames, only 2147483647 will be used"); if (!rows || !cols || !numFrames) { DCMECT_ERROR("Rows (" << rows << "), Columns (" << cols << ") and Number of Frames (" << numFrames << ") must not be 0"); return ECT_InvalidPixelInfo; } numBytesFrame = OFstatic_cast(size_t, rows) * cols * sizeof(typename ImagePixel::value_type); if (m_readPixelData) { result = readSpecific(pixel, numFrames, numBytesFrame); } return result; } template OFCondition readSpecific(IODImagePixelModule& p, const Uint32 numFrames, const size_t numBytesFrame) { // Avoid compiler warning about unused parameter (void)p; unsigned long numTotalWords = 0; const Uint16* pixData = NULL; if (m_Item.findAndGetUint16Array(DCM_PixelData, pixData, &numTotalWords).good()) { if (numTotalWords == numBytesFrame * numFrames / 2 /* we compare to num words not num bytes */) { for (Uint32 n = 0; n < numFrames; n++) { DcmIODTypes::Frame* f = new DcmIODTypes::Frame; if (f) { f->length = numBytesFrame; f->pixData = new Uint8[f->length]; memcpy(f->pixData, pixData + n * numBytesFrame / 2, numBytesFrame); m_CT.m_Frames.push_back(f); } else { return EC_MemoryExhausted; } } } else { DCMECT_ERROR("Invalid number of pixels: Expected " << numBytesFrame * numFrames / 2 << " pixels but Pixel Data has " << numTotalWords << " pixels"); return ECT_InvalidPixelInfo; } } else { DCMECT_ERROR("No Pixel Data element found"); return ECT_NoPixelData; } return EC_Normal; } // Members, i.e. parameters to operator() DcmItem& m_Item; EctEnhancedCT& m_CT; OFBool m_readPixelData; }; template EctEnhancedCT::Frames::Frames(EctEnhancedCT& ct) : m_CT(ct) { } template OFCondition EctEnhancedCT::Frames::addFrame(PixelType* data, const size_t numPixels, const OFVector& perFrameInformation) { OFCondition result; if (data && numPixels) { if (!perFrameInformation.empty()) { OFunique_ptr f(new DcmIODTypes::Frame); if (f) { f->length = numPixels * sizeof(PixelType); f->pixData = new Uint8[f->length]; memcpy(f->pixData, data, f->length); m_CT.m_Frames.push_back(f.release()); OFVector::const_iterator fg = perFrameInformation.begin(); while (result.good() && (fg != perFrameInformation.end())) { result = m_CT.m_FGInterface.addPerFrame(OFstatic_cast(Uint32, m_CT.m_Frames.size()), **fg); if (result.bad()) { DCMECT_ERROR( "Could not add functional group: " << DcmFGTypes::FGType2OFString((*fg)->getType())); break; } ++fg; } } } } // Clean up if necessary if (result.bad()) m_CT.m_FGInterface.deleteFrame(OFstatic_cast(Uint32, m_CT.m_Frames.size())); return result; } template PixelType* EctEnhancedCT::Frames::getFrame(const size_t frameNumber) { if (frameNumber < m_CT.m_Frames.size()) { return (PixelType*)(m_CT.m_Frames[frameNumber]->pixData); } return NULL; } // Helper "class" that returns Frames offering API to the pixel's frame bulk // data by offering the dedicated data type, e.g. Float32 instead of the // internally stored generic Uint8 array. // struct EctEnhancedCT::GetFramesVisitor { GetFramesVisitor(EctEnhancedCT& ct) : m_CT(ct) { } OFCondition operator()(OFmonostate) { return ECT_InvalidPixelData; } template Frames operator()(ImagePixel&) { return Frames(m_CT); } EctEnhancedCT& m_CT; }; // default constructor (protected, instance creation via create() function) template EctEnhancedCT::EctEnhancedCT(OFin_place_type_t(ImagePixel)) : IODImage(OFin_place) , m_SynchronisationModule() , m_SynchronisationModuleEnabled(OFFalse) , m_EnhancedGeneralEquipmentModule() , m_FG() , m_DimensionModule() , m_AcquisitionContextModule() , m_CommonInstanceReferenceModule() , m_Frames() , m_FGInterface() , m_ImageType(DCM_ImageType) , m_InstanceNumber(DCM_InstanceNumber) , m_MultiEnergyCTAcquisition(DCM_MultienergyCTAcquisition) , m_PixelPresentation(DCM_PixelPresentation) , m_VolumetricProperties(DCM_VolumetricProperties) , m_VolumeBasedCalculationTechnique(DCM_VolumeBasedCalculationTechnique) , m_AcquisitionNumber(DCM_AcquisitionNumber) , m_AcquisitionDateTime(DCM_AcquisitionDateTime) , m_AcquisitionDuration(DCM_AcquisitionDuration) , m_ContentQualification(DCM_ContentQualification) , m_ImageComments(DCM_ImageComments) , m_BurnedInAnnotation(DCM_BurnedInAnnotation) , m_RecognizableVisualFeatures(DCM_RecognizableVisualFeatures) , m_LossyImageCompression(DCM_LossyImageCompression) , m_LossyImageCompressionRatio(DCM_LossyImageCompressionRatio) , m_LossyImageCompressionMethod(DCM_LossyImageCompressionMethod) , m_PresentationLUTShape(DCM_PresentationLUTShape) , m_IsoCenterPosition(DCM_IsocenterPosition) { setGeneralImageModuleEnabled(OFFalse); } // ------------------ Constructors, Loading, Saving ----------------------- EctEnhancedCT::~EctEnhancedCT() { DcmIODUtil::freeContainer(m_Frames); } OFCondition EctEnhancedCT::loadFile(const OFString& filename, EctEnhancedCT*& ct) { DcmFileFormat dcmff; OFCondition cond = dcmff.loadFile(filename.c_str()); if (cond.good()) { cond = loadDataset(*(dcmff.getDataset()), ct); } return cond; } OFCondition EctEnhancedCT::loadDataset(DcmDataset& dataset, EctEnhancedCT*& ct) { OFCondition result; OFString sopClass; if (dataset.findAndGetOFStringArray(DCM_SOPClassUID, sopClass).good()) { if (sopClass == UID_EnhancedCTImageStorage) { result = decompress(dataset); if (result.good()) { DcmElement* elem = NULL; if (dataset.findAndGetElement(DCM_PixelData, elem).good()) { Uint16 pr = 0; if (dataset.findAndGetUint16(DCM_PixelRepresentation, pr).good()) { if (pr == 0) { ct = new EctEnhancedCT(OFin_place >); } else { ct = new EctEnhancedCT(OFin_place >); } } else { DCMECT_WARN("Pixel Data element found but no Pixel Presentation set, assuming 16 bit unsigned " "integer data"); ct = new EctEnhancedCT(OFin_place >); } } else { return ECT_NoPixelData; } if (ct == NULL) { return EC_MemoryExhausted; } } else { return result; } } else { DCMECT_ERROR("Invalid SOP Class: " << sopClass << ", only Enhanced CT Image Storage (1.2.840.10008.5.1.4.1.1.2.1) supported"); return ECT_InvalidSOPClass; } } result = OFvisit(ReadVisitor(dataset, *ct), ct->getImagePixel()); if (result.bad()) { delete ct; ct = NULL; } return result; } OFCondition EctEnhancedCT::loadConcatenation(ConcatenationLoader& cl, const OFString& concatenationUID, EctEnhancedCT*& ct) { DcmDataset dset; ct = NULL; OFVector frames; OFCondition result = cl.load(concatenationUID, &dset, frames); if (result.good()) { Uint16 pr = 0; if (dset.findAndGetUint16(DCM_PixelRepresentation, pr).good()) { if (pr == 0) { ct = new EctEnhancedCT(OFin_place >); } else { ct = new EctEnhancedCT(OFin_place >); } } else { DCMECT_WARN("No Pixel Presentation set, assuming 16 bit unsigned " "integer data"); ct = new EctEnhancedCT(OFin_place >); } result = OFvisit(ReadVisitor(dset, *ct, OFFalse /* do not read pixel data */), ct->getImagePixel()); if (result.good()) { ct->m_Frames = frames; } if (result.bad()) { delete ct; ct = NULL; } } return result; } OFCondition EctEnhancedCT::saveFile(const OFString& filename, const E_TransferSyntax writeXfer) { DcmFileFormat dcmff; OFCondition result; if ((result = write(*dcmff.getDataset())).good()) result = dcmff.saveFile(filename.c_str(), writeXfer); if (result.bad()) DCMECT_ERROR("Cannot save Enhanced CT object to file " << filename << ": " << result.text()); return result; } OFCondition EctEnhancedCT::writeDataset(DcmItem& dataset) { return write(dataset); } void EctEnhancedCT::setCheckFGOnWrite(const OFBool doCheck) { m_FGInterface.setCheckOnWrite(doCheck); } OFBool EctEnhancedCT::getCheckFGOnWrite() { return m_FGInterface.getCheckOnWrite(); } // ------------------ Creation ----------------------- OFCondition EctEnhancedCT::create(EctEnhancedCT*& ct, const Uint16 rows, const Uint16 columns, const OFBool signedPixelData, const EctTypes::E_ImageType1 imageType1, const OFString& imageType3, const OFString& imageType4, const OFString& instanceNumber, const EctTypes::E_ContentQualification contentQualification, const EctTypes::E_PixelPresentation pixelPresentation, const EctTypes::E_VolumetricProperties volumetricProperties, const OFString& volumeBasedCalculationTechnique, const IODEnhGeneralEquipmentModule::EquipmentInfo& equipmentInfo, const OFString& acquisitionDateTime, const Float64& acquisitionDuration) { ct = OFnullptr; OFCondition status; if (!signedPixelData) { ct = new EctEnhancedCT(OFin_place >); if (ct) status = SetImagePixelModuleVisitor(rows, columns)(*OFget >(&ct->getImagePixel())); else status = EC_MemoryExhausted; } else { ct = new EctEnhancedCT(OFin_place >); if (ct) status = SetImagePixelModuleVisitor(rows, columns)(*OFget >(&ct->getImagePixel())); else status = EC_MemoryExhausted; } if (status.bad()) return status; if (status.good()) status = ct->getSOPCommon().setSOPClassUID(UID_EnhancedCTImageStorage); if (status.good()) status = ct->setImageType(EctTypes::imageType1ToStr(imageType1) + "\\" + EctTypes::imageType2ToStr(EctTypes::E_ImageType2_Primary) + "\\" + imageType3 + "\\" + imageType4); if (status.good()) status = ct->getIODMultiFrameFGModule().setInstanceNumber(instanceNumber); if (status.good()) status = ct->setContentQualification(contentQualification); if (status.good()) status = ct->setPixelPresentation(pixelPresentation); if (status.good()) status = ct->setVolumetricProperties(volumetricProperties); if (status.good()) status = ct->setVolumeBasedCalculationTechnique(volumeBasedCalculationTechnique); if (status.good()) status = ct->setLossyImageCompression(OFFalse); if (status.good()) status = ct->getIODEnhGeneralEquipmentModule().set(equipmentInfo); if (status.good() && (!acquisitionDateTime.empty())) status = ct->setAcquisitionDateTime(acquisitionDateTime); if (status.good() && (acquisitionDuration >= 0)) status = ct->setAcquisitionDuration(acquisitionDuration); if (status.good()) status = ct->m_BurnedInAnnotation.putOFStringArray("NO"); if (status.good()) status = DcmIODUtil::setContentDateAndTimeNow(ct->getIODMultiFrameFGModule()); return status; } // ------------------ Access ----------------------- OFBool EctEnhancedCT::check(const OFBool checkFGStructure) { if (m_Frames.size() == 0) { DCMECT_ERROR("No frame data available"); return OFFalse; } if (checkFGStructure) { if (!m_FGInterface.check()) return OFFalse; } return OFTrue; } FGInterface& EctEnhancedCT::getFunctionalGroups() { return m_FGInterface; } IODMultiFrameFGModule::ConcatenationInfo& EctEnhancedCT::getConcatenationInfo() { return m_FG.getConcatenationInfo(); } size_t EctEnhancedCT::getNumberOfFrames() const { return OFconst_cast(EctEnhancedCT*, this)->m_FGInterface.getNumberOfFrames(); } // ------------------ Access ----------------------- OFCondition EctEnhancedCT::addForAllFrames(const FGBase& group) { return m_FGInterface.addShared(group); } IODMultiframeDimensionModule& EctEnhancedCT::getDimensions() { return m_DimensionModule; } EctEnhancedCT::FramesType EctEnhancedCT::getFrames() { return OFvisit(GetFramesVisitor(*this), getImagePixel()); } OFCondition EctEnhancedCT::importFromSourceImage(DcmItem& dataset, const OFBool takeOverCharset) { OFString FoR; dataset.findAndGetOFString(DCM_FrameOfReferenceUID, FoR); return IODImage::importHierarchy(dataset, OFTrue, // Patient OFTrue, // Study !FoR.empty(), // Frame of Reference OFFalse, // Series takeOverCharset); } OFCondition EctEnhancedCT::importFromSourceImage(const OFString& filename, const OFBool takeOverCharset) { DcmFileFormat dcmff; OFCondition result = dcmff.loadFile(filename); if (result.good()) { return importFromSourceImage(*(dcmff.getDataset()), takeOverCharset); } return result; } // ------------------- Module getters / setters ------------------------- IODPatientModule& EctEnhancedCT::getIODPatientModule() { return IODImage::getPatient(); } IODPatientStudyModule& EctEnhancedCT::getIODPatientStudyModule() { return IODImage::getPatientStudy(); } IODGeneralStudyModule& EctEnhancedCT::getIODGeneralStudyModule() { return IODImage::getStudy(); } IODGeneralSeriesModule& EctEnhancedCT::getIODGeneralSeriesModule() { return IODImage::getSeries(); } IODFoRModule& EctEnhancedCT::getIODFrameOfReferenceModule() { return IODImage::getFrameOfReference(); } IODSynchronizationModule& EctEnhancedCT::getIODSynchronizationModule() { return m_SynchronisationModule; } void EctEnhancedCT::setIODSynchronisationModuleEnabled(const OFBool enabled) { m_SynchronisationModuleEnabled = enabled; } OFBool EctEnhancedCT::getIODSynchronisationModuleEnabled() { return m_SynchronisationModuleEnabled; } IODGeneralEquipmentModule& EctEnhancedCT::getIODGeneralEquipmentModule() { return IODImage::getEquipment(); } IODEnhGeneralEquipmentModule& EctEnhancedCT::getIODEnhGeneralEquipmentModule() { return m_EnhancedGeneralEquipmentModule; } IODMultiFrameFGModule& EctEnhancedCT::getIODMultiFrameFGModule() { return m_FG; } IODMultiframeDimensionModule& EctEnhancedCT::getIODMultiframeDimensionModule() { return m_DimensionModule; } IODAcquisitionContextModule& EctEnhancedCT::getIODAcquisitionContextModule() { return m_AcquisitionContextModule; } IODSOPCommonModule& EctEnhancedCT::getIODSOPCommonModule() { return IODImage::getSOPCommon(); } IODCommonInstanceReferenceModule& EctEnhancedCT::getIODCommonInstanceReferenceModule() { return m_CommonInstanceReferenceModule; } // --------- Getters from Image Pixel, Enhanced CT Series, as far as applicable --------------- OFCondition EctEnhancedCT::getImageType(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_ImageType, *getData(), value, pos); } OFCondition EctEnhancedCT::getAcquisitionNumber(Sint32& value, const unsigned long pos) { return getData()->findAndGetSint32(DCM_AcquisitionNumber, value, pos); } OFCondition EctEnhancedCT::getAcquisitionNumber(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_AcquisitionNumber, *getData(), value, pos); } OFCondition EctEnhancedCT::getAcquisitionDateTime(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_AcquisitionDateTime, *getData(), value, pos); } OFCondition EctEnhancedCT::getAcquisitionDuration(Float64& value, const unsigned long pos) { return getData()->findAndGetFloat64(DCM_AcquisitionDuration, value, pos); } OFCondition EctEnhancedCT::getAcquisitionDuration(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_AcquisitionDuration, *getData(), value, pos); } OFCondition EctEnhancedCT::getContentQualification(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_ContentQualification, *getData(), value, pos); } OFCondition EctEnhancedCT::getImageComments(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_ImageComments, *getData(), value, pos); } OFCondition EctEnhancedCT::getBurnedInAnnotation(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_BurnedInAnnotation, *getData(), value, pos); } OFCondition EctEnhancedCT::getRecognizableVisualFeatures(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_RecognizableVisualFeatures, *getData(), value, pos); } OFCondition EctEnhancedCT::getLossyImageCompression(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_LossyImageCompression, *getData(), value, pos); } OFCondition EctEnhancedCT::getLossyImageCompressionRatio(Float64& value, const unsigned long pos) { return getData()->findAndGetFloat64(DCM_LossyImageCompressionRatio, value, pos); } OFCondition EctEnhancedCT::getLossyImageCompressionRatio(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_LossyImageCompressionRatio, *getData(), value, pos); } OFCondition EctEnhancedCT::getLossyImageCompressionMethod(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_LossyImageCompressionMethod, *getData(), value, pos); } OFCondition EctEnhancedCT::getPresentationLUTShape(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_PresentationLUTShape, *getData(), value, pos); } OFCondition EctEnhancedCT::getMultiEnergyCTAcquisition(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_MultienergyCTAcquisition, *getData(), value, pos); } OFCondition EctEnhancedCT::getPixelPresentation(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_PixelPresentation, *getData(), value, pos); } OFCondition EctEnhancedCT::getVolumetricProperties(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_VolumetricProperties, *getData(), value, pos); } OFCondition EctEnhancedCT::getVolumeBasedCalculationTechnique(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_VolumeBasedCalculationTechnique, *getData(), value, pos); } OFCondition EctEnhancedCT::getIsocenterPosition(OFString& value, const long pos) { return DcmIODUtil::getStringValueFromItem(DCM_IsocenterPosition, *getData(), value, pos); } OFCondition EctEnhancedCT::getIsocenterPosition(OFVector& values) { return DcmIODUtil::getFloat64ValuesFromItem(DCM_IsocenterPosition, *getData(), values); } OFCondition EctEnhancedCT::getColumns(Uint16& cols) { return getImagePixel().getColumns(cols); } OFCondition EctEnhancedCT::getRows(Uint16& rows) { return getImagePixel().getRows(rows); } // --------- Setters from Image Pixel, Enhanced CT Series, as far as applicable --------------- OFCondition EctEnhancedCT::setImageType(const OFString& value, const OFBool check) { OFCondition result = (check) ? DcmCodeString::checkStringValue(value, "4") : EC_Normal; if (result.good()) result = m_ImageType.putOFStringArray(value); return result; } OFCondition EctEnhancedCT::setAcquisitionNumber(const OFString& value, const OFBool check) { OFCondition result = (check) ? DcmIntegerString::checkStringValue(value, "1") : EC_Normal; if (result.good()) result = m_AcquisitionNumber.putOFStringArray(value); return result; } OFCondition EctEnhancedCT::setAcquisitionDateTime(const OFString& value, const OFBool check) { OFCondition result = (check) ? DcmDateTime::checkStringValue(value, "1") : EC_Normal; if (result.good()) result = m_AcquisitionDateTime.putOFStringArray(value); return result; } OFCondition EctEnhancedCT::setAcquisitionDuration(const Float64& value, const bool check) { (void)check; return m_AcquisitionDuration.putFloat64(value); } OFCondition EctEnhancedCT::setContentQualification(const EctTypes::E_ContentQualification value, const OFBool check) { (void)check; if ((value == EctTypes::E_ContentQuali_Invalid) || (value == EctTypes::E_ContentQuali_Empty)) return ECT_InvalidAttributeValue; return m_ContentQualification.putOFStringArray(EctTypes::contentQualiToStr(value)); } OFCondition EctEnhancedCT::setImageComments(const OFString& value, const OFBool check) { OFCondition result = (check) ? DcmLongText::checkStringValue(value, "1") : EC_Normal; if (result.good()) result = m_ImageComments.putOFStringArray(value); return result; } OFCondition EctEnhancedCT::setRecognizableVisualFeatures(const EctTypes::E_RecognizableVisualFeatures value, const OFBool check) { (void)check; if ((value == EctTypes::E_RecoVisFeatures_Invalid) || (value == EctTypes::E_RecoVisFeatures_Empty)) return ECT_InvalidAttributeValue; return m_RecognizableVisualFeatures.putOFStringArray(EctTypes::recoVisFeaturesToStr(value)); } OFCondition EctEnhancedCT::setLossyImageCompression(const OFBool isLossy, const OFString& ratios, const OFString& methods, const OFBool check) { if (!isLossy) { return m_LossyImageCompression.putOFStringArray("00"); } OFCondition result = m_LossyImageCompression.putOFStringArray("01"); if (result.good()) result = (check) ? DcmDecimalString::checkStringValue(ratios, "1-n") : EC_Normal; if (result.good()) result = m_LossyImageCompression.putOFStringArray(ratios); if (result.good()) result = (check) ? DcmCodeString::checkStringValue(methods, "1-n") : EC_Normal; if (result.good()) result = m_LossyImageCompressionMethod.putOFStringArray(methods); return result; } OFCondition EctEnhancedCT::setISOCenterPosition(const OFVector& values, const OFBool check) { return DcmIODUtil::setFloat64ValuesOnElement(m_IsoCenterPosition, values, "3", check); } OFCondition EctEnhancedCT::setISOCenterPosition(const OFString& value, const OFBool check) { OFCondition result = (check) ? DcmDecimalString::checkStringValue(value, "3") : EC_Normal; if (result.good()) result = m_IsoCenterPosition.putOFStringArray(value); return result; } OFCondition EctEnhancedCT::setMultiEnergyCTAcquisition(const OFString& value, const OFBool check) { OFCondition result = (check) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal; if (result.good()) result = m_MultiEnergyCTAcquisition.putOFStringArray(value); return result; } OFCondition EctEnhancedCT::setPixelPresentation(const EctTypes::E_PixelPresentation value, const OFBool check) { (void)check; if ((value == EctTypes::E_PixelPres_Empty) || (value == EctTypes::E_PixelPres_Invalid)) return ECT_InvalidAttributeValue; return m_PixelPresentation.putOFStringArray(EctTypes::pixelPresToStr(value)); } OFCondition EctEnhancedCT::setVolumetricProperties(const EctTypes::E_VolumetricProperties value, const OFBool check) { (void)check; if ((value == EctTypes::E_VolProps_Empty) || (value == EctTypes::E_VolProps_Invalid)) return ECT_InvalidAttributeValue; return m_VolumetricProperties.putOFStringArray(EctTypes::volPropsToStr(value)); } OFCondition EctEnhancedCT::setVolumeBasedCalculationTechnique(const OFString& value, const OFBool check) { OFCondition result = (check) ? DcmCodeString::checkStringValue(value, "1") : EC_Normal; if (result.good()) result = m_VolumeBasedCalculationTechnique.putOFStringArray(value); return result; } // -------------------- Protected Helpers -------------------------- OFCondition EctEnhancedCT::read(DcmItem& dataset) { OFCondition result = OFvisit(ReadVisitor(dataset, *this), getImagePixel()); return result; } OFCondition EctEnhancedCT::write(DcmItem& dataset) { if (!check()) { return IOD_EC_InvalidObject; } OFCondition result = OFvisit(WriteVisitor(dataset, *this), getImagePixel()); if (result.good()) result = writeGeneric(dataset); return result; } OFCondition EctEnhancedCT::writeConcatenation(ConcatenationCreator& cc) { if (!check()) { return IOD_EC_InvalidObject; } DcmItem* item = new DcmItem(); if (!item) return EC_MemoryExhausted; Uint8* pixData = NULL; size_t pixDataLength = 0; OFCondition result = OFvisit(WriteVisitorConcatenation(*this, pixData, pixDataLength), getImagePixel()); if (result.good()) result = writeGeneric(*item); if (result.good()) { result = cc.setCfgInput(item, pixData, pixDataLength, OFTrue /* cc should take ownership */); } return result; } OFCondition EctEnhancedCT::writeGeneric(DcmItem& dataset) { OFCondition result; if (m_SynchronisationModuleEnabled) result = m_SynchronisationModule.write(dataset); if (result.good()) result = m_FG.setNumberOfFrames( DcmIODUtil::limitMaxFrames(m_Frames.size(), "Maximum number of frames exceeded, will write 2147483647")); if (result.good()) result = m_FG.write(dataset); if (result.good()) result = m_FGInterface.write(dataset); if (result.good()) result = m_DimensionModule.write(dataset); if (result.good()) result = m_AcquisitionContextModule.write(dataset); if (result.good()) result = m_CommonInstanceReferenceModule.write(dataset); if (result.good()) result = getIODGeneralSeriesModule().setModality("CT"); if (result.good()) result = IODImage::write(dataset); // Write after General Equipment Module from IODImage if (result.good()) result = m_EnhancedGeneralEquipmentModule.write(dataset); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_ImageType, "4" /* vm */, "1" /*requirement type*/, "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_MultiEnergyCTAcquisition, "1", "3", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset(result, dataset, m_PixelPresentation, "1", "1", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_VolumetricProperties, "1", "1", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_VolumeBasedCalculationTechnique, "1", "1", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset(result, dataset, m_AcquisitionNumber, "1", "3", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_AcquisitionDateTime, "1", "1C", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_AcquisitionDuration, "1", "1C", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_ContentQualification, "1", "1C", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset(result, dataset, m_ImageComments, "1", "3", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_BurnedInAnnotation, "1", "1C", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_RecognizableVisualFeatures, "1", "3", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_LossyImageCompression, "1", "1", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_LossyImageCompressionMethod, "1-n", "1C", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_LossyImageCompressionRatio, "1-n", "1C", "EnhancedCTImageModule"); m_PresentationLUTShape.putOFStringArray("IDENTITY"); if (result.good()) result = DcmIODUtil::copyElementToDataset( result, dataset, m_PresentationLUTShape, "1", "1", "EnhancedCTImageModule"); if (result.good()) result = DcmIODUtil::copyElementToDataset(result, dataset, m_IsoCenterPosition, "3", "3", "EnhancedCTImageModule"); return result; } OFCondition EctEnhancedCT::readGeneric(DcmItem& dataset) { OFString sopClass; if (DcmIODUtil::checkSOPClass(&dataset, UID_EnhancedCTImageStorage, sopClass).bad()) { DCMECT_ERROR("Given file does not seem to be a Enhanced CT storage object since SOP class is: " << sopClass); return IOD_EC_WrongSOPClass; } IODImage::read(dataset); m_SynchronisationModule.read(dataset); m_EnhancedGeneralEquipmentModule.read(dataset); m_FG.read(dataset); m_DimensionModule.read(dataset); m_AcquisitionContextModule.read(dataset); m_CommonInstanceReferenceModule.read(dataset); m_FGInterface.read(dataset); // Enhanced CT Image Module DcmIODUtil::getAndCheckElementFromDataset(dataset, m_ImageType, "4", "1C", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_MultiEnergyCTAcquisition, "1", "3", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_PixelPresentation, "1", "1", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_VolumetricProperties, "1", "1", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset( dataset, m_VolumeBasedCalculationTechnique, "1", "1", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_AcquisitionNumber, "1", "3", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_AcquisitionDateTime, "1", "1C", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_AcquisitionDuration, "1", "1C", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_ContentQualification, "1", "1C", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_BurnedInAnnotation, "1", "1C", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_RecognizableVisualFeatures, "1", "3", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_LossyImageCompression, "1", "1", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset( dataset, m_LossyImageCompressionMethod, "1-n", "1C", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset( dataset, m_LossyImageCompressionRatio, "1-n", "1C", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_PresentationLUTShape, "1", "1", "EnhancedCTImageModule"); DcmIODUtil::getAndCheckElementFromDataset(dataset, m_IsoCenterPosition, "3", "3", "EnhancedCTImageModule"); return EC_Normal; } OFCondition EctEnhancedCT::decompress(DcmDataset& dset) { DcmXfer xfer = dset.getOriginalXfer(); OFCondition result; // If the original transfer is encapsulated and we do not already have an uncompressed version, decompress or reject // the file if (xfer.isEncapsulated()) { DCMECT_DEBUG("Enhanced CT object is compressed, converting to uncompressed transfer syntax first"); result = DcmIODUtil::decompress(dset); } return result; } // Instantiate Frames template with both possible pixel data types template class EctEnhancedCT::Frames; template class EctEnhancedCT::Frames;