diff --git a/Modules/DICOMReader/include/mitkDICOMGDCMImageFrameInfo.h b/Modules/DICOMReader/include/mitkDICOMGDCMImageFrameInfo.h index cf0c4d30c3..852113f2a6 100644 --- a/Modules/DICOMReader/include/mitkDICOMGDCMImageFrameInfo.h +++ b/Modules/DICOMReader/include/mitkDICOMGDCMImageFrameInfo.h @@ -1,64 +1,64 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDICOMGDCMImageFrameInfo_h #define mitkDICOMGDCMImageFrameInfo_h #include "mitkDICOMDatasetAccessingImageFrameInfo.h" #include "gdcmScanner.h" namespace mitk { /** \ingroup DICOMReaderModule \brief The dataset access implementation for DICOMITKSeriesGDCMReader, based on GDCM. This class combines a DICOMImageFrameInfo object with the scanning results from gdcm::Scanner. The scanning results will be used to implement the tag access methods of DICOMDatasetAccess. */ class MITKDICOMREADER_EXPORT DICOMGDCMImageFrameInfo : public DICOMDatasetAccessingImageFrameInfo { public: mitkClassMacro(DICOMGDCMImageFrameInfo, DICOMDatasetAccessingImageFrameInfo); itkFactorylessNewMacro( DICOMGDCMImageFrameInfo ); mitkNewMacro1Param( DICOMGDCMImageFrameInfo, const std::string&); mitkNewMacro2Param( DICOMGDCMImageFrameInfo, const std::string&, unsigned int ); mitkNewMacro1Param( DICOMGDCMImageFrameInfo, const DICOMImageFrameInfo::Pointer& ); mitkNewMacro2Param( DICOMGDCMImageFrameInfo, const DICOMImageFrameInfo::Pointer&, gdcm::Scanner::TagToValue const&); virtual ~DICOMGDCMImageFrameInfo(); virtual DICOMDatasetFinding GetTagValueAsString(const DICOMTag&) const override; virtual FindingsListType GetTagValueAsString(const DICOMTagPath& path) const override; std::string GetFilenameIfAvailable() const override; protected: - DICOMGDCMImageFrameInfo(const DICOMImageFrameInfo::Pointer& frameinfo); + explicit DICOMGDCMImageFrameInfo(const DICOMImageFrameInfo::Pointer& frameinfo); DICOMGDCMImageFrameInfo(const DICOMImageFrameInfo::Pointer& frameinfo, gdcm::Scanner::TagToValue const& tagToValueMapping); DICOMGDCMImageFrameInfo(const std::string& filename = "", unsigned int frameNo = 0); const gdcm::Scanner::TagToValue m_TagForValue; }; typedef std::vector DICOMGDCMImageFrameList; } #endif diff --git a/Modules/DICOMReader/include/mitkDICOMGenericImageFrameInfo.h b/Modules/DICOMReader/include/mitkDICOMGenericImageFrameInfo.h index 2525ed4398..a53a343a8d 100644 --- a/Modules/DICOMReader/include/mitkDICOMGenericImageFrameInfo.h +++ b/Modules/DICOMReader/include/mitkDICOMGenericImageFrameInfo.h @@ -1,69 +1,69 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDICOMGenericImageFrameInfo_h #define mitkDICOMGenericImageFrameInfo_h #include "mitkDICOMDatasetAccessingImageFrameInfo.h" #include namespace mitk { /** \ingroup DICOMReaderModule \brief A generic storage class for image frame info with data access. */ class MITKDICOMREADER_EXPORT DICOMGenericImageFrameInfo : public DICOMDatasetAccessingImageFrameInfo { public: mitkClassMacro(DICOMGenericImageFrameInfo, DICOMDatasetAccessingImageFrameInfo); itkFactorylessNewMacro( DICOMGenericImageFrameInfo ); mitkNewMacro1Param( DICOMGenericImageFrameInfo, const std::string&); mitkNewMacro2Param( DICOMGenericImageFrameInfo, const std::string&, unsigned int ); mitkNewMacro1Param( DICOMGenericImageFrameInfo, const DICOMImageFrameInfo::Pointer& ); virtual ~DICOMGenericImageFrameInfo(); virtual DICOMDatasetFinding GetTagValueAsString(const DICOMTag&) const override; virtual FindingsListType GetTagValueAsString(const DICOMTagPath& path) const override; std::string GetFilenameIfAvailable() const override; /** Sets the value for a passed tag path. If the tag path is already set, it will be overwritten with the new value. - @pre Path must be explicit. No wildcards are allowd. + @pre Path must be explicit. No wildcards are allowed. @post The passed value is set for the passed path. */ void SetTagValue(const DICOMTagPath& path, const std::string& value); protected: typedef std::map ValueMapType; ValueMapType m_Values; - DICOMGenericImageFrameInfo(const DICOMImageFrameInfo::Pointer& frameinfo); + explicit DICOMGenericImageFrameInfo(const DICOMImageFrameInfo::Pointer& frameinfo); DICOMGenericImageFrameInfo(const std::string& filename = "", unsigned int frameNo = 0); private: Self& operator = (const Self&); DICOMGenericImageFrameInfo(const Self&); }; } #endif diff --git a/Modules/DICOMReader/include/mitkDICOMSortCriterion.h b/Modules/DICOMReader/include/mitkDICOMSortCriterion.h index 7ee590beb3..99cfd54903 100644 --- a/Modules/DICOMReader/include/mitkDICOMSortCriterion.h +++ b/Modules/DICOMReader/include/mitkDICOMSortCriterion.h @@ -1,82 +1,82 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDICOMSortCriterion_h #define mitkDICOMSortCriterion_h #include "itkObjectFactory.h" #include "mitkCommon.h" #include "mitkDICOMDatasetAccess.h" namespace mitk { /** \ingroup DICOMReaderModule \brief A tag based sorting criterion for use in DICOMTagBasedSorter. This class is used within std::sort (see DICOMTagBasedSorter::Sort()) and has to answer a simple question by implementing IsLeftBeforeRight(). Each time IsLeftBeforeRight() is called, the method should return whether the left dataset should be sorted before the right dataset. Because there are identical tags values quite oftenly, a DICOMSortCriterion will always hold a secondary DICOMSortCriterion. In cases of equal tag values, the decision is refered to the secondary criterion. */ class MITKDICOMREADER_EXPORT DICOMSortCriterion : public itk::LightObject { public: mitkClassMacroItkParent( DICOMSortCriterion, itk::LightObject ); /// \brief Tags used for comparison (includes seconary criteria). DICOMTagList GetAllTagsOfInterest() const; /// \brief Tags used for comparison. virtual DICOMTagList GetTagsOfInterest() const = 0; /// \brief Answer the sorting question. virtual bool IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const = 0; /// \brief Calculate a distance between two datasets. /// This ansers the question of consecutive datasets. virtual double NumericDistance(const mitk::DICOMDatasetAccess* from, const mitk::DICOMDatasetAccess* to) const = 0; /// \brief The fallback criterion. DICOMSortCriterion::ConstPointer GetSecondaryCriterion() const; /// brief describe this class in given stream. virtual void Print(std::ostream& os) const = 0; virtual bool operator==(const DICOMSortCriterion& other) const = 0; protected: DICOMSortCriterion( DICOMSortCriterion::Pointer secondaryCriterion ); virtual ~DICOMSortCriterion(); bool NextLevelIsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const; - DICOMSortCriterion(const DICOMSortCriterion& other); + explicit DICOMSortCriterion(const DICOMSortCriterion& other); DICOMSortCriterion& operator=(const DICOMSortCriterion& other); DICOMSortCriterion::Pointer m_SecondaryCriterion; }; } #endif diff --git a/Modules/DICOMReader/include/mitkGantryTiltInformation.h b/Modules/DICOMReader/include/mitkGantryTiltInformation.h index 74bdf416f6..578c82044d 100644 --- a/Modules/DICOMReader/include/mitkGantryTiltInformation.h +++ b/Modules/DICOMReader/include/mitkGantryTiltInformation.h @@ -1,147 +1,147 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkGantryTiltInformation_h #define mitkGantryTiltInformation_h #include "mitkPoint.h" #include "mitkVector.h" #include "mitkPoint.h" namespace mitk { /** \ingroup DICOMReaderModule \brief Gantry tilt analysis result. Takes geometry information for two slices of a DICOM series and calculates whether these fit into an orthogonal block or not. If NOT, they can either be the result of an acquisition with gantry tilt OR completly broken by some shearing transformation. Most calculations are done in the constructor, results can then be read via the remaining methods. This class is a helper to DICOMITKSeriesGDCMReader and can not be used outside of \ref DICOMReaderModule */ class GantryTiltInformation { public: // two types to avoid any rounding errors typedef itk::Point Point3Dd; typedef itk::Vector Vector3Dd; /** \brief Just so we can create empty instances for assigning results later. */ GantryTiltInformation(); void Print(std::ostream& os) const; /** \brief THE constructor, which does all the calculations. Determining the amount of tilt is done by checking the distances of origin1 from planes through origin2. Two planes are considered: - normal vector along normal of slices (right x up): gives the slice distance - normal vector along orientation vector "up": gives the shift parallel to the plane orientation The tilt angle can then be calculated from these distances \param origin1 origin of the first slice \param origin2 origin of the second slice \param right right/up describe the orientatation of borth slices \param up right/up describe the orientatation of borth slices \param numberOfSlicesApart how many slices are the given origins apart (1 for neighboring slices) */ GantryTiltInformation( const Point3D& origin1, const Point3D& origin2, const Vector3D& right, const Vector3D& up, unsigned int numberOfSlicesApart); /** \brief Factory method to create GantryTiltInformation from tag values (strings). Parameters as the regular c'tor. */ static GantryTiltInformation MakeFromTagValues( const std::string& origin1String, const std::string& origin2String, - const std::string orientationString, + const std::string& orientationString, unsigned int numberOfSlicesApart); /** \brief Whether the slices were sheared. True if any of the shifts along right or up vector are non-zero. */ bool IsSheared() const; /** \brief Whether the shearing is a gantry tilt or more complicated. Gantry tilt will only produce shifts in ONE orientation, not in both. Since the correction code currently only coveres one tilt direction AND we don't know of medical images with two tilt directions, the loading code wants to check if our assumptions are true. */ bool IsRegularGantryTilt() const; /** \brief The offset distance in Y direction for each slice in mm (describes the tilt result). */ double GetMatrixCoefficientForCorrectionInWorldCoordinates() const; /** \brief The z / inter-slice spacing. Needed to correct ImageSeriesReader's result. */ double GetRealZSpacing() const; /** \brief The shift between first and last slice in mm. Needed to resize an orthogonal image volume. */ double GetTiltCorrectedAdditionalSize(unsigned int imageSizeZ) const; /** \brief Calculated tilt angle in degrees. */ double GetTiltAngleInDegrees() const; private: /** \brief Projection of point p onto line through lineOrigin in direction of lineDirection. */ Point3D projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection ); double m_ShiftUp; double m_ShiftRight; double m_ShiftNormal; double m_ITKAssumedSliceSpacing; unsigned int m_NumberOfSlicesApart; }; } // namespace #endif diff --git a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp index 4fb30c696e..26e376305c 100644 --- a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp +++ b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp @@ -1,858 +1,857 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMImageBlockDescriptor.h" #include "mitkStringProperty.h" #include "mitkLevelWindowProperty.h" #include mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor() : m_ReaderImplementationLevel( SOPClassUnknown ) , m_PropertyList( PropertyList::New() ) , m_TagCache( nullptr ) , m_PropertiesOutOfDate( true ) { m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } mitk::DICOMImageBlockDescriptor::~DICOMImageBlockDescriptor() { } mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor( const DICOMImageBlockDescriptor& other ) : m_ImageFrameList( other.m_ImageFrameList ) , m_MitkImage( other.m_MitkImage ) , m_SliceIsLoaded( other.m_SliceIsLoaded ) , m_ReaderImplementationLevel( other.m_ReaderImplementationLevel ) , m_TiltInformation( other.m_TiltInformation ) , m_PropertyList( other.m_PropertyList->Clone() ) , m_TagCache( other.m_TagCache ) , m_PropertiesOutOfDate( other.m_PropertiesOutOfDate ) , m_AdditionalTagMap(other.m_AdditionalTagMap) , m_FoundAdditionalTags(other.m_FoundAdditionalTags) , m_PropertyFunctor(other.m_PropertyFunctor) { if ( m_MitkImage ) { m_MitkImage = m_MitkImage->Clone(); } m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } mitk::DICOMImageBlockDescriptor& mitk::DICOMImageBlockDescriptor:: operator=( const DICOMImageBlockDescriptor& other ) { if ( this != &other ) { m_ImageFrameList = other.m_ImageFrameList; m_MitkImage = other.m_MitkImage; m_SliceIsLoaded = other.m_SliceIsLoaded; m_ReaderImplementationLevel = other.m_ReaderImplementationLevel; m_TiltInformation = other.m_TiltInformation; m_AdditionalTagMap = other.m_AdditionalTagMap; m_FoundAdditionalTags = other.m_FoundAdditionalTags; m_PropertyFunctor = other.m_PropertyFunctor; if ( other.m_PropertyList ) { m_PropertyList = other.m_PropertyList->Clone(); } if ( other.m_MitkImage ) { m_MitkImage = other.m_MitkImage->Clone(); } m_TagCache = other.m_TagCache; m_PropertiesOutOfDate = other.m_PropertiesOutOfDate; } return *this; } mitk::DICOMTagList mitk::DICOMImageBlockDescriptor::GetTagsOfInterest() { DICOMTagList completeList; completeList.push_back( DICOMTag( 0x0018, 0x1164 ) ); // pixel spacing completeList.push_back( DICOMTag( 0x0028, 0x0030 ) ); // imager pixel spacing completeList.push_back( DICOMTag( 0x0008, 0x0018 ) ); // sop instance UID completeList.push_back( DICOMTag( 0x0008, 0x0016 ) ); // sop class UID completeList.push_back( DICOMTag( 0x0020, 0x0011 ) ); // series number completeList.push_back( DICOMTag( 0x0008, 0x1030 ) ); // study description completeList.push_back( DICOMTag( 0x0008, 0x103e ) ); // series description completeList.push_back( DICOMTag( 0x0008, 0x0060 ) ); // modality completeList.push_back( DICOMTag( 0x0018, 0x0024 ) ); // sequence name completeList.push_back( DICOMTag( 0x0020, 0x0037 ) ); // image orientation completeList.push_back( DICOMTag( 0x0020, 0x1041 ) ); // slice location completeList.push_back( DICOMTag( 0x0020, 0x0012 ) ); // acquisition number completeList.push_back( DICOMTag( 0x0020, 0x0013 ) ); // instance number completeList.push_back( DICOMTag( 0x0020, 0x0032 ) ); // image position patient completeList.push_back( DICOMTag( 0x0028, 0x1050 ) ); // window center completeList.push_back( DICOMTag( 0x0028, 0x1051 ) ); // window width completeList.push_back( DICOMTag( 0x0008, 0x0008 ) ); // image type completeList.push_back( DICOMTag( 0x0028, 0x0004 ) ); // photometric interpretation return completeList; } void mitk::DICOMImageBlockDescriptor::SetAdditionalTagsOfInterest( const AdditionalTagsMapType& tagMap) { m_AdditionalTagMap = tagMap; } void mitk::DICOMImageBlockDescriptor::SetTiltInformation( const GantryTiltInformation& info ) { m_TiltInformation = info; } const mitk::GantryTiltInformation mitk::DICOMImageBlockDescriptor::GetTiltInformation() const { return m_TiltInformation; } void mitk::DICOMImageBlockDescriptor::SetImageFrameList( const DICOMImageFrameList& framelist ) { m_ImageFrameList = framelist; m_SliceIsLoaded.resize( framelist.size() ); m_SliceIsLoaded.assign( framelist.size(), false ); m_PropertiesOutOfDate = true; } const mitk::DICOMImageFrameList& mitk::DICOMImageBlockDescriptor::GetImageFrameList() const { return m_ImageFrameList; } void mitk::DICOMImageBlockDescriptor::SetMitkImage( Image::Pointer image ) { if ( m_MitkImage != image ) { if ( m_TagCache.IsNull() ) { MITK_ERROR << "Unable to describe MITK image with properties without a tag-cache object!"; m_MitkImage = nullptr; return; } if ( m_ImageFrameList.empty() ) { MITK_ERROR << "Unable to describe MITK image with properties without a frame list!"; m_MitkImage = nullptr; return; } // Should verify that the image matches m_ImageFrameList and m_TagCache // however, this is hard to do without re-analyzing all // TODO we should at least make sure that the number of frames is identical (plus rows/columns, // orientation) // without gantry tilt correction, we can also check image origin m_MitkImage = this->DescribeImageWithProperties( this->FixupSpacing( image ) ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::GetMitkImage() const { return m_MitkImage; } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::FixupSpacing( Image* mitkImage ) { if ( mitkImage ) { Vector3D imageSpacing = mitkImage->GetGeometry()->GetSpacing(); ScalarType desiredSpacingX = imageSpacing[0]; ScalarType desiredSpacingY = imageSpacing[1]; this->GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); // prefer pixel spacing over imager pixel spacing if ( desiredSpacingX <= 0 || desiredSpacingY <= 0 ) { return mitkImage; } MITK_DEBUG << "Loaded image with spacing " << imageSpacing[0] << ", " << imageSpacing[1]; MITK_DEBUG << "Found correct spacing info " << desiredSpacingX << ", " << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; mitkImage->GetGeometry()->SetSpacing( imageSpacing ); } return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetSliceIsLoaded( unsigned int index, bool isLoaded ) { if ( index < m_SliceIsLoaded.size() ) { m_SliceIsLoaded[index] = isLoaded; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::IsSliceLoaded( unsigned int index ) const { if ( index < m_SliceIsLoaded.size() ) { return m_SliceIsLoaded[index]; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::AllSlicesAreLoaded() const { bool allLoaded = true; for ( auto iter = m_SliceIsLoaded.cbegin(); iter != m_SliceIsLoaded.cend(); ++iter ) { allLoaded &= *iter; } return allLoaded; } /* PS defined IPS defined PS==IPS 0 0 --> UNKNOWN spacing, loader will invent 0 1 --> spacing as at detector surface 1 0 --> spacing as in patient 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector */ mitk::PixelSpacingInterpretation mitk::DICOMImageBlockDescriptor::GetPixelSpacingInterpretation() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetPixelSpacingInterpretation. Need to have initialized tag-cache!"; return SpacingUnknown; } const std::string pixelSpacing = this->GetPixelSpacing(); const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); if ( pixelSpacing.empty() ) { if ( imagerPixelSpacing.empty() ) { return SpacingUnknown; } else { return SpacingAtDetector; } } else // Pixel Spacing defined { if ( imagerPixelSpacing.empty() ) { return SpacingInPatient; } else if ( pixelSpacing != imagerPixelSpacing ) { return SpacingInPatient; } else { return SpacingAtDetector; } } } std::string mitk::DICOMImageBlockDescriptor::GetPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagPixelSpacing( 0x0028, 0x0030 ); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value; } std::string mitk::DICOMImageBlockDescriptor::GetImagerPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetImagerPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagImagerPixelSpacing( 0x0018, 0x1164 ); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ).value; } void mitk::DICOMImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY ) const { const std::string pixelSpacing = this->GetPixelSpacing(); // preference for "in patient" pixel spacing if ( !DICOMStringToSpacing( pixelSpacing, spacingX, spacingY ) ) { const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); // fallback to "on detector" spacing if ( !DICOMStringToSpacing( imagerPixelSpacing, spacingX, spacingY ) ) { // last resort: invent something spacingX = spacingY = 1.0; } } } void mitk::DICOMImageBlockDescriptor::SetProperty( const std::string& key, BaseProperty* value ) { m_PropertyList->SetProperty( key, value ); } mitk::BaseProperty* mitk::DICOMImageBlockDescriptor::GetProperty( const std::string& key ) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetProperty( key ); } std::string mitk::DICOMImageBlockDescriptor::GetPropertyAsString( const std::string& key ) const { this->UpdateImageDescribingProperties(); const mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty( key ); if ( property.IsNotNull() ) { return property->GetValueAsString(); } else { return std::string( "" ); } } void mitk::DICOMImageBlockDescriptor::SetFlag( const std::string& key, bool value ) { m_PropertyList->ReplaceProperty( key, BoolProperty::New( value ) ); } bool mitk::DICOMImageBlockDescriptor::GetFlag( const std::string& key, bool defaultValue ) const { this->UpdateImageDescribingProperties(); BoolProperty::ConstPointer boolProp = dynamic_cast( this->GetProperty( key ) ); if ( boolProp.IsNotNull() ) { return boolProp->GetValue(); } else { return defaultValue; } } void mitk::DICOMImageBlockDescriptor::SetIntProperty( const std::string& key, int value ) { m_PropertyList->ReplaceProperty( key, IntProperty::New( value ) ); } int mitk::DICOMImageBlockDescriptor::GetIntProperty( const std::string& key, int defaultValue ) const { this->UpdateImageDescribingProperties(); IntProperty::ConstPointer intProp = dynamic_cast( this->GetProperty( key ) ); if ( intProp.IsNotNull() ) { return intProp->GetValue(); } else { return defaultValue; } } double mitk::DICOMImageBlockDescriptor::stringtodouble( const std::string& str ) const { double d; std::string trimmedstring( str ); try { trimmedstring = trimmedstring.erase( trimmedstring.find_last_not_of( " \n\r\t" ) + 1 ); } catch ( ... ) { // no last not of } std::string firstcomponent( trimmedstring ); try { firstcomponent = trimmedstring.erase( trimmedstring.find_first_of( "\\" ) ); } catch ( ... ) { // no last not of } std::istringstream converter( firstcomponent ); if ( !firstcomponent.empty() && ( converter >> d ) && converter.eof() ) { return d; } else { throw std::invalid_argument( "Argument is not a convertable number" ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::DescribeImageWithProperties( Image* mitkImage ) { // TODO: this is a collection of properties that have been provided by the // legacy DicomSeriesReader. // We should at some point clean up this collection and name them in a more // consistent way! if ( !mitkImage ) return mitkImage; // first part: add some tags that describe individual slices // these propeties are defined at analysis time (see UpdateImageDescribingProperties()) const char* propertyKeySliceLocation = "dicom.image.0020.1041"; const char* propertyKeyInstanceNumber = "dicom.image.0020.0013"; const char* propertyKeySOPInstanceUID = "dicom.image.0008.0018"; mitkImage->SetProperty( propertyKeySliceLocation, this->GetProperty( "sliceLocationForSlices" ) ); mitkImage->SetProperty( propertyKeyInstanceNumber, this->GetProperty( "instanceNumberForSlices" ) ); mitkImage->SetProperty( propertyKeySOPInstanceUID, this->GetProperty( "SOPInstanceUIDForSlices" ) ); mitkImage->SetProperty( "files", this->GetProperty( "filenamesForSlices" ) ); // second part: add properties that describe the whole image block mitkImage->SetProperty( "dicomseriesreader.SOPClassUID", StringProperty::New( this->GetSOPClassUID() ) ); mitkImage->SetProperty( "dicomseriesreader.SOPClass", StringProperty::New( this->GetSOPClassUIDAsName() ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New( PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New( this->GetPixelSpacingInterpretation() ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevelString", StringProperty::New( ReaderImplementationLevelToString( m_ReaderImplementationLevel ) ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( m_ReaderImplementationLevel ) ); mitkImage->SetProperty( "dicomseriesreader.GantyTiltCorrected", BoolProperty::New( this->GetTiltInformation().IsRegularGantryTilt() ) ); mitkImage->SetProperty( "dicomseriesreader.3D+t", BoolProperty::New( this->GetFlag( "3D+t", false ) ) ); // level window const std::string windowCenter = this->GetPropertyAsString( "windowCenter" ); const std::string windowWidth = this->GetPropertyAsString( "windowWidth" ); try { const double level = stringtodouble( windowCenter ); const double window = stringtodouble( windowWidth ); mitkImage->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow( level, window ) ) ); } catch ( ... ) { // nothing, no levelwindow to be predicted... } const std::string modality = this->GetPropertyAsString( "modality" ); mitkImage->SetProperty( "modality", StringProperty::New( modality ) ); mitkImage->SetProperty( "dicom.pixel.PhotometricInterpretation", this->GetProperty( "photometricInterpretation" ) ); mitkImage->SetProperty( "dicom.image.imagetype", this->GetProperty( "imagetype" ) ); mitkImage->SetProperty( "dicom.study.StudyDescription", this->GetProperty( "studyDescription" ) ); mitkImage->SetProperty( "dicom.series.SeriesDescription", this->GetProperty( "seriesDescription" ) ); mitkImage->SetProperty( "dicom.pixel.Rows", this->GetProperty( "rows" ) ); mitkImage->SetProperty( "dicom.pixel.Columns", this->GetProperty( "columns" ) ); // third part: get all found additional tags of interest for (auto tag : m_FoundAdditionalTags) { BaseProperty* prop = this->GetProperty(tag); if (prop) { mitkImage->SetProperty(tag.c_str(), prop); } } // fourth part: get something from ImageIO. BUT this needs to be created elsewhere. or not at all! return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetReaderImplementationLevel( const ReaderImplementationLevel& level ) { m_ReaderImplementationLevel = level; } mitk::ReaderImplementationLevel mitk::DICOMImageBlockDescriptor::GetReaderImplementationLevel() const { return m_ReaderImplementationLevel; } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUID() const { if ( !m_ImageFrameList.empty() && m_TagCache.IsNotNull() ) { static const DICOMTag tagSOPClassUID( 0x0008, 0x0016 ); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ).value; } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUID(). Need to have initialized tag-cache!"; return std::string( "" ); } } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUIDAsName() const { if ( !m_ImageFrameList.empty() && m_TagCache.IsNotNull() ) { gdcm::UIDs uidKnowledge; uidKnowledge.SetFromUID( this->GetSOPClassUID().c_str() ); const char* name = uidKnowledge.GetName(); if ( name ) { return std::string( name ); } else { return std::string( "" ); } } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUIDAsName(). Need to have " "initialized tag-cache!"; return std::string( "" ); } } int mitk::DICOMImageBlockDescriptor::GetNumberOfTimeSteps() const { int result = 1; this->m_PropertyList->GetIntProperty("timesteps", result); return result; }; int mitk::DICOMImageBlockDescriptor::GetNumberOfFramesPerTimeStep() const { const int numberOfTimesteps = this->GetNumberOfTimeSteps(); int numberOfFramesPerTimestep = this->m_ImageFrameList.size() / numberOfTimesteps; assert(int(double((double)this->m_ImageFrameList.size() / (double)numberOfTimesteps)) == numberOfFramesPerTimestep); // this should hold return numberOfFramesPerTimestep; }; void mitk::DICOMImageBlockDescriptor::SetTagCache( DICOMTagCache* privateCache ) { // this must only be used during loading and never afterwards m_TagCache = privateCache; } #define printPropertyRange( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name "First" ); \ const std::string last = this->GetPropertyAsString( #property_name "Last" ); \ if ( !first.empty() || !last.empty() ) \ { \ if ( first == last ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ else \ { \ os << " " label ": '" << first << "' - '" << last << "'" << std::endl; \ } \ } \ \ } #define printProperty( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name ); \ if ( !first.empty() ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ \ } #define printBool( label, commands ) \ \ { \ os << " " label ": '" << ( commands ? "yes" : "no" ) << "'" << std::endl; \ \ } void mitk::DICOMImageBlockDescriptor::Print(std::ostream& os, bool filenameDetails) const { os << " Number of Frames: '" << m_ImageFrameList.size() << "'" << std::endl; os << " SOP class: '" << this->GetSOPClassUIDAsName() << "'" << std::endl; printProperty( "Series Number", seriesNumber ); printProperty( "Study Description", studyDescription ); printProperty( "Series Description", seriesDescription ); printProperty( "Modality", modality ); printProperty( "Sequence Name", sequenceName ); printPropertyRange( "Slice Location", sliceLocation ); printPropertyRange( "Acquisition Number", acquisitionNumber ); printPropertyRange( "Instance Number", instanceNumber ); printPropertyRange( "Image Position", imagePositionPatient ); printProperty( "Image Orientation", orientation ); os << " Pixel spacing interpretation: '" << PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) << "'" << std::endl; printBool( "Gantry Tilt", this->GetTiltInformation().IsRegularGantryTilt() ) // printBool("3D+t", this->GetFlag("3D+t",false)) // os << " MITK image loaded: '" << (this->GetMitkImage().IsNotNull() ? "yes" : "no") << "'" << // std::endl; if ( filenameDetails ) { os << " Files in this image block:" << std::endl; for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++frameIter ) { os << " " << ( *frameIter )->Filename; if ( ( *frameIter )->FrameNo > 0 ) { os << ", " << ( *frameIter )->FrameNo; } os << std::endl; } } } #define storeTagValueToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValue = m_TagCache->GetTagValue( firstFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name, StringProperty::New( tagValue ) ); \ \ } #define storeTagValueRangeToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValueFirst = m_TagCache->GetTagValue( firstFrame, t ).value; \ const std::string tagValueLast = m_TagCache->GetTagValue( lastFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name "First", StringProperty::New( tagValueFirst ) ); \ const_cast( this ) \ ->SetProperty( #tag_name "Last", StringProperty::New( tagValueLast ) ); \ \ } void mitk::DICOMImageBlockDescriptor::UpdateImageDescribingProperties() const { if ( !m_PropertiesOutOfDate ) return; if ( !m_ImageFrameList.empty() ) { if ( m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to " "have initialized tag-cache!"; return; } const DICOMImageFrameInfo::Pointer firstFrame = m_ImageFrameList.front(); const DICOMImageFrameInfo::Pointer lastFrame = m_ImageFrameList.back(); // see macros above storeTagValueToProperty( seriesNumber, 0x0020, 0x0011 ); storeTagValueToProperty( studyDescription, 0x0008, 0x1030 ); storeTagValueToProperty( seriesDescription, 0x0008, 0x103e ); storeTagValueToProperty( modality, 0x0008, 0x0060 ); storeTagValueToProperty( sequenceName, 0x0018, 0x0024 ); storeTagValueToProperty( orientation, 0x0020, 0x0037 ); storeTagValueToProperty( rows, 0x0028, 0x0010 ); storeTagValueToProperty( columns, 0x0028, 0x0011 ); storeTagValueRangeToProperty( sliceLocation, 0x0020, 0x1041 ); storeTagValueRangeToProperty( acquisitionNumber, 0x0020, 0x0012 ); storeTagValueRangeToProperty( instanceNumber, 0x0020, 0x0013 ); storeTagValueRangeToProperty( imagePositionPatient, 0x0020, 0x0032 ); storeTagValueToProperty( windowCenter, 0x0028, 0x1050 ); storeTagValueToProperty( windowWidth, 0x0028, 0x1051 ); storeTagValueToProperty( imageType, 0x0008, 0x0008 ); storeTagValueToProperty( photometricInterpretation, 0x0028, 0x0004 ); // some per-image attributes // frames are just numbered starting from 0. timestep 1 (the second time-step) has frames starting at // (number-of-frames-per-timestep) // std::string propertyKeySliceLocation = "dicom.image.0020.1041"; // std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; // std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceUIDForSlices; StringLookupTable filenamesForSlices; const DICOMTag tagSliceLocation( 0x0020, 0x1041 ); const DICOMTag tagInstanceNumber( 0x0020, 0x0013 ); const DICOMTag tagSOPInstanceNumber( 0x0008, 0x0018 ); std::unordered_map additionalTagResultList; unsigned int slice(0); int timePoint(-1); - unsigned int zSlice(0); const int framesPerTimeStep = this->GetNumberOfFramesPerTimeStep(); for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++slice, ++frameIter ) { - zSlice = slice%framesPerTimeStep; + unsigned int zSlice = slice%framesPerTimeStep; if ( zSlice == 0) { timePoint++; } const std::string sliceLocation = m_TagCache->GetTagValue( *frameIter, tagSliceLocation ).value; sliceLocationForSlices.SetTableValue( slice, sliceLocation ); const std::string instanceNumber = m_TagCache->GetTagValue( *frameIter, tagInstanceNumber ).value; instanceNumberForSlices.SetTableValue( slice, instanceNumber ); const std::string sopInstanceUID = m_TagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ).value; SOPInstanceUIDForSlices.SetTableValue( slice, sopInstanceUID ); const std::string filename = ( *frameIter )->Filename; filenamesForSlices.SetTableValue( slice, filename ); MITK_DEBUG << "Tag info for slice " << slice << ": SL '" << sliceLocation << "' IN '" << instanceNumber << "' SOP instance UID '" << sopInstanceUID << "'"; for (const auto& tag : m_AdditionalTagMap) { const DICOMTagCache::FindingsListType findings = m_TagCache->GetTagValue( *frameIter, tag.first ); for (const auto& finding : findings) { if (finding.isValid) { std::string propKey = (tag.second.empty()) ? DICOMTagPathToPropertyName(finding.path) : tag.second; DICOMCachedValueInfo info{ static_cast(timePoint), zSlice, finding.value }; additionalTagResultList[propKey].SetTableValue(slice, info); } } } } // add property or properties with proper names DICOMImageBlockDescriptor* thisInstance = const_cast( this ); thisInstance->SetProperty( "sliceLocationForSlices", StringLookupTableProperty::New( sliceLocationForSlices ) ); thisInstance->SetProperty( "instanceNumberForSlices", StringLookupTableProperty::New( instanceNumberForSlices ) ); thisInstance->SetProperty( "SOPInstanceUIDForSlices", StringLookupTableProperty::New( SOPInstanceUIDForSlices ) ); thisInstance->SetProperty( "filenamesForSlices", StringLookupTableProperty::New( filenamesForSlices ) ); //add properties for additional tags of interest for ( auto iter = additionalTagResultList.cbegin(); iter != additionalTagResultList.cend(); ++iter ) { thisInstance->SetProperty( iter->first, m_PropertyFunctor( iter->second ) ); thisInstance->m_FoundAdditionalTags.insert(m_FoundAdditionalTags.cend(),iter->first); } m_PropertiesOutOfDate = false; } } mitk::BaseProperty::Pointer mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues(const DICOMCachedValueLookupTable& cacheLookupTable) { const auto& lookupTable = cacheLookupTable.GetLookupTable(); typedef std::pair PairType; if ( std::adjacent_find( lookupTable.cbegin(), lookupTable.cend(), []( const PairType& lhs, const PairType& rhs ) { return lhs.second.Value != rhs.second.Value; } ) == lookupTable.cend() ) { return static_cast( mitk::StringProperty::New(cacheLookupTable.GetTableValue(0).Value).GetPointer()); } StringLookupTable stringTable; for (auto element : lookupTable) { stringTable.SetTableValue(element.first, element.second.Value); } return static_cast( mitk::StringLookupTableProperty::New(stringTable).GetPointer()); } void mitk::DICOMImageBlockDescriptor::SetTagLookupTableToPropertyFunctor( TagLookupTableToPropertyFunctor functor ) { if ( functor != nullptr ) { m_PropertyFunctor = functor; } } diff --git a/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp b/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp index 1c567bd2ab..ab885de7c2 100644 --- a/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp +++ b/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp @@ -1,690 +1,688 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMReaderConfigurator.h" #include "mitkDICOMSortByTag.h" #include "mitkSortByImagePositionPatient.h" mitk::DICOMReaderConfigurator ::DICOMReaderConfigurator() { } mitk::DICOMReaderConfigurator ::~DICOMReaderConfigurator() { } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromConfigFile(const std::string& filename) const { TiXmlDocument doc (filename); if (doc.LoadFile()) { return this->CreateFromTiXmlDocument( doc ); } else { MITK_ERROR << "Unable to load file at '" << filename <<"'"; return DICOMFileReader::Pointer(); } } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromUTF8ConfigString(const std::string& xmlContents) const { TiXmlDocument doc; doc.Parse(xmlContents.c_str(), nullptr, TIXML_ENCODING_UTF8); return this->CreateFromTiXmlDocument( doc ); } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromTiXmlDocument(TiXmlDocument& doc) const { TiXmlHandle root(doc.RootElement()); if (TiXmlElement* rootElement = root.ToElement()) { if (strcmp(rootElement->Value(), "DICOMFileReader")) // :-( no std::string methods { MITK_ERROR << "File should contain a tag at top-level! Found '" << (rootElement->Value() ? std::string(rootElement->Value()) : std::string("!nothing!")) << "' instead"; return nullptr; } const char* classnameC = rootElement->Attribute("class"); if (!classnameC) { MITK_ERROR << "File should name a reader class in the class attribute: . Found nothing instead"; return nullptr; } int version(1); if ( rootElement->QueryIntAttribute("version", &version) == TIXML_SUCCESS) { if (version != 1) { MITK_WARN << "This reader is only capable of creating DICOMFileReaders of version 1. " << "Will not continue, because given configuration is meant for version " << version << "."; return nullptr; } } else { MITK_ERROR << "File should name the version of the reader class in the version attribute: ." << " Found nothing instead, assuming version 1!"; version = 1; } std::string classname(classnameC); double decimalPlacesForOrientation(5); bool useDecimalPlacesForOrientation(false); useDecimalPlacesForOrientation = rootElement->QueryDoubleAttribute("decimalPlacesForOrientation", &decimalPlacesForOrientation) == TIXML_SUCCESS; // attribute present and a double value if (classname == "ClassicDICOMSeriesReader") { mitk::ClassicDICOMSeriesReader::Pointer reader = mitk::ClassicDICOMSeriesReader::New(); this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader.GetPointer(), rootElement); this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader.GetPointer(), rootElement); return reader.GetPointer(); } if (classname == "ThreeDnTDICOMSeriesReader") { mitk::ThreeDnTDICOMSeriesReader::Pointer reader; if (useDecimalPlacesForOrientation) reader = mitk::ThreeDnTDICOMSeriesReader::New(decimalPlacesForOrientation); else reader = mitk::ThreeDnTDICOMSeriesReader::New(); return ConfigureThreeDnTDICOMSeriesReader(reader, rootElement).GetPointer(); } else if (classname == "DICOMITKSeriesGDCMReader") { mitk::DICOMITKSeriesGDCMReader::Pointer reader; if (useDecimalPlacesForOrientation) reader = mitk::DICOMITKSeriesGDCMReader::New(decimalPlacesForOrientation); else reader = mitk::DICOMITKSeriesGDCMReader::New(); return ConfigureDICOMITKSeriesGDCMReader(reader, rootElement).GetPointer(); } else { MITK_ERROR << "DICOMFileReader tag names unknown class '" << classname << "'"; return nullptr; } } else { MITK_ERROR << "Great confusion: no root element in XML document. Expecting a DICOMFileReader tag at top-level."; return nullptr; } } #define boolStringTrue(s) \ ( s == "true" || s == "on" || s == "1" \ || s == "TRUE" || s == "ON") void mitk::DICOMReaderConfigurator ::ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, TiXmlElement* element) const { // add the "group3DnT" flag bool group3DnT(true); const char* group3DnTC = element->Attribute("group3DnT"); if (group3DnTC) { std::string group3DnTS(group3DnTC); group3DnT = boolStringTrue(group3DnTS); } reader->SetGroup3DandT( group3DnT ); } mitk::ThreeDnTDICOMSeriesReader::Pointer mitk::DICOMReaderConfigurator ::ConfigureThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, TiXmlElement* element) const { assert(element); // use all the base class configuration if (this->ConfigureDICOMITKSeriesGDCMReader( reader.GetPointer(), element ).IsNull()) { return nullptr; } this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader,element); return reader; } void mitk::DICOMReaderConfigurator ::ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, TiXmlElement* element) const { assert(element); const char* configLabelC = element->Attribute("label"); if (configLabelC) { std::string configLabel(configLabelC); reader->SetConfigurationLabel(configLabel); } const char* configDescriptionC = element->Attribute("description"); if (configDescriptionC) { - std::string configDescription(configDescriptionC); reader->SetConfigurationDescription(configDescriptionC); } // "fixTiltByShearing" flag bool fixTiltByShearing(false); const char* fixTiltByShearingC = element->Attribute("fixTiltByShearing"); if (fixTiltByShearingC) { std::string fixTiltByShearingS(fixTiltByShearingC); fixTiltByShearing = boolStringTrue(fixTiltByShearingS); } reader->SetFixTiltByShearing( fixTiltByShearing ); } mitk::DICOMITKSeriesGDCMReader::Pointer mitk::DICOMReaderConfigurator ::ConfigureDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, TiXmlElement* element) const { assert(element); this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader, element); // "acceptTwoSlicesGroups" flag bool acceptTwoSlicesGroups(true); const char* acceptTwoSlicesGroupsC = element->Attribute("acceptTwoSlicesGroups"); if (acceptTwoSlicesGroupsC) { std::string acceptTwoSlicesGroupsS(acceptTwoSlicesGroupsC); acceptTwoSlicesGroups = boolStringTrue(acceptTwoSlicesGroupsS); } reader->SetAcceptTwoSlicesGroups( acceptTwoSlicesGroups ); // "toleratedOriginError" attribute (double) bool toleratedOriginErrorIsAbsolute(false); const char* toleratedOriginErrorIsAbsoluteC = element->Attribute("toleratedOriginErrorIsAbsolute"); if (toleratedOriginErrorIsAbsoluteC) { std::string toleratedOriginErrorIsAbsoluteS(toleratedOriginErrorIsAbsoluteC); toleratedOriginErrorIsAbsolute = boolStringTrue(toleratedOriginErrorIsAbsoluteS); } double toleratedOriginError(0.3); if (element->QueryDoubleAttribute("toleratedOriginError", &toleratedOriginError) == TIXML_SUCCESS) // attribute present and a double value { if (toleratedOriginErrorIsAbsolute) { reader->SetToleratedOriginOffset( toleratedOriginError ); } else { reader->SetToleratedOriginOffsetToAdaptive( toleratedOriginError ); } } // DICOMTagBasedSorters are the only thing we create at this point // TODO for-loop over all child elements of type DICOMTagBasedSorter, BUT actually a single sorter of this type is enough. TiXmlElement* dElement = element->FirstChildElement("DICOMDatasetSorter"); if (dElement) { const char* classnameC = dElement->Attribute("class"); if (!classnameC) { MITK_ERROR << "File should name a DICOMDatasetSorter class in the class attribute of . Found nothing instead"; return nullptr; } std::string classname(classnameC); if (classname == "DICOMTagBasedSorter") { DICOMTagBasedSorter::Pointer tagSorter = CreateDICOMTagBasedSorter(dElement); if (tagSorter.IsNotNull()) { reader->AddSortingElement( tagSorter ); } } else { MITK_ERROR << "DICOMDatasetSorter tag names unknown class '" << classname << "'"; return nullptr; } } return reader; } mitk::DICOMTagBasedSorter::Pointer mitk::DICOMReaderConfigurator ::CreateDICOMTagBasedSorter(TiXmlElement* element) const { mitk::DICOMTagBasedSorter::Pointer tagSorter = mitk::DICOMTagBasedSorter::New(); // "strictSorting" parameter! bool strictSorting(true); const char* strictSortingC = element->Attribute("strictSorting"); if (strictSortingC) { std::string strictSortingS(strictSortingC); strictSorting = boolStringTrue(strictSortingS); } tagSorter->SetStrictSorting(strictSorting); // "strictSorting" parameter! bool expectDistanceOne(true); const char* expectDistanceOneC = element->Attribute("expectDistanceOne"); if (expectDistanceOneC) { std::string expectDistanceOneS(expectDistanceOneC); expectDistanceOne = boolStringTrue(expectDistanceOneS); } tagSorter->SetExpectDistanceOne(expectDistanceOne); TiXmlElement* dElement = element->FirstChildElement("Distinguishing"); if (dElement) { for ( TiXmlElement* tChild = dElement->FirstChildElement(); tChild != nullptr; tChild = tChild->NextSiblingElement() ) { try { mitk::DICOMTag tag = tagFromXMLElement(tChild); int i(5); if (tChild->QueryIntAttribute("cutDecimalPlaces", &i) == TIXML_SUCCESS) { tagSorter->AddDistinguishingTag( tag, new mitk::DICOMTagBasedSorter::CutDecimalPlaces(i) ); } else { tagSorter->AddDistinguishingTag( tag ); } } catch(...) { return nullptr; } } } // "sorting tags" TiXmlElement* sElement = element->FirstChildElement("Sorting"); if (sElement) { DICOMSortCriterion::Pointer previousCriterion; DICOMSortCriterion::Pointer currentCriterion; for ( TiXmlNode* tChildNode = sElement->LastChild(); tChildNode != nullptr; tChildNode = tChildNode->PreviousSibling() ) { TiXmlElement* tChild = tChildNode->ToElement(); if (!tChild) continue; if (!strcmp(tChild->Value(), "Tag")) { try { currentCriterion = this->CreateDICOMSortByTag(tChild, previousCriterion); } catch(...) { std::stringstream ss; ss << "Could not parse element at (input line " << tChild->Row() << ", col. " << tChild->Column() << ")!"; MITK_ERROR << ss.str(); return nullptr; } } else if (!strcmp(tChild->Value(), "ImagePositionPatient")) { try { currentCriterion = this->CreateSortByImagePositionPatient(tChild, previousCriterion); } catch(...) { std::stringstream ss; ss << "Could not parse element at (input line " << tChild->Row() << ", col. " << tChild->Column() << ")!"; MITK_ERROR << ss.str(); return nullptr; } } else { MITK_ERROR << "File contain unknown tag <" << tChild->Value() << "> tag as child to ! Cannot interpret..."; } previousCriterion = currentCriterion; } tagSorter->SetSortCriterion( currentCriterion.GetPointer() ); } return tagSorter; } std::string mitk::DICOMReaderConfigurator ::requiredStringAttribute(TiXmlElement* xmlElement, const std::string& key) const { assert(xmlElement); const char* gC = xmlElement->Attribute(key.c_str()); if (gC) { std::string gS(gC); return gS; } else { std::stringstream ss; ss << "Expected an attribute '" << key << "' at this position " "(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } } unsigned int mitk::DICOMReaderConfigurator ::hexStringToUInt(const std::string& s) const { std::stringstream converter(s); unsigned int ui; converter >> std::hex >> ui; MITK_DEBUG << "Converted string '" << s << "' to unsigned int " << ui; return ui; } mitk::DICOMTag mitk::DICOMReaderConfigurator ::tagFromXMLElement(TiXmlElement* xmlElement) const { assert(xmlElement); if (strcmp(xmlElement->Value(), "Tag")) // :-( no std::string methods { std::stringstream ss; ss << "Expected a tag at this position " "(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } - std::string name = requiredStringAttribute(xmlElement, "name"); std::string groupS = requiredStringAttribute(xmlElement, "group"); std::string elementS = requiredStringAttribute(xmlElement, "element"); try { // convert string to int (assuming string is in hex format with leading "0x" like "0x0020") unsigned int group = hexStringToUInt(groupS); unsigned int element = hexStringToUInt(elementS); return DICOMTag(group, element); } catch(...) { std::stringstream ss; ss << "Expected group and element values in to be hexadecimal with leading 0x, e.g. '0x0020'" "(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } } mitk::DICOMSortCriterion::Pointer mitk::DICOMReaderConfigurator ::CreateDICOMSortByTag(TiXmlElement* xmlElement, DICOMSortCriterion::Pointer secondaryCriterion) const { mitk::DICOMTag tag = tagFromXMLElement(xmlElement); return DICOMSortByTag::New(tag, secondaryCriterion).GetPointer(); } mitk::DICOMSortCriterion::Pointer mitk::DICOMReaderConfigurator ::CreateSortByImagePositionPatient(TiXmlElement*, DICOMSortCriterion::Pointer secondaryCriterion) const { return SortByImagePositionPatient::New(secondaryCriterion).GetPointer(); } std::string mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(DICOMFileReader::ConstPointer reader) const { // check possible sub-classes from the most-specific one up to the most generic one const DICOMFileReader* cPointer = reader; TiXmlElement* root; if (const ClassicDICOMSeriesReader* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(specificReader); } else if (const ThreeDnTDICOMSeriesReader* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(specificReader); } else if (const DICOMITKSeriesGDCMReader* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(specificReader); } else { MITK_WARN << "Unknown reader class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize."; return ""; // no serialization, what a pity } if (root) { TiXmlDocument document; document.LinkEndChild( root ); TiXmlPrinter printer; printer.SetIndent( " " ); document.Accept( &printer ); std::string xmltext = printer.CStr(); return xmltext; } else { MITK_WARN << "DICOMReaderConfigurator::CreateConfigStringFromReader() created empty serialization. Problem?"; return ""; } } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(const DICOMITKSeriesGDCMReader* reader) const { TiXmlElement* root = this->CreateDICOMFileReaderTag(reader); assert(root); root->SetAttribute("fixTiltByShearing", toString(reader->GetFixTiltByShearing())); root->SetAttribute("acceptTwoSlicesGroups", toString(reader->GetAcceptTwoSlicesGroups())); root->SetDoubleAttribute("toleratedOriginError", reader->GetToleratedOriginError()); root->SetAttribute("toleratedOriginErrorIsAbsolute", toString(reader->IsToleratedOriginOffsetAbsolute())); root->SetDoubleAttribute("decimalPlacesForOrientation", reader->GetDecimalPlacesForOrientation()); // iterate DICOMDatasetSorter objects DICOMITKSeriesGDCMReader::ConstSorterList sorterList = reader->GetFreelyConfiguredSortingElements(); for(auto sorterIter = sorterList.begin(); sorterIter != sorterList.end(); ++sorterIter) { const DICOMDatasetSorter* sorter = *sorterIter; if (const DICOMTagBasedSorter* specificSorter = dynamic_cast(sorter)) { TiXmlElement* sorterTag = this->CreateConfigStringFromDICOMDatasetSorter(specificSorter); root->LinkEndChild(sorterTag); } else { MITK_WARN << "Unknown DICOMDatasetSorter class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize."; return nullptr; } } return root; } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromDICOMDatasetSorter(const DICOMTagBasedSorter* sorter) const { assert(sorter); auto sorterTag = new TiXmlElement("DICOMDatasetSorter"); sorterTag->SetAttribute("class", sorter->GetNameOfClass()); sorterTag->SetAttribute("strictSorting", toString(sorter->GetStrictSorting())); sorterTag->SetAttribute("expectDistanceOne", toString(sorter->GetExpectDistanceOne())); auto distinguishingTagsElement = new TiXmlElement("Distinguishing"); sorterTag->LinkEndChild(distinguishingTagsElement); mitk::DICOMTagList distinguishingTags = sorter->GetDistinguishingTags(); for (auto tagIter = distinguishingTags.begin(); tagIter != distinguishingTags.end(); ++tagIter) { TiXmlElement* tag = this->CreateConfigStringFromDICOMTag(*tagIter); distinguishingTagsElement->LinkEndChild(tag); const DICOMTagBasedSorter::TagValueProcessor* processor = sorter->GetTagValueProcessorForDistinguishingTag(*tagIter); if (const DICOMTagBasedSorter::CutDecimalPlaces* specificProcessor = dynamic_cast(processor)) { tag->SetDoubleAttribute("cutDecimalPlaces", specificProcessor->GetPrecision()); } } auto sortingElement = new TiXmlElement("Sorting"); sorterTag->LinkEndChild(sortingElement); mitk::DICOMSortCriterion::ConstPointer sortCriterion = sorter->GetSortCriterion(); while (sortCriterion.IsNotNull()) { std::string classname = sortCriterion->GetNameOfClass(); if (classname == "SortByImagePositionPatient") { sortingElement->LinkEndChild( new TiXmlElement("ImagePositionPatient") ); // no parameters } else if (classname == "DICOMSortByTag") { DICOMTagList pseudoTagList = sortCriterion->GetTagsOfInterest(); if (pseudoTagList.size() == 1) { DICOMTag firstTag = pseudoTagList.front(); TiXmlElement* tagElement = this->CreateConfigStringFromDICOMTag(firstTag); sortingElement->LinkEndChild( tagElement ); } else { MITK_ERROR << "Encountered SortByTag class with MULTIPLE tag in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize."; return nullptr; } } else { MITK_ERROR << "Encountered unknown class '" << classname << "' in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize."; return nullptr; } sortCriterion = sortCriterion->GetSecondaryCriterion(); } return sorterTag; } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromDICOMTag(const DICOMTag& tag) const { auto tagElement = new TiXmlElement("Tag"); // name group element tagElement->SetAttribute("name", tag.GetName().c_str()); tagElement->SetAttribute("group", toHexString(tag.GetGroup())); tagElement->SetAttribute("element", toHexString(tag.GetElement())); return tagElement; } std::string mitk::DICOMReaderConfigurator ::toHexString(unsigned int i) const { std::stringstream ss; ss << "0x" << std::setfill ('0') << std::setw(4) << std::hex << i; return ss.str(); } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(const ThreeDnTDICOMSeriesReader* reader) const { TiXmlElement* root = this->CreateConfigStringFromReader(static_cast(reader)); assert(root); root->SetAttribute("group3DnT", toString(reader->GetGroup3DandT())); return root; } const char* mitk::DICOMReaderConfigurator ::toString(bool b) const { return b ? "true" : "false"; } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(const ClassicDICOMSeriesReader* reader) const { return this->CreateDICOMFileReaderTag(reader); } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateDICOMFileReaderTag(const DICOMFileReader* reader) const { auto readerTag = new TiXmlElement("DICOMFileReader"); readerTag->SetAttribute("class", reader->GetNameOfClass()); readerTag->SetAttribute("label", reader->GetConfigurationLabel().c_str()); readerTag->SetAttribute("description", reader->GetConfigurationDescription().c_str()); readerTag->SetAttribute("version", "1"); return readerTag; } diff --git a/Modules/DICOMReader/src/mitkGantryTiltInformation.cpp b/Modules/DICOMReader/src/mitkGantryTiltInformation.cpp index d4b1dea4f0..8e85c35ca8 100644 --- a/Modules/DICOMReader/src/mitkGantryTiltInformation.cpp +++ b/Modules/DICOMReader/src/mitkGantryTiltInformation.cpp @@ -1,258 +1,258 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "mitkGantryTiltInformation.h" #include "mitkDICOMTag.h" #include "mitkLogMacros.h" mitk::GantryTiltInformation::GantryTiltInformation() : m_ShiftUp(0.0) , m_ShiftRight(0.0) , m_ShiftNormal(0.0) , m_ITKAssumedSliceSpacing(0.0) , m_NumberOfSlicesApart(0) { } #define doublepoint(x) \ Point3Dd x; \ x[0] = x ## f[0]; \ x[1] = x ## f[1]; \ x[2] = x ## f[2]; #define doublevector(x) \ Vector3Dd x; \ x[0] = x ## f[0]; \ x[1] = x ## f[1]; \ x[2] = x ## f[2]; mitk::GantryTiltInformation::GantryTiltInformation( const Point3D& origin1f, const Point3D& origin2f, const Vector3D& rightf, const Vector3D& upf, unsigned int numberOfSlicesApart) : m_ShiftUp(0.0) , m_ShiftRight(0.0) , m_ShiftNormal(0.0) , m_NumberOfSlicesApart(numberOfSlicesApart) { assert(numberOfSlicesApart); doublepoint(origin1); doublepoint(origin2); doublevector(right); doublevector(up); // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack: // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1) // check if this line passes through origin 2 /* Determine if line (imagePosition2 + l * normal) contains imagePosition1. Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal) E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2 / |pointAlongNormal - origin2| ^ 2 ( x meaning the cross product ) */ Vector3Dd normal = itk::CrossProduct(right, up); Point3Dd pointAlongNormal = origin2 + normal; double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm(); double denominator = (pointAlongNormal - origin2).GetSquaredNorm(); double distance = sqrt(numerator / denominator); if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry"; MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; MITK_DEBUG << " ==> storing this shift for later analysis:"; MITK_DEBUG << " v origin1: " << origin1; MITK_DEBUG << " v origin2: " << origin2; MITK_DEBUG << " v right: " << right; MITK_DEBUG << " v up: " << up; MITK_DEBUG << " v normal: " << normal; Point3Dd projectionRight = projectPointOnLine( origin1, origin2, right ); Point3Dd projectionNormal = projectPointOnLine( origin1, origin2, normal ); m_ShiftRight = (projectionRight - origin2).GetNorm(); m_ShiftNormal = (projectionNormal - origin2).GetNorm(); /* now also check to which side the image is shifted. Calculation e.g. from http://mathworld.wolfram.com/Point-PlaneDistance.html */ Point3Dd testPoint = origin1; Vector3Dd planeNormal = up; double signedDistance = ( planeNormal[0] * testPoint[0] + planeNormal[1] * testPoint[1] + planeNormal[2] * testPoint[2] - ( planeNormal[0] * origin2[0] + planeNormal[1] * origin2[1] + planeNormal[2] * origin2[2] ) ) / sqrt( planeNormal[0] * planeNormal[0] + planeNormal[1] * planeNormal[1] + planeNormal[2] * planeNormal[2] ); m_ShiftUp = signedDistance; m_ITKAssumedSliceSpacing = (origin2 - origin1).GetNorm(); // How do we now this is assumed? See header documentation for ITK code references //double itkAssumedSliceSpacing = sqrt( m_ShiftUp * m_ShiftUp + m_ShiftNormal * m_ShiftNormal ); MITK_DEBUG << " calculated from slices " << m_NumberOfSlicesApart << " slices apart"; MITK_DEBUG << " shift normal: " << m_ShiftNormal; MITK_DEBUG << " shift normal assumed by ITK: " << m_ITKAssumedSliceSpacing; MITK_DEBUG << " shift up: " << m_ShiftUp; MITK_DEBUG << " shift right: " << m_ShiftRight; MITK_DEBUG << " tilt angle (deg): " << atan( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535; } } mitk::GantryTiltInformation mitk::GantryTiltInformation ::MakeFromTagValues( const std::string& origin1String, const std::string& origin2String, - const std::string orientationString, + const std::string& orientationString, unsigned int numberOfSlicesApart) { Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point bool orientationConversion(false); DICOMStringToOrientationVectors( orientationString, right, up, orientationConversion ); if (orientationConversion && !origin1String.empty() && !origin2String.empty() ) { bool firstOriginConversion(false); bool lastOriginConversion(false); Point3D firstOrigin = DICOMStringToPoint3D( origin1String, firstOriginConversion ); Point3D lastOrigin = DICOMStringToPoint3D( origin2String, lastOriginConversion ); if (firstOriginConversion && lastOriginConversion) { return GantryTiltInformation( firstOrigin, lastOrigin, right, up, numberOfSlicesApart ); } } std::stringstream ss; ss << "Invalid tag values when constructing tilt information from origin1 '" << origin1String << "', origin2 '" << origin2String << "', and orientation '" << orientationString << "'"; throw std::invalid_argument(ss.str()); } void mitk::GantryTiltInformation ::Print(std::ostream& os) const { os << " calculated from slices " << m_NumberOfSlicesApart << " slices apart" << std::endl; os << " shift normal: " << m_ShiftNormal << std::endl; os << " shift normal assumed by ITK: " << m_ITKAssumedSliceSpacing << std::endl; os << " shift up: " << m_ShiftUp << std::endl; os << " shift right: " << m_ShiftRight << std::endl; os << " tilt angle (deg): " << atan( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535 << std::endl; } mitk::Point3D mitk::GantryTiltInformation::projectPointOnLine( Point3Dd p, Point3Dd lineOrigin, Vector3Dd lineDirection ) { /** See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/ vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) ) */ Vector3Dd lineOriginToP = p - lineOrigin; double innerProduct = lineOriginToP * lineDirection; double factor = innerProduct / lineDirection.GetSquaredNorm(); Point3Dd projection = lineOrigin + factor * lineDirection; return projection; } double mitk::GantryTiltInformation::GetTiltCorrectedAdditionalSize(unsigned int imageSizeZ) const { return fabs(m_ShiftUp / static_cast(m_NumberOfSlicesApart) * static_cast(imageSizeZ-1)); } double mitk::GantryTiltInformation::GetTiltAngleInDegrees() const { return atan( fabs(m_ShiftUp) / m_ShiftNormal ) * 180.0 / 3.1415926535; } double mitk::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const { // so many mm need to be shifted per slice! return m_ShiftUp / static_cast(m_NumberOfSlicesApart); } double mitk::GantryTiltInformation::GetRealZSpacing() const { return m_ShiftNormal / static_cast(m_NumberOfSlicesApart); } bool mitk::GantryTiltInformation::IsSheared() const { return m_NumberOfSlicesApart && ( fabs(m_ShiftRight) > 0.001 || fabs(m_ShiftUp) > 0.001); } bool mitk::GantryTiltInformation::IsRegularGantryTilt() const { return m_NumberOfSlicesApart && ( fabs(m_ShiftRight) < 0.001 && fabs(m_ShiftUp) > 0.001); } diff --git a/Modules/DataTypesExt/src/mitkMesh.cpp b/Modules/DataTypesExt/src/mitkMesh.cpp index 369f6a1479..bfab35a501 100644 --- a/Modules/DataTypesExt/src/mitkMesh.cpp +++ b/Modules/DataTypesExt/src/mitkMesh.cpp @@ -1,810 +1,809 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMesh.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkLineOperation.h" #include "mitkLineOperation.h" #include "mitkNumericTypes.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPointOperation.h" #include "mitkRenderingManager.h" #include "mitkStatusBar.h" mitk::Mesh::Mesh() { } mitk::Mesh::~Mesh() { } const mitk::Mesh::DataType *mitk::Mesh::GetMesh(int t) const { return m_PointSetSeries[t]; } mitk::Mesh::DataType *mitk::Mesh::GetMesh(int t) { return m_PointSetSeries[t]; } void mitk::Mesh::SetMesh(DataType *mesh, int t) { this->Expand(t + 1); m_PointSetSeries[t] = mesh; } unsigned long mitk::Mesh::GetNumberOfCells(int t) { return m_PointSetSeries[t]->GetNumberOfCells(); } // search a line that is close enough according to the given position bool mitk::Mesh::SearchLine(Point3D point, float distance, unsigned long &lineId, unsigned long &cellId, int t) { // returns true if a line is found ScalarType bestDist = distance; // iterate through all cells. ConstCellIterator cellIt = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator cellEnd = m_PointSetSeries[t]->GetCells()->End(); while (cellIt != cellEnd) { if (cellIt.Value()->GetNumberOfPoints() > 1) { // then iterate through all indexes of points in it->Value() PointIdIterator inAIt = cellIt.Value()->PointIdsBegin(); // first point PointIdIterator inBIt = cellIt.Value()->PointIdsBegin(); // second point PointIdIterator inEnd = cellIt.Value()->PointIdsEnd(); ++inAIt; // so it points to the point before inBIt int currentLineId = 0; while (inAIt != inEnd) { mitk::PointSet::PointType pointA, pointB; if (m_PointSetSeries[t]->GetPoint((*inAIt), &pointA) && m_PointSetSeries[t]->GetPoint((*inBIt), &pointB)) { auto line = new Line(); line->SetPoints(pointA, pointB); double thisDistance = line->Distance(point); if (thisDistance < bestDist) { cellId = cellIt->Index(); lineId = currentLineId; bestDist = thisDistance; } } ++inAIt; ++inBIt; ++currentLineId; } // If the cell is closed, then check the line from the last index to // the first index if inAIt points to inEnd, then inBIt points to the // last index. CellDataType cellData; bool dataOk = m_PointSetSeries[t]->GetCellData(cellIt->Index(), &cellData); if (dataOk) { if (cellData.closed) { // get the points PointIdIterator inAIt = cellIt.Value()->PointIdsBegin(); // first point // inBIt points to last. mitk::PointSet::PointType pointA, pointB; if (m_PointSetSeries[t]->GetPoint((*inAIt), &pointA) && m_PointSetSeries[t]->GetPoint((*inBIt), &pointB)) { auto line = new Line(); line->SetPoints(pointA, pointB); double thisDistance = line->Distance(point); if (thisDistance < bestDist) { cellId = cellIt->Index(); lineId = currentLineId; bestDist = thisDistance; } } } } } ++cellIt; } return (bestDist < distance); } int mitk::Mesh::SearchFirstCell(unsigned long pointId, int t) { // iterate through all cells and find the cell the given pointId is inside ConstCellIterator it = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator end = m_PointSetSeries[t]->GetCells()->End(); while (it != end) { PointIdIterator position = std::find(it->Value()->PointIdsBegin(), it->Value()->PointIdsEnd(), pointId); if (position != it->Value()->PointIdsEnd()) { return it->Index(); } ++it; } return -1; } // Due to not implemented itk::CellInterface::EvaluatePosition and errors in // using vtkCell::EvaluatePosition (changing iterator!) we must implement // it in mitk::Mesh // make it easy and look for hit points and hit lines: needs to be done anyway! bool mitk::Mesh::EvaluatePosition(mitk::Point3D point, unsigned long &cellId, float precision, int t) { int pointId = this->SearchPoint(point, precision, t); if (pointId > -1) { // search the cell the point lies inside cellId = this->SearchFirstCell(pointId, t); return true; } unsigned long lineId = 0; if (this->SearchLine(point, precision, lineId, cellId, t)) { return true; } return false; } unsigned long mitk::Mesh::GetNewCellId(int t) { long nextCellId = -1; ConstCellIterator it = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator end = m_PointSetSeries[t]->GetCells()->End(); while (it != end) { nextCellId = it.Index(); ++it; } ++nextCellId; return nextCellId; } int mitk::Mesh::SearchSelectedCell(int t) { CellDataIterator cellDataIt, cellDataEnd; cellDataEnd = m_PointSetSeries[t]->GetCellData()->End(); for (cellDataIt = m_PointSetSeries[t]->GetCellData()->Begin(); cellDataIt != cellDataEnd; cellDataIt++) { // then declare an operation which unselects this line; UndoOperation as well! if (cellDataIt->Value().selected) { return cellDataIt->Index(); } } return -1; } // get the cell; then iterate through the Ids times lineId. Then IdA ist the // one, IdB ist ne next.don't forget the last closing line if the cell is // closed bool mitk::Mesh::GetPointIds(unsigned long cellId, unsigned long lineId, int &idA, int &idB, int t) { - bool ok = false; - bool found = false; CellAutoPointer cellAutoPointer; - ok = m_PointSetSeries[t]->GetCell(cellId, cellAutoPointer); + bool ok = m_PointSetSeries[t]->GetCell(cellId, cellAutoPointer); if (ok) { CellType *cell = cellAutoPointer.GetPointer(); // Get the cellData to also check the closing line CellDataType cellData; m_PointSetSeries[t]->GetCellData(cellId, &cellData); bool closed = cellData.closed; PointIdIterator pointIdIt = cell->PointIdsBegin(); PointIdIterator pointIdEnd = cell->PointIdsEnd(); unsigned int counter = 0; + bool found = false; while (pointIdIt != pointIdEnd) { if (counter == lineId) { idA = (*pointIdIt); ++pointIdIt; found = true; break; } ++counter; ++pointIdIt; } if (found) { // if in the middle if (pointIdIt != pointIdEnd) { idB = (*pointIdIt); } // if found but on the end, then it is the closing connection, so the // last and the first point else if (closed) { pointIdIt = cell->PointIdsBegin(); idB = (*pointIdIt); } } else ok = false; } return ok; } void mitk::Mesh::ExecuteOperation(Operation *operation) { // adding only the operations, that aren't implemented by the pointset. switch (operation->GetOperationType()) { case OpNOTHING: break; case OpNEWCELL: { mitk::LineOperation *lineOp = dynamic_cast(operation); // if no lineoperation, then call superclass pointSet if (lineOp == nullptr) { Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); // if it doesn't already exist if (!ok) { cellAutoPointer.TakeOwnership(new PolygonType); m_PointSetSeries[0]->SetCell(cellId, cellAutoPointer); CellDataType cellData; cellData.selected = true; cellData.selectedLines.clear(); cellData.closed = false; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpDELETECELL: { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } m_PointSetSeries[0]->GetCells()->DeleteIndex((unsigned)lineOp->GetCellId()); m_PointSetSeries[0]->GetCellData()->DeleteIndex((unsigned)lineOp->GetCellId()); } break; case OpCLOSECELL: // sets the bolean flag closed from a specified cell to true. { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { // then search the selected cell!//TODO Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); if (cellId < 0) // cellId isn't set { cellId = this->SearchSelectedCell(0); if (cellId < 0) // still not found return; } CellAutoPointer cellAutoPointer; // get directly the celldata!TODO ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.closed = true; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpOPENCELL: { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.closed = false; ; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpADDLINE: // inserts the ID of the selected point into the indexes of lines in the // selected cell afterwars the added line is selected { mitk::LineOperation *lineOp = dynamic_cast(operation); int cellId = -1; int pId = -1; if (lineOp == nullptr) { cellId = this->SearchSelectedCell(0); if (cellId == -1) return; pId = this->SearchSelectedPoint(0); if (pId == -1) return; } else { cellId = lineOp->GetCellId(); if (cellId == -1) return; pId = lineOp->GetPIdA(); if (pId == -1) return; } bool ok; CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellType *cell = cellAutoPointer.GetPointer(); if (cell->GetType() == CellType::POLYGON_CELL) { PolygonType *polygon = static_cast(cell); // add the pointId to the Cell. filling the empty cell with // one id doesn't mean to add a line, it means, that the // initilal PointId is set. The next addition of a pointId adds // a line polygon->AddPointId(pId); // select the line, if we really added a line, so now have more than // 1 pointID in the cell CellDataType cellData; ok = m_PointSetSeries[0]->GetCellData(cellId, &cellData); if (ok) { // A line between point 0 and 1 has the Id 0. A line between // 1 and 2 has a Id = 1. So we add getnumberofpoints-2. if (polygon->GetNumberOfPoints() > 1) cellData.selectedLines.push_back(polygon->GetNumberOfPoints() - 2); } m_PointSetSeries[0]->SetCellData(cellId, cellData); m_PointSetSeries[0]->SetCell(cellId, cellAutoPointer); } } } break; case OpDELETELINE: { // deleted the last line through removing the index PIdA // (if set to -1, use the last point) in the given cellId mitk::LineOperation *lineOp = dynamic_cast(operation); int cellId = -1; int pId = -1; if (lineOp == nullptr) { cellId = this->SearchSelectedCell(0); if (cellId == -1) return; pId = this->SearchSelectedPoint(0); } else { cellId = lineOp->GetCellId(); if (cellId == -1) return; pId = lineOp->GetPIdA(); } bool ok; CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellType *cell = cellAutoPointer.GetPointer(); if (cell->GetType() == CellType::POLYGON_CELL) { PolygonType *oldPolygon = static_cast(cell); auto newPolygonCell = new PolygonType; CellAutoPointer newCell; newCell.TakeOwnership(newPolygonCell); PointIdConstIterator it, oldend; oldend = oldPolygon->PointIdsEnd(); if (pId >= 0) { for (it = oldPolygon->PointIdsBegin(); it != oldend; ++it) { if ((*it) != (MeshType::PointIdentifier)pId) { newPolygonCell->AddPointId(*it); } } } else { --oldend; for (it = oldPolygon->PointIdsBegin(); it != oldend; ++it) newPolygonCell->AddPointId(*it); } oldPolygon->SetPointIds(0, newPolygonCell->GetNumberOfPoints(), newPolygonCell->PointIdsBegin()); } } } break; case OpREMOVELINE: // Remove the given Index in the given cell through copying everything // into a new cell accept the one that has to be deleted. { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } bool ok; CellAutoPointer cellAutoPointer; int cellId = lineOp->GetCellId(); ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (!ok) return; CellType *cell = cellAutoPointer.GetPointer(); CellAutoPointer newCellAutoPointer; newCellAutoPointer.TakeOwnership(new PolygonType); PolygonType *newPolygon = static_cast(cell); PointIdIterator it = cell->PointIdsBegin(); PointIdIterator end = cell->PointIdsEnd(); int pointId = lineOp->GetPIdA(); if (pointId < 0) // if not initialized!! return; while (it != end) { if ((*it) == (unsigned int)pointId) { break; } else { newPolygon->AddPointId(*it); } ++it; } while (it != end) { newPolygon->AddPointId(*it); it++; } m_PointSetSeries[0]->SetCell(cellId, newCellAutoPointer); } break; case OpINSERTLINE: // //insert line between two other points. ////before A--B after A--C--B // //the points A, B and C have to be in the pointset. // //needed: CellId, Id of C , Id A and Id B ////the cell has to exist! //{ // mitk::LineOperation *lineOp = dynamic_cast(operation); // if (lineOp == NULL)//if no lineoperation, then call superclass pointSet // { // Superclass::ExecuteOperation(operation); // } // int cellId = lineOp->GetCellId(); // int pIdC = lineOp->GetPIdC(); // int pIdA = lineOp->GetPIdA(); // int pIdB = lineOp->GetPIdB(); // //the points of the given PointIds have to exist in the PointSet // bool ok; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdA); // if (!ok) // return; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdB); // if (!ok) // return; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdC); // if (!ok) // return; // // so the points do exist. So now check, if there is already a cell // // with the given Id // DataType::CellAutoPointer cell; // ok = m_PointSetSeries[0]->GetCell(cellId, cell); // if (!ok) // return; // //pIdA and pIdB should exist in the cell // // PointIdIterator pit = cell->PointIdsBegin(); // PointIdIterator end = cell->PointIdsEnd(); // // //now arrange the new Ids in the cell like desired; pIdC between // // pIdA and pIdB // unsigned int nuPoints = cell->GetNumberOfPoints(); // std::vector newPoints; // pit = cell->PointIdsBegin(); // end = cell->PointIdsEnd(); // int i = 0; // while( pit != end ) // { // if ((*pit) = pIdA) // { // //now we have found the place to add pIdC after // newPoints[i] = (*pit); // i++; // newPoints[i] = pIdC; // } // else // newPoints[i] = (*pit); // pit++; // } // //now we have the Ids, that existed before combined with the new ones // //so delete the old cell // //doesn't seem to be necessary! // //cell->ClearPoints(); // pit = cell->PointIdsBegin(); // cell->SetPointIds(pit); //} break; case OpMOVELINE: //(moves two points) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) { mitk::StatusBar::GetInstance()->DisplayText( "Message from mitkMesh: Recieved wrong type of operation! See mitkMeshInteractor.cpp", 10000); return; } // create two operations out of the one operation and call superclass // through the transmitted pointIds get the koordinates of the points. // then add the transmitted vestor to them // create two operations and send them to superclass Point3D pointA, pointB; pointA.Fill(0.0); pointB.Fill(0.0); m_PointSetSeries[0]->GetPoint(lineOp->GetPIdA(), &pointA); m_PointSetSeries[0]->GetPoint(lineOp->GetPIdB(), &pointB); pointA[0] += lineOp->GetVector()[0]; pointA[1] += lineOp->GetVector()[1]; pointA[2] += lineOp->GetVector()[2]; pointB[0] += lineOp->GetVector()[0]; pointB[1] += lineOp->GetVector()[1]; pointB[2] += lineOp->GetVector()[2]; auto operationA = new mitk::PointOperation(OpMOVE, pointA, lineOp->GetPIdA()); auto operationB = new mitk::PointOperation(OpMOVE, pointB, lineOp->GetPIdB()); Superclass::ExecuteOperation(operationA); Superclass::ExecuteOperation(operationB); } break; case OpSELECTLINE: //(select the given line) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); SelectedLinesType *selectedLines = &(cellData.selectedLines); auto position = std::find(selectedLines->begin(), selectedLines->end(), (unsigned int)lineOp->GetId()); if (position == selectedLines->end()) // if not alsready selected { cellData.selectedLines.push_back(lineOp->GetId()); } m_PointSetSeries[0]->SetCellData(lineOp->GetCellId(), cellData); } } break; case OpDESELECTLINE: //(deselect the given line) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); SelectedLinesType *selectedLines = &(cellData.selectedLines); auto position = std::find(selectedLines->begin(), selectedLines->end(), (unsigned int)lineOp->GetId()); if (position != selectedLines->end()) // if found { selectedLines->erase(position); } m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpSELECTCELL: //(select the given cell) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; // directly get the data!//TODO bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.selected = true; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpDESELECTCELL: //(deselect the given cell) { mitk::LineOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.selected = false; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpMOVECELL: // moves all Points of one cell according to the given vector { mitk::CellOperation *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no celloperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); Vector3D vector = lineOp->GetVector(); // get the cell CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (!ok) return; CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); // iterate through the pointIds of the CellData and move those points in // the pointset PointIdIterator it = cellAutoPointer->PointIdsBegin(); PointIdIterator end = cellAutoPointer->PointIdsEnd(); while (it != end) { unsigned int position = (*it); PointType point; point.Fill(0); m_PointSetSeries[0]->GetPoint(position, &point); point = point + vector; m_PointSetSeries[0]->SetPoint(position, point); ++it; } } break; default: // if the operation couldn't be handled here, then send it to superclass Superclass::ExecuteOperation(operation); return; } // to tell the mappers, that the data is modifierd and has to be updated this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object *)this)->InvokeEvent(endevent); // As discussed lately, don't mess with rendering from inside data structures //*todo has to be done here, cause of update-pipeline not working yet // mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } mitk::Mesh::DataType::BoundingBoxPointer mitk::Mesh::GetBoundingBoxFromCell(unsigned long cellId, int t) { // itk::CellInterface has also a GetBoundingBox, but it // returns CoordRepType [PointDimension *2] DataType::BoundingBoxPointer bBoxPointer = nullptr; CellAutoPointer cellAutoPointer; if (m_PointSetSeries[t]->GetCell(cellId, cellAutoPointer)) { DataType::PointsContainerPointer pointsContainer = DataType::PointsContainer::New(); PointIdIterator bbIt = cellAutoPointer.GetPointer()->PointIdsBegin(); PointIdIterator bbEnd = cellAutoPointer.GetPointer()->PointIdsEnd(); while (bbIt != bbEnd) { mitk::PointSet::PointType point; bool pointOk = m_PointSetSeries[t]->GetPoint((*bbIt), &point); if (pointOk) pointsContainer->SetElement((*bbIt), point); ++bbIt; } bBoxPointer = DataType::BoundingBoxType::New(); bBoxPointer->SetPoints(pointsContainer); bBoxPointer->ComputeBoundingBox(); } return bBoxPointer; } diff --git a/Modules/DicomRT/include/mitkIsoDoseLevelCollections.h b/Modules/DicomRT/include/mitkIsoDoseLevelCollections.h index 7cf0d76a77..b0e97a5096 100644 --- a/Modules/DicomRT/include/mitkIsoDoseLevelCollections.h +++ b/Modules/DicomRT/include/mitkIsoDoseLevelCollections.h @@ -1,158 +1,158 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_DOSE_ISO_LEVEL_COLLECTIONS_H_ #define _MITK_DOSE_ISO_LEVEL_COLLECTIONS_H_ #include #include #include "mitkIsoDoseLevel.h" namespace mitk { /** \class IsoDoseLevelVector \brief Simple vector that stores dose iso levels. * * This class is for example used to store the user defined free iso values. */ typedef ::itk::VectorContainer IsoDoseLevelVector; /** \class IsoDoseLevelSet \brief Stores values needed for the representation/visualization of dose iso levels. * * Set of dose iso levels sorted by the dose values of the iso levels (low to high values). * This data structure is used to represent the dose iso level setup used for the * visualization of a dose distribution. */ class MITKDICOMRT_EXPORT IsoDoseLevelSet: public itk::Object { public: mitkClassMacroItkParent(IsoDoseLevelSet, itk::Object); itkNewMacro(Self); private: /** Quick access to the STL vector type that was inherited. */ typedef std::vector< IsoDoseLevel::Pointer > InternalVectorType; typedef InternalVectorType::size_type size_type; typedef InternalVectorType::iterator VectorIterator; typedef InternalVectorType::const_iterator VectorConstIterator; InternalVectorType m_IsoLevels; protected: IsoDoseLevelSet() {}; - IsoDoseLevelSet(const IsoDoseLevelSet & other); + explicit IsoDoseLevelSet(const IsoDoseLevelSet & other); virtual ~IsoDoseLevelSet() {}; mitkCloneMacro(IsoDoseLevelSet); public: typedef size_type IsoLevelIndexType; typedef IsoDoseLevel::DoseValueType DoseValueType; /** Convenient typedefs for the iterator and const iterator. */ class ConstIterator; /** Friends to this class. */ friend class ConstIterator; /** \class ConstIterator * Simulate STL-vector style const iteration where dereferencing the iterator * gives read access to the value. */ class ConstIterator { public: ConstIterator() {} ConstIterator(const VectorConstIterator & i): m_Iter(i) {} ConstIterator(const ConstIterator & r) { m_Iter = r.m_Iter; } const IsoDoseLevel & operator*() { return *(m_Iter->GetPointer()); } const IsoDoseLevel * operator->() { return m_Iter->GetPointer(); } ConstIterator & operator++() {++m_Iter; return *this; } ConstIterator operator++(int) { ConstIterator temp(*this); ++m_Iter; return temp; } ConstIterator & operator--() {--m_Iter; return *this; } ConstIterator operator--(int) { ConstIterator temp(*this); --m_Iter; return temp; } ConstIterator & operator=(const ConstIterator & r) {m_Iter = r.m_Iter; return *this; } bool operator==(const ConstIterator & r) const { return m_Iter == r.m_Iter; } bool operator!=(const ConstIterator & r) const { return m_Iter != r.m_Iter; } const IsoDoseLevel & Value(void) const { return *(m_Iter->GetPointer()); } private: VectorConstIterator m_Iter; }; /* Declare the public interface routines. */ /** * Read the element from the given index. * It is assumed that the index exists. */ const IsoDoseLevel& GetIsoDoseLevel(IsoLevelIndexType) const; const IsoDoseLevel& GetIsoDoseLevel(DoseValueType) const; /** * Set the element value at the given index. * It is assumed that the index exists. */ void SetIsoDoseLevel(const IsoDoseLevel*); /** * Check if the index range of the vector is large enough to allow the * given index without expansion. */ bool DoseLevelExists(IsoLevelIndexType) const; bool DoseLevelExists(DoseValueType) const; /** * Delete the element defined by the index identifier. In practice, it * doesn't make sense to delete a vector index. Instead, this method just * overwrites the index with the default element. */ void DeleteIsoDoseLevel(DoseValueType); void DeleteIsoDoseLevel(IsoLevelIndexType); /** * Get a begin const iterator for the vector. */ ConstIterator Begin(void) const; /** * Get an end const iterator for the vector. */ ConstIterator End(void) const; /** * Get the number of elements currently stored in the vector. */ IsoLevelIndexType Size(void) const; /** * Clear the elements. The final size will be zero. */ void Reset(void); }; } #endif //_MITK_DOSE_ISO_LEVEL_COLLECTIONS_H_ diff --git a/Modules/DicomRT/include/mitkIsoDoseLevelSetProperty.h b/Modules/DicomRT/include/mitkIsoDoseLevelSetProperty.h index a3e58bcabb..980f831b40 100644 --- a/Modules/DicomRT/include/mitkIsoDoseLevelSetProperty.h +++ b/Modules/DicomRT/include/mitkIsoDoseLevelSetProperty.h @@ -1,80 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_DOSE_ISO_LEVEL_SET_PROPERTY_H_ #define _MITK_DOSE_ISO_LEVEL_SET_PROPERTY_H_ #include "mitkBaseProperty.h" #include "mitkIsoDoseLevelCollections.h" #include "MitkDicomRTExports.h" namespace mitk { /** \brief Property class for dose iso level sets. */ class MITKDICOMRT_EXPORT IsoDoseLevelSetProperty : public BaseProperty { protected: IsoDoseLevelSet::Pointer m_IsoLevelSet; IsoDoseLevelSetProperty(); - IsoDoseLevelSetProperty(const IsoDoseLevelSetProperty& other); + explicit IsoDoseLevelSetProperty(const IsoDoseLevelSetProperty& other); - IsoDoseLevelSetProperty(IsoDoseLevelSet* levelSet); + explicit IsoDoseLevelSetProperty(IsoDoseLevelSet* levelSet); public: mitkClassMacro(IsoDoseLevelSetProperty, BaseProperty); itkNewMacro(IsoDoseLevelSetProperty); mitkNewMacro1Param(IsoDoseLevelSetProperty, IsoDoseLevelSet*); typedef IsoDoseLevelSet ValueType; virtual ~IsoDoseLevelSetProperty(); const IsoDoseLevelSet * GetIsoDoseLevelSet() const; const IsoDoseLevelSet * GetValue() const; IsoDoseLevelSet * GetIsoDoseLevelSet(); IsoDoseLevelSet * GetValue(); void SetIsoDoseLevelSet(IsoDoseLevelSet* levelSet); void SetValue(IsoDoseLevelSet* levelSet); virtual std::string GetValueAsString() const override; using BaseProperty::operator=; private: // purposely not implemented IsoDoseLevelSetProperty& operator=(const IsoDoseLevelSetProperty&); itk::LightObject::Pointer InternalClone() const override; virtual bool IsEqual(const BaseProperty& property) const override; virtual bool Assign(const BaseProperty& property) override; }; } // namespace mitk #endif /* _MITK_DOSE_ISO_LEVEL_SET_PROPERTY_H_ */ diff --git a/Modules/DicomRT/include/mitkIsoDoseLevelVectorProperty.h b/Modules/DicomRT/include/mitkIsoDoseLevelVectorProperty.h index 3549c453d0..ba4462bb5a 100644 --- a/Modules/DicomRT/include/mitkIsoDoseLevelVectorProperty.h +++ b/Modules/DicomRT/include/mitkIsoDoseLevelVectorProperty.h @@ -1,81 +1,81 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_DOSE_ISO_LEVEL_VECTOR_PROPERTY_H_ #define _MITK_DOSE_ISO_LEVEL_VECTOR_PROPERTY_H_ #include "mitkBaseProperty.h" #include "mitkIsoDoseLevelCollections.h" #include "MitkDicomRTExports.h" namespace mitk { /** \brief Property class for dose iso level vector. */ class MITKDICOMRT_EXPORT IsoDoseLevelVectorProperty : public BaseProperty { protected: IsoDoseLevelVector::Pointer m_IsoLevelVector; IsoDoseLevelVectorProperty(); - IsoDoseLevelVectorProperty(const IsoDoseLevelVectorProperty& other); + explicit IsoDoseLevelVectorProperty(const IsoDoseLevelVectorProperty& other); - IsoDoseLevelVectorProperty(IsoDoseLevelVector* levelVector); + explicit IsoDoseLevelVectorProperty(IsoDoseLevelVector* levelVector); public: mitkClassMacro(IsoDoseLevelVectorProperty, BaseProperty); itkNewMacro(IsoDoseLevelVectorProperty); mitkNewMacro1Param(IsoDoseLevelVectorProperty, IsoDoseLevelVector*); typedef IsoDoseLevelVector ValueType; virtual ~IsoDoseLevelVectorProperty(); const IsoDoseLevelVector * GetIsoDoseLevelVector() const; const IsoDoseLevelVector * GetValue() const; IsoDoseLevelVector * GetIsoDoseLevelVector(); IsoDoseLevelVector * GetValue(); void SetIsoDoseLevelVector(IsoDoseLevelVector* levelVector); void SetValue(IsoDoseLevelVector* levelVector); virtual std::string GetValueAsString() const override; using BaseProperty::operator=; private: // purposely not implemented IsoDoseLevelVectorProperty& operator=(const IsoDoseLevelVectorProperty&); itk::LightObject::Pointer InternalClone() const override; virtual bool IsEqual(const BaseProperty& property) const override; virtual bool Assign(const BaseProperty& property) override; }; } // namespace mitk #endif /* _MITK_DOSE_ISO_LEVEL_SET_PROPERTY_H_ */ diff --git a/Modules/DicomRT/include/mitkRTConstants.h b/Modules/DicomRT/include/mitkRTConstants.h index e5d1990407..f7be5b89b0 100644 --- a/Modules/DicomRT/include/mitkRTConstants.h +++ b/Modules/DicomRT/include/mitkRTConstants.h @@ -1,89 +1,111 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_RT_CONSTANTS_H_ #define _MITK_RT_CONSTANTS_H_ #include #include "MitkDicomRTExports.h" namespace mitk { struct MITKDICOMRT_EXPORT RTConstants { /** * Name of the property that indicates if a data/node is a dose. */ static const std::string DOSE_PROPERTY_NAME; /** * Name of the property that encodes the prescribed dose associated with the data node * If a RTPLAN file exists the value can be extracted from the tag (300A,0026) - Target Prescription Dose in the plan file. */ static const std::string PRESCRIBED_DOSE_PROPERTY_NAME; /** * Name of the property that encodes the reference dose that should be used for relative dose vizualization/evaluation purpose. * It is often the prescribed dose but may differ e.g. when to dose distributions sould be compared using the same reference. */ static const std::string REFERENCE_DOSE_PROPERTY_NAME; + /** + * Name of the property that encodes the reference structure set. + */ + static const std::string REFERENCE_STRUCTURE_SET_PROPERTY_NAME; + /** * Name of the property that encodes the optional string property holding the information from the tag (3004,0004) - Dose Type. * This contains useful information for medical doctors */ static const std::string DOSE_TYPE_PROPERTY_NAME; + /** + * Name of the property that encodes the optional string property holding the description information from the tag (300A,0016) - Dose Reference Description. + */ + static const std::string REFERENCE_DESCRIPTION_DOSE_PROPERTY_NAME; + /** * Name of the property that encodes the optional string property holding the information from the tag (3004,000A) - Dose Summation Type. * This contains useful information for medical doctors */ static const std::string DOSE_SUMMATION_TYPE_PROPERTY_NAME; /** * Name of the property that encodes the number of fractions. * It is for example in DICOM stored in tag (300A,0078) - Number of Fractions Prescribed (from the RTPLAN file if this file exists). * This value could be used to further scale the dose according to dose summation type. * For example a given plan consists of 8 fractions. Scaling the fraction dose by 8 gives the complete planned dose. */ static const std::string DOSE_FRACTION_COUNT_PROPERTY_NAME; + /** + * Name of the property that encodes the number of beams. + * It is for example in DICOM stored in tag (300A,0080) - Number of Beams (from the RTPLAN file if this file exists). + */ + static const std::string DOSE_FRACTION_NUMBER_OF_BEAMS_PROPERTY_NAME; + /** + * Name of the property that encodes the radiation type of beams. + * It is for example in DICOM stored in tag (300A,00C6) - Radiation Type (from the RTPLAN file if this file exists). + */ + static const std::string DOSE_RADIATION_TYPE_PROPERTY_NAME; + /** * Name of the property that encodes if the iso line rendering should be activated for the node. */ static const std::string DOSE_SHOW_ISOLINES_PROPERTY_NAME; /** * Name of the property that encodes if the color wash rendering should be activated for the node. */ static const std::string DOSE_SHOW_COLORWASH_PROPERTY_NAME; /** * Name of the property that encodes if the set of iso levels should be used to visualize the dose distribution. */ static const std::string DOSE_ISO_LEVELS_PROPERTY_NAME; /** * Name of the property that encodes user defined iso values that mark special dose values in the distribution. */ static const std::string DOSE_FREE_ISO_VALUES_PROPERTY_NAME; + }; } #endif diff --git a/Modules/DicomRT/include/mitkRTDoseReader.h b/Modules/DicomRT/include/mitkRTDoseReader.h index 5b36f3f6c6..a170bb6226 100644 --- a/Modules/DicomRT/include/mitkRTDoseReader.h +++ b/Modules/DicomRT/include/mitkRTDoseReader.h @@ -1,77 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDicomRTReader_h #define mitkDicomRTReader_h #include #include #include #include namespace mitk { /** * \brief RTDoseReader reads DICOM files of modality RTDOSE. */ class MITKDICOMRT_EXPORT RTDoseReader : public mitk::AbstractFileReader { public: - RTDoseReader(const RTDoseReader& other); RTDoseReader(); /** * @brief Reads a dicom dataset from a RTDOSE file * @param filename The path with the dose file * @return Returns a mitkDataNode::Pointer in which a mitk::Image is stored * * The method reads the PixelData from the DicomRT dose file and scales * them with a factor for getting Gray-values instead of pixel-values. * The Gray-values are stored in a mitkImage with a vtkColorTransferFunc. * Relative values are used for coloring the image. The relative values are * relative to a PrescriptionDose defined in the RT-Plan. If there is no * RT-Plan file PrescriptionDose is set to 80% of the maximum dose. */ //mitk::DataNode::Pointer LoadRTDose(const char* filename); virtual ~RTDoseReader(); using AbstractFileReader::Read; virtual std::vector > Read() override; private: RTDoseReader* Clone() const override; /** * \brief Scales an image with a factor * * \param gridscale the factor to scale with */ template void MultiplyGridScaling(itk::Image< TPixel, VImageDimension>* image, float gridscale); mitk::IDICOMTagsOfInterest* GetDicomTagsOfInterestService(); mitk::Image::Pointer scaledDoseImage; us::ServiceRegistration m_ServiceReg; }; } #endif diff --git a/Modules/DicomRT/src/mitkDoseNodeHelper.cpp b/Modules/DicomRT/src/mitkDoseNodeHelper.cpp index 72cb39975c..d9d98baf7f 100644 --- a/Modules/DicomRT/src/mitkDoseNodeHelper.cpp +++ b/Modules/DicomRT/src/mitkDoseNodeHelper.cpp @@ -1,81 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDoseNodeHelper.h" #include #include #include #include #include #include #include #include #include #include #include void mitk::ConfigureNodeAsDoseNode(mitk::DataNode* node, mitk::DoseValueAbs referenceDose) { if (node) { //set some specific colorwash and isoline properties node->SetBoolProperty(mitk::RTConstants::DOSE_SHOW_COLORWASH_PROPERTY_NAME.c_str(), true); node->SetBoolProperty(mitk::RTConstants::DOSE_SHOW_ISOLINES_PROPERTY_NAME.c_str(), false); node->SetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), referenceDose); mitk::IsoDoseLevelSet::Pointer isoDoseLevelPreset = mitk::GeneratIsoLevels_Virtuos(); mitk::IsoDoseLevelSetProperty::Pointer levelSetProp = mitk::IsoDoseLevelSetProperty::New(isoDoseLevelPreset); node->SetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str(), levelSetProp); mitk::IsoDoseLevelVector::Pointer levelVector = mitk::IsoDoseLevelVector::New(); mitk::IsoDoseLevelVectorProperty::Pointer levelVecProp = mitk::IsoDoseLevelVectorProperty::New(levelVector); node->SetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str(), levelVecProp); mitk::RenderingModeProperty::Pointer renderingModeProp = mitk::RenderingModeProperty::New(); //Generating the Colorwash vtkSmartPointer transferFunction = vtkSmartPointer::New(); for (mitk::IsoDoseLevelSet::ConstIterator itIsoDoseLevel = isoDoseLevelPreset->Begin(); itIsoDoseLevel != isoDoseLevelPreset->End(); ++itIsoDoseLevel) { - float *hsv = new float[3]; - //used for transfer rgb to hsv - vtkSmartPointer cCalc = vtkSmartPointer::New(); if (itIsoDoseLevel->GetVisibleColorWash()) { - cCalc->RGBToHSV(itIsoDoseLevel->GetColor()[0], itIsoDoseLevel->GetColor()[1], itIsoDoseLevel->GetColor()[2], &hsv[0], &hsv[1], &hsv[2]); - transferFunction->AddHSVPoint(itIsoDoseLevel->GetDoseValue()*referenceDose, hsv[0], hsv[1], hsv[2], 1.0, 1.0); + double rgbValue[] = { itIsoDoseLevel->GetColor()[0], itIsoDoseLevel->GetColor()[1], itIsoDoseLevel->GetColor()[2] }; + transferFunction->AddRGBPoint(itIsoDoseLevel->GetDoseValue()*referenceDose, rgbValue[0], rgbValue[1], rgbValue[2]); } } mitk::TransferFunction::Pointer mitkTransFunc = mitk::TransferFunction::New(); mitk::TransferFunctionProperty::Pointer mitkTransFuncProp = mitk::TransferFunctionProperty::New(); mitkTransFunc->SetColorTransferFunction(transferFunction); mitkTransFuncProp->SetValue(mitkTransFunc); node->SetProperty("Image Rendering.Transfer Function", mitkTransFuncProp); renderingModeProp->SetValue(mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR); node->SetProperty("Image Rendering.Mode", renderingModeProp); node->SetProperty("opacity", mitk::FloatProperty::New(0.5)); } }; diff --git a/Modules/DicomRT/src/mitkIsoDoseLevelCollections.cpp b/Modules/DicomRT/src/mitkIsoDoseLevelCollections.cpp index 0a001b32e9..174054a3ba 100644 --- a/Modules/DicomRT/src/mitkIsoDoseLevelCollections.cpp +++ b/Modules/DicomRT/src/mitkIsoDoseLevelCollections.cpp @@ -1,145 +1,145 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mitkIsoDoseLevelCollections.h" #include "mitkExceptionMacro.h" namespace mitk { /** "Private" functor used in std::find_if to find IsoDoseLevels with a given reference value */ class EqualDoseFunctor { public: typedef IsoDoseLevel::DoseValueType DoseValueType; - EqualDoseFunctor(const DoseValueType& refValue) : m_refValue(refValue) + explicit EqualDoseFunctor(const DoseValueType& refValue) : m_refValue(refValue) {} bool operator () (const IsoDoseLevel* level) { return level->GetDoseValue() == m_refValue; } protected: DoseValueType m_refValue; }; /** "Private" binary function used in std::sort to check the order of IsoDoseLeveles */ bool lesserIsoDoseLevel(const IsoDoseLevel* first, const IsoDoseLevel* second) { return first->GetDoseValue() < second->GetDoseValue(); } } mitk::IsoDoseLevelSet::IsoDoseLevelSet(const IsoDoseLevelSet & other) { if (&other != this) { this->m_IsoLevels = other.m_IsoLevels; } } const mitk::IsoDoseLevel& mitk::IsoDoseLevelSet::GetIsoDoseLevel(IsoLevelIndexType index) const { if (index < this->m_IsoLevels.size()) { return *(this->m_IsoLevels[index].GetPointer()); } else { mitkThrow() << "Try to access non existing dose iso level."; } } const mitk::IsoDoseLevel& mitk::IsoDoseLevelSet::GetIsoDoseLevel(DoseValueType value) const { auto pos = std::find_if(this->m_IsoLevels.begin(), this->m_IsoLevels.end(), EqualDoseFunctor(value)); if (pos != this->m_IsoLevels.end()) { return *(pos->GetPointer()); } else { mitkThrow() << "Try to access non existing dose iso level."; } } void mitk::IsoDoseLevelSet::SetIsoDoseLevel(const IsoDoseLevel* level) { if (!level) { mitkThrow() << "Cannot set iso level. Passed null pointer."; } this->DeleteIsoDoseLevel(level->GetDoseValue()); this->m_IsoLevels.push_back(level->Clone()); std::sort(this->m_IsoLevels.begin(), this->m_IsoLevels.end(),lesserIsoDoseLevel); } bool mitk::IsoDoseLevelSet::DoseLevelExists(IsoLevelIndexType index) const { return index < this->m_IsoLevels.size(); } bool mitk::IsoDoseLevelSet::DoseLevelExists(DoseValueType value) const { auto pos = std::find_if(this->m_IsoLevels.begin(), this->m_IsoLevels.end(), EqualDoseFunctor(value)); return pos != this->m_IsoLevels.end(); } void mitk::IsoDoseLevelSet::DeleteIsoDoseLevel(DoseValueType value) { auto pos = std::find_if(this->m_IsoLevels.begin(), this->m_IsoLevels.end(), EqualDoseFunctor(value)); if (pos != this->m_IsoLevels.end()) { this->m_IsoLevels.erase(pos); } } void mitk::IsoDoseLevelSet::DeleteIsoDoseLevel(IsoLevelIndexType index) { if (DoseLevelExists(index)) { this->m_IsoLevels.erase(this->m_IsoLevels.begin()+index); } } mitk::IsoDoseLevelSet::ConstIterator mitk::IsoDoseLevelSet::Begin(void) const { return ConstIterator(this->m_IsoLevels.begin()); } mitk::IsoDoseLevelSet::ConstIterator mitk::IsoDoseLevelSet::End(void) const { return ConstIterator(this->m_IsoLevels.end()); } mitk::IsoDoseLevelSet::IsoLevelIndexType mitk::IsoDoseLevelSet::Size(void) const { return this->m_IsoLevels.size(); } void mitk::IsoDoseLevelSet::Reset(void) { this->m_IsoLevels.clear(); } diff --git a/Modules/DicomRT/src/mitkRTConstants.cpp b/Modules/DicomRT/src/mitkRTConstants.cpp index 54828fda5e..6f3f936e78 100644 --- a/Modules/DicomRT/src/mitkRTConstants.cpp +++ b/Modules/DicomRT/src/mitkRTConstants.cpp @@ -1,27 +1,32 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkRTConstants.h" +const std::string mitk::RTConstants::DOSE_PROPERTY_NAME = "dose"; const std::string mitk::RTConstants::PRESCRIBED_DOSE_PROPERTY_NAME = "dose.PrescribedDose"; const std::string mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME = "dose.ReferenceDose"; +const std::string mitk::RTConstants::REFERENCE_STRUCTURE_SET_PROPERTY_NAME = "plan.ReferenceStructureSet"; +const std::string mitk::RTConstants::REFERENCE_DESCRIPTION_DOSE_PROPERTY_NAME = "dose.ReferenceDescription"; +const std::string mitk::RTConstants::DOSE_RADIATION_TYPE_PROPERTY_NAME = "plan.RadiationType"; const std::string mitk::RTConstants::DOSE_TYPE_PROPERTY_NAME = "dose.type"; const std::string mitk::RTConstants::DOSE_SUMMATION_TYPE_PROPERTY_NAME = "dose.summationType"; const std::string mitk::RTConstants::DOSE_FRACTION_COUNT_PROPERTY_NAME = "dose.fractionCount"; +const std::string mitk::RTConstants::DOSE_FRACTION_NUMBER_OF_BEAMS_PROPERTY_NAME = "dose.numerOfBeams"; const std::string mitk::RTConstants::DOSE_SHOW_ISOLINES_PROPERTY_NAME = "dose.showIsoLines"; const std::string mitk::RTConstants::DOSE_SHOW_COLORWASH_PROPERTY_NAME = "dose.showColorWash"; const std::string mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME = "dose.isoLevels"; const std::string mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME = "dose.freeIsoValues"; diff --git a/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp b/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp index 0b6f39c90b..76c1d49223 100644 --- a/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp +++ b/Modules/DicomRT/test/mitkRTStructureSetReaderTest.cpp @@ -1,158 +1,158 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkRTStructureSetReader.h" #include #include class mitkRTStructureSetReaderTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkRTStructureSetReaderTestSuite); // MITK_TEST(TestBody); MITK_TEST(TestStructureSets); CPPUNIT_TEST_SUITE_END(); private: mitk::RTStructureSetReader::Pointer m_rtStructureReader; public: void setUp() override { m_rtStructureReader = mitk::RTStructureSetReader::New(); CPPUNIT_ASSERT_MESSAGE("Failed to initialize RTStructureSetReader", m_rtStructureReader.IsNotNull()); } void TestStructureSets() { std::deque contourModelVectorCorrect; std::deque contourModelVectorCorrectSequ; std::deque contourModelVectorTest; std::deque contourModelVectorTestDel; LoadData(contourModelVectorCorrect); contourModelVectorTest = m_rtStructureReader->ReadStructureSet(GetTestDataFilePath("RT/StructureSet/RS.dcm").c_str()); //Deleting all empty contourmodelsets - empty contourmodelsets cant be //saved so we have reference for the comparison for(unsigned int i=0; i(contourModelVectorTest.at(i)->GetData())->GetSize()>0){ contourModelVectorTestDel.push_back(dynamic_cast(contourModelVectorTest.at(i)->GetData())); } } //Loop for ordering the loaded contourmodelsets(contourModelVectorCorrect) for(unsigned int i=0; iGetProperty("name"); for(unsigned int j=0; jGetProperty("name"); if(tmp->GetValueAsString().compare(name->GetValueAsString()) == 0) contourModelVectorCorrectSequ.push_back(contourModelVectorCorrect.at(j)); } } //Testing wheather the two deques are equal bool equal = true; for(unsigned int i=0;iGetSize()!=c2->GetSize()) { MITK_INFO << "Number of ContourModelSets different" << std::endl; return false; } else { for(int i=0;iGetSize();++i) { mitk::ContourModel::Pointer cm1 = c1->GetContourModelAt(i); mitk::ContourModel::Pointer cm2 = c2->GetContourModelAt(i); if(cm1->GetNumberOfVertices()!=cm2->GetNumberOfVertices()) { MITK_INFO << "Number of Vertices different" << std::endl; return false; } else { float ep = 0.001; for(int j=0;jGetNumberOfVertices();++j) { mitk::Point3D p1 = cm1->GetVertexAt(i)->Coordinates; mitk::Point3D p2 = cm2->GetVertexAt(i)->Coordinates; - if(fabs(p1[0]-p2[0]) > ep || fabs(p1[0]-p2[0]) > ep || fabs(p1[0]-p2[0]) > ep) + if(fabs(p1[0]-p2[0]) > ep || fabs(p1[1]-p2[1]) > ep || fabs(p1[2]-p2[2]) > ep) { return false; } } } } } return true; } void LoadData(std::deque &r) { std::vector > readerOutput; readerOutput = mitk::IOUtil::Load(GetTestDataFilePath("RT/StructureSet/BODY.cnt_set")); mitk::ContourModelSet::Pointer cnt_set = dynamic_cast(readerOutput.at(0).GetPointer()); cnt_set->SetProperty("name", mitk::StringProperty::New("BODY")); r.push_back(cnt_set); readerOutput = mitk::IOUtil::Load(GetTestDataFilePath("RT/StructureSet/Bladder.cnt_set")); cnt_set = dynamic_cast(readerOutput.at(0).GetPointer()); cnt_set->SetProperty("name", mitk::StringProperty::New("Bladder")); r.push_back(cnt_set); readerOutput = mitk::IOUtil::Load(GetTestDataFilePath("RT/StructureSet/Femoral Head Lt.cnt_set")); cnt_set = dynamic_cast(readerOutput.at(0).GetPointer()); cnt_set->SetProperty("name", mitk::StringProperty::New("Femoral Head Lt")); r.push_back(cnt_set); readerOutput = mitk::IOUtil::Load(GetTestDataFilePath("RT/StructureSet/Femoral Head RT.cnt_set")); cnt_set = dynamic_cast(readerOutput.at(0).GetPointer()); cnt_set->SetProperty("name", mitk::StringProperty::New("Femoral Head RT")); r.push_back(cnt_set); readerOutput = mitk::IOUtil::Load(GetTestDataFilePath("RT/StructureSet/PTV.cnt_set")); cnt_set = dynamic_cast(readerOutput.at(0).GetPointer()); cnt_set->SetProperty("name", mitk::StringProperty::New("PTV")); r.push_back(cnt_set); readerOutput = mitk::IOUtil::Load(GetTestDataFilePath("RT/StructureSet/Rectum.cnt_set")); cnt_set = dynamic_cast(readerOutput.at(0).GetPointer()); cnt_set->SetProperty("name", mitk::StringProperty::New("Rectum")); r.push_back(cnt_set); } }; MITK_TEST_SUITE_REGISTRATION(mitkRTStructureSetReader) diff --git a/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp b/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp index 879dcb0060..4500968fdc 100644 --- a/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp +++ b/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp @@ -1,212 +1,212 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Qmitk #include "QmitkDicomExternalDataWidget.h" #include // CTK #include // Qt #include #include const std::string QmitkDicomExternalDataWidget::Widget_ID = "org.mitk.Widgets.QmitkDicomExternalDataWidget"; QmitkDicomExternalDataWidget::QmitkDicomExternalDataWidget(QWidget *parent) : QWidget(parent), m_Controls(nullptr), m_ProgressDialog(nullptr) { Initialize(); CreateQtPartControl(this); } QmitkDicomExternalDataWidget::~QmitkDicomExternalDataWidget() { } void QmitkDicomExternalDataWidget::CreateQtPartControl(QWidget *parent) { // build up qt Widget, unless already done if (!m_Controls) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkDicomExternalDataWidgetControls; m_Controls->setupUi(parent); m_Controls->viewExternalDataButton->setVisible(true); m_Controls->ctkDICOMBrowser->setTableOrientation(Qt::Vertical); m_Controls->ctkDICOMBrowser->setDICOMDatabase(m_ExternalDatabase); SetupImportDialog(); SetupProgressDialog(); // connect buttons connect(m_Controls->downloadButton, SIGNAL(clicked()), this, SLOT(OnDownloadButtonClicked())); connect(m_Controls->viewExternalDataButton, SIGNAL(clicked()), this, SLOT(OnViewButtonClicked())); connect(m_Controls->directoryButton, SIGNAL(clicked()), m_ImportDialog, SLOT(show())); connect(m_Controls->ctkDICOMBrowser, SIGNAL(seriesSelectionChanged(const QStringList &)), this, SLOT(OnSeriesSelectionChanged(const QStringList &))); connect( m_Controls->ctkDICOMBrowser, SIGNAL(seriesDoubleClicked(const QModelIndex &)), this, SLOT(OnViewButtonClicked())); connect(m_ImportDialog, SIGNAL(fileSelected(QString)), this, SLOT(OnStartDicomImport(QString))); connect(m_ExternalIndexer, SIGNAL(indexingFilePath(const QString &)), m_ProgressDialog, SLOT(setLabelText(const QString &))); connect(m_ExternalIndexer, SIGNAL(progress(int)), m_ProgressDialog, SLOT(setValue(int))); // actually the progress dialog closes if the maximum value is reached, BUT // the following line is needed since the external indexer wont reach maximum value (100 % progress) connect(m_ExternalIndexer, SIGNAL(indexingComplete()), m_ProgressDialog, SLOT(close())); connect(m_ProgressDialog, SIGNAL(canceled()), m_ExternalIndexer, SLOT(cancel())); } } void QmitkDicomExternalDataWidget::Initialize() { m_ExternalDatabase = new ctkDICOMDatabase(this); try { m_ExternalDatabase->openDatabase(QString(":memory:"), QString("EXTERNAL-DB")); } - catch (std::exception e) + catch (const std::exception&) { MITK_ERROR << "Database error: " << m_ExternalDatabase->lastError().toStdString(); m_ExternalDatabase->closeDatabase(); return; } m_ExternalIndexer = new ctkDICOMIndexer(this); } void QmitkDicomExternalDataWidget::OnDownloadButtonClicked() { QStringList filesToDownload = GetFileNamesFromIndex(); if (filesToDownload.size() == 0) { QMessageBox info; info.setText("You have to select an entry in the DICOM browser for import."); info.exec(); return; } emit SignalStartDicomImport(GetFileNamesFromIndex()); } void QmitkDicomExternalDataWidget::OnViewButtonClicked() { QStringList uids = m_Controls->ctkDICOMBrowser->currentSeriesSelection(); QString uid; foreach (uid, uids) { QStringList filesForSeries = m_ExternalDatabase->filesForSeries(uid); QHash eventProperty; eventProperty.insert("FilesForSeries", filesForSeries); if (!filesForSeries.isEmpty()) { QString modality = m_ExternalDatabase->fileValue(filesForSeries.at(0), "0008,0060"); eventProperty.insert("Modality", modality); } emit SignalDicomToDataManager(eventProperty); } } QStringList QmitkDicomExternalDataWidget::GetFileNamesFromIndex() { QStringList filePaths; QString uid; QStringList seriesUIDs = m_Controls->ctkDICOMBrowser->currentSeriesSelection(); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } if (!filePaths.empty()) return filePaths; QStringList studyUIDs = m_Controls->ctkDICOMBrowser->currentStudiesSelection(); foreach (uid, studyUIDs) { seriesUIDs = m_ExternalDatabase->seriesForStudy(uid); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } } if (!filePaths.empty()) return filePaths; QStringList patientsUIDs = m_Controls->ctkDICOMBrowser->currentPatientsSelection(); foreach (uid, patientsUIDs) { studyUIDs = m_ExternalDatabase->studiesForPatient(uid); foreach (uid, studyUIDs) { seriesUIDs = m_ExternalDatabase->seriesForStudy(uid); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } } } return filePaths; } void QmitkDicomExternalDataWidget::OnStartDicomImport(const QString &directory) { m_ImportDialog->close(); // no need to show / start the progress dialog, as the dialog // appears by receiving the progress signal from the external indexer m_LastImportDirectory = directory; m_ExternalIndexer->addDirectory(*m_ExternalDatabase, m_LastImportDirectory); } void QmitkDicomExternalDataWidget::OnSeriesSelectionChanged(const QStringList &s) { m_Controls->viewExternalDataButton->setEnabled((s.size() != 0)); } void QmitkDicomExternalDataWidget::SetupImportDialog() { // Initialize import widget m_ImportDialog = new ctkFileDialog(this); // Since copy on import is not working at the moment // this feature is disabled // QCheckBox* importCheckbox = new QCheckBox("Copy on import", m_ImportDialog); // m_ImportDialog->setBottomWidget(importCheckbox); m_ImportDialog->setFileMode(QFileDialog::Directory); m_ImportDialog->setLabelText(QFileDialog::Accept, "Import"); m_ImportDialog->setWindowTitle("Import DICOM files from directory"); m_ImportDialog->setWindowModality(Qt::ApplicationModal); } void QmitkDicomExternalDataWidget::SetupProgressDialog() { m_ProgressDialog = new QProgressDialog("Initialization ...", "Cancel", 0, 100, this); m_ProgressDialog->setWindowTitle("DICOM Import"); m_ProgressDialog->setWindowModality(Qt::ApplicationModal); m_ProgressDialog->setMinimumDuration(0); // FIX T20008: immediately set the progress dialog value to maximum --> will close the dialog m_ProgressDialog->setValue(100); } diff --git a/Modules/RTUI/Qmitk/QmitkDoseColorDelegate.h b/Modules/RTUI/Qmitk/QmitkDoseColorDelegate.h index 1cafcc1d48..f42463989a 100644 --- a/Modules/RTUI/Qmitk/QmitkDoseColorDelegate.h +++ b/Modules/RTUI/Qmitk/QmitkDoseColorDelegate.h @@ -1,44 +1,44 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkDoseColorDelegate_h #define QmitkDoseColorDelegate_h #include #include "MitkRTUIExports.h" /** \class QmitkDoseColorDelegate \brief An item delegate for rendering and editing dose color in a QTableView.*/ class MITKRTUI_EXPORT QmitkDoseColorDelegate : public QStyledItemDelegate { Q_OBJECT public: /// /// Creates a new PropertyDelegate. /// - QmitkDoseColorDelegate(QObject *parent = 0); + explicit QmitkDoseColorDelegate(QObject *parent = 0); bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; }; #endif diff --git a/Modules/RTUI/Qmitk/QmitkDoseValueDelegate.h b/Modules/RTUI/Qmitk/QmitkDoseValueDelegate.h index 3e25a7799d..8f7cf66ba5 100644 --- a/Modules/RTUI/Qmitk/QmitkDoseValueDelegate.h +++ b/Modules/RTUI/Qmitk/QmitkDoseValueDelegate.h @@ -1,63 +1,63 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkDoseValueDelegate_h #define QmitkDoseValueDelegate_h #include #include "MitkRTUIExports.h" /** \class QmitkDoseValueDelegate \brief An item delegate for rendering and editing dose values. The delegate assumes that the model uses the role Qt::UserRole+1 to indicate if the returned dose value is an absolute (data(Qt::UserRole+1) == true) or an relative dose (data(Qt::UserRole+1) == false).*/ class MITKRTUI_EXPORT QmitkDoseValueDelegate : public QStyledItemDelegate { Q_OBJECT public: /// /// Creates a new PropertyDelegate. /// - QmitkDoseValueDelegate(QObject *parent = 0); + explicit QmitkDoseValueDelegate(QObject *parent = 0); /// /// Renders a specific property (overwritten from QItemDelegate) /// void paint(QPainter *painter, const QStyleOptionViewItem &option , const QModelIndex &index) const; /// /// Create an editor for a specific property (overwritten from QItemDelegate) /// QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option , const QModelIndex &index) const; /// /// Create an editor for a specific property (overwritten from QItemDelegate) /// void setEditorData(QWidget *editor, const QModelIndex &index) const; /// /// When the user accepts input this func commits the data to the model (overwritten from QItemDelegate) /// void setModelData(QWidget *editor, QAbstractItemModel* model, const QModelIndex &index) const; }; #endif /* QMITKPROPERTIESTABLEMODEL_H_ */ diff --git a/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.h b/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.h index 2cd3a558ca..d00fe17f17 100644 --- a/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.h +++ b/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.h @@ -1,47 +1,47 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkDoseVisualStyleDelegate_h #define QmitkDoseVisualStyleDelegate_h /// Toolkit includes. #include #include "MitkRTUIExports.h" /** \class QmitkDoseVisualStyleDelegate \brief An item delegate for rendering and editing dose visualization options. The delegate is used to handle aspects of a isodose level like visualization of the isodose lines or colorwash display.*/ class MITKRTUI_EXPORT QmitkDoseVisualStyleDelegate : public QStyledItemDelegate { Q_OBJECT public: - QmitkDoseVisualStyleDelegate(QObject *parent = 0); + explicit QmitkDoseVisualStyleDelegate(QObject *parent = 0); void paint(QPainter *painter, const QStyleOptionViewItem &option , const QModelIndex &index) const; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); }; #endif diff --git a/Modules/RTUI/Qmitk/QmitkFreeIsoDoseLevelWidget.h b/Modules/RTUI/Qmitk/QmitkFreeIsoDoseLevelWidget.h index 8d6c175f66..4e97921ae4 100644 --- a/Modules/RTUI/Qmitk/QmitkFreeIsoDoseLevelWidget.h +++ b/Modules/RTUI/Qmitk/QmitkFreeIsoDoseLevelWidget.h @@ -1,78 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_FREE_ISO_DOSE_LEVEL_WIDGET_H #define QMITK_FREE_ISO_DOSE_LEVEL_WIDGET_H #include "MitkRTUIExports.h" #include "ui_QmitkFreeIsoDoseLevelWidget.h" #include #include "mitkIsoDoseLevel.h" /** * \class QmitkFreeIsoDoseLevelWidget * \brief Widget that allows to show and edit the content of an mitk::IsoDoseLevel instance. */ class MITKRTUI_EXPORT QmitkFreeIsoDoseLevelWidget : public QWidget, private Ui::QmitkFreeIsoDoseLevelWidget { Q_OBJECT public: - QmitkFreeIsoDoseLevelWidget(QWidget* parent=0); + explicit QmitkFreeIsoDoseLevelWidget(QWidget* parent=0); mitk::DoseValueAbs getReferenceDose() const; mitk::IsoDoseLevel* getIsoDoseLevel() const; signals: void ValueChanged(mitk::IsoDoseLevel*, mitk::DoseValueRel oldValue); void ColorChanged(mitk::IsoDoseLevel*); void VisualizationStyleChanged(mitk::IsoDoseLevel*); public Q_SLOTS: /** * \brief Slot that can be used to set the reference dose. */ void setReferenceDose(double newReferenceDose); /** * \brief Slot that can be used to set the dose level instance that should be handled by the widget. */ void setIsoDoseLevel(mitk::IsoDoseLevel* level); void OnRelValueChanged(double newValue); void OnAbsValueChanged(double newValue); void OnSliderChanged(int newValue); void OnVisibleClicked(bool checked); void OnColorChanged(QColor color); protected: /** * \brief Updates the widget according to its current settings. */ void update(); void updateValue(mitk::DoseValueRel newDose); mitk::DoseValueAbs m_ReferenceDose; mitk::IsoDoseLevel::Pointer m_IsoDoseLevel; bool m_InternalUpdate; }; #endif // QmitkFreeIsoDoseLevelWidget_H diff --git a/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h b/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h index af7f19c9f5..c01806c844 100644 --- a/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h +++ b/Modules/RTUI/Qmitk/QmitkIsoDoseLevelSetModel.h @@ -1,98 +1,98 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkIsoDoseLevelSetModel_h #define QmitkIsoDoseLevelSetModel_h #include #include "mitkIsoDoseLevelCollections.h" #include "MitkRTUIExports.h" /*! \class QmitkIsoDoseLevelSetModel Model that handles a iso dose level set and allows viewing and editing of its contents. Please see special delegates (QmitkDoseColorDelegate, QmitkDoseValueDelegate, QmitkDoseVisualStyleDelegate) to handle visualization and editing in views that work on this model. \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. */ class MITKRTUI_EXPORT QmitkIsoDoseLevelSetModel : public QAbstractTableModel { Q_OBJECT public: - QmitkIsoDoseLevelSetModel(QObject *parent = NULL); + explicit QmitkIsoDoseLevelSetModel(QObject *parent = NULL); virtual ~QmitkIsoDoseLevelSetModel() {}; /** Sets the data handled by the model and resets the modified flag*/ void setIsoDoseLevelSet(mitk::IsoDoseLevelSet *pSet); virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual QVariant data(const QModelIndex &index, int role) const; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); bool getShowAbsoluteDose() const; mitk::DoseValueAbs getReferenceDose() const; bool getVisibilityEditOnly() const; void switchVisibilityIsoLines(bool activate); void switchVisibilityColorWash(bool activate); void invertVisibilityIsoLines(); void invertVisibilityColorWash(); void swapVisibility(); void addLevel(); void deleteLevel(const QModelIndex &index); /**Indicates if the content of the model was modified since the data was set via setIsoDoseLevelSet()*/ bool isModified(); public Q_SLOTS: /** * \brief Slot that can be used to set the prescribed dose. */ void setReferenceDose(double newReferenceDose); /** * \brief Slot that can be used to adjust whether the dose should be displayed in absolute or relative units. */ void setShowAbsoluteDose(bool showAbsoluteDose); /** * \brief Slat that can be used to adjust wether the model allows to edit only visibilities (no dose value or color) */ void setVisibilityEditOnly(bool onlyVisibility); private: mitk::IsoDoseLevelSet::Pointer m_DoseSet; bool m_showAbsoluteDose; bool m_visibilityEditOnly; mitk::DoseValueAbs m_referenceDose; /** Indicates if the data of the model was modified, since the model was set. */ bool m_modified; }; #endif // QmitkIsoDoseLevelSetModel_h