diff --git a/Core/Code/DataManagement/mitkBaseData.cpp b/Core/Code/DataManagement/mitkBaseData.cpp index c4e91ec5b4..4080f9ef0a 100644 --- a/Core/Code/DataManagement/mitkBaseData.cpp +++ b/Core/Code/DataManagement/mitkBaseData.cpp @@ -1,355 +1,364 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkBaseData.h" #include #include template class MITK_CORE_EXPORT itk::SmartPointerForwardReference; #define MITK_WEAKPOINTER_PROBLEM_WORKAROUND_ENABLED mitk::BaseData::BaseData() : m_RequestedRegionInitialized(false), m_SmartSourcePointer(NULL), m_SourceOutputIndexDuplicate(0), m_Initialized(true), m_Unregistering(false), m_CalculatingExternalReferenceCount(false), m_ExternalReferenceCount(-1) { m_TimeSlicedGeometry = TimeSlicedGeometry::New(); m_PropertyList = PropertyList::New(); } + mitk::BaseData::BaseData( const BaseData &other ): m_RequestedRegionInitialized(other.m_RequestedRegionInitialized), + m_SmartSourcePointer(other.m_SmartSourcePointer), m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate), + m_Initialized(other.m_Initialized), m_Unregistering(other.m_Unregistering), + m_CalculatingExternalReferenceCount(other.m_CalculatingExternalReferenceCount), + m_ExternalReferenceCount(other.m_ExternalReferenceCount) + { + m_TimeSlicedGeometry = other.m_TimeSlicedGeometry->CloneCopy(); + m_PropertyList = other.m_PropertyList->Clone(); + } mitk::BaseData::~BaseData() { m_SmartSourcePointer = NULL; } void mitk::BaseData::InitializeTimeSlicedGeometry(unsigned int timeSteps) { mitk::TimeSlicedGeometry::Pointer timeGeometry = this->GetTimeSlicedGeometry(); mitk::Geometry3D::Pointer g3d = mitk::Geometry3D::New(); g3d->Initialize(); if ( timeSteps > 1 ) { mitk::ScalarType timeBounds[] = {0.0, 1.0}; g3d->SetTimeBounds( timeBounds ); } // The geometry is propagated automatically to the other items, // if EvenlyTimed is true... timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps ); } void mitk::BaseData::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if(m_TimeSlicedGeometry.IsNotNull()) m_TimeSlicedGeometry->UpdateInformation(); } const mitk::TimeSlicedGeometry* mitk::BaseData::GetUpdatedTimeSlicedGeometry() { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetTimeSlicedGeometry(); } void mitk::BaseData::Expand( unsigned int timeSteps ) { if( m_TimeSlicedGeometry.IsNotNull() ) m_TimeSlicedGeometry->ExpandToNumberOfTimeSteps( timeSteps ); } const mitk::Geometry3D* mitk::BaseData::GetUpdatedGeometry(int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetGeometry(t); } void mitk::BaseData::SetGeometry(Geometry3D* aGeometry3D) { if(aGeometry3D!=NULL) { TimeSlicedGeometry::Pointer timeSlicedGeometry = dynamic_cast(aGeometry3D); if ( timeSlicedGeometry.IsNotNull() ) m_TimeSlicedGeometry = timeSlicedGeometry; else { timeSlicedGeometry = TimeSlicedGeometry::New(); m_TimeSlicedGeometry = timeSlicedGeometry; timeSlicedGeometry->InitializeEvenlyTimed(aGeometry3D, 1); } Modified(); } else if( m_TimeSlicedGeometry.IsNotNull() ) { m_TimeSlicedGeometry = NULL; Modified(); } return; } void mitk::BaseData::SetGeometry(Geometry3D* aGeometry3D, unsigned int time) { if ( m_TimeSlicedGeometry ) m_TimeSlicedGeometry->SetGeometry3D(aGeometry3D, time); } void mitk::BaseData::SetClonedGeometry(const Geometry3D* aGeometry3D) { SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); } void mitk::BaseData::SetClonedGeometry(const Geometry3D* aGeometry3D, unsigned int time) { SetGeometry(static_cast(aGeometry3D->Clone().GetPointer()), time); } bool mitk::BaseData::IsEmpty(unsigned int) const { return IsInitialized() == false; } bool mitk::BaseData::IsEmpty() const { if(IsInitialized() == false) return true; const TimeSlicedGeometry* timeGeometry = const_cast(this)->GetUpdatedTimeSlicedGeometry(); if(timeGeometry == NULL) return true; unsigned int timeSteps = timeGeometry->GetTimeSteps(); for ( unsigned int t = 0 ; t < timeSteps ; ++t ) { if(IsEmpty(t) == false) return false; } return true; } itk::SmartPointerForwardReference mitk::BaseData::GetSource() const { return static_cast(Superclass::GetSource().GetPointer()); } int mitk::BaseData::GetExternalReferenceCount() const { if(m_CalculatingExternalReferenceCount==false) //this is only needed because a smart-pointer to m_Outputs (private!!) must be created by calling GetOutputs. { m_CalculatingExternalReferenceCount = true; m_ExternalReferenceCount = -1; int realReferenceCount = GetReferenceCount(); if(GetSource()==NULL) { m_ExternalReferenceCount = realReferenceCount; m_CalculatingExternalReferenceCount = false; return m_ExternalReferenceCount; } mitk::BaseProcess::DataObjectPointerArray outputs = m_SmartSourcePointer->GetOutputs(); unsigned int idx; for (idx = 0; idx < outputs.size(); ++idx) { //references of outputs that are not referenced from someone else (reference additional to the reference from this BaseProcess object) are interpreted as non-existent if(outputs[idx]==this) --realReferenceCount; } m_ExternalReferenceCount = realReferenceCount; if(m_ExternalReferenceCount<0) m_ExternalReferenceCount=0; m_CalculatingExternalReferenceCount = false; } else return -1; return m_ExternalReferenceCount; } void mitk::BaseData::UnRegister() const { #ifdef MITK_WEAKPOINTER_PROBLEM_WORKAROUND_ENABLED if(GetReferenceCount()>1) { Superclass::UnRegister(); if((m_Unregistering==false) && (m_SmartSourcePointer.IsNotNull())) { m_Unregistering=true; // the order of the following boolean statement is important: // this->GetSource() returns a SmartPointerForwardReference, // which increases and afterwards decreases the reference count, // which may result in an ExternalReferenceCount of 0, causing // BaseProcess::UnRegister() to destroy us (also we already // about to do that). if((this->m_SmartSourcePointer->GetExternalReferenceCount()==0) || (this->GetSource()==NULL)) m_SmartSourcePointer=NULL; // now the reference count is zero and this object has been destroyed; thus nothing may be done after this line!! else m_Unregistering=false; } } else #endif Superclass::UnRegister(); // now the reference count is zero and this object has been destroyed; thus nothing may be done after this line!! } void mitk::BaseData::ConnectSource(itk::ProcessObject *arg, unsigned int idx) const { #ifdef MITK_WEAKPOINTER_PROBLEM_WORKAROUND_ENABLED itkDebugMacro( "connecting source " << arg << ", source output index " << idx); if ( GetSource() != arg || m_SourceOutputIndexDuplicate != idx) { m_SmartSourcePointer = dynamic_cast(arg); m_SourceOutputIndexDuplicate = idx; Modified(); } #endif } mitk::PropertyList::Pointer mitk::BaseData::GetPropertyList() const { return m_PropertyList; } mitk::BaseProperty::Pointer mitk::BaseData::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void mitk::BaseData::SetProperty(const char *propertyKey, BaseProperty* propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::BaseData::SetPropertyList(PropertyList *pList) { m_PropertyList = pList; } void mitk::BaseData::SetOrigin(const mitk::Point3D& origin) { mitk::TimeSlicedGeometry* timeSlicedGeometry = GetTimeSlicedGeometry(); assert(timeSlicedGeometry!=NULL); mitk::Geometry3D* geometry; unsigned int steps = timeSlicedGeometry->GetTimeSteps(); for(unsigned int timestep = 0; timestep < steps; ++timestep) { geometry = GetGeometry(timestep); if(geometry != NULL) { geometry->SetOrigin(origin); } if(GetTimeSlicedGeometry()->GetEvenlyTimed()) { GetTimeSlicedGeometry()->InitializeEvenlyTimed(geometry, steps); break; } } } unsigned long mitk::BaseData::GetMTime() const { unsigned long time = Superclass::GetMTime(); if(m_TimeSlicedGeometry.IsNotNull()) { if((time < m_TimeSlicedGeometry->GetMTime())) { Modified(); return Superclass::GetMTime(); } //unsigned long geometryTime = m_TimeSlicedGeometry->GetMTime(); //if(time < geometryTime) //{ // return geometryTime; //} } return time; } void mitk::BaseData::CopyInformation( const itk::DataObject* data ) { const Self* bd = dynamic_cast(data); if (bd != NULL) { m_TimeSlicedGeometry = dynamic_cast(bd->GetTimeSlicedGeometry()->Clone().GetPointer()); m_PropertyList = bd->GetPropertyList()->Clone(); } else { // pointer could not be cast back down; this can be the case if your filters input // and output objects differ in type; then you have to write your own GenerateOutputInformation method itkExceptionMacro(<< "mitk::BaseData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self*).name() ); } } bool mitk::BaseData::IsInitialized() const { return m_Initialized; } void mitk::BaseData::Clear() { this->ClearData(); this->InitializeEmpty(); } void mitk::BaseData::ClearData() { if(m_Initialized) { ReleaseData(); m_Initialized = false; } } void mitk::BaseData::ExecuteOperation(mitk::Operation* /*operation*/) { //empty by default. override if needed! } void mitk::BaseData::PrintSelf(std::ostream& os, itk::Indent indent) const { os << std::endl; os << indent << " TimeSlicedGeometry: "; if(GetTimeSlicedGeometry() == NULL) os << "NULL" << std::endl; else GetTimeSlicedGeometry()->Print(os, indent); } diff --git a/Core/Code/DataManagement/mitkBaseData.h b/Core/Code/DataManagement/mitkBaseData.h index 75693e884e..d026ad3544 100644 --- a/Core/Code/DataManagement/mitkBaseData.h +++ b/Core/Code/DataManagement/mitkBaseData.h @@ -1,395 +1,397 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #include #include "mitkBaseProcess.h" #include "mitkTimeSlicedGeometry.h" #include "mitkCommon.h" #include "mitkOperationActor.h" #include "mitkPropertyList.h" namespace mitk { class BaseProcess; //##Documentation //## @brief Base of all data objects //## //## Base of all data objects, e.g., images, contours, surfaces etc. Inherits //## from itk::DataObject and thus can be included in a pipeline. //## Inherits also from OperationActor and can be used as a destination for Undo //## @ingroup Data class MITK_CORE_EXPORT BaseData : public itk::DataObject, public OperationActor { public: - mitkClassMacro(BaseData,itk::DataObject) + mitkClassMacro(BaseData,itk::DataObject); + //mitkCloneMacro(BaseData); //##Documentation //## @brief Return the TimeSlicedGeometry of the data as const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. const mitk::TimeSlicedGeometry* GetTimeSlicedGeometry() const { return m_TimeSlicedGeometry.GetPointer(); } //##Documentation //## @brief Return the TimeSlicedGeometry of the data as pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. mitk::TimeSlicedGeometry* GetTimeSlicedGeometry() { return m_TimeSlicedGeometry.GetPointer(); } //##Documentation //## @brief Return the Geometry3D of the data. //## //## The method does not simply return the value of the m_TimeSlicedGeometry //## member. Before doing this, it makes sure that the TimeSlicedGeometry //## is up-to-date (by setting the update extent to largest possible and //## calling UpdateOutputInformation). const mitk::TimeSlicedGeometry* GetUpdatedTimeSlicedGeometry(); //##Documentation //## @brief Expands the TimeSlicedGeometry to a number of TimeSteps. //## //## The method expands the TimeSlicedGeometry to the given number of TimeSteps, //## filling newly created elements with empty geometries. Sub-classes should override //## this method to handle the elongation of their data vectors, too. //## Note that a shrinking is neither possible nor intended. virtual void Expand( unsigned int timeSteps ); //##Documentation //## @brief Return the Geometry3D of the data at time \a t. //## //## The method does not simply return //## m_TimeSlicedGeometry->GetGeometry(t). //## Before doing this, it makes sure that the Geometry3D is up-to-date //## (by setting the update extent appropriately and calling //## UpdateOutputInformation). //## //## @todo Appropriate setting of the update extent is missing. const mitk::Geometry3D* GetUpdatedGeometry(int t=0); //##Documentation //## @brief Return the geometry, which is a TimeSlicedGeometry, of the data //## as non-const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. mitk::Geometry3D* GetGeometry(int t=0) const { if(m_TimeSlicedGeometry.IsNull()) return NULL; return m_TimeSlicedGeometry->GetGeometry3D(t); } //##Documentation //## @brief Helps to deal with the weak-pointer-problem. virtual void UnRegister() const; //##Documentation //## @brief for internal use only. Helps to deal with the //## weak-pointer-problem. virtual int GetExternalReferenceCount() const; //##Documentation //## @brief Update the information for this BaseData (the geometry in particular) //## so that it can be used as an output of a BaseProcess. //## //## This method is used in the pipeline mechanism to propagate information and //## initialize the meta data associated with a BaseData. Any implementation //## of this method in a derived class is assumed to call its source's //## BaseProcess::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. Default implementation simply call's it's source's //## UpdateOutputInformation(). //## \note Implementations of this methods in derived classes must take care //## that the geometry is updated by calling //## GetTimeSlicedGeometry()->UpdateInformation() //## \em after calling its source's BaseProcess::UpdateOutputInformation(). void UpdateOutputInformation(); //##Documentation //## @brief Set the RequestedRegion to the LargestPossibleRegion. //## //## This forces a filter to produce all of the output in one execution //## (i.e. not streaming) on the next call to Update(). void SetRequestedRegionToLargestPossibleRegion()=0; //##Documentation //## @brief Determine whether the RequestedRegion is outside of the BufferedRegion. //## //## This method returns true if the RequestedRegion //## is outside the BufferedRegion (true if at least one pixel is //## outside). This is used by the pipeline mechanism to determine //## whether a filter needs to re-execute in order to satisfy the //## current request. If the current RequestedRegion is already //## inside the BufferedRegion from the previous execution (and the //## current filter is up to date), then a given filter does not need //## to re-execute bool RequestedRegionIsOutsideOfTheBufferedRegion()=0; //##Documentation //## @brief Verify that the RequestedRegion is within the LargestPossibleRegion. //## //## If the RequestedRegion is not within the LargestPossibleRegion, //## then the filter cannot possibly satisfy the request. This method //## returns true if the request can be satisfied (even if it will be //## necessary to process the entire LargestPossibleRegion) and //## returns false otherwise. This method is used by //## PropagateRequestedRegion(). PropagateRequestedRegion() throws a //## InvalidRequestedRegionError exception if the requested region is //## not within the LargestPossibleRegion. virtual bool VerifyRequestedRegion() = 0; //##Documentation //## @brief Copy information from the specified data set. //## //## This method is part of the pipeline execution model. By default, a //## BaseProcess will copy meta-data from the first input to all of its //## outputs. See ProcessObject::GenerateOutputInformation(). Each //## subclass of DataObject is responsible for being able to copy //## whatever meta-data it needs from another DataObject. //## The default implementation of this method copies the time sliced geometry //## and the property list of an object. If a subclass overrides this //## method, it should always call its superclass' version. void CopyInformation(const itk::DataObject* data); //##Documentation //## @brief Check whether the data has been initialized, i.e., //## at least the Geometry and other header data has been set //## //## \warning Set to \a true by default for compatibility reasons. //## Set m_Initialized=false in constructors of sub-classes that //## support distinction between initialized and uninitialized state. virtual bool IsInitialized() const; //##Documentation //## @brief Calls ClearData() and InitializeEmpty(); //## \warning Only use in subclasses that reimplemented these methods. //## Just calling Clear from BaseData will reset an object to a not initialized, //## invalid state. virtual void Clear(); //##Documentation //## @brief Check whether object contains data (at //## a specified time), e.g., a set of points may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty(unsigned int t) const; //##Documentation //## @brief Check whether object contains data (at //## least at one point in time), e.g., a set of points //## may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty() const; //##Documentation //## @brief Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. //## //## This method is implemented in the concrete subclasses of BaseData. void SetRequestedRegion(itk::DataObject *data)=0; //##Documentation //##@brief overwrite if the Data can be called by an Interactor (StateMachine). //## //## Empty by default. Overwrite and implement all the necessary operations here //## and get the necessary information from the parameter operation. void ExecuteOperation(Operation* operation); //##Documentation //## @brief Set the Geometry3D of the data, which will be referenced (not copied!). //## Assumes the data object has only 1 time step ( is a 3D object ). //## //## For convenience (and historic) reasons, it is also possible to set a complete //## mitk::TimeSlicedGeometry*, which will be referenced (not copied!). //## //## @warning This method will normally be called internally by the sub-class of BaseData //## during initialization. //## \sa SetClonedGeometry virtual void SetGeometry(Geometry3D* aGeometry3D); //##Documentation //## @brief Set the Geometry3D of a given time step, which will be referenced (not copied!). //## //## @warning This method will normally be called internally by the sub-class of BaseData //## during initialization. //## \sa SetClonedGeometry virtual void SetGeometry(Geometry3D* aGeometry3D, unsigned int time); //##Documentation //## @brief Set a clone of the provided geometry as Geometry3D of the data. //## Assumes the data object has only 1 time step ( is a 3D object ) //## //## \sa SetGeometry virtual void SetClonedGeometry(const Geometry3D* aGeometry3D); //##Documentation //## @brief Set a clone of the provided geometry as Geometry3D of a given time step. //## //## \sa SetGeometry virtual void SetClonedGeometry(const Geometry3D* aGeometry3D, unsigned int time); //##Documentation //## @brief Get the data's property list //## @sa GetProperty //## @sa m_PropertyList mitk::PropertyList::Pointer GetPropertyList() const; //##Documentation //## @brief Set the data's property list //## @sa SetProperty //## @sa m_PropertyList void SetPropertyList(PropertyList* propertyList); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList, //## and set it to this, respectively; //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const; void SetProperty(const char *propertyKey, BaseProperty* property); //##Documentation //## @brief Convenience method for setting the origin of //## the Geometry3D instances of all time steps //## //## \warning Geometries contained in the Geometry3D will //## \em not be changed, e.g. in case the Geometry3D is a //## SlicedGeometry3D the origin will \em not be propagated //## to the contained slices. The sub-class SlicedData //## does this for the case that the SlicedGeometry3D is //## evenly spaced. virtual void SetOrigin(const Point3D& origin); /** \brief Get the process object that generated this data object. * * If there is no process object, then the data object has * been disconnected from the pipeline, or the data object * was created manually. (Note: we cannot use the GetObjectMacro() * defined in itkMacro because the mutual dependency of * DataObject and ProcessObject causes compile problems. Also, * a forward reference smart pointer is returned, not a smart pointer, * because of the circular dependency between the process and data object.) * * GetSource() returns a SmartPointerForwardReference and not a WeakPointer * because it is assumed the code calling GetSource() wants to hold a * long term reference to the source. */ itk::SmartPointerForwardReference GetSource() const; //##Documentation //## @brief Get the number of time steps from the Timeslicedgeometry //## As the base data has not a data vector given by itself, the number //## of time steps is defined over the time sliced geometry. In sub classes, //## a better implementation could be over the length of the data vector. unsigned int GetTimeSteps() const { return m_TimeSlicedGeometry->GetTimeSteps(); }; //##Documentation //## @brief Get the modified time of the last change of the contents //## this data object or its geometry. virtual unsigned long GetMTime() const; protected: BaseData(); + BaseData(const BaseData &other) ; ~BaseData(); //##Documentation //## @brief Initialize the TimeSlicedGeometry for a number of time steps. //## The TimeSlicedGeometry is initialized empty and evenly timed. //## In many cases it will be necessary to overwrite this in sub-classes. virtual void InitializeTimeSlicedGeometry( unsigned int timeSteps = 1 ); //##Documentation //## @brief reset to non-initialized state, release memory virtual void ClearData(); //##Documentation //## @brief Pure virtual; Must be used in subclasses to get a data object to a //## valid state. Should at least create one empty object and call //## Superclass::InitializeTimeSlicedGeometry() to ensure an existing valid geometry virtual void InitializeEmpty(){}; virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; bool m_RequestedRegionInitialized; bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion; mutable itk::SmartPointer m_SmartSourcePointer; mutable unsigned int m_SourceOutputIndexDuplicate; //##Documentation //## @brief for internal use only. Helps to deal with the //## weak-pointer-problem. virtual void ConnectSource(itk::ProcessObject *arg, unsigned int idx) const; bool m_Initialized; private: //##Documentation //## @brief Helps to deal with the weak-pointer-problem. mutable bool m_Unregistering; //##Documentation //## @brief Helps to deal with the weak-pointer-problem. mutable bool m_CalculatingExternalReferenceCount; //##Documentation //## @brief Helps to deal with the weak-pointer-problem. mutable int m_ExternalReferenceCount; //##Documentation //## @brief PropertyList, f.e. to hold pic-tags, tracking-data,.. //## PropertyList::Pointer m_PropertyList; TimeSlicedGeometry::Pointer m_TimeSlicedGeometry; //##Documentation //## @brief Helps to deal with the weak-pointer-problem. friend class mitk::BaseProcess; }; } // namespace mitk #endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */ diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index b1f2121bce..d5cb696f39 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1435 +1,1434 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkImage.h" #include "mitkHistogramGenerator.h" #include "mitkPicHelper.h" #include "mitkImageTimeSelector.h" #include "ipFunc/mitkIpFunc.h" #include "mitkIpPicTypeMultiplex.h" -#include - #include +#include template class MITK_CORE_EXPORT itk::SmartPointerForwardReference; mitk::Image::Image() : m_Dimension(0), m_Dimensions(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_PixelType(NULL), m_TimeSelectorForExtremaObject(NULL) { m_CountOfMinValuedVoxels.resize(1, 0); m_CountOfMaxValuedVoxels.resize(1, 0); m_ScalarMin.resize(1, itk::NumericTraits::max()); m_ScalarMax.resize(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(1, itk::NumericTraits::max()); m_Scalar2ndMax.resize(1, itk::NumericTraits::NonpositiveMin()); m_Initialized = false; mitk::HistogramGenerator::Pointer generator = mitk::HistogramGenerator::New(); m_HistogramGeneratorObject = generator; } mitk::Image::~Image() { Clear(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 3; m_ReferenceCountLock.Unlock(); m_HistogramGeneratorObject = NULL; m_TimeSelectorForExtremaObject = NULL; m_ReferenceCountLock.Lock(); m_ReferenceCount = 0; m_ReferenceCountLock.Unlock(); delete [] m_OffsetTable; } const mitk::PixelType& mitk::Image::GetPixelType(int /*n*/) const { return m_PixelType; } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if((i>=0) && (i<(int)m_Dimension)) return m_Dimensions[i]; return 1; } void* mitk::Image::GetData() { if(m_Initialized==false) { if(GetSource()==NULL) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } m_CompleteData=GetChannelData(); return m_CompleteData->GetData(); } template void AccessPixel(mitkIpPicDescriptor* pic, const mitk::Index3D& p, double& value, int timestep) { if ( (p[0]>=0 && p[1] >=0 && p[2]>=0 && timestep>=0) && (unsigned int)p[0] < pic->n[0] && (unsigned int)p[1] < pic->n[1] && (unsigned int)p[2] < pic->n[2] && (unsigned int)timestep < pic->n[3] ) { if(pic->bpe!=24) { value = (double) (((T*) pic->data)[ p[0] + p[1]*pic->n[0] + p[2]*pic->n[0]*pic->n[1] + timestep*pic->n[0]*pic->n[1]*pic->n[2] ]); } else { double returnvalue = (((T*) pic->data)[p[0]*3 + 0 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 + timestep*pic->n[0]*pic->n[1]*pic->n[2]*3 ]); returnvalue += (((T*) pic->data)[p[0]*3 + 1 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 + timestep*pic->n[0]*pic->n[1]*pic->n[2]*3]); returnvalue += (((T*) pic->data)[p[0]*3 + 2 + p[1]*pic->n[0]*3 + p[2]*pic->n[0]*pic->n[1]*3 + timestep*pic->n[0]*pic->n[1]*pic->n[2]*3]); value = returnvalue; } } else { value = 0; } }; double mitk::Image::GetPixelValueByIndex(const mitk::Index3D &position, unsigned int timestep) { mitkIpPicDescriptor* pic = this->GetPic(); double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } mitkIpPicTypeMultiplex3(AccessPixel, pic, position, value, timestep); return value; } double mitk::Image::GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep) { mitkIpPicDescriptor* pic = this->GetPic(); double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } Index3D itkIndex; this->GetGeometry()->WorldToIndex(position,itkIndex); mitkIpPicTypeMultiplex3(AccessPixel, pic, itkIndex, value, timestep); return value; } vtkImageData* mitk::Image::GetVtkImageData(int t, int n) { if(m_Initialized==false) { if(GetSource()==NULL) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); if(volume.GetPointer()==NULL || volume->GetVtkImageData() == NULL) return NULL; #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION==4) && (VTK_MINOR_VERSION>=4) )) float *fspacing = const_cast(GetSlicedGeometry(t)->GetFloatSpacing()); double dspacing[3] = {fspacing[0],fspacing[1],fspacing[2]}; volume->GetVtkImageData()->SetSpacing( dspacing ); #else volume->GetVtkImageData()->SetSpacing(const_cast(GetSlicedGeometry(t)->GetFloatSpacing())); #endif return volume->GetVtkImageData(); } mitkIpPicDescriptor* mitk::Image::GetPic() { if(m_Initialized==false) { if(GetSource()==NULL) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } m_CompleteData=GetChannelData(); if(m_CompleteData.GetPointer()==NULL) return NULL; return m_CompleteData->GetPicDescriptor(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return NULL; // slice directly available? int pos=GetSliceIndex(s,t,n); if(m_Slices[pos].GetPointer()!=NULL) return m_Slices[pos]; // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) { sl=new ImageDataItem(*vol, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(m_PixelType.GetBpe()/8)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { sl=new ImageDataItem(*ch, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(m_PixelType.GetBpe()/8)); sl->SetComplete(true); return m_Slices[pos]=sl; } // slice is unavailable. Can we calculate it? if((GetSource()!=NULL) && (GetSource()->Updating()==false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsSliceSet(s,t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData(s,t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateSliceData(s,t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return NULL; ImageDataItemPointer ch, vol; // volume directly available? int pos=GetVolumeIndex(t,n); vol=m_Volumes[pos]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return vol; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) { vol=new ImageDataItem(*ch, 3, data, importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(m_PixelType.GetBpe()/8)); vol->SetComplete(true); return m_Volumes[pos]=vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete=true; unsigned int s; for(s=0;sSetComplete(true); } else { vol=m_Volumes[pos]; // ok, let's combine the slices! if(vol.GetPointer()==NULL) vol=new ImageDataItem(m_PixelType, 3, m_Dimensions, NULL, true); vol->SetComplete(true); size_t size=m_OffsetTable[2]*(m_PixelType.GetBpe()/8); for(s=0;sGetParent()!=vol) { // copy data of slices in volume size_t offset = ((size_t) s)*size; std::memcpy(static_cast(vol->GetData())+offset, sl->GetData(), size); mitkIpPicDescriptor * pic = sl->GetPicDescriptor(); // replace old slice with reference to volume sl=new ImageDataItem(*vol, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*size); sl->SetComplete(true); mitkIpFuncCopyTags(sl->GetPicDescriptor(), pic); m_Slices[posSl]=sl; } } if(vol->GetPicDescriptor()->info->tags_head==NULL) mitkIpFuncCopyTags(vol->GetPicDescriptor(), m_Slices[GetSliceIndex(0,t,n)]->GetPicDescriptor()); } return m_Volumes[pos]=vol; } // volume is unavailable. Can we calculate it? if((GetSource()!=NULL) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); if(IsVolumeSet(t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData(t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateVolumeData(t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return NULL; ImageDataItemPointer ch, vol; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if(IsChannelSet(n)) { // if there is only one time frame we do not need to combine anything if(m_Dimensions[3]<=1) { vol=GetVolumeData(0,n,data,importMemoryManagement); ch=new ImageDataItem(*vol, 3, data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { ch=m_Channels[n]; // ok, let's combine the volumes! if(ch.GetPointer()==NULL) ch=new ImageDataItem(m_PixelType, m_Dimension, m_Dimensions, NULL, true); ch->SetComplete(true); size_t size=m_OffsetTable[m_Dimension-1]*(m_PixelType.GetBpe()/8); unsigned int t; ImageDataItemPointerArray::iterator slicesIt = m_Slices.begin()+n*m_Dimensions[2]*m_Dimensions[3]; for(t=0;tGetParent()!=ch) { // copy data of volume in channel size_t offset = ((size_t) t)*m_OffsetTable[3]*(m_PixelType.GetBpe()/8); std::memcpy(static_cast(ch->GetData())+offset, vol->GetData(), size); mitkIpPicDescriptor * pic = vol->GetPicDescriptor(); // replace old volume with reference to channel vol=new ImageDataItem(*ch, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); mitkIpFuncCopyTags(vol->GetPicDescriptor(), pic); m_Volumes[posVol]=vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull=NULL; for(unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } if(ch->GetPicDescriptor()->info->tags_head==NULL) mitkIpFuncCopyTags(ch->GetPicDescriptor(), m_Volumes[GetVolumeIndex(0,n)]->GetPicDescriptor()); } return m_Channels[n]=ch; } // channel is unavailable. Can we calculate it? if((GetSource()!=NULL) && (GetSource()->Updating()==false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized=true; GetSource()->Update(); // did it work? if(IsChannelSet(n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData(n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateChannelData(n,data,importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; if(m_Slices[GetSliceIndex(s,t,n)].GetPointer()!=NULL) return true; ImageDataItemPointer ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { if(IsValidVolume(t,n)==false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for(s=0;sIsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for(t=0;t(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return false; ImageDataItemPointer sl; if(IsSliceSet(s,t,n)) { sl=GetSliceData(s,t,n,data,importMemoryManagement); if(sl->GetManageMemory()==false) { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; } if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(m_PixelType.GetBpe()/8)); sl->Modified(); //we have changed the data: call Modified()! Modified(); } else { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==NULL) return false; if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(m_PixelType.GetBpe()/8)); //we just added a missing slice, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return false; ImageDataItemPointer vol; if(IsVolumeSet(t,n)) { vol=GetVolumeData(t,n,data,importMemoryManagement); if(vol->GetManageMemory()==false) { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; } if ( vol->GetData() != data ) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(m_PixelType.GetBpe()/8)); vol->Modified(); vol->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==NULL) return false; if ( vol->GetData() != data ) { std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(m_PixelType.GetBpe()/8)); } vol->SetComplete(true); //we just added a missing Volume, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return false; ImageDataItemPointer ch; if(IsChannelSet(n)) { ch=GetChannelData(n,data,importMemoryManagement); if(ch->GetManageMemory()==false) { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; } if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(m_PixelType.GetBpe()/8)); ch->Modified(); ch->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==NULL) return false; if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(m_PixelType.GetBpe()/8)); ch->SetComplete(true); //we just added a missing Channel, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetPicSlice(const mitkIpPicDescriptor *pic, int s, int t, int n, ImportMemoryManagementType /*importMemoryManagement*/) { if(pic==NULL) return false; if(pic->dim!=2) return false; if((pic->n[0]!=m_Dimensions[0]) || (pic->n[1]!=m_Dimensions[1])) return false; if(SetSlice(pic->data,s,t,n)) //@todo: add geometry! { ImageDataItemPointer sl; sl=GetSliceData(s,t,n,NULL,CopyMemory); mitkIpFuncCopyTags(sl->GetPicDescriptor(), const_cast(pic)); return true; } else return false; } bool mitk::Image::SetPicVolume(const mitkIpPicDescriptor *pic, int t, int n, ImportMemoryManagementType /*importMemoryManagement*/) { if(pic==NULL) return false; if((pic->dim==2) && ((m_Dimension==2) || ((m_Dimension>2) && (m_Dimensions[2]==1)))) return SetPicSlice(pic, 0, t, n); if(pic->dim!=3) return false; if((pic->n[0]!=m_Dimensions[0]) || (pic->n[1]!=m_Dimensions[1]) || (pic->n[2]!=m_Dimensions[2])) return false; if(SetVolume(pic->data,t,n)) //@todo: add geometry! { ImageDataItemPointer vol; vol=GetVolumeData(t,n,NULL,CopyMemory); mitkIpFuncCopyTags(vol->GetPicDescriptor(), const_cast(pic)); return true; } else return false; } bool mitk::Image::SetPicChannel(const mitkIpPicDescriptor *pic, int n, ImportMemoryManagementType /*importMemoryManagement*/) { if(pic==NULL) return false; if(pic->dim<=3) return SetPicVolume(pic, 0, n); if(pic->dim!=m_Dimension) return false; unsigned int i; for(i=0;in[i]!=m_Dimensions[i]) return false; } if(SetChannel(pic->data,n)) //@todo: add geometry! { ImageDataItemPointer ch; ch=GetChannelData(n,NULL,CopyMemory); // commented the next line, because // it crashes when called from mitkDICOMFileReader for the Live3D data // mitkIpFuncCopyTags(ch->GetPicDescriptor(), pic); return true; } else return false; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for( it=m_Slices.begin(), end=m_Slices.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Volumes.begin(), end=m_Volumes.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Channels.begin(), end=m_Channels.end(); it!=end; ++it ) { (*it)=NULL; } m_CompleteData = NULL; this->GetTimeSelector(); // just to create m_TimeSelectorForExtremaObject SetRequestedRegionToLargestPossibleRegion(); } mitk::ImageTimeSelector* mitk::Image::GetTimeSelector() const { if(m_TimeSelectorForExtremaObject.IsNull()) { m_TimeSelectorForExtremaObject = ImageTimeSelector::New(); ImageTimeSelector* timeSelector = static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); timeSelector->SetInput(this); this->UnRegister(); } return static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); } void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension=dimension; if(!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for(i=0;i4?m_Dimension:4]; std::memcpy(m_Dimensions, dimensions, sizeof(unsigned int)*m_Dimension); if(m_Dimension<4) { unsigned int *p; for(i=0,p=m_Dimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if(m_LargestPossibleRegion.GetNumberOfPixels()==0) { delete [] m_Dimensions; m_Dimensions = NULL; return; } m_PixelType=type; PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); if(dimension>=4) { TimeBounds timebounds; timebounds[0] = 0.0; timebounds[1] = 1.0; slicedGeometry->SetTimeBounds(timebounds); } TimeSlicedGeometry::Pointer timeSliceGeometry = TimeSlicedGeometry::New(); timeSliceGeometry->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); timeSliceGeometry->ImageGeometryOn(); SetGeometry(timeSliceGeometry); ImageDataItemPointer dnull=NULL; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels()*m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels()*m_Dimensions[3]*m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetExtent(2)+0.5); dimensions[3] = 0; dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( tDim > 0) { dimensions[3] = tDim; } else { const mitk::TimeSlicedGeometry* timeGeometry = dynamic_cast(&geometry); if ( timeGeometry != NULL ) { dimensions[3] = timeGeometry->GetTimeSteps(); } } if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); SetGeometry(static_cast(geometry.Clone().GetPointer())); mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBox()->GetBounds(); if( (bounds[0] != 0.0) || (bounds[2] != 0.0) || (bounds[4] != 0.0) ) { SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); mitk::Point3D origin; origin.Fill(0.0); slicedGeometry->IndexToWorld(origin, origin); bounds[1]-=bounds[0]; bounds[3]-=bounds[2]; bounds[5]-=bounds[4]; bounds[0] = 0.0; bounds[2] = 0.0; bounds[4] = 0.0; slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.Get_vnl_vector().data_block()); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); } } void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(static_cast(geometry2d.Clone().GetPointer()), sDim, flipped); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image* image) { Initialize(*image->GetPixelType().GetTypeId(), *image->GetTimeSlicedGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim) { if(vtkimagedata==NULL) return; m_Dimension=vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetDimensions()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } if(sDim>=0) { tmpDimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { tmpDimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } mitk::PixelType pixelType; switch ( vtkimagedata->GetScalarType() ) { case VTK_BIT: case VTK_CHAR: pixelType.Initialize(typeid(char), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_UNSIGNED_CHAR: pixelType.Initialize(typeid(unsigned char), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_SHORT: pixelType.Initialize(typeid(short), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_UNSIGNED_SHORT: pixelType.Initialize(typeid(unsigned short), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_INT: pixelType.Initialize(typeid(int), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_UNSIGNED_INT: pixelType.Initialize(typeid(unsigned int), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_LONG: pixelType.Initialize(typeid(long), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_UNSIGNED_LONG: pixelType.Initialize(typeid(unsigned long), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_FLOAT: pixelType.Initialize(typeid(float), vtkimagedata->GetNumberOfScalarComponents()); break; case VTK_DOUBLE: pixelType.Initialize(typeid(double), vtkimagedata->GetNumberOfScalarComponents()); break; default: break; } Initialize(pixelType, m_Dimension, tmpDimensions, channels); #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION==4) && (VTK_MINOR_VERSION>=4) )) const double *spacinglist = vtkimagedata->GetSpacing(); #else const float *spacinglist = vtkimagedata->GetSpacing(); #endif Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if(m_Dimension>=2) spacing[1]=spacinglist[1]; if(m_Dimension>=3) spacing[2]=spacinglist[2]; // access origin of vtkImage Point3D origin; vtkFloatingPointType vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=vtkorigin[1]; if(m_Dimension>=3) origin[2]=vtkorigin[2]; SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(slicedGeometry->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); delete [] tmpDimensions; } void mitk::Image::Initialize(const mitkIpPicDescriptor* pic, int channels, int tDim, int sDim) { if(pic==NULL) return; Clear(); m_Dimension=pic->dim; m_Dimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; std::memcpy(m_Dimensions, pic->n, sizeof(unsigned int)*m_Dimension); if(m_Dimension<4) { unsigned int i, *p; for(i=0,p=m_Dimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } if(sDim>=0) { m_Dimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { m_Dimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } unsigned int i; for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); m_PixelType=PixelType(pic); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); PicHelper::InitializeEvenlySpaced(pic, m_Dimensions[2], slicedGeometry); TimeSlicedGeometry::Pointer timeSliceGeometry = TimeSlicedGeometry::New(); timeSliceGeometry->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); timeSliceGeometry->ImageGeometryOn(); SetGeometry(timeSliceGeometry); ImageDataItemPointer dnull=NULL; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels()*m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels()*m_Dimensions[3]*m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if(m_Initialized) return ((s>=0) && (s<(int)m_Dimensions[2]) && (t>=0) && (t< (int) m_Dimensions[3]) && (n>=0) && (n< (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if(m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if(m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if(m_OffsetTable!=NULL) delete [] m_OffsetTable; m_OffsetTable=new size_t[m_Dimension>4 ? m_Dimension+1 : 4+1]; unsigned int i; size_t num=1; m_OffsetTable[0] = 1; for (i=0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i+1] = num; } for (;i < 4; ++i) m_OffsetTable[i+1] = num; } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; return ((size_t)s)+((size_t) t)*m_Dimensions[2]+((size_t) n)*m_Dimensions[3]*m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if(IsValidVolume(t,n)==false) return false; return ((size_t)t)+((size_t) n)*m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetSliceIndex(s,t,n); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if(vol.GetPointer()!=NULL) { sl=new ImageDataItem(*vol, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(m_PixelType.GetBpe()/8)); sl->SetComplete(true); return m_Slices[pos]=sl; } // is slice available as part of a channel that is available? ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { sl=new ImageDataItem(*ch, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(m_PixelType.GetBpe()/8)); sl->SetComplete(true); return m_Slices[pos]=sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t,n)]=vol=AllocateVolumeData(t,n,NULL,importMemoryManagement); sl=new ImageDataItem(*vol, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(m_PixelType.GetBpe()/8)); sl->SetComplete(true); return m_Slices[pos]=sl; ////ALTERNATIVE: //// allocate new slice //sl=new ImageDataItem(m_PixelType, 2, m_Dimensions); //m_Slices[pos]=sl; //return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { int pos; pos=GetVolumeIndex(t,n); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch=m_Channels[n]; if(ch.GetPointer()!=NULL) { vol=new ImageDataItem(*ch, 3, data, importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(m_PixelType.GetBpe()/8)); return m_Volumes[pos]=vol; } // allocate new volume if(importMemoryManagement == CopyMemory) { vol=new ImageDataItem(m_PixelType, 3, m_Dimensions, NULL, true); if(data != NULL) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(m_PixelType.GetBpe()/8)); } else { vol=new ImageDataItem(m_PixelType, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos]=vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { ImageDataItemPointer ch; // allocate new channel if(importMemoryManagement == CopyMemory) { ch=new ImageDataItem(m_PixelType, m_Dimension, m_Dimensions, NULL, true); if(data != NULL) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(m_PixelType.GetBpe()/8)); } else { ch=new ImageDataItem(m_PixelType, m_Dimension, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Channels[n]=ch; return ch; } unsigned int* mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete [] m_Dimensions; m_Dimensions = NULL; } void mitk::Image::SetGeometry(Geometry3D* aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if(aGeometry3D->GetImageGeometry()==false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is pixel-center-based! If it is not, you need to call Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); GetTimeSlicedGeometry()->ImageGeometryOn(); } const mitk::Image::HistogramType* mitk::Image::GetScalarHistogram(int t) const { mitk::ImageTimeSelector* timeSelector = this->GetTimeSelector(); if(timeSelector!=NULL) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); mitk::HistogramGenerator* generator = static_cast(m_HistogramGeneratorObject.GetPointer()); generator->SetImage(timeSelector->GetOutput()); generator->ComputeHistogram(); return static_cast(generator->GetHistogram()); } return NULL; } #include "mitkImageAccessByItk.h" //#define BOUNDINGOBJECT_IGNORE template < typename ItkImageType > void mitk::_ComputeExtremaInItkImage(ItkImageType* itkImage, mitk::Image* mitkImage, int t) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if(region.Crop(itkImage->GetRequestedRegion()) == false) return; if(region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); typedef typename ItkImageType::PixelType TPixel; TPixel value = 0; if ( !mitkImage || !mitkImage->IsValidTimeStep( t ) ) return; mitkImage->Expand(t+1); // make sure we have initialized all arrays mitkImage->m_CountOfMinValuedVoxels[t] = 0; mitkImage->m_CountOfMaxValuedVoxels[t] = 0; mitkImage->m_Scalar2ndMin[t]= mitkImage->m_ScalarMin[t] = itk::NumericTraits::max(); mitkImage->m_Scalar2ndMax[t]= mitkImage->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while( !it.IsAtEnd() ) { value = it.Get(); // if ( (value > mitkImage->m_ScalarMin) && (value < mitkImage->m_Scalar2ndMin) ) mitkImage->m_Scalar2ndMin = value; // else if ( (value < mitkImage->m_ScalarMax) && (value > mitkImage->m_Scalar2ndMax) ) mitkImage->m_Scalar2ndMax = value; // else if (value > mitkImage->m_ScalarMax) mitkImage->m_ScalarMax = value; // else if (value < mitkImage->m_ScalarMin) mitkImage->m_ScalarMin = value; // if numbers start with 2ndMin or 2ndMax and never have that value again, the previous above logic failed #ifdef BOUNDINGOBJECT_IGNORE if( value > -32765) { #endif // update min if ( value < mitkImage->m_ScalarMin[t] ) { mitkImage->m_Scalar2ndMin[t] = mitkImage->m_ScalarMin[t]; mitkImage->m_ScalarMin[t] = value; mitkImage->m_CountOfMinValuedVoxels[t] = 1; } else if ( value == mitkImage->m_ScalarMin[t] ) { ++mitkImage->m_CountOfMinValuedVoxels[t]; } else if ( value < mitkImage->m_Scalar2ndMin[t] ) { mitkImage->m_Scalar2ndMin[t] = value; } // update max if ( value > mitkImage->m_ScalarMax[t] ) { mitkImage->m_Scalar2ndMax[t] = mitkImage->m_ScalarMax[t]; mitkImage->m_ScalarMax[t] = value; mitkImage->m_CountOfMaxValuedVoxels[t] = 1; } else if ( value == mitkImage->m_ScalarMax[t] ) { ++mitkImage->m_CountOfMaxValuedVoxels[t]; } else if ( value > mitkImage->m_Scalar2ndMax[t] ) { mitkImage->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (mitkImage->m_ScalarMax[t] == mitkImage->m_ScalarMin[t]) { mitkImage->m_Scalar2ndMax[t] = mitkImage->m_Scalar2ndMin[t] = mitkImage->m_ScalarMax[t]; } mitkImage->m_LastRecomputeTimeStamp.Modified(); //MITK_DEBUG <<"extrema "<::NonpositiveMin()<<" "<m_ScalarMin<<" "<m_Scalar2ndMin<<" "<m_Scalar2ndMax<<" "<m_ScalarMax<<" "<::max(); } bool mitk::Image::IsValidTimeStep(int t) const { return ( ( m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0 ) || (t == 0) ); } void mitk::Image::Expand( int timeSteps ) const { if(timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); if(! IsValidTimeStep( timeSteps-1 ) ) return; if(timeSteps > (int)m_ScalarMin.size() ) { m_ScalarMin.resize(timeSteps, itk::NumericTraits::max()); m_ScalarMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(timeSteps, itk::NumericTraits::max()); m_Scalar2ndMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.resize(timeSteps, 0); m_CountOfMaxValuedVoxels.resize(timeSteps, 0); } } void mitk::Image::ResetImageStatistics() const { m_ScalarMin.assign(1, itk::NumericTraits::max()); m_ScalarMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.assign(1, itk::NumericTraits::max()); m_Scalar2ndMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.assign(1, 0); m_CountOfMaxValuedVoxels.assign(1, 0); } void mitk::Image::ComputeImageStatistics(int t) const { // timestep valid? if (!IsValidTimeStep(t)) return; // image modified? if (this->GetMTime() > m_LastRecomputeTimeStamp.GetMTime()) this->ResetImageStatistics(); // adapt vector length this->Expand(t+1); // do we have valid information already? if( m_ScalarMin[t] != itk::NumericTraits::max() || m_Scalar2ndMin[t] != itk::NumericTraits::max() ) return; // Values already calculated before... if(this->m_PixelType.GetNumberOfComponents() == 1) { // recompute mitk::ImageTimeSelector* timeSelector = this->GetTimeSelector(); if(timeSelector!=NULL) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); mitk::Image* image = timeSelector->GetOutput(); mitk::Image* thisImage = const_cast(this); AccessByItk_2( image, _ComputeExtremaInItkImage, thisImage, t ); } } else if(this->m_PixelType.GetNumberOfComponents() > 1) { m_ScalarMin[t] = 0; m_ScalarMax[t] = 255; } } mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { ComputeImageStatistics(t); return m_ScalarMin[t]; } mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { ComputeImageStatistics(t); return m_ScalarMax[t]; } mitk::ScalarType mitk::Image::GetScalarValue2ndMin(int t) const { ComputeImageStatistics(t); return m_Scalar2ndMin[t]; } mitk::ScalarType mitk::Image::GetScalarValue2ndMax(int t) const { ComputeImageStatistics(t); return m_Scalar2ndMax[t]; } mitk::ScalarType mitk::Image::GetCountOfMinValuedVoxels(int t) const { ComputeImageStatistics(t); return m_CountOfMinValuedVoxels[t]; } mitk::ScalarType mitk::Image::GetCountOfMaxValuedVoxels(int t) const { ComputeImageStatistics(t); return m_CountOfMaxValuedVoxels[t]; } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { unsigned char i; if(m_Initialized) { os << indent << " PixelType: " << m_PixelType.GetTypeId()->name() << std::endl; os << indent << " BitsPerElement: " << m_PixelType.GetBpe() << std::endl; os << indent << " NumberOfComponents: " << m_PixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << m_PixelType.GetBitsPerComponent() << std::endl; os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for(i=0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os,indent); } bool mitk::Image::IsRotated() const { const mitk::Geometry3D* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); float ref = 0; for(short k = 0; k < 3; ++k) ref += mx[k][k]; ref/=1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for(short i = 0; i < 3; ++i) { for(short j = 0; j < 3; ++j) { if(i != j) { if(abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } diff --git a/Core/Code/DataManagement/mitkImage.h b/Core/Code/DataManagement/mitkImage.h index 74f9337cdb..e97517d487 100644 --- a/Core/Code/DataManagement/mitkImage.h +++ b/Core/Code/DataManagement/mitkImage.h @@ -1,658 +1,659 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include "mitkCommon.h" #include "mitkSlicedData.h" #include "mitkPixelType.h" #include "mitkBaseData.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" +#include "mitkImageDataItem.h" #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace mitk { class SubImageSelector; -class ImageDataItem; +//class ImageDataItem; class ImageTimeSelector; //##Documentation //## @brief Image class for storing images //## //## Can be asked for header information, the data vector, //## the mitkIpPicDescriptor struct or vtkImageData objects. If not the complete //## data is required, the appropriate SubImageSelector class should be used //## for access. //## Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n //## x ND). Channels are for different kind of data, e.g., morphology in //## channel 0, velocities in channel 1. All channels must have the same Geometry! In //## particular, the dimensions of all channels are the same, only the pixel-type //## may differ between channels. //## //## For importing ITK images use of mitk::ITKImageImport is recommended, see //## \ref Adaptor. //## //## For ITK v3.8 and older: Converting coordinates from the ITK physical //## coordinate system (which does not support rotated images) to the MITK world //## coordinate system should be performed via the Geometry3D of the Image, see //## Geometry3D::WorldToItkPhysicalPoint. //## @ingroup Data class MITK_CORE_EXPORT Image : public SlicedData { friend class SubImageSelector; public: mitkClassMacro(Image, SlicedData); itkNewMacro(Self); /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointerForwardReference ImageDataItemPointer; //## @param ImportMemoryManagementType This parameter is evaluated when setting new data to an image. //## The different options are: //## CopyMemory: Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. //## MamageMemory: Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. //## Reference Memory: Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. //## DontManageMemory = ReferenceMemory. enum ImportMemoryManagementType { CopyMemory, ManageMemory, ReferenceMemory, DontManageMemory = ReferenceMemory }; //##Documentation //## @brief Vector container of SmartPointers to ImageDataItems; //## Class is only for internal usage to allow convenient access to all slices over iterators; //## See documentation of ImageDataItem for details. typedef std::vector ImageDataItemPointerArray; typedef itk::Statistics::Histogram HistogramType; public: //##Documentation //## @brief Returns the PixelType of channel @a n. const mitk::PixelType& GetPixelType(int n = 0) const; //##Documentation //## @brief Get dimension of the image //## unsigned int GetDimension() const; //##Documentation //## @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). //## //## @sa GetDimensions() unsigned int GetDimension(int i) const; //## @brief Get the data vector of the complete image, i.e., of all channels linked together. //## //## If you only want to access a slice, volume at a specific time or single channel //## use one of the SubImageSelector classes. virtual void* GetData(); //## @brief Get the pixel value at one specific index position. //## @brief Get the pixel value at one specific position. //## //## The pixel type is always being converted to double. double GetPixelValueByIndex(const mitk::Index3D& position, unsigned int timestep = 0); //## @brief Get the pixel value at one specific world position. //## //## The pixel type is always being converted to double. double GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep = 0); //##Documentation //## @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. virtual vtkImageData* GetVtkImageData(int t = 0, int n = 0); //##Documentation //## @brief Get the complete image, i.e., all channels linked together, as a @a mitkIpPicDescriptor. //## //## If you only want to access a slice, volume at a specific time or single channel //## use one of the SubImageSelector classes. virtual mitkIpPicDescriptor* GetPic(); //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is set virtual bool IsSliceSet(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is set virtual bool IsVolumeSet(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is set virtual bool IsChannelSet(int n = 0) const; //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportSlice with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicSlice, SetImportSlice, SetImportVolume virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportVolume with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicVolume, SetImportVolume virtual bool SetVolume(const void *data, int t = 0, int n = 0); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is copied to an array managed by the image. If the image shall //## reference the data, use SetImportChannel with ImportMemoryManagementType //## set to ReferenceMemory. For importing ITK images use of mitk:: //## ITKImageImport is recommended. //## @sa SetPicChannel, SetImportChannel virtual bool SetChannel(const void *data, int n = 0); //##Documentation //## @brief Set @a data as slice @a s at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a slice (at least is not smaller than a slice), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicSlice virtual bool SetImportSlice(void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data as volume at time @a t in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a volume (at least is not smaller than a volume), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicVolume virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a data in channel @a n. It is in //## the responsibility of the caller to ensure that the data vector @a data //## is really a channel (at least is not smaller than a channel), since there is //## no chance to check this. //## //## The data is managed according to the parameter \a importMemoryManagement. //## @sa SetPicChannel virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a pic as slice @a s at time @a t in channel @a n. //## //## The data is copied to an array managed by the image. //## @todo The corresponding @a Geomety3D and depending @a Geometry2D entries //## are updated according to the information provided in the tags of @a pic. //## @return @a false : dimensions and/or data-type of @a pic does not //## comply with image //## @a true success virtual bool SetPicSlice(const mitkIpPicDescriptor *pic, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a pic as volume at time @a t in channel @a n. //## //## The data is copied to an array managed by the image. //## @todo The corresponding @a Geomety3D and depending @a Geometry2D entries //## are updated according to the information provided in the tags of @a pic. //## @return @a false : dimensions and/or data-type of @a pic does not //## comply with image //## @a true success virtual bool SetPicVolume(const mitkIpPicDescriptor *pic, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## @brief Set @a pic in channel @a n. //## //## The data is copied to an array managed by the image. //## @todo The corresponding @a Geomety3D and depending @a Geometry2D entries //## are updated according to the information provided in the tags of @a pic. //## @return @a false : dimensions and/or data-type of @a pic does not //## comply with image //## @a true success virtual bool SetPicChannel(const mitkIpPicDescriptor *pic, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory ); //##Documentation //## initialize new (or re-initialize) image information //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). virtual void Initialize(const mitk::PixelType& type, unsigned int dimension, unsigned int *dimensions, unsigned int channels = 1); //##Documentation //## initialize new (or re-initialize) image information by a Geometry3D //## //## @param tDim override time dimension (@a n[3]) if @a geometry is a TimeSlicedGeometry (if >0) virtual void Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels = 1, int tDim=-1); //##Documentation //## initialize new (or re-initialize) image information by a Geometry2D and number of slices //## //## Initializes the bounding box according to the width/height of the //## Geometry2D and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. //## The spacing is calculated from the Geometry2D. //## @param tDim override time dimension (@a n[3]) if @a geometry is a TimeSlicedGeometry (if >0) //## \sa SlicedGeometry3D::InitializeEvenlySpaced virtual void Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped = false, unsigned int channels = 1, int tDim=-1); //##Documentation //## initialize new (or re-initialize) image information by another //## mitk-image. //## Only the header is used, not the data vector! //## virtual void Initialize(const mitk::Image* image); //##Documentation //## initialize new (or re-initialize) image information by @a pic. //## Dimensions and @a Geometry3D /@a Geometry2D are set according //## to the tags in @a pic. //## Only the header is used, not the data vector! Use SetPicVolume(pic) //## to set the data vector. //## //## @param tDim override time dimension (@a n[3]) in @a pic (if >0) //## @param sDim override z-space dimension (@a n[2]) in @a pic (if >0) //## @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). virtual void Initialize(const mitkIpPicDescriptor* pic, int channels = 1, int tDim = -1, int sDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a vtkimagedata, //## a vtk-image. //## Only the header is used, not the data vector! Use //## SetVolume(vtkimage->GetScalarPointer()) to set the data vector. //## //## @param tDim override time dimension in @a vtkimagedata (if >0 and <) //## @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) virtual void Initialize(vtkImageData* vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1); //##Documentation //## initialize new (or re-initialize) image information by @a itkimage, //## a templated itk-image. //## Only the header is used, not the data vector! Use //## SetVolume(itkimage->GetBufferPointer()) to set the data vector. //## //## @param tDim override time dimension in @a itkimage (if >0 and <) //## @param sDim override z-space dimension in @a itkimage (if >0 and <) template void InitializeByItk(const itkImageType* itkimage, int channels = 1, int tDim = -1, int sDim=-1) { if(itkimage==NULL) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension=itkimage->GetImageDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetLargestPossibleRegion().GetSize().GetSize()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } // overwrite number of slices if sDim is set if((m_Dimension>2) && (sDim>=0)) tmpDimensions[2]=sDim; // overwrite number of time points if tDim is set if((m_Dimension>3) && (tDim>=0)) tmpDimensions[3]=tDim; // rough initialization of Image Initialize(mitk::PixelType(typeid(typename itkImageType::PixelType)), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType & itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if(m_Dimension >= 2) spacing[1]=itkspacing[1]; if(m_Dimension >= 3) spacing[2]=itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType & itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=itkorigin[1]; if(m_Dimension>=3) origin[2]=itkorigin[2]; // access direction of itk::Image and include spacing const typename itkImageType::DirectionType & itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for( j=0; j < itkDimMax3; ++j ) { columnSum = 0.0; for ( i=0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if(columnSum < mitk::eps) { itkdirectionOk = false; } if ( (spacing[j] < - mitk::eps) // (normally sized) negative value && (j==2) && (m_Dimensions[2] == 1) ) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). // I experienced that ITK produced something meaningful nonetheless because is is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j <<"]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if(itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) if ( i == j ) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = itkdirection[i][j]*spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(GetSlicedGeometry(0)->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeSlicedGeometry GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); // clean-up delete [] tmpDimensions; this->Initialize(); }; //##Documentation //## @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; //##Documentation //## @brief Check whether volume at time @a t in channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidVolume(int t = 0, int n = 0) const; //##Documentation //## @brief Check whether the channel @a n is valid, i.e., //## is (or can be) inside of the image virtual bool IsValidChannel(int n = 0) const; //##Documentation //## @brief Returns true if an image is rotated, i.e. its geometry's //## transformation matrix has nonzero elements besides the diagonal. //## Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. bool IsRotated() const; //##Documentation //## @brief Get the sizes of all dimensions as an integer-array. //## //## @sa GetDimension(int i); unsigned int* GetDimensions() const; //##Documentation //## @brief Sets a geometry to an image. virtual void SetGeometry(Geometry3D* aGeometry3D); virtual const HistogramType* GetScalarHistogram(int t=0) const; //##Documentation //## \brief Get the minimum for scalar images virtual ScalarType GetScalarValueMin(int t=0) const; //##Documentation //## \brief Get the maximum for scalar images virtual ScalarType GetScalarValueMax(int t=0) const; //##Documentation //## \brief Get the second smallest value for scalar images virtual ScalarType GetScalarValue2ndMin(int t=0) const; //##Documentation //## \brief Get the smallest value for scalar images, but do not recompute it first virtual mitk::ScalarType GetScalarValueMinNoRecompute( unsigned int t = 0 ) const { if ( t < m_ScalarMin.size() ) return m_ScalarMin[t]; else return itk::NumericTraits::max(); } //##Documentation //## \brief Get the second smallest value for scalar images, but do not recompute it first virtual mitk::ScalarType GetScalarValue2ndMinNoRecompute( unsigned int t = 0 ) const { if ( t < m_Scalar2ndMin.size() ) return m_Scalar2ndMin[t]; else return itk::NumericTraits::max(); } //##Documentation //## \brief Get the second largest value for scalar images virtual ScalarType GetScalarValue2ndMax(int t=0) const; //##Documentation //## \brief Get the largest value for scalar images, but do not recompute it first virtual mitk::ScalarType GetScalarValueMaxNoRecompute( unsigned int t = 0 ) const { if ( t < m_ScalarMax.size() ) return m_ScalarMax[t]; else return itk::NumericTraits::NonpositiveMin(); } //##Documentation //## \brief Get the second largest value for scalar images, but do not recompute it first virtual mitk::ScalarType GetScalarValue2ndMaxNoRecompute( unsigned int t = 0 ) const { if ( t < m_Scalar2ndMax.size() ) return m_Scalar2ndMax[t]; else return itk::NumericTraits::NonpositiveMin(); } //##Documentation //## \brief Get the count of voxels with the smallest scalar value in the dataset mitk::ScalarType GetCountOfMinValuedVoxels(int t = 0) const; //##Documentation //## \brief Get the count of voxels with the largest scalar value in the dataset mitk::ScalarType GetCountOfMaxValuedVoxels(int t = 0) const; //##Documentation //## \brief Get the count of voxels with the largest scalar value in the dataset virtual unsigned int GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t = 0 ) const { if ( t < m_CountOfMaxValuedVoxels.size() ) return m_CountOfMaxValuedVoxels[t]; else return 0; } //##Documentation //## \brief Get the count of voxels with the smallest scalar value in the dataset virtual unsigned int GetCountOfMinValuedVoxelsNoRecompute( unsigned int t = 0 ) const { if ( t < m_CountOfMinValuedVoxels.size() ) return m_CountOfMinValuedVoxels[t]; else return 0; } //##Documentation //## @warning for internal use only virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); //##Documentation //## @warning for internal use only virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); //##Documentation //## @warning for internal use only virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); template < typename ItkImageType > friend void _ComputeExtremaInItkImage(ItkImageType* itkImage, mitk::Image * mitkImage, int t); protected: int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual void Expand( int timeSteps ) const; virtual bool IsValidTimeStep(int t) const; virtual void ResetImageStatistics() const; virtual void ComputeImageStatistics(int t=0) const; virtual ImageDataItemPointer AllocateSliceData(int s = 0, int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateVolumeData(int t = 0, int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual ImageDataItemPointer AllocateChannelData(int n = 0, void *data = NULL, ImportMemoryManagementType importMemoryManagement = CopyMemory); Image(); virtual ~Image(); virtual void Clear(); //## @warning Has to be called by every Initialize method! virtual void Initialize(); ImageTimeSelector* GetTimeSelector() const; virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; unsigned int m_Dimension; unsigned int *m_Dimensions; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; PixelType m_PixelType; mutable itk::Object::Pointer m_HistogramGeneratorObject; mutable itk::Object::Pointer m_TimeSelectorForExtremaObject; mutable std::vector m_CountOfMinValuedVoxels; mutable std::vector m_CountOfMaxValuedVoxels; mutable std::vector m_ScalarMin; mutable std::vector m_ScalarMax; mutable std::vector m_Scalar2ndMin; mutable std::vector m_Scalar2ndMax; itk::TimeStamp m_LastRecomputeTimeStamp; }; //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const itk::SmartPointer& itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage.GetPointer()); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } //##Documentation //## @brief Cast an itk::Image (with a specific type) to an mitk::Image. //## //## CastToMitkImage does not cast pixel types etc., just image data //## Needs "mitkImage.h" header included. //## If you get a compile error, try image.GetPointer(); //## @ingroup Adaptor //## \sa mitkITKImageImport template void CastToMitkImage(const ItkOutputImageType* itkimage, itk::SmartPointer& mitkoutputimage) { if(mitkoutputimage.IsNull()) { mitkoutputimage = mitk::Image::New(); } mitkoutputimage->InitializeByItk(itkimage); mitkoutputimage->SetChannel(itkimage->GetBufferPointer()); } } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Core/Code/DataManagement/mitkSurface.cpp b/Core/Code/DataManagement/mitkSurface.cpp index cc8eeb0dcf..eb4bac5b69 100644 --- a/Core/Code/DataManagement/mitkSurface.cpp +++ b/Core/Code/DataManagement/mitkSurface.cpp @@ -1,375 +1,382 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkSurface.h" #include "mitkInteractionConst.h" #include "mitkSurfaceOperation.h" #include #include "vtkSmartPointer.h" #include mitk::Surface::Surface() : m_CalculateBoundingBox( false ) { this->InitializeEmpty(); } mitk::Surface::Surface(const mitk::Surface& other) : m_CalculateBoundingBox(other.m_CalculateBoundingBox), m_RequestedRegion(other.m_RequestedRegion), m_LargestPossibleRegion(other.m_LargestPossibleRegion) { - m_PolyDataSeries = std::vector(); - for ( VTKPolyDataSeries::const_iterator it = other.m_PolyDataSeries.begin(); it != other.m_PolyDataSeries.end(); ++it ) + if(other.m_PolyDataSeries.at(0) != NULL) { - vtkSmartPointer poly = vtkSmartPointer::New(); - poly->DeepCopy(*it); - m_PolyDataSeries.push_back(poly.GetPointer()); + m_PolyDataSeries = std::vector(); + for ( VTKPolyDataSeries::const_iterator it = other.m_PolyDataSeries.begin(); it != other.m_PolyDataSeries.end(); ++it ) + { + vtkSmartPointer poly = vtkSmartPointer::New(); + poly->DeepCopy(*it); + m_PolyDataSeries.push_back(poly.GetPointer()); + } + } + else + { + this->InitializeEmpty(); } } mitk::Surface::~Surface() { this->ClearData(); } void mitk::Surface::ClearData() { for ( VTKPolyDataSeries::iterator it = m_PolyDataSeries.begin(); it != m_PolyDataSeries.end(); ++it ) { if ( ( *it ) != NULL ) ( *it )->Delete(); } m_PolyDataSeries.clear(); Superclass::ClearData(); } void mitk::Surface::InitializeEmpty() { vtkPolyData* pdnull = NULL; m_PolyDataSeries.resize( 1, pdnull ); Superclass::InitializeTimeSlicedGeometry(1); m_Initialized = true; } void mitk::Surface::SetVtkPolyData( vtkPolyData* polydata, unsigned int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); if(m_PolyDataSeries[ t ] != NULL) { if ( m_PolyDataSeries[ t ] == polydata ) return; // we do not need the reference on the object any longer m_PolyDataSeries[ t ]->Delete(); } m_PolyDataSeries[ t ] = polydata; // call m_VtkPolyData->Register(NULL) to tell // the reference counting that we want to keep a // reference on the object if(m_PolyDataSeries[ t ] != NULL) { m_PolyDataSeries[ t ]->Register( NULL ); } this->Modified(); m_CalculateBoundingBox = true; } bool mitk::Surface::IsEmpty(unsigned int t) const { if(!IsInitialized()) return false; vtkPolyData* polydata = const_cast(this)->GetVtkPolyData(t); return (polydata == NULL) || ( (polydata->GetNumberOfVerts() <= 0) && (polydata->GetNumberOfPolys() <= 0) && (polydata->GetNumberOfStrips() <= 0) && (polydata->GetNumberOfLines() <= 0) ); } vtkPolyData* mitk::Surface::GetVtkPolyData( unsigned int t ) { if ( t < m_PolyDataSeries.size() ) { vtkPolyData* polydata = m_PolyDataSeries[ t ]; if((polydata==NULL) && (GetSource().GetPointer()!=NULL)) { RegionType requestedregion; requestedregion.SetIndex(3, t); requestedregion.SetSize(3, 1); SetRequestedRegion(&requestedregion); GetSource()->Update(); } polydata = m_PolyDataSeries[ t ]; return polydata; } else return NULL; } void mitk::Surface::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if ( ( m_CalculateBoundingBox ) && ( m_PolyDataSeries.size() > 0 ) ) CalculateBoundingBox(); else GetTimeSlicedGeometry()->UpdateInformation(); } void mitk::Surface::CalculateBoundingBox() { // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as vtkPolyDatas are present // mitk::TimeSlicedGeometry* timeGeometry = GetTimeSlicedGeometry(); if ( timeGeometry->GetTimeSteps() != m_PolyDataSeries.size() ) { itkExceptionMacro(<<"timeGeometry->GetTimeSteps() != m_PolyDataSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // // Iterate over the vtkPolyDatas and update the Geometry // information of each of the items. // for ( unsigned int i = 0 ; i < m_PolyDataSeries.size() ; ++i ) { vtkPolyData* polyData = m_PolyDataSeries[ i ]; vtkFloatingPointType bounds[ ] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; if ( ( polyData != NULL ) && ( polyData->GetNumberOfPoints() > 0 ) ) { polyData->Update(); polyData->ComputeBounds(); polyData->GetBounds( bounds ); } mitk::Geometry3D::Pointer g3d = timeGeometry->GetGeometry3D( i ); assert( g3d.IsNotNull() ); g3d->SetFloatBounds( bounds ); } timeGeometry->UpdateInformation(); mitk::BoundingBox::Pointer bb = const_cast( timeGeometry->GetBoundingBox() ); itkDebugMacro( << "boundingbox min: "<< bb->GetMinimum()); itkDebugMacro( << "boundingbox max: "<< bb->GetMaximum()); m_CalculateBoundingBox = false; } void mitk::Surface::SetRequestedRegionToLargestPossibleRegion() { m_RequestedRegion = GetLargestPossibleRegion(); } bool mitk::Surface::RequestedRegionIsOutsideOfTheBufferedRegion() { RegionType::IndexValueType end = m_RequestedRegion.GetIndex(3)+m_RequestedRegion.GetSize(3); if(((RegionType::IndexValueType)m_PolyDataSeries.size())=0) && (m_RequestedRegion.GetIndex(3)+m_RequestedRegion.GetSize(3)<=m_PolyDataSeries.size()) ) return true; return false; } void mitk::Surface::SetRequestedRegion( itk::DataObject *data ) { mitk::Surface *surfaceData; surfaceData = dynamic_cast(data); if (surfaceData) { m_RequestedRegion = surfaceData->GetRequestedRegion(); } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::Surface::SetRequestedRegion(DataObject*) cannot cast " << typeid(data).name() << " to " << typeid(Surface*).name() ); } } void mitk::Surface::SetRequestedRegion(Surface::RegionType *region) //by arin { if(region!=NULL) { m_RequestedRegion = *region; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::Surface::SetRequestedRegion(Surface::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(Surface*).name() ); } } void mitk::Surface::CopyInformation( const itk::DataObject * data) { Superclass::CopyInformation( data ); const mitk::Surface* surfaceData; surfaceData = dynamic_cast( data ); if ( surfaceData ) { m_LargestPossibleRegion = surfaceData->GetLargestPossibleRegion(); } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::Surface::CopyInformation(const DataObject *data) cannot cast " << typeid(data).name() << " to " << typeid(surfaceData).name() ); } } void mitk::Surface::Update() { if ( GetSource() == NULL ) { for ( VTKPolyDataSeries::iterator it = m_PolyDataSeries.begin() ; it != m_PolyDataSeries.end() ; ++it ) { if ( ( *it ) != NULL ) ( *it )->Update(); } } Superclass::Update(); } void mitk::Surface::Expand( unsigned int timeSteps ) { // check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient zero-filled elements. if ( timeSteps > m_PolyDataSeries.size() ) { Superclass::Expand( timeSteps ); vtkPolyData* pdnull = NULL; m_PolyDataSeries.resize( timeSteps, pdnull ); m_CalculateBoundingBox = true; } } void mitk::Surface::ExecuteOperation(Operation *operation) { switch ( operation->GetOperationType() ) { case OpSURFACECHANGED: mitk::SurfaceOperation* surfOp = dynamic_cast(operation); if( ! surfOp ) break; unsigned int time = surfOp->GetTimeStep(); if(m_PolyDataSeries[ time ] != NULL) { vtkPolyData* updatePoly = surfOp->GetVtkPolyData(); if( updatePoly ){ this->SetVtkPolyData( updatePoly, time ); this->CalculateBoundingBox(); } } break; } this->Modified(); } unsigned int mitk::Surface::GetSizeOfPolyDataSeries() const { return m_PolyDataSeries.size(); } void mitk::Surface::Graft( const DataObject* data ) { const Self* surface; try { surface = dynamic_cast( data ); } catch(...) { itkExceptionMacro( << "mitk::Surface::Graft cannot cast " << typeid(data).name() << " to " << typeid(const Self *).name() ); return; } if(!surface) { // pointer could not be cast back down itkExceptionMacro( << "mitk::Surface::Graft cannot cast " << typeid(data).name() << " to " << typeid(const Self *).name() ); return; } this->CopyInformation( data ); //clear list of PolyData's m_PolyDataSeries.clear(); // do copy for (unsigned int i=0; iGetSizeOfPolyDataSeries(); i++) { m_PolyDataSeries.push_back(vtkPolyData::New()); m_PolyDataSeries.back()->DeepCopy( const_cast(surface)->GetVtkPolyData( i ) ); //CopyStructure( const_cast(surface)->GetVtkPolyData( i ) ); } } void mitk::Surface::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "\nNumber PolyDatas: " << m_PolyDataSeries.size() << "\n"; unsigned int count = 0; for (VTKPolyDataSeries::const_iterator it = m_PolyDataSeries.begin(); it != m_PolyDataSeries.end(); ++it) { vtkPolyData* pd = *it; if(pd != NULL) { os << "\n"; os << indent << "PolyData at time step " << count << ". \n"; os << indent << "Number of cells " << pd->GetNumberOfCells() << ": \n"; os << indent << "Number of points " << pd->GetNumberOfPoints() << ": \n\n"; os << indent << "VTKPolyData : \n"; pd->Print(os); } else os << indent << "\nEmpty PolyData at time step " << count << ".\n"; count++; } } \ No newline at end of file diff --git a/Core/Code/DataManagement/mitkSurface.h b/Core/Code/DataManagement/mitkSurface.h index 805edf389e..450183ac1a 100644 --- a/Core/Code/DataManagement/mitkSurface.h +++ b/Core/Code/DataManagement/mitkSurface.h @@ -1,120 +1,116 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKSURFACEDATA_H_HEADER_INCLUDED #define MITKSURFACEDATA_H_HEADER_INCLUDED #include "mitkBaseData.h" #include "itkImageRegion.h" class vtkPolyData; namespace mitk { //##Documentation //## @brief Class for storing surfaces (vtkPolyData) //## @ingroup Data class MITK_CORE_EXPORT Surface : public BaseData { protected: public: // not yet the best chioce of a region-type for surfaces, but it works for the time being typedef itk::ImageRegion< 5 > RegionType; mitkClassMacro(Surface, BaseData); - itkNewMacro(Self); - mitkCloneMacro(Surface); virtual void SetVtkPolyData(vtkPolyData* polydata, unsigned int t = 0); virtual vtkPolyData* GetVtkPolyData(unsigned int t = 0); virtual void UpdateOutputInformation(); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual bool VerifyRequestedRegion(); virtual void SetRequestedRegion(itk::DataObject *data); virtual void SetRequestedRegion(Surface::RegionType *region); virtual void CopyInformation(const itk::DataObject *data); virtual bool IsEmpty(unsigned int t) const; unsigned int GetSizeOfPolyDataSeries() const; virtual void Update(); virtual void Expand( unsigned int timeSteps = 1 ); virtual void Graft( const DataObject* data ); const RegionType& GetLargestPossibleRegion() const { m_LargestPossibleRegion.SetIndex(3, 0); m_LargestPossibleRegion.SetSize(3, GetTimeSlicedGeometry()->GetTimeSteps()); return m_LargestPossibleRegion; } //##Documentation //## Get the region object that defines the size and starting index //## for the region of the image requested (i.e., the region of the //## image to be operated on by a filter). virtual const RegionType& GetRequestedRegion() const { return m_RequestedRegion; } void CalculateBoundingBox(); virtual void PrintSelf( std::ostream& os, itk::Indent indent ) const; virtual void ExecuteOperation(Operation *operation); protected: typedef std::vector< vtkPolyData* > VTKPolyDataSeries; Surface(); Surface(const Surface& other); virtual ~Surface(); virtual void ClearData(); virtual void InitializeEmpty(); - VTKPolyDataSeries m_PolyDataSeries; - - mutable RegionType m_LargestPossibleRegion; - + //member variables + VTKPolyDataSeries m_PolyDataSeries; /// variable holds the poly datas of the surface + mutable RegionType m_LargestPossibleRegion; /// variable holds the largest possible region the surface is contained in RegionType m_RequestedRegion; - - bool m_CalculateBoundingBox; + bool m_CalculateBoundingBox; /// flag to calculate the bounding box }; } // namespace mitk #endif /* MITKSURFACEDATA_H_HEADER_INCLUDED */ diff --git a/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp b/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp index cc53a4c0c1..d4bfdc26ba 100644 --- a/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp +++ b/Core/Code/DataManagement/mitkTimeSlicedGeometry.cpp @@ -1,418 +1,426 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkTimeSlicedGeometry.h" void mitk::TimeSlicedGeometry::UpdateInformation() { if(m_TimeSteps==0) return; unsigned long maxModifiedTime = 0, curModifiedTime; mitk::ScalarType stmin, stmax; stmin= ScalarTypeNumericTraits::NonpositiveMin(); stmax= ScalarTypeNumericTraits::max(); TimeBounds timeBounds; timeBounds[0]=stmax; timeBounds[1]=stmin; mitk::BoundingBox::Pointer boundingBox=mitk::BoundingBox::New(); mitk::BoundingBox::PointsContainer::Pointer pointscontainer=mitk::BoundingBox::PointsContainer::New(); unsigned int t; mitk::Geometry3D* geometry3d; mitk::BoundingBox::ConstPointer nextBoundingBox; mitk::BoundingBox::PointIdentifier pointid=0; // Need to check for zero bounding boxes mitk::ScalarType zeropoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsZero(zeropoint); for(t=0; t < m_TimeSteps; ++t) { geometry3d = GetGeometry3D(t); assert(geometry3d!=NULL); curModifiedTime = geometry3d->GetMTime(); if(maxModifiedTime < curModifiedTime) maxModifiedTime = curModifiedTime; const TimeBounds & curTimeBounds = geometry3d->GetTimeBounds(); if((curTimeBounds[0] > stmin) && (curTimeBounds[0] < timeBounds[0])) timeBounds[0] = curTimeBounds[0]; if((curTimeBounds[1] < stmax) && (curTimeBounds[1] > timeBounds[1])) timeBounds[1] = curTimeBounds[1]; nextBoundingBox = geometry3d->GetBoundingBox(); assert(nextBoundingBox.IsNotNull()); // Only respect non-zero BBes if (nextBoundingBox->GetBounds() == itkBoundsZero) { continue; } const mitk::BoundingBox::PointsContainer * nextPoints = nextBoundingBox->GetPoints(); if(nextPoints!=NULL) { mitk::BoundingBox::PointsContainer::ConstIterator pointsIt = nextPoints->Begin(); while (pointsIt != nextPoints->End() ) { pointscontainer->InsertElement( pointid++, pointsIt->Value()); ++pointsIt; } } } if(!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } m_TimeBounds = timeBounds; assert(timeBounds[0]<=timeBounds[1]); boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); m_BoundingBox = boundingBox; SetIndexToWorldTransform(GetGeometry3D(0)->GetIndexToWorldTransform()); if(this->GetMTime() < maxModifiedTime) Modified(); } mitk::Geometry3D* mitk::TimeSlicedGeometry::GetGeometry3D(int t) const { mitk::Geometry3D::Pointer geometry3d = NULL; if(IsValidTime(t)) { geometry3d = m_Geometry3Ds[t]; //if (a) we don't have a Geometry3D stored for the requested time, //(b) m_EvenlyTimed is activated and (c) the first geometry (t=0) //is set, then we clone the geometry and set the m_TimeBounds accordingly. if((m_EvenlyTimed) && (geometry3d.IsNull())) { const Geometry3D* firstgeometry=m_Geometry3Ds[0].GetPointer(); assert(firstgeometry != NULL); mitk::Geometry3D::Pointer requestedgeometry; requestedgeometry = dynamic_cast(firstgeometry->Clone().GetPointer()); if ( requestedgeometry.IsNull() ) itkExceptionMacro("Geometry is NULL!"); TimeBounds timebounds = requestedgeometry->GetTimeBounds(); if(timebounds[1]SetTimeBounds(timebounds); } geometry3d = requestedgeometry; m_Geometry3Ds[t] = geometry3d; } } else return NULL; return geometry3d; } bool mitk::TimeSlicedGeometry::SetGeometry3D(mitk::Geometry3D* geometry3D, int t) { if(IsValidTime(t)) { m_Geometry3Ds[t]=geometry3D; return true; } return false; } int mitk::TimeSlicedGeometry::MSToTimeStep(mitk::ScalarType time_in_ms) const { if(time_in_ms < m_TimeBounds[0]) return -1; if(time_in_ms >= m_TimeBounds[1]) return m_TimeSteps; if(m_EvenlyTimed) { if(m_TimeBounds[0] == m_TimeBounds[1]) return 0; if((m_TimeBounds[0]>ScalarTypeNumericTraits::NonpositiveMin()) && (m_TimeBounds[1]GetTimeBounds(); if( (timeBounds[0] <= time_in_ms) && (time_in_ms <= timeBounds[1]) ) { return t; } } } return 0; } mitk::ScalarType mitk::TimeSlicedGeometry::TimeStepToMS(int timestep) const { if(IsValidTime(timestep)==false) return ScalarTypeNumericTraits::max(); if(m_EvenlyTimed) { if ( timestep == 0 ) return m_TimeBounds[0]; else { assert( ! (m_TimeBounds[0] == ScalarTypeNumericTraits::NonpositiveMin() && m_TimeBounds[1] == ScalarTypeNumericTraits::max() ) ); return ((mitk::ScalarType)timestep)/m_TimeSteps*(m_TimeBounds[1]-m_TimeBounds[0])+m_TimeBounds[0]; } } else { return GetGeometry3D(timestep)->GetTimeBounds()[0]; } } int mitk::TimeSlicedGeometry::TimeStepToTimeStep( const mitk::TimeSlicedGeometry *referenceGeometry, int t) const { int timeStep; if ( referenceGeometry->GetTimeSteps() > 1 ) { // referenceGeometry is nD+t timeStep = this->MSToTimeStep( referenceGeometry->TimeStepToMS( t ) ); } else { // referenceGEometry is nD (only one time step) timeStep = 0; } return timeStep; } void mitk::TimeSlicedGeometry::Initialize(unsigned int timeSteps) { Geometry3D::Pointer geometry3D = Geometry3D::New(); geometry3D->Initialize(); InitializeEvenlyTimed(geometry3D, timeSteps); } void mitk::TimeSlicedGeometry::InitializeEvenlyTimed(mitk::Geometry3D* geometry3D, unsigned int timeSteps) { assert(geometry3D!=NULL); geometry3D->Register(); InitializeEmpty(timeSteps); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(geometry3D->GetIndexToWorldTransform()->GetMatrix()); transform->SetOffset(geometry3D->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); SetBounds(geometry3D->GetBounds()); SetGeometry3D(geometry3D, 0); SetEvenlyTimed(); UpdateInformation(); SetFrameOfReferenceID(geometry3D->GetFrameOfReferenceID()); SetImageGeometry(geometry3D->GetImageGeometry()); geometry3D->UnRegister(); } void mitk::TimeSlicedGeometry::InitializeEmpty(unsigned int timeSteps) { m_IndexToWorldTransform = NULL; Superclass::Initialize(); m_TimeSteps = timeSteps; // initialize with empty geometries Geometry3D::Pointer gnull=NULL; m_Geometry3Ds.assign(m_TimeSteps, gnull); } void mitk::TimeSlicedGeometry::ExpandToNumberOfTimeSteps( unsigned int timeSteps ) { if( timeSteps <= m_TimeSteps ) return; if(m_TimeSteps == 1) { Geometry3D* g3d = m_Geometry3Ds[0]; const TimeBounds & timeBounds = g3d->GetTimeBounds(); if( (timeBounds[0] == ScalarTypeNumericTraits::NonpositiveMin()) || (timeBounds[1]==ScalarTypeNumericTraits::max()) ) { mitk::ScalarType timeBounds[] = {0.0, 1.0}; m_Geometry3Ds[0]->SetTimeBounds( timeBounds ); } } // Expand to Number of time steps; initialize with empty geometries Geometry3D::Pointer gnull=NULL; m_Geometry3Ds.resize(timeSteps, gnull); m_TimeSteps = timeSteps; UpdateInformation(); } mitk::TimeSlicedGeometry::TimeSlicedGeometry() : m_TimeSteps(0), m_EvenlyTimed(false) { } mitk::TimeSlicedGeometry::~TimeSlicedGeometry() { } void mitk::TimeSlicedGeometry::SetImageGeometry(const bool isAnImageGeometry) { Superclass::SetImageGeometry(isAnImageGeometry); mitk::Geometry3D* geometry3d; unsigned int t; for(t=0; tSetImageGeometry(isAnImageGeometry); } } void mitk::TimeSlicedGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { mitk::Geometry3D* geometry3d; unsigned int t; for(t=0; tChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } Superclass::ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } void mitk::TimeSlicedGeometry::SetEvenlyTimed(bool on) { m_EvenlyTimed = on; Modified(); } bool mitk::TimeSlicedGeometry::IsValidTime(int t) const { return (t>=0) && (t< (int)m_TimeSteps); } void mitk::TimeSlicedGeometry::CopyTimes(const mitk::TimeSlicedGeometry* timeslicedgeometry, unsigned int t, unsigned int endtimeindex) { if(endtimeindex >= timeslicedgeometry->GetTimeSteps()) endtimeindex = timeslicedgeometry->GetTimeSteps()-1; if(endtimeindex >= this->GetTimeSteps()) endtimeindex = this->GetTimeSteps()-1; for(; t <= endtimeindex; ++t) { mitk::Geometry3D* geometry3d = GetGeometry3D(t); mitk::Geometry3D* othergeometry3d = timeslicedgeometry->GetGeometry3D(t); assert((geometry3d!=NULL) && (othergeometry3d!=NULL)); geometry3d->SetTimeBounds(othergeometry3d->GetTimeBounds()); } UpdateInformation(); } mitk::AffineGeometryFrame3D::Pointer mitk::TimeSlicedGeometry::Clone() const { Self::Pointer newGeometry = Self::New(); newGeometry->Initialize(m_TimeSteps); InitializeGeometry(newGeometry); return newGeometry.GetPointer(); } +mitk::TimeSlicedGeometry::Pointer mitk::TimeSlicedGeometry::CloneCopy() const +{ + Self::Pointer newGeometry = Self::New(); + newGeometry->Initialize(m_TimeSteps); + InitializeGeometry(newGeometry); + return newGeometry.GetPointer(); +} + void mitk::TimeSlicedGeometry::InitializeGeometry(Self * newGeometry) const { Superclass::InitializeGeometry(newGeometry); newGeometry->SetEvenlyTimed(m_EvenlyTimed); unsigned int t; for(t=0; tSetGeometry3D(dynamic_cast(m_Geometry3Ds[t]->Clone().GetPointer()), t); } } } void mitk::TimeSlicedGeometry::PrintSelf(std::ostream& os, itk::Indent indent) const { //Superclass::PrintSelf(os,indent); os << indent << " EvenlyTimed: " << m_EvenlyTimed << std::endl; os << indent << " TimeSteps: " << m_TimeSteps << std::endl; os << std::endl; os << indent << " GetGeometry3D(0): "; if(GetGeometry3D(0)==NULL) os << "NULL" << std::endl; else GetGeometry3D(0)->Print(os, indent); } void mitk::TimeSlicedGeometry::ExecuteOperation(Operation* operation) { // reach through to all time steps for (std::vector::iterator iter = m_Geometry3Ds.begin(); iter != m_Geometry3Ds.end(); ++iter) { (*iter)->ExecuteOperation(operation); } Geometry3D::ExecuteOperation(operation); this->Modified(); } diff --git a/Core/Code/DataManagement/mitkTimeSlicedGeometry.h b/Core/Code/DataManagement/mitkTimeSlicedGeometry.h index 68e273fa55..8e1f0374a2 100644 --- a/Core/Code/DataManagement/mitkTimeSlicedGeometry.h +++ b/Core/Code/DataManagement/mitkTimeSlicedGeometry.h @@ -1,179 +1,179 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef TIMESLICEDGEOMETRY_H_HEADER_INCLUDED_C1EBD0AD #define TIMESLICEDGEOMETRY_H_HEADER_INCLUDED_C1EBD0AD #include "mitkGeometry3D.h" namespace mitk { //##Documentation //## @brief Describes a geometry consisting of several geometries which //## exist at different times. //## //## The geometry contains m_TimeSteps geometries, which can be accessed //## using GetGeometry3D(int t). To convert between world-time in //## milliseconds and the integer timestep-number use MSToTimeStep. //## The hull (in space and time) of the TimeSlicedGeometry contains all //## contained geometries. //## @warning The hull (i.e., transform, bounding-box and //## time-bounds) is only guaranteed to be up-to-date after calling //## UpdateInformation(). //## //## TimeSlicedGeometry and the associated Geometry3Ds have to be //## initialized in the method GenerateOutputInformation() of BaseProcess (or //## CopyInformation/ UpdateOutputInformation of BaseData, if possible, e.g., //## by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## @ingroup Geometry class MITK_CORE_EXPORT TimeSlicedGeometry : public Geometry3D { public: mitkClassMacro(TimeSlicedGeometry, Geometry3D); itkNewMacro(Self); //##Documentation //## @brief Re-calculate the hull of the contained geometries. //## //## The transforms, bounding-box and time-bounds of this //## geometry (stored in members of the super-class Geometry3D) //## are re-calculated from the contained geometries. void UpdateInformation(); //##Documentation //## @brief Get the number of time-steps itkGetConstMacro(TimeSteps, unsigned int); //##Documentation //## @brief Set/Get whether the TimeSlicedGeometry is evenly-timed (m_EvenlyTimed) //## //## If (a) we don't have a Geometry3D stored for the requested time, //## (b) m_EvenlyTimed is activated and (c) the first geometry (t=0) //## is set, then we clone the geometry and set the m_TimeBounds accordingly. //## \sa GetGeometry3D itkGetConstMacro(EvenlyTimed, bool); virtual void SetEvenlyTimed(bool on = true); //##Documentation //## @brief Set the Geometry3D for time @a t virtual bool SetGeometry3D(mitk::Geometry3D* geometry3D, int t); //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to change the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ); //##Documentation //## @brief Get the Geometry3D at time @a t virtual mitk::Geometry3D* GetGeometry3D(int t) const; //##Documentation //## @brief Test whether @a t is a valid time step virtual bool IsValidTime(int t) const; //##Documentation //## @brief Convert time in ms to a time step virtual int MSToTimeStep(mitk::ScalarType time_in_ms) const; //##Documentation //## @brief Convert time step to time in ms virtual mitk::ScalarType TimeStepToMS(int timestep) const; //##Documentation //## @brief Convert time step in the reference TimeSlicedGeometry to time step //## in this TimeSlicedGeometry. virtual int TimeStepToTimeStep(const mitk::TimeSlicedGeometry *referenceGeometry, int t) const; //##Documentation //## @brief Completely initialize this instance as evenly-timed with //## \a timeSteps geometries of type Geometry3D, each initialized by //## Geometry3D::Initialize(). virtual void Initialize(unsigned int timeSteps); //##Documentation //## @brief Completely initialize this instance as evenly-timed with //## \a timeSteps geometries identical to the provided Geometry3D //## except for the time bounds virtual void InitializeEvenlyTimed(mitk::Geometry3D* geometry3D, unsigned int timeSteps); //##Documentation //## @brief Initialize this instance to contain \a timeSteps //## geometries, but without setting them yet virtual void InitializeEmpty(unsigned int timeSteps); //##Documentation //## @brief Expand the number of time steps contained //## to \a timeSteps. //## //## New, additional time steps will be initialized empty. //## Only enlargement of the time steps vector is intended and possible. virtual void ExpandToNumberOfTimeSteps( unsigned int timeSteps ); virtual void SetImageGeometry(const bool isAnImageGeometry); //##Documentation //## @brief Copy the m_TimeBounds of the geometries contained //## in timeslicedgeometry into the geometries contained in this //## TimeSlicedGeometry object. //## //## Useful for initialization of the TimeSlicedGeometry of the //## output in GenerateOutputInformation() methods of process objects, //## see for example BoundingObjectCutter::GenerateOutputInformation(). //## @param t start time index //## @param endtimeindex (endtimeindex) is the time index of //## the last geometry whose time-bounds are copied. If //## timeslicedgeometry or this TimeSlicedGeometry object does //## not contain enough geometries, endtimeindex is reduced //## appropriately. void CopyTimes(const mitk::TimeSlicedGeometry* timeslicedgeometry, unsigned int t=0, unsigned int endtimeindex = itk::NumericTraits::max()); //##Documentation //## @brief duplicates the geometry virtual AffineGeometryFrame3D::Pointer Clone() const; - + TimeSlicedGeometry::Pointer CloneCopy() const; virtual void ExecuteOperation(Operation* operation); protected: TimeSlicedGeometry(); virtual ~TimeSlicedGeometry(); void InitializeGeometry(Self * newGeometry) const; virtual void PrintSelf(std::ostream& os, itk::Indent indent) const; mutable std::vector m_Geometry3Ds; //##Documentation //## @brief Number of time steps unsigned int m_TimeSteps; //##Documentation //## @brief \a true in case the time steps have equal length bool m_EvenlyTimed; static const std::string EVENLY_TIMED; static const std::string TIME_STEPS; }; } // namespace mitk #endif /* TIMESLICEDGEOMETRY_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Core/Code/Testing/mitkSurfaceTest.cpp b/Core/Code/Testing/mitkSurfaceTest.cpp index c3a8c45b20..34c6cb4271 100644 --- a/Core/Code/Testing/mitkSurfaceTest.cpp +++ b/Core/Code/Testing/mitkSurfaceTest.cpp @@ -1,230 +1,236 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkSurface.h" #include "mitkCommon.h" #include "mitkVector.h" #include "mitkTestingMacros.h" #include "mitkTimeSlicedGeometry.h" #include "vtkPolyData.h" #include "vtkSphereSource.h" #include int mitkSurfaceTest(int /*argc*/, char* /*argv*/[]) { + + MITK_TEST_BEGIN("Surface"); + mitk::Surface::Pointer surface = mitk::Surface::New(); - MITK_TEST_CONDITION_REQUIRED(surface.GetPointer(), "Testing initialization!"); + MITK_TEST_CONDITION_REQUIRED( surface.GetPointer(), "Testing initialization!" ); + + mitk::Surface::Pointer cloneSurface = surface->Clone(); + MITK_TEST_CONDITION_REQUIRED( cloneSurface.GetPointer(), "Testing clone surface initialization!" ); std::filebuf fb; std::ostream s = std::ostream(&fb); surface->PrintSelf( s, 0); - - MITK_TEST_CONDITION_REQUIRED(s.width()!= 0, "Testing PrintSelf method!"); + MITK_INFO<SetCenter(0,0,0); sphereSource->SetRadius(5.0); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); vtkPolyData* polys = sphereSource->GetOutput(); MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData() == NULL, "Testing initial state of vtkPolyData"); surface->SetVtkPolyData( polys ); sphereSource->Delete(); MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData()!= NULL, "Testing set vtkPolyData"); + + cloneSurface = surface->Clone(); + MITK_TEST_CONDITION_REQUIRED(cloneSurface->GetVtkPolyData()!= NULL, "Testing set vtkPolyData of cloned surface!"); + //std::cout << "Testing mitk::Surface::SetVtkPolyData(): "; - //if (surface->GetVtkPolyData() == NULL ) { + //if (surface->GetVtkPolyData() == NULL ) + //{ // std::cout<<"[FAILED]"<ComputeBounds(); polys->GetBounds( bounds ); - std::cout << "Testing GetBoundingBox() "; surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); - // mitk::BoundingBox bb = const_cast( mitk::BoundingBox* bb = const_cast(surface->GetGeometry()->GetBoundingBox()); mitk::BoundingBox::BoundsArrayType surfBounds = bb->GetBounds(); - if ( bounds[0] != surfBounds[0] - || bounds[1] != surfBounds[1] - || bounds[2] != surfBounds[2] - || bounds[3] != surfBounds[3] - || bounds[4] != surfBounds[4] - || bounds[5] != surfBounds[5] - ) { - std::cout<<"[FAILED]"<Expand(5); surface->Update(); surface->SetRequestedRegionToLargestPossibleRegion(); mitk::Surface::RegionType requestedRegion = surface->GetRequestedRegion(); + MITK_TEST_CONDITION_REQUIRED(requestedRegion.GetSize(3) == 5, "Testing mitk::Surface::Expand( timesteps ): "); + //if ( requestedRegion.GetSize(3) != 5 ) { + // std::cout<<"[FAILED]"<GetTimeSlicedGeometry()->GetGeometry3D(0); //geometry->GetVtkTransform()->Identity(); //geometry->GetVtkTransform()->Translate(10,10,10); //geometry->TransferVtkToItkTransform(); //mitk::TimeSlicedGeometry* timeSlicedGeometry = surface->GetTimeSlicedGeometry(); //timeSlicedGeometry->InitializeEvenlyTimed(geometry, 5); - vtkFloatingPointType bounds[5][6]; + vtkFloatingPointType boundsMat[5][6]; for (int i=0;i<5;i++) { vtkSphereSource* sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0,0,0); sphereSource->SetRadius(1.0 * (i+1.0)); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); sphereSource->GetOutput()->ComputeBounds(); - sphereSource->GetOutput()->GetBounds( bounds[i] ); + sphereSource->GetOutput()->GetBounds( boundsMat[i] ); surface->SetVtkPolyData( sphereSource->GetOutput(),i ); sphereSource->Delete(); } surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); - bool passed = true; + passed = true; for (int i=0;i<5;i++) { mitk::BoundingBox::BoundsArrayType surfBounds = (const_cast(surface->GetTimeSlicedGeometry()->GetGeometry3D(i)->GetBoundingBox()))->GetBounds(); - if ( bounds[i][0] != surfBounds[0] - || bounds[i][1] != surfBounds[1] - || bounds[i][2] != surfBounds[2] - || bounds[i][3] != surfBounds[3] - || bounds[i][4] != surfBounds[4] - || bounds[i][5] != surfBounds[5] ) + if ( boundsMat[i][0] != surfBounds[0] + || boundsMat[i][1] != surfBounds[1] + || boundsMat[i][2] != surfBounds[2] + || boundsMat[i][3] != surfBounds[3] + || boundsMat[i][4] != surfBounds[4] + || boundsMat[i][5] != surfBounds[5] ) { passed = false; break; } } - - if (!passed) - { - std::cout<<"[FAILED]"<GetUpdatedTimeSlicedGeometry(): \n"; + //std::cout << "Testing correctness of geometry for surface->GetUpdatedTimeSlicedGeometry(): \n"; const mitk::TimeSlicedGeometry* inputTimeGeometry = surface->GetUpdatedTimeSlicedGeometry(); int time = 3; int timestep=0; timestep = inputTimeGeometry->MSToTimeStep( time ); + MITK_TEST_CONDITION_REQUIRED(time == timestep, "Testing correctness of geometry for surface->GetUpdatedTimeSlicedGeometry()!"); - std::cout << "time: "<< time << std::endl; - std::cout << "timestep: "<SetCenter(0,0,0); sphereSource->SetRadius( 100.0 ); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); surface->SetVtkPolyData( sphereSource->GetOutput(), 3 ); sphereSource->Delete(); inputTimeGeometry = surface->GetUpdatedTimeSlicedGeometry(); time = 3; timestep=0; + timestep = inputTimeGeometry->MSToTimeStep( time ); + MITK_TEST_CONDITION_REQUIRED(time == timestep, "Explicitly changing the data of timestep 3 and checking for timebounds correctness of surface's geometry again!"); - std::cout << "time: "<< time << std::endl; - std::cout << "timestep: "<GetTimeSteps(); mitk::Surface::Pointer dummy = mitk::Surface::New(); dummy->Graft(surface); - std::cout << "polyData != NULL ??" << std::endl; - if (dummy->GetVtkPolyData() == NULL) - { - std::cout<<"[FAILED]"<GetTimeSteps() << std::endl; - if (dummy->GetTimeSteps() != numberoftimesteps) - { - std::cout<<"[FAILED]"<GetVtkPolyData() != NULL, "Testing copying a Surface with Graft()!"); + MITK_TEST_CONDITION_REQUIRED( dummy->GetTimeSteps() == numberoftimesteps, "orig-numberofTimeSteps:" << numberoftimesteps << " copy-numberofTimeSteps:" << dummy->GetTimeSteps()); + + //std::cout << "polyData != NULL ??" << std::endl; + //if (dummy->GetVtkPolyData() == NULL) + //{ + // std::cout<<"[FAILED]"<GetTimeSteps() << std::endl; + //if (dummy->GetTimeSteps() != numberoftimesteps) + //{ + // std::cout<<"[FAILED]"<