/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkDCMTKFileReader_h #define itkDCMTKFileReader_h #include "ITKIODCMTKExport.h" #include #include #include "itkByteSwapper.h" #include "itkIntTypes.h" #include "vnl/vnl_vector.h" #include "dcmtk/dcmdata/dcdict.h" // For DcmDataDictionary #include "dcmtk/dcmdata/dcdicent.h" #include "dcmtk/dcmdata/dcxfer.h" #include "dcmtk/dcmdata/dcvrds.h" #include "dcmtk/dcmdata/dcstack.h" #include "dcmtk/dcmdata/dcdatset.h" #include "dcmtk/dcmdata/dcitem.h" #include "dcmtk/dcmdata/dcvrobow.h" #include "dcmtk/dcmdata/dcsequen.h" #include "itkMacro.h" #include "itkImageIOBase.h" class DcmSequenceOfItems; class DcmFileFormat; class DcmDictEntry; // Don't print error messages if you're not throwing // an exception // std::cerr body; #define DCMTKExceptionOrErrorReturn(body) \ { \ if (throwException) \ { \ itkGenericExceptionMacro(body); \ } \ else \ { \ return EXIT_FAILURE; \ } \ } namespace itk { // Forward reference because of circular dependencies class ITK_FORWARD_EXPORT DCMTKSequence; class ITKIODCMTK_EXPORT DCMTKItem { public: DCMTKItem() = default; void SetDcmItem(DcmItem * item); int GetElementSQ(const unsigned short group, const unsigned short entry, DCMTKSequence & sequence, const bool throwException = true) const; private: DcmItem * m_DcmItem{ nullptr }; }; class ITKIODCMTK_EXPORT DCMTKSequence { public: DCMTKSequence() = default; void SetDcmSequenceOfItems(DcmSequenceOfItems * seq); int card() const; int GetSequence(unsigned long index, DCMTKSequence & target, const bool throwException = true) const; int GetStack(const unsigned short group, const unsigned short element, DcmStack & resultStack, const bool throwException = true) const; int GetElementCS(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetElementOB(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetElementCSorOB(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; template int GetElementDSorOB(const unsigned short group, const unsigned short element, TType & target, const bool throwException = true) const { if (this->GetElementDS(group, element, 1, &target, false) == EXIT_SUCCESS) { return EXIT_SUCCESS; } std::string val; if (this->GetElementOB(group, element, val, throwException) != EXIT_SUCCESS) { DCMTKExceptionOrErrorReturn(<< "Cant find DecimalString element " << std::hex << group << ' ' << std::hex << element << std::dec); } const char * data = val.c_str(); const auto * fptr = reinterpret_cast(data); target = *fptr; return EXIT_SUCCESS; } template int GetElementDSorOB(const unsigned short group, const unsigned short element, int count, TType * target, const bool throwException = true) const { if (this->GetElementDS(group, element, count, target, false) == EXIT_SUCCESS) { return EXIT_SUCCESS; } std::string val; if (this->GetElementOB(group, element, val, throwException) != EXIT_SUCCESS) { DCMTKExceptionOrErrorReturn(<< "Cant find DecimalString element " << std::hex << group << ' ' << std::hex << element << std::dec); } const char * data = val.c_str(); const auto * fptr = reinterpret_cast(data); for (int i = 0; i < count; ++i) { target[i] = fptr[i]; } return EXIT_SUCCESS; } int GetElementFD(const unsigned short group, const unsigned short element, int count, double * target, const bool throwException = true) const; int GetElementFD(const unsigned short group, const unsigned short element, double & target, const bool throwException = true) const; int GetElementDS(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetElementTM(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; /** Get an array of data values, as contained in a DICOM * DecimalString Item */ template int GetElementDS(const unsigned short group, const unsigned short element, unsigned short count, TType * target, const bool throwException = true) const { DcmStack resultStack; if (this->GetStack(group, element, resultStack, throwException) != EXIT_SUCCESS) { return EXIT_FAILURE; } auto * dsItem = dynamic_cast(resultStack.top()); if (dsItem == nullptr) { DCMTKExceptionOrErrorReturn(<< "Can't get DecimalString Element at tag " << std::hex << group << ' ' << element << std::dec); } OFVector doubleVals; if (dsItem->getFloat64Vector(doubleVals) != EC_Normal) { DCMTKExceptionOrErrorReturn(<< "Cant extract Array from DecimalString " << std::hex << group << ' ' << std::hex << element << std::dec); } if (doubleVals.size() != count) { DCMTKExceptionOrErrorReturn(<< "DecimalString " << std::hex << group << ' ' << std::hex << element << " expected " << count << "items, but found " << doubleVals.size() << std::dec); } for (unsigned int i = 0; i < count; ++i) { target[i] = static_cast(doubleVals[i]); } return EXIT_SUCCESS; } int GetElementSQ(const unsigned short group, const unsigned short element, DCMTKSequence & target, const bool throwException = true) const; int GetElementItem(unsigned short itemIndex, DCMTKItem & target, const bool throwException = true) const; void print(std::ostream & out) const { this->m_DcmSequenceOfItems->print(out); } private: DcmSequenceOfItems * m_DcmSequenceOfItems{ nullptr }; }; class ITKIODCMTK_EXPORT DCMTKFileReader { public: using Self = DCMTKFileReader; DCMTKFileReader() = default; ~DCMTKFileReader(); void SetFileName(const std::string & fileName); const std::string & GetFileName() const; void LoadFile(); int GetElementLO(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetElementLO(const unsigned short group, const unsigned short element, std::vector & target, const bool throwException = true) const; /** Get an array of data values, as contained in a DICOM * DecimalString Item */ template int GetElementDS(const unsigned short group, const unsigned short element, unsigned short count, TType * target, const bool throwException = true) const { DcmTagKey tagkey(group, element); DcmElement * el; if (this->m_Dataset->findAndGetElement(tagkey, el) != EC_Normal) { DCMTKExceptionOrErrorReturn(<< "Cant find tag " << std::hex << group << ' ' << std::hex << element << std::dec); } auto * dsItem = dynamic_cast(el); if (dsItem == nullptr) { DCMTKExceptionOrErrorReturn(<< "Cant find DecimalString element " << std::hex << group << ' ' << std::hex << element << std::dec); } OFVector doubleVals; if (dsItem->getFloat64Vector(doubleVals) != EC_Normal) { DCMTKExceptionOrErrorReturn(<< "Cant extract Array from DecimalString " << std::hex << group << ' ' << std::hex << element << std::dec); } if (doubleVals.size() != count) { DCMTKExceptionOrErrorReturn(<< "DecimalString " << std::hex << group << ' ' << std::hex << element << " expected " << count << "items, but found " << doubleVals.size() << std::dec); } for (unsigned int i = 0; i < count; ++i) { target[i] = static_cast(doubleVals[i]); } return EXIT_SUCCESS; } template int GetElementDSorOB(const unsigned short group, const unsigned short element, TType & target, const bool throwException = true) const { if (this->GetElementDS(group, element, 1, &target, false) == EXIT_SUCCESS) { return EXIT_SUCCESS; } std::string val; if (this->GetElementOB(group, element, val, throwException) != EXIT_SUCCESS) { DCMTKExceptionOrErrorReturn(<< "Cant find DecimalString element " << std::hex << group << ' ' << std::hex << element << std::dec); } const char * data = val.c_str(); const auto * fptr = reinterpret_cast(data); target = *fptr; switch (this->GetTransferSyntax()) { case EXS_LittleEndianImplicit: case EXS_LittleEndianExplicit: itk::ByteSwapper::SwapFromSystemToLittleEndian(&target); break; case EXS_BigEndianImplicit: case EXS_BigEndianExplicit: itk::ByteSwapper::SwapFromSystemToBigEndian(&target); break; default: break; } return EXIT_SUCCESS; } /** Get a DecimalString Item as a single string */ int GetElementDS(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetElementFD(const unsigned short group, const unsigned short element, double & target, const bool throwException = true) const; int GetElementFD(const unsigned short group, const unsigned short element, int count, double * target, const bool throwException = true) const; int GetElementFL(const unsigned short group, const unsigned short element, float & target, const bool throwException = true) const; int GetElementFLorOB(const unsigned short group, const unsigned short element, float & target, const bool throwException = true) const; int GetElementUS(const unsigned short group, const unsigned short element, unsigned short & target, const bool throwException = true) const; int GetElementUS(const unsigned short group, const unsigned short element, unsigned short *& target, const bool throwException = true) const; /** Get a DecimalString Item as a single string */ int GetElementCS(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; /** Get a PersonName Item as a single string */ int GetElementPN(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; /** get an IS (Integer String Item */ int GetElementIS(const unsigned short group, const unsigned short element, itk::int32_t & target, const bool throwException = true) const; int GetElementSL(const unsigned short group, const unsigned short element, itk::int32_t & target, const bool throwException = true) const; int GetElementISorOB(const unsigned short group, const unsigned short element, itk::int32_t & target, const bool throwException = true) const; int GetElementCSorOB(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; /** get an OB OtherByte Item */ int GetElementOB(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetElementSQ(const unsigned short group, unsigned short entry, DCMTKSequence & sequence, const bool throwException = true) const; int GetElementUI(const unsigned short group, unsigned short entry, std::string & target, const bool throwException = true) const; int GetElementDA(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetElementTM(const unsigned short group, const unsigned short element, std::string & target, const bool throwException = true) const; int GetDirCosines(vnl_vector & dir1, vnl_vector & dir2, vnl_vector & dir3) const; int GetDirCosArray(double * const dircos) const; int GetFrameCount() const; int GetSlopeIntercept(double & slope, double & intercept) const; int GetDimensions(unsigned short & rows, unsigned short & columns) const; IOComponentEnum GetImageDataType() const; IOPixelEnum GetImagePixelType() const; int GetSpacing(double * const spacing) const; int GetOrigin(double * const origin) const; bool HasPixelData() const; E_TransferSyntax GetTransferSyntax() const; long GetFileNumber() const; static void AddDictEntry(DcmDictEntry * entry); static bool CanReadFile(const std::string & filename); static bool IsImageFile(const std::string & filename); private: std::string m_FileName; DcmFileFormat * m_DFile{ nullptr }; DcmDataset * m_Dataset{ nullptr }; E_TransferSyntax m_Xfer{ EXS_Unknown }; Sint32 m_FrameCount{ 0 }; long m_FileNumber{ -1L }; }; extern bool CompareDCMTKFileReaders(DCMTKFileReader * a, DCMTKFileReader * b); } // namespace itk #endif // itkDCMTKFileReader_h