diff --git a/Modules/Core/include/mitkArbitraryTimeGeometry.h b/Modules/Core/include/mitkArbitraryTimeGeometry.h index d46161ef5a..bb6f6743d4 100644 --- a/Modules/Core/include/mitkArbitraryTimeGeometry.h +++ b/Modules/Core/include/mitkArbitraryTimeGeometry.h @@ -1,247 +1,247 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef ArbitraryTimeGeometry_h #define ArbitraryTimeGeometry_h //MITK #include #include #include namespace mitk { /** * \brief Organizes geometries over arbitrary defined time steps * * For this TimeGeometry implementation it is assumed that * the durations of the time steps are arbitrary and may differ. * The geometries of the time steps are independent, * and not linked to each other. Since the timeBounds of the * geometries are different for each time step it is not possible * to set the same geometry to different time steps. Instead * copies should be used. * @remark The lower time bound of a succeeding time step may not be smaller - * to the upper time bound of its predecessor. Thus the list of time points is + * than the upper time bound of its predecessor. Thus the list of time points is * always sorted by its lower time bounds. * @remark For the conversion between time step and time point the following assumption * is used.:\n * time step -> time point: time point is the lower time bound of the geometry indicated by step.\n * time point -> time step: associated time step is last step which lower time bound is smaller or equal then the time * point. * * \addtogroup geometry */ class MITKCORE_EXPORT ArbitraryTimeGeometry : public TimeGeometry { public: mitkClassMacro(ArbitraryTimeGeometry, TimeGeometry); ArbitraryTimeGeometry(); typedef ArbitraryTimeGeometry self; itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ virtual TimeStepType CountTimeSteps() const; /** * \brief Returns the first time point for which the time geometry instance is valid. * * Returns the first valid time point for this geometry. It is the lower time bound of * the first step. The time point is given in ms. */ virtual TimePointType GetMinimumTimePoint() const; /** * \brief Returns the last time point for which the time geometry instance is valid * * Gives the last time point for which a valid geometry is saved in * this time geometry. It is the upper time bound of the last step. * The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint() const; /** * \brief Returns the first time point for which the time geometry instance is valid. * * Returns the first valid time point for the given TimeStep. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint(TimeStepType step) const; /** * \brief Returns the last time point for which the time geometry instance is valid * * Gives the last time point for the Geometry specified by the given TimeStep. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint(TimeStepType step) const; /** * \brief Get the time bounds (in ms) * it returns GetMinimumTimePoint() and GetMaximumTimePoint() results as bounds. */ virtual TimeBounds GetTimeBounds() const; /** * \brief Get the time bounds for the given TimeStep (in ms) */ virtual TimeBounds GetTimeBounds(TimeStepType step) const; /** * \brief Tests if a given time point is covered by this time geometry instance * * Returns true if a geometry can be returned for the given time * point (so it is within GetTimeBounds() and fails if not. * The time point must be given in ms. */ virtual bool IsValidTimePoint(TimePointType timePoint) const; /** * \brief Test for the given time step if a geometry is availible * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. * The time step is defined as positiv number. */ virtual bool IsValidTimeStep(TimeStepType timeStep) const; /** * \brief Converts a time step to a time point * * Converts a time step to a time point by using the time steps lower * time bound. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ virtual TimePointType TimeStepToTimePoint(TimeStepType timeStep) const; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. * The associated time step is the last step which lower time bound * is smaller or equal then the time point. * If a negative invalid time point is given always time step 0 is * returned. If an positive invalid time step is given an invalid * time step will be returned. */ virtual TimeStepType TimePointToTimeStep(TimePointType timePoint) const; /** * \brief Returns the geometry which corresponds to the given time step * * Returns a clone of the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. */ virtual BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const; /** * \brief Returns the geometry which corresponds to the given time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ virtual BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ virtual BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const; /** * \brief Tests if all necessary informations are set and the object is valid */ virtual bool IsValid() const; /** * \brief Initializes a new object with one time steps which contains an empty geometry. */ virtual void Initialize(); /** * \brief Expands the time geometry to the given number of time steps. * * Initializes the new time steps with empty geometries. This default geometries will behave like * ProportionalTimeGeometry. * Shrinking is not supported. The new steps will have the same duration like the last step before extension. */ virtual void Expand(TimeStepType size); /** * \brief Replaces the geometry instances with clones of the passed geometry. * * Replaces the geometries of all time steps with clones of the passed * geometry. Replacement strategy depends on the implementation of TimeGeometry * sub class. * @remark The time points itself stays untouched. Use this method if you want * to change the spatial properties of a TimeGeometry and preserve the time * "grid". */ virtual void ReplaceTimeStepGeometries(const BaseGeometry *geometry); /** * \brief Sets the geometry for the given time step * * If passed time step is not valid. Nothing will be changed. * @pre geometry must point to a valid instance. */ virtual void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep); /** * \brief Makes a deep copy of the current object */ virtual itk::LightObject::Pointer InternalClone() const; void ClearAllGeometries(); /** Append the passed geometry to the time geometry. * @pre The passed geometry pointer must be valid. - * @pre The minimumTimePoint must not be smaller then the the maximum time point of the currently last time step. + * @pre The minimumTimePoint must not be smaller than the maximum time point of the currently last time step. * Therefore time steps must not be overlapping in time. * @pre minimumTimePoint must not be larger then maximumTimePoint.*/ - void AppendTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint); + void AppendNewTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint); - /** Same than AppendTimeStep. But clones geometry before adding it.*/ - void AppendTimeStepClone(const BaseGeometry* geometry, + /** Same than AppendNewTimeStep. But clones geometry before adding it.*/ + void AppendNewTimeStepClone(const BaseGeometry* geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint ); void ReserveSpaceForGeometries( TimeStepType numberOfGeometries ); virtual void PrintSelf(std::ostream &os, itk::Indent indent) const; protected: virtual ~ArbitraryTimeGeometry(); std::vector m_GeometryVector; std::vector m_MinimumTimePoints; std::vector m_MaximumTimePoints; }; // end class ArbitraryTimeGeometry } // end namespace MITK #endif // ArbitraryTimeGeometry_h diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp index f799991f06..6bae144303 100644 --- a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp @@ -1,659 +1,659 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSliceNavigationController.h" #include "mitkAction.h" #include "mitkBaseRenderer.h" #include "mitkCrosshairPositionEvent.h" #include "mitkInteractionConst.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPlaneGeometry.h" #include "mitkProportionalTimeGeometry.h" #include "mitkArbitraryTimeGeometry.h" #include "mitkRenderingManager.h" #include "mitkSlicedGeometry3D.h" #include "mitkVtkPropRenderer.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkInteractionConst.h" #include "mitkNodePredicateDataType.h" #include "mitkOperationEvent.h" #include "mitkPixelTypeMultiplex.h" #include "mitkPlaneOperation.h" #include "mitkPointOperation.h" #include "mitkStatusBar.h" #include "mitkUndoController.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController() : BaseController(), m_InputWorldGeometry3D( mitk::BaseGeometry::ConstPointer() ), m_InputWorldTimeGeometry( mitk::TimeGeometry::ConstPointer() ), m_CreatedWorldGeometry( mitk::TimeGeometry::Pointer() ), m_ViewDirection(Axial), m_DefaultViewDirection(Axial), m_RenderingManager( mitk::RenderingManager::Pointer() ), m_Renderer( nullptr ), m_Top(false), m_FrontSide(false), m_Rotated(false), m_BlockUpdate(false), m_SliceLocked(false), m_SliceRotationLocked(false), m_OldPos(0) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendSlice); timeStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendTime); m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); m_Time->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); m_Slice->SetUnitName("mm"); m_Time->SetUnitName("ms"); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() {} void SliceNavigationController::SetInputWorldGeometry3D(const BaseGeometry *geometry) { if ( geometry != nullptr ) { if (const_cast(geometry->GetBoundingBox())->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldGeometry3D != geometry) { m_InputWorldGeometry3D = geometry; m_InputWorldTimeGeometry = mitk::TimeGeometry::ConstPointer(); this->Modified(); } } void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry *geometry) { if ( geometry != nullptr ) { if (const_cast(geometry->GetBoundingBoxInWorld())->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; m_InputWorldGeometry3D = mitk::BaseGeometry::ConstPointer(); this->Modified(); } } RenderingManager *SliceNavigationController::GetRenderingManager() const { mitk::RenderingManager *renderingManager = m_RenderingManager.GetPointer(); if (renderingManager != nullptr) return renderingManager; if ( m_Renderer != nullptr ) { renderingManager = m_Renderer->GetRenderingManager(); if (renderingManager != nullptr) return renderingManager; } return mitk::RenderingManager::GetInstance(); } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char *SliceNavigationController::GetViewDirectionAsString() const { const char *viewDirectionString; switch (m_ViewDirection) { case SliceNavigationController::Axial: viewDirectionString = "Axial"; break; case SliceNavigationController::Sagittal: viewDirectionString = "Sagittal"; break; case SliceNavigationController::Frontal: viewDirectionString = "Coronal"; break; case SliceNavigationController::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if (!m_BlockUpdate) { if (m_ViewDirection == Sagittal) { this->Update(Sagittal, true, true, false); } else if (m_ViewDirection == Frontal) { this->Update(Frontal, false, true, false); } else if (m_ViewDirection == Axial) { this->Update(Axial, false, false, true); } else { this->Update(m_ViewDirection); } } } void SliceNavigationController::Update(SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated) { TimeGeometry::ConstPointer worldTimeGeometry = m_InputWorldTimeGeometry; if (m_BlockUpdate || (m_InputWorldTimeGeometry.IsNull() && m_InputWorldGeometry3D.IsNull()) || ((worldTimeGeometry.IsNotNull()) && (worldTimeGeometry->CountTimeSteps() == 0))) { return; } m_BlockUpdate = true; if (m_InputWorldTimeGeometry.IsNotNull() && m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } if (m_InputWorldGeometry3D.IsNotNull() && m_LastUpdateTime < m_InputWorldGeometry3D->GetMTime()) { Modified(); } this->SetViewDirection(viewDirection); this->SetTop(top); this->SetFrontSide(frontside); this->SetRotated(rotated); if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = SlicedGeometry3D::Pointer(); BaseGeometry::ConstPointer currentGeometry = BaseGeometry::ConstPointer(); if (m_InputWorldTimeGeometry.IsNotNull()) if (m_InputWorldTimeGeometry->IsValidTimeStep(GetTime()->GetPos())) currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(GetTime()->GetPos()); else currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); else currentGeometry = m_InputWorldGeometry3D; m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); switch (viewDirection) { case Original: if (worldTimeGeometry.IsNotNull()) { m_CreatedWorldGeometry = worldTimeGeometry->Clone(); worldTimeGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry.IsNotNull()) { break; } } else { const SlicedGeometry3D *worldSlicedGeometry = dynamic_cast(currentGeometry.GetPointer()); if ( worldSlicedGeometry != nullptr ) { slicedWorldGeometry = static_cast(currentGeometry->Clone().GetPointer()); break; } } slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::None, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Axial: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Axial, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Frontal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Sagittal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos(0); m_Slice->SetSteps((int)slicedWorldGeometry->GetSlices()); if ( worldTimeGeometry.IsNull() ) { auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, 1 ); m_CreatedWorldGeometry = createdTimeGeometry; m_Time->SetSteps(0); m_Time->SetPos(0); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps(worldTimeGeometry->CountTimeSteps()); m_Time->SetPos(0); const TimeBounds &timeBounds = worldTimeGeometry->GetTimeBounds(); m_Time->SetRange(timeBounds[0], timeBounds[1]); m_BlockUpdate = false; const auto currentTemporalPosition = this->GetTime()->GetPos(); assert( worldTimeGeometry->GetGeometryForTimeStep( currentTemporalPosition ).IsNotNull() ); if ( dynamic_cast( worldTimeGeometry.GetPointer() ) != nullptr ) { const TimePointType minimumTimePoint = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition ); const TimePointType stepDuration = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition + 1 ) - minimumTimePoint; auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, worldTimeGeometry->CountTimeSteps() ); createdTimeGeometry->SetFirstTimePoint( minimumTimePoint ); createdTimeGeometry->SetStepDuration( stepDuration ); m_CreatedWorldGeometry = createdTimeGeometry; } else { auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); const TimeStepType numberOfTimeSteps = worldTimeGeometry->CountTimeSteps(); createdTimeGeometry->ReserveSpaceForGeometries( numberOfTimeSteps ); for ( TimeStepType i = 0; i < numberOfTimeSteps; ++i ) { const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone().GetPointer(); const auto bounds = worldTimeGeometry->GetTimeBounds( i ); - createdTimeGeometry->AppendTimeStep( clonedGeometry, + createdTimeGeometry->AppendNewTimeStep( clonedGeometry, bounds[0], bounds[1]); } createdTimeGeometry->Update(); m_CreatedWorldGeometry = createdTimeGeometry; } } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if (!m_BlockUpdate) { this->InvokeEvent(GeometrySendEvent(m_CreatedWorldGeometry, 0)); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if (!m_BlockUpdate) { this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); } } void SliceNavigationController::SendSlice() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); // send crosshair event crosshairPositionEvent.Send(); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos())); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry(const itk::EventObject &) {} void SliceNavigationController::SetGeometryTime(const itk::EventObject &geometryTimeEvent) { if (m_CreatedWorldGeometry.IsNull()) { return; } const SliceNavigationController::GeometryTimeEvent *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >(&geometryTimeEvent); assert( timeEvent != nullptr ); TimeGeometry *timeGeometry = timeEvent->GetTimeGeometry(); assert( timeGeometry != nullptr ); int timeStep = (int)timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint(timeStep); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep(timeInMS); this->GetTime()->SetPos(timeStep); } void SliceNavigationController::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent *sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent!=nullptr); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint(const Point3D &point) { if (m_CreatedWorldGeometry.IsNull()) { return; } //@todo add time to PositionEvent and use here!! SlicedGeometry3D *slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if (slicedWorldGeometry->GetEvenlySpaced()) { mitk::PlaneGeometry *plane = slicedWorldGeometry->GetPlaneGeometry(0); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project(point, projectedPoint); // Check whether the point is somewhere within the slice stack volume; // otherwise, the default slice (0) will be selected if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0) { bestSlice = (int)(plane->Distance(point) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for (s = 0; s < slices; ++s) { slicedWorldGeometry->GetPlaneGeometry(s)->Project(point, projectedPoint); const Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if (currentDistance < bestDistance) { bestDistance = currentDistance; bestSlice = s; } } } if (bestSlice >= 0) { this->GetSlice()->SetPos(bestSlice); } else { this->GetSlice()->SetPos(0); } this->SendCreatedWorldGeometryUpdate(); } } void SliceNavigationController::ReorientSlices(const Point3D &point, const Vector3D &normal) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, normal); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1) { if (m_CreatedWorldGeometry) { PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } } mitk::TimeGeometry *SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::BaseGeometry *SliceNavigationController::GetCurrentGeometry3D() { if (m_CreatedWorldGeometry.IsNotNull()) { return m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()); } else { return nullptr; } } const mitk::PlaneGeometry *SliceNavigationController::GetCurrentPlaneGeometry() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); if (slicedGeometry) { const mitk::PlaneGeometry *planeGeometry = (slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos())); return planeGeometry; } else { return nullptr; } } void SliceNavigationController::SetRenderer(BaseRenderer *renderer) { m_Renderer = renderer; } BaseRenderer *SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for (i = 0; i < 3; ++i) { if (fabs(direction[i]) < 0.000000001) { ++c; } else { k = i; } } if (c == 2) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM(k); m_Slice->SetRange(min, max); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation(Operation *operation) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if (!operation || m_CreatedWorldGeometry.IsNull()) { return; } switch (operation->GetOperationType()) { case OpMOVE: // should be a point operation { if (!m_SliceLocked) // do not move the cross position { // select a slice PointOperation *po = dynamic_cast(operation); if (po && po->GetIndex() == -1) { this->SelectSliceByPoint(po->GetPoint()); } else if (po && po->GetIndex() != -1) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos(po->GetIndex()); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp index 9d671c90ca..596e123b1f 100644 --- a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp @@ -1,293 +1,293 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include mitk::ArbitraryTimeGeometry::ArbitraryTimeGeometry() = default; mitk::ArbitraryTimeGeometry::~ArbitraryTimeGeometry() = default; void mitk::ArbitraryTimeGeometry::Initialize() { this->ClearAllGeometries(); Geometry3D::Pointer geo = Geometry3D::New(); - this->AppendTimeStep(geo, 0, 1); + this->AppendNewTimeStep(geo, 0, 1); } mitk::TimeStepType mitk::ArbitraryTimeGeometry::CountTimeSteps() const { return static_cast(m_GeometryVector.size()); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint() const { return m_MinimumTimePoints.empty() ? 0.0 : m_MinimumTimePoints.front(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint() const { TimePointType result = 0; if ( !m_MaximumTimePoints.empty() ) { result = m_MaximumTimePoints.back(); } return result; } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint( TimeStepType step ) const { TimePointType result = GetMinimumTimePoint(); if (step > 0 && step <= m_MaximumTimePoints.size()) { result = m_MaximumTimePoints[step - 1]; } return result; }; mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint( TimeStepType step ) const { TimePointType result = 0; if (step < m_MaximumTimePoints.size()) { result = m_MaximumTimePoints[step]; } return result; }; mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds() const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint(); bounds[1] = this->GetMaximumTimePoint(); return bounds; } mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds(TimeStepType step) const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint( step ); bounds[1] = this->GetMaximumTimePoint( step ); return bounds; } bool mitk::ArbitraryTimeGeometry::IsValidTimePoint(TimePointType timePoint) const { return this->GetMinimumTimePoint() <= timePoint && timePoint < this->GetMaximumTimePoint(); } bool mitk::ArbitraryTimeGeometry::IsValidTimeStep(TimeStepType timeStep) const { return timeStep < this->CountTimeSteps(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::TimeStepToTimePoint( TimeStepType timeStep ) const { TimePointType result = 0.0; if (timeStep < m_MinimumTimePoints.size() ) { result = m_MinimumTimePoints[timeStep]; } return result; } mitk::TimeStepType mitk::ArbitraryTimeGeometry::TimePointToTimeStep(TimePointType timePoint) const { mitk::TimeStepType result = 0; if ( timePoint >= GetMinimumTimePoint() ) { for ( auto pos = m_MaximumTimePoints.cbegin(); pos != m_MaximumTimePoints.cend(); ++pos ) { if (timePoint < *pos) { result = pos - m_MaximumTimePoints.begin(); break; } } } return result; } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimeStep(TimeStepType timeStep) const { if ( IsValidTimeStep( timeStep ) ) { return m_GeometryVector[timeStep]; } else { return 0; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimePoint( TimePointType timePoint ) const { if ( this->IsValidTimePoint( timePoint ) ) { const TimeStepType timeStep = this->TimePointToTimeStep( timePoint ); return this->GetGeometryForTimeStep( timeStep ); } else { return 0; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryCloneForTimeStep( TimeStepType timeStep ) const { if ( timeStep >= m_GeometryVector.size() ) return 0; return m_GeometryVector[timeStep]->Clone(); } bool mitk::ArbitraryTimeGeometry::IsValid() const { bool isValid = true; isValid &= m_GeometryVector.size() > 0; return isValid; } void mitk::ArbitraryTimeGeometry::ClearAllGeometries() { m_GeometryVector.clear(); m_MinimumTimePoints.clear(); m_MaximumTimePoints.clear(); } void mitk::ArbitraryTimeGeometry::ReserveSpaceForGeometries( TimeStepType numberOfGeometries ) { m_GeometryVector.reserve( numberOfGeometries ); m_MinimumTimePoints.reserve( numberOfGeometries ); m_MaximumTimePoints.reserve( numberOfGeometries ); } void mitk::ArbitraryTimeGeometry::Expand( mitk::TimeStepType size ) { m_GeometryVector.reserve( size ); const mitk::TimeStepType lastIndex = this->CountTimeSteps() - 1; const TimePointType minTP = this->GetMinimumTimePoint( lastIndex ); TimePointType maxTP = this->GetMaximumTimePoint( lastIndex ); const TimePointType duration = maxTP - minTP; while (m_GeometryVector.size() < size) { m_GeometryVector.push_back( Geometry3D::New().GetPointer() ); m_MinimumTimePoints.push_back( maxTP ); maxTP += duration; m_MaximumTimePoints.push_back( maxTP ); } } void mitk::ArbitraryTimeGeometry::ReplaceTimeStepGeometries(const BaseGeometry *geometry) { for ( auto pos = m_GeometryVector.begin(); pos != m_GeometryVector.end(); ++pos ) { *pos = geometry->Clone(); } } void mitk::ArbitraryTimeGeometry::SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) { assert( timeStep <= m_GeometryVector.size() ); if ( timeStep == m_GeometryVector.size() ) { m_GeometryVector.push_back( geometry ); } m_GeometryVector[timeStep] = geometry; } itk::LightObject::Pointer mitk::ArbitraryTimeGeometry::InternalClone() const { itk::LightObject::Pointer parent = Superclass::InternalClone(); ArbitraryTimeGeometry::Pointer newTimeGeometry = dynamic_cast(parent.GetPointer()); newTimeGeometry->m_MinimumTimePoints = this->m_MinimumTimePoints; newTimeGeometry->m_MaximumTimePoints = this->m_MaximumTimePoints; newTimeGeometry->m_GeometryVector.clear(); for (TimeStepType i = 0; i < CountTimeSteps(); ++i) { newTimeGeometry->m_GeometryVector.push_back( this->m_GeometryVector[i]->Clone() ); } return parent; } -void mitk::ArbitraryTimeGeometry::AppendTimeStep(BaseGeometry *geometry, +void mitk::ArbitraryTimeGeometry::AppendNewTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { if ( !geometry ) { mitkThrow() << "Cannot append geometry to time geometry. Invalid geometry passed (NULL pointer)."; } if (maximumTimePoint < minimumTimePoint) { mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict. Maxmimum time point ("< minimumTimePoint ) { mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict new time point and currently last time point overlapp."; } } m_GeometryVector.push_back( geometry ); m_MinimumTimePoints.push_back( minimumTimePoint ); m_MaximumTimePoints.push_back( maximumTimePoint ); } -void mitk::ArbitraryTimeGeometry::AppendTimeStepClone(const BaseGeometry *geometry, +void mitk::ArbitraryTimeGeometry::AppendNewTimeStepClone(const BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { BaseGeometry::Pointer clone = geometry->Clone(); - this->AppendTimeStep(clone, minimumTimePoint, maximumTimePoint); + this->AppendNewTimeStep(clone, minimumTimePoint, maximumTimePoint); }; void mitk::ArbitraryTimeGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << " MinimumTimePoint: " << this->GetMinimumTimePoint() << " ms" << std::endl; os << indent << " MaximumTimePoint: " << this->GetMaximumTimePoint() << " ms" << std::endl; os << std::endl; os << indent << " min TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MinimumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MinimumTimePoints[i] << " ms" << std::endl; } os << std::endl; os << indent << " max TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MaximumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MaximumTimePoints[i] << " ms" << std::endl; } } diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index 1bf72ad955..6a3c1c9af4 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,671 +1,671 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if (imageIOName == "GE4ImageIO" || imageIOName == "GE5ImageIO") { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be NULL"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be NULL"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } /**Helper function that converts the content of a meta data into a time point vector. * If MetaData is not valid or cannot be converted an empty vector is returned.*/ std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase *data) { const itk::MetaDataObject *timeGeometryTimeData = dynamic_cast *>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; std::vector ItkImageIO::Read() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass() << std::endl; typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback" << std::endl; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); TimePointVector::const_iterator prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { - arbitraryTimeGeometry->AppendTimeStepClone(slicedGeometry, *prePos, *pos); + arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass() << std::endl; ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = NULL; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. IPropertyPersistence::InfoResultType infoList = mitk::CoreServices::GetPropertyPersistence()->GetInfoByKey(key); auto predicate = [mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info PropertyPersistenceInfo::Pointer newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { mitk::CoreServices::GetPropertyPersistence()->AddInfo(info); } } } MITK_INFO << "...finished!" << std::endl; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const mitk::Image *image = dynamic_cast(this->GetInput()); if (image == NULL) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const ArbitraryTimeGeometry *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); std::stringstream stream; stream << arbitraryTG->GetTimeBounds(0)[0]; for (TimeStepType pos = 0; pos < arbitraryTG->CountTimeSteps(); ++pos) { stream << " " << arbitraryTG->GetTimeBounds(pos)[1]; } std::string data = stream.str(); itk::EncapsulateMetaData( m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, data); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { IPropertyPersistence::InfoResultType infoList = mitk::CoreServices::GetPropertyPersistence()->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } ImageReadAccessor imageAccess(image); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const Image *image = dynamic_cast(this->GetInput()); if (image == NULL) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp index 3db132720c..c48dedc5dc 100644 --- a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp +++ b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp @@ -1,469 +1,469 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkArbitraryTimeGeometry.h" #include "mitkGeometry3D.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkArbitraryTimeGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkArbitraryTimeGeometryTestSuite); // Test the append method MITK_TEST(CountTimeSteps); MITK_TEST(GetMinimumTimePoint); MITK_TEST(GetMaximumTimePoint); MITK_TEST(GetTimeBounds); MITK_TEST(IsValidTimePoint); MITK_TEST(TimeStepToTimePoint); MITK_TEST(TimePointToTimeStep); MITK_TEST(GetGeometryCloneForTimeStep); MITK_TEST(GetGeometryForTimeStep); MITK_TEST(GetGeometryForTimePoint); MITK_TEST(IsValid); MITK_TEST(Expand); MITK_TEST(ReplaceTimeStepGeometries); MITK_TEST(ClearAllGeometries); - MITK_TEST(AppendTimeStep); + MITK_TEST(AppendNewTimeStep); CPPUNIT_TEST_SUITE_END(); private: mitk::Geometry3D::Pointer m_Geometry1; mitk::Geometry3D::Pointer m_Geometry2; mitk::Geometry3D::Pointer m_Geometry3; mitk::Geometry3D::Pointer m_Geometry3_5; mitk::Geometry3D::Pointer m_Geometry4; mitk::Geometry3D::Pointer m_Geometry5; mitk::Geometry3D::Pointer m_InvalidGeometry; mitk::Geometry3D::Pointer m_NewGeometry; mitk::TimePointType m_Geometry1MinTP; mitk::TimePointType m_Geometry2MinTP; mitk::TimePointType m_Geometry3MinTP; mitk::TimePointType m_Geometry3_5MinTP; mitk::TimePointType m_Geometry4MinTP; mitk::TimePointType m_Geometry5MinTP; mitk::TimePointType m_NewGeometryMinTP; mitk::TimePointType m_Geometry1MaxTP; mitk::TimePointType m_Geometry2MaxTP; mitk::TimePointType m_Geometry3MaxTP; mitk::TimePointType m_Geometry3_5MaxTP; mitk::TimePointType m_Geometry4MaxTP; mitk::TimePointType m_Geometry5MaxTP; mitk::TimePointType m_NewGeometryMaxTP; mitk::ArbitraryTimeGeometry::Pointer m_emptyTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_initTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_12345TimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometry; public: void setUp() override { mitk::TimeBounds bounds; m_Geometry1 = mitk::Geometry3D::New(); m_Geometry2 = mitk::Geometry3D::New(); m_Geometry3 = mitk::Geometry3D::New(); m_Geometry3_5 = mitk::Geometry3D::New(); m_Geometry4 = mitk::Geometry3D::New(); m_Geometry5 = mitk::Geometry3D::New(); m_Geometry1MinTP = 1; m_Geometry2MinTP = 2; m_Geometry3MinTP = 3; m_Geometry3_5MinTP = 3.5; m_Geometry4MinTP = 4; m_Geometry5MinTP = 5; m_Geometry1MaxTP = 1.9; m_Geometry2MaxTP = 2.9; m_Geometry3MaxTP = 3.9; m_Geometry3_5MaxTP = 3.9; m_Geometry4MaxTP = 4.9; m_Geometry5MaxTP = 5.9; m_NewGeometry = mitk::Geometry3D::New(); m_NewGeometryMinTP = 20; m_NewGeometryMaxTP = 21.9; mitk::Point3D origin(42); m_NewGeometry->SetOrigin(origin); m_emptyTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_emptyTimeGeometry->ClearAllGeometries(); m_initTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_initTimeGeometry->Initialize(); m_12345TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_12345TimeGeometry->ClearAllGeometries(); - m_12345TimeGeometry->AppendTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); - m_12345TimeGeometry->AppendTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); - m_12345TimeGeometry->AppendTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); - m_12345TimeGeometry->AppendTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); - m_12345TimeGeometry->AppendTimeStep(m_Geometry5, m_Geometry5MinTP, m_Geometry5MaxTP); + m_12345TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); + m_12345TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); + m_12345TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); + m_12345TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); + m_12345TimeGeometry->AppendNewTimeStep(m_Geometry5, m_Geometry5MinTP, m_Geometry5MaxTP); m_123TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometry->ClearAllGeometries(); - m_123TimeGeometry->AppendTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); - m_123TimeGeometry->AppendTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); - m_123TimeGeometry->AppendTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); + m_123TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); + m_123TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); + m_123TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); } void tearDown() override {} void CountTimeSteps() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 0, "Testing CountTimeSteps with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->CountTimeSteps() == 1, "Testing CountTimeSteps with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing CountTimeSteps with m_12345TimeGeometry"); } void GetMinimumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 1.0, "Testing GetMinimumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 2.9, "Testing GetMinimumTimePoint(2) with m_12345TimeGeometry"); } void GetMaximumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 0.0, "Testing GetMaximumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint() == 1.0, "Testing GetMaximumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 5.9, "Testing GetMaximumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); } void GetTimeBounds() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[0] == 1.0, "Testing GetTimeBounds lower part with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[1] == 0.0, "Testing GetTimeBounds with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[1] == 1.0, "Testing GetTimeBounds with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[1] == 5.9, "Testing GetTimeBounds with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 3.9, "Testing GetTimeBounds(3) lower part with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[1] == 4.9, "Testing GetTimeBounds(3) with m_12345TimeGeometry"); } void IsValidTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(0) == true, "Testing IsValidTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(1) == true, "Testing IsValidTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(2.5) == true, "Testing IsValidTimePoint(2.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(5.89) == true, "Testing IsValidTimePoint(5.89) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(0) == false, "Testing IsValidTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(1) == true, "Testing IsValidTimeStep(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_12345TimeGeometry"); } void TimeStepToTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(0) == 1.0, "Testing TimeStepToTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(1) == 2.0, "Testing TimeStepToTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_12345TimeGeometry"); } void TimePointToTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(3.5) == 0, "Testing TimePointToTimeStep(3.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(3.5) == 0, "Testing TimePointToTimeStep(3.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(3.5) == 2, "Testing TimePointToTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(5.8) == 0, "Testing TimePointToTimeStep(5.8) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(5.8) == 0, "Testing TimePointToTimeStep(5.8) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.8) == 4, "Testing TimePointToTimeStep(5.8) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.9) == 0, "Testing TimePointToTimeStep(5.9) with m_12345TimeGeometry"); } void GetGeometryCloneForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryCloneForTimeStep(0).IsNull(), "Testing GetGeometryCloneForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_12345TimeGeometry"); } void GetGeometryForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimeStep(0).IsNull(), "Testing GetGeometryForTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(0).IsNotNull(), "Testing GetGeometryForTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(1).IsNull(), "Testing GetGeometryForTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(3).GetPointer() == m_Geometry4.GetPointer(), "Testing GetGeometryForTimePoint(3) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4).GetPointer() == m_Geometry5.GetPointer(), "Testing GetGeometryForTimePoint(4) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimeStep(5).IsNull(), "Testing GetGeometryForTimePoint(5) with m_12345TimeGeometry"); } void GetGeometryForTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(0).IsNotNull(), "Testing GetGeometryForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(1.5).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimeStep(1.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(3.5).GetPointer() == m_Geometry3.GetPointer(), "Testing GetGeometryForTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(5.9).IsNull(), "Testing GetGeometryForTimeStep(5.9) with m_12345TimeGeometry"); } void IsValid() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValid() == false, "Testing IsValid() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValid() == true, "Testing IsValid() with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValid() == true, "Testing IsValid() with m_12345TimeGeometry"); } void Expand() { m_12345TimeGeometry->Expand(3); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing Expand(3) doesn't change m_12345TimeGeometry"); m_12345TimeGeometry->Expand(7); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 7, "Testing Expand(7) with m_12345TimeGeometry"); } void ReplaceTimeStepGeometries() { // Test replace time step geometries m_12345TimeGeometry->ReplaceTimeStepGeometries(m_NewGeometry); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing ReplaceTimeStepGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if first geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if last geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); } void ClearAllGeometries() { // Test clear all geometries m_12345TimeGeometry->ClearAllGeometries(); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); } - void AppendTimeStep() + void AppendNewTimeStep() { // Test append - MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendTimeStep(NULL, 0, 1)); - MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendTimeStep(m_Geometry3_5,m_Geometry3_5MinTP,m_Geometry3_5MaxTP)); - MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendTimeStep(m_Geometry4, m_Geometry4MaxTP, m_Geometry4MinTP)); //valid but inverted bounds + MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(NULL, 0, 1)); + MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry3_5,m_Geometry3_5MinTP,m_Geometry3_5MaxTP)); + MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MaxTP, m_Geometry4MinTP)); //valid but inverted bounds - m_emptyTimeGeometry->AppendTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); + m_emptyTimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 1, - "Testing AppendTimeStep() with m_emptyTimeGeometry"); + "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 4, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 4.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 3, - "Testing AppendTimeStep() with m_emptyTimeGeometry"); + "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 3.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); - m_123TimeGeometry->AppendTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); + m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 4, - "Testing AppendTimeStep() with m_123TimeGeometry"); + "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, - "Testing AppendTimeStep() with m_123TimeGeometry"); + "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 4.9, - "Testing AppendTimeStep() with m_123TimeGeometry"); + "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 3.9, - "Testing AppendTimeStep() with m_123TimeGeometry"); + "Testing AppendNewTimeStep() with m_123TimeGeometry"); } }; MITK_TEST_SUITE_REGISTRATION(mitkArbitraryTimeGeometry) diff --git a/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp b/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp index 92169515e0..ab4b745f23 100644 --- a/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp +++ b/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp @@ -1,393 +1,393 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include #define BOOST_DATE_TIME_NO_LIB //Prevent unnecessary/unwanted auto link in this compilation when activating boost libraries in the MITK superbuild //It is necessary because BOOST_ALL_DYN_LINK overwrites BOOST_DATE_TIME_NO_LIB #if defined(BOOST_ALL_DYN_LINK) #undef BOOST_ALL_DYN_LINK #endif #include #include "mitkITKDICOMSeriesReaderHelper.h" #include "mitkITKDICOMSeriesReaderHelper.txx" #include "mitkDICOMGDCMTagScanner.h" #include "mitkArbitraryTimeGeometry.h" #define switch3DCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK( filenames, correctTilt, tiltInfo, io ); bool mitk::ITKDICOMSeriesReaderHelper::CanHandleFile( const std::string& filename ) { MITK_DEBUG << "ITKDICOMSeriesReaderHelper::CanHandleFile " << filename; itk::GDCMImageIO::Pointer tester = itk::GDCMImageIO::New(); return tester->CanReadFile( filename.c_str() ); } mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenames.front().c_str() ) ) { io->SetFileName( filenames.front().c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) { switch ( io->GetComponentType() ) { switch3DCase(DcmIoType::UCHAR, unsigned char) switch3DCase(DcmIoType::CHAR, char) switch3DCase( DcmIoType::USHORT, unsigned short) switch3DCase(DcmIoType::SHORT, short) switch3DCase(DcmIoType::UINT, unsigned int) switch3DCase(DcmIoType::INT, int) switch3DCase( DcmIoType::ULONG, long unsigned int) switch3DCase(DcmIoType::LONG, long int) switch3DCase(DcmIoType::FLOAT, float) switch3DCase(DcmIoType::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) { switch ( io->GetComponentType() ) { switch3DCase(DcmIoType::UCHAR, itk::RGBPixel) switch3DCase( DcmIoType::CHAR, itk::RGBPixel) switch3DCase(DcmIoType::USHORT, itk::RGBPixel) switch3DCase(DcmIoType::SHORT, itk::RGBPixel) switch3DCase( DcmIoType::UINT, itk::RGBPixel) switch3DCase(DcmIoType::INT, itk::RGBPixel) switch3DCase(DcmIoType::ULONG, itk::RGBPixel) switch3DCase(DcmIoType::LONG, itk::RGBPixel) switch3DCase( DcmIoType::FLOAT, itk::RGBPixel) switch3DCase(DcmIoType::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } #define switch3DnTCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK3DnT( filenamesLists, correctTilt, tiltInfo, io ); mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load3DnT( const StringContainerList& filenamesLists, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenamesLists.empty() || filenamesLists.front().empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenamesLists.front().front().c_str() ) ) { io->SetFileName( filenamesLists.front().front().c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) { switch ( io->GetComponentType() ) { switch3DnTCase(DcmIoType::UCHAR, unsigned char) switch3DnTCase(DcmIoType::CHAR, char) switch3DnTCase(DcmIoType::USHORT, unsigned short) switch3DnTCase( DcmIoType::SHORT, short) switch3DnTCase(DcmIoType::UINT, unsigned int) switch3DnTCase(DcmIoType::INT, int) switch3DnTCase(DcmIoType::ULONG, long unsigned int) switch3DnTCase(DcmIoType::LONG, long int) switch3DnTCase(DcmIoType::FLOAT, float) switch3DnTCase(DcmIoType::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) { switch ( io->GetComponentType() ) { switch3DnTCase(DcmIoType::UCHAR, itk::RGBPixel) switch3DnTCase(DcmIoType::CHAR, itk::RGBPixel) switch3DnTCase( DcmIoType::USHORT, itk::RGBPixel) switch3DnTCase(DcmIoType::SHORT, itk::RGBPixel) switch3DnTCase(DcmIoType::UINT, itk::RGBPixel) switch3DnTCase( DcmIoType::INT, itk::RGBPixel) switch3DnTCase(DcmIoType::ULONG, itk::RGBPixel) switch3DnTCase(DcmIoType::LONG, itk::RGBPixel) switch3DnTCase( DcmIoType::FLOAT, itk::RGBPixel) switch3DnTCase(DcmIoType::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } bool ConvertDICOMDateTimeString( const std::string& dateString, const std::string& timeString, OFDateTime& time ) { OFString content( timeString.c_str() ); if ( !dateString.empty() ) { content = OFString( dateString.c_str() ).append( content ); } const OFCondition result = DcmDateTime::getOFDateTimeFromString( content, time ); return result.good(); } boost::posix_time::ptime ConvertOFDateTimeToPTime( const OFDateTime& time ) { const boost::gregorian::date boostDate( time.getDate().getYear(), time.getDate().getMonth(), time.getDate().getDay() ); const boost::posix_time::time_duration boostTime = boost::posix_time::hours( time.getTime().getHour() ) + boost::posix_time::minutes( time.getTime().getMinute() ) + boost::posix_time::seconds( time.getTime().getSecond() ) + boost::posix_time::milliseconds( time.getTime().getMilliSecond() ); boost::posix_time::ptime result( boostDate, boostTime ); return result; } OFDateTime GetLowerDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() < time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() < time1.getTime() ) ) ) { result = time2; } return result; } OFDateTime GetUpperDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() > time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() > time1.getTime() ) ) ) { result = time2; } return result; } double ComputeMiliSecDuration( const OFDateTime& start, const OFDateTime& stop ) { const boost::posix_time::ptime startTime = ConvertOFDateTimeToPTime( start ); const boost::posix_time::ptime stopTime = ConvertOFDateTimeToPTime( stop ); ::boost::posix_time::time_duration duration = stopTime - startTime; double result = duration.total_milliseconds(); return result; } bool mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeStep( const StringContainer& filenamesOfTimeStep, DateTimeBounds& bounds ) { const DICOMTag acquisitionDateTag( 0x0008, 0x0022 ); const DICOMTag acquisitionTimeTag( 0x0008, 0x0032 ); DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New(); filescanner->SetInputFiles( filenamesOfTimeStep ); filescanner->AddTag( acquisitionDateTag ); filescanner->AddTag( acquisitionTimeTag ); filescanner->Scan(); const DICOMDatasetAccessingImageFrameList frameList = filescanner->GetFrameInfoList(); bool result = false; bool first = true; for (DICOMDatasetAccessingImageFrameList::const_iterator pos = frameList.cbegin(); pos != frameList.cend(); ++pos) { const std::string dateStr = ( *pos )->GetTagValueAsString( acquisitionDateTag ).value; const std::string timeStr = ( *pos )->GetTagValueAsString( acquisitionTimeTag ).value; OFDateTime time; const bool convertResult = ConvertDICOMDateTimeString( dateStr, timeStr, time ); if ( convertResult ) { if ( first ) { bounds[0] = time; bounds[1] = time; first = false; } else { bounds[0] = GetLowerDateTime( bounds[0], time ); bounds[1] = GetUpperDateTime( bounds[1], time ); } result = true; } } return result; }; mitk::ITKDICOMSeriesReaderHelper::TimeBoundsList mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeSteps( const StringContainerList& filenamesOfTimeSteps ) { TimeBoundsList result; OFDateTime baseLine; bool baseLineSet = false; for ( StringContainerList::const_iterator pos = filenamesOfTimeSteps.cbegin(); pos != filenamesOfTimeSteps.cend(); ++pos ) { TimeBounds bounds( 0.0 ); DateTimeBounds dateTimeBounds; if ( ExtractTimeBoundsOfTimeStep( *pos, dateTimeBounds ) ) { if ( !baseLineSet ) { baseLineSet = true; baseLine = dateTimeBounds[0]; } bounds[0] = ComputeMiliSecDuration( baseLine, dateTimeBounds[0] ); bounds[1] = ComputeMiliSecDuration( baseLine, dateTimeBounds[1] ); } result.push_back( bounds ); } return result; }; mitk::TimeGeometry::Pointer mitk::ITKDICOMSeriesReaderHelper::GenerateTimeGeometry( const BaseGeometry* templateGeometry, const TimeBoundsList& boundsList ) { TimeGeometry::Pointer timeGeometry; double check = 0.0; const auto boundListSize = boundsList.size(); for ( auto pos = 0; pos < boundListSize; ++pos ) { check += boundsList[pos][0]; check += boundsList[pos][1]; } if ( check < mitk::eps ) { // if all bounds are zero we assume that the bounds could not be correctly determined // and as a fallback generate a time geometry in the old mitk style ProportionalTimeGeometry::Pointer newTimeGeometry = ProportionalTimeGeometry::New(); newTimeGeometry->Initialize( templateGeometry, boundListSize ); timeGeometry = newTimeGeometry.GetPointer(); } else { ArbitraryTimeGeometry::Pointer newTimeGeometry = ArbitraryTimeGeometry::New(); newTimeGeometry->ClearAllGeometries(); newTimeGeometry->ReserveSpaceForGeometries( boundListSize ); for ( auto pos = 0; pos < boundListSize; ++pos ) { TimeBounds bounds = boundsList[pos]; if ( pos + 1 < boundListSize ) { //Currently we do not explicitly support "gaps" in the time coverage //thus we set the max time bound of a time step to the min time bound //of its successor. bounds[1] = boundsList[pos + 1][0]; } - newTimeGeometry->AppendTimeStepClone(templateGeometry, bounds[0], bounds[1]); + newTimeGeometry->AppendNewTimeStepClone(templateGeometry, bounds[0], bounds[1]); } timeGeometry = newTimeGeometry.GetPointer(); } return timeGeometry; };