diff --git a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp index ee30f3c5ab..b958e9dff5 100644 --- a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp @@ -1,454 +1,454 @@ /*=================================================================== 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. ===================================================================*/ //MITK #include #include #include #include #include // VTK #include #include //us #include #include mitk::InteractionTestHelper::InteractionTestHelper(const std::string &interactionXmlFilePath) : m_InteractionFilePath(interactionXmlFilePath) { this->Initialize(interactionXmlFilePath); } void mitk::InteractionTestHelper::Initialize(const std::string &interactionXmlFilePath) { //TiXmlDocument document(interactionXmlPath.c_str()); TiXmlDocument document(interactionXmlFilePath); bool loadOkay = document.LoadFile(); if (loadOkay) { //get RenderingManager instance mitk::RenderingManager* rm = mitk::RenderingManager::GetInstance(); //create data storage m_DataStorage = mitk::StandaloneDataStorage::New(); //for each renderer found create a render window and configure for( TiXmlElement* element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions())->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot())->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer()); element != NULL; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer()) ) { //get name of renderer const char* rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); //get view direction mitk::SliceNavigationController::ViewDirection viewDirection = mitk::SliceNavigationController::Axial; if(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection()) != NULL) { int viewDirectionNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection())->c_str()); viewDirection = static_cast(viewDirectionNum); } //get mapper slot id mitk::BaseRenderer::MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID()) != NULL) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID())->c_str()); mapperID = static_cast(mapperIDNum); } // Get Size of Render Windows int size[3]; size[0]=size[1]=size[2]=0; if(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX()) != NULL) { size[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX())->c_str()); } if(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY()) != NULL) { size[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY())->c_str()); } if(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ()) != NULL) { size[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ())->c_str()); } //create renderWindow, renderer and dispatcher mitk::RenderWindow::Pointer rw = mitk::RenderWindow::New(NULL, rendererName, rm); //VtkRenderWindow is created within constructor if NULL if (size[0] != 0 && size[1] != 0) { rw->SetSize(size[0],size[1]); rw->GetRenderer()->Resize( size[0],size[1]); } //set storage of renderer rw->GetRenderer()->SetDataStorage(m_DataStorage); //set view direction to axial rw->GetSliceNavigationController()->SetDefaultViewDirection( viewDirection ); //set renderer to render 2D rw->GetRenderer()->SetMapperID(mapperID); rw->GetRenderer()->PrepareRender(); // Some more magic for the 3D render window case: // Camera view direction, position and focal point if (mapperID == mitk::BaseRenderer::Standard3D) { if(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX()) != NULL) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX())->c_str()); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY())->c_str()); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ())->c_str()); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX()) != NULL) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX())->c_str()); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY())->c_str()); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ())->c_str()); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if(element->Attribute(mitk::InteractionEventConst::xmlViewUpX()) != NULL) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX())->c_str()); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY())->c_str()); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ())->c_str()); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } } rw->GetVtkRenderWindow()->Render(); rw->GetVtkRenderWindow()->WaitForCompletion(); //connect SliceNavigationControllers to timestep changed event of TimeNavigationController rw->GetSliceNavigationController()->ConnectGeometryTimeEvent(rm->GetTimeNavigationController(), false); rm->GetTimeNavigationController()->ConnectGeometryTimeEvent(rw->GetSliceNavigationController(), false); //add to list of kown render windows m_RenderWindowList.push_back(rw); } //TODO: check the following lines taken from QmitkStdMultiWidget and adapt them to be executed in our code here. // mitkWidget1->GetSliceNavigationController() // ->ConnectGeometrySendEvent(mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); //########### register display interactor to handle scroll events ################## //use MouseModeSwitcher to ensure that the statemachine of DisplayInteractor is loaded correctly m_MouseModeSwitcher = mitk::MouseModeSwitcher::New(); } else { mitkThrow() << "Can not load interaction xml file <" << m_InteractionFilePath << ">"; } // WARNING assumes a 3D window exists !!!! this->AddDisplayPlaneSubTree(); } mitk::InteractionTestHelper::~InteractionTestHelper() { mitk::RenderingManager* rm = mitk::RenderingManager::GetInstance(); //unregister renderers InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); - for(; it != end; it++) + for(; it != end; ++it) { rm->GetTimeNavigationController()->Disconnect((*it)->GetSliceNavigationController()); (*it)->GetSliceNavigationController()->Disconnect(rm->GetTimeNavigationController()); mitk::BaseRenderer::RemoveInstance((*it)->GetVtkRenderWindow()); } rm->RemoveAllObservers(); } mitk::DataStorage::Pointer mitk::InteractionTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::InteractionTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); this->Set3dCameraSettings(); } void mitk::InteractionTestHelper::PlaybackInteraction() { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); //load events if not loaded yet if(m_Events.empty()) this->LoadInteraction(); InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); - for(; it != end; it++) + for(; it != end; ++it) { (*it)->GetRenderer()->PrepareRender(); (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); it = m_RenderWindowList.begin(); - for(; it != end; it++) + for(; it != end; ++it) { (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } //mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); //playback all events in queue for (unsigned long i=0; i < m_Events.size(); ++i) { //let dispatcher of sending renderer process the event m_Events.at(i)->GetSender()->GetDispatcher()->ProcessEvent(m_Events.at(i)); } if (false) { it--; (*it)->GetVtkRenderWindow()->GetInteractor()->Start(); } } void mitk::InteractionTestHelper::LoadInteraction() { //load interaction pattern from xml file std::ifstream xmlStream(m_InteractionFilePath.c_str()); mitk::XML2EventParser parser(xmlStream); m_Events = parser.GetInteractions(); xmlStream.close(); // Avoid VTK warning: Trying to delete object with non-zero reference count. parser.SetReferenceCount(0); } void mitk::InteractionTestHelper::SetTimeStep(int newTimeStep) { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); bool timeStepIsvalid = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetCreatedWorldGeometry()->IsValidTimeStep(newTimeStep); if(timeStepIsvalid) { mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(newTimeStep); } } mitk::RenderWindow* mitk::InteractionTestHelper::GetRenderWindowByName(const std::string &name) { InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); - for(; it != end; it++) + for(; it != end; ++it) { if( name.compare( (*it)->GetRenderer()->GetName() ) == 0) return (*it).GetPointer(); } return NULL; } mitk::RenderWindow* mitk::InteractionTestHelper::GetRenderWindowByDefaultViewDirection(mitk::SliceNavigationController::ViewDirection viewDirection) { InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); - for(; it != end; it++) + for(; it != end; ++it) { if( viewDirection == (*it)->GetSliceNavigationController()->GetDefaultViewDirection() ) return (*it).GetPointer(); } return NULL; } mitk::RenderWindow* mitk::InteractionTestHelper::GetRenderWindow(unsigned int index) { if( index < m_RenderWindowList.size() ) { return m_RenderWindowList.at(index).GetPointer(); } else { return NULL; } } void mitk::InteractionTestHelper::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; mitk::IntProperty::Pointer layer = mitk::IntProperty::New(1000); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetProperty("name", mitk::StringProperty::New("Widgets")); node->SetProperty("helper object", mitk::BoolProperty::New(true)); m_DataStorage->Add(node); for (auto it : m_RenderWindowList) { if (it->GetRenderer()->GetMapperID() == BaseRenderer::Standard3D) continue; // ... of widget 1 mitk::DataNode::Pointer planeNode1 = (mitk::BaseRenderer::GetInstance(it->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); planeNode1->SetProperty("visible", mitk::BoolProperty::New(true)); planeNode1->SetProperty("name", mitk::StringProperty::New("widget1Plane")); planeNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); planeNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); planeNode1->SetProperty("layer", layer); planeNode1->SetColor(1.0, 0.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); planeNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_DataStorage->Add(planeNode1, node); } } void mitk::InteractionTestHelper::Set3dCameraSettings() { TiXmlDocument document(m_InteractionFilePath); bool loadOkay = document.LoadFile(); if (loadOkay) { //for each renderer found create a render window and configure for( TiXmlElement* element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions())->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot())->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer()); element != NULL; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer()) ) { //get name of renderer const char* rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); //get mapper slot id mitk::BaseRenderer::MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID()) != NULL) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID())->c_str()); mapperID = static_cast(mapperIDNum); } if (mapperID == mitk::BaseRenderer::Standard3D) { RenderWindow* namedRenderer = nullptr; for (auto it : m_RenderWindowList) { if (strcmp(it->GetRenderer()->GetName(), rendererName) == 0) { namedRenderer = it.GetPointer(); break; } } if (namedRenderer == nullptr) { MITK_ERROR << "No match for render window was found."; return; } namedRenderer->GetRenderer()->PrepareRender(); if(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX()) != NULL) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX())->c_str()); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY())->c_str()); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ())->c_str()); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX()) != NULL) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX())->c_str()); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY())->c_str()); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ())->c_str()); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if(element->Attribute(mitk::InteractionEventConst::xmlViewUpX()) != NULL) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX())->c_str()); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY())->c_str()); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ())->c_str()); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } namedRenderer->GetVtkRenderWindow()->Render(); } } } } diff --git a/Modules/Core/include/mitkDicomSeriesReader.txx b/Modules/Core/include/mitkDicomSeriesReader.txx index 9b9b3afaa6..9265b29a55 100644 --- a/Modules/Core/include/mitkDicomSeriesReader.txx +++ b/Modules/Core/include/mitkDicomSeriesReader.txx @@ -1,298 +1,297 @@ /*=================================================================== 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 MITKDICOMSERIESREADER_TXX_ #define MITKDICOMSERIESREADER_TXX_ #include #include #include #include #include #include #include #include #include namespace mitk { template Image::Pointer DicomSeriesReader::LoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock ) { mitk::Image::Pointer image = mitk::Image::New(); // It is 3D+t! Read it and store into mitk image typedef itk::Image ImageType; typedef itk::ImageSeriesReader ReaderType; typename ReaderType::Pointer reader = ReaderType::New(); reader->SetImageIO(io); reader->ReverseOrderOff(); if (command) { reader->AddObserver(itk::ProgressEvent(), command); } - unsigned int act_volume = 1u; - if (preLoadedImageBlock.IsNull()) { reader->SetFileNames(imageBlocks.front()); reader->Update(); typename ImageType::Pointer readVolume = reader->GetOutput(); // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position if (correctTilt) { readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); } unsigned int volume_count = imageBlocks.size(); image->InitializeByItk( readVolume.GetPointer(), 1, volume_count); image->SetImportVolume( readVolume->GetBufferPointer(), 0u); FixSpacingInformation( image, imageBlockDescriptor ); } else { StringContainer fakeList; fakeList.push_back( imageBlocks.front().front() ); reader->SetFileNames(fakeList); // only ONE first filename to get MetaDataDictionary image = preLoadedImageBlock; } gdcm::Scanner scanner; ScanForSliceInformation(imageBlockDescriptor.GetFilenames(), scanner); CopyMetaDataToImageProperties( imageBlocks, scanner.GetMappings(), io, imageBlockDescriptor, image); MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", " << image->GetDimension(1) << ", " << image->GetDimension(2) << ", " << image->GetDimension(3) << "]"; MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", " << image->GetGeometry()->GetSpacing()[1] << ", " << image->GetGeometry()->GetSpacing()[2] << "]"; if ( preLoadedImageBlock.IsNull() ) { + unsigned int act_volume = 1u; for (auto df_it = ++imageBlocks.begin(); df_it != imageBlocks.end(); ++df_it) { reader->SetFileNames(*df_it); reader->Update(); typename ImageType::Pointer readVolume = reader->GetOutput(); if (correctTilt) { readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); } image->SetImportVolume(readVolume->GetBufferPointer(), act_volume++); } } return image; } template Image::Pointer DicomSeriesReader::LoadDICOMByITK( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock ) { /******** Normal Case, 3D (also for GDCM < 2 usable) ***************/ mitk::Image::Pointer image = mitk::Image::New(); typedef itk::Image ImageType; typedef itk::ImageSeriesReader ReaderType; io = DcmIoType::New(); typename ReaderType::Pointer reader = ReaderType::New(); reader->SetImageIO(io); reader->ReverseOrderOff(); if (command) { reader->AddObserver(itk::ProgressEvent(), command); } if (preLoadedImageBlock.IsNull()) { reader->SetFileNames(filenames); reader->Update(); typename ImageType::Pointer readVolume = reader->GetOutput(); // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position if (correctTilt) { readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); } image->InitializeByItk(readVolume.GetPointer()); image->SetImportVolume(readVolume->GetBufferPointer()); } else { image = preLoadedImageBlock; StringContainer fakeList; fakeList.push_back( filenames.front() ); reader->SetFileNames( fakeList ); // we always need to load at least one file to get the MetaDataDictionary reader->Update(); } MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", " << image->GetDimension(1) << ", " << image->GetDimension(2) << "]"; MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", " << image->GetGeometry()->GetSpacing()[1] << ", " << image->GetGeometry()->GetSpacing()[2] << "]"; return image; } template typename ImageType::Pointer DicomSeriesReader::InPlaceFixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo ) { typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetInput( input ); /* Transform for a point is - transform from actual position to index coordinates - apply a shear that undoes the gantry tilt - transform back into world coordinates Anybody who does this in a simpler way: don't forget to write up how and why your solution works */ typedef itk::ScalableAffineTransform< double, ImageType::ImageDimension > TransformType; typename TransformType::Pointer transformShear = TransformType::New(); /** - apply a shear and spacing correction to the image block that corrects the ITK reader's error - ITK ignores the shear and loads slices into an orthogonal volume - ITK calculates the spacing from the origin distance, which is more than the actual spacing with gantry tilt images - to undo the effect - we have calculated some information in tiltInfo: - the shift in Y direction that is added with each additional slice is the most important information - the Y-shift is calculated in mm world coordinates - we apply a shearing transformation to the ITK-read image volume - to do this locally, - we transform the image volume back to origin and "normal" orientation by applying the inverse of its transform (this brings us into the image's "index coordinate" system) - we apply a shear with the Y-shift factor put into a unit transform at row 1, col 2 - we transform the image volume back to its actual position (from index to world coordinates) - we lastly apply modify the image spacing in z direction by replacing this number with the correctly calulcated inter-slice distance */ ScalarType factor = tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates() / input->GetSpacing()[1]; // row 1, column 2 corrects shear in parallel to Y axis, proportional to distance in Z direction transformShear->Shear( 1, 2, factor ); typename TransformType::Pointer imageIndexToWorld = TransformType::New(); imageIndexToWorld->SetOffset( input->GetOrigin().GetVectorFromOrigin() ); typename TransformType::MatrixType indexToWorldMatrix; indexToWorldMatrix = input->GetDirection(); typename ImageType::DirectionType scale; for ( unsigned int i = 0; i < ImageType::ImageDimension; i++ ) { scale[i][i] = input->GetSpacing()[i]; } indexToWorldMatrix *= scale; imageIndexToWorld->SetMatrix( indexToWorldMatrix ); typename TransformType::Pointer imageWorldToIndex = TransformType::New(); imageIndexToWorld->GetInverse( imageWorldToIndex ); typename TransformType::Pointer gantryTiltCorrection = TransformType::New(); gantryTiltCorrection->Compose( imageWorldToIndex ); gantryTiltCorrection->Compose( transformShear ); gantryTiltCorrection->Compose( imageIndexToWorld ); resampler->SetTransform( gantryTiltCorrection ); typedef itk::LinearInterpolateImageFunction< ImageType, double > InterpolatorType; typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); resampler->SetInterpolator( interpolator ); /* This would be the right place to invent a meaningful value for positions outside of the image. For CT, HU -1000 might be meaningful, but a general solution seems not possible. Even for CT, -1000 would only look natural for many not all images. */ // TODO use (0028,0120) Pixel Padding Value if present resampler->SetDefaultPixelValue( itk::NumericTraits< typename ImageType::PixelType >::min() ); // adjust size in Y direction! (maybe just transform the outer last pixel to see how much space we would need resampler->SetOutputParametersFromImage( input ); // we basically need the same image again, just sheared // if tilt positive, then we need additional pixels BELOW origin, otherwise we need pixels behind the end of the block // in any case we need more size to accomodate shifted slices typename ImageType::SizeType largerSize = resampler->GetSize(); // now the resampler already holds the input image's size. largerSize[1] += static_cast(tiltInfo.GetTiltCorrectedAdditionalSize() / input->GetSpacing()[1]+ 2.0); resampler->SetSize( largerSize ); // in SOME cases this additional size is below/behind origin if ( tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates() > 0.0 ) { typename ImageType::DirectionType imageDirection = input->GetDirection(); Vector3D yDirection; yDirection[0] = imageDirection[0][1]; yDirection[1] = imageDirection[1][1]; yDirection[2] = imageDirection[2][1]; yDirection.Normalize(); typename ImageType::PointType shiftedOrigin; shiftedOrigin = input->GetOrigin(); // add some pixels to make everything fit shiftedOrigin[0] -= yDirection[0] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]); shiftedOrigin[1] -= yDirection[1] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]); shiftedOrigin[2] -= yDirection[2] * (tiltInfo.GetTiltCorrectedAdditionalSize() + 1.0 * input->GetSpacing()[1]); resampler->SetOutputOrigin( shiftedOrigin ); } resampler->Update(); typename ImageType::Pointer result = resampler->GetOutput(); // ImageSeriesReader calculates z spacing as the distance between the first two origins. // This is not correct in case of gantry tilt, so we set our calculated spacing. typename ImageType::SpacingType correctedSpacing = result->GetSpacing(); correctedSpacing[2] = tiltInfo.GetRealZSpacing(); result->SetSpacing( correctedSpacing ); return result; } } #endif diff --git a/Modules/Core/include/mitkSlicedGeometry3D.h b/Modules/Core/include/mitkSlicedGeometry3D.h index 571eb84f10..9b16df4575 100644 --- a/Modules/Core/include/mitkSlicedGeometry3D.h +++ b/Modules/Core/include/mitkSlicedGeometry3D.h @@ -1,321 +1,321 @@ /*=================================================================== 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 MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #define MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD #include "mitkBaseGeometry.h" #include "mitkPlaneGeometry.h" namespace mitk { class SliceNavigationController; class NavigationController; /** \brief Describes the geometry of a data object consisting of slices. * * A PlaneGeometry can be requested for each slice. In the case of * \em evenly-spaced, \em plane geometries (m_EvenlySpaced==true), * only the 2D-geometry of the first slice has to be set (to an instance of * PlaneGeometry). The 2D geometries of the other slices are calculated * by shifting the first slice in the direction m_DirectionVector by * m_Spacing.z * sliceNumber. The m_Spacing member (which is only * relevant in the case m_EvenlySpaced==true) descibes the size of a voxel * (in mm), i.e., m_Spacing.x is the voxel width in the x-direction of the * plane. It is derived from the reference geometry of this SlicedGeometry3D, * which usually would be the global geometry describing how datasets are to * be resliced. * * By default, slices are oriented in the direction of one of the main axes * (x, y, z). However, by means of rotation, it is possible to realign the * slices in any possible direction. In case of an inclined plane, the spacing * is derived as a product of the (regular) geometry spacing and the direction * vector of the plane. * * SlicedGeometry3D and the associated PlaneGeometries 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 * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \a itk::DataObject::UpdateOutputInformation(). * * Rule: everything is in mm (or ms for temporal information) if not * stated otherwise. * * \warning The hull (i.e., transform, bounding-box and * time-bounds) is only guaranteed to be up-to-date after calling * UpdateInformation(). * * \ingroup Geometry */ class MITKCORE_EXPORT SlicedGeometry3D : public mitk::BaseGeometry { public: mitkClassMacro(SlicedGeometry3D, BaseGeometry) /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** * \brief Returns the PlaneGeometry of the slice (\a s). * * If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing[3]*s * in the direction of m_DirectionVector. * * \warning The PlaneGeometries are not necessarily up-to-date and not even * initialized. * * The PlaneGeometries 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 * * \sa itk::ProcessObject::GenerateOutputInformation(), * \sa itk::DataObject::CopyInformation() and * \sa itk::DataObject::UpdateOutputInformation(). */ virtual mitk::PlaneGeometry* GetPlaneGeometry(int s) const; /** * \deprecatedSince{2014_10} Please use GetPlaneGeometry */ DEPRECATED(const PlaneGeometry* GetGeometry2D(int s)){ return GetPlaneGeometry(s); } /** * \deprecatedSince{2014_10} Please use SetPlaneGeometry */ DEPRECATED(void SetGeometry2D(PlaneGeometry* geo, int s)){ SetPlaneGeometry(geo, s); } //##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) override; //virtual void SetTimeBounds( const mitk::TimeBounds& timebounds ); virtual const mitk::BoundingBox* GetBoundingBox() const override; /** * \brief Get the number of slices */ itkGetConstMacro(Slices, unsigned int) /** * \brief Set PlaneGeometry of slice \a s. */ virtual bool SetPlaneGeometry(mitk::PlaneGeometry *geometry2D, int s); /** * \brief Check whether a slice exists */ virtual bool IsValidSlice(int s = 0) const; virtual void SetReferenceGeometry(BaseGeometry *referenceGeometry); /** * \brief Set the SliceNavigationController corresponding to this sliced * geometry. * * The SNC needs to be informed when the number of slices in the geometry * changes, which can occur whenthe slices are re-oriented by rotation. */ virtual void SetSliceNavigationController( mitk::SliceNavigationController *snc); mitk::SliceNavigationController *GetSliceNavigationController(); /** * \brief Set/Get whether the SlicedGeometry3D is evenly-spaced * (m_EvenlySpaced) * * If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored for * the requested slice, and (c) the first slice (s=0) is a PlaneGeometry * instance, then we calculate the geometry of the requested as the plane * of the first slice shifted by m_Spacing.z * s in the direction of * m_DirectionVector. * * \sa GetPlaneGeometry */ itkGetConstMacro(EvenlySpaced, bool) virtual void SetEvenlySpaced(bool on = true); /** * \brief Set/Get the vector between slices for the evenly-spaced case * (m_EvenlySpaced==true). * * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. * * \sa m_DirectionVector */ virtual void SetDirectionVector(const mitk::Vector3D& directionVector); itkGetConstMacro(DirectionVector, const mitk::Vector3D&) virtual itk::LightObject::Pointer InternalClone() const override; static const std::string SLICES; const static std::string DIRECTION_VECTOR; const static std::string EVENLY_SPACED; /** * \brief Tell this instance how many PlaneGeometries it shall manage. Bounding * box and the PlaneGeometries must be set additionally by calling the respective * methods! * * \warning Bounding box and the 2D-geometries must be set additionally: use * SetBounds(), SetGeometry(). */ virtual void InitializeSlicedGeometry(unsigned int slices); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided PlaneGeometry that is used as the first slice and * for spacing calculation. * * Initializes the bounding box according to the width/height of the * PlaneGeometry and \a slices. The spacing is calculated from the PlaneGeometry. */ virtual void InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices, bool flipped = false); /** * \brief Completely initialize this instance as evenly-spaced with slices * parallel to the provided PlaneGeometry that is used as the first slice and * for spacing calculation (except z-spacing). * * Initializes the bounding box according to the width/height of the * PlaneGeometry and \a slices. The x-/y-spacing is calculated from the * PlaneGeometry. */ virtual void InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, mitk::ScalarType zSpacing, unsigned int slices, bool flipped = false); /** * \brief Completely initialize this instance as evenly-spaced plane slices * parallel to a side of the provided BaseGeometry and using its spacing * information. * * Initializes the bounding box according to the width/height of the * BaseGeometry and the number of slices according to * BaseGeometry::GetExtent(2). * * \param planeorientation side parallel to which the slices will be oriented * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Axial, for other plane locations respectively) * \param frontside defines the side of the plane (the definition of * front/back is somewhat arbitrary) * * \param rotate rotates the plane by 180 degree around its normal (the * definition of rotated vs not rotated is somewhat arbitrary) */ virtual void InitializePlanes(const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top = true, bool frontside = true, bool rotated = false); virtual void SetImageGeometry(const bool isAnImageGeometry) override; virtual void ExecuteOperation(Operation* operation) override; - static double CalculateSpacing(const mitk::Vector3D spacing, const mitk::Vector3D &d); + static double CalculateSpacing(const mitk::Vector3D &spacing, const mitk::Vector3D &d); protected: SlicedGeometry3D(); SlicedGeometry3D(const SlicedGeometry3D& other); virtual ~SlicedGeometry3D(); /** * Reinitialize plane stack after rotation. More precisely, the first plane * of the stack needs to spatially aligned, in two respects: * * 1. Re-alignment with respect to the dataset center; this is necessary * since the distance from the first plane to the center could otherwise * continuously decrease or increase. * 2. Re-alignment with respect to a given reference point; the reference * point is a location which the user wants to be exactly touched by one * plane of the plane stack. The first plane is minimally shifted to * ensure this touching. Usually, the reference point would be the * point around which the geometry is rotated. */ virtual void ReinitializePlanes(const Point3D ¢er, const Point3D &referencePoint); ScalarType GetLargestExtent(const BaseGeometry *geometry); void PrintSelf(std::ostream& os, itk::Indent indent) const override; /** Calculate "directed spacing", i.e. the spacing in directions * non-orthogonal to the coordinate axes. This is done via the * ellipsoid equation. */ double CalculateSpacing(const mitk::Vector3D &direction) const; /** The extent of the slice stack, i.e. the number of slices, depends on the * plane normal. For rotated geometries, the geometry's transform needs to * be accounted in this calculation. */ mitk::Vector3D AdjustNormal(const mitk::Vector3D &normal) const; /** * Container for the 2D-geometries contained within this SliceGeometry3D. */ mutable std::vector m_PlaneGeometries; /** * If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored * for the requested slice, and (c) the first slice (s=0) * is a PlaneGeometry instance, then we calculate the geometry of the * requested as the plane of the first slice shifted by m_Spacing.z*s * in the direction of m_DirectionVector. * * \sa GetPlaneGeometry */ bool m_EvenlySpaced; /** * Vector between slices for the evenly-spaced case (m_EvenlySpaced==true). * If the direction-vector is (0,0,0) (the default) and the first * 2D geometry is a PlaneGeometry, then the direction-vector will be * calculated from the plane normal. */ mutable mitk::Vector3D m_DirectionVector; /** Number of slices this SliceGeometry3D is descibing. */ unsigned int m_Slices; /** Underlying BaseGeometry for this SlicedGeometry */ mitk::BaseGeometry *m_ReferenceGeometry; /** SNC correcsponding to this geometry; used to reflect changes in the * number of slices due to rotation. */ //mitk::NavigationController *m_NavigationController; mitk::SliceNavigationController *m_SliceNavigationController; //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; virtual void PreSetSpacing(const mitk::Vector3D& aSpacing) override; }; } // namespace mitk #endif /* MITKSLICEDGEOMETRY3D_H_HEADER_INCLUDED_C1EBD0AD */ diff --git a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp index f921724967..457a8e2e37 100644 --- a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp @@ -1,451 +1,451 @@ /*=================================================================== 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 "mitkExtractSliceFilter.h" #include #include #include #include #include #include #include mitk::ExtractSliceFilter::ExtractSliceFilter(vtkImageReslice* reslicer ){ if(reslicer == nullptr){ m_Reslicer = vtkSmartPointer::New(); } else { m_Reslicer = reslicer; } m_TimeStep = 0; m_Reslicer->ReleaseDataFlagOn(); m_InterpolationMode = ExtractSliceFilter::RESLICE_NEAREST; m_ResliceTransform = nullptr; m_InPlaneResampleExtentByGeometry = false; m_OutPutSpacing = new mitk::ScalarType[2]; m_OutputDimension = 2; m_ZSpacing = 1.0; m_ZMin = 0; m_ZMax = 0; m_VtkOutputRequested = false; m_BackgroundLevel = -32768.0; } mitk::ExtractSliceFilter::~ExtractSliceFilter(){ m_ResliceTransform = nullptr; m_WorldGeometry = nullptr; delete [] m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateOutputInformation(){ Superclass::GenerateOutputInformation(); //TODO try figure out how to set the specs of the slice before it is actually extracted /*Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; unsigned int dimensions[2]; dimensions[0] = m_WorldGeometry->GetExtent(0); dimensions[1] = m_WorldGeometry->GetExtent(1); output->Initialize(input->GetPixelType(), 2, dimensions, 1);*/ } void mitk::ExtractSliceFilter::GenerateInputRequestedRegion(){ //As we want all pixel information fo the image in our plane, the requested region //is set to the largest possible region in the image. //This is needed because an oblique plane has a larger extent then the image //and the in pipeline it is checked via PropagateResquestedRegion(). But the //extent of the slice is actually fitting because it is oblique within the image. ImageToImageFilter::InputImagePointer input = const_cast< ImageToImageFilter::InputImageType* > ( this->GetInput() ); input->SetRequestedRegionToLargestPossibleRegion(); } mitk::ScalarType* mitk::ExtractSliceFilter::GetOutputSpacing(){ return m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateData(){ mitk::Image *input = const_cast< mitk::Image * >( this->GetInput() ); if (!input) { MITK_ERROR << "mitk::ExtractSliceFilter: No input image available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No input image available. Please set the input!"); return; } if(!m_WorldGeometry) { MITK_ERROR << "mitk::ExtractSliceFilter: No Geometry for reslicing available." << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No Geometry for reslicing available."); return; } const TimeGeometry* inputTimeGeometry = this->GetInput()->GetTimeGeometry(); if ( ( inputTimeGeometry == nullptr ) || ( inputTimeGeometry->CountTimeSteps() <= 0 ) ) { itkWarningMacro(<<"Error reading input image TimeGeometry."); return; } // is it a valid timeStep? if ( inputTimeGeometry->IsValidTimeStep( m_TimeStep ) == false ) { itkWarningMacro(<<"This is not a valid timestep: "<< m_TimeStep ); return; } // check if there is something to display. if ( ! input->IsVolumeSet( m_TimeStep ) ) { itkWarningMacro(<<"No volume data existent at given timestep "<< m_TimeStep ); return; } /*================#BEGIN setup vtkImageRslice properties================*/ Point3D origin; Vector3D right, bottom, normal; double widthInMM, heightInMM; Vector2D extent; const PlaneGeometry* planeGeometry = dynamic_cast(m_WorldGeometry); //Code for curved planes, mostly taken 1:1 from imageVtkMapper2D and not tested yet. // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a ThinPlateSplineCurvedGeometry ) const mitk::AbstractTransformGeometry* abstractGeometry = dynamic_cast< const AbstractTransformGeometry * >(m_WorldGeometry); if(abstractGeometry != nullptr) { m_ResliceTransform = abstractGeometry; extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkSmartPointer composedResliceTransform = vtkSmartPointer::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputTimeGeometry->GetGeometryForTimeStep( m_TimeStep )->GetVtkTransform()->GetLinearInverse() ); composedResliceTransform->Concatenate( abstractGeometry->GetVtkAbstractTransform() ); m_Reslicer->SetResliceTransform( composedResliceTransform ); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see PlaneGeometryDataVtkMapper3D) // Note: Backgroundlevel was hardcoded before to -1023 m_Reslicer->SetBackgroundLevel( m_BackgroundLevel ); } else{ if ( planeGeometry != nullptr ) { //if the worldGeomatry is a PlaneGeometry everthing is straight forward origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector( 0 ); bottom = planeGeometry->GetAxisVector( 1 ); normal = planeGeometry->GetNormal(); if ( m_InPlaneResampleExtentByGeometry ) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent( 0 ); extent[1] = m_WorldGeometry->GetExtent( 1 ); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. Vector3D rightInIndex, bottomInIndex; inputTimeGeometry->GetGeometryForTimeStep( m_TimeStep )->WorldToIndex( right, rightInIndex ); inputTimeGeometry->GetGeometryForTimeStep( m_TimeStep )->WorldToIndex( bottom, bottomInIndex ); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM( 0 ); heightInMM = m_WorldGeometry->GetExtentInMM( 1 ); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); /* * Transform the origin to center based coordinates. * Note: * This is needed besause vtk's origin is center based too (!!!) ( see 'The VTK book' page 88 ) * and the worldGeometry surrouding the image is no imageGeometry. So the worldGeometry * has its origin at the corner of the voxel and needs to be transformed. */ origin += right * ( m_OutPutSpacing[0] * 0.5 ); origin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //set the tranform for reslicing. // Use inverse transform of the input geometry for reslicing the 3D image. // This is needed if the image volume already transformed if(m_ResliceTransform.IsNotNull()) m_Reslicer->SetResliceTransform(m_ResliceTransform->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see PlaneGeometryDataVtkMapper3D), // else the background of the image turns out gray // Note: Backgroundlevel was hardcoded to -32768 m_Reslicer->SetBackgroundLevel( m_BackgroundLevel ); } else { itkExceptionMacro("mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"); return; } } if(m_ResliceTransform.IsNotNull()){ //if the resliceTransform is set the reslice axis are recalculated. //Thus the geometry information is not fitting. Therefor a unitSpacingFilter //is used to set up a global spacing of 1 and compensate the transform. vtkSmartPointer unitSpacingImageFilter = vtkSmartPointer::New() ; unitSpacingImageFilter->ReleaseDataFlagOn(); unitSpacingImageFilter->SetOutputSpacing( 1.0, 1.0, 1.0 ); unitSpacingImageFilter->SetInputData( input->GetVtkImageData(m_TimeStep) ); m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort() ); } else { //if no tranform is set the image can be used directly m_Reslicer->SetInputData(input->GetVtkImageData(m_TimeStep)); } /*setup the plane where vktImageReslice extracts the slice*/ //ResliceAxesOrigin is the ancor point of the plane double originInVtk[3]; itk2vtk(origin, originInVtk); m_Reslicer->SetResliceAxesOrigin(originInVtk); //the cosines define the plane: x and y are the direction vectors, n is the planes normal //this specifies a matrix 3x3 // x1 y1 n1 // x2 y2 n2 // x3 y3 n3 double cosines[9]; vnl2vtk(right.GetVnlVector(), cosines);//x vnl2vtk(bottom.GetVnlVector(), cosines + 3);//y vnl2vtk(normal.GetVnlVector(), cosines + 6);//n m_Reslicer->SetResliceAxesDirectionCosines(cosines); //we only have one slice, not a volume m_Reslicer->SetOutputDimensionality(m_OutputDimension); //set the interpolation mode for slicing switch(this->m_InterpolationMode){ case RESLICE_NEAREST: m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case RESLICE_LINEAR: m_Reslicer->SetInterpolationModeToLinear(); break; case RESLICE_CUBIC: m_Reslicer->SetInterpolationModeToCubic(); break; default: //the default interpolation used by mitk m_Reslicer->SetInterpolationModeToNearestNeighbor(); } /*========== BEGIN setup extent of the slice ==========*/ int xMin, xMax, yMin, yMax; xMin = yMin = 0; xMax = static_cast< int >( extent[0]); yMax = static_cast< int >( extent[1]); - double sliceBounds[6]; if (m_WorldGeometry->GetReferenceGeometry()) { + double sliceBounds[6]; for (auto & sliceBound : sliceBounds) { sliceBound = 0.0; } if (this->GetClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), planeGeometry, sliceBounds )) { // Calculate output extent (integer values) xMin = static_cast< int >( sliceBounds[0] / m_OutPutSpacing[0] + 0.5 ); xMax = static_cast< int >( sliceBounds[1] / m_OutPutSpacing[0] + 0.5 ); yMin = static_cast< int >( sliceBounds[2] / m_OutPutSpacing[1] + 0.5 ); yMax = static_cast< int >( sliceBounds[3] / m_OutPutSpacing[1] + 0.5 ); } // ELSE we use the default values } // Set the output extents! First included pixel index and last included pixel index // xMax and yMax are one after the last pixel. so they have to be decremented by 1. // In case we have a 2D image, xMax or yMax might be 0. in this case, do not decrement, but take 0. m_Reslicer->SetOutputExtent(xMin, std::max(0, xMax-1), yMin, std::max(0, yMax-1), m_ZMin, m_ZMax ); /*========== END setup extent of the slice ==========*/ m_Reslicer->SetOutputOrigin( 0.0, 0.0, 0.0 ); m_Reslicer->SetOutputSpacing( m_OutPutSpacing[0], m_OutPutSpacing[1], m_ZSpacing ); //TODO check the following lines, they are responsible wether vtk error outputs appear or not m_Reslicer->UpdateWholeExtent(); //this produces a bad allocation error for 2D images //m_Reslicer->GetOutput()->UpdateInformation(); //m_Reslicer->GetOutput()->SetUpdateExtentToWholeExtent(); //start the pipeline m_Reslicer->Update(); /*================ #END setup vtkImageRslice properties================*/ if(m_VtkOutputRequested){ return; //no converting to mitk //no mitk geometry will be set, as the output is vtkImageData only!!! } else { /*================ #BEGIN Get the slice from vtkImageReslice and convert it to mit::Image================*/ vtkImageData* reslicedImage; reslicedImage = m_Reslicer->GetOutput(); if(!reslicedImage) { itkWarningMacro(<<"Reslicer returned empty image"); return; } mitk::Image::Pointer resultImage = this->GetOutput(); //initialize resultimage with the specs of the vtkImageData object returned from vtkImageReslice if (reslicedImage->GetDataDimension() == 1) { // If original image was 2D, the slice might have an y extent of 0. // Still i want to ensure here that Image is 2D resultImage->Initialize(reslicedImage,1,-1,-1,1); } else { resultImage->Initialize(reslicedImage); } //transfer the voxel data resultImage->SetVolume(reslicedImage->GetScalarPointer()); //set the geometry from current worldgeometry for the reusultimage //this is needed that the image has the correct mitk geometry //the originalGeometry is the Geometry of the result slice // mitk::AffineGeometryFrame3D::Pointer originalGeometryAGF = m_WorldGeometry->Clone(); // PlaneGeometry::Pointer originalGeometry = dynamic_cast( originalGeometryAGF.GetPointer() ); PlaneGeometry::Pointer originalGeometry = m_WorldGeometry->Clone(); originalGeometry->GetIndexToWorldTransform()->SetMatrix(m_WorldGeometry->GetIndexToWorldTransform()->GetMatrix()); //the origin of the worldGeometry is transformed to center based coordinates to be an imageGeometry Point3D sliceOrigin = originalGeometry->GetOrigin(); sliceOrigin += right * ( m_OutPutSpacing[0] * 0.5 ); sliceOrigin += bottom * ( m_OutPutSpacing[1] * 0.5 ); //a worldGeometry is no imageGeometry, thus it is manually set to true originalGeometry->ImageGeometryOn(); /*At this point we have to adjust the geometry because the origin isn't correct. The wrong origin is related to the rotation of the current world geometry plane. This causes errors on transfering world to index coordinates. We just shift the origin in each direction about the amount of the expanding (needed while rotating the plane). */ Vector3D axis0 = originalGeometry->GetAxisVector(0); Vector3D axis1 = originalGeometry->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); //adapt the origin. Note that for orthogonal planes the minima are '0' and thus the origin stays the same. sliceOrigin += (axis0 * (xMin * m_OutPutSpacing[0])) + (axis1 * (yMin * m_OutPutSpacing[1])); originalGeometry->SetOrigin(sliceOrigin); originalGeometry->Modified(); resultImage->SetGeometry( originalGeometry ); /*the bounds as well as the extent of the worldGeometry are not adapted correctly during crosshair rotation. This is only a quick fix and has to be evaluated. The new bounds are set via the max values of the calcuted slice extent. It will look like [ 0, x, 0, y, 0, 1]. */ mitk::BoundingBox::BoundsArrayType boundsCopy; boundsCopy[0] = boundsCopy[2] = boundsCopy[4] = 0; boundsCopy[5] = 1; boundsCopy[1] = xMax - xMin; boundsCopy[3] = yMax - yMin; resultImage->GetGeometry()->SetBounds(boundsCopy); /*================ #END Get the slice from vtkImageReslice and convert it to mitk Image================*/ } } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(double bounds[6]){ if(!m_WorldGeometry || !this->GetInput()) return false; return this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), dynamic_cast< const PlaneGeometry * >( m_WorldGeometry ), bounds); } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds( const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds ) { bool b = mitk::PlaneClipping::CalculateClippedPlaneBounds(boundingGeometry, planeGeometry, bounds); return b; } diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index 78331d6fb6..4269646fb5 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,837 +1,837 @@ /*=================================================================== 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 "mitkRenderingManager.h" #include "mitkRenderingManagerFactory.h" #include "mitkBaseRenderer.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkProportionalTimeGeometry.h" #include "mitkCameraController.h" #include #include #include "mitkNumericTypes.h" #include #include #include #include namespace mitk { itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent) RenderingManager::Pointer RenderingManager::s_Instance = 0; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = 0; RenderingManager ::RenderingManager() : m_UpdatePending(false), m_MaxLOD(1), m_LODIncreaseBlocked(false), m_LODAbortMechanismEnabled(false), m_ClippingPlaneEnabled(false), m_TimeNavigationController(SliceNavigationController::New()), m_DataStorage(NULL), m_ConstrainedPanningZooming(true), m_FocusedRenderWindow(nullptr) { m_ShadingEnabled.assign(3, false); m_ShadingValues.assign(4, 0.0); InitializePropertyList(); } RenderingManager ::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it) { (*it)->UnRegister(NULL); RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager ::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory * RenderingManager ::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager ::HasFactory() { if (RenderingManager::s_RenderingManagerFactory) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager ::New() { const RenderingManagerFactory* factory = GetFactory(); if (factory == NULL) return NULL; return factory->CreateRenderingManager(); } RenderingManager * RenderingManager ::GetInstance() { if (!RenderingManager::s_Instance) { if (s_RenderingManagerFactory) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager ::IsInstantiated() { if (RenderingManager::s_Instance) return true; else return false; } void RenderingManager ::AddRenderWindow(vtkRenderWindow *renderWindow) { if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end())) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back(renderWindow); if (m_DataStorage.IsNotNull()) mitk::BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer()); // Register vtkRenderWindow instance renderWindow->Register(NULL); // Add callbacks for rendering abort mechanism //BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback( RenderingManager::RenderingStartCallback); renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback( RenderingManager::RenderingProgressCallback); renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback( RenderingManager::RenderingEndCallback); renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; //Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager ::RemoveRenderWindow(vtkRenderWindow *renderWindow) { if (m_RenderWindowList.erase(renderWindow)) { RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } RenderWindowVector::iterator rw_it = std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow); if (rw_it != m_AllRenderWindows.cend()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(NULL); m_AllRenderWindows.erase(rw_it); } } } const RenderingManager::RenderWindowVector& RenderingManager ::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager ::RequestUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if (!m_UpdatePending) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager ::ForceImmediateUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crashes int *size = renderWindow->GetSize(); if (0 != size[0] && 0 != size[1]) { //prepare the camera etc. before rendering //Note: this is a very important step which should be called before the VTK render! //If you modify the camera anywhere else or after the render call, the scene cannot be seen. mitk::VtkPropRenderer *vPR = dynamic_cast(mitk::BaseRenderer::GetInstance(renderWindow)); if (vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager ::RequestUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->RequestUpdate(it->first); } } } void RenderingManager ::ForceImmediateUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage *ds) { if (!ds) return; // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("includeInBoundingBox" , mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(pred); // calculate bounding geometry of these nodes mitk::TimeGeometry::Pointer bounds = ds->ComputeBoundingGeometry3D(rs, "visible"); // initialize the views to the bounding geometry this->InitializeViews(bounds); } //TODO_GOETZ // Remove old function, so only this one is working. bool RenderingManager ::InitializeViews(const BaseGeometry * dataGeometry, RequestType type, bool preserveRoughOrientationInWorldSpace) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(dataGeometry->Clone().GetPointer()), 1); return InitializeViews(propTimeGeometry, type, preserveRoughOrientationInWorldSpace); } bool RenderingManager ::InitializeViews(const TimeGeometry * dataGeometry, RequestType type, bool /*preserveRoughOrientationInWorldSpace*/) { MITK_DEBUG << "initializing views"; bool boundingBoxInitialized = false; TimeGeometry::ConstPointer timeGeometry = dataGeometry; TimeGeometry::Pointer modifiedGeometry = NULL; if (dataGeometry != NULL) { modifiedGeometry = dataGeometry->Clone(); } int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((timeGeometry.IsNotNull()) && (const_cast( timeGeometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } if (timeGeometry.IsNotNull()) {// make sure bounding box has an extent bigger than zero in any direction // clone the input geometry //Old Geometry3D::Pointer modifiedGeometry = dynamic_cast( dataGeometry->Clone().GetPointer() ); assert(modifiedGeometry.IsNotNull()); for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++) { //check for equality but for an epsilon if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1])) { newBounds[2 * dimension + 1] += 1; if( Equal( newBounds[ 2 * dimension ], newBounds[ 2 * dimension + 1 ] ) ) // newBounds will still be equal if values are beyond double precision { mitkThrow()<< "One dimension of object data has zero length, please make sure you're not using numbers beyond double precision as coordinates."; } } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } } timeGeometry = modifiedGeometry; RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); baseRenderer->SetConstrainZoomingAndPanning(m_ConstrainedPanningZooming); int id = baseRenderer->GetMapperID(); if (((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) ) { this->InternalViewInitialization(baseRenderer, timeGeometry, boundingBoxInitialized, id); } } if (boundingBoxInitialized) { m_TimeNavigationController->SetInputWorldTimeGeometry(timeGeometry); } m_TimeNavigationController->Update(); this->RequestUpdateAll(type); vtkObject::SetGlobalWarningDisplay(warningLevel); // Inform listeners that views have been initialized this->InvokeEvent(mitk::RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager ::InitializeViews(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); } } this->RequestUpdateAll(type); return true; } bool RenderingManager::InitializeView(vtkRenderWindow * renderWindow, const BaseGeometry * geometry, bool initializeGlobalTimeSNC) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return InitializeView(renderWindow, propTimeGeometry, initializeGlobalTimeSNC); } bool RenderingManager::InitializeView(vtkRenderWindow * renderWindow, const TimeGeometry * geometry, bool initializeGlobalTimeSNC) { bool boundingBoxInitialized = false; int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((geometry != NULL) && (const_cast( geometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, geometry, boundingBoxInitialized, id); if (boundingBoxInitialized && initializeGlobalTimeSNC) { m_TimeNavigationController->SetInputWorldTimeGeometry(geometry); } m_TimeNavigationController->Update(); this->RequestUpdate(renderWindow); vtkObject::SetGlobalWarningDisplay(warningLevel); return boundingBoxInitialized; } bool RenderingManager::InitializeView(vtkRenderWindow * renderWindow) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); this->RequestUpdate(renderWindow); return true; } void RenderingManager::InternalViewInitialization(mitk::BaseRenderer *baseRenderer, const mitk::TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if (boundingBoxInitialized) { // Set geometry for NC nc->SetInputWorldTimeGeometry(geometry); nc->Update(); if (mapperID == BaseRenderer::Standard2D) { // For 2D SNCs, steppers are set so that the cross is centered // in the image nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2); } baseRenderer->GetCameraController()->SetViewToAnterior(); baseRenderer->GetCameraController()->Fit(); } else { nc->Update(); } } const SliceNavigationController* RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController* RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::const_iterator it; int i = 0; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i) { if (it->second == RENDERING_REQUESTED) { this->ForceImmediateUpdate(it->first); } } } void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *) { vtkRenderWindow *renderWindow = dynamic_cast(caller); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; if (renderWindow) { renderWindowList[renderWindow] = RENDERING_INPROGRESS; } renman->m_UpdatePending = false; } void RenderingManager ::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *) { vtkRenderWindow *renderWindow = dynamic_cast(caller); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); if (renman->m_LODAbortMechanismEnabled) { vtkRenderWindow *renderWindow = dynamic_cast(caller); if (renderWindow) { BaseRenderer *renderer = BaseRenderer::GetInstance(renderWindow); if (renderer && (renderer->GetNumberOfVisibleLODEnabledMappers() > 0)) { renman->DoMonitorRendering(); } } } } void RenderingManager ::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *) { vtkRenderWindow *renderWindow = dynamic_cast(caller); mitk::RenderingManager* renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; RendererIntMap &nextLODMap = renman->m_NextLODMap; if (renderWindow) { BaseRenderer *renderer = BaseRenderer::GetInstance(renderWindow); if (renderer) { renderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; // Level-of-Detail handling if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (nextLODMap[renderer] == 0) renman->StartOrResetTimer(); else nextLODMap[renderer] = 0; } } } } bool RenderingManager ::IsRendering() const { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { return true; } } return false; } void RenderingManager ::AbortRendering() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { it->first->SetAbortRender(true); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager ::GetNextLOD(BaseRenderer *renderer) { if (renderer != NULL) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager ::ExecutePendingHighResRenderingRequest() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { BaseRenderer *renderer = BaseRenderer::GetInstance(it->first); if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (m_NextLODMap[renderer] == 0) { m_NextLODMap[renderer] = 1; RequestUpdate(it->first); } } } } void RenderingManager ::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; } //enable/disable shading void RenderingManager ::SetShading(bool state, unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager ::GetShading(unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } //enable/disable the clipping plane void RenderingManager ::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager ::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager ::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector & RenderingManager ::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty* RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty* propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage(DataStorage* storage) { if (storage != NULL) { m_DataStorage = storage; RenderingManager::RenderWindowVector::const_iterator iter; - for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); iter++) + for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter) { mitk::BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer()); } } } mitk::DataStorage* RenderingManager::GetDataStorage() { return m_DataStorage; } void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow) { if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend())) { m_FocusedRenderWindow = focusWindow; this->InvokeEvent(FocusChangedEvent()); return; } MITK_ERROR << "Tried to set a RenderWindow that does not exist."; } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Modules/Core/src/Controllers/mitkVtkLayerController.cpp b/Modules/Core/src/Controllers/mitkVtkLayerController.cpp index 7da4f450d3..5e4b82d2c6 100644 --- a/Modules/Core/src/Controllers/mitkVtkLayerController.cpp +++ b/Modules/Core/src/Controllers/mitkVtkLayerController.cpp @@ -1,343 +1,343 @@ /*=================================================================== 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 "mitkVtkLayerController.h" #include #include #include #include #include mitk::VtkLayerController::vtkLayerControllerMapType mitk::VtkLayerController::s_LayerControllerMap; mitk::VtkLayerController* mitk::VtkLayerController::GetInstance(vtkSmartPointer renWin) { for(auto mapit = s_LayerControllerMap.begin(); - mapit != s_LayerControllerMap.end(); mapit++) + mapit != s_LayerControllerMap.end(); ++mapit) { if( (*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::VtkLayerController::AddInstance(vtkSmartPointer renWin, vtkSmartPointer mitkSceneRenderer) { // ensure that no vtkRenderWindow is managed twice mitk::VtkLayerController::RemoveInstance(renWin); // instanciate controller, add it to the map mitk::VtkLayerController* ControllerInstance = new mitk::VtkLayerController(renWin); ControllerInstance->InsertSceneRenderer(mitkSceneRenderer); s_LayerControllerMap.insert(vtkLayerControllerMapType::value_type(renWin,ControllerInstance)); } void mitk::VtkLayerController::RemoveInstance(vtkSmartPointer renWin) { auto mapit = s_LayerControllerMap.find(renWin); if(mapit != s_LayerControllerMap.end()) { delete mapit->second; s_LayerControllerMap.erase( mapit ); } } mitk::VtkLayerController::VtkLayerController(vtkSmartPointer renderWindow) { m_RenderWindow = renderWindow; m_RenderWindow->Register( nullptr ); m_BackgroundRenderers.clear(); m_ForegroundRenderers.clear(); m_SceneRenderers.clear(); } mitk::VtkLayerController::~VtkLayerController() { if ( m_RenderWindow != nullptr ) { m_RenderWindow->UnRegister( nullptr ); } } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the background. * With forceAbsoluteBackground set true a renderer can be placed at the absolute background of the scene. * Multiple calls with forceAbsoluteBackground set true will set the latest registered renderer as background. */ void mitk::VtkLayerController::InsertBackgroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteBackground) { if(renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); if(forceAbsoluteBackground) { auto it = m_BackgroundRenderers.begin(); m_BackgroundRenderers.insert(it,renderer); } else m_BackgroundRenderers.push_back(renderer); UpdateLayers(); } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the foreground. * With forceAbsoluteBackground set true a renderer can be placed at the absolute foreground of the scene. * Multiple calls with forceAbsoluteForeground set true will set the latest registered renderer as foreground. */ void mitk::VtkLayerController::InsertForegroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteForeground) { if(renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); if(forceAbsoluteForeground) { auto it = m_ForegroundRenderers.begin(); m_ForegroundRenderers.insert(it,renderer); } else m_ForegroundRenderers.push_back(renderer); renderer->PreserveDepthBufferOn(); UpdateLayers(); } /** * Returns the Scene Renderer */ vtkSmartPointer mitk::VtkLayerController::GetSceneRenderer() { if(m_SceneRenderers.size() > 0) { auto it = m_SceneRenderers.begin(); return (*it); } else return nullptr; } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered between background renderers and * foreground renderers. */ void mitk::VtkLayerController::InsertSceneRenderer(vtkSmartPointer renderer) { if(renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); m_SceneRenderers.push_back(renderer); UpdateLayers(); } /** * A renderer which has been inserted via a insert... function can be removed from the vtkRenderWindow with * RemoveRenderer. */ void mitk::VtkLayerController::RemoveRenderer(vtkSmartPointer renderer) { RendererVectorType::iterator it; // background layers if(m_BackgroundRenderers.size() > 0) { it = std::find(m_BackgroundRenderers.begin(),m_BackgroundRenderers.end(),renderer); if(it != m_BackgroundRenderers.end()) { m_BackgroundRenderers.erase(it); UpdateLayers(); return; } } // scene layers if(m_SceneRenderers.size() > 0) { it = std::find(m_SceneRenderers.begin(),m_SceneRenderers.end(),renderer); if(it != m_SceneRenderers.end()) { m_SceneRenderers.erase(it); UpdateLayers(); return; } } // foreground layers if(m_ForegroundRenderers.size() > 0 ) { it = std::find(m_ForegroundRenderers.begin(),m_ForegroundRenderers.end(),renderer); if(it != m_ForegroundRenderers.end()) { m_ForegroundRenderers.erase(it); UpdateLayers(); return; } } } /** * Connects a VtkRenderWindow with the layer controller. */ void mitk::VtkLayerController::SetRenderWindow(vtkSmartPointer renwin) { if(renwin != nullptr) { RendererVectorType::iterator it; // Tell all renderers that there is a new renderwindow - for(it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); it++) + for(it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } - for(it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); it++) + for(it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } - for(it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); it++) + for(it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } // Set the new RenderWindow m_RenderWindow = renwin; } // Now sort renderers and add them to the renderwindow UpdateLayers(); } /** * Returns true if a renderer has been inserted */ bool mitk::VtkLayerController::IsRendererInserted(vtkSmartPointer renderer) { RendererVectorType::iterator it; // background layers if(m_BackgroundRenderers.size() > 0) { it = std::find(m_BackgroundRenderers.begin(),m_BackgroundRenderers.end(),renderer); if ( it != m_BackgroundRenderers.end() ) { return true; } } // scene layers if(m_SceneRenderers.size() > 0) { it = std::find(m_SceneRenderers.begin(),m_SceneRenderers.end(),renderer); if ( it != m_SceneRenderers.end() ) { return true; } } // foreground layers if(m_ForegroundRenderers.size() > 0 ) { it = std::find(m_ForegroundRenderers.begin(),m_ForegroundRenderers.end(),renderer); if ( it != m_ForegroundRenderers.end() ) { return true; } } return false; } /** * Internally used to sort all registered renderers and to connect the with the vtkRenderWindow. * Mention that VTK Version 5 and above is rendering higher numbers in the background and VTK * Verison < 5 in the foreground. */ void mitk::VtkLayerController::UpdateLayers() { // Remove all Renderers from renderwindow vtkSmartPointer v = m_RenderWindow->GetRenderers(); v->RemoveAllItems(); unsigned int numberOfLayers = static_cast(m_BackgroundRenderers.size() + m_SceneRenderers.size() + m_ForegroundRenderers.size()); int currentLayerNumber; bool traverseUpwards; currentLayerNumber = 0; traverseUpwards = true; m_RenderWindow->SetNumberOfLayers(numberOfLayers); RendererVectorType::iterator it; // assign a layer number for the backround renderers - for(it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); it++) + for(it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if(traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } // assign a layer number for the scene renderers - for(it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); it++) + for(it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if(traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } // assign a layer number for the foreground renderers - for(it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); it++) + for(it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if(traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } } /** * Returns the number of renderers in the renderwindow. */ unsigned int mitk::VtkLayerController::GetNumberOfRenderers() { return static_cast(m_BackgroundRenderers.size() + m_SceneRenderers.size() + m_ForegroundRenderers.size()); } void mitk::VtkLayerController::SetEraseForAllRenderers(int i) { this->m_RenderWindow->SetErase(i); RendererVectorType::iterator it; - for(it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); it++) + for(it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) (*it)->SetErase(i); - for(it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); it++) + for(it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) (*it)->SetErase(i); - for(it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); it++) + for(it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) (*it)->SetErase(i); } diff --git a/Modules/Core/src/DataManagement/mitkBaseData.cpp b/Modules/Core/src/DataManagement/mitkBaseData.cpp index ee5b5a8b4c..b0e438e889 100644 --- a/Modules/Core/src/DataManagement/mitkBaseData.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseData.cpp @@ -1,296 +1,295 @@ /*=================================================================== 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 "mitkBaseData.h" #include #include #include #include mitk::BaseData::BaseData() : m_SourceOutputIndexDuplicate(0), m_Initialized(true) { m_TimeGeometry = mitk::ProportionalTimeGeometry::New(); m_PropertyList = PropertyList::New(); } mitk::BaseData::BaseData( const BaseData &other ): itk::DataObject(), mitk::OperationActor(), m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate), m_Initialized(other.m_Initialized) { m_TimeGeometry = dynamic_cast(other.m_TimeGeometry->Clone().GetPointer()); m_PropertyList = other.m_PropertyList->Clone(); } mitk::BaseData::~BaseData() { } void mitk::BaseData::InitializeTimeGeometry(unsigned int timeSteps) { mitk::Geometry3D::Pointer geo3D = mitk::Geometry3D::New(); mitk::BaseGeometry::Pointer baseGeo = dynamic_cast(geo3D.GetPointer()); baseGeo->Initialize(); // The geometry is propagated automatically to the other items, // if EvenlyTimed is true... //Old timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps ); TimeGeometry::Pointer timeGeometry = this->GetTimeGeometry(); timeGeometry->Initialize(); timeGeometry->Expand(timeSteps); for (TimeStepType step = 0; step < timeSteps; ++step) { timeGeometry->SetTimeStepGeometry(baseGeo.GetPointer(),step); } } void mitk::BaseData::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->UpdateBoundingBox(); } } const mitk::TimeGeometry* mitk::BaseData::GetUpdatedTimeGeometry() { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetTimeGeometry(); } void mitk::BaseData::Expand( unsigned int timeSteps ) { if (m_TimeGeometry.IsNotNull() ) { m_TimeGeometry->Expand(timeSteps); } else { this->InitializeTimeGeometry(timeSteps); } } const mitk::BaseGeometry* mitk::BaseData::GetUpdatedGeometry(int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetGeometry(t); } void mitk::BaseData::SetGeometry(BaseGeometry* geometry) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); if(geometry!=nullptr) { timeGeometry->Initialize(geometry, 1); } SetTimeGeometry(timeGeometry); return; } void mitk::BaseData::SetTimeGeometry(TimeGeometry* geometry) { m_TimeGeometry = geometry; this->Modified(); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry* aGeometry3D) { SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); } void mitk::BaseData::SetClonedTimeGeometry(const TimeGeometry* geometry) { TimeGeometry::Pointer clonedGeometry = geometry->Clone(); SetTimeGeometry(clonedGeometry.GetPointer()); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry* aGeometry3D, unsigned int time) { if (m_TimeGeometry) { m_TimeGeometry->SetTimeStepGeometry(static_cast(aGeometry3D->Clone().GetPointer()),time); } } bool mitk::BaseData::IsEmptyTimeStep(unsigned int) const { return IsInitialized() == false; } bool mitk::BaseData::IsEmpty() const { if(IsInitialized() == false) return true; const TimeGeometry* timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); if(timeGeometry == nullptr) return true; unsigned int timeSteps = timeGeometry->CountTimeSteps(); for ( unsigned int t = 0 ; t < timeSteps ; ++t ) { if(IsEmptyTimeStep(t) == false) return false; } return true; } itk::SmartPointer mitk::BaseData::GetSource() const { return static_cast(Superclass::GetSource().GetPointer()); } 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) { TimeGeometry* timeGeom = GetTimeGeometry(); assert (timeGeom != nullptr); - BaseGeometry* geometry; TimeStepType steps = timeGeom->CountTimeSteps(); for (TimeStepType timestep = 0; timestep < steps; ++timestep) { - geometry = GetGeometry(timestep); + auto geometry = GetGeometry(timestep); if (geometry != nullptr) { geometry->SetOrigin(origin); } } } unsigned long mitk::BaseData::GetMTime() const { unsigned long time = Superclass::GetMTime(); if(m_TimeGeometry.IsNotNull()) { if((time < m_TimeGeometry->GetMTime())) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::BaseData::Graft(const itk::DataObject*) { itkExceptionMacro(<< "Graft not implemented for mitk::BaseData subclass " << this->GetNameOfClass()) } void mitk::BaseData::CopyInformation( const itk::DataObject* data ) { const Self* bd = dynamic_cast(data); if (bd != nullptr) { m_PropertyList = bd->GetPropertyList()->Clone(); if (bd->GetTimeGeometry()!=nullptr) { m_TimeGeometry = bd->GetTimeGeometry()->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 << " TimeGeometry: "; if(GetTimeGeometry() == nullptr) os << "NULL" << std::endl; else GetTimeGeometry()->Print(os, indent); // print out all properties PropertyList::Pointer propertyList = this->GetPropertyList(); if (propertyList.IsNotNull() && !propertyList->IsEmpty()) { //general headline os << "Properties of BaseData:" << std::endl; const PropertyList::PropertyMap* map = propertyList->GetMap(); - for (PropertyList::PropertyMap::const_iterator iter = map->begin(); iter != map->end(); iter++) + for (PropertyList::PropertyMap::const_iterator iter = map->begin(); iter != map->end(); ++iter) { os << " " << (*iter).first << " " << (*iter).second->GetValueAsString() << std::endl; } } } diff --git a/Modules/Core/src/DataManagement/mitkDataStorage.cpp b/Modules/Core/src/DataManagement/mitkDataStorage.cpp index d3a243cbc7..586d6969bd 100644 --- a/Modules/Core/src/DataManagement/mitkDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkDataStorage.cpp @@ -1,502 +1,502 @@ /*=================================================================== 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 "mitkDataStorage.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "itkMutexLockHolder.h" #include "itkCommand.h" mitk::DataStorage::DataStorage() : itk::Object() , m_BlockNodeModifiedEvents(false) { } mitk::DataStorage::~DataStorage() { ///// we can not call GetAll() in destructor, because it is implemented in a subclass //SetOfObjects::ConstPointer all = this->GetAll(); //for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); //m_NodeModifiedObserverTags.clear(); //m_NodeDeleteObserverTags.clear(); } void mitk::DataStorage::Add(mitk::DataNode* node, mitk::DataNode* parent) { mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); if (parent != NULL) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const mitk::DataStorage::SetOfObjects* nodes) { if (nodes == NULL) return; for (mitk::DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase* condition) const { mitk::DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode* mitk::DataStorage::GetNamedNode(const char* name) const { if (name == NULL) return NULL; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } mitk::DataNode* mitk::DataStorage::GetNode(const NodePredicateBase* condition) const { if (condition == NULL) return NULL; mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } mitk::DataNode* mitk::DataStorage::GetNamedDerivedNode(const char* name, const mitk::DataNode* sourceNode, bool onlyDirectDerivations) const { if (name == NULL) return NULL; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return NULL; } void mitk::DataStorage::PrintSelf(std::ostream& os, itk::Indent indent) const { //Superclass::PrintSelf(os, indent); mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (mitk::DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != NULL) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; mitk::DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (mitk::DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (mitk::DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects* set, const NodePredicateBase* condition) const { if (set == NULL) return NULL; mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == NULL || condition->CheckNode(it.Value()) == true) //alway copy the set, otherwise the iterator in mitk::DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return mitk::DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; for (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { mitk::PropertyList* pl = nodeIt.Value()->GetPropertyList(); - for (mitk::PropertyList::PropertyMap::const_iterator propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); propIt++) + for (mitk::PropertyList::PropertyMap::const_iterator propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); ++propIt) if (dynamic_cast(propIt->second.GetPointer()) != NULL) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const mitk::DataNode* node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const mitk::DataNode* node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged( itk::Object *caller, const itk::EventObject& ) { const mitk::DataNode* _Node = dynamic_cast(caller); if(_Node) { InteractorChangedNodeEvent.Send( _Node ); } } void mitk::DataStorage::OnNodeModifiedOrDeleted( const itk::Object *caller, const itk::EventObject &event ) { if( m_BlockNodeModifiedEvents ) return; const mitk::DataNode* _Node = dynamic_cast(caller); if(_Node) { const itk::ModifiedEvent* modEvent = dynamic_cast(&event); if(modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners( const mitk::DataNode* _Node ) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must not be yet registered mitk::DataNode* NonConstNode = const_cast(_Node); if(_Node && m_NodeModifiedObserverTags .find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this , &mitk::DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); interactorChangedCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = NonConstNode->AddObserver( mitk::DataNode::InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners( const mitk::DataNode* _Node ) { itk::MutexLockHolder locked(m_MutexOne) ; // node must not be 0 and must be registered mitk::DataNode* NonConstNode = const_cast(_Node); if(_Node && m_NodeModifiedObserverTags .find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags .find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags .find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags .find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D( const SetOfObjects* input, const char* boolPropertyKey, const mitk::BaseRenderer* renderer, const char* boolPropertyKey2) const { if (input == NULL) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer=BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid=0; Point3D point; Vector3D minSpacing; minSpacing.Fill(itk::NumericTraits::max()); ScalarType stmin, stmax; stmin= itk::NumericTraits::NonpositiveMin(); stmax= itk::NumericTraits::max(); ScalarType minimalIntervallSize = stmax; ScalarType minimalTime = stmax; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if((node.IsNotNull()) && (node->GetData() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const TimeGeometry* timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != NULL ) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for(i=0; i<8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if(point[0]*point[0]+point[1]*point[1]+point[2]*point[2] < large) pointscontainer->InsertElement( pointid++, point); else { itkGenericOutputMacro( << "Unrealistically distant corner point encountered. Ignored. Node: " << node ); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i=0; iCountTimeSteps(); i++) { Vector3D spacing = node->GetData()->GetGeometry(i)->GetSpacing(); for (int axis = 0; axis < 3; ++ axis) { if (spacing[axis] < minSpacing[axis]) minSpacing[axis] = spacing[axis]; } const TimeBounds & curTimeBounds = node->GetData()->GetTimeGeometry()->GetTimeBounds(i); // get the minimal time of all objects in the DataStorage if ((curTimeBounds[0]stmin)) { minimalTime=curTimeBounds[0]; } // get the maximal time of all objects in the DataStorage if ((curTimeBounds[1]>maximalTime)&&(curTimeBounds[1]SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps unsigned int numberOfTimeSteps = 1; if (maximalTime==0) // make sure that there is at least one time sliced geometry in the data storage { minimalTime = 0; maximalTime = 1; minimalIntervallSize = 1; } numberOfTimeSteps = static_cast((maximalTime-minimalTime)/minimalIntervallSize); TimeGeometry::Pointer timeGeometry = NULL; if ( result->GetPoints()->Size()>0 ) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); int i; for(i = 0; i < 6; ++i) { bounds[i] /= minSpacing[i/2]; } geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry timeGeometry = ProportionalTimeGeometry::New(); dynamic_cast(timeGeometry.GetPointer())->Initialize(geometry,numberOfTimeSteps); dynamic_cast(timeGeometry.GetPointer())->SetFirstTimePoint(minimalTime); dynamic_cast(timeGeometry.GetPointer())->SetStepDuration(minimalIntervallSize); } return timeGeometry; } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D( const char* boolPropertyKey, const mitk::BaseRenderer* renderer, const char* boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D( const mitk::BaseRenderer* renderer, const char* boolPropertyKey ) { return ComputeBoundingGeometry3D( "visible", renderer, boolPropertyKey ); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox( const char* boolPropertyKey, const mitk::BaseRenderer* renderer, const char* boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer=BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid=0; Point3D point; // Needed for check of zero bounding boxes mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if((node.IsNotNull()) && (node->GetData() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const TimeGeometry* geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != NULL ) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for(i=0; i<8; ++i) { point = geometry->GetCornerPointInWorld(i); if(point[0]*point[0]+point[1]*point[1]+point[2]*point[2] < large) pointscontainer->InsertElement( pointid++, point); else { itkGenericOutputMacro( << "Unrealistically distant corner point encountered. Ignored. Node: " << node ); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds( const char* boolPropertyKey, const mitk::BaseRenderer* renderer, const char* boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; stmin= itk::NumericTraits::NonpositiveMin(); stmax= itk::NumericTraits::max(); timeBounds[0]=stmax; timeBounds[1]=stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if((node.IsNotNull()) && (node->GetData() != NULL) && (node->GetData()->IsEmpty()==false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer) ) { const TimeGeometry* geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != NULL ) { const TimeBounds & curTimeBounds = geometry->GetTimeBounds(); cur=curTimeBounds[0]; //is it after -infinity, but before everything else that we found until now? if((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur=curTimeBounds[1]; //is it before infinity, but after everything else that we found until now? if((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if(!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents( bool block ) { m_BlockNodeModifiedEvents = block; } diff --git a/Modules/Core/src/DataManagement/mitkImage.cpp b/Modules/Core/src/DataManagement/mitkImage.cpp index 8183798cf9..ae2c2506c9 100644 --- a/Modules/Core/src/DataManagement/mitkImage.cpp +++ b/Modules/Core/src/DataManagement/mitkImage.cpp @@ -1,1422 +1,1422 @@ /*=================================================================== 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. ===================================================================*/ //MITK #include "mitkImage.h" #include "mitkImageStatisticsHolder.h" #include "mitkPixelTypeMultiplex.h" #include #include "mitkCompareImageDataFilter.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" //VTK #include //ITK #include //Other #include #define FILL_C_ARRAY( _arr, _size, _value) for(unsigned int i=0u; i<_size; i++) \ { _arr[i] = _value; } mitk::Image::Image() : m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY( m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize( other.GetPixelType(), other.GetDimension(), other.GetDimensions()); //Since the above called "Initialize" method doesn't take the geometry into account we need to set it //here manually TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone(); this->SetTimeGeometry(cloned.GetPointer()); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = const_cast(other).GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = const_cast(other).GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { this->Clear(); m_ReferenceCount = 3; m_ReferenceCount = 0; delete [] m_OffsetTable; delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } 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().IsNull()) return nullptr; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } m_CompleteData=GetChannelData(); // update channel's data // if data was not available at creation point, the m_Data of channel descriptor is NULL // if data present, it won't be overwritten m_ImageDescriptor->GetChannelDescriptor(0).SetData(m_CompleteData->GetData()); return m_CompleteData->GetData(); } template void AccessPixel( const mitk::PixelType ptype, void* data, const unsigned int offset, double& value ) { value = 0.0; if( data == nullptr ) return; if(ptype.GetBpe() != 24) { value = (double) (((T*) data)[ offset ]); } else { const unsigned int rgboffset = offset; double returnvalue = (((T*) data)[rgboffset ]); returnvalue += (((T*) data)[rgboffset + 1]); returnvalue += (((T*) data)[rgboffset + 2]); value = returnvalue; } } double mitk::Image::GetPixelValueByIndex(const itk::Index<3> &position, unsigned int timestep, unsigned int component) { double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } value = 0.0; const unsigned int* imageDims = this->m_ImageDescriptor->GetDimensions(); const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if ( position[0] < 0 || position[1] < 0 || position[2] < 0 ) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } // check if the given position is inside the index range of the image, the 3rd dimension needs to be compared only if the dimension is not 0 else if ( (unsigned int)position[0] >= imageDims[0] || (unsigned int)position[1] >= imageDims[1] || ( imageDims[2] && (unsigned int)position[2] >= imageDims[2] )) { MITK_WARN << "Given position ("<< position << ") is out of image range, returning 0." ; } else { const unsigned int offset = component + ptype.GetNumberOfComponents()*(position[0] + position[1]*imageDims[0] + position[2]*imageDims[0]*imageDims[1] + timestep*imageDims[0]*imageDims[1]*imageDims[2]); mitkPixelTypeMultiplex3( AccessPixel, ptype, this->GetData(), offset, value ); } return value; } double mitk::Image::GetPixelValueByWorldCoordinate(const mitk::Point3D& position, unsigned int timestep, unsigned int component) { double value = 0.0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } itk::Index<3> itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); value = this->GetPixelValueByIndex( itkIndex, timestep, component); return value; } vtkImageData* mitk::Image::GetVtkImageData(int t, int n) { if(m_Initialized==false) { if(GetSource().IsNull()) return nullptr; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } const vtkImageData* mitk::Image::GetVtkImageData(int t, int n) const { if(m_Initialized==false) { if(GetSource().IsNull()) return nullptr; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData_unlocked(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if(IsValidSlice(s,t,n)==false) return nullptr; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos=GetSliceIndex(s,t,n); if(m_Slices[pos].GetPointer()!=nullptr) { 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()!=nullptr) && (vol->IsComplete())) { sl=new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); 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()!=nullptr) && (ch->IsComplete())) { sl=new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); sl->SetComplete(true); return m_Slices[pos]=sl; } // slice is unavailable. Can we calculate it? if((GetSource().IsNotNull()) && (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_unlocked(s,t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData_unlocked(s,t,n,data,importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateSliceData_unlocked(s,t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if(IsValidVolume(t,n)==false) return nullptr; ImageDataItemPointer ch, vol; // volume directly available? int pos=GetVolumeIndex(t,n); vol=m_Volumes[pos]; if((vol.GetPointer()!=nullptr) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=nullptr) && (ch->IsComplete())) { vol=new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); 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 { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol=m_Volumes[pos]; // ok, let's combine the slices! if(vol.GetPointer()==nullptr) { vol=new ImageDataItem( chPixelType, t, 3, m_Dimensions, nullptr, true); } vol->SetComplete(true); size_t size=m_OffsetTable[2]*(ptypeSize); 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); // FIXME mitkIpPicDescriptor * pic = sl->GetPicDescriptor(); // replace old slice with reference to volume sl=new ImageDataItem(*vol, m_ImageDescriptor, t, 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().IsNotNull()) && (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_unlocked(t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData_unlocked(t,n,data,importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateVolumeData_unlocked(t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if(IsValidChannel(n)==false) return nullptr; ImageDataItemPointer ch, vol; ch=m_Channels[n]; if((ch.GetPointer()!=nullptr) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if(IsChannelSet_unlocked(n)) { // if there is only one time frame we do not need to combine anything if(m_Dimensions[3]<=1) { vol=GetVolumeData_unlocked(0,n,data,importMemoryManagement); ch=new ImageDataItem(*vol, m_ImageDescriptor, 0, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=m_Channels[n]; // ok, let's combine the volumes! if(ch.GetPointer()==nullptr) ch=new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); ch->SetComplete(true); size_t size=m_OffsetTable[m_Dimension-1]*(ptypeSize); unsigned int t; auto 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]*(ptypeSize); std::memcpy(static_cast(ch->GetData())+offset, vol->GetData(), size); // REVEIW FIX mitkIpPicDescriptor * pic = vol->GetPicDescriptor(); // replace old volume with reference to channel vol=new ImageDataItem(*ch, m_ImageDescriptor, t, 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=nullptr; for(unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } // REVIEW FIX // 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().IsNotNull()) && (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_unlocked(n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData_unlocked(n,data,importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateChannelData_unlocked(n,data,importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsSliceSet_unlocked(s, t, n); } bool mitk::Image::IsSliceSet_unlocked(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; if(m_Slices[GetSliceIndex(s,t,n)].GetPointer()!=nullptr) { return true; } ImageDataItemPointer ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=nullptr) && (vol->IsComplete())) { return true; } ch=m_Channels[n]; if((ch.GetPointer()!=nullptr) && (ch->IsComplete())) { return true; } return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsVolumeSet_unlocked(t, n); } bool mitk::Image::IsVolumeSet_unlocked(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()!=nullptr) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=nullptr) && (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; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); 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()==nullptr) return false; } if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); sl->Modified(); //we have changed the data: call Modified()! Modified(); } else { sl=AllocateSliceData(s,t,n,data,importMemoryManagement); if(sl.GetPointer()==nullptr) return false; if ( sl->GetData() != data ) std::memcpy(sl->GetData(), data, m_OffsetTable[2]*(ptypeSize)); //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; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); 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()==nullptr) return false; } if ( vol->GetData() != data ) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); vol->Modified(); vol->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { vol=AllocateVolumeData(t,n,data,importMemoryManagement); if(vol.GetPointer()==nullptr) return false; if ( vol->GetData() != data ) { std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( vol->GetData() ); //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; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if(IsChannelSet(n)) { ch=GetChannelData(n,data,importMemoryManagement); if(ch->GetManageMemory()==false) { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==nullptr) return false; } if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->Modified(); ch->SetComplete(true); //we have changed the data: call Modified()! Modified(); } else { ch=AllocateChannelData(n,data,importMemoryManagement); if(ch.GetPointer()==nullptr) return false; if ( ch->GetData() != data ) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData( ch->GetData() ); //we just added a missing Channel, which is not regarded as modification. //Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for( it=m_Slices.begin(), end=m_Slices.end(); it!=end; ++it ) { (*it)=nullptr; } for( it=m_Volumes.begin(), end=m_Volumes.end(); it!=end; ++it ) { (*it)=nullptr; } for( it=m_Channels.begin(), end=m_Channels.end(); it!=end; ++it ) { (*it)=nullptr; } m_CompleteData = nullptr; if( m_ImageStatistics == nullptr) { m_ImageStatistics = new mitk::ImageStatisticsHolder( this ); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1 ); } void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension=dimension; if(!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for(i=0;im_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); 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 = nullptr; return; } for( unsigned int i=0u; im_ImageDescriptor->AddNewChannel( 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]); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step) { timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn(); } SetTimeGeometry(timeGeometry); ImageDataItemPointer dnull=nullptr; 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::BaseGeometry& geometry, unsigned int channels, int tDim ) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry.Clone(), tDim); this->Initialize(type, *timeGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::TimeGeometry& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2)+0.5); dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps(); dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); if (geometry.CountTimeSteps() > 1) { TimeGeometry::Pointer cloned = geometry.Clone(); SetTimeGeometry(cloned.GetPointer()); // make sure the image geometry flag is properly set for all time steps for (TimeStepType step = 0; step < cloned->CountTimeSteps(); ++step) { if( ! cloned->GetGeometryCloneForTimeStep(step)->GetImageGeometry() ) { MITK_WARN("Image.3DnT.Initialize") << " Attempt to initialize an image with a non-image geometry. Re-interpretting the initialization geometry for timestep " << step << " as image geometry, the original geometry remains unchanged."; cloned->GetGeometryForTimeStep(step)->ImageGeometryOn(); } } } else { // make sure the image geometry coming from outside has proper value of the image geometry flag BaseGeometry::Pointer cloned = geometry.GetGeometryCloneForTimeStep(0)->Clone(); if( ! cloned->GetImageGeometry() ) { MITK_WARN("Image.Initialize") << " Attempt to initialize an image with a non-image geometry. Re-interpretting the initialization geometry as image geometry, the original geometry remains unchanged."; cloned->ImageGeometryOn(); } Superclass::SetGeometry( cloned ); } /* //Old //TODO_GOETZ Really necessary? mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBoxInWorld()->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; this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.GetVnlVector().data_block()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); }*/ } void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& 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(), *image->GetTimeGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim, int pDim) { if(vtkimagedata==nullptr) 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(pDim>=0) { tmpDimensions[1]=pDim; if(m_Dimension < 2) m_Dimension = 2; } 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(MakePixelType(vtkimagedata)); Initialize(pixelType, m_Dimension, tmpDimensions, channels); const double *spacinglist = vtkimagedata->GetSpacing(); 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; double 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->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); delete [] tmpDimensions; } 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!=nullptr) 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; } bool mitk::Image::IsValidTimeStep(int t) const { return ( ( m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0 ) || (t == 0) ); } void mitk::Image::Expand(unsigned int timeSteps) { if(timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } 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) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData_unlocked(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos=GetSliceIndex(s,t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if(vol.GetPointer()!=nullptr) { sl=new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); 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()!=nullptr) { sl=new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t) s)*m_OffsetTable[2]+((size_t) t)*m_OffsetTable[3])*(ptypeSize)); 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_unlocked(t,n,nullptr,importMemoryManagement); sl=new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t) s)*m_OffsetTable[2]*(ptypeSize)); 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) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos=GetVolumeIndex(t,n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch=m_Channels[n]; if(ch.GetPointer()!=nullptr) { vol=new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data,importMemoryManagement == ManageMemory, (((size_t) t)*m_OffsetTable[3])*(ptypeSize)); return m_Volumes[pos]=vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if(importMemoryManagement == CopyMemory) { vol=new ImageDataItem( chPixelType, t, 3, m_Dimensions, nullptr, true); if(data != nullptr) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } else { vol=new ImageDataItem( chPixelType, t, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos]=vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { ImageDataItemPointer ch; // allocate new channel if(importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); if(data != nullptr) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); } else { ch=new ImageDataItem(this->m_ImageDescriptor, -1, 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 = nullptr; } void mitk::Image::SetGeometry(BaseGeometry* 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); for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step) GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { - unsigned char i; if(m_Initialized) { + unsigned char i; os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for(i=0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for(unsigned int ch=0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os,indent); } bool mitk::Image::IsRotated() const { const mitk::BaseGeometry* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::ScalarType 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(std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } //## \brief Get the second smallest value for scalar images mitk::ScalarType mitk::Image::GetScalarValue2ndMin(int t) const { return m_ImageStatistics->GetScalarValue2ndMin(t); } mitk::ScalarType mitk::Image::GetScalarValueMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValueMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMinNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMax(int t) const { return m_ImageStatistics->GetScalarValue2ndMax(t); } mitk::ScalarType mitk::Image::GetScalarValueMaxNoRecompute( unsigned int t) const { return m_ImageStatistics->GetScalarValueMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMaxNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetScalarValue2ndMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetCountOfMinValuedVoxels(int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxels(t); } mitk::ScalarType mitk::Image::GetCountOfMaxValuedVoxels(int t) const { return m_ImageStatistics->GetCountOfMaxValuedVoxels(t); } unsigned int mitk::Image::GetCountOfMaxValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMaxValuedVoxelsNoRecompute(t); } unsigned int mitk::Image::GetCountOfMinValuedVoxelsNoRecompute( unsigned int t ) const { return m_ImageStatistics->GetCountOfMinValuedVoxelsNoRecompute(t); } bool mitk::Equal(const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose) { if((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal(const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool verbose) does not work with NULL pointer input."; return false; } return mitk::Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::Image& leftHandSide, const mitk::Image& rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if( rightHandSide.GetDimension() != leftHandSide.GetDimension() ) { if(verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is " << rightHandSide.GetDimension(); } returnValue = false; } // Pair-wise dimension (size) comparison unsigned int minDimensionality = std::min(rightHandSide.GetDimension(),leftHandSide.GetDimension()); for( unsigned int i=0; i< minDimensionality; ++i) { if( rightHandSide.GetDimension(i) != leftHandSide.GetDimension(i) ) { returnValue = false; if(verbose) { MITK_INFO << "[( Image )] dimension differs."; MITK_INFO << "leftHandSide->GetDimension("<GetDimension("<SetInput(0, &rightHandSide); compareFilter->SetInput(1, &leftHandSide); compareFilter->SetTolerance(eps); compareFilter->Update(); if(( !compareFilter->GetResult() ) ) { returnValue = false; if(verbose) { MITK_INFO << "[(Image)] Pixel values differ: "; compareFilter->GetCompareResults().PrintSelf(); } } } return returnValue; } diff --git a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp index dde4f28f9f..10cc6d506c 100644 --- a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp +++ b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp @@ -1,365 +1,364 @@ /*=================================================================== 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 "mitkImageStatisticsHolder.h" #include "mitkHistogramGenerator.h" //#include "mitkImageTimeSelector.h" #include mitk::ImageStatisticsHolder::ImageStatisticsHolder( mitk::Image* image) : m_Image(image)/*, 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()); mitk::HistogramGenerator::Pointer generator = mitk::HistogramGenerator::New(); m_HistogramGeneratorObject = generator; //m_Image = image; // create time selector //this->GetTimeSelector(); } mitk::ImageStatisticsHolder::~ImageStatisticsHolder() { m_HistogramGeneratorObject = nullptr; //m_TimeSelectorForExtremaObject = NULL; //m_Image = NULL; } const mitk::ImageStatisticsHolder::HistogramType* mitk::ImageStatisticsHolder::GetScalarHistogram(int t, unsigned int /*component*/) { mitk::ImageTimeSelector* timeSelector = this->GetTimeSelector(); if(timeSelector!=nullptr) { 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 nullptr; } bool mitk::ImageStatisticsHolder::IsValidTimeStep( int t) const { return m_Image->IsValidTimeStep(t); } mitk::ImageTimeSelector::Pointer mitk::ImageStatisticsHolder::GetTimeSelector() { //if(m_TimeSelectorForExtremaObject.IsNull()) //{ // m_TimeSelectorForExtremaObject = ImageTimeSelector::New(); ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New();//static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); timeSelector->SetInput(m_Image); //} return timeSelector; //static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); } void mitk::ImageStatisticsHolder::Expand( unsigned int timeSteps ) { if(! m_Image->IsValidTimeStep(timeSteps - 1) ) return; // The BaseData needs to be expanded, call the mitk::Image::Expand() method m_Image->Expand(timeSteps); if(timeSteps > 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::ImageStatisticsHolder::ResetImageStatistics() { 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); } #include "mitkImageAccessByItk.h" //#define BOUNDINGOBJECT_IGNORE template < typename ItkImageType > void mitk::_ComputeExtremaInItkImage( const ItkImageType* itkImage, mitk::ImageStatisticsHolder* statisticsHolder, 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 ( statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep( t ) ) return; statisticsHolder->Expand(t+1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t]= statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t]= statisticsHolder->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 < statisticsHolder->m_ScalarMin[t] ) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if ( value == statisticsHolder->m_ScalarMin[t] ) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if ( value < statisticsHolder->m_Scalar2ndMin[t] ) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if ( value > statisticsHolder->m_ScalarMax[t] ) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if ( value == statisticsHolder->m_ScalarMax[t] ) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if ( value > statisticsHolder->m_Scalar2ndMax[t] ) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); //MITK_DEBUG <<"extrema "<::NonpositiveMin()<<" "<m_ScalarMin<<" "<m_Scalar2ndMin<<" "<m_Scalar2ndMax<<" "<m_ScalarMax<<" "<::max(); } template < typename ItkImageType > void mitk::_ComputeExtremaInItkVectorImage( const ItkImageType* itkImage, mitk::ImageStatisticsHolder* statisticsHolder, int t, unsigned int component) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if(region.Crop(itkImage->GetRequestedRegion()) == false) return; if(region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); - double value = 0; if ( statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep( t ) ) return; statisticsHolder->Expand(t+1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t]= statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t]= statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while( !it.IsAtEnd() ) { - value = it.Get()[component]; + double value = it.Get()[component]; // 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 < statisticsHolder->m_ScalarMin[t] ) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if ( value == statisticsHolder->m_ScalarMin[t] ) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if ( value < statisticsHolder->m_Scalar2ndMin[t] ) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if ( value > statisticsHolder->m_ScalarMax[t] ) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if ( value == statisticsHolder->m_ScalarMax[t] ) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if ( value > statisticsHolder->m_Scalar2ndMax[t] ) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); //MITK_DEBUG <<"extrema "<::NonpositiveMin()<<" "<m_ScalarMin<<" "<m_Scalar2ndMin<<" "<m_Scalar2ndMax<<" "<m_ScalarMax<<" "<::max(); } void mitk::ImageStatisticsHolder::ComputeImageStatistics(int t, unsigned int component) { // timestep valid? if (!m_Image->IsValidTimeStep(t)) return; // image modified? if (this->m_Image->GetMTime() > m_LastRecomputeTimeStamp.GetMTime()) this->ResetImageStatistics(); 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... // used to avoid statistics calculation on qball images. property will be replaced as soons as bug 17928 is merged and the diffusion image refactoring is complete. mitk::BoolProperty* isqball = dynamic_cast< mitk::BoolProperty* >( m_Image->GetProperty( "IsQballImage" ).GetPointer() ); const mitk::PixelType pType = m_Image->GetPixelType(0); if(pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::ImageIOBase::UNKNOWNPIXELTYPE) && (pType.GetPixelType() != itk::ImageIOBase::VECTOR) ) { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if(timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image* image = timeSelector->GetOutput(); AccessByItk_2( image, _ComputeExtremaInItkImage, this, t ); } } else if (pType.GetPixelType() == itk::ImageIOBase::VECTOR && (!isqball || !isqball->GetValue())) // we have a vector image { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if(timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image* image = timeSelector->GetOutput(); AccessVectorPixelTypeByItk_n( image, _ComputeExtremaInItkVectorImage, (this, t, component) ); } } else { m_ScalarMin[t] = 0; m_ScalarMax[t] = 255; m_Scalar2ndMin[t] = 0; m_Scalar2ndMax[t] = 255; } } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMinValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMinValuedVoxels[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMaxValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMaxValuedVoxels[t]; } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index 8cff89c1bf..ea4dd15ab4 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,623 +1,623 @@ /*=================================================================== 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 "mitkLevelWindowManager.h" #include #include "mitkDataStorage.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateNot.h" #include "mitkProperties.h" #include "mitkMessage.h" #include "mitkRenderingModeProperty.h" #include "mitkImage.h" mitk::LevelWindowManager::LevelWindowManager() : m_DataStorage(NULL) , m_LevelWindowProperty(NULL) , m_AutoTopMost(true) , m_IsObserverTagSet(false) , m_CurrentImage(NULL) , m_IsPropertyModifiedTagSet(false) , m_SettingImgForLvlWinProp(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageAddedNode )); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageRemovedNode )); m_DataStorage = NULL; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } //clear both observer maps this->ClearPropObserverLists(); } void mitk::LevelWindowManager::SetDataStorage( mitk::DataStorage* ds ) { if (ds == NULL) return; /* remove listeners of old DataStorage */ if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageAddedNode )); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1( this, &LevelWindowManager::DataStorageRemovedNode )); } /* register listener for new DataStorage */ m_DataStorage = ds; // register m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1( this, &LevelWindowManager::DataStorageAddedNode )); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1( this, &LevelWindowManager::DataStorageRemovedNode )); this->DataStorageAddedNode(); // update us with new DataStorage } void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject& ) { Modified(); } void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const mitk::DataNode* removedNode) { m_AutoTopMost = autoTopMost; if (m_AutoTopMost == false) return; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } /* search topmost image */ if (m_DataStorage.IsNull()) { itkExceptionMacro("DataStorage not set"); } int maxLayer = itk::NumericTraits::min(); m_LevelWindowProperty = NULL; mitk::DataNode::Pointer topLevelNode; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull() || (removedNode != NULL && node == removedNode)) continue; m_SettingImgForLvlWinProp = true; node->SetBoolProperty( "imageForLevelWindow", false ); m_SettingImgForLvlWinProp = false; if (node->IsVisible(NULL) == false) continue; int layer = 0; node->GetIntProperty("layer", layer); if ( layer < maxLayer ) continue; mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNull()) continue; int nonLvlWinMode1 = mitk::RenderingModeProperty::LOOKUPTABLE_COLOR; int nonLvlWinMode2 = mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(node->GetProperty( "Image Rendering.Mode" )); if( mode.IsNotNull() ) { int currMode = mode->GetRenderingMode(); if ( currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2 ) { continue; } } else continue; m_LevelWindowProperty = levelWindowProperty; m_CurrentImage = dynamic_cast(node->GetData()); topLevelNode = node; maxLayer = layer; } if (topLevelNode.IsNotNull()) { m_SettingImgForLvlWinProp = true; topLevelNode->SetBoolProperty( "imageForLevelWindow", true ); m_SettingImgForLvlWinProp = false; } this->SetLevelWindowProperty( m_LevelWindowProperty ); if ( m_LevelWindowProperty.IsNull() ) { Modified(); } // else SetLevelWindowProperty will call Modified(); } // sets an specific LevelWindowProperty, all changes will affect the image belonging to this property. void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if ( levelWindowProperty.IsNull() ) return; /* search image than belongs to the property */ typedef mitk::DataStorage::SetOfObjects NodeSetType; NodeSetType::ConstPointer nodes = m_DataStorage->GetAll(); NodeSetType::ConstIterator it = nodes->Begin(); mitk::DataNode::Pointer propNode = 0; while ( it != nodes->End() ) { mitk::DataNode::Pointer node = it.Value(); mitk::LevelWindowProperty::Pointer prop = dynamic_cast< mitk::LevelWindowProperty* >( node->GetProperty( "levelwindow" ) ); if ( prop == levelWindowProperty ) { propNode = node; } else { m_SettingImgForLvlWinProp = true; node->SetBoolProperty( "imageForLevelWindow", false ); m_SettingImgForLvlWinProp = false; } ++it; } if ( propNode.IsNull() ) { mitkThrow() << "No Image in DataStorage that belongs to LevelWindow property" << m_LevelWindowProperty; } if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver( itk::ModifiedEvent(), command ); m_IsPropertyModifiedTagSet = true; m_CurrentImage = dynamic_cast( propNode->GetData() ); m_SettingImgForLvlWinProp = true; propNode->SetBoolProperty( "imageForLevelWindow", true ); m_SettingImgForLvlWinProp = false; this->Modified(); } // returns the current mitkLevelWindowProperty object from the image that is affected by changes mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() { return m_LevelWindowProperty; } // returns Level/Window values for the current image const mitk::LevelWindow& mitk::LevelWindowManager::GetLevelWindow() { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { itkExceptionMacro("No LevelWindow available!"); } } // sets new Level/Window values and informs all listeners about changes void mitk::LevelWindowManager::SetLevelWindow(const mitk::LevelWindow& levelWindow) { if (m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->SetLevelWindow(levelWindow); } this->Modified(); } void mitk::LevelWindowManager::DataStorageAddedNode( const mitk::DataNode* ) { //update observers with new data storage UpdateObservers(); //Initialize LevelWindowsManager to new image SetAutoTopMostImage(true); //check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != this->GetRelevantNodes()->size())) {mitkThrow() << "Wrong number of observers in Level Window Manager!";} } void mitk::LevelWindowManager::DataStorageRemovedNode( const mitk::DataNode* removedNode ) { //first: check if deleted node is part of relevant nodes. If not, abort method because there is no need change anything. if ((this->GetRelevantNodes()->size() == 0)) return; bool removedNodeIsRelevant = false; /* Iterator code: is crashing, don't know why... so using for loop for (mitk::DataStorage::SetOfObjects::ConstIterator it = this->GetRelevantNodes()->Begin(); it != this->GetRelevantNodes()->End(); ++it) {if (it->Value() == removedNode) {removedNodeIsRelevant=true;}}*/ for (unsigned int i=0; iGetRelevantNodes()->size(); i++) { if (this->GetRelevantNodes()->at(i) == removedNode) {removedNodeIsRelevant=true;} } if (!removedNodeIsRelevant) return; //remember node which will be removed m_NodeMarkedToDelete = removedNode; //update observers UpdateObservers(); /* search image than belongs to the property */ if (m_LevelWindowProperty.IsNull()) { SetAutoTopMostImage(true, removedNode); } else { mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); mitk::DataNode* n = m_DataStorage->GetNode(p2); if (n == NULL || m_AutoTopMost) // if node was deleted, change our behaviour to AutoTopMost, if AutoTopMost is true change level window to topmost node { SetAutoTopMostImage(true, removedNode); } } //reset variable m_NodeMarkedToDelete = NULL; //check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != (this->GetRelevantNodes()->size()-1))) {mitkThrow() << "Wrong number of observers in Level Window Manager!";} } void mitk::LevelWindowManager::UpdateObservers() { this->ClearPropObserverLists(); //remove old observers CreatePropObserverLists(); //create new observer lists } int mitk::LevelWindowManager::GetNumberOfObservers() { return m_PropObserverToNode.size(); } mitk::DataStorage* mitk::LevelWindowManager::GetDataStorage() { return m_DataStorage.GetPointer(); } // true if changes on slider or line-edits will affect always the topmost layer image bool mitk::LevelWindowManager::isAutoTopMost() { return m_AutoTopMost; } void mitk::LevelWindowManager::RecaluclateLevelWindowForSelectedComponent(const itk::EventObject& event) { mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; bool isSelected = false; node->GetBoolProperty("selected", isSelected); if (isSelected) { mitk::LevelWindow selectedLevelWindow; node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates mitk::Image* image = dynamic_cast(node->GetData()); int displayedComponent = 0; if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) { // we found a selected image with a displayed component // let's recalculate the levelwindow for this. selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); node->SetLevelWindow(selectedLevelWindow); } } mitk::LevelWindow levelWindow; node->GetLevelWindow(levelWindow); } this->Update(event); } void mitk::LevelWindowManager::Update(const itk::EventObject&) // visible property of a image has changed { if ( m_SettingImgForLvlWinProp )//no mutex, should still help { return; } if (m_AutoTopMost) { SetAutoTopMostImage(true); return; } int maxVisibleLayer = itk::NumericTraits::min(); mitk::DataNode::Pointer highestVisible = NULL; std::vector< mitk::DataNode::Pointer > visProbNodes; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if ( node.IsNull() ) { continue; } bool visible = node->IsVisible(NULL); - int layer = -1; if ( node->IsVisible(NULL) ) { + int layer = -1; node->GetIntProperty( "layer", layer ); if ( layer > maxVisibleLayer ) { maxVisibleLayer = layer; highestVisible = node; } } bool prop = false; node->GetBoolProperty( "imageForLevelWindow", prop ); if ( prop && visible ) { visProbNodes.push_back( node ); } } int numVisProbNodes = visProbNodes.size(); if ( numVisProbNodes > 2 ) { MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at any point."; } else if ( numVisProbNodes == 2 ) { for ( std::vector< mitk::DataNode::Pointer >::const_iterator it = visProbNodes.begin(); it != visProbNodes.end(); ++it ) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast< mitk::LevelWindowProperty* >( (*it)->GetProperty( "levelwindow" ) ); if ( newProp != m_LevelWindowProperty ) { this->SetLevelWindowProperty( newProp ); return; } } } else if (numVisProbNodes == 1 ) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast< mitk::LevelWindowProperty* >( visProbNodes[0]->GetProperty( "levelwindow" ) ); if ( newProp != m_LevelWindowProperty ) { this->SetLevelWindowProperty( newProp ); return; } } else if ( highestVisible ) { mitk::LevelWindowProperty::Pointer lvlProp = dynamic_cast< mitk::LevelWindowProperty* >( highestVisible->GetProperty( "levelwindow" ) ); this->SetLevelWindowProperty( lvlProp ); } else { Modified(); } } mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() { if (m_DataStorage.IsNull()) return mitk::DataStorage::SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return empty set mitk::NodePredicateProperty::Pointer notBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(false)); mitk::NodePredicateProperty::Pointer hasLevelWindow = mitk::NodePredicateProperty::New("levelwindow", NULL); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateDataType::Pointer isDImage = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isTImage = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isQImage = mitk::NodePredicateDataType::New("QBallImage"); mitk::NodePredicateOr::Pointer predicateTypes = mitk::NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isQImage); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); mitk::DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset( predicate ); return relevantNodes; } mitk::Image* mitk::LevelWindowManager::GetCurrentImage() { return m_CurrentImage; } void mitk::LevelWindowManager::ClearPropObserverLists() { for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode.begin(); iter != m_PropObserverToNode.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode.clear(); for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode2.begin(); iter != m_PropObserverToNode2.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode2.clear(); for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode3.begin(); iter != m_PropObserverToNode3.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode3.clear(); for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode4.begin(); iter != m_PropObserverToNode4.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode4.clear(); for( ObserverToPropertyMap::iterator iter = m_PropObserverToNode5.begin(); iter != m_PropObserverToNode5.end(); ++iter ) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = 0; } m_PropObserverToNode5.clear(); } void mitk::LevelWindowManager::CreatePropObserverLists() { if (m_DataStorage.IsNull()) //check if data storage is set {itkExceptionMacro("DataStorage not set");} /* add observers for all relevant nodes */ mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) {continue;} /* register listener for changes in visible property */ itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long visIdx = it->Value()->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ); m_PropObserverToNode[PropDataPair(visIdx, it->Value())] = it->Value()->GetProperty("visible"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long layerIdx = it->Value()->GetProperty("layer")->AddObserver( itk::ModifiedEvent(), command2 ); m_PropObserverToNode2[PropDataPair(layerIdx, it->Value())] = it->Value()->GetProperty("layer"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); if( imageRenderingMode.IsNotNull() ) { unsigned long rendIdx = imageRenderingMode->AddObserver( itk::ModifiedEvent(), command3 ); m_PropObserverToNode3[PropDataPair(rendIdx, it->Value())] = imageRenderingMode.GetPointer(); } itk::ReceptorMemberCommand::Pointer command4 = itk::ReceptorMemberCommand::New(); command4->SetCallbackFunction(this, &LevelWindowManager::RecaluclateLevelWindowForSelectedComponent); mitk::BaseProperty::Pointer displayedImageComponent = it->Value()->GetProperty("Image.Displayed Component"); if( displayedImageComponent.IsNotNull() ) { unsigned long dispIdx = displayedImageComponent->AddObserver( itk::ModifiedEvent(), command4 ); m_PropObserverToNode4[PropDataPair(dispIdx, it->Value())] = displayedImageComponent.GetPointer(); } itk::ReceptorMemberCommand::Pointer command5 = itk::ReceptorMemberCommand::New(); command5->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); if( imgForLvlWin.IsNull() ) { it->Value()->SetBoolProperty( "imageForLevelWindow", false ); imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); } unsigned long lvlWinIdx = imgForLvlWin->AddObserver( itk::ModifiedEvent(), command5 ); m_PropObserverToNode5[PropDataPair(lvlWinIdx, it->Value())] = it->Value()->GetProperty("imageForLevelWindow"); } } diff --git a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp index c94b8ff526..189d18da89 100644 --- a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp @@ -1,985 +1,985 @@ /*=================================================================== 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 "mitkPlaneGeometry.h" #include "mitkPlaneOperation.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include #include namespace mitk { PlaneGeometry::PlaneGeometry() : Superclass(), m_ReferenceGeometry( nullptr ) { Initialize(); } PlaneGeometry::~PlaneGeometry() { } PlaneGeometry::PlaneGeometry( const PlaneGeometry& other ) : Superclass( other ), m_ReferenceGeometry( other.m_ReferenceGeometry ) { } void PlaneGeometry::EnsurePerpendicularNormal( mitk::AffineTransform3D *transform ) { /** \brief ensure column(2) of indexToWorldTransform-matrix to be perpendicular to plane, keep length and handedness. */ VnlVector normal = vnl_cross_3d( transform->GetMatrix().GetVnlMatrix().get_column(0), transform->GetMatrix().GetVnlMatrix().get_column(1) ); normal.normalize(); // Now normal is a righthand normal unit vector, perpendicular to the plane. ScalarType len = transform->GetMatrix().GetVnlMatrix().get_column(2).two_norm(); if (len==0) { len = 1; } normal *= len; // Get the existing normal vector zed: vnl_vector_fixed zed = transform->GetMatrix().GetVnlMatrix().get_column(2); /** If det(matrix)<0, multiply normal vector by (-1) to keep geometry lefthanded. */ if( vnl_determinant( transform->GetMatrix().GetVnlMatrix()) < 0 ) { MITK_DEBUG << "EnsurePerpendicularNormal(): Lefthanded geometry preserved, rh-normal: [ " << normal << " ],"; normal *= (-1.0); MITK_DEBUG << "lh-normal: [ " << normal << " ], original vector zed is: [ " << zed << " ]"; } // Now lets compare and only replace if necessary and only then warn the user: // float epsilon is precise enough here, but we need to respect numerical condition: // Higham, N., 2002, Accuracy and Stability of Numerical Algorithms, // SIAM, page 37, 2nd edition: double feps = std::numeric_limits::epsilon(); double zedsMagnitude = zed.two_norm(); feps = feps * zedsMagnitude * 2; /** Check if normal (3. column) was perpendicular: If not, replace with calculated normal vector: */ if( normal != zed ) { vnl_vector_fixed parallel; for ( unsigned int i = 0; i<3 ; ++i ) { parallel[i] = normal[i] / zed[i]; // Remember linear algebra: checking for parallelity. } // Checking if really not paralell i.e. non-colinear vectors by comparing these floating point numbers: if( (parallel[0]+feps < parallel[1] || feps+parallel[1] < parallel[0]) && (parallel[0]+feps < parallel[2] || feps+parallel[2] < parallel[0]) ) { MITK_WARN << "EnsurePerpendicularNormal(): Plane geometry was _/askew/_, so here it gets rectified by substituting" << " the 3rd column of the indexToWorldMatrix with an appropriate normal vector: [ " << normal << " ], original vector zed was: [ " << zed << " ]."; Matrix3D matrix = transform->GetMatrix(); matrix.GetVnlMatrix().set_column( 2, normal ); transform->SetMatrix( matrix ); } } else { // Nothing to do, 3rd column of indexToWorldTransformMatrix already was perfectly perpendicular. } } void PlaneGeometry::CheckIndexToWorldTransform(mitk::AffineTransform3D* transform) { EnsurePerpendicularNormal(transform); } void PlaneGeometry::CheckBounds(const BoundingBox::BoundsArrayType &bounds) { // error: unused parameter 'bounds' // this happens in release mode, where the assert macro is defined empty // hence we "use" the parameter: (void)bounds; //currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0]==0); assert(bounds[2]==0); //the unit rectangle must be two-dimensional assert(bounds[1]>0); assert(bounds[3]>0); } void PlaneGeometry::IndexToWorld( const Point2D &pt_units, Point2D &pt_mm ) const { pt_mm[0] = GetExtentInMM(0) / GetExtent(0)*pt_units[0]; pt_mm[1] = GetExtentInMM(1) / GetExtent(1)*pt_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D &pt_mm, Point2D &pt_units ) const { pt_units[0] = pt_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); pt_units[1] = pt_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::IndexToWorld( const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = (GetExtentInMM(0) / GetExtent(0)) * vec_units[0]; vec_mm[1] = (GetExtentInMM(1) / GetExtent(1)) * vec_units[1]; } void PlaneGeometry::WorldToIndex( const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN<<"Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex( const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); vec_units[1] = vec_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D & spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0,0) = spacing[0]; vnlmatrix(1,1) = spacing[1]; vnlmatrix(2,2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, mitk::ScalarType height, const AffineTransform3D* transform /* = nullptr */, PlaneGeometry::PlaneOrientation planeorientation /* = Axial */, mitk::ScalarType zPosition /* = 0 */, bool frontside /* = true */, bool rotated /* = false */ ) { Superclass::Initialize(); /// construct standard view. // We define at the moment "frontside" as: axial from above, // coronal from front (nose), saggital from right. // TODO: Double check with medicals doctors or radiologists [ ]. // We define the orientation in patient's view, e.g. LAI is in a axial cut // (parallel to the triangle ear-ear-nose): // first axis: To the left ear of the patient // seecond axis: To the nose of the patient // third axis: To the legs of the patient. // Options are: L/R left/right; A/P anterior/posterior; I/S inferior/superior // (AKA caudal/cranial). // We note on all cases in the following switch block r.h. for right handed // or l.h. for left handed to describe the different cases. // However, which system is chosen is defined at the end of the switch block. // CAVE / be careful: the vectors right and bottom are relative to the plane // and do NOT describe e.g. the right side of the patient. Point3D origin; /** Bottom means downwards, DV means Direction Vector. Both relative to the image! */ VnlVector rightDV(3), bottomDV(3); /** Origin of this plane is by default a zero vector and implicitly in the top-left corner: */ origin.Fill(0); /** This is different to all definitions in MITK, except the QT mouse clicks. * But it is like this here and we don't want to change a running system. * Just be aware, that IN THIS FUNCTION we define the origin at the top left (e.g. your screen). */ /** NormalDirection defines which axis (i.e. column index in the transform matrix) * is perpendicular to the plane: */ int normalDirection; switch(planeorientation) // Switch through our limited choice of standard planes: { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: if(frontside) // Radiologist's view from below. A cut along the triangle ear-ear-nose. { if(rotated==false) /** Origin in the top-left corner, x=[1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], * origin=[0,0,zpos]: LAI (r.h.) * * 0---rightDV----> | * | | * | Picture of a finite, rectangular plane | * | ( insert LOLCAT-scan here ^_^ ) | * | | * v _________________________________________| * */ { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin rotated to the bottom-right corner, x=[-1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[w,h,zpos]: RPI (r.h.) { // Caveat emptor: Still using top-left as origin of index coordinate system! FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else // 'Backside, not frontside.' Neuro-Surgeons's view from above patient. { if(rotated==false) // x=[-1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], origin=[w,0,zpos]: RAS (r.h.) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin in the bottom-left corner, x=[1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[0,h,zpos]: LPS (r.h.) { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; // That is S=Superior=z=third_axis=middlefinger in righthanded LPS-system. break; // Frontal is known as Coronal in mitk. Plane cuts through patient's ear-ear-heel-heel: case Frontal: if(frontside) { if(rotated==false) // x=[1; 0; 0], y=[0; 0; 1], z=[0; 1; 0], origin=[0,zpos,0]: LAI (r.h.) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[-1;0;0], y=[0;0;-1], z=[0;1;0], origin=[w,zpos,h]: RAS (r.h.) { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) // x=[-1;0;0], y=[0;0;1], z=[0;1;0], origin=[w,zpos,0]: RPI (r.h.) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[1;0;0], y=[0;1;0], z=[0;0;-1], origin=[0,zpos,h]: LPS (r.h.) { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; // Normal vector = posterior direction. break; case Sagittal: // Sagittal=Medial plane, the symmetry-plane mirroring your face. if(frontside) { if(rotated==false) // x=[0;1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,0,0]: LAI (r.h.) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;-1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,w,h]: LPS (r.h.) { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if(rotated==false) // x=[0;-1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,w,0]: RPI (r.h.) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,0,h]: RAS (r.h.) { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; // Normal vector = Lateral direction: Left in a LPS-system. break; default: itkExceptionMacro("unknown PlaneOrientation"); } /// Checking if lefthanded or righthanded: mitk::ScalarType lhOrRhSign=(+1); if ( transform != nullptr ) { lhOrRhSign = ( (0 < vnl_determinant( transform->GetMatrix().GetVnlMatrix() )) ? (+1) : (-1) ); MITK_DEBUG << "mitk::PlaneGeometry::InitializeStandardPlane(): lhOrRhSign, normalDirection, NameOfClass, ObjectName = " << lhOrRhSign << ", " << normalDirection << ", " << transform->GetNameOfClass() << ", " << transform->GetObjectName() << "."; origin = transform->TransformPoint( origin ); rightDV = transform->TransformVector( rightDV ); bottomDV = transform->TransformVector( bottomDV ); /// Signing normal vector according to l.h./r.h. coordinate system: this->SetMatrixByVectors( rightDV, bottomDV, transform->GetMatrix().GetVnlMatrix().get_column(normalDirection).two_norm() * lhOrRhSign ); } - else if ( transform == nullptr ) + else { this->SetMatrixByVectors( rightDV, bottomDV ); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); this->SetOrigin( origin ); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated ) { this->SetReferenceGeometry( const_cast< BaseGeometry * >( geometry3D ) ); ScalarType width, height; const BoundingBox::BoundsArrayType& boundsarray = geometry3D->GetBoundingBox()->GetBounds(); Vector3D originVector; FillVector3D(originVector, boundsarray[0], boundsarray[2], boundsarray[4]); if(geometry3D->GetImageGeometry()) { FillVector3D( originVector, originVector[0] - 0.5, originVector[1] - 0.5, originVector[2] - 0.5 ); } switch(planeorientation) { case None: case Axial: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(1); break; case Frontal: width = geometry3D->GetExtent(0); height = geometry3D->GetExtent(2); break; case Sagittal: width = geometry3D->GetExtent(1); height = geometry3D->GetExtent(2); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( width, height, geometry3D->GetIndexToWorldTransform(), planeorientation, zPosition, frontside, rotated ); ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); Point3D origin; originVector = geometry3D->GetIndexToWorldTransform() ->TransformVector( originVector ); origin = GetOrigin() + originVector; SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated ) { ScalarType zPosition; switch(planeorientation) { case Axial: zPosition = (top ? 0.5 : geometry3D->GetExtent(2)-0.5); break; case Frontal: zPosition = (top ? 0.5 : geometry3D->GetExtent(1)-0.5); break; case Sagittal: zPosition = (top ? 0.5 : geometry3D->GetExtent(0)-0.5); break; case None: zPosition = (top ? 0 : geometry3D->GetExtent(2)-1.0); break; default: itkExceptionMacro("unknown PlaneOrientation"); } InitializeStandardPlane( geometry3D, planeorientation, zPosition, frontside, rotated ); } void PlaneGeometry::InitializeStandardPlane( const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( const VnlVector& rightVector, const VnlVector &downVector, const Vector3D *spacing ) { ScalarType width = rightVector.two_norm(); ScalarType height = downVector.two_norm(); InitializeStandardPlane( width, height, rightVector, downVector, spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing ) { InitializeStandardPlane( width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing ); } void PlaneGeometry::InitializeStandardPlane( mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing ) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // spacing with 1 or 3 negative components could still make it lefthanded. if(spacing!=nullptr) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); ScalarType bounds[6] = { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); this->SetIndexToWorldTransform( transform ); } void PlaneGeometry::InitializePlane( const Point3D &origin, const Vector3D &normal ) { VnlVector rightVectorVnl(3), downVectorVnl; if( Equal( normal[1], 0.0f ) == false ) { FillVector3D( rightVectorVnl, 1.0f, -normal[0]/normal[1], 0.0f ); rightVectorVnl.normalize(); } else { FillVector3D( rightVectorVnl, 0.0f, 1.0f, 0.0f ); } downVectorVnl = vnl_cross_3d( normal.GetVnlVector(), rightVectorVnl ); downVectorVnl.normalize(); // Crossproduct vnl_cross_3d is always righthanded. InitializeStandardPlane( rightVectorVnl, downVectorVnl ); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors( const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness /* = 1.0 */ ) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // a negative thickness could still make it lefthanded. AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; frontToBack.SetVnlVector( this->GetIndexToWorldTransform() ->GetMatrix().GetVnlMatrix().get_column(2) ); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return this->GetIndexToWorldTransform() ->GetMatrix().GetVnlMatrix().get_column(2); } ScalarType PlaneGeometry::DistanceFromPlane( const Point3D &pt3d_mm ) const { return fabs(SignedDistance( pt3d_mm )); } ScalarType PlaneGeometry::SignedDistance( const Point3D &pt3d_mm ) const { return SignedDistanceFromPlane(pt3d_mm); } bool PlaneGeometry::IsAbove( const Point3D &pt3d_mm , bool considerBoundingBox) const { if(considerBoundingBox) { Point3D pt3d_units; BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); } else return SignedDistanceFromPlane(pt3d_mm) > 0; } bool PlaneGeometry::IntersectionLine( const PlaneGeometry* plane, Line3D& crossline ) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct( normal, planeNormal ); if ( direction.GetSquaredNorm() < eps ) return false; crossline.SetDirection( direction ); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = ( d1 - d2 * N1dN2 ) / determinant; double c2 = ( d2 - d1 * N1dN2 ) / determinant; Vector3D p = normal * c1 + planeNormal * c2; crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); return true; } unsigned int PlaneGeometry::IntersectWithPlane2D( const PlaneGeometry* plane, Point2D& lineFrom, Point2D &lineTo ) const { Line3D crossline; if ( this->IntersectionLine( plane, crossline ) == false ) return 0; Point2D point2; Vector2D direction2; this->Map( crossline.GetPoint(), point2 ); this->Map( crossline.GetPoint(), crossline.GetDirection(), direction2 ); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo ); } double PlaneGeometry::Angle( const PlaneGeometry *plane ) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle( const Line3D &line ) const { return vnl_math::pi_over_2 - angle( line.GetDirection().GetVnlVector(), GetMatrixColumn(2) ); } bool PlaneGeometry::IntersectionPoint( const Line3D &line, Point3D &intersectionPoint ) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam( const Line3D &line, double &t ) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if ( fabs( t ) < eps ) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = ( planeNormal * diff ) / t; return true; } bool PlaneGeometry::IsParallel( const PlaneGeometry *plane ) const { return ( (Angle(plane) < 10.0 * mitk::sqrteps ) || ( Angle(plane) > ( vnl_math::pi - 10.0 * sqrteps ) ) ) ; } bool PlaneGeometry::IsOnPlane( const Point3D &point ) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane( const Line3D &line ) const { return ( (Distance( line.GetPoint() ) < eps) && (Distance( line.GetPoint2() ) < eps) ); } bool PlaneGeometry::IsOnPlane( const PlaneGeometry *plane ) const { return ( IsParallel( plane ) && (Distance( plane->GetOrigin() ) < eps) ); } Point3D PlaneGeometry::ProjectPointOntoPlane( const Point3D& pt ) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane( pt ) / len; } itk::LightObject::Pointer PlaneGeometry::InternalClone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation( Operation *operation ) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix( this->GetVtkMatrix()); switch ( operation->GetOperationType() ) { case OpORIENT: { mitk::PlaneOperation *planeOp = dynamic_cast< mitk::PlaneOperation * >( operation ); if ( planeOp == nullptr ) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D( defaultVector, 0.0, 0.0, 1.0 ); Vector3D rotationAxis = itk::CrossProduct( orientationVector, defaultVector ); //double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); double rotationAngle = atan2( (double) rotationAxis.GetNorm(), (double) (orientationVector * defaultVector) ); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate( center[0], center[1], center[2] ); transform->RotateWXYZ( rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2] ); transform->Translate( -center[0], -center[1], -center[2] ); break; } case OpRESTOREPLANEPOSITION: { RestorePlanePositionOperation *op = dynamic_cast< mitk::RestorePlanePositionOperation* >(operation); if(op == nullptr) { return; } AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform2->SetMatrix(matrix); Vector3D offset = op->GetTransform()->GetOffset(); transform2->SetOffset(offset); this->SetIndexToWorldTransform(transform2); ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0 ,1 }; this->SetBounds(bounds); this->Modified(); transform->Delete(); return; } default: Superclass::ExecuteOperation( operation ); transform->Delete(); return; } this->SetVtkMatrixDeepCopy(transform); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << " ScaleFactorMMPerUnitX: " << GetExtentInMM(0) / GetExtent(0) << std::endl; os << indent << " ScaleFactorMMPerUnitY: " << GetExtentInMM(1) / GetExtent(1) << std::endl; os << indent << " Normal: " << GetNormal() << std::endl; } bool PlaneGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert(this->IsBoundingBoxNull()==false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt2d_mm[0] = pt3d_units[0] * GetExtentInMM(0) / GetExtent(0); pt2d_mm[1] = pt3d_units[1] * GetExtentInMM(1) / GetExtent(1); pt3d_units[2]=0; return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { //pt2d_mm is measured from the origin of the world geometry (at leats it called form BaseRendere::Mouse...Event) Point3D pt3d_units; pt3d_units[0] = pt2d_mm[0] / (GetExtentInMM(0) / GetExtent(0)); pt3d_units[1] = pt2d_mm[1] / (GetExtentInMM(1) / GetExtent(1)); pt3d_units[2]=0; //pt3d_units is a continuos index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm to index units. // pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); //now we convert the 3d index to a 3D world point in mm. We could have used IndexToWorld as well as GetITW->Transform... } void PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) { ScalarType bounds[6]={0, width, 0, height, 0, 1}; ScalarType extent, newextentInMM; if(GetExtent(0)>0) { extent = GetExtent(0); if(width>extent) newextentInMM = GetExtentInMM(0)/width*extent; else newextentInMM = GetExtentInMM(0)*extent/width; SetExtentInMM(0, newextentInMM); } if(GetExtent(1)>0) { extent = GetExtent(1); if(width>extent) newextentInMM = GetExtentInMM(1)/height*extent; else newextentInMM = GetExtentInMM(1)*extent/height; SetExtentInMM(1, newextentInMM); } SetBounds(bounds); } bool PlaneGeometry::Project( const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull()==false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt3d_units[2] = 0; projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { assert(this->IsBoundingBoxNull()==false); Vector3D vec3d_units; Superclass::WorldToIndex(vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); return true; } bool PlaneGeometry::Project(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; assert(this->IsBoundingBoxNull()==false); Vector3D vec3d_units; Superclass::WorldToIndex(atPt3d_mm, vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; Superclass::WorldToIndex(atPt3d_mm, pt3d_units); return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool PlaneGeometry::Map(const mitk::Point3D & atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { Point2D pt2d_mm_start, pt2d_mm_end; Point3D pt3d_mm_end; bool inside=Map(atPt3d_mm, pt2d_mm_start); pt3d_mm_end = atPt3d_mm+vec3d_mm; inside&=Map(pt3d_mm_end, pt2d_mm_end); vec2d_mm=pt2d_mm_end-pt2d_mm_start; return inside; } void PlaneGeometry::Map(const mitk::Point2D &/*atPt2d_mm*/, const mitk::Vector2D &/*vec2d_mm*/, mitk::Vector3D &/*vec3d_mm*/) const { //@todo implement parallel to the other Map method! assert(false); } void PlaneGeometry::SetReferenceGeometry( mitk::BaseGeometry *geometry ) { m_ReferenceGeometry = geometry; } mitk::BaseGeometry * PlaneGeometry::GetReferenceGeometry() const { return m_ReferenceGeometry; } bool PlaneGeometry::HasReferenceGeometry() const { return ( m_ReferenceGeometry != nullptr ); } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkShaderProperty.cpp b/Modules/Core/src/DataManagement/mitkShaderProperty.cpp index 9fe0529747..f2db565f6a 100644 --- a/Modules/Core/src/DataManagement/mitkShaderProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkShaderProperty.cpp @@ -1,120 +1,120 @@ /*=================================================================== 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 "mitkShaderProperty.h" #include "mitkCoreServices.h" #include "mitkIShaderRepository.h" #include #include mitk::ShaderProperty::ShaderProperty( ) { AddShaderTypes(); SetShader( (IdType)0 ); } mitk::ShaderProperty::ShaderProperty(const ShaderProperty& other) : mitk::EnumerationProperty(other) , shaderList(other.shaderList) { } mitk::ShaderProperty::ShaderProperty( const IdType& value ) { AddShaderTypes(); SetShader(value); } mitk::ShaderProperty::ShaderProperty( const std::string& value ) { AddShaderTypes(); SetShader(value); } void mitk::ShaderProperty::SetShader( const IdType& value ) { if ( IsValidEnumerationValue( value ) ) SetValue( value ); else SetValue( (IdType)0 ); } void mitk::ShaderProperty::SetShader( const std::string& value ) { if ( IsValidEnumerationValue( value ) ) SetValue( value ); else SetValue( (IdType)0 ); } mitk::EnumerationProperty::IdType mitk::ShaderProperty::GetShaderId() { return GetValueAsId(); } std::string mitk::ShaderProperty::GetShaderName() { return GetValueAsString(); } void mitk::ShaderProperty::AddShaderTypes() { AddEnum( "fixed" ); IShaderRepository* shaderRepo = CoreServices::GetShaderRepository(); if (shaderRepo == NULL) return; std::list l = shaderRepo->GetShaders(); std::list::const_iterator i = l.begin(); while( i != l.end() ) { AddEnum( (*i)->GetName() ); - i++; + ++i; } } bool mitk::ShaderProperty::AddEnum( const std::string& name ,const IdType& /*id*/) { Element e; e.name=name; bool success=Superclass::AddEnum( e.name, (IdType)shaderList.size() ); shaderList.push_back(e); return success; } bool mitk::ShaderProperty::Assign(const BaseProperty &property) { Superclass::Assign(property); this->shaderList = static_cast(property).shaderList; return true; } itk::LightObject::Pointer mitk::ShaderProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkSlicedData.cpp b/Modules/Core/src/DataManagement/mitkSlicedData.cpp index 77f5c2b3da..c6e22dff20 100644 --- a/Modules/Core/src/DataManagement/mitkSlicedData.cpp +++ b/Modules/Core/src/DataManagement/mitkSlicedData.cpp @@ -1,364 +1,362 @@ /*=================================================================== 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 "mitkSlicedData.h" #include "mitkBaseProcess.h" #include #include "mitkAbstractTransformGeometry.h" mitk::SlicedData::SlicedData() : m_RequestedRegionInitialized(false) , m_UseLargestPossibleRegion(false) { unsigned int i; for(i=0;i<4;++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize (i, 1); } } mitk::SlicedData::SlicedData( const SlicedData &other ) : BaseData(other) , m_LargestPossibleRegion(other.m_LargestPossibleRegion) , m_RequestedRegion(other.m_RequestedRegion) , m_RequestedRegionInitialized(other.m_RequestedRegionInitialized) , m_BufferedRegion(other.m_BufferedRegion) , m_UseLargestPossibleRegion(other.m_UseLargestPossibleRegion) { } mitk::SlicedData::~SlicedData() { } void mitk::SlicedData::UpdateOutputInformation() { Superclass::UpdateOutputInformation(); if (this->GetSource().IsNull()) // If we don't have a source, then let's make our Image // span our buffer { m_UseLargestPossibleRegion = true; } // Now we should know what our largest possible region is. If our // requested region was not set yet, (or has been set to something // invalid - with no data in it ) then set it to the largest possible // region. if ( ! m_RequestedRegionInitialized) { this->SetRequestedRegionToLargestPossibleRegion(); m_RequestedRegionInitialized = true; } m_LastRequestedRegionWasOutsideOfTheBufferedRegion = 0; } void mitk::SlicedData::PrepareForNewData() { if ( GetUpdateMTime() < GetPipelineMTime() || GetDataReleased() ) { ReleaseData(); } } void mitk::SlicedData::SetRequestedRegionToLargestPossibleRegion() { m_UseLargestPossibleRegion = true; if(GetGeometry()==nullptr) return; unsigned int i; const RegionType::IndexType & index = GetLargestPossibleRegion().GetIndex(); const RegionType::SizeType & size = GetLargestPossibleRegion().GetSize(); for(i=0;i(requestedRegionSize[4]); if(requestedRegionSize[3] == largestPossibleRegionSize[3]) { for (; c< cEnd; ++c) if(IsChannelSet(c)==false) return true; return false; } // are whole volumes requested? int t, tEnd; t=requestedRegionIndex[3]; tEnd=t+static_cast(requestedRegionSize[3]); if(requestedRegionSize[2] == largestPossibleRegionSize[2]) { for (; c< cEnd; ++c) for (; t< tEnd; ++t) if(IsVolumeSet(t, c)==false) return true; return false; } // ok, only slices are requested. Check if they are available. int s, sEnd; s=requestedRegionIndex[2]; sEnd=s+static_cast(requestedRegionSize[2]); for (; c< cEnd; ++c) for (; t< tEnd; ++t) for (; s< sEnd; ++s) if(IsSliceSet(s, t, c)==false) return true; return false; } bool mitk::SlicedData::VerifyRequestedRegion() { if(GetTimeGeometry() == nullptr) return false; unsigned int i; // Is the requested region within the LargestPossibleRegion? // Note that the test is indeed against the largest possible region // rather than the buffered region; see DataObject::VerifyRequestedRegion. const IndexType &requestedRegionIndex = m_RequestedRegion.GetIndex(); const IndexType &largestPossibleRegionIndex = GetLargestPossibleRegion().GetIndex(); const SizeType& requestedRegionSize = m_RequestedRegion.GetSize(); const SizeType& largestPossibleRegionSize = GetLargestPossibleRegion().GetSize(); for (i=0; i< RegionDimension; ++i) { if ( (requestedRegionIndex[i] < largestPossibleRegionIndex[i]) || ((requestedRegionIndex[i] + static_cast(requestedRegionSize[i])) > (largestPossibleRegionIndex[i]+static_cast(largestPossibleRegionSize[i])))) { return false; } } return true; } void mitk::SlicedData::SetRequestedRegion( const itk::DataObject *data) { m_UseLargestPossibleRegion=false; const mitk::SlicedData *slicedData = dynamic_cast(data); if (slicedData) { m_RequestedRegion = slicedData->GetRequestedRegion(); m_RequestedRegionInitialized = true; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::SetRequestedRegion(DataObject*) cannot cast " << typeid(data).name() << " to " << typeid(SlicedData*).name() ); } } void mitk::SlicedData::SetRequestedRegion(SlicedData::RegionType *region) { m_UseLargestPossibleRegion=false; if(region!=nullptr) { m_RequestedRegion = *region; m_RequestedRegionInitialized = true; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::SetRequestedRegion(SlicedData::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(SlicedData*).name() ); } } void mitk::SlicedData::SetLargestPossibleRegion(SlicedData::RegionType *region) { if(region!=nullptr) { m_LargestPossibleRegion = *region; m_UseLargestPossibleRegion=true; } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::SetLargestPossibleRegion(SlicedData::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(SlicedData*).name() ); } } void mitk::SlicedData::CopyInformation(const itk::DataObject *data) { // Standard call to the superclass' method Superclass::CopyInformation(data); const mitk::SlicedData *slicedData; slicedData = dynamic_cast(data); if (slicedData) { m_LargestPossibleRegion = slicedData->GetLargestPossibleRegion(); } else { // pointer could not be cast back down itkExceptionMacro( << "mitk::SlicedData::CopyInformation(const DataObject *data) cannot cast " << typeid(data).name() << " to " << typeid(SlicedData*).name() ); } } //const mitk::PlaneGeometry* mitk::SlicedData::GetPlaneGeometry(int s, int t) const //{ // const_cast(this)->SetRequestedRegionToLargestPossibleRegion(); // // const_cast(this)->UpdateOutputInformation(); // // return GetSlicedGeometry(t)->GetPlaneGeometry(s); //} // mitk::SlicedGeometry3D* mitk::SlicedData::GetSlicedGeometry(unsigned int t) const { if (GetTimeGeometry() == nullptr) return nullptr; return dynamic_cast(GetTimeGeometry()->GetGeometryForTimeStep(t).GetPointer()); } const mitk::SlicedGeometry3D* mitk::SlicedData::GetUpdatedSlicedGeometry(unsigned int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetSlicedGeometry(t); } void mitk::SlicedData::SetGeometry(BaseGeometry* aGeometry3D) { if(aGeometry3D!=nullptr) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); SlicedGeometry3D::Pointer slicedGeometry = dynamic_cast(aGeometry3D); if(slicedGeometry.IsNull()) { PlaneGeometry* geometry2d = dynamic_cast(aGeometry3D); if(geometry2d!=nullptr && dynamic_cast(aGeometry3D) == nullptr) { if((GetSlicedGeometry()->GetPlaneGeometry(0)==geometry2d) && (GetSlicedGeometry()->GetSlices()==1)) return; slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(geometry2d, 1); } else { slicedGeometry = SlicedGeometry3D::New(); PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(aGeometry3D); slicedGeometry->InitializeEvenlySpaced(planeGeometry, (unsigned int)(aGeometry3D->GetExtent(2))); } } assert(slicedGeometry.IsNotNull()); timeGeometry->Initialize(slicedGeometry, 1); Superclass::SetTimeGeometry(timeGeometry); } else { if(GetGeometry()==nullptr) return; Superclass::SetGeometry(nullptr); } } void mitk::SlicedData::SetSpacing(const ScalarType aSpacing[3]) { this->SetSpacing((mitk::Vector3D)aSpacing); } void mitk::SlicedData::SetOrigin(const mitk::Point3D& origin) { TimeGeometry* timeGeometry = GetTimeGeometry(); assert(timeGeometry!=nullptr); mitk::SlicedGeometry3D* slicedGeometry; unsigned int steps = timeGeometry->CountTimeSteps(); for(unsigned int timestep = 0; timestep < steps; ++timestep) { slicedGeometry = GetSlicedGeometry(timestep); if(slicedGeometry != nullptr) { slicedGeometry->SetOrigin(origin); if(slicedGeometry->GetEvenlySpaced()) { mitk::PlaneGeometry* geometry2D = slicedGeometry->GetPlaneGeometry(0); geometry2D->SetOrigin(origin); slicedGeometry->InitializeEvenlySpaced(geometry2D, slicedGeometry->GetSlices()); } } //ProportionalTimeGeometry* timeGeometry = dynamic_cast(GetTimeGeometry()); //if(timeGeometry != NULL) //{ // timeGeometry->Initialize(slicedGeometry, steps); // break; //} } } void mitk::SlicedData::SetSpacing(mitk::Vector3D aSpacing) { TimeGeometry* timeGeometry = GetTimeGeometry(); assert(timeGeometry!=nullptr); - mitk::SlicedGeometry3D* slicedGeometry; - unsigned int steps = timeGeometry->CountTimeSteps(); for(unsigned int timestep = 0; timestep < steps; ++timestep) { - slicedGeometry = GetSlicedGeometry(timestep); + mitk::SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(timestep); if(slicedGeometry != nullptr) { slicedGeometry->SetSpacing(aSpacing); } } timeGeometry->Update(); } diff --git a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp index 019989dc94..5e936f8ef4 100644 --- a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,1044 +1,1036 @@ /*=================================================================== 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 "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkRotationOperation.h" #include "mitkPlaneOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkInteractionConst.h" #include "mitkSliceNavigationController.h" #include "mitkAbstractTransformGeometry.h" const mitk::ScalarType PI = 3.14159265359; mitk::SlicedGeometry3D::SlicedGeometry3D() : m_EvenlySpaced( true ), m_Slices( 0 ), m_ReferenceGeometry( nullptr ), m_SliceNavigationController( nullptr ) { m_DirectionVector.Fill(0); this->InitializeSlicedGeometry( m_Slices ); } mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D& other) : Superclass(other), m_EvenlySpaced( other.m_EvenlySpaced ), m_Slices( other.m_Slices ), m_ReferenceGeometry( other.m_ReferenceGeometry ), m_SliceNavigationController( other.m_SliceNavigationController ) { m_DirectionVector.Fill(0); SetSpacing( other.GetSpacing() ); SetDirectionVector( other.GetDirectionVector() ); if ( m_EvenlySpaced ) { PlaneGeometry::Pointer geometry = other.m_PlaneGeometries[0]->Clone(); assert(geometry.IsNotNull()); SetPlaneGeometry(geometry, 0); } else { unsigned int s; for ( s = 0; s < other.m_Slices; ++s ) { if ( other.m_PlaneGeometries[s].IsNull() ) { assert(other.m_EvenlySpaced); m_PlaneGeometries[s] = nullptr; } else { PlaneGeometry* geometry2D = other.m_PlaneGeometries[s]->Clone(); assert(geometry2D!=nullptr); SetPlaneGeometry(geometry2D, s); } } } } mitk::SlicedGeometry3D::~SlicedGeometry3D() { } mitk::PlaneGeometry * mitk::SlicedGeometry3D::GetPlaneGeometry( int s ) const { mitk::PlaneGeometry::Pointer geometry2D = nullptr; if ( this->IsValidSlice(s) ) { geometry2D = m_PlaneGeometries[s]; // If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored // for the requested slice, and (c) the first slice (s=0) // is a PlaneGeometry instance, then we calculate the geometry of the // requested as the plane of the first slice shifted by m_Spacing[2]*s // in the direction of m_DirectionVector. if ( (m_EvenlySpaced) && (geometry2D.IsNull()) ) { PlaneGeometry *firstSlice = m_PlaneGeometries[0]; if ( firstSlice != nullptr && dynamic_cast(m_PlaneGeometries[0].GetPointer() )==nullptr) { if ( (m_DirectionVector[0] == 0.0) && (m_DirectionVector[1] == 0.0) && (m_DirectionVector[2] == 0.0) ) { m_DirectionVector = firstSlice->GetNormal(); m_DirectionVector.Normalize(); } Vector3D direction; direction = m_DirectionVector * this->GetSpacing()[2]; mitk::PlaneGeometry::Pointer requestedslice; requestedslice = static_cast< mitk::PlaneGeometry * >( firstSlice->Clone().GetPointer() ); requestedslice->SetOrigin( requestedslice->GetOrigin() + direction * s ); geometry2D = requestedslice; m_PlaneGeometries[s] = geometry2D; } } return geometry2D; } else { return nullptr; } } const mitk::BoundingBox * mitk::SlicedGeometry3D::GetBoundingBox() const { assert(this->IsBoundingBoxNull()==false); return Superclass::GetBoundingBox(); } bool mitk::SlicedGeometry3D::SetPlaneGeometry( mitk::PlaneGeometry *geometry2D, int s ) { if ( this->IsValidSlice(s) ) { m_PlaneGeometries[s] = geometry2D; m_PlaneGeometries[s]->SetReferenceGeometry( m_ReferenceGeometry ); return true; } return false; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry( unsigned int slices ) { Superclass::Initialize(); m_Slices = slices; PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign( m_Slices, gnull ); Vector3D spacing; spacing.Fill( 1.0 ); this->SetSpacing( spacing ); m_DirectionVector.Fill( 0 ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( mitk::PlaneGeometry* geometry2D, unsigned int slices, bool flipped ) { assert( geometry2D != nullptr ); this->InitializeEvenlySpaced( geometry2D, geometry2D->GetExtentInMM(2)/geometry2D->GetExtent(2), slices, flipped ); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced( mitk::PlaneGeometry* geometry2D, mitk::ScalarType zSpacing, unsigned int slices, bool flipped ) { assert( geometry2D != nullptr ); assert( geometry2D->GetExtent(0) > 0 ); assert( geometry2D->GetExtent(1) > 0 ); geometry2D->Register(); Superclass::Initialize(); m_Slices = slices; BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); bounds[4] = 0; bounds[5] = slices; // clear and reserve PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign( m_Slices, gnull ); Vector3D directionVector = geometry2D->GetAxisVector(2); directionVector.Normalize(); directionVector *= zSpacing; if ( flipped == false ) { // Normally we should use the following four lines to create a copy of // the transform contrained in geometry2D, because it may not be changed // by us. But we know that SetSpacing creates a new transform without // changing the old (coming from geometry2D), so we can use the fifth // line instead. We check this at (**). // // AffineTransform3D::Pointer transform = AffineTransform3D::New(); // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); // SetIndexToWorldTransform(transform); this->SetIndexToWorldTransform( const_cast< AffineTransform3D * >( geometry2D->GetIndexToWorldTransform() )); } else { directionVector *= -1.0; this->SetIndexToWorldTransform( AffineTransform3D::New()); this->GetIndexToWorldTransform()->SetMatrix( geometry2D->GetIndexToWorldTransform()->GetMatrix() ); AffineTransform3D::OutputVectorType scaleVector; FillVector3D(scaleVector, 1.0, 1.0, -1.0); this->GetIndexToWorldTransform()->Scale(scaleVector, true); this->GetIndexToWorldTransform()->SetOffset( geometry2D->GetIndexToWorldTransform()->GetOffset() ); } mitk::Vector3D spacing; FillVector3D( spacing, geometry2D->GetExtentInMM(0) / bounds[1], geometry2D->GetExtentInMM(1) / bounds[3], zSpacing ); this->SetDirectionVector( directionVector ); this->SetBounds( bounds ); this->SetPlaneGeometry( geometry2D, 0 ); this->SetSpacing( spacing, true); this->SetEvenlySpaced(); //this->SetTimeBounds( geometry2D->GetTimeBounds() ); assert(this->GetIndexToWorldTransform() != geometry2D->GetIndexToWorldTransform()); // (**) see above. this->SetFrameOfReferenceID( geometry2D->GetFrameOfReferenceID() ); this->SetImageGeometry( geometry2D->GetImageGeometry() ); geometry2D->UnRegister(); } void mitk::SlicedGeometry3D::InitializePlanes( const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top, bool frontside, bool rotated ) { m_ReferenceGeometry = const_cast< BaseGeometry * >( geometry3D ); PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane( geometry3D, top, planeorientation, frontside, rotated ); ScalarType viewSpacing = 1; unsigned int slices = 1; switch ( planeorientation ) { case PlaneGeometry::None: break; case PlaneGeometry::Axial: viewSpacing = geometry3D->GetSpacing()[2]; slices = (unsigned int) geometry3D->GetExtent( 2 ); break; case PlaneGeometry::Frontal: viewSpacing = geometry3D->GetSpacing()[1]; slices = (unsigned int) geometry3D->GetExtent( 1 ); break; case PlaneGeometry::Sagittal: viewSpacing = geometry3D->GetSpacing()[0]; slices = (unsigned int) geometry3D->GetExtent( 0 ); break; default: itkExceptionMacro("unknown PlaneOrientation"); } mitk::Vector3D normal = this->AdjustNormal( planeGeometry->GetNormal() ); ScalarType directedExtent = std::abs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); if ( directedExtent >= viewSpacing ) { slices = static_cast< int >(directedExtent / viewSpacing + 0.5); } else { slices = 1; } bool flipped = (top == false); if ( frontside == false ) { flipped = !flipped; } if ( planeorientation == PlaneGeometry::Frontal ) { flipped = !flipped; } this->InitializeEvenlySpaced( planeGeometry, viewSpacing, slices, flipped ); } void mitk::SlicedGeometry3D ::ReinitializePlanes( const Point3D ¢er, const Point3D &referencePoint ) { // Need a reference frame to align the rotated planes if ( !m_ReferenceGeometry ) { return; } // Get first plane of plane stack PlaneGeometry *firstPlane = m_PlaneGeometries[0]; // If plane stack is empty, exit if ( !firstPlane || dynamic_cast(firstPlane) ) { return; } // Calculate the "directed" spacing when taking the plane (defined by its axes // vectors and normal) as the reference coordinate frame. // // This is done by calculating the radius of the ellipsoid defined by the // original volume spacing axes, in the direction of the respective axis of the // reference frame. mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); mitk::Vector3D normal = firstPlane->GetNormal(); normal.Normalize(); Vector3D spacing; spacing[0] = this->CalculateSpacing( axis0 ); spacing[1] = this->CalculateSpacing( axis1 ); spacing[2] = this->CalculateSpacing( normal ); Superclass::SetSpacing( spacing ); // Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above. ScalarType directedExtent = std::abs( m_ReferenceGeometry->GetExtentInMM( 0 ) * normal[0] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 1 ) * normal[1] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 2 ) * normal[2] ); if ( directedExtent >= spacing[2] ) { m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } // The origin of our "first plane" needs to be adapted to this new extent. // To achieve this, we first calculate the current distance to the volume's // center, and then shift the origin in the direction of the normal by the // difference between this distance and half of the new extent. double centerOfRotationDistance = firstPlane->SignedDistanceFromPlane( center ); if ( centerOfRotationDistance > 0 ) { firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * (centerOfRotationDistance - directedExtent / 2.0) ); m_DirectionVector = normal; } else { firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * (directedExtent / 2.0 + centerOfRotationDistance) ); m_DirectionVector = -normal; } // Now we adjust this distance according with respect to the given reference // point: we need to make sure that the point is touched by one slice of the // new slice stack. double referencePointDistance = firstPlane->SignedDistanceFromPlane( referencePoint ); int referencePointSlice = static_cast< int >( referencePointDistance / spacing[2]); double alignmentValue = referencePointDistance / spacing[2] - referencePointSlice; firstPlane->SetOrigin( firstPlane->GetOrigin() + normal * alignmentValue * spacing[2] ); // Finally, we can clear the previous geometry stack and initialize it with // our re-initialized "first plane". m_PlaneGeometries.assign( m_Slices, PlaneGeometry::Pointer( nullptr ) ); if ( m_Slices > 0 ) { m_PlaneGeometries[0] = firstPlane; } // Reinitialize SNC with new number of slices m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); this->Modified(); } double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D &d ) const { // Need the spacing of the underlying dataset / geometry if ( !m_ReferenceGeometry ) { return 1.0; } const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); return SlicedGeometry3D::CalculateSpacing( spacing, d ); } -double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D spacing, const mitk::Vector3D &d ) +double mitk::SlicedGeometry3D::CalculateSpacing( const mitk::Vector3D& spacing, const mitk::Vector3D &d ) { // The following can be derived from the ellipsoid equation // // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 // // where (a,b,c) = spacing of original volume (ellipsoid radii) // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) // double scaling = d[0]*d[0] / (spacing[0] * spacing[0]) + d[1]*d[1] / (spacing[1] * spacing[1]) + d[2]*d[2] / (spacing[2] * spacing[2]); scaling = sqrt( scaling ); return ( sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ) / scaling ); } mitk::Vector3D mitk::SlicedGeometry3D::AdjustNormal( const mitk::Vector3D &normal ) const { TransformType::Pointer inverse = TransformType::New(); m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse( inverse ); Vector3D transformedNormal = inverse->TransformVector( normal ); transformedNormal.Normalize(); return transformedNormal; } void mitk::SlicedGeometry3D::SetImageGeometry( const bool isAnImageGeometry ) { Superclass::SetImageGeometry( isAnImageGeometry ); - mitk::BaseGeometry* geometry; - unsigned int s; for ( s = 0; s < m_Slices; ++s ) { - geometry = m_PlaneGeometries[s]; + mitk::BaseGeometry* geometry = m_PlaneGeometries[s]; if ( geometry!=nullptr ) { geometry->SetImageGeometry( isAnImageGeometry ); } } } void mitk::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset( const bool isAnImageGeometry ) { - mitk::BaseGeometry* geometry; - unsigned int s; for ( s = 0; s < m_Slices; ++s ) { - geometry = m_PlaneGeometries[s]; + mitk::BaseGeometry* geometry = m_PlaneGeometries[s]; if ( geometry!=nullptr ) { geometry->ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); } } Superclass::ChangeImageGeometryConsideringOriginOffset( isAnImageGeometry ); } bool mitk::SlicedGeometry3D::IsValidSlice( int s ) const { return ((s >= 0) && (s < (int)m_Slices)); } void mitk::SlicedGeometry3D::SetReferenceGeometry( BaseGeometry *referenceGeometry ) { m_ReferenceGeometry = referenceGeometry; std::vector::iterator it; for ( it = m_PlaneGeometries.begin(); it != m_PlaneGeometries.end(); ++it ) { (*it)->SetReferenceGeometry( referenceGeometry ); } } void mitk::SlicedGeometry3D::PreSetSpacing( const mitk::Vector3D &aSpacing ) { bool hasEvenlySpacedPlaneGeometry = false; mitk::Point3D origin; mitk::Vector3D rightDV, bottomDV; BoundingBox::BoundsArrayType bounds; // Check for valid spacing if(!(aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0)) { mitkThrow() << "You try to set a spacing with at least one element equal or " "smaller to \"0\". This might lead to a crash during rendering. Please double" " check your data!"; } // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0)) { const PlaneGeometry *planeGeometry = m_PlaneGeometries[0]; if ( planeGeometry && !dynamic_cast( planeGeometry ) ) { this->WorldToIndex( planeGeometry->GetOrigin(), origin ); this->WorldToIndex( planeGeometry->GetAxisVector(0), rightDV ); this->WorldToIndex( planeGeometry->GetAxisVector(1), bottomDV ); bounds = planeGeometry->GetBounds(); hasEvenlySpacedPlaneGeometry = true; } } BaseGeometry::_SetSpacing(aSpacing); mitk::PlaneGeometry::Pointer firstGeometry; // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if ( hasEvenlySpacedPlaneGeometry ) { //create planeGeometry according to new spacing this->IndexToWorld( origin, origin ); this->IndexToWorld( rightDV, rightDV ); this->IndexToWorld( bottomDV, bottomDV ); mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->SetImageGeometry( this->GetImageGeometry() ); planeGeometry->SetReferenceGeometry( m_ReferenceGeometry ); //Store spacing, as Initialize... needs a pointer mitk::Vector3D lokalSpacing = this->GetSpacing(); planeGeometry->InitializeStandardPlane( rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &lokalSpacing ); planeGeometry->SetOrigin(origin); planeGeometry->SetBounds(bounds); firstGeometry = planeGeometry; } else if ( (m_EvenlySpaced) && (m_PlaneGeometries.size() > 0) ) { firstGeometry = m_PlaneGeometries[0].GetPointer(); } //clear and reserve PlaneGeometry::Pointer gnull=nullptr; m_PlaneGeometries.assign(m_Slices, gnull); if ( m_Slices > 0 ) { m_PlaneGeometries[0] = firstGeometry; } this->Modified(); } void mitk::SlicedGeometry3D ::SetSliceNavigationController( SliceNavigationController *snc ) { m_SliceNavigationController = snc; } mitk::SliceNavigationController * mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { if(m_EvenlySpaced!=on) { m_EvenlySpaced=on; this->Modified(); } } void mitk::SlicedGeometry3D ::SetDirectionVector( const mitk::Vector3D& directionVector ) { Vector3D newDir = directionVector; newDir.Normalize(); if ( newDir != m_DirectionVector ) { m_DirectionVector = newDir; this->Modified(); } } //void //mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds ) //{ // Superclass::SetTimeBounds( timebounds ); // // unsigned int s; // for ( s = 0; s < m_Slices; ++s ) // { // if(m_Geometry2Ds[s].IsNotNull()) // { // m_Geometry2Ds[s]->SetTimeBounds( timebounds ); // } // } // m_TimeBounds = timebounds; //} itk::LightObject::Pointer mitk::SlicedGeometry3D::InternalClone() const { Self::Pointer newGeometry = new SlicedGeometry3D(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::SlicedGeometry3D::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os,indent); os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; if ( m_EvenlySpaced ) { os << indent << " DirectionVector: " << m_DirectionVector << std::endl; } os << indent << " Slices: " << m_Slices << std::endl; os << std::endl; os << indent << " GetPlaneGeometry(0): "; if ( this->GetPlaneGeometry(0) == nullptr ) { os << "NULL" << std::endl; } else { this->GetPlaneGeometry(0)->Print(os, indent); } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation* operation) { PlaneGeometry::Pointer geometry2D; ApplyTransformMatrixOperation *applyMatrixOp; Point3D center; switch ( operation->GetOperationType() ) { case OpNOTHING: break; case OpROTATE: if ( m_EvenlySpaced ) { // Need a reference frame to align the rotation if ( m_ReferenceGeometry ) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Save first slice PlaneGeometry::Pointer geometry2D = m_PlaneGeometries[0]; RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); // Generate a RotationOperation using the dataset center instead of // the supplied rotation center. This is necessary so that the rotated // zero-plane does not shift away. The supplied center is instead used // to adjust the slice stack afterwards. Point3D center = m_ReferenceGeometry->GetCenter(); RotationOperation centeredRotation( rotOp->GetOperationType(), center, rotOp->GetVectorOfRotation(), rotOp->GetAngleOfRotation() ); // Rotate first slice geometry2D->ExecuteOperation( ¢eredRotation ); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes( center, rotOp->GetCenterOfRotation() ); geometry2D->SetSpacing(this->GetSpacing()); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( rotOp->GetCenterOfRotation() ); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation( ¢eredRotation ); } else { // we also have to consider the case, that there is no reference geometry available. if ( m_PlaneGeometries.size() > 0 ) { // Reach through to all slices in my container for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { // Test for empty slices, which can happen if evenly spaced geometry if ((*iter).IsNotNull()) { (*iter)->ExecuteOperation(operation); } } // rotate overall geometry RotationOperation *rotOp = dynamic_cast< RotationOperation * >( operation ); BaseGeometry::ExecuteOperation( rotOp); } } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpORIENT: if ( m_EvenlySpaced ) { // get operation data PlaneOperation *planeOp = dynamic_cast< PlaneOperation * >( operation ); // Get first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation. If not all avaialble, stop here if ( !m_ReferenceGeometry || ( !planeGeometry || dynamic_cast(planeGeometry.GetPointer()) ) || !planeOp ) { break; } // General Behavior: // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // // 1st Step: Reorient Normal Vector of first plane // Point3D center = planeOp->GetPoint(); //m_ReferenceGeometry->GetCenter(); mitk::Vector3D currentNormal = planeGeometry->GetNormal(); mitk::Vector3D newNormal; if (planeOp->AreAxisDefined()) { // If planeOp was defined by one centerpoint and two axis vectors newNormal = CrossProduct(planeOp->GetAxisVec0(), planeOp->GetAxisVec1()); } else { // If planeOp was defined by one centerpoint and one normal vector newNormal = planeOp->GetNormal(); } // Get Rotation axis und angle currentNormal.Normalize(); newNormal.Normalize(); ScalarType rotationAngle = angle(currentNormal.GetVnlVector(),newNormal.GetVnlVector()); rotationAngle *= 180.0 / vnl_math::pi; // from rad to deg Vector3D rotationAxis = itk::CrossProduct( currentNormal, newNormal ); if (std::abs(rotationAngle-180) < mitk::eps ) { // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis should be ANY vector that is 90� to current Normal mitk::Vector3D helpNormal; helpNormal = currentNormal; helpNormal[0] += 1; helpNormal[1] -= 1; helpNormal[2] += 1; helpNormal.Normalize(); rotationAxis = itk::CrossProduct( helpNormal, currentNormal ); } RotationOperation centeredRotation( mitk::OpROTATE, center, rotationAxis, rotationAngle ); // Rotate first slice planeGeometry->ExecuteOperation( ¢eredRotation ); // Reinitialize planes and select slice, if my rotations are all done. if (!planeOp->AreAxisDefined()) { // Clear the slice stack and adjust it according to the center of // rotation and plane position (see documentation of ReinitializePlanes) this->ReinitializePlanes( center, planeOp->GetPoint() ); planeGeometry->SetSpacing(this->GetSpacing()); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); m_SliceNavigationController->AdjustSliceStepperRange(); } } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation( ¢eredRotation ); // // 2nd step. If axis vectors were defined, rotate the plane around its normal to fit these // if (planeOp->AreAxisDefined()) { mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0(); vecAxixNew.Normalize(); mitk::Vector3D VecAxisCurr = planeGeometry->GetAxisVector(0); VecAxisCurr.Normalize(); ScalarType rotationAngle = angle(VecAxisCurr.GetVnlVector(),vecAxixNew.GetVnlVector()); rotationAngle = rotationAngle * 180 / PI; // Rad to Deg // we rotate around the normal of the plane, but we do not know, if we need to rotate clockwise // or anti-clockwise. So we rotate around the crossproduct of old and new Axisvector. // Since both axis vectors lie in the plane, the crossproduct is the planes normal or the negative planes normal rotationAxis = itk::CrossProduct( VecAxisCurr, vecAxixNew ); if (std::abs(rotationAngle-180) < mitk::eps ) { // current axisVec and desired axisVec are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis can be just plane Normal. (have to rotate by 180�) rotationAxis = newNormal; } // Perfom Rotation mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle); planeGeometry->ExecuteOperation( &op ); // Apply changes on first slice to whole slice stack this->ReinitializePlanes( center, planeOp->GetPoint() ); planeGeometry->SetSpacing(this->GetSpacing()); if ( m_SliceNavigationController ) { m_SliceNavigationController->SelectSliceByPoint( planeOp->GetPoint() ); m_SliceNavigationController->AdjustSliceStepperRange(); } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation( &op ); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpRESTOREPLANEPOSITION: if ( m_EvenlySpaced ) { // Save first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; RestorePlanePositionOperation *restorePlaneOp = dynamic_cast< RestorePlanePositionOperation* >( operation ); // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation if ( m_ReferenceGeometry && (planeGeometry && dynamic_cast(planeGeometry.GetPointer()) == nullptr) && restorePlaneOp ) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Rotate first slice planeGeometry->ExecuteOperation( restorePlaneOp ); m_DirectionVector = restorePlaneOp->GetDirectionVector(); double centerOfRotationDistance = planeGeometry->SignedDistanceFromPlane( m_ReferenceGeometry->GetCenter() ); - if ( centerOfRotationDistance > 0 ) - { - m_DirectionVector = m_DirectionVector; - } - else + if ( centerOfRotationDistance <= 0 ) { m_DirectionVector = -m_DirectionVector; } Vector3D spacing = restorePlaneOp->GetSpacing(); Superclass::SetSpacing( spacing ); // /*Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above.*/ ScalarType directedExtent = std::abs( m_ReferenceGeometry->GetExtentInMM( 0 ) * m_DirectionVector[0] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 1 ) * m_DirectionVector[1] ) + std::abs( m_ReferenceGeometry->GetExtentInMM( 2 ) * m_DirectionVector[2] ); if ( directedExtent >= spacing[2] ) { m_Slices = static_cast< unsigned int >(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } m_PlaneGeometries.assign( m_Slices, PlaneGeometry::Pointer( nullptr ) ); if ( m_Slices > 0 ) { m_PlaneGeometries[0] = planeGeometry; } m_SliceNavigationController->GetSlice()->SetSteps( m_Slices ); this->Modified(); //End Reinitialization if ( m_SliceNavigationController ) { m_SliceNavigationController->GetSlice()->SetPos( restorePlaneOp->GetPos() ); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(restorePlaneOp); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpAPPLYTRANSFORMMATRIX: // Clear all generated geometries and then transform only the first slice. // The other slices will be re-generated on demand // Save first slice geometry2D = m_PlaneGeometries[0]; applyMatrixOp = dynamic_cast< ApplyTransformMatrixOperation* >( operation ); // Apply transformation to first plane geometry2D->ExecuteOperation( applyMatrixOp ); // Generate a ApplyTransformMatrixOperation using the dataset center instead of // the supplied rotation center. The supplied center is instead used to adjust the // slice stack afterwards (see OpROTATE). center = m_ReferenceGeometry->GetCenter(); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes( center, applyMatrixOp->GetReferencePoint() ); BaseGeometry::ExecuteOperation( applyMatrixOp ); break; default: // let handle by base class if we don't do anything BaseGeometry::ExecuteOperation( operation ); } this->Modified(); } diff --git a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp index 627f62dec9..d630030ef6 100644 --- a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp @@ -1,250 +1,250 @@ /*=================================================================== 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 "mitkStandaloneDataStorage.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkGroupTagProperty.h" #include "itkSimpleFastMutexLock.h" #include "itkMutexLockHolder.h" mitk::StandaloneDataStorage::StandaloneDataStorage() : mitk::DataStorage() { } mitk::StandaloneDataStorage::~StandaloneDataStorage() { for(AdjacencyList::iterator it = m_SourceNodes.begin(); - it != m_SourceNodes.end(); it++) + it != m_SourceNodes.end(); ++it) { this->RemoveListeners(it->first); } } bool mitk::StandaloneDataStorage::IsInitialized() const { return true; } void mitk::StandaloneDataStorage::Add(mitk::DataNode* node, const mitk::DataStorage::SetOfObjects* parents) { { itk::MutexLockHolder locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); /* check if node is in its own list of sources */ if ((parents != NULL) && (std::find(parents->begin(), parents->end(), node) != parents->end())) throw std::invalid_argument("Node is it's own parent"); /* check if node already exists in StandaloneDataStorage */ if (m_SourceNodes.find(node) != m_SourceNodes.end()) throw std::invalid_argument("Node is already in DataStorage"); /* create parent list if it does not exist */ mitk::DataStorage::SetOfObjects::ConstPointer sp; if (parents != NULL) sp = parents; else sp = mitk::DataStorage::SetOfObjects::New(); /* Store node and parent list in sources adjacency list */ m_SourceNodes.insert(std::make_pair(node, sp)); /* Store node and an empty children list in derivations adjacency list */ mitk::DataStorage::SetOfObjects::Pointer childrenPointer = mitk::DataStorage::SetOfObjects::New(); mitk::DataStorage::SetOfObjects::ConstPointer children = childrenPointer.GetPointer(); m_DerivedNodes.insert(std::make_pair(node, children)); /* create entry in derivations adjacency list for each parent of the new node */ for (SetOfObjects::ConstIterator it = sp->Begin(); it != sp->End(); it++) { mitk::DataNode::ConstPointer parent = it.Value().GetPointer(); mitk::DataStorage::SetOfObjects::ConstPointer derivedObjects = m_DerivedNodes[parent]; // get or create pointer to list of derived objects for that parent node if (derivedObjects.IsNull()) m_DerivedNodes[parent] = mitk::DataStorage::SetOfObjects::New(); // Create a set of Objects, if it does not already exist mitk::DataStorage::SetOfObjects* deob = const_cast(m_DerivedNodes[parent].GetPointer()); // temporarily get rid of const pointer to insert new element deob->InsertElement(deob->Size(), node); // node is derived from parent. Insert it into the parents list of derived objects } // register for ITK changed events this->AddListeners(node); } /* Notify observers */ EmitAddNodeEvent(node); } void mitk::StandaloneDataStorage::Remove(const mitk::DataNode* node) { if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); if (node == NULL) return; // remove ITK modified event listener this->RemoveListeners(node); // muellerm, 22.9.10: add additional reference count to ensure // that the node is not deleted when removed from the relation map // while m_Mutex is locked. This would cause the an itk::DeleteEvent // is thrown and a deadlock will occur when event receivers // access the DataStorage again in their event processing function // mitk::DataNode::ConstPointer nodeGuard(node); /* Notify observers of imminent node removal */ EmitRemoveNodeEvent(node); { itk::MutexLockHolder locked(m_Mutex); /* remove node from both relation adjacency lists */ this->RemoveFromRelation(node, m_SourceNodes); this->RemoveFromRelation(node, m_DerivedNodes); } } bool mitk::StandaloneDataStorage::Exists(const mitk::DataNode* node) const { itk::MutexLockHolder locked(m_Mutex); return (m_SourceNodes.find(node) != m_SourceNodes.end()); } void mitk::StandaloneDataStorage::RemoveFromRelation(const mitk::DataNode* node, AdjacencyList& relation) { for (AdjacencyList::const_iterator mapIter = relation.cbegin(); mapIter != relation.cend(); ++mapIter) // for each node in the relation if (mapIter->second.IsNotNull()) // if node has a relation list { SetOfObjects::Pointer s = const_cast(mapIter->second.GetPointer()); // search for node to be deleted in the relation list SetOfObjects::STLContainerType::iterator relationListIter = std::find(s->begin(), s->end(), node); // this assumes, that the relation list does not contain duplicates (which should be safe to assume) if (relationListIter != s->end()) // if node to be deleted is in relation list s->erase(relationListIter); // remove it from parentlist } /* now remove node from the relation */ AdjacencyList::iterator adIt; adIt = relation.find(node); if (adIt != relation.end()) relation.erase(adIt); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetAll() const { itk::MutexLockHolder locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); mitk::DataStorage::SetOfObjects::Pointer resultset = mitk::DataStorage::SetOfObjects::New(); /* Fill resultset with all objects that are managed by the StandaloneDataStorage object */ unsigned int index = 0; for (AdjacencyList::const_iterator it = m_SourceNodes.cbegin(); it != m_SourceNodes.cend(); ++it) if (it->first.IsNull()) continue; else resultset->InsertElement(index++, const_cast(it->first.GetPointer())); return SetOfObjects::ConstPointer(resultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetRelations(const mitk::DataNode* node, const AdjacencyList& relation, const NodePredicateBase* condition, bool onlyDirectlyRelated) const { if (node == NULL) throw std::invalid_argument("invalid node"); /* Either read direct relations directly from adjacency list */ if (onlyDirectlyRelated) { AdjacencyList::const_iterator it = relation.find(node); // get parents of current node if ((it == relation.cend()) || (it->second.IsNull())) // node not found in list or no set of parents return SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return an empty set else return this->FilterSetOfObjects(it->second, condition); } /* Or traverse adjacency list to collect all related nodes */ std::vector resultset; std::vector openlist; /* Initialize openlist with node. this will add node to resultset, but that is necessary to detect circular relations that would lead to endless recursion */ openlist.push_back(node); while (openlist.size() > 0) { mitk::DataNode::ConstPointer current = openlist.back(); // get element that needs to be processed openlist.pop_back(); // remove last element, because it gets processed now resultset.push_back(current); // add current element to resultset AdjacencyList::const_iterator it = relation.find(current); // get parents of current node if ( (it == relation.cend()) // if node not found in list || (it->second.IsNull()) // or no set of parents available || (it->second->Size() == 0)) // or empty set of parents continue; // then continue with next node in open list else for (SetOfObjects::ConstIterator parentIt = it->second->Begin(); parentIt != it->second->End(); ++parentIt) // for each parent of current node { mitk::DataNode::ConstPointer p = parentIt.Value().GetPointer(); if ( !(std::find(resultset.cbegin(), resultset.cend(), p) != resultset.end()) // if it is not already in resultset && !(std::find(openlist.cbegin(), openlist.cend(), p) != openlist.cend())) // and not already in openlist openlist.push_back(p); // then add it to openlist, so that it can be processed } } /* now finally copy the results to a proper SetOfObjects variable exluding the initial node and checking the condition if any is given */ mitk::DataStorage::SetOfObjects::Pointer realResultset = mitk::DataStorage::SetOfObjects::New(); if (condition != NULL) { - for (std::vector::const_iterator resultIt = resultset.cbegin(); resultIt != resultset.cend(); resultIt++) + for (std::vector::const_iterator resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if ((*resultIt != node) && (condition->CheckNode(*resultIt) == true)) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } else { - for (std::vector::const_iterator resultIt = resultset.cbegin(); resultIt != resultset.cend(); resultIt++) + for (std::vector::const_iterator resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if (*resultIt != node) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } return SetOfObjects::ConstPointer(realResultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetSources(const mitk::DataNode* node, const NodePredicateBase* condition, bool onlyDirectSources) const { itk::MutexLockHolder locked(m_Mutex); return this->GetRelations(node, m_SourceNodes, condition, onlyDirectSources); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetDerivations(const mitk::DataNode* node, const NodePredicateBase* condition, bool onlyDirectDerivations) const { itk::MutexLockHolderlocked(m_Mutex); return this->GetRelations(node, m_DerivedNodes, condition, onlyDirectDerivations); } void mitk::StandaloneDataStorage::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << "StandaloneDataStorage:\n"; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index 331d692b82..9952446a77 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -1,344 +1,343 @@ /*=================================================================== 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 "mitkTemporoSpatialStringProperty.h" #ifdef _MSC_VER //has to be deactivated because of a bug in boost v1.59. see Boost bug ticket #11599 //as soon as MITK uses a boost version with a bug fix we can remove the disableling. # pragma warning(push) # pragma warning(disable: 4715) #endif #include #include #include mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty( const char* s ) { if (s) { - std::string value = s; SliceMapType slices{ { 0, s } }; m_Values.insert(std::make_pair(0, slices)); } } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty( const std::string& s ) { SliceMapType slices{ { 0, s } }; m_Values.insert(std::make_pair(0, slices)); } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const TemporoSpatialStringProperty& other) : BaseProperty(other) , m_Values(other.m_Values) { } bool mitk::TemporoSpatialStringProperty::IsEqual(const BaseProperty& property ) const { return this->m_Values == static_cast(property).m_Values; } bool mitk::TemporoSpatialStringProperty::Assign(const BaseProperty& property ) { this->m_Values = static_cast(property).m_Values; return true; } std::string mitk::TemporoSpatialStringProperty::GetValueAsString() const { return GetValue(); } itk::LightObject::Pointer mitk::TemporoSpatialStringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue() const { std::string result = ""; if (!m_Values.empty()) { if (!m_Values.begin()->second.empty()) { result = m_Values.begin()->second.begin()->second; } } return result; }; std::pair mitk::TemporoSpatialStringProperty::CheckValue(const TimeStepType& timeStep, const IndexValueType& zSlice, bool allowCloseTime, bool allowCloseSlice) const { std::string value = ""; bool found = false; TimeMapType::const_iterator timeIter = m_Values.find(timeStep); TimeMapType::const_iterator timeEnd = m_Values.end(); if (timeIter == timeEnd && allowCloseTime) { //search for closest time step (earlier preverd) timeIter = m_Values.upper_bound(timeStep); if (timeIter != m_Values.begin()) { //there is a key lower than time step timeIter = std::prev(timeIter); } } if (timeIter != timeEnd) { const SliceMapType & slices = timeIter->second; SliceMapType::const_iterator sliceIter = slices.find(zSlice); SliceMapType::const_iterator sliceEnd = slices.end(); if (sliceIter == sliceEnd && allowCloseSlice) { //search for closest slice (earlier preverd) sliceIter = slices.upper_bound(zSlice); if (sliceIter != slices.begin()) { //there is a key lower than slice sliceIter = std::prev(sliceIter); } } if (sliceIter != sliceEnd) { value = sliceIter->second; found = true; } } return std::make_pair(found, value); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue(const TimeStepType& timeStep, const IndexValueType& zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).second; }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueBySlice(const IndexValueType& zSlice, bool allowClose) const { return GetValue(0, zSlice, true, allowClose); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueByTimeStep(const TimeStepType& timeStep, bool allowClose) const { return GetValue(timeStep, 0, allowClose, true); }; bool mitk::TemporoSpatialStringProperty::HasValue() const { return !m_Values.empty(); }; bool mitk::TemporoSpatialStringProperty::HasValue(const TimeStepType& timeStep, const IndexValueType& zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).first; }; bool mitk::TemporoSpatialStringProperty::HasValueBySlice(const IndexValueType& zSlice, bool allowClose) const { return HasValue(0, zSlice, true, allowClose); }; bool mitk::TemporoSpatialStringProperty::HasValueByTimeStep(const TimeStepType& timeStep, bool allowClose) const { return HasValue(timeStep, 0, allowClose, true); }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices(const TimeStepType& timeStep) const { std::vector result; TimeMapType::const_iterator timeIter = m_Values.find(timeStep); TimeMapType::const_iterator timeEnd = m_Values.end(); if (timeIter != timeEnd) { for (auto const& element : timeIter->second) { result.push_back(element.first); } } return result; }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps() const { std::vector result; for (auto const& element : m_Values) { result.push_back(element.first); } return result; }; void mitk::TemporoSpatialStringProperty::SetValue(const TimeStepType& timeStep, const IndexValueType& zSlice, const ValueType& value) { TimeMapType::iterator timeIter = m_Values.find(timeStep); TimeMapType::iterator timeEnd = m_Values.end(); if (timeIter == timeEnd) { SliceMapType slices{ { zSlice, value } }; m_Values.insert(std::make_pair(timeStep, slices)); } else { timeIter->second[zSlice] = value; } this->Modified(); }; void mitk::TemporoSpatialStringProperty::SetValue(const ValueType& value) { this->Modified(); m_Values.clear(); this->SetValue(0, 0, value); }; // Create necessary escape sequences from illegal characters // the following function was copied from the json writer of boost::property_tree std::string CreateJSONEscapes(const std::string &s) { std::string result; std::string::const_iterator b = s.begin(); std::string::const_iterator e = s.end(); while (b != e) { typedef boost::make_unsigned::type UCh; typedef std::string::value_type Ch; UCh c(*b); // This assumes an ASCII superset. // We escape everything outside ASCII, because this code can't // handle high unicode characters. if (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x2E) || (c >= 0x30 && c <= 0x5B) || (c >= 0x5D && c <= 0x7F)) result += *b; else if (*b == Ch('\b')) result += Ch('\\'), result += Ch('b'); else if (*b == Ch('\f')) result += Ch('\\'), result += Ch('f'); else if (*b == Ch('\n')) result += Ch('\\'), result += Ch('n'); else if (*b == Ch('\r')) result += Ch('\\'), result += Ch('r'); else if (*b == Ch('\t')) result += Ch('\\'), result += Ch('t'); else if (*b == Ch('/')) result += Ch('\\'), result += Ch('/'); else if (*b == Ch('"')) result += Ch('\\'), result += Ch('"'); else if (*b == Ch('\\')) result += Ch('\\'), result += Ch('\\'); else { const char *hexdigits = "0123456789ABCDEF"; unsigned long u = (std::min)(static_cast( static_cast(*b)), 0xFFFFul); int d1 = u / 4096; u -= d1 * 4096; int d2 = u / 256; u -= d2 * 256; int d3 = u / 16; u -= d3 * 16; int d4 = u; result += Ch('\\'); result += Ch('u'); result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]); result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]); } ++b; } return result; } ::std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty* prop) { //REMARK: Implemented own serialization instead of using boost::ptree::json_write because //currently everything (even numbers) are converted into string representations by the writer //so e.g. it becomes "t":"2" instaed of "t":2 const mitk::TemporoSpatialStringProperty* tsProp = dynamic_cast(prop); if (!tsProp) { return ""; } std::ostringstream stream; stream << "{\"values\":["; std::vector ts = tsProp->GetAvailableTimeSteps(); bool first = true; for (auto t : ts) { std::vector zs = tsProp->GetAvailableSlices(t); for (auto z : zs) { std::string value = CreateJSONEscapes(tsProp->GetValue(t, z)); if (first) { first = false; } else { stream << ", "; } stream << "{\"t\":" << t << ", \"z\":" << z << ", \"value\":\"" << value << "\"}"; } } stream << "]}"; return stream.str(); } mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(const std::string& value) { mitk::TemporoSpatialStringProperty::Pointer prop = mitk::TemporoSpatialStringProperty::New(); boost::property_tree::ptree root; std::istringstream stream(value); boost::property_tree::read_json(stream, root); // Iterator over all animals for (boost::property_tree::ptree::value_type &element : root.get_child("values")) { std::string value = element.second.get("value", ""); mitk::TemporoSpatialStringProperty::IndexValueType z = element.second.get< mitk::TemporoSpatialStringProperty::IndexValueType>("z", 0); TimeStepType t = element.second.get("t", 0); prop->SetValue(t, z, value); } return prop.GetPointer(); } #ifdef _MSC_VER # pragma warning(pop) #endif \ No newline at end of file diff --git a/Modules/Core/src/IO/mitkDicomSeriesReader.cpp b/Modules/Core/src/IO/mitkDicomSeriesReader.cpp index c720e0ac34..c05eec77fa 100644 --- a/Modules/Core/src/IO/mitkDicomSeriesReader.cpp +++ b/Modules/Core/src/IO/mitkDicomSeriesReader.cpp @@ -1,1788 +1,1787 @@ /*=================================================================== 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. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { std::string DicomSeriesReader::ReaderImplementationLevelToString( const ReaderImplementationLevel& enumValue ) { switch (enumValue) { case ReaderImplementationLevel_Supported: return "Supported"; case ReaderImplementationLevel_PartlySupported: return "PartlySupported"; case ReaderImplementationLevel_Implemented: return "Implemented"; case ReaderImplementationLevel_Unsupported: return "Unsupported"; default: return ""; }; } std::string DicomSeriesReader::PixelSpacingInterpretationToString( const PixelSpacingInterpretation& enumValue ) { switch (enumValue) { case PixelSpacingInterpretation_SpacingInPatient: return "In Patient"; case PixelSpacingInterpretation_SpacingAtDetector: return "At Detector"; case PixelSpacingInterpretation_SpacingUnknown: return "Unknown spacing"; default: return ""; }; } const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; // Image Pixel module dictionary["0028|0004"] = "dicom.pixel.PhotometricInterpretation"; dictionary["0028|0010"] = "dicom.pixel.Rows"; dictionary["0028|0011"] = "dicom.pixel.Columns"; // Image Plane module dictionary["0028|0030"] = "dicom.PixelSpacing"; dictionary["0018|1164"] = "dicom.ImagerPixelSpacing"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback, preLoadedImageBlock)) { if( filenames.empty() ) { return nullptr; } return node; } else { return nullptr; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, itk::SmartPointer preLoadedImageBlock) { if( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(nullptr); return true; // this is not actually an error but the result is very simple } 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 || io->GetPixelType() == itk::ImageIOBase::RGB) { LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); } if (node.GetData()) { return true; } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, itk::SmartPointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // axial gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } std::string DicomSeriesReader::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } bool DicomSeriesReader::DICOMStringToSpacing(const std::string& s, ScalarType& spacingX, ScalarType& spacingY) { bool successful = false; std::istringstream spacingReader(s); std::string spacing; if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingY = atof( spacing.c_str() ); if ( std::getline( spacingReader, spacing, '\\' ) ) { spacingX = atof( spacing.c_str() ); successful = true; } } return successful; } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( originReader, coordinate, '\\' ) && dim < 3) { p[dim++]= atof(coordinate.c_str()); } if (dim && dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } else if (dim == 0) { successful = false; p.Fill(0.0); // assume default (0,0,0) } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 ) { if (dim<3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim && dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } else if (dim == 0) { // fill with defaults right.Fill(0.0); right[0] = 1.0; up.Fill(0.0); up[1] = 1.0; successful = false; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (auto fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] ); if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position if ( result.GetBlockFilenames().empty() ) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock( *fileIter ); StringContainer remainingFiles; remainingFiles.insert( remainingFiles.end(), fileIter+1, files.end() ); result.AddFilesToUnsortedBlock( remainingFiles ); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { // check if this is at least roughly the same angle as recorded in DICOM tags if ( tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end() ) { // read value, compare to calculated angle std::string tiltStr = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagGantryTilt] ); double angle = atof(tiltStr.c_str()); MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees(); // TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing serious) // TODO TODO TODO when angle -27 and tiltangle 63, this will never trigger the if-clause... useless check in this case! old bug..?! if ( fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25) { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this is fine { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we are right) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if ( result.GetBlockFilenames().size() == 2 ) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID scanner.AddTag( tagSOPClassUID ); const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing scanner.AddTag( tagImagerPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt scanner.AddTag( tagGantryTilt ); const gdcm::Tag tagModality(0x0008, 0x0060); // modality scanner.AddTag( tagModality ); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag( tagNumberOfFrames ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments (when anybody asks for it) FileNamesGrouping result; // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return result; } // assign files IDs that will separate them for loading into image blocks for (auto fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue; /* sort out multi-frame if ( scanner.GetValue( fileIter->first , tagNumberOfFrames ) ) { MITK_INFO << "Ignoring " << fileIter->first << " because we cannot handle multi-frame images."; continue; } */ // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); result[ moreUniqueSeriesId ].AddFile( fileIter->first ); } // PART II: sort slices spatially (or at least consistently if this is NOT possible, see method) for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { try { result[ groupIter->first ] = ImageBlockDescriptor( SortSeriesSlices( groupIter->second.GetFilenames() ) ); // sort each slice group spatially } catch(...) { MITK_ERROR << "Caught something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks // * this includes images which DO NOT PROVIDE spatial information, i.e. all images w/o ImagePositionPatient will be loaded separately FileNamesGrouping groupsOf3DPlusTBlocks; // final result of this function for ( FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter ) { FileNamesGrouping groupsOf3DBlocks; // intermediate result for only this group(!) - std::map mapOf3DBlockAnalysisResults; StringContainer filesStillToAnalyze = groupIter->second.GetFilenames(); std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID << " of " << groupIter->second.GetFilenames().size() << " files"; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; ImageBlockDescriptor thisBlock( analysisResult.GetBlockFilenames() ); std::string firstFileInBlock = thisBlock.GetFilenames().front(); thisBlock.SetImageBlockUID( newGroupUID.str() ); thisBlock.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSeriesInstanceUID ) ) ); thisBlock.SetHasGantryTiltCorrected( analysisResult.ContainsGantryTilt() ); thisBlock.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagSOPClassUID ) ) ); thisBlock.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagNumberOfFrames ) ) ); thisBlock.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagModality ) ) ); thisBlock.SetPixelSpacingInformation( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagPixelSpacing ) ), DicomSeriesReader::ConstCharStarToString( scanner.GetValue( firstFileInBlock.c_str(), tagImagerPixelSpacing ) ) ); thisBlock.SetHasMultipleTimePoints( false ); groupsOf3DBlocks[ newGroupUID.str() ] = thisBlock; //MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; MITK_DEBUG << "Result: sorted 3D group with " << groupsOf3DBlocks[ newGroupUID.str() ].GetFilenames().size() << " files"; StringContainer debugOutputFiles = analysisResult.GetBlockFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " IN " << *siter; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis for (StringContainer::const_iterator siter = filesStillToAnalyze.begin(); siter != filesStillToAnalyze.end(); ++siter) MITK_DEBUG << " OUT " << *siter; } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of groupsOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output groupsOf3DPlusTBlocks.insert( groupsOf3DBlocks.begin(), groupsOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( FileNamesGrouping::const_iterator block3DIter = groupsOf3DBlocks.begin(); block3DIter != groupsOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.GetFilenames().size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; MITK_DEBUG << " 3D+t group " << thisBlockKey; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *previous_origin_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient ), *destination_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ), *previous_destination_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient ); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString( origin_value ); std::string previousOriginString = ConstCharStarToString( previous_origin_value ); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString( destination_value ); std::string previousDestinationString = ConstCharStarToString( previous_destination_value ); identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); } } catch(...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block groupsOf3DPlusTBlocks[previousBlockKey].AddFiles( block3DIter->second.GetFilenames() ); groupsOf3DPlusTBlocks[previousBlockKey].SetHasMultipleTimePoints(true); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; int numberOfTimeSteps = groupsOf3DPlusTBlocks[previousBlockKey].GetFilenames().size() / numberOfFilesInPreviousBlock; MITK_DEBUG << " ==> group closed with " << numberOfTimeSteps << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( FileNamesGrouping::const_iterator groupIter = groupsOf3DPlusTBlocks.begin(); groupIter != groupsOf3DPlusTBlocks.end(); ++groupIter ) { ImageBlockDescriptor block = groupIter->second; MITK_DEBUG << " " << block.GetFilenames().size() << " '" << block.GetModality() << "' images (" << block.GetSOPClassUIDAsString() << ") in volume " << block.GetImageBlockUID(); MITK_DEBUG << " (gantry tilt : " << (block.HasGantryTiltCorrected()?"Yes":"No") << "; " "pixel spacing : " << PixelSpacingInterpretationToString( block.GetPixelSpacingType() ) << "; " "3D+t: " << (block.HasMultipleTimePoints()?"Yes":"No") << "; " "reader support: " << ReaderImplementationLevelToString( block.GetReaderImplementationLevel() ) << ")"; StringContainer debugOutputFiles = block.GetFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " F " << *siter; } MITK_DEBUG << "================================================================================"; return groupsOf3DPlusTBlocks; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception&) { // we are happy with even nothing, this will just group images of a series //MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImagerPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfFrames ); // be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); if (tagValueMap.find(tagImageOrientation) != tagValueMap.end()) { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError ); //string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue( simplifiedOrientationString ); } constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { FileNamesGrouping allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for ( FileNamesGrouping::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { return idIter->second.GetFilenames(); } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns (each piece of information except the rows/columns might be missing) sorting with GdcmSortFunction tries its best by sorting by spatial position and more hints (acquisition number, acquisition time, trigger time) but will always produce a sorting by falling back to SOP Instance UID. */ gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { if (sorter.Sort(unsortedFilenames)) { return sorter.GetFilenames(); } else { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } catch(std::logic_error&) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // This method MUST accept missing location and position information (and all else, too) // because we cannot rely on anything // (restriction on the sentence before: we have to provide consistent sorting, so we // rely on the minimum information all DICOM files need to provide: SOP Instance UID) /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns */ static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) static const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation // see if we have Image Position and Orientation if ( ds1.FindDataElement(tagImagePositionPatient) && ds1.FindDataElement(tagImageOrientation) && ds2.FindDataElement(tagImagePositionPatient) && ds2.FindDataElement(tagImageOrientation) ) { gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 ) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } // if we can sort by just comparing the distance, we do exactly that if ( fabs(dist1 - dist2) >= mitk::eps) { // default: compare position return dist1 < dist2; } else // we need to check more properties to distinguish slices { // try to sort by Acquisition Number static const gdcm::Tag tagAcquisitionNumber(0x0020, 0x0012); if (ds1.FindDataElement(tagAcquisitionNumber) && ds2.FindDataElement(tagAcquisitionNumber)) { gdcm::Attribute<0x0020,0x0012> acquisition_number1; // Acquisition number gdcm::Attribute<0x0020,0x0012> acquisition_number2; acquisition_number1.Set(ds1); acquisition_number2.Set(ds2); if (acquisition_number1 != acquisition_number2) { return acquisition_number1 < acquisition_number2; } else // neither position nor acquisition number are good for sorting, so check more { // try to sort by Acquisition Time static const gdcm::Tag tagAcquisitionTime(0x0008, 0x0032); if (ds1.FindDataElement(tagAcquisitionTime) && ds2.FindDataElement(tagAcquisitionTime)) { gdcm::Attribute<0x0008,0x0032> acquisition_time1; // Acquisition time gdcm::Attribute<0x0008,0x0032> acquisition_time2; acquisition_time1.Set(ds1); acquisition_time2.Set(ds2); if (acquisition_time1 != acquisition_time2) { return acquisition_time1 < acquisition_time2; } else // we gave up on image position, acquisition number and acquisition time now { // let's try trigger time static const gdcm::Tag tagTriggerTime(0x0018, 0x1060); if (ds1.FindDataElement(tagTriggerTime) && ds2.FindDataElement(tagTriggerTime)) { gdcm::Attribute<0x0018,0x1060> trigger_time1; // Trigger time gdcm::Attribute<0x0018,0x1060> trigger_time2; trigger_time1.Set(ds1); trigger_time2.Set(ds2); if (trigger_time1 != trigger_time2) { return trigger_time1 < trigger_time2; } // ELSE! // for this and many previous ifs we fall through if nothing lets us sort } // . } // . } // . } } } } // . // LAST RESORT: all valuable information for sorting is missing. // Sort by some meaningless but unique identifiers to satisfy the sort function static const gdcm::Tag tagSOPInstanceUID(0x0008, 0x0018); if (ds1.FindDataElement(tagSOPInstanceUID) && ds2.FindDataElement(tagSOPInstanceUID)) { MITK_DEBUG << "Dicom images are missing attributes for a meaningful sorting, falling back to SOP instance UID comparison."; gdcm::Attribute<0x0008,0x0018> SOPInstanceUID1; // SOP instance UID is mandatory and unique gdcm::Attribute<0x0008,0x0018> SOPInstanceUID2; SOPInstanceUID1.Set(ds1); SOPInstanceUID2.Set(ds2); return SOPInstanceUID1 < SOPInstanceUID2; } else { // no DICOM file should really come down here, this should only be reached with unskillful and unlucky manipulation of files std::string error_message("Malformed DICOM images, which do not even contain a SOP Instance UID."); MITK_ERROR << error_message; throw std::logic_error( error_message ); } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor& blockInfo, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image); } void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, const ImageBlockDescriptor& blockInfo, Image* image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); //DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for ( auto i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ ) { const StringContainer& files = (*i); unsigned int slice(0); for ( auto fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); //If more than one time step add postfix ".t" + timestep if(timeStep != 0) { std::ostringstream postfix; postfix << ".t" << timeStep; propertyKeySliceLocation.append(postfix.str()); propertyKeyInstanceNumber.append(postfix.str()); propertyKeySOPInstanceNumber.append(postfix.str()); } image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) ); image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) ); image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) ); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary(); const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); auto dictIter = dict.Begin(); while ( dictIter != dict.End() ) { //MITK_DEBUG << "Key " << dictIter->first; std::string value; if ( itk::ExposeMetaData( dict, dictIter->first, value ) ) { //MITK_DEBUG << "Value " << value; auto valuePosition = propertyLookup.find( dictIter->first ); if ( valuePosition != propertyLookup.end() ) { std::string propertyKey = valuePosition->second; //MITK_DEBUG << "--> " << propertyKey; image->SetProperty( propertyKey.c_str(), StringProperty::New(value) ); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ; } ++dictIter; } // copy imageblockdescriptor as properties image->SetProperty("dicomseriesreader.SOPClass", StringProperty::New(blockInfo.GetSOPClassUIDAsString())); image->SetProperty("dicomseriesreader.ReaderImplementationLevelString", StringProperty::New(ReaderImplementationLevelToString( blockInfo.GetReaderImplementationLevel() ))); image->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( blockInfo.GetReaderImplementationLevel() )); image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New(PixelSpacingInterpretationToString( blockInfo.GetPixelSpacingType() ))); image->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New(blockInfo.GetPixelSpacingType())); image->SetProperty("dicomseriesreader.MultiFrameImage", BoolProperty::New(blockInfo.IsMultiFrameImage())); image->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New(blockInfo.HasGantryTiltCorrected())); image->SetProperty("dicomseriesreader.3D+t", BoolProperty::New(blockInfo.HasMultipleTimePoints())); } void DicomSeriesReader::FixSpacingInformation( mitk::Image* image, const ImageBlockDescriptor& imageBlockDescriptor ) { // spacing provided by ITK/GDCM Vector3D imageSpacing = image->GetGeometry()->GetSpacing(); ScalarType imageSpacingX = imageSpacing[0]; ScalarType imageSpacingY = imageSpacing[1]; // spacing as desired by MITK (preference for "in patient", else "on detector", or "1.0/1.0") ScalarType desiredSpacingX = imageSpacingX; ScalarType desiredSpacingY = imageSpacingY; imageBlockDescriptor.GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); MITK_DEBUG << "Loaded spacing: " << imageSpacingX << "/" << imageSpacingY; MITK_DEBUG << "Corrected spacing: " << desiredSpacingX << "/" << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; image->GetGeometry()->SetSpacing( imageSpacing ); } void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { const char* previousCLocale = setlocale(LC_NUMERIC, nullptr); setlocale(LC_NUMERIC, "C"); std::locale previousCppLocale( std::cin.getloc() ); std::locale l( "C" ); std::cin.imbue(l); ImageBlockDescriptor imageBlockDescriptor; const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID const gdcm::Tag tagModality(0x0008, 0x0060); // modality const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames try { Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock; CallbackCommand *command = callback ? new CallbackCommand(callback) : nullptr; bool initialize_node = false; /* special case for Philips 3D+t ultrasound images */ if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) ) { // TODO what about imageBlockDescriptor? // TODO what about preLoadedImageBlock? ReadPhilips3DDicom(filenames.front().c_str(), image); initialize_node = true; } else { /* default case: assume "normal" image blocks, possibly 3D+t */ bool canLoadAs4D(true); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); // need non-const access for map gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D ); unsigned int volume_count = imageBlocks.size(); imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSeriesInstanceUID ) ) ); imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagSOPClassUID ) ) ); imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagModality ) ) ); imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagNumberOfFrames ) ) ); imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagPixelSpacing ) ), ConstCharStarToString( scanner.GetValue( filenames.front().c_str(), tagImagerPixelSpacing ) ) ); GantryTiltInformation tiltInfo; // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) { // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents // check first and last position slice from tags, make some calculations to detect tilt std::string firstFilename(imageBlocks.front().front()); // calculate from first and last slice to minimize rounding errors std::string secondFilename(imageBlocks.front().back()); std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) ); std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) ); std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) ); bool ignoredConversionError(-42); // hard to get here, no graceful way to react Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) ); Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) ); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError ); tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 ); correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); } else { correctTilt = false; // we CANNOT do that } imageBlockDescriptor.SetHasGantryTiltCorrected( correctTilt ); if (volume_count == 1 || !canLoadAs4D || !load4D) { DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock ); // load first 3D block imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded imageBlockDescriptor.SetHasMultipleTimePoints( false ); FixSpacingInformation( image, imageBlockDescriptor ); CopyMetaDataToImageProperties( imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image); initialize_node = true; } else if (volume_count > 1) { imageBlockDescriptor.AddFiles(filenames); // all is loaded imageBlockDescriptor.SetHasMultipleTimePoints( true ); DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock ); initialize_node = true; } } if (initialize_node) { // forward some image properties to node node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true ); std::string patientName = "NoName"; if(node.GetProperty("dicom.patient.PatientsName")) patientName = node.GetProperty("dicom.patient.PatientsName")->GetValueAsString(); node.SetData( image ); node.SetName(patientName); setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); } MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):"; MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage()?"Yes":"No"); MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString( imageBlockDescriptor.GetPixelSpacingType() ) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0]; MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected()?"Yes":"No"); MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints()?"Yes":"No"); MITK_DEBUG << "--------------------------------------------------------------------------------"; } catch (std::exception& e) { // reset locale then throw up setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom"; throw e; } } void DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner) { const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) scanner.AddTag(tagImagePositionPatient); const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID scanner.AddTag(tagSeriesInstanceUID); const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation scanner.AddTag(tagImageOrientation); const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag( tagSliceLocation ); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag( tagInstanceNumber ); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag( tagSOPInstanceNumber ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing scanner.AddTag( tagImagerPixelSpacing ); const gdcm::Tag tagModality(0x0008, 0x0060); // Modality scanner.AddTag( tagModality ); const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID scanner.AddTag( tagSOPClassUID ); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag( tagNumberOfFrames ); scanner.Scan(filenames); // make available image information for each file } std::list DicomSeriesReader::SortIntoBlocksFor3DplusT( const StringContainer& presortedFilenames, const gdcm::Scanner::MappingType& tagValueMappings, bool /*sort*/, bool& canLoadAs4D ) { std::list imageBlocks; // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); std::string firstPosition; unsigned int numberOfBlocks(0); // number of 3D image blocks static const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); //Image position (Patient) const gdcm::Tag tagModality(0x0008,0x0060); // loop files to determine number of image blocks for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); fileIter != sorted_filenames.end(); ++fileIter) { gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second; if(tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end()) { const std::string& modality = tagToValueMap.find(tagModality)->second; if ( modality.compare("RTIMAGE ") == 0 || modality.compare("RTIMAGE") == 0 ) { MITK_WARN << "Modality "<< modality <<" is not fully supported yet."; numberOfBlocks = 1; break; } else { // we expect to get images w/ missing position information ONLY as separated blocks. assert( presortedFilenames.size() == 1 ); numberOfBlocks = 1; break; } } std::string position = tagToValueMap.find(tagImagePositionPatient)->second; MITK_DEBUG << " " << *fileIter << " at " << position; if (firstPosition.empty()) { firstPosition = position; } if ( position == firstPosition ) { ++numberOfBlocks; } else { break; // enough information to know the number of image blocks } } MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files // loop files to sort them into image blocks unsigned int numberOfExpectedSlices(0); for (unsigned int block = 0; block < numberOfBlocks; ++block) { StringContainer filesOfCurrentBlock; for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; fileIter != sorted_filenames.end(); //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts ) { filesOfCurrentBlock.push_back( *fileIter ); for (unsigned int b = 0; b < numberOfBlocks; ++b) { if (fileIter != sorted_filenames.end()) ++fileIter; } } imageBlocks.push_back(filesOfCurrentBlock); if (block == 0) { numberOfExpectedSlices = filesOfCurrentBlock.size(); } else { if (filesOfCurrentBlock.size() != numberOfExpectedSlices) { MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks << " image blocks of " << numberOfExpectedSlices << " images each. Block " << block << " got " << filesOfCurrentBlock.size() << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible) canLoadAs4D = false; } } } return imageBlocks; } Image::Pointer DicomSeriesReader ::MultiplexLoadDICOMByITK(const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else { return nullptr; } } Image::Pointer DicomSeriesReader ::MultiplexLoadDICOMByITK4D( std::list& imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation& tiltInfo, DcmIoType::Pointer& io, CallbackCommand* command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(imageBlocks.front().front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITK4DScalar(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITK4DRGBPixel(imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command ,preLoadedImageBlock); } else { return nullptr; } } } // end namespace mitk diff --git a/Modules/Core/src/IO/mitkIOUtil.cpp b/Modules/Core/src/IO/mitkIOUtil.cpp index 247afac955..8844e53a02 100644 --- a/Modules/Core/src/IO/mitkIOUtil.cpp +++ b/Modules/Core/src/IO/mitkIOUtil.cpp @@ -1,1104 +1,1104 @@ /*=================================================================== 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 "mitkIOUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //ITK #include //VTK #include #include #include #include #include static std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } #ifdef US_PLATFORM_WINDOWS #include #include // make the posix flags point to the obsolte bsd types on windows #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #else #include #include #include #endif #include #include static const char validLetters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // A cross-platform version of the mkstemps function static int mkstemps_compat(char* tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary files to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return -1; } /* This is where the Xs start. */ char* XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return -1; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, NULL); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif for (unsigned int count = 0; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; int fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { errno = savedErrno; return fd; } else if (errno != EEXIST) { return -1; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } // A cross-platform version of the POSIX mkdtemp function static char* mkdtemps_compat(char* tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary dirs to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary dir. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return NULL; } /* This is where the Xs start. */ char* XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return NULL; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, NULL); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif unsigned int count = 0; for (; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; #ifdef US_PLATFORM_WINDOWS int fd = _mkdir (tmpl); //, _S_IREAD | _S_IWRITE | _S_IEXEC); #else int fd = mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); #endif if (fd >= 0) { errno = savedErrno; return tmpl; } else if (errno != EEXIST) { return NULL; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return NULL; } //#endif //************************************************************** // mitk::IOUtil method definitions namespace mitk { struct IOUtil::Impl { struct FixedReaderOptionsFunctor : public ReaderOptionsFunctorBase { FixedReaderOptionsFunctor(const IFileReader::Options& options) : m_Options(options) {} virtual bool operator()(LoadInfo& loadInfo) override { IFileReader* reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader) { reader->SetOptions(m_Options); } return false; } private: const IFileReader::Options& m_Options; }; struct FixedWriterOptionsFunctor : public WriterOptionsFunctorBase { FixedWriterOptionsFunctor(const IFileReader::Options& options) : m_Options(options) {} virtual bool operator()(SaveInfo& saveInfo) override { IFileWriter* writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer) { writer->SetOptions(m_Options); } return false; } private: const IFileWriter::Options& m_Options; }; static BaseData::Pointer LoadBaseDataFromFile(const std::string& path); static void SetDefaultDataNodeProperties(mitk::DataNode* node, const std::string& filePath = std::string()); }; #ifdef US_PLATFORM_WINDOWS std::string IOUtil::GetProgramPath() { char path[512]; std::size_t index = std::string(path, GetModuleFileName(NULL, path, 512)).find_last_of('\\'); return std::string(path, index); } #elif defined(US_PLATFORM_APPLE) #include std::string IOUtil::GetProgramPath() { char path[512]; uint32_t size = sizeof(path); if (_NSGetExecutablePath(path, &size) == 0) { std::size_t index = std::string(path).find_last_of('/'); std::string strPath = std::string(path, index); //const char* execPath = strPath.c_str(); //mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch(execPath,false); return strPath; } return std::string(); } #else #include #include #include std::string IOUtil::GetProgramPath() { std::stringstream ss; ss << "/proc/" << getpid() << "/exe"; char proc[512] = {0}; ssize_t ch = readlink(ss.str().c_str(), proc, 512); if (ch == -1) return std::string(); std::size_t index = std::string(proc).find_last_of('/'); return std::string(proc, index); } #endif char IOUtil::GetDirectorySeparator() { #ifdef US_PLATFORM_WINDOWS return '\\'; #else return '/'; #endif } std::string IOUtil::GetTempPath() { static std::string result; if (result.empty()) { #ifdef US_PLATFORM_WINDOWS char tempPathTestBuffer[1]; DWORD bufferLength = ::GetTempPath(1, tempPathTestBuffer); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } std::vector tempPath(bufferLength); bufferLength = ::GetTempPath(bufferLength, &tempPath[0]); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } result.assign(tempPath.begin(), tempPath.begin() + static_cast(bufferLength)); #else result = "/tmp/"; #endif } return result; } std::string IOUtil::CreateTemporaryFile(const std::string& templateName, std::string path) { ofstream tmpOutputStream; std::string returnValue = CreateTemporaryFile(tmpOutputStream,templateName,path); tmpOutputStream.close(); return returnValue; } std::string IOUtil::CreateTemporaryFile(std::ofstream& f, const std::string& templateName, std::string path) { return CreateTemporaryFile(f, std::ios_base::out | std::ios_base::trunc, templateName, path); } std::string IOUtil::CreateTemporaryFile(std::ofstream& f, std::ios_base::openmode mode, const std::string& templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; int fd = mkstemps_compat(&dst_path[0], suffixlen); if(fd != -1) { path.assign(dst_path.begin(), dst_path.end() - 1); f.open(path.c_str(), mode | std::ios_base::out | std::ios_base::trunc); close(fd); } else { mitkThrow() << "Creating temporary file " << &dst_path[0] << " failed: " << GetLastErrorStr(); } return path; } std::string IOUtil::CreateTemporaryDirectory(const std::string& templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += GetDirectorySeparator() + templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? - 1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; if(mkdtemps_compat(&dst_path[0], suffixlen) == NULL) { mitkThrow() << "Creating temporary directory " << &dst_path[0] << " failed: " << GetLastErrorStr(); } path.assign(dst_path.begin(), dst_path.end() - 1); return path; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string& path, DataStorage& storage) { std::vector paths; paths.push_back(path); return Load(paths, storage); } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string& path, const IFileReader::Options& options, DataStorage& storage) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::string& path) { std::vector paths; paths.push_back(path); return Load(paths); } std::vector IOUtil::Load(const std::string& path, const IFileReader::Options& options) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, NULL, NULL, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return loadInfos.front().m_Output; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::vector& paths, DataStorage& storage) { DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); std::vector loadInfos; for (auto loadInfo : paths) { loadInfos.push_back(loadInfo); } std::string errMsg = Load(loadInfos, nodeResult, &storage, NULL); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::vector& paths) { std::vector result; std::vector loadInfos; for (auto loadInfo : paths) { loadInfos.push_back(loadInfo); } std::string errMsg = Load(loadInfos, NULL, NULL, NULL); if (!errMsg.empty()) { mitkThrow() << errMsg; } for (std::vector::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd; ++iter) { result.insert(result.end(), iter->m_Output.begin(), iter->m_Output.end()); } return result; } int IOUtil::LoadFiles(const std::vector &fileNames, DataStorage& ds) { return static_cast(Load(fileNames, ds)->Size()); } DataStorage::Pointer IOUtil::LoadFiles(const std::vector& fileNames) { mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); Load(fileNames, *ds); return ds.GetPointer(); } BaseData::Pointer IOUtil::LoadBaseData(const std::string& path) { return Impl::LoadBaseDataFromFile(path); } BaseData::Pointer IOUtil::Impl::LoadBaseDataFromFile(const std::string& path) { std::vector baseDataList = Load(path); // The Load(path) call above should throw an exception if nothing could be loaded assert(!baseDataList.empty()); return baseDataList.front(); } DataNode::Pointer IOUtil::LoadDataNode(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(baseData); Impl::SetDefaultDataNodeProperties(node, path); return node; } Image::Pointer IOUtil::LoadImage(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::Image::Pointer image = dynamic_cast(baseData.GetPointer()); if(image.IsNull()) { mitkThrow() << path << " is not a mitk::Image but a " << baseData->GetNameOfClass(); } return image; } Surface::Pointer IOUtil::LoadSurface(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::Surface::Pointer surface = dynamic_cast(baseData.GetPointer()); if(surface.IsNull()) { mitkThrow() << path << " is not a mitk::Surface but a " << baseData->GetNameOfClass(); } return surface; } PointSet::Pointer IOUtil::LoadPointSet(const std::string& path) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path); mitk::PointSet::Pointer pointset = dynamic_cast(baseData.GetPointer()); if(pointset.IsNull()) { mitkThrow() << path << " is not a mitk::PointSet but a " << baseData->GetNameOfClass(); } return pointset; } std::string IOUtil::Load(std::vector& loadInfos, DataStorage::SetOfObjects* nodeResult, DataStorage* ds, ReaderOptionsFunctorBase* optionsCallback) { if (loadInfos.empty()) { return "No input files given"; } int filesToRead = loadInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2*filesToRead); std::string errMsg; std::map usedReaderItems; for(auto & loadInfo : loadInfos) { std::vector readers = loadInfo.m_ReaderSelector.Get(); if (readers.empty()) { if (!itksys::SystemTools::FileExists(loadInfo.m_Path.c_str())) { errMsg += "File '" + loadInfo.m_Path + "' does not exist\n"; } else { errMsg += "No reader available for '" + loadInfo.m_Path + "'\n"; } continue; } bool callOptionsCallback = readers.size() > 1 || !readers.front().GetReader()->GetOptions().empty(); // check if we already used a reader which should be re-used std::vector currMimeTypes = loadInfo.m_ReaderSelector.GetMimeTypes(); std::string selectedMimeType; for (std::vector::const_iterator mimeTypeIter = currMimeTypes.begin(), mimeTypeIterEnd = currMimeTypes.end(); mimeTypeIter != mimeTypeIterEnd; ++mimeTypeIter) { std::map::const_iterator oldSelectedItemIter = usedReaderItems.find(mimeTypeIter->GetName()); if (oldSelectedItemIter != usedReaderItems.end()) { // we found an already used item for a mime-type which is contained // in the current reader set, check all current readers if there service // id equals the old reader for (std::vector::const_iterator currReaderItem = readers.begin(), currReaderItemEnd = readers.end(); currReaderItem != currReaderItemEnd; ++currReaderItem) { if (currReaderItem->GetMimeType().GetName() == mimeTypeIter->GetName() && currReaderItem->GetServiceId() == oldSelectedItemIter->second.GetServiceId() && currReaderItem->GetConfidenceLevel() >= oldSelectedItemIter->second.GetConfidenceLevel()) { // okay, we used the same reader already, re-use its options selectedMimeType = mimeTypeIter->GetName(); callOptionsCallback = false; loadInfo.m_ReaderSelector.Select(oldSelectedItemIter->second.GetServiceId()); loadInfo.m_ReaderSelector.GetSelected().GetReader()->SetOptions( oldSelectedItemIter->second.GetReader()->GetOptions()); break; } } if (!selectedMimeType.empty()) break; } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(loadInfo); if (!callOptionsCallback && !loadInfo.m_Cancel) { usedReaderItems.erase(selectedMimeType); FileReaderSelector::Item selectedItem = loadInfo.m_ReaderSelector.GetSelected(); usedReaderItems.insert(std::make_pair(selectedItem.GetMimeType().GetName(), selectedItem)); } } if (loadInfo.m_Cancel) { errMsg += "Reading operation(s) cancelled."; break; } IFileReader* reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader == NULL) { errMsg += "Unexpected NULL reader."; break; } // Do the actual reading try { DataStorage::SetOfObjects::Pointer nodes; if (ds != NULL) { nodes = reader->Read(*ds); } else { nodes = DataStorage::SetOfObjects::New(); std::vector baseData = reader->Read(); for (std::vector::iterator iter = baseData.begin(); iter != baseData.end(); ++iter) { if (iter->IsNotNull()) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); nodes->InsertElement(nodes->Size(), node); } } } for (DataStorage::SetOfObjects::ConstIterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { const mitk::DataNode::Pointer& node = nodeIter->Value(); mitk::BaseData::Pointer data = node->GetData(); if (data.IsNull()) { continue; } mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New(loadInfo.m_Path); data->SetProperty("path", pathProp); loadInfo.m_Output.push_back(data); if (nodeResult) { nodeResult->push_back(nodeIter->Value()); } } if (loadInfo.m_Output.empty() || (nodeResult && nodeResult->Size() == 0)) { errMsg += "Unknown read error occurred reading " + loadInfo.m_Path; } } catch (const std::exception& e) { errMsg += "Exception occured when reading file " + loadInfo.m_Path + ":\n" + e.what() + "\n\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToRead; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2*filesToRead); return errMsg; } std::vector IOUtil::Load(const us::ModuleResource &usResource, std::ios_base::openmode mode) { us::ModuleResourceStream resStream(usResource,mode); mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::vector mimetypes = mimeTypeProvider->GetMimeTypesForFile(usResource.GetResourcePath()); std::vector data; if(mimetypes.empty()) { mitkThrow() << "No mimetype for resource stream: "<< usResource.GetResourcePath(); return data; } mitk::FileReaderRegistry fileReaderRegistry; std::vector > refs = fileReaderRegistry.GetReferences(mimetypes[0]); if(refs.empty()) { mitkThrow() << "No reader available for resource stream: "<< usResource.GetResourcePath(); return data; } mitk::IFileReader* reader = fileReaderRegistry.GetReader(refs[0]); reader->SetInput(usResource.GetResourcePath(),&resStream); data = reader->Read(); return data; } void IOUtil::Save(const BaseData* data, const std::string& path) { Save(data, path, IFileWriter::Options()); } void IOUtil::Save(const BaseData* data, const std::string& path, const IFileWriter::Options& options) { Save(data, std::string(), path, options); } void IOUtil::Save(const BaseData* data, const std::string& mimeType, const std::string& path, bool addExtension) { Save(data, mimeType, path, IFileWriter::Options(), addExtension); } void IOUtil::Save(const BaseData* data, const std::string& mimeType, const std::string& path, const IFileWriter::Options& options, bool addExtension) { if ((data == NULL) || (data->IsEmpty())) mitkThrow() << "BaseData cannotbe null or empty for save methods in IOUtil.h."; std::string errMsg; if (options.empty()) { errMsg = Save(data, mimeType, path, NULL, addExtension); } else { Impl::FixedWriterOptionsFunctor optionsCallback(options); errMsg = Save(data, mimeType, path, &optionsCallback, addExtension); } if (!errMsg.empty()) { mitkThrow() << errMsg; } } void IOUtil::Save(std::vector& saveInfos) { std::string errMsg = Save(saveInfos, NULL); if (!errMsg.empty()) { mitkThrow() << errMsg; } } bool IOUtil::SaveImage(mitk::Image::Pointer image, const std::string& path) { Save(image, path); return true; } bool IOUtil::SaveSurface(Surface::Pointer surface, const std::string& path) { Save(surface, path); return true; } bool IOUtil::SavePointSet(PointSet::Pointer pointset, const std::string& path) { Save(pointset, path); return true; } bool IOUtil::SaveBaseData( mitk::BaseData* data, const std::string& path) { Save(data, path); return true; } std::string IOUtil::Save(const BaseData* data, const std::string& mimeTypeName, const std::string& path, WriterOptionsFunctorBase* optionsCallback, bool addExtension) { if (path.empty()) { return "No output filename given"; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); SaveInfo saveInfo(data, mimeType, path); std::string ext = itksys::SystemTools::GetFilenameExtension(path); if (saveInfo.m_WriterSelector.IsEmpty()) { return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() + (mimeType.IsValid() ? (std::string(" and mime-type ") + mimeType.GetName()) : std::string()) + (ext.empty() ? std::string() : (std::string(" with extension ") + ext)); } // Add an extension if not already specified if (ext.empty() && addExtension) { - ext = saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front(); + saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front(); } std::vector infos; infos.push_back(saveInfo); return Save(infos, optionsCallback); } std::string IOUtil::Save(std::vector& saveInfos, WriterOptionsFunctorBase* optionsCallback) { if (saveInfos.empty()) { return "No data for saving available"; } int filesToWrite = saveInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2*filesToWrite); std::string errMsg; std::set usedSaveInfos; for (auto & saveInfo : saveInfos) { const std::string baseDataType = saveInfo.m_BaseData->GetNameOfClass(); std::vector writers = saveInfo.m_WriterSelector.Get(); // Error out if no compatible Writer was found if (writers.empty()) { errMsg += std::string("No writer available for ") + baseDataType + " data.\n"; continue; } bool callOptionsCallback = writers.size() > 1 || !writers[0].GetWriter()->GetOptions().empty(); // check if we already used a writer for this base data type // which should be re-used std::set::const_iterator oldSaveInfoIter = usedSaveInfos.find(saveInfo); if (oldSaveInfoIter != usedSaveInfos.end()) { // we previously saved a base data object of the same data with the same mime-type, // check if the same writer is contained in the current writer set and if the // confidence level matches FileWriterSelector::Item oldSelectedItem = oldSaveInfoIter->m_WriterSelector.Get(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); for (std::vector::const_iterator currWriterItem = writers.begin(), currWriterItemEnd = writers.end(); currWriterItem != currWriterItemEnd; ++currWriterItem) { if (currWriterItem->GetServiceId() == oldSelectedItem.GetServiceId() && currWriterItem->GetConfidenceLevel() >= oldSelectedItem.GetConfidenceLevel()) { // okay, we used the same writer already, re-use its options callOptionsCallback = false; saveInfo.m_WriterSelector.Select(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); saveInfo.m_WriterSelector.GetSelected().GetWriter()->SetOptions( oldSelectedItem.GetWriter()->GetOptions()); break; } } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(saveInfo); if (!callOptionsCallback && !saveInfo.m_Cancel) { usedSaveInfos.erase(saveInfo); usedSaveInfos.insert(saveInfo); } } if (saveInfo.m_Cancel) { errMsg += "Writing operation(s) cancelled."; break; } IFileWriter* writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer == NULL) { errMsg += "Unexpected NULL writer."; break; } // Do the actual writing try { writer->SetOutputLocation(saveInfo.m_Path); writer->Write(); } catch(const std::exception& e) { errMsg += std::string("Exception occurred when writing to ") + saveInfo.m_Path + ":\n" + e.what() + "\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToWrite; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2*filesToWrite); return errMsg; } // This method can be removed after the deprecated LoadDataNode() method was removed void IOUtil::Impl::SetDefaultDataNodeProperties(DataNode* node, const std::string& filePath) { // path mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenamePath(filePath) ); node->SetProperty(StringProperty::PATH, pathProp); // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if(nameProp.IsNull() || (strcmp(nameProp->GetValue(),"No Name!")==0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer() ); if(baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(),"No Name!")==0)) { // name neither defined in node, nor in BaseData -> name = filename nameProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenameWithoutExtension(filePath)); node->SetProperty("name", nameProp); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue()); node->SetProperty("name", nameProp); } } // visibility if(!node->GetProperty("visible")) { node->SetVisibility(true); } } IOUtil::SaveInfo::SaveInfo(const BaseData* baseData, const MimeType& mimeType, const std::string& path) : m_BaseData(baseData) , m_WriterSelector(baseData, mimeType.GetName(), path) , m_MimeType(mimeType.IsValid() ? mimeType // use the original mime-type : (m_WriterSelector.IsEmpty() ? mimeType // no writer found, use the original invalid mime-type : m_WriterSelector.GetDefault().GetMimeType() // use the found default mime-type ) ) , m_Path(path) , m_Cancel(false) { } bool IOUtil::SaveInfo::operator<(const IOUtil::SaveInfo& other) const { int r = strcmp(m_BaseData->GetNameOfClass(), other.m_BaseData->GetNameOfClass()); if (r == 0) { return m_WriterSelector.GetSelected().GetMimeType() < other.m_WriterSelector.GetSelected().GetMimeType(); } return r < 0; } IOUtil::LoadInfo::LoadInfo(const std::string& path) : m_Path(path) , m_ReaderSelector(path) , m_Cancel(false) { } } diff --git a/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp b/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp index 83cbaf977a..d9356ebaff 100644 --- a/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp +++ b/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp @@ -1,88 +1,88 @@ /*=================================================================== 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 "mitkLegacyFileWriterService.h" #include #include #include mitk::LegacyFileWriterService::LegacyFileWriterService(mitk::FileWriter::Pointer legacyWriter, const std::string& description) : AbstractFileWriter(legacyWriter->GetSupportedBaseData()) , m_LegacyWriter(legacyWriter) { this->SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".legacy."); this->SetDescription(description); CustomMimeType customMimeType; std::vector extensions = legacyWriter->GetPossibleFileExtensions(); - for(std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ext++) + for(std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ++ext) { if (ext->empty()) continue; std::string extension = *ext; if (extension.size() > 1 && extension[0] == '*') { // remove "*" extension = extension.substr(1); } if (extension.size() > 1 && extension[0] == '.') { // remove "." extension = extension.substr(1); } std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); customMimeType.AddExtension(extension); } this->SetMimeType(customMimeType); m_ServiceRegistration = RegisterService(); } mitk::LegacyFileWriterService::~LegacyFileWriterService() { } ////////////////////// Writing ///////////////////////// void mitk::LegacyFileWriterService::Write() { if (m_LegacyWriter.IsNull()) mitkThrow() << "LegacyFileWriterService was incorrectly initialized: Has no LegacyFileWriter."; ValidateOutputLocation(); LocalFile localFile(this); m_LegacyWriter->SetFileName(localFile.GetFileName().c_str()); m_LegacyWriter->SetInput(const_cast(this->GetInput())); m_LegacyWriter->Write(); } mitk::IFileWriter::ConfidenceLevel mitk::LegacyFileWriterService::GetConfidenceLevel() const { if (mitk::AbstractFileWriter::GetConfidenceLevel() == Unsupported) return Unsupported; DataNode::Pointer node = DataNode::New(); node->SetData(const_cast(this->GetInput())); return m_LegacyWriter->CanWriteDataType(node) ? Supported : Unsupported; } mitk::LegacyFileWriterService* mitk::LegacyFileWriterService::Clone() const { return new LegacyFileWriterService(*this); } diff --git a/Modules/Core/src/IO/mitkStandardFileLocations.cpp b/Modules/Core/src/IO/mitkStandardFileLocations.cpp index da4ee71930..8bfa5b9edb 100644 --- a/Modules/Core/src/IO/mitkStandardFileLocations.cpp +++ b/Modules/Core/src/IO/mitkStandardFileLocations.cpp @@ -1,241 +1,239 @@ /*=================================================================== 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 #include #include mitk::StandardFileLocations::StandardFileLocations() { } mitk::StandardFileLocations::~StandardFileLocations() { } mitk::StandardFileLocations* mitk::StandardFileLocations::GetInstance() { static StandardFileLocations::Pointer m_Instance = nullptr; if(m_Instance.IsNull()) m_Instance = StandardFileLocations::New(); return m_Instance; } void mitk::StandardFileLocations::AddDirectoryForSearch(const char * dir, bool insertInFrontOfSearchList) { - std::string directory = dir; - // Do nothing if directory is already included into search list (TODO more clever: search only once!) FileSearchVectorType::iterator iter; if(m_SearchDirectories.size() > 0) { iter = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(),std::string(dir)); if ( iter != m_SearchDirectories.end() ) return; } // insert dir into queue if(insertInFrontOfSearchList) { auto it = m_SearchDirectories.begin(); m_SearchDirectories.insert(it,std::string(dir)); } else m_SearchDirectories.push_back(std::string(dir)); } void mitk::StandardFileLocations::RemoveDirectoryForSearch(const char * dir) { FileSearchVectorType::iterator it; // background layers if(m_SearchDirectories.size() > 0) { it = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(),std::string(dir)); if(it != m_SearchDirectories.end()) { m_SearchDirectories.erase(it); return; } } } std::string mitk::StandardFileLocations::SearchDirectoriesForFile(const char * filename) { FileSearchVectorType::iterator it; - for(it = m_SearchDirectories.begin(); it != m_SearchDirectories.end(); it++) + for(it = m_SearchDirectories.begin(); it != m_SearchDirectories.end(); ++it) { std::string currDir = (*it); // Perhaps append "/" before appending filename if(currDir.find_last_of("\\")+1 != currDir.size() || currDir.find_last_of("/")+1 != currDir.size()) currDir += "/"; // Append filename currDir += filename; // Perhaps remove "/" after filename if(currDir.find_last_of("\\")+1 == currDir.size() || currDir.find_last_of("/")+1 == currDir.size()) currDir.erase(currDir.size()-1,currDir.size()); // convert to OS dependent path schema currDir = itksys::SystemTools::ConvertToOutputPath(currDir.c_str()); // On windows systems, the ConvertToOutputPath method quotes pathes that contain empty spaces. // These quotes are not expected by the FileExists method and therefore removed, if existing. if(currDir.find_last_of("\"")+1 == currDir.size()) currDir.erase(currDir.size()-1,currDir.size()); if(currDir.find_last_of("\"") == 0) currDir.erase(0,1); // Return first found path if(itksys::SystemTools::FileExists(currDir.c_str())) return currDir; } return std::string(""); } std::string mitk::StandardFileLocations::FindFile(const char* filename, const char* pathInSourceDir) { std::string directoryPath; // 1. look for MITKCONF environment variable const char* mitkConf = itksys::SystemTools::GetEnv("MITKCONF"); if (mitkConf!=nullptr) AddDirectoryForSearch(mitkConf, false); // 2. use .mitk-subdirectory in home directory of the user #if defined(_WIN32) && !defined(__CYGWIN__) const char* homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE"); const char* homePath = itksys::SystemTools::GetEnv("HOMEPATH"); if((homeDrive!=NULL) || (homePath!=NULL)) { directoryPath = homeDrive; directoryPath += homePath; directoryPath += "/.mitk/"; AddDirectoryForSearch(directoryPath.c_str(), false); } #else const char* homeDirectory = itksys::SystemTools::GetEnv("HOME"); if(homeDirectory != nullptr) { directoryPath = homeDirectory; directoryPath += "/.mitk/"; AddDirectoryForSearch(directoryPath.c_str(), false); } #endif // defined(_WIN32) && !defined(__CYGWIN__) // 3. look in the current working directory directoryPath = ""; AddDirectoryForSearch(directoryPath.c_str()); directoryPath = itksys::SystemTools::GetCurrentWorkingDirectory(); AddDirectoryForSearch(directoryPath.c_str(), false); std::string directoryBinPath = directoryPath + "/bin"; AddDirectoryForSearch(directoryBinPath.c_str(), false); // 4. use a source tree location from compile time directoryPath = MITK_ROOT; if (pathInSourceDir) { directoryPath += pathInSourceDir; } directoryPath += '/'; AddDirectoryForSearch(directoryPath.c_str(), false); return SearchDirectoriesForFile(filename); } std::string mitk::StandardFileLocations::GetOptionDirectory() { const char* mitkoptions = itksys::SystemTools::GetEnv("MITKOPTIONS"); std::string optionsDirectory; if (mitkoptions!=nullptr) { // 1. look for MITKOPTIONS environment variable optionsDirectory = mitkoptions; optionsDirectory += "/"; } else { // 2. use .mitk-subdirectory in home directory of the user std::string homeDirectory; #if defined(_WIN32) && !defined(__CYGWIN__) const char* homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE"); const char* homePath = itksys::SystemTools::GetEnv("HOMEPATH"); if((homeDrive==NULL) || (homePath==NULL)) { itkGenericOutputMacro( << "Environment variables HOMEDRIVE and/or HOMEPATH not set" << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } else { homeDirectory = homeDrive; homeDirectory +=homePath; } if(itksys::SystemTools::FileExists(homeDirectory.c_str())==false) { itkGenericOutputMacro( << "Could not find home directory at " << homeDirectory << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } #else const char* home = itksys::SystemTools::GetEnv("HOME"); if(home==nullptr) { itkGenericOutputMacro( << "Environment variable HOME not set" << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } else homeDirectory = home; if(itksys::SystemTools::FileExists(homeDirectory.c_str())==false) { itkGenericOutputMacro( << "Could not find home directory at " << homeDirectory << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } #endif // defined(_WIN32) && !defined(__CYGWIN__) optionsDirectory = homeDirectory; optionsDirectory += "/.mitk"; } optionsDirectory = itksys::SystemTools::ConvertToOutputPath(optionsDirectory.c_str()); if(itksys::SystemTools::CountChar(optionsDirectory.c_str(),'"') > 0) { char * unquoted = itksys::SystemTools::RemoveChars(optionsDirectory.c_str(),"\""); optionsDirectory = unquoted; delete [] unquoted; } if(itksys::SystemTools::MakeDirectory(optionsDirectory.c_str())==false) { itkGenericExceptionMacro( << "Could not create .mitk directory at " << optionsDirectory ); } return optionsDirectory; } diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp index f994e33ccf..9660e93721 100644 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp @@ -1,944 +1,945 @@ /*=================================================================== 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 "mitkDisplayInteractor.h" #include "mitkBaseRenderer.h" #include "mitkInteractionPositionEvent.h" #include "mitkCameraController.h" #include "mitkPropertyList.h" #include #include #include // level window #include "mitkStandaloneDataStorage.h" #include "mitkNodePredicateDataType.h" #include "mitkLevelWindowProperty.h" #include "mitkLevelWindow.h" #include "vtkRenderWindowInteractor.h" #include "mitkLine.h" // Rotation #include #include #include "rotate_cursor.xpm" #include "mitkInteractionConst.h" // #include "mitkStatusBar.h" #include "mitkImage.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImagePixelReadAccessor.h" void mitk::DisplayInteractor::Notify(InteractionEvent* interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { this->HandleEvent(interactionEvent, NULL); } } void mitk::DisplayInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION( "check_position_event", CheckPositionEvent ); CONNECT_CONDITION( "check_can_rotate", CheckRotationPossible ); CONNECT_CONDITION( "check_can_swivel", CheckSwivelPossible ); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION ("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); } mitk::DisplayInteractor::DisplayInteractor() : m_IndexToSliceModifier(4) , m_AutoRepeat(false) , m_InvertScrollDirection( false ) , m_InvertZoomDirection( false ) , m_InvertMoveDirection( false ) , m_InvertLevelWindowDirection( false ) , m_AlwaysReact(false) , m_ZoomFactor(2) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); } mitk::DisplayInteractor::~DisplayInteractor() { } bool mitk::DisplayInteractor::CheckPositionEvent( const InteractionEvent* interactionEvent ) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) { return false; } return true; } bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO interesection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const InteractionPositionEvent* posEvent = dynamic_cast(interactionEvent); if (posEvent == nullptr) return false; BaseRenderer* clickedRenderer = posEvent->GetSender(); const PlaneGeometry* ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry()); if (!ourViewportGeometry) return false; Point3D cursorPosition = posEvent->GetPositionInWorld(); const PlaneGeometry* geometryToBeRotated = NULL; // this one is under the mouse cursor const PlaneGeometry* anyOtherGeometry = NULL; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const double threshholdDistancePixels = 12.0; auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for(auto renWin : renWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if ( BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry* otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == NULL) continue; // ignore, we don't see a plane // check if there is an intersection Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition); // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (geometryToBeRotated == NULL) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { return true; } else { return false; } } return false; } bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent) { const ScalarType ThresholdDistancePixels = 6.0; // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const InteractionPositionEvent *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if ( !posEvent || !renderer ) return false; const Point3D &cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry( NULL ); const PlaneGeometry *otherGeometry1( NULL ); const PlaneGeometry *otherGeometry2( NULL ); auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for(auto renWin : renWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if ( BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; //unsigned int slice = (*iter)->GetSlice()->GetPos(); //unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if ( !planeGeometry ) continue; if ( snc == renderer->GetSliceNavigationController() ) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if ( otherGeometry1 == NULL ) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if ( m_LinkPlanes ) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } mitk::Line3D line; mitk::Point3D point; if ( (clickedGeometry != NULL) && (otherGeometry1 != NULL) && (otherGeometry2 != NULL) && clickedGeometry->IntersectionLine( otherGeometry1, line ) && otherGeometry2->IntersectionPoint( line, point )) { m_CenterOfRotation = point; if ( m_CenterOfRotation.EuclideanDistanceTo( cursor ) < ThresholdDistancePixels ) { return false; } else { m_ReferenceCursor = posEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = { 1.0, 0.0, 0.0 }; ScalarType yVector[] = { 0.0, 1.0, 0.0 }; clickedGeometry->BaseGeometry::IndexToWorld( Vector3D( xVector), m_RotationPlaneXVector ); clickedGeometry->BaseGeometry::IndexToWorld( Vector3D( yVector), m_RotationPlaneYVector ); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill( 0.0 ); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayInteractor::Init(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate,m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayInteractor::Move(StateMachineAction*, InteractionEvent* interactionEvent) { BaseRenderer* sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = static_cast(interactionEvent); float invertModifier = -1.0; if ( m_InvertMoveDirection ) { invertModifier = 1.0; } // perform translation Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; moveVector*=sender->GetScaleFactorMMPerDisplayUnit(); sender->GetCameraController()->MoveBy(moveVector); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); InteractionPositionEvent* positionEvent = static_cast(interactionEvent); Point3D pos = positionEvent->GetPositionInWorld(); for(auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); } } void mitk::DisplayInteractor::Zoom(StateMachineAction*, InteractionEvent* interactionEvent) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = static_cast(interactionEvent); float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if ( m_InvertZoomDirection ) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } if (factor != 1.0) { sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); } m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::Scroll(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = static_cast(interactionEvent); mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "updown") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } if ( m_InvertScrollDirection ) { delta *= -1; } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } // set the new position sliceNaviController->GetSlice()->SetPos(newPos); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } } void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper* stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } } void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper* stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); } } void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction*, InteractionEvent* interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); InteractionPositionEvent* positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = NULL; mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); for (unsigned int i = 0; i < allImageNodes->size(); i++) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); int levelIndex = 0; int windowIndex = 1; if ( m_LevelDirection != "leftright" ) { levelIndex = 1; windowIndex = 0; } int directionModifier = 1; if ( m_InvertLevelWindowDirection ) { directionModifier = -1; } // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast(2) * directionModifier; window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) * static_cast(2) * directionModifier; lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); sender->GetRenderingManager()->RequestUpdateAll(); } void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->ResetMouseCursor(); } void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent * event) { const InteractionPositionEvent* posEvent = dynamic_cast(event); if (posEvent == nullptr) return; Point3D cursor = posEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = cursor - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed< ScalarType, 3 > vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); axisOfRotation.SetVnlVector(vnlDirection); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = cursor; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection for (SNCVector::iterator iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const InteractionPositionEvent *posEvent = dynamic_cast(event); if (!posEvent) return; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct( m_RotationPlaneNormal, relativeCursorAxis ); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op( OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle ); SNCVector::iterator iter; for ( iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter ) { if ( !(*iter)->GetSliceRotationLocked() ) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2( OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle ); for ( iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if ( !(*iter)->GetSliceRotationLocked() ) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const InteractionPositionEvent *posEvent = dynamic_cast(event); if (!posEvent) return; std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = posEvent->GetSender()->GetDataStorage()->GetSubset(isImageData).GetPointer(); mitk::Point3D worldposition = posEvent->GetPositionInWorld(); mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; - bool isBinary (false); int component = 0; node = this->GetTopLayerNode(nodes,worldposition,posEvent->GetSender()); if(node.IsNotNull()) { + bool isBinary (false); node->GetBoolProperty("binary", isBinary); if(isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = posEvent->GetSender()->GetDataStorage()->GetSources(node, NULL, true); if(!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes,worldposition,posEvent->GetSender()); } if(topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } std::stringstream stream; stream.imbue(std::locale::classic()); // get the position and gray value from the image and build up status bar text if(image3D.IsNotNull()) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); stream.precision(2); stream<<"Position: <" << std::fixed < mm"; stream<<"; Index: <"< "; mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5( mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(posEvent->GetSender()->GetTimeStep()), p, pixelValue, component); if (fabs(pixelValue)>1000000 || fabs(pixelValue) < 0.01) { stream<<"; Time: " << posEvent->GetSender()->GetTime() << " ms; Pixelvalue: " << std::scientific<< pixelValue <<" "; } else { stream<<"; Time: " << posEvent->GetSender()->GetTime() << " ms; Pixelvalue: "<< pixelValue <<" "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty( properties, "invertScrollDirection", false ); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty( properties, "invertZoomDirection", false ); m_InvertMoveDirection = GetBoolProperty( properties, "invertMoveDirection", false ); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty( properties, "invertLevelWindowDirection", false ); // coupled rotation std::string strCoupled= ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") m_LinkPlanes = true; else m_LinkPlanes = false; } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent* interactionEvent, DataNode* /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } bool mitk::DisplayInteractor::GetBoolProperty( mitk::PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue ) { std::string valueAsString; if ( !propertyList->GetStringProperty( propertyName, valueAsString ) ) { return defaultValue; } else { if ( valueAsString == "true" ) { return true; } else { return false; } } } mitk::DataNode::Pointer mitk::DisplayInteractor::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, mitk::Point3D worldposition, BaseRenderer *ren) { mitk::DataNode::Pointer node; - int maxlayer = -32768; - bool isHelper (false); + if(nodes.IsNotNull()) { + int maxlayer = -32768; + bool isHelper(false); for (unsigned int x = 0; x < nodes->size(); x++) { nodes->at(x)->GetBoolProperty("helper object", isHelper); if(nodes->at(x)->GetData()->GetGeometry()->IsInside(worldposition) && isHelper == false) { int layer = 0; if(!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if(layer > maxlayer) { if(static_cast(nodes->at(x))->IsVisible(ren)) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp index 4d6a88fa11..ee1b572b92 100644 --- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp @@ -1,797 +1,797 @@ /*=================================================================== 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 "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" // Controllers #include "mitkCameraController.h" #include "mitkSliceNavigationController.h" #include "mitkCameraRotationController.h" #include "mitkVtkLayerController.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" #include "mitkInteractionConst.h" #include "mitkOverlayManager.h" // VTK #include #include #include #include #include #include #include mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer* mitk::BaseRenderer::GetInstance(vtkRenderWindow * renWin) { - for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) + for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow* renWin, BaseRenderer* baseRenderer) { if (renWin == nullptr || baseRenderer == nullptr) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow* renWin) { BaseRendererMapType::iterator mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer* mitk::BaseRenderer::GetByName(const std::string& name) { - for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) + for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return nullptr; } vtkRenderWindow* mitk::BaseRenderer::GetRenderWindowByName(const std::string& name) { - for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); mapit++) + for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return nullptr; } mitk::BaseRenderer::BaseRenderer(const char* name, vtkRenderWindow * renWin, mitk::RenderingManager* rm, RenderingMode::Type renderingMode) : m_RenderWindow(nullptr) , m_VtkRenderer(nullptr) , m_MapperID(defaultMapper) , m_DataStorage(nullptr) , m_RenderingManager(rm) , m_LastUpdateTime(0) , m_CameraController(nullptr) , m_SliceNavigationController(nullptr) , m_CameraRotationController(nullptr) , m_WorldTimeGeometry(nullptr) , m_CurrentWorldGeometry(nullptr) , m_CurrentWorldPlaneGeometry(nullptr) , m_Slice(0) , m_TimeStep() , m_CurrentWorldPlaneGeometryUpdateTime() , m_TimeStepUpdateTime() , m_KeepDisplayedRegion(true) , m_CurrentWorldPlaneGeometryData(nullptr) , m_CurrentWorldPlaneGeometryNode(nullptr) , m_CurrentWorldPlaneGeometryTransformTime(0) , m_Name(name) , m_EmptyWorldGeometry(true) , m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != nullptr) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != nullptr) { m_RenderWindow = renWin; m_RenderWindow->Register(nullptr); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } //instances.insert( this ); //adding this BaseRenderer to the List of all BaseRenderer m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName()); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object*) this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this, false); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); m_CameraController = mitk::CameraController::New(); m_CameraController->SetRenderer(this); m_VtkRenderer = vtkRenderer::New(); if (renderingMode == RenderingMode::DepthPeeling) { m_VtkRenderer->SetUseDepthPeeling(1); m_VtkRenderer->SetMaximumNumberOfPeels(8); m_VtkRenderer->SetOcclusionRatio(0.0); } if (mitk::VtkLayerController::GetInstance(m_RenderWindow) == nullptr) { mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); } mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_OverlayManager.IsNotNull()) { m_OverlayManager->RemoveBaseRenderer(this); } if (m_VtkRenderer != nullptr) { m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(nullptr); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = nullptr; if (m_BindDispatcherInteractor != nullptr) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); m_RenderWindow = nullptr; } } void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent()); std::list::iterator it; - for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); it++) + for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage* storage) { if (storage != m_DataStorage && storage != nullptr ) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; void mitk::BaseRenderer::Paint() { } void mitk::BaseRenderer::Initialize() { } void mitk::BaseRenderer::Resize(int w, int h) { this->m_RenderWindow->SetSize(w,h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow* renderwindow) { if (m_RenderWindow != renderwindow) { if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != nullptr) { m_RenderWindow->Register(nullptr); } } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { this->m_RenderWindow->SetSize(w,h); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice != slice) { m_Slice = slice; if (m_WorldTimeGeometry.IsNotNull()) { // get world geometry which may be rotated, for the current time step SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { // if slice position is part of the world geometry... if (m_Slice >= slicedWorldGeometry->GetSlices()) // set the current worldplanegeomety as the selected 2D slice of the world geometry m_Slice = slicedWorldGeometry->GetSlices() - 1; SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } void mitk::BaseRenderer::SetOverlayManager(itk::SmartPointer overlayManager) { if (overlayManager.IsNull()) return; if (this->m_OverlayManager.IsNotNull()) { if (this->m_OverlayManager.GetPointer() == overlayManager.GetPointer()) { return; } else { this->m_OverlayManager->RemoveBaseRenderer(this); } } this->m_OverlayManager = overlayManager; this->m_OverlayManager->AddBaseRenderer(this); //TODO } itk::SmartPointer mitk::BaseRenderer::GetOverlayManager() { if (this->m_OverlayManager.IsNull()) { m_OverlayManager = mitk::OverlayManager::New(); m_OverlayManager->AddBaseRenderer(this); } return this->m_OverlayManager; } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); if (m_WorldTimeGeometry.IsNotNull()) { if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } int mitk::BaseRenderer::GetTimeStep(const mitk::BaseData* data) const { if ((data == nullptr) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == itk::NumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetWorldTimeGeometry(mitk::TimeGeometry* geometry) { assert(geometry != nullptr); itkDebugMacro("setting WorldTimeGeometry to " << geometry); if (m_WorldTimeGeometry != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0) return; m_WorldTimeGeometry = geometry; itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry); if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; BaseGeometry* geometry3d; geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); SetWorldGeometry3D(geometry3d); } } void mitk::BaseRenderer::SetWorldGeometry3D(mitk::BaseGeometry* geometry) { itkDebugMacro("setting WorldGeometry3D to " << geometry); if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) return; SlicedGeometry3D* slicedWorldGeometry; slicedWorldGeometry = dynamic_cast(geometry); PlaneGeometry::Pointer geometry2d; if (slicedWorldGeometry != nullptr) { if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) m_Slice = slicedWorldGeometry->GetSlices() - 1; geometry2d = slicedWorldGeometry->GetPlaneGeometry(m_Slice); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(slicedWorldGeometry); geometry2d = plane; } SetCurrentWorldGeometry(slicedWorldGeometry); } else { geometry2d = dynamic_cast(geometry); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->InitializeStandardPlane(geometry); geometry2d = plane; } SetCurrentWorldGeometry(geometry); } SetCurrentWorldPlaneGeometry(geometry2d); // calls Modified() if (m_CurrentWorldPlaneGeometry.IsNull()) itkWarningMacro("m_CurrentWorldPlaneGeometry is nullptr"); } void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(mitk::PlaneGeometry* geometry2d) { if (m_CurrentWorldPlaneGeometry != geometry2d) { m_CurrentWorldPlaneGeometry = geometry2d; m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SendUpdateSlice() { m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } int *mitk::BaseRenderer::GetSize() const { return this->m_RenderWindow->GetSize(); } int *mitk::BaseRenderer::GetViewportSize() const { return this->m_VtkRenderer->GetSize(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(mitk::BaseGeometry* geometry) { m_CurrentWorldGeometry = geometry; if (geometry == nullptr) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); const BoundingBox::BoundsArrayType& worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) m_EmptyWorldGeometry = true; else m_EmptyWorldGeometry = false; } void mitk::BaseRenderer::UpdateOverlays() { if (m_OverlayManager.IsNotNull()) { m_OverlayManager->UpdateOverlays(this); } } void mitk::BaseRenderer::SetGeometry(const itk::EventObject & geometrySendEvent) { const SliceNavigationController::GeometrySendEvent* sendEvent = dynamic_cast(&geometrySendEvent); assert(sendEvent != nullptr); SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject & geometryUpdateEvent) { const SliceNavigationController::GeometryUpdateEvent* updateEvent = dynamic_cast(&geometryUpdateEvent); if (updateEvent == nullptr) return; if (m_CurrentWorldGeometry.IsNotNull()) { SlicedGeometry3D* slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { PlaneGeometry* geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() } } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent != nullptr); SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject & geometryTimeEvent) { const SliceNavigationController::GeometryTimeEvent * timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent != nullptr); SetTimeStep(timeEvent->GetPos()); } const double* mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D& itkNotUsed(p2d)) { MITK_INFO << "BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer." << std::endl; } void mitk::BaseRenderer::RequestUpdate() { SetConstrainZoomingAndPanning(true); m_RenderingManager->RequestUpdate(this->m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(this->m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } mitk::RenderingManager* mitk::BaseRenderer::GetRenderingManager() const { return m_RenderingManager.GetPointer(); } /*! Sets the new Navigation controller */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == nullptr) return; //copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); //set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); } } void mitk::BaseRenderer::DisplayToWorld(const Point2D& displayPoint, Point3D& worldIndex) const { if (m_MapperID == BaseRenderer::Standard2D) { double display[3], *world; //For the rigth z-position in display coordinates, take the focal point, convert it to display and use it for correct depth. double *displayCoord; double cameraFP[4]; // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP); cameraFP[3] = 0.0; this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]); this->GetVtkRenderer()->WorldToDisplay(); displayCoord = this->GetVtkRenderer()->GetDisplayPoint(); // now convert the display point to world coordinates display[0] = displayPoint[0]; display[1] = displayPoint[1]; display[2] = displayCoord[2]; this->GetVtkRenderer()->SetDisplayPoint(display); this->GetVtkRenderer()->DisplayToWorld(); world = this->GetVtkRenderer()->GetWorldPoint(); for (int i = 0; i < 3; i++) { worldIndex[i] = world[i] / world[3]; } } else if (m_MapperID == BaseRenderer::Standard3D) { PickWorldPoint(displayPoint, worldIndex); //Seems to be the same code as above, but subclasses may contain different implementations. } return; } void mitk::BaseRenderer::DisplayToPlane(const Point2D& displayPoint, Point2D& planePointInMM) const { if (m_MapperID == BaseRenderer::Standard2D) { Point3D worldPoint; this->DisplayToWorld(displayPoint, worldPoint); this->m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); } else if (m_MapperID == BaseRenderer::Standard3D) { MITK_WARN << "No conversion possible with 3D mapper."; return; } return; } void mitk::BaseRenderer::WorldToDisplay(const Point3D& worldIndex, Point2D& displayPoint) const { double world[4], *display; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToDisplay(); display = this->GetVtkRenderer()->GetDisplayPoint(); displayPoint[0] = display[0]; displayPoint[1] = display[1]; return; } void mitk::BaseRenderer::PlaneToDisplay(const Point2D& planePointInMM, Point2D& displayPoint) const { Point3D worldPoint; this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToDisplay(worldPoint, displayPoint); return; } double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const { if (this->GetMapperID() == BaseRenderer::Standard2D) { //GetParallelScale returns half of the height of the render window in mm. //Divided by the half size of the Display size in pixel givest the mm per pixel. return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1]; } else return 1.0; } mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const { Point2D originPx; originPx[0] = m_VtkRenderer->GetOrigin()[0]; originPx[1] = m_VtkRenderer->GetOrigin()[1]; Point2D displayGeometryOriginInMM; DisplayToPlane(originPx, displayGeometryOriginInMM);//top left of the render window (Origin) return displayGeometryOriginInMM; } void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->GetCameraController()->AdjustCameraToPlane(); } } mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(const Point2D& mousePosition) const { //DEPRECATED: Map2DRendererPositionTo3DWorldPosition is deprecated. use DisplayToWorldInstead Point3D position; DisplayToWorld(mousePosition, position); return position; } void mitk::BaseRenderer::PrintSelf(std::ostream& os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "nullptr" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/Rendering/mitkOverlayManager.cpp b/Modules/Core/src/Rendering/mitkOverlayManager.cpp index f6c11ec4c9..9cf862b67f 100644 --- a/Modules/Core/src/Rendering/mitkOverlayManager.cpp +++ b/Modules/Core/src/Rendering/mitkOverlayManager.cpp @@ -1,235 +1,234 @@ /*=================================================================== 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 "mitkOverlayManager.h" #include "mitkBaseRenderer.h" #include namespace mitk { itkEventMacroDefinition(OverlayAddEvent, itk::AnyEvent) OverlayManager::OverlayManager() { } OverlayManager::~OverlayManager() { RemoveAllOverlays(); RemoveAllBaseRenderers(); } void OverlayManager::AddBaseRenderer(BaseRenderer* renderer) { if(!m_ForegroundRenderer[renderer]) { m_ForegroundRenderer[renderer] = vtkSmartPointer::New(); vtkRenderer* rendererVtk = m_ForegroundRenderer[renderer]; rendererVtk->SetActiveCamera(renderer->GetVtkRenderer()->GetActiveCamera()); VtkLayerController::GetInstance(renderer->GetRenderWindow())->InsertForegroundRenderer(rendererVtk,false); rendererVtk->SetInteractive(false); } std::pair inSet; inSet = m_BaseRendererSet.insert(renderer); if(inSet.second) { OverlaySet::const_iterator it; - for ( it=m_OverlaySet.cbegin() ; it != m_OverlaySet.cend(); it++ ) + for ( it=m_OverlaySet.cbegin() ; it != m_OverlaySet.cend(); ++it ) { if((*it)->IsForceInForeground()) (*it)->AddToRenderer(renderer,m_ForegroundRenderer[renderer]); else (*it)->AddToBaseRenderer(renderer); } } } void OverlayManager::RemoveBaseRenderer(BaseRenderer* renderer) { if(!renderer) return; vtkRenderer* forgroundRenderer = m_ForegroundRenderer[renderer]; OverlaySet::const_iterator it; for ( it=m_OverlaySet.cbegin() ; it != m_OverlaySet.cend(); ++it ) { (*it)->RemoveFromBaseRenderer(renderer); if(forgroundRenderer) (*it)->RemoveFromRenderer(renderer,forgroundRenderer); } BaseRendererSet::const_iterator i = m_BaseRendererSet.find(renderer); if( i == m_BaseRendererSet.cend() ) return; m_BaseRendererSet.erase(i); m_ForegroundRenderer[renderer] = NULL; } void OverlayManager::RemoveAllBaseRenderers() { BaseRendererSet::const_iterator it; for ( it=m_BaseRendererSet.cbegin() ; it != m_BaseRendererSet.cend(); ++it) { this->RemoveBaseRenderer(*it); } } const OverlayManager::OverlaySet &OverlayManager::GetAllOverlays() { return m_OverlaySet; } OverlayManager* OverlayManager::GetInstance() { auto renderwindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); - BaseRenderer* renderer = nullptr; for(auto renderwindow : renderwindows) { - renderer = BaseRenderer::GetInstance(renderwindow); + BaseRenderer* renderer = BaseRenderer::GetInstance(renderwindow); if(renderer && renderer->GetOverlayManager().IsNotNull()) return renderer->GetOverlayManager(); } return nullptr; } void OverlayManager::AddOverlay(const Overlay::Pointer& overlay, bool ForceInForeground) { std::pair inSet; inSet = m_OverlaySet.insert(overlay); if(inSet.second) { BaseRendererSet::const_iterator it; - for ( it=m_BaseRendererSet.cbegin() ; it != m_BaseRendererSet.cend(); it++ ) + for ( it=m_BaseRendererSet.cbegin() ; it != m_BaseRendererSet.cend(); ++it ) { if(ForceInForeground) { vtkRenderer* forgroundRenderer = m_ForegroundRenderer[*it]; overlay->AddToRenderer(*it,forgroundRenderer); } else overlay->AddToBaseRenderer(*it); this->InvokeEvent(OverlayAddEvent()); } } } void OverlayManager::AddOverlay(const Overlay::Pointer& overlay, BaseRenderer* renderer, bool ForceInForeground) { std::pair inSet; inSet = m_OverlaySet.insert(overlay); if(inSet.second) { if(ForceInForeground) { vtkRenderer* forgroundRenderer = m_ForegroundRenderer[renderer]; overlay->AddToRenderer(renderer,forgroundRenderer); } else overlay->AddToBaseRenderer(renderer); this->InvokeEvent(OverlayAddEvent()); } } void OverlayManager::RemoveOverlay(const Overlay::Pointer &overlay) { OverlaySet::const_iterator overlayIt = m_OverlaySet.find(overlay); if( overlayIt == m_OverlaySet.cend() ) return; BaseRendererSet::const_iterator it; - for ( it=m_BaseRendererSet.cbegin() ; it != m_BaseRendererSet.cend(); it++) + for ( it=m_BaseRendererSet.cbegin() ; it != m_BaseRendererSet.cend(); ++it) { overlay->RemoveFromBaseRenderer(*it); vtkRenderer* forgroundRenderer = m_ForegroundRenderer[*it]; if(forgroundRenderer) overlay->RemoveFromRenderer(*it,forgroundRenderer); } m_OverlaySet.erase(overlayIt); this->InvokeEvent(OverlayAddEvent()); } void OverlayManager::RemoveAllOverlays() { while(!m_OverlaySet.empty()) RemoveOverlay(*m_OverlaySet.cbegin()); } void OverlayManager::UpdateOverlays(BaseRenderer* baseRenderer) { OverlaySet::const_iterator it; - for ( it=m_OverlaySet.cbegin() ; it != m_OverlaySet.cend(); it++ ) + for ( it=m_OverlaySet.cbegin() ; it != m_OverlaySet.cend(); ++it ) { (*it)->Update(baseRenderer); } UpdateLayouts(baseRenderer); } void OverlayManager::SetLayouter(Overlay *overlay, const std::string &identifier, BaseRenderer *renderer) { if(renderer) { AbstractOverlayLayouter::Pointer layouter = GetLayouter(renderer,identifier); if(layouter.IsNull()) { MITK_WARN << "Layouter " << identifier << " cannot be found or created!"; return; } else { layouter->AddOverlay(overlay); } } } void OverlayManager::UpdateLayouts(BaseRenderer *renderer) { const LayouterMap layouters = m_LayouterMap[renderer]; LayouterMap::const_iterator it; - for ( it=layouters.cbegin() ; it != layouters.cend(); it++ ) + for ( it=layouters.cbegin() ; it != layouters.cend(); ++it ) { (it->second)->PrepareLayout(); } } AbstractOverlayLayouter::Pointer OverlayManager::GetLayouter(BaseRenderer *renderer, const std::string& identifier) { AbstractOverlayLayouter::Pointer layouter = m_LayouterMap[renderer][identifier]; return layouter; } void OverlayManager::AddLayouter(const AbstractOverlayLayouter::Pointer& layouter) { if(layouter.IsNotNull()) { AbstractOverlayLayouter::Pointer oldLayouter = m_LayouterMap[layouter->GetBaseRenderer()][layouter->GetIdentifier()]; if(oldLayouter.IsNotNull() && oldLayouter.GetPointer() != layouter.GetPointer()) { MITK_WARN << "Layouter " << layouter->GetIdentifier() << " does already exist!"; } else if(oldLayouter.IsNull()) { m_LayouterMap[layouter->GetBaseRenderer()][layouter->GetIdentifier()] = layouter; } } } } diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp index c13ca2ef58..d2aa24a225 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp @@ -1,707 +1,705 @@ /*=================================================================== 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 "mitkPlaneGeometryDataMapper2D.h" //mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include //vtk includes #include #include #include #include #include #include #include #include #include #include /// #include #include #include #include namespace { /// Some simple interval arithmetic template class SimpleInterval { public: SimpleInterval(T start = T(), T end = T()) : m_LowerBoundary(std::min(start, end)) , m_UpperBoundary(std::max(start, end)) { } T GetLowerBoundary() const { return m_LowerBoundary; } T GetUpperBoundary() const { return m_UpperBoundary; } bool empty() const { return m_LowerBoundary == m_UpperBoundary; } bool operator<(const SimpleInterval& otherInterval) const { return this->m_UpperBoundary < otherInterval.GetLowerBoundary(); } private: T m_LowerBoundary; T m_UpperBoundary; }; template class IntervalSet { public: typedef SimpleInterval IntervalType; IntervalSet(IntervalType startingInterval) { m_IntervalsContainer.insert(std::move(startingInterval)); } void operator-=(const IntervalType& interval) { // equal_range will find all the intervals in the interval set which intersect with the input interval // due to the nature of operator< of SimpleInterval auto range = m_IntervalsContainer.equal_range(interval); for (auto iter = range.first; iter != range.second;) { auto subtractionResult = SubtractIntervals(*iter, interval); // Remove the old interval from the set iter = m_IntervalsContainer.erase(iter); for (auto&& interval : subtractionResult) { if (!interval.empty()) { // Add the new interval to the set // emplace_hint adds the element at the closest valid place before the hint iterator, // which is exactly where the new interval should be iter = m_IntervalsContainer.insert(iter, std::move(interval)); ++iter; } } } } IntervalSet operator-(const IntervalType& interval) { IntervalSet result = *this; result -= interval; return result; } typedef std::set IntervalsContainer; const IntervalsContainer& getIntervals() const { return m_IntervalsContainer; } private: IntervalsContainer m_IntervalsContainer; std::array SubtractIntervals(const IntervalType& firstInterval, const IntervalType& secondInterval) { assert(secondInterval.GetUpperBoundary() >= firstInterval.GetLowerBoundary() && firstInterval.GetUpperBoundary() >= secondInterval.GetLowerBoundary()); // Non-intersecting intervals should never reach here if (secondInterval.GetLowerBoundary() < firstInterval.GetLowerBoundary()) { if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = { { IntervalType(), IntervalType() } }; return result; // firstInterval completely enclosed } std::array result = { { IntervalType(firstInterval.GetUpperBoundary(), secondInterval.GetUpperBoundary()), IntervalType() } }; return result; // secondInterval removes the beginning of firstInterval } if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = { { IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType() } }; return result; // secondInterval removes the end of firstInterval } std::array result = { { IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType(secondInterval.GetUpperBoundary(), firstInterval.GetUpperBoundary()) } }; return result; // secondInterval is completely enclosed in firstInterval and removes the middle } }; } mitk::PlaneGeometryDataMapper2D::AllInstancesContainer mitk::PlaneGeometryDataMapper2D::s_AllInstances; // input for this mapper ( = PlaneGeometryData) const mitk::PlaneGeometryData* mitk::PlaneGeometryDataMapper2D::GetInput() const { return static_cast< PlaneGeometryData * >(GetDataNode()->GetData()); } mitk::PlaneGeometryDataMapper2D::PlaneGeometryDataMapper2D() : m_RenderOrientationArrows( false ), m_ArrowOrientationPositive( true ), m_DepthValue(1.0f) { s_AllInstances.insert(this); } mitk::PlaneGeometryDataMapper2D::~PlaneGeometryDataMapper2D() { s_AllInstances.erase(this); } vtkProp* mitk::PlaneGeometryDataMapper2D::GetVtkProp(mitk::BaseRenderer * renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_CrosshairAssembly; } void mitk::PlaneGeometryDataMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); // The PlaneGeometryDataMapper2D mapper is special in that the rendering of // OTHER PlaneGeometryDatas affects how we render THIS PlaneGeometryData // (for the gap at the point where they intersect). A change in any of the // other PlaneGeometryData nodes could mean that we render ourself // differently, so we check for that here. - bool generateDataRequired = false; for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { - generateDataRequired = ls->IsGenerateDataRequired(renderer, this, (*it)->GetDataNode()); + bool generateDataRequired = ls->IsGenerateDataRequired(renderer, this, (*it)->GetDataNode()); if (generateDataRequired) break; } ls->UpdateGenerateDataTime(); // Collect all other PlaneGeometryDatas that are being mapped by this mapper m_OtherPlaneGeometries.clear(); for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { Self *otherInstance = *it; // Skip ourself if (otherInstance == this) continue; mitk::DataNode *otherNode = otherInstance->GetDataNode(); if (!otherNode) continue; // Skip other PlaneGeometryData nodes that are not visible on this renderer if (!otherNode->IsVisible(renderer)) continue; PlaneGeometryData* otherData = dynamic_cast(otherNode->GetData()); if (!otherData) continue; PlaneGeometry* otherGeometry = dynamic_cast(otherData->GetPlaneGeometry()); if ( otherGeometry && !dynamic_cast(otherData->GetPlaneGeometry()) ) { m_OtherPlaneGeometries.push_back(otherNode); } } CreateVtkCrosshair(renderer); ApplyAllProperties(renderer); } void mitk::PlaneGeometryDataMapper2D::CreateVtkCrosshair(mitk::BaseRenderer *renderer) { bool visible = true; LocalStorage* ls = m_LSH.GetLocalStorage(renderer); ls->m_CrosshairActor->SetVisibility(0); ls->m_ArrowActor->SetVisibility(0); ls->m_CrosshairHelperLineActor->SetVisibility(0); GetDataNode()->GetVisibility(visible, renderer, "visible"); if(!visible) { return; } PlaneGeometryData::Pointer input = const_cast< PlaneGeometryData * >(this->GetInput()); mitk::DataNode* geometryDataNode = renderer->GetCurrentWorldPlaneGeometryNode(); const PlaneGeometryData* rendererWorldPlaneGeometryData = dynamic_cast< PlaneGeometryData * >(geometryDataNode->GetData()); // intersecting with ourself? if ( input.IsNull() || input.GetPointer() == rendererWorldPlaneGeometryData) { return; //nothing to do in this case } const PlaneGeometry *inputPlaneGeometry = dynamic_cast< const PlaneGeometry * >( input->GetPlaneGeometry() ); const PlaneGeometry* worldPlaneGeometry = dynamic_cast< const PlaneGeometry* >( rendererWorldPlaneGeometryData->GetPlaneGeometry() ); if ( worldPlaneGeometry && dynamic_cast(worldPlaneGeometry)==NULL && inputPlaneGeometry && dynamic_cast(input->GetPlaneGeometry() )==NULL) { const BaseGeometry *referenceGeometry = inputPlaneGeometry->GetReferenceGeometry(); // calculate intersection of the plane data with the border of the // world geometry rectangle Point3D point1, point2; Line3D crossLine; // Calculate the intersection line of the input plane with the world plane if ( worldPlaneGeometry->IntersectionLine( inputPlaneGeometry, crossLine ) ) { bool hasIntersection = referenceGeometry ? CutCrossLineWithReferenceGeometry(referenceGeometry, crossLine) : CutCrossLineWithPlaneGeometry(inputPlaneGeometry, crossLine); if (!hasIntersection) { return; } point1 = crossLine.GetPoint1(); point2 = crossLine.GetPoint2(); vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer linesPolyData = vtkSmartPointer::New(); // Now iterate through all other lines displayed in this window and // calculate the positions of intersection with the line to be // rendered; these positions will be stored in lineParams to form a // gap afterwards. NodesVectorType::iterator otherPlanesIt = m_OtherPlaneGeometries.begin(); NodesVectorType::iterator otherPlanesEnd = m_OtherPlaneGeometries.end(); - otherPlanesIt = m_OtherPlaneGeometries.begin(); int gapSize = 32; this->GetDataNode()->GetPropertyValue("Crosshair.Gap Size", gapSize, NULL); auto intervals = IntervalSet( SimpleInterval(0, 1)); ScalarType lineLength = point1.EuclideanDistanceTo(point2); ScalarType gapInMM = gapSize * renderer->GetScaleFactorMMPerDisplayUnit(); float gapSizeParam = gapInMM / lineLength; if( gapSize != 0 ) { while ( otherPlanesIt != otherPlanesEnd ) { bool ignorePlane = false; (*otherPlanesIt)->GetPropertyValue("Crosshair.Ignore", ignorePlane); if (ignorePlane) { ++otherPlanesIt; continue; } PlaneGeometry *otherPlaneGeometry = static_cast< PlaneGeometry * >( static_cast< PlaneGeometryData * >((*otherPlanesIt)->GetData() )->GetPlaneGeometry() ); if (otherPlaneGeometry != inputPlaneGeometry && otherPlaneGeometry != worldPlaneGeometry) { double intersectionParam; if (otherPlaneGeometry->IntersectionPointParam(crossLine, intersectionParam) && intersectionParam > 0 && intersectionParam < 1) { Point3D point = crossLine.GetPoint() + intersectionParam * crossLine.GetDirection(); bool intersectionPointInsideOtherPlane = otherPlaneGeometry->HasReferenceGeometry() ? TestPointInReferenceGeometry(otherPlaneGeometry->GetReferenceGeometry(), point) : TestPointInPlaneGeometry(otherPlaneGeometry, point); if (intersectionPointInsideOtherPlane) { intervals -= SimpleInterval(intersectionParam - gapSizeParam, intersectionParam + gapSizeParam); } } } ++otherPlanesIt; } } for (const auto& interval : intervals.getIntervals()) { this->DrawLine(crossLine.GetPoint(interval.GetLowerBoundary()), crossLine.GetPoint(interval.GetUpperBoundary()), lines, points); } // Add the points to the dataset linesPolyData->SetPoints(points); // Add the lines to the dataset linesPolyData->SetLines(lines); Vector3D orthogonalVector; orthogonalVector = inputPlaneGeometry->GetNormal(); worldPlaneGeometry->Project(orthogonalVector,orthogonalVector); orthogonalVector.Normalize(); // Visualize ls->m_Mapper->SetInputData(linesPolyData); ls->m_CrosshairActor->SetMapper(ls->m_Mapper); // Determine if we should draw the area covered by the thick slicing, default is false. // This will also show the area of slices that do not have thick slice mode enabled bool showAreaOfThickSlicing = false; GetDataNode()->GetBoolProperty( "reslice.thickslices.showarea", showAreaOfThickSlicing ); // determine the pixelSpacing in that direction double thickSliceDistance = SlicedGeometry3D::CalculateSpacing( referenceGeometry ? referenceGeometry->GetSpacing() : inputPlaneGeometry->GetSpacing(), orthogonalVector); IntProperty *intProperty=0; if( GetDataNode()->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) thickSliceDistance *= intProperty->GetValue()+0.5; else showAreaOfThickSlicing = false; // not the nicest place to do it, but we have the width of the visible bloc in MM here // so we store it in this fancy property GetDataNode()->SetFloatProperty( "reslice.thickslices.sizeinmm", thickSliceDistance*2 ); ls->m_CrosshairActor->SetVisibility(1); vtkSmartPointer arrowPolyData = vtkSmartPointer::New(); ls->m_Arrowmapper->SetInputData(arrowPolyData); if(this->m_RenderOrientationArrows) { ScalarType triangleSizeMM = 7.0 * renderer->GetScaleFactorMMPerDisplayUnit(); vtkSmartPointer triangles = vtkSmartPointer::New(); vtkSmartPointer triPoints = vtkSmartPointer::New(); DrawOrientationArrow(triangles,triPoints,triangleSizeMM,orthogonalVector,point1,point2); DrawOrientationArrow(triangles,triPoints,triangleSizeMM,orthogonalVector,point2,point1); arrowPolyData->SetPoints(triPoints); arrowPolyData->SetPolys(triangles); ls->m_ArrowActor->SetVisibility(1); } // Visualize vtkSmartPointer helperlinesPolyData = vtkSmartPointer::New(); ls->m_HelperLinesmapper->SetInputData(helperlinesPolyData); if ( showAreaOfThickSlicing ) { vtkSmartPointer helperlines = vtkSmartPointer::New(); // vectorToHelperLine defines how to reach the helperLine from the mainLine // got the right direction, so we multiply the width Vector3D vecToHelperLine = orthogonalVector * thickSliceDistance; this->DrawLine(point1 - vecToHelperLine, point2 - vecToHelperLine,helperlines,points); this->DrawLine(point1 + vecToHelperLine, point2 + vecToHelperLine,helperlines,points); // Add the points to the dataset helperlinesPolyData->SetPoints(points); // Add the lines to the dataset helperlinesPolyData->SetLines(helperlines); ls->m_CrosshairActor->GetProperty()->SetLineStipplePattern(0xf0f0); ls->m_CrosshairActor->GetProperty()->SetLineStippleRepeatFactor(1); ls->m_CrosshairHelperLineActor->SetVisibility(1); } } } } bool mitk::PlaneGeometryDataMapper2D::TestPointInPlaneGeometry(const PlaneGeometry *planeGeometry, const Point3D &point) { Point2D mappedPoint; planeGeometry->Map(point, mappedPoint); planeGeometry->WorldToIndex(mappedPoint, mappedPoint); return (planeGeometry->GetBounds()[0] < mappedPoint[0] && mappedPoint[0] < planeGeometry->GetBounds()[1] && planeGeometry->GetBounds()[2] < mappedPoint[1] && mappedPoint[1] < planeGeometry->GetBounds()[3]); } bool mitk::PlaneGeometryDataMapper2D::TestPointInReferenceGeometry(const BaseGeometry* referenceGeometry, const Point3D& point) { return referenceGeometry->IsInside(point); } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithPlaneGeometry(const PlaneGeometry* planeGeometry, Line3D& crossLine) { Point2D indexLinePoint; Vector2D indexLineDirection; planeGeometry->Map(crossLine.GetPoint(), indexLinePoint); planeGeometry->Map(crossLine.GetPoint(), crossLine.GetDirection(), indexLineDirection); planeGeometry->WorldToIndex(indexLinePoint, indexLinePoint); planeGeometry->WorldToIndex(indexLineDirection, indexLineDirection); mitk::Point2D intersectionPoints[2]; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::RectangleLineIntersection(planeGeometry->GetBounds()[0], planeGeometry->GetBounds()[2], planeGeometry->GetBounds()[1], planeGeometry->GetBounds()[3], indexLinePoint, indexLineDirection, intersectionPoints[0], intersectionPoints[1]); if (nIntersections < 2) { return false; } planeGeometry->IndexToWorld(intersectionPoints[0], intersectionPoints[0]); planeGeometry->IndexToWorld(intersectionPoints[1], intersectionPoints[1]); Point3D point1, point2; planeGeometry->Map(intersectionPoints[0], point1); planeGeometry->Map(intersectionPoints[1], point2); crossLine.SetPoints(point1, point2); return true; } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithReferenceGeometry(const BaseGeometry* referenceGeometry, Line3D& crossLine) { Point3D boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetCornerPoint(0); boundingBoxMax = referenceGeometry->GetCornerPoint(7); Point3D indexLinePoint; Vector3D indexLineDirection; referenceGeometry->WorldToIndex(crossLine.GetPoint(), indexLinePoint); referenceGeometry->WorldToIndex(crossLine.GetDirection(), indexLineDirection); referenceGeometry->WorldToIndex(boundingBoxMin, boundingBoxMin); referenceGeometry->WorldToIndex(boundingBoxMax, boundingBoxMax); Point3D point1, point2; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::BoxLineIntersection(boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2], boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2], indexLinePoint, indexLineDirection, point1, point2); if (nIntersections < 2) { return false; } referenceGeometry->IndexToWorld(point1, point1); referenceGeometry->IndexToWorld(point2, point2); crossLine.SetPoints(point1, point2); return true; } void mitk::PlaneGeometryDataMapper2D::DrawLine(mitk::Point3D p0, mitk::Point3D p1, vtkCellArray* lines, vtkPoints* points ) { vtkIdType pidStart = points->InsertNextPoint(p0[0],p0[1], p0[2]); vtkIdType pidEnd = points->InsertNextPoint(p1[0],p1[1], p1[2]); vtkSmartPointer lineVtk = vtkSmartPointer::New(); lineVtk->GetPointIds()->SetId(0,pidStart); lineVtk->GetPointIds()->SetId(1,pidEnd); lines->InsertNextCell(lineVtk); } void mitk::PlaneGeometryDataMapper2D::DrawOrientationArrow(vtkSmartPointer triangles, vtkSmartPointer triPoints, double triangleSizeMM, Vector3D& orthogonalVector, Point3D& point1, Point3D& point2) { // Draw arrows to indicate plane orientation // Vector along line Vector3D v1 = point2 - point1; v1.Normalize(); v1 *= triangleSizeMM; // Orthogonal vector Vector3D v2 = orthogonalVector; v2 *= triangleSizeMM; if(!this->m_ArrowOrientationPositive) v2*=-1.0; // Initialize remaining triangle coordinates accordingly Point3D p1 = point1 + v1 * 2.0; Point3D p2 = point1 + v1 + v2; vtkIdType t0 = triPoints->InsertNextPoint(point1[0],point1[1], point1[2]); // start of the line vtkIdType t1 = triPoints->InsertNextPoint(p1[0],p1[1], p1[2]); // point on line vtkIdType t2 = triPoints->InsertNextPoint(p2[0],p2[1], p2[2]); // direction point vtkSmartPointer triangle = vtkSmartPointer::New(); triangle->GetPointIds()->SetId ( 0, t0 ); triangle->GetPointIds()->SetId ( 1, t1 ); triangle->GetPointIds()->SetId ( 2, t2 ); triangles->InsertNextCell(triangle); } int mitk::PlaneGeometryDataMapper2D::DetermineThickSliceMode( DataNode * dn, int &thickSlicesNum ) { int thickSlicesMode = 0; // determine the state and the extend of the thick-slice mode mitk::ResliceMethodProperty *resliceMethodEnumProperty=0; if( dn->GetProperty( resliceMethodEnumProperty, "reslice.thickslices" ) && resliceMethodEnumProperty ) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty=0; if( dn->GetProperty( intProperty, "reslice.thickslices.num" ) && intProperty ) { thickSlicesNum = intProperty->GetValue(); if(thickSlicesNum < 1) thickSlicesNum=0; if(thickSlicesNum > 10) thickSlicesNum=10; } if ( thickSlicesMode == 0 ) thickSlicesNum = 0; return thickSlicesMode; } void mitk::PlaneGeometryDataMapper2D::ApplyAllProperties( BaseRenderer *renderer ) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairHelperLineActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_ArrowActor); float thickness; this->GetDataNode()->GetFloatProperty("Line width",thickness,renderer); ls->m_CrosshairActor->GetProperty()->SetLineWidth(thickness); ls->m_CrosshairHelperLineActor->GetProperty()->SetLineWidth(thickness); PlaneOrientationProperty* decorationProperty; this->GetDataNode()->GetProperty( decorationProperty, "decoration", renderer ); if ( decorationProperty != NULL ) { if ( decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_POSITIVE_ORIENTATION ) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = true; } else if ( decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_NEGATIVE_ORIENTATION ) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = false; } else { m_RenderOrientationArrows = false; } } } void mitk::PlaneGeometryDataMapper2D::ApplyColorAndOpacityProperties2D(BaseRenderer* renderer, vtkActor2D* actor) { float rgba[4]={1.0f,1.0f,1.0f,1.0f}; DataNode * node = GetDataNode(); // check for color prop and use it for rendering if it exists node->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists node->GetOpacity(rgba[3], renderer, "opacity"); double drgba[4]={rgba[0],rgba[1],rgba[2],rgba[3]}; actor->GetProperty()->SetColor(drgba); actor->GetProperty()->SetOpacity(drgba[3]); } void mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::IPropertyAliases* aliases = mitk::CoreServices::GetPropertyAliases(); node->AddProperty( "Line width", mitk::FloatProperty::New(1), renderer, overwrite ); aliases->AddAlias( "line width", "Crosshair.Line Width", ""); node->AddProperty( "Crosshair.Gap Size", mitk::IntProperty::New(32), renderer, overwrite ); node->AddProperty( "decoration", mitk::PlaneOrientationProperty ::New(PlaneOrientationProperty::PLANE_DECORATION_NONE), renderer, overwrite ); aliases->AddAlias( "decoration", "Crosshair.Orientation Decoration", ""); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::PlaneGeometryDataMapper2D::UpdateVtkTransform(mitk::BaseRenderer* /*renderer*/) { } mitk::PlaneGeometryDataMapper2D::LocalStorage::LocalStorage() { m_CrosshairAssembly = vtkSmartPointer ::New(); m_CrosshairActor = vtkSmartPointer ::New(); m_ArrowActor = vtkSmartPointer ::New(); m_CrosshairHelperLineActor = vtkSmartPointer ::New(); m_HelperLinesmapper = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Arrowmapper = vtkSmartPointer::New(); m_CrosshairActor->SetMapper(m_Mapper); m_ArrowActor->SetMapper(m_Arrowmapper); m_CrosshairHelperLineActor->SetMapper(m_HelperLinesmapper); m_CrosshairActor->SetVisibility(0); m_ArrowActor->SetVisibility(0); m_CrosshairHelperLineActor->SetVisibility(0); m_CrosshairAssembly->AddPart(m_CrosshairActor); m_CrosshairAssembly->AddPart(m_ArrowActor); m_CrosshairAssembly->AddPart(m_CrosshairHelperLineActor); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); m_HelperLinesmapper->SetTransformCoordinate(tcoord); m_Mapper->SetTransformCoordinate(tcoord); // tcoord->SetCoordinateSystemToNormalizedDisplay(); m_Arrowmapper->SetTransformCoordinate(tcoord); tcoord->Delete(); } mitk::PlaneGeometryDataMapper2D::LocalStorage::~LocalStorage() { } diff --git a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp index ef8fc572ea..d1a8ec7792 100644 --- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper2D.cpp @@ -1,728 +1,728 @@ /*=================================================================== 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 "mitkPointSetVtkMapper2D.h" //mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include //vtk includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // constructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::LocalStorage() { // points m_UnselectedPoints = vtkSmartPointer::New(); m_SelectedPoints = vtkSmartPointer::New(); m_ContourPoints = vtkSmartPointer::New(); // scales m_UnselectedScales = vtkSmartPointer::New(); m_SelectedScales = vtkSmartPointer::New(); // distances m_DistancesBetweenPoints = vtkSmartPointer::New(); // lines m_ContourLines = vtkSmartPointer::New(); // glyph source (provides the different shapes) m_UnselectedGlyphSource2D = vtkSmartPointer::New(); m_SelectedGlyphSource2D = vtkSmartPointer::New(); // glyphs m_UnselectedGlyph3D = vtkSmartPointer::New(); m_SelectedGlyph3D = vtkSmartPointer::New(); // polydata m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); m_VtkSelectedPointListPolyData = vtkSmartPointer ::New(); m_VtkContourPolyData = vtkSmartPointer::New(); // actors m_UnselectedActor = vtkSmartPointer ::New(); m_SelectedActor = vtkSmartPointer ::New(); m_ContourActor = vtkSmartPointer ::New(); // mappers m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkContourPolyDataMapper = vtkSmartPointer::New(); // propassembly m_PropAssembly = vtkSmartPointer ::New(); } // destructor LocalStorage mitk::PointSetVtkMapper2D::LocalStorage::~LocalStorage() { } // input for this mapper ( = point set) const mitk::PointSet* mitk::PointSetVtkMapper2D::GetInput() const { return static_cast (GetDataNode()->GetData()); } // constructor PointSetVtkMapper2D mitk::PointSetVtkMapper2D::PointSetVtkMapper2D() : m_ShowContour(false), m_CloseContour(false), m_ShowPoints(true), m_ShowDistances(false), m_DistancesDecimalDigits(1), m_ShowAngles(false), m_ShowDistantLines(false), m_LineWidth(1), m_PointLineWidth(1), m_Point2DSize(6), m_IDShapeProperty(mitk::PointSetShapeProperty::CROSS), m_FillShape(false), m_DistanceToPlane(4.0f) { } // destructor mitk::PointSetVtkMapper2D::~PointSetVtkMapper2D() { } // reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly void mitk::PointSetVtkMapper2D::ResetMapper(BaseRenderer* renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } // returns propassembly vtkProp* mitk::PointSetVtkMapper2D::GetVtkProp(mitk::BaseRenderer * renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } static bool makePerpendicularVector2D(const mitk::Vector2D& in, mitk::Vector2D& out) { // The dot product of orthogonal vectors is zero. // In two dimensions the slopes of perpendicular lines are negative reciprocals. if ((fabs(in[0]) > 0) && ((fabs(in[0]) > fabs(in[1])) || (in[1] == 0))) { // negative reciprocal out[0] = -in[1] / in[0]; out[1] = 1; out.Normalize(); return true; } else if (fabs(in[1]) > 0) { out[0] = 1; // negative reciprocal out[1] = -in[0] / in[1]; out.Normalize(); return true; } else return false; } void mitk::PointSetVtkMapper2D::CreateVTKRenderObjects(mitk::BaseRenderer* renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); unsigned i = 0; // The vtk text actors need to be removed manually from the propassembly // since the same vtk text actors are not overwriten within this function, // but new actors are added to the propassembly each time this function is executed. // Thus, the actors from the last call must be removed in the beginning. for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextLabelActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextDistanceActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { if (ls->m_PropAssembly->GetParts()->IsItemPresent(ls->m_VtkTextAngleActors.at(i))) ls->m_PropAssembly->RemovePart(ls->m_VtkTextAngleActors.at(i)); } // initialize polydata here, otherwise we have update problems when // executing this function again ls->m_VtkUnselectedPointListPolyData = vtkSmartPointer::New(); ls->m_VtkSelectedPointListPolyData = vtkSmartPointer ::New(); ls->m_VtkContourPolyData = vtkSmartPointer::New(); // get input point set and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); // only update the input data, if the property tells us to bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep); if (itkPointSet.GetPointer() == NULL) { ls->m_PropAssembly->VisibilityOff(); return; } //iterator for point set mitk::PointSet::PointsContainer::Iterator pointsIter = itkPointSet->GetPoints()->Begin(); // PointDataContainer has additional information to each point, e.g. whether // it is selected or not mitk::PointSet::PointDataContainer::Iterator pointDataIter; pointDataIter = itkPointSet->GetPointData()->Begin(); //check if the list for the PointDataContainer is the same size as the PointsContainer. //If not, then the points were inserted manually and can not be visualized according to the PointData (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken) { ls->m_PropAssembly->VisibilityOff(); return; } ls->m_PropAssembly->VisibilityOn(); // empty point sets, cellarrays, scalars ls->m_UnselectedPoints->Reset(); ls->m_SelectedPoints->Reset(); ls->m_ContourPoints->Reset(); ls->m_ContourLines->Reset(); ls->m_UnselectedScales->Reset(); ls->m_SelectedScales->Reset(); ls->m_DistancesBetweenPoints->Reset(); ls->m_VtkTextLabelActors.clear(); ls->m_VtkTextDistanceActors.clear(); ls->m_VtkTextAngleActors.clear(); ls->m_UnselectedScales->SetNumberOfComponents(3); ls->m_SelectedScales->SetNumberOfComponents(3); int NumberContourPoints = 0; bool pointsOnSameSideOfPlane = false; const int text2dDistance = 10; // initialize points with a random start value // current point in point set itk::Point point = pointsIter->Value(); mitk::Point3D p = point; // currently visited point mitk::Point3D lastP = point; // last visited point (predecessor in point set of "point") mitk::Vector3D vec; // p - lastP mitk::Vector3D lastVec; // lastP - point before lastP vec.Fill(0.0); lastVec.Fill(0.0); mitk::Point2D pt2d; pt2d[0] = point[0]; // projected_p in display coordinates pt2d[1] = point[1]; mitk::Point2D lastPt2d = pt2d; // last projected_p in display coordinates (predecessor in point set of "pt2d") mitk::Point2D preLastPt2d = pt2d; // projected_p in display coordinates before lastPt2 const mitk::PlaneGeometry* geo2D = renderer->GetCurrentWorldPlaneGeometry(); vtkLinearTransform* dataNodeTransform = input->GetGeometry()->GetVtkTransform(); int count = 0; for (pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++) { lastP = p; // valid for number of points count > 0 preLastPt2d = lastPt2d; // valid only for count > 1 lastPt2d = pt2d; // valid for number of points count > 0 lastVec = vec; // valid only for counter > 1 // get current point in point set point = pointsIter->Value(); // transform point { float vtkp[3]; itk2vtk(point, vtkp); dataNodeTransform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, point); } p[0] = point[0]; p[1] = point[1]; p[2] = point[2]; renderer->WorldToDisplay(p, pt2d); vec = p - lastP; // valid only for counter > 0 // compute distance to current plane float dist = geo2D->Distance(point); //draw markers on slices a certain distance away from the points //location according to the tolerance threshold (m_DistanceToPlane) if(dist < m_DistanceToPlane) { // is point selected or not? if (pointDataIter->Value().selected) { ls->m_SelectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane ls->m_SelectedScales->InsertNextTuple3(std::max(0.0f, m_Point2DSize - (2*dist)),0,0); } else { ls->m_UnselectedPoints->InsertNextPoint(point[0], point[1], point[2]); // point is scaled according to its distance to the plane ls->m_UnselectedScales->InsertNextTuple3(std::max(0.0f, m_Point2DSize - (2*dist)),0,0); } //---- LABEL -----// //paint label for each point if available if (dynamic_cast(this->GetDataNode()->GetProperty("label")) != NULL) { const char * pointLabel = dynamic_cast( this->GetDataNode()->GetProperty("label"))->GetValue(); std::string l = pointLabel; if (input->GetSize() > 1) { std::stringstream ss; ss << pointsIter->Index(); l.append(ss.str()); } ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pt2d[0] + text2dDistance, pt2d[1] + text2dDistance); ls->m_VtkTextActor->SetInput(l.c_str()); ls->m_VtkTextActor->GetTextProperty()->SetOpacity(100); float unselectedColor[4] = { 1.0, 1.0, 0.0, 1.0 }; //check if there is a color property GetDataNode()->GetColor(unselectedColor); ls->m_VtkTextActor->GetTextProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_VtkTextLabelActors.push_back(ls->m_VtkTextActor); } } // draw contour, distance text and angle text in render window // lines between points, which intersect the current plane, are drawn if (m_ShowContour && count > 0) { ScalarType distance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(point); ScalarType lastDistance = renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(lastP); pointsOnSameSideOfPlane = (distance * lastDistance) > 0.5; // Points must be on different side of plane in order to draw a contour. // If "show distant lines" is enabled this condition is disregarded. if (!pointsOnSameSideOfPlane || m_ShowDistantLines) { vtkSmartPointer line = vtkSmartPointer::New(); ls->m_ContourPoints->InsertNextPoint(lastP[0], lastP[1], lastP[2]); line->GetPointIds()->SetId(0, NumberContourPoints); NumberContourPoints++; ls->m_ContourPoints->InsertNextPoint(point[0], point[1], point[2]); line->GetPointIds()->SetId(1, NumberContourPoints); NumberContourPoints++; ls->m_ContourLines->InsertNextCell(line); if (m_ShowDistances) // calculate and print distance between adjacent points { float distancePoints = point.EuclideanDistanceTo(lastP); std::stringstream buffer; buffer << std::fixed << std::setprecision(m_DistancesDecimalDigits) << distancePoints << " mm"; // compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; makePerpendicularVector2D(vec2d, vec2d); // text is rendered within text2dDistance perpendicular to current line Vector2D pos2d = (lastPt2d.GetVectorFromOrigin() + pt2d.GetVectorFromOrigin()) * 0.5 + vec2d * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextDistanceActors.push_back(ls->m_VtkTextActor); } if (m_ShowAngles && count > 1) // calculate and print angle between connected lines { std::stringstream buffer; buffer << angle(vec.GetVnlVector(), -lastVec.GetVnlVector()) * 180 / vnl_math::pi << "°"; //compute desired display position of text Vector2D vec2d = pt2d - lastPt2d; // first arm enclosing the angle vec2d.Normalize(); Vector2D lastVec2d = lastPt2d - preLastPt2d; // second arm enclosing the angle lastVec2d.Normalize(); vec2d = vec2d - lastVec2d; // vector connecting both arms vec2d.Normalize(); // middle between two vectors that enclose the angle Vector2D pos2d = lastPt2d.GetVectorFromOrigin() + vec2d * text2dDistance * text2dDistance; ls->m_VtkTextActor = vtkSmartPointer::New(); ls->m_VtkTextActor->SetDisplayPosition(pos2d[0], pos2d[1]); ls->m_VtkTextActor->SetInput(buffer.str().c_str()); ls->m_VtkTextActor->GetTextProperty()->SetColor(0.0, 1.0, 0.0); ls->m_VtkTextAngleActors.push_back(ls->m_VtkTextActor); } } } if (pointDataIter != itkPointSet->GetPointData()->End()) { pointDataIter++; count++; } } // add each single text actor to the assembly for (i = 0; i < ls->m_VtkTextLabelActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextLabelActors.at(i)); } for (i = 0; i < ls->m_VtkTextDistanceActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextDistanceActors.at(i)); } for (i = 0; i < ls->m_VtkTextAngleActors.size(); i++) { ls->m_PropAssembly->AddPart(ls->m_VtkTextAngleActors.at(i)); } //---- CONTOUR -----// //create lines between the points which intersect the plane if (m_ShowContour) { // draw line between first and last point which is rendered if (m_CloseContour && NumberContourPoints > 1){ vtkSmartPointer closingLine = vtkSmartPointer::New(); closingLine->GetPointIds()->SetId(0, 0); // index of first point closingLine->GetPointIds()->SetId(1, NumberContourPoints - 1); // index of last point ls->m_ContourLines->InsertNextCell(closingLine); } ls->m_VtkContourPolyData->SetPoints(ls->m_ContourPoints); ls->m_VtkContourPolyData->SetLines(ls->m_ContourLines); ls->m_VtkContourPolyDataMapper->SetInputData(ls->m_VtkContourPolyData); ls->m_ContourActor->SetMapper(ls->m_VtkContourPolyDataMapper); ls->m_ContourActor->GetProperty()->SetLineWidth(m_LineWidth); ls->m_PropAssembly->AddPart(ls->m_ContourActor); } // the point set must be transformed in order to obtain the appropriate glyph orientation // according to the current view vtkSmartPointer transform = vtkSmartPointer::New(); vtkSmartPointer a, b = vtkSmartPointer::New(); a = geo2D->GetVtkTransform()->GetMatrix(); b->DeepCopy(a); // delete transformation from matrix, only take orientation b->SetElement(3, 3, 1); b->SetElement(2, 3, 0); b->SetElement(1, 3, 0); b->SetElement(0, 3, 0); b->SetElement(3, 2, 0); b->SetElement(3, 1, 0); b->SetElement(3, 0, 0); Vector3D spacing = geo2D->GetSpacing(); // If you find a way to simplyfy the following, feel free to change! b->SetElement(0, 0, b->GetElement(0, 0) / spacing[0]); b->SetElement(1, 0, b->GetElement(1, 0) / spacing[0]); b->SetElement(2, 0, b->GetElement(2, 0) / spacing[0]); b->SetElement(1, 1, b->GetElement(1, 1) / spacing[1]); b->SetElement(2, 1, b->GetElement(2, 1) / spacing[1]); b->SetElement(0, 2, b->GetElement(0, 2) / spacing[2]); b->SetElement(1, 2, b->GetElement(1, 2) / spacing[2]); b->SetElement(2, 2, b->GetElement(2, 2) / spacing[2]); transform->SetMatrix( b ); //---- UNSELECTED POINTS -----// // apply properties to glyph ls->m_UnselectedGlyphSource2D->SetGlyphType(m_IDShapeProperty); if (m_FillShape) ls->m_UnselectedGlyphSource2D->FilledOn(); else ls->m_UnselectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterU = vtkSmartPointer::New(); transformFilterU->SetInputConnection(ls->m_UnselectedGlyphSource2D->GetOutputPort()); transformFilterU->SetTransform(transform); ls->m_VtkUnselectedPointListPolyData->SetPoints(ls->m_UnselectedPoints); ls->m_VtkUnselectedPointListPolyData->GetPointData()->SetVectors(ls->m_UnselectedScales); // apply transform of current plane to glyphs ls->m_UnselectedGlyph3D->SetSourceConnection(transformFilterU->GetOutputPort()); ls->m_UnselectedGlyph3D->SetInputData(ls->m_VtkUnselectedPointListPolyData); ls->m_UnselectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_UnselectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkUnselectedPolyDataMapper->SetInputConnection(ls->m_UnselectedGlyph3D->GetOutputPort()); ls->m_UnselectedActor->SetMapper(ls->m_VtkUnselectedPolyDataMapper); ls->m_UnselectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_UnselectedActor); //---- SELECTED POINTS -----// ls->m_SelectedGlyphSource2D->SetGlyphTypeToDiamond(); ls->m_SelectedGlyphSource2D->CrossOn(); ls->m_SelectedGlyphSource2D->FilledOff(); // apply transform vtkSmartPointer transformFilterS = vtkSmartPointer::New(); transformFilterS->SetInputConnection(ls->m_SelectedGlyphSource2D->GetOutputPort()); transformFilterS->SetTransform(transform); ls->m_VtkSelectedPointListPolyData->SetPoints(ls->m_SelectedPoints); ls->m_VtkSelectedPointListPolyData->GetPointData()->SetVectors(ls->m_SelectedScales); // apply transform of current plane to glyphs ls->m_SelectedGlyph3D->SetSourceConnection(transformFilterS->GetOutputPort()); ls->m_SelectedGlyph3D->SetInputData(ls->m_VtkSelectedPointListPolyData); ls->m_SelectedGlyph3D->SetScaleModeToScaleByVector(); ls->m_SelectedGlyph3D->SetVectorModeToUseVector(); ls->m_VtkSelectedPolyDataMapper->SetInputConnection(ls->m_SelectedGlyph3D->GetOutputPort()); ls->m_SelectedActor->SetMapper(ls->m_VtkSelectedPolyDataMapper); ls->m_SelectedActor->GetProperty()->SetLineWidth(m_PointLineWidth); ls->m_PropAssembly->AddPart(ls->m_SelectedActor); } void mitk::PointSetVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { const mitk::DataNode* node = GetDataNode(); if (node == NULL) return; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); // check whether the input data has been changed bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); // toggle visibility bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); ls->m_ContourActor->VisibilityOff(); ls->m_PropAssembly->VisibilityOff(); return; } else{ ls->m_PropAssembly->VisibilityOn(); } node->GetBoolProperty("show contour", m_ShowContour, renderer); node->GetBoolProperty("close contour", m_CloseContour, renderer); node->GetBoolProperty("show points", m_ShowPoints, renderer); node->GetBoolProperty("show distances", m_ShowDistances, renderer); node->GetIntProperty("distance decimal digits", m_DistancesDecimalDigits, renderer); node->GetBoolProperty("show angles", m_ShowAngles, renderer); node->GetBoolProperty("show distant lines", m_ShowDistantLines, renderer); node->GetIntProperty("line width", m_LineWidth, renderer); node->GetIntProperty("point line width", m_PointLineWidth, renderer); if ( !node->GetFloatProperty("point 2D size", m_Point2DSize, renderer) ) // re-defined to float 2015-08-13, keep a fallback { int oldPointSize = m_Point2DSize; if ( node->GetIntProperty("point 2D size", oldPointSize, renderer) ) { m_Point2DSize = oldPointSize; } } node->GetBoolProperty("Pointset.2D.fill shape", m_FillShape, renderer); node->GetFloatProperty("Pointset.2D.distance to plane", m_DistanceToPlane, renderer); mitk::PointSetShapeProperty::Pointer shape = dynamic_cast(this->GetDataNode()->GetProperty("Pointset.2D.shape", renderer)); if (shape.IsNotNull()) { m_IDShapeProperty = shape->GetPointSetShape(); } //check for color props and use it for rendering of selected/unselected points and contour //due to different params in VTK (double/float) we have to convert - float unselectedColor[4]; - double selectedColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; //red - double contourColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; //red - float opacity = 1.0; GetDataNode()->GetOpacity(opacity, renderer); // apply color and opacity if (m_ShowPoints) { + float unselectedColor[4]; + double selectedColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; //red + ls->m_UnselectedActor->VisibilityOn(); ls->m_SelectedActor->VisibilityOn(); //check if there is a color property GetDataNode()->GetColor(unselectedColor); //get selected color property if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("selectedcolor"))->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; // alpha value } ls->m_SelectedActor->GetProperty()->SetColor(selectedColor); ls->m_SelectedActor->GetProperty()->SetOpacity(opacity); ls->m_UnselectedActor->GetProperty()->SetColor(unselectedColor[0], unselectedColor[1], unselectedColor[2]); ls->m_UnselectedActor->GetProperty()->SetOpacity(opacity); } else { ls->m_UnselectedActor->VisibilityOff(); ls->m_SelectedActor->VisibilityOff(); } if (m_ShowContour) { + double contourColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; //red ls->m_ContourActor->VisibilityOn(); //get contour color property if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor")) != NULL) { mitk::Color tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(NULL)->GetProperty("contourcolor"))->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } ls->m_ContourActor->GetProperty()->SetColor(contourColor); ls->m_ContourActor->GetProperty()->SetOpacity(opacity); } else { ls->m_ContourActor->VisibilityOff(); } if (needGenerateData) { // create new vtk render objects (e.g. a circle for a point) this->CreateVTKRenderObjects(renderer); } } void mitk::PointSetVtkMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("point line width", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("point 2D size", mitk::FloatProperty::New(6), renderer, overwrite); node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("show distances", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("distance decimal digits", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("show angles", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("show distant lines", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(1), renderer, overwrite); node->AddProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(false), renderer, overwrite); // fill or do not fill the glyph shape mitk::PointSetShapeProperty::Pointer pointsetShapeProperty = mitk::PointSetShapeProperty::New(); node->AddProperty("Pointset.2D.shape", pointsetShapeProperty, renderer, overwrite); node->AddProperty("Pointset.2D.distance to plane", mitk::FloatProperty::New(4.0f), renderer, overwrite); //show the point at a certain distance above/below the 2D imaging plane. Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp b/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp index 42fd99274d..df3b756d32 100644 --- a/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp +++ b/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp @@ -1,240 +1,240 @@ /*=================================================================== 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 "mitkVtkEventProvider.h" #include "mitkVtkEventAdapter.h" #include #include #include #include #include #include "mitkInteractionEvent.h" #define VTKEVENTPROVIDER_INFO MBI_INFO("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_WARN MBI_WARN("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_ERROR MBI_ERROR("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_DEBUG MBI_DEBUG("mitk.core.vtkeventprovider") namespace mitk { vtkStandardNewMacro(vtkEventProvider); } //---------------------------------------------------------------------------- mitk::vtkEventProvider::vtkEventProvider() { // priority of the observer/command; we want MITK events processed in the very beginning this->Priority = 99999.99; //take over the processing of delete and keypress events from the superclass this->EventCallbackCommand->SetCallback(vtkEventProvider::ProcessEvents); // Set/Get the passive observer flag. If this is set to true, this // indicates that this command does not change the state of the // system in any way. Passive observers are processed first, and // are not called even when another command has focus. this->EventCallbackCommand->SetPassiveObserver(1); // get events first // mouse move AddInteractionEvent(vtkCommand::MouseMoveEvent); // mouse press AddInteractionEvent(vtkCommand::LeftButtonPressEvent); AddInteractionEvent(vtkCommand::MiddleButtonPressEvent); AddInteractionEvent(vtkCommand::RightButtonPressEvent); // mouse release AddInteractionEvent(vtkCommand::LeftButtonReleaseEvent); AddInteractionEvent(vtkCommand::MiddleButtonReleaseEvent); AddInteractionEvent(vtkCommand::RightButtonReleaseEvent); // wheel event AddInteractionEvent(vtkCommand::MouseWheelBackwardEvent); AddInteractionEvent(vtkCommand::MouseWheelForwardEvent); // key press event AddInteractionEvent(vtkCommand::KeyPressEvent); } mitk::vtkEventProvider::~vtkEventProvider() { this->SetInteractor(0); } void mitk::vtkEventProvider::SetMitkRenderWindow(mitk::RenderWindow* renWin) { m_RenderWindow = renWin; } mitk::RenderWindow* mitk::vtkEventProvider::GetRenderWindow() { return m_RenderWindow; } void mitk::vtkEventProvider::SetEnabled(int enabling) { if (!this->Interactor) { VTKEVENTPROVIDER_ERROR <<"The interactor must be set prior to enabling/disabling widget"; return; } if ( enabling ) //---------------------------------------------------------- { VTKEVENTPROVIDER_DEBUG << "Enabling widget"; if ( this->Enabled ) //already enabled, just return { return; } this->Enabled = 1; // listen to all event types specified in m_InteractionEventsVector vtkRenderWindowInteractor *i = this->Interactor; InteractionEventsVectorType::iterator it; - for(it = m_InteractionEventsVector.begin(); it != m_InteractionEventsVector.end(); it++) + for(it = m_InteractionEventsVector.begin(); it != m_InteractionEventsVector.end(); ++it) { // add observer to interactorStyle i->GetInteractorStyle()->AddObserver((vtkCommand::EventIds) (*it), this->EventCallbackCommand, this->Priority); } this->InvokeEvent(vtkCommand::EnableEvent,NULL); } else //disabling----------------------------------------------------------- { VTKEVENTPROVIDER_DEBUG <<"Disabling widget"; if ( ! this->Enabled ) //already disabled, just return { return; } this->Enabled = 0; // don't listen for events any more this->Interactor->RemoveObserver(this->EventCallbackCommand); //this->Interactor->HandleEventLoop = 0; this->InvokeEvent(vtkCommand::DisableEvent,NULL); } } //---------------------------------------------------------------------------- // This adds the keypress event observer and the delete event observer void mitk::vtkEventProvider::SetInteractor(vtkRenderWindowInteractor* i) { if (i == this->Interactor) { return; } // if we already have an Interactor then stop observing it if (this->Interactor) this->SetEnabled(0); //disable the old interactor this->Interactor = i; this->Modified(); } //---------------------------------------------------------------------------- void mitk::vtkEventProvider::ProcessEvents(vtkObject* object, unsigned long event, void* clientData, void* vtkNotUsed(callData)) { vtkEventProvider* self = reinterpret_cast(clientData); vtkRenderWindowInteractor* rwi = static_cast(object)->GetInteractor(); // base renderer mitk::BaseRenderer* baseRenderer = mitk::BaseRenderer::GetInstance(self->GetRenderWindow()->GetVtkRenderWindow()); switch (event) { // key press case vtkCommand::KeyPressEvent: { VTKEVENTPROVIDER_DEBUG << "key press event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptInteractionKeyEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } // mouse events case vtkCommand::MouseMoveEvent: { VTKEVENTPROVIDER_DEBUG << "mouse move event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseMoveEvent(baseRenderer,event,rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } case vtkCommand::LeftButtonPressEvent: case vtkCommand::MiddleButtonPressEvent: case vtkCommand::RightButtonPressEvent: { VTKEVENTPROVIDER_DEBUG << "mouse press event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMousePressEvent(baseRenderer,event,rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } case vtkCommand::LeftButtonReleaseEvent: case vtkCommand::MiddleButtonReleaseEvent: case vtkCommand::RightButtonReleaseEvent: { VTKEVENTPROVIDER_DEBUG << "mouse release event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseReleaseEvent(baseRenderer,event,rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } // mouse WHEEL case vtkCommand::MouseWheelForwardEvent: case vtkCommand::MouseWheelBackwardEvent: { VTKEVENTPROVIDER_DEBUG << "mouse wheel event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseWheelEvent(baseRenderer,event,rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } default: VTKEVENTPROVIDER_INFO << "VTK event not mapped properly."; break; } } void mitk::vtkEventProvider::RemoveInteractionEvent(unsigned long ievent) { InteractionEventsVectorType::iterator it; if (m_InteractionEventsVector.size() > 0) { it = std::find(m_InteractionEventsVector.begin(), m_InteractionEventsVector.end(), ievent); if (it != m_InteractionEventsVector.end()) { m_InteractionEventsVector.erase(it); return; } } } void mitk::vtkEventProvider::AddInteractionEvent(unsigned long ievent) { // Remove event if it already exists RemoveInteractionEvent(ievent); m_InteractionEventsVector.push_back(ievent); } diff --git a/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp b/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp index 2e659cc20f..c46fb89807 100644 --- a/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp @@ -1,685 +1,685 @@ /*=================================================================== 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 "mitkVtkPropRenderer.h" // MAPPERS #include "mitkMapper.h" #include "mitkImageVtkMapper2D.h" #include "mitkVtkMapper.h" #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkCameraController.h" #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::VtkPropRenderer::VtkPropRenderer(const char* name, vtkRenderWindow * renWin, mitk::RenderingManager* rm, mitk::BaseRenderer::RenderingMode::Type renderingMode) : BaseRenderer(name, renWin, rm, renderingMode), m_CameraInitializedForMapperID(0) { didCount = false; m_WorldPointPicker = vtkWorldPointPicker::New(); m_PointPicker = vtkPointPicker::New(); m_PointPicker->SetTolerance(0.0025); m_CellPicker = vtkCellPicker::New(); m_CellPicker->SetTolerance(0.0025); mitk::PlaneGeometryDataVtkMapper3D::Pointer geometryMapper = mitk::PlaneGeometryDataVtkMapper3D::New(); m_CurrentWorldPlaneGeometryMapper = geometryMapper; m_CurrentWorldPlaneGeometryNode->SetMapper(2, geometryMapper); m_LightKit = vtkLightKit::New(); m_LightKit->AddLightsToRenderer(m_VtkRenderer); m_PickingMode = WorldPointPicking; m_TextRenderer = vtkRenderer::New(); m_TextRenderer->SetRenderWindow(renWin); m_TextRenderer->SetInteractive(0); m_TextRenderer->SetErase(0); } /*! \brief Destructs the VtkPropRenderer. */ mitk::VtkPropRenderer::~VtkPropRenderer() { // Workaround for GLDisplayList Bug { m_MapperID = 0; checkState(); } if (m_LightKit != NULL) m_LightKit->Delete(); if (m_VtkRenderer != NULL) { m_CameraController = NULL; m_VtkRenderer->Delete(); m_VtkRenderer = NULL; } else m_CameraController = NULL; if (m_WorldPointPicker != NULL) m_WorldPointPicker->Delete(); if (m_PointPicker != NULL) m_PointPicker->Delete(); if (m_CellPicker != NULL) m_CellPicker->Delete(); if (m_TextRenderer != NULL) m_TextRenderer->Delete(); } void mitk::VtkPropRenderer::SetDataStorage(mitk::DataStorage* storage) { if (storage == NULL) return; BaseRenderer::SetDataStorage(storage); static_cast(m_CurrentWorldPlaneGeometryMapper.GetPointer())->SetDataStorageForTexture(m_DataStorage.GetPointer()); // Compute the geometry from the current data tree bounds and set it as world geometry this->SetWorldGeometryToDataStorageBounds(); } bool mitk::VtkPropRenderer::SetWorldGeometryToDataStorageBounds() { if (m_DataStorage.IsNull()) return false; //initialize world geometry mitk::TimeGeometry::Pointer geometry = m_DataStorage->ComputeVisibleBoundingGeometry3D( NULL, "includeInBoundingBox" ); if ( geometry.IsNull() ) return false; this->SetWorldTimeGeometry(geometry); this->GetVtkRenderer()->ResetCamera(); this->GetCameraController()->Fit(); this->Modified(); return true; } /*! \brief Called by the vtkMitkRenderProp in order to start MITK rendering process. */ int mitk::VtkPropRenderer::Render(mitk::VtkPropRenderer::RenderType type) { //Update all overlays in any case this->UpdateOverlays(); // Do we have objects to render? if (this->GetEmptyWorldGeometry()) return 0; if (m_DataStorage.IsNull()) return 0; // Update mappers and prepare mapper queue if (type == VtkPropRenderer::Opaque) this->PrepareMapperQueue(); //go through the generated list and let the sorted mappers paint for ( auto it = m_MappersMap.cbegin(); it != m_MappersMap.cend(); it++) { Mapper * mapper = (*it).second; mapper->MitkRender(this, type); } //Update overlays in case a mapper has changed them this->UpdateOverlays(); // Render text if (type == VtkPropRenderer::Overlay) { if (m_TextCollection.size() > 0) { m_TextRenderer->SetViewport(this->GetVtkRenderer()->GetViewport()); - for (TextMapType::iterator it = m_TextCollection.begin(); it != m_TextCollection.end(); it++) + for (TextMapType::iterator it = m_TextCollection.begin(); it != m_TextCollection.end(); ++it) m_TextRenderer->AddViewProp((*it).second); m_TextRenderer->Render(); } } return 1; } /*! \brief PrepareMapperQueue iterates the datatree PrepareMapperQueue iterates the datatree in order to find mappers which shall be rendered. Also, it sortes the mappers wrt to their layer. */ void mitk::VtkPropRenderer::PrepareMapperQueue() { // variable for counting LOD-enabled mappers m_NumberOfVisibleLODEnabledMappers = 0; // Do we have to update the mappers ? if (m_LastUpdateTime < GetMTime() || m_LastUpdateTime < this->GetCurrentWorldPlaneGeometry()->GetMTime()) { Update(); } else if (m_MapperID >= 1 && m_MapperID < 6) Update(); // remove all text properties before mappers will add new ones m_TextRenderer->RemoveAllViewProps(); for (unsigned int i = 0; i < m_TextCollection.size(); i++) { m_TextCollection[i]->Delete(); } m_TextCollection.clear(); // clear priority_queue m_MappersMap.clear(); int mapperNo = 0; //DataStorage if (m_DataStorage.IsNull()) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { const DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; const mitk::Mapper::Pointer mapper = node->GetMapper(m_MapperID); if (mapper.IsNull()) continue; bool visible = true; node->GetVisibility(visible, this, "visible"); // The information about LOD-enabled mappers is required by RenderingManager if (mapper->IsLODEnabled(this) && visible) { ++m_NumberOfVisibleLODEnabledMappers; } // mapper without a layer property get layer number 1 int layer = 1; node->GetIntProperty("layer", layer, this); int nr = (layer << 16) + mapperNo; m_MappersMap.insert(std::pair< int, Mapper * >(nr, mapper)); mapperNo++; } } void mitk::VtkPropRenderer::Update(mitk::DataNode* datatreenode) { if (datatreenode != NULL) { mitk::Mapper::Pointer mapper = datatreenode->GetMapper(m_MapperID); if (mapper.IsNotNull()) { if (GetCurrentWorldPlaneGeometry()->IsValid()) { mapper->Update(this); { VtkMapper* vtkmapper = dynamic_cast(mapper.GetPointer()); if (vtkmapper != NULL) { vtkmapper->UpdateVtkTransform(this); } } } } } } void mitk::VtkPropRenderer::Update() { if (m_DataStorage.IsNull()) return; mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetAll(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) Update(it->Value()); Modified(); m_LastUpdateTime = GetMTime(); } /*! \brief This method is called from the two Constructors */ void mitk::VtkPropRenderer::InitRenderer(vtkRenderWindow* renderWindow) { BaseRenderer::InitRenderer(renderWindow); vtkCallbackCommand *renderCallbackCommand = vtkCallbackCommand::New(); renderCallbackCommand->SetCallback( VtkPropRenderer::RenderingCallback ); renderWindow->GetInteractor()->AddObserver( vtkCommand::RenderEvent, renderCallbackCommand ); renderCallbackCommand->Delete(); if(renderWindow == NULL) { m_InitNeeded = false; m_ResizeNeeded = false; return; } m_InitNeeded = true; m_ResizeNeeded = true; m_LastUpdateTime = 0; } void mitk::VtkPropRenderer::RenderingCallback( vtkObject *caller, unsigned long , void *, void * ) { vtkRenderWindowInteractor *renderWindowInteractor = dynamic_cast< vtkRenderWindowInteractor * >( caller ); if(!renderWindowInteractor) return; mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(renderWindowInteractor->GetRenderWindow()); if(renderer) renderer->RequestUpdate(); } /*! \brief Resize the OpenGL Window */ void mitk::VtkPropRenderer::Resize(int w, int h) { BaseRenderer::Resize(w, h); m_RenderingManager->RequestUpdate(this->GetRenderWindow()); } void mitk::VtkPropRenderer::InitSize(int w, int h) { m_RenderWindow->SetSize(w, h); Superclass::InitSize(w, h); Modified(); Update(); if (m_VtkRenderer != NULL) { int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); m_VtkRenderer->ResetCamera(); vtkObject::SetGlobalWarningDisplay(w); } this->GetCameraController()->Fit(); } int mitk::VtkPropRenderer::WriteSimpleText(std::string text, double posX, double posY, double color1, double color2, double color3, float opacity) { this->GetVtkRenderer()->ViewToDisplay(); if (!text.empty()) { Point2D p; vtkTextActor* textActor = vtkTextActor::New(); textActor->SetDisplayPosition(posX, posY); textActor->SetInput(text.c_str()); textActor->SetTextScaleModeToNone(); textActor->GetTextProperty()->SetColor(color1, color2, color3); //TODO: Read color from node property textActor->GetTextProperty()->SetOpacity(opacity); int text_id = m_TextCollection.size(); m_TextCollection.insert(TextMapType::value_type(text_id, textActor)); return text_id; } else { return -1; } } void mitk::VtkPropRenderer::SetMapperID(const MapperSlotId mapperId) { if (m_MapperID != mapperId) Superclass::SetMapperID(mapperId); // Workaround for GL Displaylist Bug checkState(); } /*! \brief Activates the current renderwindow. */ void mitk::VtkPropRenderer::MakeCurrent() { if (m_RenderWindow != NULL) m_RenderWindow->MakeCurrent(); } void mitk::VtkPropRenderer::PickWorldPoint(const mitk::Point2D& displayPoint, mitk::Point3D& worldPoint) const { if (this->GetRenderWindow()->GetNeverRendered() != 0) return; // somebody called picking before we ever rendered; cannot have enough information yet switch (m_PickingMode) { case (WorldPointPicking) : { m_WorldPointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_WorldPointPicker->GetPickPosition(), worldPoint); break; } case (PointPicking) : { m_PointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_PointPicker->GetPickPosition(), worldPoint); break; } case(CellPicking) : { m_CellPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_CellPicker->GetPickPosition(), worldPoint); break; } } //todo: is this picking in 2D renderwindows? // Superclass::PickWorldPoint(displayPoint, worldPoint); } mitk::DataNode * mitk::VtkPropRenderer::PickObject(const Point2D &displayPosition, Point3D &worldPosition) const { m_CellPicker->InitializePickList(); // Iterate over all DataStorage objects to determine all vtkProps intended // for picking DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { const DataNode *node = it->Value(); if (node == NULL) continue; bool pickable = false; node->GetBoolProperty("pickable", pickable); if (!pickable) continue; VtkMapper *mapper = dynamic_cast (node->GetMapper(m_MapperID)); if (mapper == NULL) continue; vtkProp *prop = mapper->GetVtkProp((mitk::BaseRenderer *)this); if (prop == NULL) continue; m_CellPicker->AddPickList(prop); } // Do the picking and retrieve the picked vtkProp (if any) m_CellPicker->PickFromListOn(); m_CellPicker->Pick(displayPosition[0], displayPosition[1], 0.0, m_VtkRenderer); m_CellPicker->PickFromListOff(); vtk2itk(m_CellPicker->GetPickPosition(), worldPosition); vtkProp *prop = m_CellPicker->GetViewProp(); if (prop == NULL) { return NULL; } // Iterate over all DataStorage objects to determine if the retrieved // vtkProp is owned by any associated mapper. for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; mitk::Mapper * mapper = node->GetMapper(m_MapperID); if (mapper == NULL) continue; mitk::VtkMapper * vtkmapper = dynamic_cast(mapper); if (vtkmapper){ //if vtk-based, then ... if (vtkmapper->HasVtkProp(prop, const_cast(this))) { return node; } } } return NULL; } //todo: is this 2D renderwindow picking? // return Superclass::PickObject( displayPosition, worldPosition ); vtkTextProperty* mitk::VtkPropRenderer::GetTextLabelProperty(int text_id) { return this->m_TextCollection[text_id]->GetTextProperty(); } void mitk::VtkPropRenderer::InitPathTraversal() { if (m_DataStorage.IsNotNull()) { this->UpdatePaths(); this->m_Paths->InitTraversal(); } } void mitk::VtkPropRenderer::UpdatePaths() { if (m_DataStorage.IsNull()) { return; } if (GetMTime() > m_PathTime || (m_Paths != nullptr && m_Paths->GetMTime() > m_PathTime)) { // Create the list to hold all the paths m_Paths = vtkSmartPointer::New(); DataStorage::SetOfObjects::ConstPointer objects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::const_iterator iter = objects->begin(); iter != objects->end(); ++iter) { vtkSmartPointer onePath = vtkSmartPointer::New(); Mapper* mapper = (*iter)->GetMapper(BaseRenderer::Standard3D); if (mapper) { VtkMapper* vtkmapper = dynamic_cast(mapper); { vtkProp* prop = vtkmapper->GetVtkProp(this); if (prop && prop->GetVisibility()) { // add to assembly path onePath->AddNode(prop, prop->GetMatrix()); m_Paths->AddItem(onePath); } } } } m_PathTime.Modified(); } } int mitk::VtkPropRenderer::GetNumberOfPaths() { UpdatePaths(); return m_Paths->GetNumberOfItems(); } vtkAssemblyPath* mitk::VtkPropRenderer::GetNextPath() { return m_Paths ? m_Paths->GetNextItem() : 0; } void mitk::VtkPropRenderer::ReleaseGraphicsResources(vtkWindow* /*renWin*/) { if (m_DataStorage.IsNull()) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::const_iterator iter = allObjects->begin(); iter != allObjects->end(); ++iter) { DataNode::Pointer node = *iter; if (node.IsNull()) continue; Mapper * mapper = node->GetMapper(m_MapperID); if (mapper) { VtkMapper* vtkmapper = dynamic_cast(mapper); if (vtkmapper) vtkmapper->ReleaseGraphicsResources(this); } } } const vtkWorldPointPicker *mitk::VtkPropRenderer::GetWorldPointPicker() const { return m_WorldPointPicker; } const vtkPointPicker *mitk::VtkPropRenderer::GetPointPicker() const { return m_PointPicker; } const vtkCellPicker *mitk::VtkPropRenderer::GetCellPicker() const { return m_CellPicker; } mitk::VtkPropRenderer::MappersMapType mitk::VtkPropRenderer::GetMappersMap() const { return m_MappersMap; } // Workaround for GL Displaylist bug static int glWorkAroundGlobalCount = 0; bool mitk::VtkPropRenderer::useImmediateModeRendering() { return glWorkAroundGlobalCount>1; } void mitk::VtkPropRenderer::checkState() { if (m_MapperID == Standard3D) { if (!didCount) { didCount = true; glWorkAroundGlobalCount++; if (glWorkAroundGlobalCount == 2) { MITK_INFO << "Multiple 3D Renderwindows active...: turning Immediate Rendering ON for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOn(); } } } else { if (didCount) { didCount = false; glWorkAroundGlobalCount--; if (glWorkAroundGlobalCount == 1) { MITK_INFO << "Single 3D Renderwindow active...: turning Immediate Rendering OFF for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOff(); } } } } //### Contains all methods which are neceassry before each VTK Render() call void mitk::VtkPropRenderer::PrepareRender() { if (this->GetMapperID() != m_CameraInitializedForMapperID) { Initialize2DvtkCamera(); //Set parallel projection etc. } GetCameraController()->AdjustCameraToPlane(); } bool mitk::VtkPropRenderer::Initialize2DvtkCamera() { if (this->GetMapperID() == Standard3D) { //activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(false); vtkSmartPointer style = vtkSmartPointer::New(); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style); this->GetRenderWindow()->GetInteractor()->EnableRenderOff(); m_CameraInitializedForMapperID = Standard3D; } else if (this->GetMapperID() == Standard2D) { //activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(true); //turn the light out in the scene in order to render correct grey values. //TODO Implement a property for light in the 2D render windows (in another method) this->GetVtkRenderer()->RemoveAllLights(); vtkSmartPointer style = vtkSmartPointer::New(); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style); this->GetRenderWindow()->GetInteractor()->EnableRenderOff(); m_CameraInitializedForMapperID = Standard2D; } return true; } diff --git a/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp b/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp index fef872ed3d..acddc78c59 100644 --- a/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp +++ b/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp @@ -1,113 +1,113 @@ /*=================================================================== 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 "vtkMitkRenderProp.h" #include #include #include #include "mitkVtkMapper.h" //#include "mitkGLMapper.h" vtkStandardNewMacro(vtkMitkRenderProp); vtkMitkRenderProp::vtkMitkRenderProp() { } vtkMitkRenderProp::~vtkMitkRenderProp() { } double *vtkMitkRenderProp::GetBounds() { return const_cast(m_VtkPropRenderer->GetBounds()); } void vtkMitkRenderProp::SetPropRenderer(mitk::VtkPropRenderer::Pointer propRenderer) { this->m_VtkPropRenderer = propRenderer; } int vtkMitkRenderProp::RenderOpaqueGeometry(vtkViewport* /*viewport*/) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Opaque); } int vtkMitkRenderProp::RenderOverlay(vtkViewport* /*viewport*/) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Overlay); } void vtkMitkRenderProp::ReleaseGraphicsResources(vtkWindow* window) { m_VtkPropRenderer->ReleaseGraphicsResources(window); } void vtkMitkRenderProp::InitPathTraversal() { m_VtkPropRenderer->InitPathTraversal(); } vtkAssemblyPath* vtkMitkRenderProp::GetNextPath() { return m_VtkPropRenderer->GetNextPath(); } int vtkMitkRenderProp::GetNumberOfPaths() { return m_VtkPropRenderer->GetNumberOfPaths(); } //BUG (#1551) added method depth peeling int vtkMitkRenderProp::HasTranslucentPolygonalGeometry() { typedef std::map MappersMapType; const MappersMapType mappersMap = m_VtkPropRenderer->GetMappersMap(); - for(MappersMapType::const_iterator it = mappersMap.cbegin(); it != mappersMap.cend(); it++) + for(MappersMapType::const_iterator it = mappersMap.cbegin(); it != mappersMap.cend(); ++it) { mitk::Mapper * mapper = (*it).second; const mitk::VtkMapper::Pointer vtkMapper = dynamic_cast(mapper); if(vtkMapper) { // Due to VTK 5.2 bug, we need to initialize the Paths object in vtkPropAssembly // manually (see issue #8186 committed to VTK's Mantis issue tracker) // --> VTK bug resolved on 2008-12-01 vtkPropAssembly *propAssembly = dynamic_cast< vtkPropAssembly * >( vtkMapper->GetVtkProp(m_VtkPropRenderer) ); if ( propAssembly ) { propAssembly->InitPathTraversal(); } if (vtkMapper->GetVtkProp(m_VtkPropRenderer)->HasTranslucentPolygonalGeometry()==1) return 1; } } return 0; } int vtkMitkRenderProp::RenderTranslucentPolygonalGeometry( vtkViewport * ) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Translucent); } int vtkMitkRenderProp::RenderVolumetricGeometry( vtkViewport * ) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Volumetric); } diff --git a/Modules/Core/src/mitkCoreObjectFactory.cpp b/Modules/Core/src/mitkCoreObjectFactory.cpp index 91c90fb942..731933601f 100644 --- a/Modules/Core/src/mitkCoreObjectFactory.cpp +++ b/Modules/Core/src/mitkCoreObjectFactory.cpp @@ -1,446 +1,445 @@ /*=================================================================== 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 "mitkConfig.h" #include "mitkCoreObjectFactory.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkEnumerationProperty.h" #include "mitkPlaneGeometryData.h" #include "mitkPlaneGeometryDataMapper2D.h" #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkGeometry3D.h" #include "mitkGeometryData.h" #include "mitkImage.h" #include #include "mitkLevelWindowProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkPlaneGeometry.h" #include "mitkPointSet.h" #include "mitkPointSetVtkMapper2D.h" #include "mitkPointSetVtkMapper3D.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include "mitkSlicedGeometry3D.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkSurface.h" #include "mitkSurfaceVtkMapper2D.h" #include "mitkSurfaceVtkMapper3D.h" #include "mitkTimeGeometry.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkResliceInterpolationProperty.h" // Legacy Support: #include #include #include void mitk::CoreObjectFactory::RegisterExtraFactory(CoreObjectFactoryBase* factory) { MITK_DEBUG << "CoreObjectFactory: registering extra factory of type " << factory->GetNameOfClass(); m_ExtraFactories.insert(CoreObjectFactoryBase::Pointer(factory)); // Register Legacy Reader and Writer this->RegisterLegacyReaders(factory); this->RegisterLegacyWriters(factory); } void mitk::CoreObjectFactory::UnRegisterExtraFactory(CoreObjectFactoryBase *factory) { MITK_DEBUG << "CoreObjectFactory: un-registering extra factory of type " << factory->GetNameOfClass(); this->UnRegisterLegacyWriters(factory); this->UnRegisterLegacyReaders(factory); try { m_ExtraFactories.erase(factory); } catch( std::exception const& e) { MITK_ERROR << "Caugt exception while unregistering: " << e.what(); } } mitk::CoreObjectFactory::Pointer mitk::CoreObjectFactory::GetInstance() { static mitk::CoreObjectFactory::Pointer instance; if (instance.IsNull()) { instance = mitk::CoreObjectFactory::New(); } return instance; } mitk::CoreObjectFactory::~CoreObjectFactory() { for (std::map >::iterator iter = m_LegacyReaders.begin(); iter != m_LegacyReaders.end(); ++iter) { for (auto & elem : iter->second) { delete elem; } } for (std::map >::iterator iter = m_LegacyWriters.begin(); iter != m_LegacyWriters.end(); ++iter) { for (auto & elem : iter->second) { delete elem; } } } void mitk::CoreObjectFactory::SetDefaultProperties(mitk::DataNode* node) { if(node==NULL) return; mitk::DataNode::Pointer nodePointer = node; mitk::Image::Pointer image = dynamic_cast(node->GetData()); if(image.IsNotNull() && image->IsInitialized()) { mitk::ImageVtkMapper2D::SetDefaultProperties(node); } mitk::PlaneGeometryData::Pointer planeGeometry = dynamic_cast(node->GetData()); if(planeGeometry.IsNotNull()) { mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(node); } mitk::Surface::Pointer surface = dynamic_cast(node->GetData()); if(surface.IsNotNull()) { mitk::SurfaceVtkMapper2D::SetDefaultProperties(node); mitk::SurfaceVtkMapper3D::SetDefaultProperties(node); } mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if(pointSet.IsNotNull()) { mitk::PointSetVtkMapper2D::SetDefaultProperties(node); mitk::PointSetVtkMapper3D::SetDefaultProperties(node); } - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; it++ ) { + for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; ++it ) { (*it)->SetDefaultProperties(node); } } mitk::CoreObjectFactory::CoreObjectFactory() { static bool alreadyDone = false; if (!alreadyDone) { CreateFileExtensionsMap(); //RegisterLegacyReaders(this); //RegisterLegacyWriters(this); alreadyDone = true; } } mitk::Mapper::Pointer mitk::CoreObjectFactory::CreateMapper(mitk::DataNode* node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = NULL; mitk::Mapper::Pointer tmpMapper = NULL; // check whether extra factories provide mapper - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; it++ ) { + for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; ++it ) { tmpMapper = (*it)->CreateMapper(node,id); if(tmpMapper.IsNotNull()) newMapper = tmpMapper; } if (newMapper.IsNull()) { mitk::BaseData *data = node->GetData(); if ( id == mitk::BaseRenderer::Standard2D ) { if((dynamic_cast(data)!=NULL)) { newMapper = mitk::ImageVtkMapper2D::New(); newMapper->SetDataNode(node); } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::PlaneGeometryDataMapper2D::New(); newMapper->SetDataNode(node); } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::SurfaceVtkMapper2D::New(); // cast because SetDataNode is not virtual mitk::SurfaceVtkMapper2D *castedMapper = dynamic_cast(newMapper.GetPointer()); castedMapper->SetDataNode(node); } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::PointSetVtkMapper2D::New(); newMapper->SetDataNode(node); } } else if ( id == mitk::BaseRenderer::Standard3D ) { if((dynamic_cast(data)!=NULL)) { newMapper = mitk::PlaneGeometryDataVtkMapper3D::New(); newMapper->SetDataNode(node); } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::SurfaceVtkMapper3D::New(); newMapper->SetDataNode(node); } else if((dynamic_cast(data)!=NULL)) { newMapper = mitk::PointSetVtkMapper3D::New(); newMapper->SetDataNode(node); } } } return newMapper; } const char* mitk::CoreObjectFactory::GetFileExtensions() { MultimapType aMap; - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; it++ ) + for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; ++it ) { aMap = (*it)->GetFileExtensionsMap(); this->MergeFileExtensions(m_FileExtensionsMap, aMap); } this->CreateFileExtensions(m_FileExtensionsMap, m_FileExtensions); return m_FileExtensions.c_str(); } void mitk::CoreObjectFactory::MergeFileExtensions(MultimapType& fileExtensionsMap, MultimapType inputMap) { - bool duplicateFound = false; std::pair pairOfIter; for (MultimapType::iterator it = inputMap.begin(); it != inputMap.end(); ++it) { - duplicateFound = false; + bool duplicateFound = false; pairOfIter = fileExtensionsMap.equal_range((*it).first); for (MultimapType::iterator it2 = pairOfIter.first; it2 != pairOfIter.second; ++it2) { //cout << " [" << (*it).first << ", " << (*it).second << "]" << endl; std::string aString = (*it2).second; if (aString.compare((*it).second) == 0) { //cout << " DUP!! [" << (*it).first << ", " << (*it).second << "]" << endl; duplicateFound = true; break; } } if (!duplicateFound) { fileExtensionsMap.insert(std::pair((*it).first, (*it).second)); } } } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetFileExtensionsMap() { return m_FileExtensionsMap; } void mitk::CoreObjectFactory::CreateFileExtensionsMap() { /* m_FileExtensionsMap.insert(std::pair("*.dcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DCM", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.dc3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DC3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.seq", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.seq.gz", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.dcm", "Sets of 2D slices")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "Sets of 2D slices")); */ } const char* mitk::CoreObjectFactory::GetSaveFileExtensions() { MultimapType aMap; - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; it++ ) + for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end() ; ++it ) { aMap = (*it)->GetSaveFileExtensionsMap(); this->MergeFileExtensions(m_SaveFileExtensionsMap, aMap); } this->CreateFileExtensions(m_SaveFileExtensionsMap, m_SaveFileExtensions); return m_SaveFileExtensions.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetSaveFileExtensionsMap() { return m_SaveFileExtensionsMap; } mitk::CoreObjectFactory::FileWriterList mitk::CoreObjectFactory::GetFileWriters() { FileWriterList allWriters = m_FileWriters; //sort to merge lists later on typedef std::set FileWriterSet; FileWriterSet fileWritersSet; fileWritersSet.insert(allWriters.begin(), allWriters.end()); //collect all extra factories for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); - it != m_ExtraFactories.end(); it++ ) + it != m_ExtraFactories.end(); ++it ) { FileWriterList list2 = (*it)->GetFileWriters(); //add them to the sorted set fileWritersSet.insert(list2.begin(), list2.end()); } //write back to allWriters to return a list allWriters.clear(); allWriters.insert(allWriters.end(), fileWritersSet.begin(), fileWritersSet.end()); return allWriters; } void mitk::CoreObjectFactory::MapEvent(const mitk::Event*, const int) { } std::string mitk::CoreObjectFactory::GetDescriptionForExtension(const std::string& extension) { std::multimap fileExtensionMap = GetSaveFileExtensionsMap(); - for(std::multimap::iterator it = fileExtensionMap.begin(); it != fileExtensionMap.end(); it++) + for(std::multimap::iterator it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) if (it->first == extension) return it->second; return ""; // If no matching extension was found, return emtpy string } void mitk::CoreObjectFactory::RegisterLegacyReaders(mitk::CoreObjectFactoryBase* factory) { // We are not really interested in the string, just call the method since // many readers initialize the map the first time when this method is called factory->GetFileExtensions(); std::map > extensionsByCategories; std::multimap fileExtensionMap = factory->GetFileExtensionsMap(); - for(std::multimap::iterator it = fileExtensionMap.begin(); it != fileExtensionMap.end(); it++) + for(std::multimap::iterator it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) { std::string extension = it->first; // remove "*." extension = extension.erase(0,2); extensionsByCategories[it->second].push_back(extension); } for(auto & extensionsByCategorie : extensionsByCategories) { m_LegacyReaders[factory].push_back(new mitk::LegacyFileReaderService(extensionsByCategorie.second, extensionsByCategorie.first)); } } void mitk::CoreObjectFactory::UnRegisterLegacyReaders(mitk::CoreObjectFactoryBase* factory) { std::map >::iterator iter = m_LegacyReaders.find(factory); if (iter != m_LegacyReaders.end()) { for (auto & elem : iter->second) { delete elem; } m_LegacyReaders.erase(iter); } } void mitk::CoreObjectFactory::RegisterLegacyWriters(mitk::CoreObjectFactoryBase* factory) { // Get all external Writers mitk::CoreObjectFactory::FileWriterList writers = factory->GetFileWriters(); // We are not really interested in the string, just call the method since // many writers initialize the map the first time when this method is called factory->GetSaveFileExtensions(); MultimapType fileExtensionMap = factory->GetSaveFileExtensionsMap(); - for(mitk::CoreObjectFactory::FileWriterList::iterator it = writers.begin(); it != writers.end(); it++) + for(mitk::CoreObjectFactory::FileWriterList::iterator it = writers.begin(); it != writers.end(); ++it) { std::vector extensions = (*it)->GetPossibleFileExtensions(); if (extensions.empty()) continue; std::string description; - for(std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ext++) + for(std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ++ext) { if (ext->empty()) continue; std::string extension = *ext; std::string extensionWithStar = extension; if (extension.find_first_of('*') == 0) { // remove "*." extension = extension.substr(0, extension.size()-2); } else { extensionWithStar.insert(extensionWithStar.begin(), '*'); } for(MultimapType::iterator fileExtensionIter = fileExtensionMap.begin(); - fileExtensionIter != fileExtensionMap.end(); fileExtensionIter++) + fileExtensionIter != fileExtensionMap.end(); ++fileExtensionIter) { if (fileExtensionIter->first == extensionWithStar) { description = fileExtensionIter->second; break; } } if (!description.empty()) break; } if (description.empty()) { description = std::string("Legacy ") + (*it)->GetNameOfClass() + " Reader"; } mitk::FileWriter::Pointer fileWriter(it->GetPointer()); mitk::LegacyFileWriterService* lfws = new mitk::LegacyFileWriterService(fileWriter, description); m_LegacyWriters[factory].push_back(lfws); } } void mitk::CoreObjectFactory::UnRegisterLegacyWriters(mitk::CoreObjectFactoryBase* factory) { std::map >::iterator iter = m_LegacyWriters.find(factory); if (iter != m_LegacyWriters.end()) { for (auto & elem : iter->second) { delete elem; } m_LegacyWriters.erase(iter); } } diff --git a/Modules/Core/test/DirectOverlayTest.cpp b/Modules/Core/test/DirectOverlayTest.cpp index c75d64cacd..2b94b4b1b6 100644 --- a/Modules/Core/test/DirectOverlayTest.cpp +++ b/Modules/Core/test/DirectOverlayTest.cpp @@ -1,254 +1,254 @@ #include #include #include #include #include #include #include #include #include #include //#include class DirectOverlayTestClass { public: template static void InternalThreshold( const itk::Image* image, mitk::Image::Pointer& output, const double th[]) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::BinaryThresholdImageFilter< InputImageType, OutputImageType> BinaryThresholdFilterType; typename BinaryThresholdFilterType::Pointer thresholder = BinaryThresholdFilterType::New(); thresholder->SetInput(image); thresholder->SetLowerThreshold(th[0]); thresholder->SetUpperThreshold(th[1]); thresholder->SetInsideValue(255); thresholder->SetOutsideValue(0); thresholder->Update(); output = mitk::ImportItkImage( thresholder->GetOutput() ); // mitk::IOUtil::SaveImage( output, "/tmp/out.nii" ); std::cout << "extra line"; } template static void InternalThreshold2( const itk::Image* image, itk::Image< unsigned int, 3>::Pointer& output, const double th[]) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::BinaryThresholdImageFilter< InputImageType, OutputImageType> BinaryThresholdFilterType; typename BinaryThresholdFilterType::Pointer thresholder = BinaryThresholdFilterType::New(); thresholder->SetInput(image); thresholder->SetLowerThreshold(th[0]); thresholder->SetUpperThreshold(th[1]); thresholder->SetInsideValue(255); thresholder->SetOutsideValue(0); thresholder->Update(); output = thresholder->GetOutput(); } static void TestOverlay(mitk::Image::Pointer original, mitk::Image::Pointer truth, const double lower, const double upper) { mitk::Image::Pointer overlayImage; const double th[] = {lower, upper}; typedef itk::Image< unsigned int, 3> ImageType; ImageType::Pointer itkOverlayImage = ImageType::New(); AccessByItk_2(original, InternalThreshold, overlayImage, th); /* AccessFixedDimensionByItk_2( original, InternalThreshold2, 3, itkOverlayImage, th ); overlayImage = mitk::ImportItkImage( itkOverlayImage ); */ //mitk::IOUtil::SaveImage(truth, "/tmp/truth_TestOverlay.nii"); try { //mitk::Image::Pointer temp = overlayImage; mitk::IOUtil::SaveImage(overlayImage, "/tmp/overlayImage_TestOverlay.nrrd"); } catch( const itk::ExceptionObject& e) { MITK_ERROR << "Save image: exception : " << e.what(); } typedef itk::Image InputImageType; InputImageType::Pointer overlayItk; try { mitk::CastToItkImage(overlayImage, overlayItk); } catch( const mitk::Exception &e) { MITK_ERROR << "(CAST) Catched exception while creating accessor "<< e.what(); //MITK_TEST_FAILED_MSG("Exception for ouverlay image"); } /* typedef itk::ImageFileWriter< InputImageType > WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetFileName("/tmp/overlayITK_TestOverlay.nii"); writer->SetInput(overlayItk); writer->Update(); */ InputImageType::Pointer truthItk; mitk::CastToItkImage(truth, truthItk); bool difference = false; /* try { typedef unsigned int TPixel; itk::ImageRegionConstIteratorWithIndex< InputImageType > iter( truthItk, truthItk->GetLargestPossibleRegion() ); iter.GoToBegin(); mitk::ImagePixelReadAccessor< TPixel, 3 > readAccessor( overlayImage, overlayImage->GetVolumeData(0), mitk::ImageAccessorBase::ExceptionIfLocked ); while( !iter.IsAtEnd() ) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex( iter.GetIndex() ); difference |= ( ref != val ); //if( difference ) //{ std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; //} ++iter; } } catch( const mitk::Exception &e) { MITK_ERROR << "Catched exception while creating accessor "<< e.what(); //MITK_TEST_FAILED_MSG("Exception for ouverlay image"); } */ /* typedef itk::Testing::ComparisonImageFilter ComparisonImageFilterType; ComparisonImageFilterType::Pointer comp = ComparisonImageFilterType::New(); comp->SetValidInput(truthItk); comp->SetTestInput(overlayItk); try { comp->Update(); } catch( const itk::ExceptionObject& e) { MITK_ERROR << "ITK Exception: " << e.what(); } */ typedef unsigned int TPixel; itk::ImageRegionConstIteratorWithIndex< InputImageType > iter( truthItk, truthItk->GetLargestPossibleRegion() ); itk::ImageRegionConstIteratorWithIndex< InputImageType > iter2( overlayItk, overlayItk->GetLargestPossibleRegion() ); iter.GoToBegin(); unsigned int counter = 0; while( !iter.IsAtEnd() && !iter2.IsAtEnd() ) { TPixel ref = iter.Get(); TPixel val = iter2.Get(); if( ref != val ) { counter++; // std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; ++iter2; } std::cout << "Differs in " << counter << "voxels" << std::endl; MITK_TEST_CONDITION_REQUIRED( //comp->GetNumberOfPixelsWithDifferences() == 0, counter == 0, "Comparing overlay with ground truth") } static void TestDirectOverlay(char* in, char* gt, const int lower, const int upper) { mitk::Image::Pointer original = mitk::IOUtil::LoadImage(in); mitk::Image::Pointer truth = mitk::IOUtil::LoadImage(gt); if(original.IsNotNull() && original->GetDimension() == 3 && truth.IsNotNull() && truth->GetDimension() == 3 && upper > lower) { TestOverlay(original, truth, lower, upper); } else { MITK_TEST_FAILED_MSG(<<"Invalid parameters"); } } }; int DirectOverlayTest(int argc, char* argv[]) { MITK_TEST_BEGIN("DirectOverlay") MITK_TEST_CONDITION_REQUIRED(argc >= 5, "File to load has been specified on the command line"); unsigned int lower = 0, upper = 0; try { - sscanf( argv[3], "%d", &lower); - sscanf( argv[4], "%d", &upper); + sscanf( argv[3], "%u", &lower); + sscanf( argv[4], "%u", &upper); //lower = boost::lexical_cast(argv[3]); //upper = boost::lexical_cast(argv[4]); MITK_INFO << "Got values: " << lower << " : " << upper ; } catch(std::exception& e) { MITK_TEST_FAILED_MSG(< #include #include int mitkActionTest(int /*argc*/, char* /*argv*/[]) { int actionId = 10; //Create Action mitk::Action::Pointer action = mitk::Action::New(actionId); //check ActionID std::cout << "check ActionId"; if (action->GetActionId()!=actionId) { std::cout<<"[FAILED]"<AddProperty("boolproperty", mitk::BoolProperty::New(true)); action->AddProperty("intproperty", mitk::IntProperty::New(10)); action->AddProperty("floatproperty", mitk::FloatProperty::New(10.05)); std::cout << "try adding property BOOL and read them: "; - bool boolproperty = false; - boolproperty = dynamic_cast(action->GetProperty("boolproperty"))->GetValue(); + bool boolproperty = dynamic_cast(action->GetProperty("boolproperty"))->GetValue(); if (boolproperty != true) { std::cout<<"[FAILED]"<(action->GetProperty("intproperty"))->GetValue(); if (intproperty != 10) { std::cout<<"[FAILED]"<(action->GetProperty("floatproperty"))->GetValue(); + float floatproperty = dynamic_cast(action->GetProperty("floatproperty"))->GetValue(); if (floatproperty != 10.05f) { std::cout<<"[FAILED]"<Delete(); //well done!!! Passed! std::cout<<"[EVERYTHING PASSED]"< #include "mitkClippedSurfaceBoundsCalculator.h" #include "mitkGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkNumericTypes.h" #include static void CheckPlanesInsideBoundingBoxOnlyOnOneSlice(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //Check planes which are only on one slice: //Slice 0 mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceZero , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); //Slice 3 origin[2] = 3; mitk::PlaneGeometry::Pointer planeOnSliceThree = mitk::PlaneGeometry::New(); planeOnSliceThree->InitializePlane(origin, normal); planeOnSliceThree->SetImageGeometry(false); calculator->SetInput( planeOnSliceThree , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 3 && minMax.second == 3, "Check if plane is on slice 3"); //Slice 17 origin[2] = 17; mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceSeventeen , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 17 && minMax.second == 17, "Check if plane is on slice 17"); //Slice 20 origin[2] = 19; mitk::PlaneGeometry::Pointer planeOnSliceTwenty = mitk::PlaneGeometry::New(); planeOnSliceTwenty->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceTwenty , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 19 && minMax.second == 19, "Check if plane is on slice 19"); delete calculator; } static void CheckPlanesInsideBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are inside the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //Check planes which are only on one slice: //Slice 0 mitk::Point3D origin; origin[0] = 510; // Set to 511.9 so that the intersection point is inside the bounding box origin[1] = 0; origin[2] = 0; mitk::Vector3D normal; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeSagittalOne = mitk::PlaneGeometry::New(); planeSagittalOne->InitializePlane(origin, normal); calculator->SetInput( planeSagittalOne , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Slice 3 origin[0] = 256; MITK_INFO << "Case1 origin: " << origin; mitk::PlaneGeometry::Pointer planeSagittalTwo = mitk::PlaneGeometry::New(); planeSagittalTwo->InitializePlane(origin, normal); MITK_INFO << "PlaneNormal: " << planeSagittalTwo->GetNormal(); MITK_INFO << "PlaneOrigin: " << planeSagittalTwo->GetOrigin(); calculator->SetInput( planeSagittalTwo , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Slice 17 origin[0] = 0; // Set to 0.1 so that the intersection point is inside the bounding box mitk::PlaneGeometry::Pointer planeOnSliceSeventeen = mitk::PlaneGeometry::New(); planeOnSliceSeventeen->InitializePlane(origin, normal); calculator->SetInput( planeOnSliceSeventeen , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); //Crooked planes: origin[0] = 0; origin[1] = 507; origin[2] = 0; normal[0] = 1; normal[1] = -1; normal[2] = 1; mitk::PlaneGeometry::Pointer planeCrookedOne = mitk::PlaneGeometry::New(); planeCrookedOne->InitializePlane(origin, normal); calculator->SetInput( planeCrookedOne , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 5, "Check if plane is from slice 0 to slice 5 with inclined plane"); origin[0] = 512; origin[1] = 0; origin[2] = 16; mitk::PlaneGeometry::Pointer planeCrookedTwo = mitk::PlaneGeometry::New(); planeCrookedTwo->InitializePlane(origin, normal); calculator->SetInput( planeCrookedTwo , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 16 && minMax.second == 19, "Check if plane is from slice 16 to slice 19 with inclined plane"); origin[0] = 510; origin[1] = 0; origin[2] = 0; normal[1] = 0; normal[2] = 0.04; mitk::PlaneGeometry::Pointer planeCrookedThree = mitk::PlaneGeometry::New(); planeCrookedThree->InitializePlane(origin, normal); calculator->SetInput( planeCrookedThree , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19 with inclined plane"); delete calculator; } static void CheckPlanesOutsideOfBoundingBox(mitk::BaseGeometry::Pointer geometry3D) { //Check planes which are outside of the bounding box mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); //In front of the bounding box mitk::Point3D origin; origin[0] = 510; origin[1] = 0; origin[2] = -5; mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); mitk::PlaneGeometry::Pointer planeInFront = mitk::PlaneGeometry::New(); planeInFront->InitializePlane(origin, normal); calculator->SetInput( planeInFront , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Behind the bounding box origin[2] = 515; mitk::PlaneGeometry::Pointer planeBehind = mitk::PlaneGeometry::New(); planeBehind->InitializePlane(origin, normal); calculator->SetInput( planeBehind , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Above origin[1] = 515; mitk::FillVector3D(normal, 0, 1, 0); mitk::PlaneGeometry::Pointer planeAbove = mitk::PlaneGeometry::New(); planeAbove->InitializePlane(origin, normal); calculator->SetInput( planeAbove , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Below origin[1] = -5; mitk::PlaneGeometry::Pointer planeBelow = mitk::PlaneGeometry::New(); planeBelow->InitializePlane(origin, normal); calculator->SetInput( planeBelow , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Left side origin[0] = -5; mitk::FillVector3D(normal, 1, 0, 0); mitk::PlaneGeometry::Pointer planeLeftSide = mitk::PlaneGeometry::New(); planeLeftSide->InitializePlane(origin, normal); calculator->SetInput( planeLeftSide , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); //Right side origin[1] = 515; mitk::PlaneGeometry::Pointer planeRightSide = mitk::PlaneGeometry::New(); planeRightSide->InitializePlane(origin, normal); calculator->SetInput( planeRightSide , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == std::numeric_limits::max(), "Check if min value hasn't been set"); MITK_TEST_CONDITION(minMax.second == std::numeric_limits::min(), "Check if max value hasn't been set"); delete calculator; } static void CheckIntersectionPointsOfTwoGeometry3D(mitk::BaseGeometry::Pointer firstGeometry3D, mitk::BaseGeometry::Pointer secondGeometry3D) { mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer firstImage = mitk::Image::New(); firstImage->Initialize( mitk::MakePixelType(), *(firstGeometry3D.GetPointer()) ); calculator->SetInput( secondGeometry3D, firstImage); calculator->Update(); - mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); - minMax = calculator->GetMinMaxSpatialDirectionZ(); + auto minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 19, "Check if plane is from slice 0 to slice 19"); delete calculator; } static void CheckIntersectionWithPointCloud( mitk::BaseGeometry::Pointer geometry3D ) { //Check planes which are inside the bounding box mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(geometry3D.GetPointer()) ); { mitk::Point3D pnt1, pnt2; pnt1[0] = 3; pnt1[1] = 5; pnt1[2] = 3; pnt2[0] = 8; pnt2[1] = 3; pnt2[2] = 8; mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back( pnt1 ); pointlist.push_back( pnt2 ); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput( pointlist, image ); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 3 && minMaxZ.second == 8, "Check if points span from slice 3 to slice 8 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 3 && minMaxX.second == 5, "Check if points span from slice 3 to slice 5 in sagittal"); } { mitk::Point3D pnt1, pnt2; pnt1.Fill( -3 ); pnt2.Fill( 600 ); mitk::ClippedSurfaceBoundsCalculator::PointListType pointlist; pointlist.push_back( pnt1 ); pointlist.push_back( pnt2 ); mitk::ClippedSurfaceBoundsCalculator calculator; calculator.SetInput( pointlist, image ); calculator.Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxZ = calculator.GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMaxZ.first == 0 && minMaxZ.second == 19, "Check if points are correctly clipped to slice 0 and slice 19 in axial"); mitk::ClippedSurfaceBoundsCalculator::OutputType minMaxX = calculator.GetMinMaxSpatialDirectionX(); MITK_TEST_CONDITION(minMaxX.first == 0 && minMaxX.second == 511, "Check if points are correctly clipped to slice 0 and slice 511 in sagittal"); } } static void CheckIntersectionWithRotatedGeometry() { //Define origin for second Geometry3D; mitk::Point3D origin3; origin3[0] = -45.25; origin3[1] = -113.22; origin3[2] = 0.62; //Define normal: mitk::Vector3D normal3; normal3[0] = -0.02; normal3[1] = -0.18; normal3[2] = 2.99; mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 1.43; spacing[2] = 3.0; //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New(); planeGeometry3->InitializePlane(origin3, normal3); planeGeometry3->SetSpacing(spacing); mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds(); moreBounds[0] = 0; moreBounds[1] = 64; moreBounds[2] = 0; moreBounds[3] = 140; moreBounds[4] = 0; moreBounds[5] = 1; planeGeometry3->SetBounds(moreBounds); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New(); rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry3.GetPointer()), 25); rotatedSlicedGeometry3D->SetImageGeometry( true ); mitk::AffineTransform3D* geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix; matrix[0][0] = 1.43; matrix[0][1] = -0.01; matrix[0][2] = -0.02; matrix[0][0] = 0.01; matrix[1][1] = 1.43; matrix[2][2] = -0.18; matrix[0][0] = 0.01; matrix[1][1] = 0.08; matrix[2][2] = 2.99; geom->SetMatrix(matrix); mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer()) ); //Check planes which are only on one slice: { //last Slice mitk::Point3D origin; origin[0] = -47.79; origin[1] = 81.41; origin[2] = 84.34; mitk::Vector3D normal; mitk::FillVector3D(normal, 0.02, 0.18, -2.99); mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 1.43; spacing[2] = 3.0; mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); planeOnSliceZero->SetSpacing(spacing); calculator->SetInput( planeOnSliceZero , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 24 && minMax.second == 24, "Check if plane is on slice 24"); } { //Slice 0-24 mitk::Point3D origin; origin[0] = -45; origin[1] = -105; origin[2] = 35; mitk::Vector3D normal; mitk::FillVector3D(normal, 1.43, 0.01, 0.01 ); mitk::Vector3D spacing; spacing[0] = 1.43; spacing[1] = 3.0; spacing[2] = 1.43; mitk::PlaneGeometry::Pointer planeOnSliceZero = mitk::PlaneGeometry::New(); planeOnSliceZero->InitializePlane(origin, normal); planeOnSliceZero->SetSpacing(spacing); calculator->SetInput( planeOnSliceZero , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionZ(); MITK_TEST_CONDITION(minMax.first != minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 24, "Check if plane is on slices 0-24"); } delete calculator; } static void CheckIntersectionWithRotatedGeometry90() { //Define origin for second Geometry3D; mitk::Point3D origin3; origin3[0] = 0.0; origin3[1] = 0.0; origin3[2] = 0.0; //Define normal: mitk::Vector3D normal3; normal3[0] = 0; normal3[1] = 0; normal3[2] = 1; mitk::Vector3D spacing; spacing[0] = 1.0; spacing[1] = 2.0; spacing[2] = 1.0; //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry3 = mitk::PlaneGeometry::New(); planeGeometry3->InitializePlane(origin3, normal3); planeGeometry3->SetSpacing(spacing); mitk::BoundingBox::BoundsArrayType moreBounds = planeGeometry3->GetBounds(); moreBounds[0] = 0; moreBounds[1] = 50; moreBounds[2] = 0; moreBounds[3] = 50; moreBounds[4] = 0; moreBounds[5] = 1; planeGeometry3->SetBounds(moreBounds); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer rotatedSlicedGeometry3D = mitk::SlicedGeometry3D::New(); rotatedSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry3.GetPointer()), 50); rotatedSlicedGeometry3D->SetImageGeometry( true ); // Set the rotation to zero (identity times spacing matrix) because the default makes a rotation mitk::AffineTransform3D* geom = rotatedSlicedGeometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix; matrix[0][0] = spacing[0]; matrix[0][1] = 0.0; matrix[0][2] = 0.0; matrix[1][0] = 0.0; matrix[1][1] = spacing[1]; matrix[1][2] = 0.0; matrix[2][0] = 0.0 ; matrix[2][1] = 0.0 ; matrix[2][2] = spacing[2] ; geom->SetMatrix(matrix); mitk::ClippedSurfaceBoundsCalculator* calculator = new mitk::ClippedSurfaceBoundsCalculator(); mitk::Image::Pointer image = mitk::Image::New(); image->Initialize( mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer()) ); // construct the planes in y direction { //first Slice in Y direction mitk::Point3D originFirstSlice; originFirstSlice[0] = 0.0; originFirstSlice[1] = -1.0; originFirstSlice[2] = 0.0; mitk::Vector3D normalFitrstSlice; mitk::FillVector3D(normalFitrstSlice, 0.0 , 1.0 , 0.0); mitk::Vector3D spacingFirstSlice; spacingFirstSlice[0] = 1.0; spacingFirstSlice[1] = 1.0; spacingFirstSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New(); planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice); planeOnFirstSlice->SetSpacing(spacingFirstSlice); //last Slice in Y Direction mitk::Point3D originLastSlice; originLastSlice[0] = 0.0; originLastSlice[1] = 99.0; originLastSlice[2] = 0.0; mitk::Vector3D normalLastSlice; mitk::FillVector3D(normalLastSlice, 0.0 , 1.0 , 0.0); mitk::Vector3D spacingLastSlice; spacingLastSlice[0] = 1.0; spacingLastSlice[1] = 1.0; spacingLastSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New(); planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice); planeOnLastSlice->SetSpacing(spacingLastSlice); // calculate the intersection on the last slice calculator->SetInput( planeOnLastSlice , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; // calculate the intersection on the first slice calculator->SetInput( planeOnFirstSlice , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; } // now we make a rotation of the Image about 270 degrees around Z Axis to get an aligment on the positiv x axis double angleInDegrees = 270 * M_PI / 180 ; mitk::AffineTransform3D::MatrixType matrix2; matrix2[0][0] = cos( angleInDegrees ); matrix2[0][1] = -sin( angleInDegrees ); matrix2[0][2] = 0.0; matrix2[1][0] = sin( angleInDegrees ); matrix2[1][1] = cos( angleInDegrees ); matrix2[1][2] = 0.0; matrix2[2][0] = 0.0 ; matrix2[2][1] = 0.0 ; matrix2[2][2] = 1.0 ; // multiplie the identity with the transformation matrix mitk::AffineTransform3D::MatrixType TransformationMatrix = matrix2 * matrix; // initialize the image with the new rotation Matrix geom->SetMatrix(TransformationMatrix); image->Initialize( mitk::MakePixelType(), *(rotatedSlicedGeometry3D.GetPointer()) ); { //first Slice in X Diection mitk::Point3D originFirstSlice; originFirstSlice[0] = -1.0; originFirstSlice[1] = 0.0; originFirstSlice[2] = 0.0; mitk::Vector3D normalFitrstSlice; mitk::FillVector3D(normalFitrstSlice, 1.0 , 0.0 , 0.0); mitk::Vector3D spacingFirstSlice; spacingFirstSlice[0] = 1.0; spacingFirstSlice[1] = 1.0; spacingFirstSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnFirstSlice = mitk::PlaneGeometry::New(); planeOnFirstSlice->InitializePlane(originFirstSlice, normalFitrstSlice); planeOnFirstSlice->SetSpacing(spacingFirstSlice); //last Slice in X Direction mitk::Point3D originLastSlice; originLastSlice[0] = 99.0; originLastSlice[1] = 0.0; originLastSlice[2] = 0.0; mitk::Vector3D normalLastSlice; mitk::FillVector3D(normalLastSlice, 1.0 , 0.0 , 0.0); mitk::Vector3D spacingLastSlice; spacingLastSlice[0] = 1.0; spacingLastSlice[1] = 1.0; spacingLastSlice[2] = 1.0; mitk::PlaneGeometry::Pointer planeOnLastSlice = mitk::PlaneGeometry::New(); planeOnLastSlice->InitializePlane(originLastSlice, normalLastSlice); planeOnLastSlice->SetSpacing(spacingLastSlice); calculator->SetInput( planeOnLastSlice , image); calculator->Update(); mitk::ClippedSurfaceBoundsCalculator::OutputType minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 49 && minMax.second == 49, "Check if plane is on slice 49"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; calculator->SetInput( planeOnFirstSlice , image); calculator->Update(); minMax = calculator->GetMinMaxSpatialDirectionY(); MITK_TEST_CONDITION(minMax.first == minMax.second, "Check if plane is only on one slice"); MITK_TEST_CONDITION(minMax.first == 0 && minMax.second == 0, "Check if plane is on slice 0"); MITK_INFO << "min: " << minMax.first << " max: " << minMax.second; } delete calculator; } int mitkClippedSurfaceBoundsCalculatorTest(int, char* []) { // always start with this! MITK_TEST_BEGIN("ClippedSurfaceBoundsCalculator"); /** The class mitkClippedSurfaceBoundsCalculator calculates the intersection points of a PlaneGeometry and a Geometry3D. * This unit test checks if the correct min and max values for the three spatial directions (x, y, z) * are calculated. To test this we define artificial PlaneGeometries and Geometry3Ds and test different * scenarios: * * 1. planes which are inside the bounding box of a 3D geometry but only on one slice * 2. planes which are outside of the bounding box * 3. planes which are inside the bounding box but over more than one slice * * Note: Currently rotated geometries are not tested! */ /********************* Define Geometry3D ***********************/ //Define origin: mitk::Point3D origin; origin[0] = 511; origin[1] = 0; origin[2] = 0; //Define normal: mitk::Vector3D normal; mitk::FillVector3D(normal, 0, 0, 1); //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializePlane(origin, normal); //Set Bounds: mitk::BoundingBox::BoundsArrayType bounds = planeGeometry->GetBounds(); bounds[0] = 0; bounds[1] = 512; bounds[2] = 0; bounds[3] = 512; bounds[4] = 0; bounds[5] = 1; planeGeometry->SetBounds(bounds); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer slicedGeometry3D = mitk::SlicedGeometry3D::New(); slicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry.GetPointer()), 20); mitk::BaseGeometry::Pointer geometry3D = dynamic_cast< mitk::BaseGeometry* > ( slicedGeometry3D.GetPointer() ); geometry3D->SetImageGeometry( true ); //Define origin for second Geometry3D; mitk::Point3D origin2; origin2[0] = 511; origin2[1] = 60; origin2[2] = 0; //Define normal: mitk::Vector3D normal2; mitk::FillVector3D(normal2, 0, 1, 0); //Initialize PlaneGeometry: mitk::PlaneGeometry::Pointer planeGeometry2 = mitk::PlaneGeometry::New(); planeGeometry2->InitializePlane(origin2, normal2); //Initialize SlicedGeometry3D: mitk::SlicedGeometry3D::Pointer secondSlicedGeometry3D = mitk::SlicedGeometry3D::New(); secondSlicedGeometry3D->InitializeEvenlySpaced(dynamic_cast(planeGeometry2.GetPointer()), 20); mitk::BaseGeometry::Pointer secondGeometry3D = dynamic_cast< mitk::BaseGeometry* > ( secondSlicedGeometry3D.GetPointer() ); secondGeometry3D->SetImageGeometry( true ); /***************************************************************/ CheckPlanesInsideBoundingBoxOnlyOnOneSlice(geometry3D); CheckPlanesOutsideOfBoundingBox(geometry3D); CheckPlanesInsideBoundingBox(geometry3D); CheckIntersectionPointsOfTwoGeometry3D(geometry3D, secondGeometry3D); CheckIntersectionWithPointCloud( geometry3D ); CheckIntersectionWithRotatedGeometry(); CheckIntersectionWithRotatedGeometry90(); /** ToDo: * test also rotated 3D geometry! */ MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkDICOMLocaleTest.cpp b/Modules/Core/test/mitkDICOMLocaleTest.cpp index 6b514da15a..6b45c6c7dd 100644 --- a/Modules/Core/test/mitkDICOMLocaleTest.cpp +++ b/Modules/Core/test/mitkDICOMLocaleTest.cpp @@ -1,141 +1,141 @@ /*=================================================================== 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. ===================================================================*/ /* This test is meant to reproduce the following error: - The machine or current user has a German locale. - This esp. means that stream IO expects the decimal separator as a comma: "," - DICOM files use a point "." as the decimal separator to be locale independent - The parser used by MITK (ITK's GDCM) seems to use the current locale instead of the "C" or "POSIX" locale - This leads to spacings (and probably other numbers) being trimmed/rounded, e.g. the correct spacing of 0.314 is read as 1.0 etc. */ #include "mitkStandardFileLocations.h" #include "mitkDicomSeriesReader.h" #include "mitkIOUtil.h" #include "mitkImage.h" #include "mitkTestingMacros.h" #include "mitkTestFixture.h" #include #include #include class mitkDICOMLocaleTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkDICOMLocaleTestSuite); CPPUNIT_TEST_SUITE_ADD_CUSTOM_TESTS(addDICOMLocaleWithReferenceImageTests); CPPUNIT_TEST_SUITE_END(); private: // A custom method for adding a combination of filename and locale tests static void addDICOMLocaleWithReferenceImageTests(TestSuiteBuilderContextType& context) { std::vector fileArgs; fileArgs.push_back("spacing-ok-ct.dcm"); fileArgs.push_back("spacing-ok-mr.dcm"); fileArgs.push_back("spacing-ok-sc.dcm"); // load a reference DICOM file with German locales being set std::vector localeArgs; localeArgs.push_back("C"); localeArgs.push_back("de_DE"); localeArgs.push_back("de_DE.utf8"); localeArgs.push_back("de_DE.UTF8"); localeArgs.push_back("de_DE@euro"); localeArgs.push_back("German_Germany"); for (std::size_t fileIndex = 0; fileIndex < fileArgs.size(); ++fileIndex) { for (std::size_t localeIndex = 0; localeIndex < localeArgs.size(); ++localeIndex) { MITK_PARAMETERIZED_TEST_2(testLocaleWithReferenceImage, fileArgs[fileIndex], localeArgs[localeIndex]); } } } private: std::string m_FileName; std::string m_Locale; bool m_SkipImageTest; char* m_OldLocale; void SetTestParameter() { std::vector parameter = GetTestParameter(); CPPUNIT_ASSERT(parameter.size() == 2); m_FileName = GetTestDataFilePath(parameter[0]); m_Locale = parameter[1]; } public: - mitkDICOMLocaleTestSuite() : m_OldLocale(NULL) {} + mitkDICOMLocaleTestSuite() : m_OldLocale(NULL), m_SkipImageTest(false) {} // Change the current locale to m_Locale void setUp() override { m_SkipImageTest = false; m_OldLocale = NULL; SetTestParameter(); try { m_OldLocale = setlocale(LC_ALL, NULL); MITK_TEST_OUTPUT(<< " ** Changing locale from " << m_OldLocale << " to '" << m_Locale << "'") setlocale(LC_ALL, m_Locale.c_str()); std::cin.imbue(std::locale(m_Locale.c_str())); } catch(...) { MITK_TEST_OUTPUT(<< "Could not activate locale " << m_Locale) m_SkipImageTest = true; } } void tearDown() override { if (m_OldLocale) { setlocale(LC_ALL, m_OldLocale); std::cin.imbue(std::locale(m_OldLocale)); } } void testLocaleWithReferenceImage() { if (m_SkipImageTest) return; mitk::Image::Pointer image = mitk::IOUtil::LoadImage(m_FileName); CPPUNIT_ASSERT(image.IsNotNull()); // note importance of minor differences in spacings: // DICOM has order y-spacing, x-spacing, while in MITK we assume x-spacing, y-spacing (both meant for 0 and 1 index in array) CPPUNIT_ASSERT_MESSAGE("incorrect x spacing", mitk::Equal(image->GetGeometry()->GetSpacing()[0], 0.3141592)); CPPUNIT_ASSERT_MESSAGE("incorrect y spacing ", mitk::Equal(image->GetGeometry()->GetSpacing()[1], 0.3411592)); } }; MITK_TEST_SUITE_REGISTRATION(mitkDICOMLocale) diff --git a/Modules/Core/test/mitkExceptionTest.cpp b/Modules/Core/test/mitkExceptionTest.cpp index 541fe5b111..78a94a1b44 100644 --- a/Modules/Core/test/mitkExceptionTest.cpp +++ b/Modules/Core/test/mitkExceptionTest.cpp @@ -1,324 +1,324 @@ /*=================================================================== 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 "mitkExceptionMacro.h" #include "mitkTestingMacros.h" #include #include #include class SpecializedTestException : public mitk::Exception { public: mitkExceptionClassMacro(SpecializedTestException,mitk::Exception); }; class ExceptionTestClass : public itk::Object { public: mitkClassMacroItkParent( ExceptionTestClass , itk::Object ); itkFactorylessNewMacro(Self) itkCloneMacro(Self) void throwExceptionManually() //this method is ONLY to test the constructor and no code example //normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("test.cpp",155,"",""); } void throwSpecializedExceptionManually() //this method is ONLY to test the constructor and no code example //normally exceptions should only be thrown by using the exception macro! { throw SpecializedTestException("test.cpp",155,"",""); } void throwExceptionManually(std::string message1, std::string message2) //this method is ONLY to test methods of mitk::Exception and no code example //normally exceptions should only be thrown by using the exception macro! { throw mitk::Exception("testfile.cpp",155,message1.c_str(),"") << message2; } void throwExceptionWithThrowMacro() { mitkThrow()<<"TEST EXCEPION THROWING WITH mitkThrow()"; } void throwExceptionWithThrowMacro(std::string message) { mitkThrow()<throwExceptionManually(); } catch(mitk::Exception) { exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing constructor of mitkException"); exceptionThrown = false; try { myExceptionTestObject->throwSpecializedExceptionManually(); } catch(SpecializedTestException) { exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing constructor specialized exception (deriving from mitkException)"); } static void TestExceptionMessageStream() { //##### this method is ONLY to test the streaming operators of the exceptions and //##### NO code example. Please do not instantiate exceptions by yourself in normal code! //##### Normally exceptions should only be thrown by using the exception macro! mitk::Exception myException = mitk::Exception("testfile.cpp",111,"testmessage"); myException << " and additional stream"; MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() == std::string("testmessage and additional stream"),"Testing mitkException message stream (adding std::string)"); myException.SetDescription("testmessage2"); myException << ' ' << 'a' << 'n' << 'd' << ' ' << 'c' << 'h' << 'a' << 'r' << 's'; MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() == std::string("testmessage2 and chars"),"Testing mitkException message stream (adding single chars)"); myException.SetDescription("testmessage3"); myException << myException; //adding the object itself makes no sense but should work MITK_TEST_CONDITION_REQUIRED(myException.GetDescription() != std::string(""),"Testing mitkException message stream (adding object)"); SpecializedTestException mySpecializedException = SpecializedTestException("testfile.cpp",111,"testmessage","test"); mySpecializedException << " and additional stream"; MITK_TEST_CONDITION_REQUIRED(mySpecializedException.GetDescription() == std::string("testmessage and additional stream"),"Testing specialized exception message stream (adding std::string)"); } static void TestExceptionMessageStreamThrowing() { bool exceptionThrown = false; ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); std::string thrownMessage = ""; try { myExceptionTestObject->throwExceptionManually("message1"," and message2"); } - catch(mitk::Exception e) + catch(mitk::Exception &e) { thrownMessage = e.GetDescription(); exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown && (thrownMessage == std::string("message1 and message2")),"Testing throwing and streaming of mitk::Exception together.") } static void TestMitkThrowMacro() { bool exceptionThrown = false; ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); //case 1: test throwing try { myExceptionTestObject->throwExceptionWithThrowMacro(); } catch(mitk::Exception) { exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing mitkThrow()"); //case 2: test message text exceptionThrown = false; std::string messageText = ""; try { myExceptionTestObject->throwExceptionWithThrowMacro("test123"); } - catch(mitk::Exception e) + catch(mitk::Exception &e) { exceptionThrown = true; messageText = e.GetDescription(); } MITK_TEST_CONDITION_REQUIRED((exceptionThrown && (messageText=="test123")),"Testing message test of mitkThrow()"); //case 3: specialized exception / command mitkThrow(mitk::Exception) exceptionThrown = false; messageText = ""; try { myExceptionTestObject->throwSpecializedExceptionWithThrowMacro("test123"); } - catch(mitk::Exception e) + catch(mitk::Exception &e) { exceptionThrown = true; messageText = e.GetDescription(); } MITK_TEST_CONDITION_REQUIRED(exceptionThrown && messageText=="test123","Testing special exception with mitkThrow(mitk::Exception)"); //case 4: specialized exception / command mitkThrow(mitk::SpecializedException) exceptionThrown = false; messageText = ""; try { myExceptionTestObject->throwSpecializedExceptionWithThrowMacro2("test123"); } - catch(SpecializedTestException e) + catch(SpecializedTestException &e) { exceptionThrown = true; messageText = e.GetDescription(); } MITK_TEST_CONDITION_REQUIRED(exceptionThrown && messageText=="test123","Testing special exception with mitkThrow(mitk::SpecializedException)"); } static void TestRethrowInformation() //this method is ONLY to test methods of mitk::Exception and no code example //normally exceptions should only be instantiated and thrown by using the exception macros! { //first: testing rethrow information methods, when no information is stored //case 1.1: method GetNumberOfRethrows() mitk::Exception e = mitk::Exception("test.cpp",155,"",""); MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows()==0,"Testing GetNumberOfRethrows() with empty rethrow information"); //case 1.2: GetRethrowData() with negative number { std::string file = "invalid"; int line = -1; std::string message = "invalid"; e.GetRethrowData(-1,file,line,message); MITK_TEST_CONDITION_REQUIRED(((file == "")&&(line==0)&&(message == "")),"Testing GetRethrowData() with invalid rethrow number (negative)."); } //case 1.3: GetRethrowData() with number 0 { std::string file = "invalid"; int line= -1; std::string message = "invalid"; e.GetRethrowData(0,file,line,message); MITK_TEST_CONDITION_REQUIRED(((file == "")&&(line==0)&&(message == "")),"Testing GetRethrowData() with non-existing rethrow number (0)."); } //case 1.4: GetRethrowData() with number 1 { std::string file = "invalid"; int line= -1; std::string message = "invalid"; e.GetRethrowData(1,file,line,message); MITK_TEST_CONDITION_REQUIRED(((file == "")&&(line==0)&&(message == "")),"Testing GetRethrowData() with non-existing rethrow number (1)."); } //second: add rethrow data e.AddRethrowData("test2.cpp",10,"Rethrow one"); MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows()==1,"Testing adding of rethrow data."); e.AddRethrowData("test3.cpp",15,"Rethrow two"); MITK_TEST_CONDITION_REQUIRED(e.GetNumberOfRethrows()==2,"Testing adding of more rethrow data."); //third: test if this rethrow data was stored properly { std::string file = "invalid"; int line= -1; std::string message = "invalid"; e.GetRethrowData(0,file,line,message); MITK_TEST_CONDITION_REQUIRED(((file == "test2.cpp")&&(line==10)&&(message == "Rethrow one")),"Testing stored information of first rethrow."); } { std::string file = "invalid"; int line= -1; std::string message = "invalid"; e.GetRethrowData(1,file,line,message); MITK_TEST_CONDITION_REQUIRED(((file == "test3.cpp")&&(line==15)&&(message == "Rethrow two")),"Testing stored information of second rethrow."); } } static void TestRethrowMacro() { bool exceptionThrown = false; std::string message = ""; ExceptionTestClass::Pointer myExceptionTestObject = ExceptionTestClass::New(); //case 1: test throwing try { myExceptionTestObject->reThrowExceptionWithReThrowMacro("Test original message.","Test rethrow message."); } - catch(mitk::Exception e) + catch(mitk::Exception &e) { message = e.GetDescription(); exceptionThrown = true; } MITK_TEST_CONDITION_REQUIRED(exceptionThrown,"Testing mitkReThrow()"); MITK_TEST_CONDITION_REQUIRED(message == "Test original message.Test rethrow message.", "Testing message/descriprion after rethrow.") } }; int mitkExceptionTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("MITKException"); ExceptionTestClass::TestExceptionConstructor(); ExceptionTestClass::TestExceptionMessageStream(); ExceptionTestClass::TestExceptionMessageStreamThrowing(); ExceptionTestClass::TestMitkThrowMacro(); ExceptionTestClass::TestRethrowInformation(); ExceptionTestClass::TestRethrowMacro(); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkExtractSliceFilterTest.cpp b/Modules/Core/test/mitkExtractSliceFilterTest.cpp index 5f4be034e0..97f094e37d 100644 --- a/Modules/Core/test/mitkExtractSliceFilterTest.cpp +++ b/Modules/Core/test/mitkExtractSliceFilterTest.cpp @@ -1,1158 +1,1151 @@ /*=================================================================== 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //use this to create the test volume on the fly #define CREATE_VOLUME //use this to save the created volume //#define SAVE_VOLUME //use this to calculate the error from the sphere mathematical model to our pixel based one //#define CALC_TESTFAILURE_DEVIATION //use this to render an oblique slice through a specified image //#define SHOW_SLICE_IN_RENDER_WINDOW //use this to have infos printed in mbilog //#define EXTRACTOR_DEBUG /*these are the deviations calculated by the function CalcTestFailureDeviation (see for details)*/ #define Testfailure_Deviation_Mean_128 0.853842 #define Testfailure_Deviation_Volume_128 0.145184 #define Testfailure_Deviation_Diameter_128 1.5625 #define Testfailure_Deviation_Mean_256 0.397693 #define Testfailure_Deviation_Volume_256 0.0141357 #define Testfailure_Deviation_Diameter_256 0.78125 #define Testfailure_Deviation_Mean_512 0.205277 #define Testfailure_Deviation_Volume_512 0.01993 #define Testfailure_Deviation_Diameter_512 0.390625 class mitkExtractSliceFilterTestClass{ public: static void TestSlice(mitk::PlaneGeometry* planeGeometry, std::string testname) { TestPlane = planeGeometry; TestName = testname; mitk::ScalarType centerCoordValue = TestvolumeSize / 2.0; mitk::ScalarType center[3] = {centerCoordValue, centerCoordValue, centerCoordValue}; mitk::Point3D centerIndex(center); double radius = TestvolumeSize / 4.0; if(TestPlane->Distance(centerIndex) >= radius ) return;//outside sphere //feed ExtractSliceFilter mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(TestVolume); slicer->SetWorldGeometry(TestPlane); slicer->Update(); MITK_TEST_CONDITION_REQUIRED(slicer->GetOutput() != NULL, "Extractor returned a slice"); mitk::Image::Pointer reslicedImage = slicer->GetOutput(); AccessFixedDimensionByItk(reslicedImage, TestSphereRadiusByItk, 2); AccessFixedDimensionByItk(reslicedImage, TestSphereAreaByItk, 2); /* double devArea, devDiameter; if(TestvolumeSize == 128.0){ devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128; } else if(TestvolumeSize == 256.0){devArea = Testfailure_Deviation_Volume_256; devDiameter = Testfailure_Deviation_Diameter_256;} else if (TestvolumeSize == 512.0){devArea = Testfailure_Deviation_Volume_512; devDiameter = Testfailure_Deviation_Diameter_512;} else{devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128;} */ std::string areatestName = TestName.append(" area"); std::string diametertestName = TestName.append(" testing diameter"); //TODO think about the deviation, 1% makes no sense at all MITK_TEST_CONDITION(std::abs(100 - testResults.percentageAreaCalcToPixel) < 1, areatestName ); MITK_TEST_CONDITION(std::abs(100 - testResults.percentageRadiusToPixel) < 1, diametertestName ); #ifdef EXTRACTOR_DEBUG MITK_INFO << TestName << " >>> " << "planeDistanceToSphereCenter: " << testResults.planeDistanceToSphereCenter; MITK_INFO << "area in pixels: " << testResults.areaInPixel << " <-> area in mm: " << testResults.areaCalculated << " = " << testResults.percentageAreaCalcToPixel << "%"; MITK_INFO << "calculated diameter: " << testResults.diameterCalculated << " <-> diameter in mm: " << testResults.diameterInMM << " <-> diameter in pixel: " << testResults.diameterInPixel << " = " << testResults.percentageRadiusToPixel << "%"; #endif } /* * get the radius of the slice of a sphere based on pixel distance from edge to edge of the circle. */ template static void TestSphereRadiusByItk (itk::Image* inputImage) { typedef itk::Image InputImageType; //set the index to the middle of the image's edge at x and y axis typename InputImageType::IndexType currentIndexX; currentIndexX[0] = (int)(TestvolumeSize / 2.0); currentIndexX[1] = 0; typename InputImageType::IndexType currentIndexY; currentIndexY[0] = 0; currentIndexY[1] = (int)(TestvolumeSize / 2.0); //remember the last pixel value double lastValueX = inputImage->GetPixel(currentIndexX); double lastValueY = inputImage->GetPixel(currentIndexY); //storage for the index marks std::vector indicesX; std::vector indicesY; /*Get four indices on the edge of the circle*/ while(currentIndexX[1] < TestvolumeSize && currentIndexX[0] < TestvolumeSize) { //move x direction currentIndexX[1] += 1; //move y direction currentIndexY[0] += 1; if(inputImage->GetPixel(currentIndexX) > lastValueX) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1]; indicesX.push_back(markIndex); } else if( inputImage->GetPixel(currentIndexX) < lastValueX) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1] - 1;//value inside the sphere indicesX.push_back(markIndex); } if(inputImage->GetPixel(currentIndexY) > lastValueY) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1]; indicesY.push_back(markIndex); } else if( inputImage->GetPixel(currentIndexY) < lastValueY) { //mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1] - 1;//value inside the sphere indicesY.push_back(markIndex); } //found both marks? if(indicesX.size() == 2 && indicesY.size() == 2) break; //the new 'last' values lastValueX = inputImage->GetPixel(currentIndexX); lastValueY = inputImage->GetPixel(currentIndexY); } /* *If we are here we found the four marks on the edge of the circle. *For the case our plane is rotated and shifted, we have to calculate the center of the circle, *else the center is the intersection of both straight lines between the marks. *When we have the center, the diameter of the circle will be checked to the reference value(math!). */ //each distance from the first mark of each direction to the center of the straight line between the marks double distanceToCenterX = std::abs(indicesX[0][1] - indicesX[1][1]) / 2.0; //double distanceToCenterY = std::abs(indicesY[0][0] - indicesY[1][0]) / 2.0; //the center of the straight lines typename InputImageType::IndexType centerX; //typename InputImageType::IndexType centerY; centerX[0] = indicesX[0][0]; centerX[1] = indicesX[0][1] + distanceToCenterX; //TODO think about implicit cast to int. this is not the real center of the image, which could be between two pixels //centerY[0] = indicesY[0][0] + distanceToCenterY; //centerY[1] = inidcesY[0][1]; typename InputImageType::IndexType currentIndex(centerX); lastValueX = inputImage->GetPixel(currentIndex); long sumpixels = 0; std::vector diameterIndices; //move up while(currentIndex[1] < TestvolumeSize) { currentIndex[1] += 1; if( inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1] - 1; diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } currentIndex[1] -= sumpixels; //move back to center to go in the other direction lastValueX = inputImage->GetPixel(currentIndex); //move down while(currentIndex[1] >= 0) { currentIndex[1] -= 1; if( inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1];//outside sphere because we want to calculate the distance from edge to edge diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } /* *Now sumpixels should be the apromximate diameter of the circle. This is checked with the calculated diameter from the plane transformation(math). */ mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize/4.0; //calculate the radius of the circle cut from the sphere by the plane double diameter = 2.0 * std::sqrt(std::pow(sphereRadius, 2) - std::pow( planeDistanceToSphereCenter , 2)); double percentageRadiusToPixel = 100 / diameter * sumpixels; /* *calculate the radius in mm by the both marks of the center line by using the world coordinates */ //get the points as 3D coordinates mitk::Vector3D diameterPointRight, diameterPointLeft; diameterPointRight[2] = diameterPointLeft[2] = 0.0; diameterPointLeft[0] = diameterIndices[0][0]; diameterPointLeft[1] = diameterIndices[0][1]; diameterPointRight[0] = diameterIndices[1][0]; diameterPointRight[1] = diameterIndices[1][1]; //transform to worldcoordinates TestVolume->GetGeometry()->IndexToWorld(diameterPointLeft, diameterPointLeft); TestVolume->GetGeometry()->IndexToWorld(diameterPointRight, diameterPointRight); //euklidian distance double diameterInMM = ( (diameterPointLeft * -1.0) + diameterPointRight).GetNorm(); testResults.diameterInMM = diameterInMM; testResults.diameterCalculated = diameter; testResults.diameterInPixel = sumpixels; testResults.percentageRadiusToPixel = percentageRadiusToPixel; testResults.planeDistanceToSphereCenter = planeDistanceToSphereCenter; } /*brute force the area pixel by pixel*/ template static void TestSphereAreaByItk (itk::Image* inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator< InputImageType > ImageIterator; ImageIterator imageIterator( inputImage, inputImage->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); int sumPixelsInArea = 0; while( !imageIterator.IsAtEnd() ) { if(inputImage->GetPixel(imageIterator.GetIndex()) == pixelValueSet) sumPixelsInArea++; ++imageIterator; } mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize/4.0; //calculate the radius of the circle cut from the sphere by the plane double radius = std::sqrt(std::pow(sphereRadius, 2) - std::pow( planeDistanceToSphereCenter , 2)); double areaInMM = 3.14159265358979 * std::pow(radius, 2); testResults.areaCalculated = areaInMM; testResults.areaInPixel = sumPixelsInArea; testResults.percentageAreaCalcToPixel = 100 / areaInMM * sumPixelsInArea; } /* * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel vaues of the voxel * in the volume with the pixel value in the resliced image. * there are some indice shifting problems which causes the test to fail for oblique planes. seems like the chosen * worldcoordinate is not corrresponding to the index in the 2D image. and so the pixel values are not the same as * expected. */ static void PixelvalueBasedTest() { /* setup itk image */ typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator< ImageType > ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = 32; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator( image, image->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); unsigned short pixelValue = 0; //fill the image with distinct values while ( !imageIterator.IsAtEnd() ) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer imageInMitk; CastToMitkImage(image, imageInMitk); /*mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(imageInMitk); std::string file = "C:\\Users\\schroedt\\Desktop\\cube.nrrd"; writer->SetFileName(file); writer->Update();*/ PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Frontal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Sagittal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Axial); } static void PixelvalueBasedTestByPlane(mitk::Image* imageInMitk, mitk::PlaneGeometry::PlaneOrientation orientation){ typedef itk::Image ImageType; //set the seed of the rand function srand((unsigned)time(0)); /* setup a random orthogonal plane */ int sliceindex = 17;//rand() % 32; bool isFrontside = true; bool isRotated = false; if( orientation == mitk::PlaneGeometry::Axial) { /*isFrontside = false; isRotated = true;*/ } mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(imageInMitk->GetGeometry(), orientation, sliceindex, isFrontside, isRotated); mitk::Point3D origin = plane->GetOrigin(); mitk::Vector3D normal; normal = plane->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 plane->SetOrigin(origin); //we dont need this any more, because we are only testing orthogonal planes /*mitk::Vector3D rotationVector; rotationVector[0] = randFloat(); rotationVector[1] = randFloat(); rotationVector[2] = randFloat(); float degree = randFloat() * 180.0; mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, plane->GetCenter(), rotationVector, degree); plane->ExecuteOperation(op); delete op;*/ /* end setup plane */ /* define a point in the 3D volume. * add the two axis vectors of the plane (each multiplied with a * random number) to the origin. now the two random numbers * become our index coordinates in the 2D image, because the * length of the axis vectors is 1. */ mitk::Point3D planeOrigin = plane->GetOrigin(); mitk::Vector3D axis0, axis1; axis0 = plane->GetAxisVector(0); axis1 = plane->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); unsigned char n1 = 7;// rand() % 32; unsigned char n2 = 13;// rand() % 32; mitk::Point3D testPoint3DInWorld; testPoint3DInWorld = planeOrigin + (axis0 * n1) + (axis1 * n2); //get the index of the point in the 3D volume ImageType::IndexType testPoint3DInIndex; imageInMitk->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint3DInIndex); itk::Index<3> testPoint2DInIndex; /* end define a point in the 3D volume.*/ //do reslicing at the plane mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(imageInMitk); slicer->SetWorldGeometry(plane); slicer->Update(); mitk::Image::Pointer slice = slicer->GetOutput(); // Get TestPoiont3D as Index in Slice slice->GetGeometry()->WorldToIndex(testPoint3DInWorld,testPoint2DInIndex); mitk::Point3D p, sliceIndexToWorld, imageIndexToWorld; p[0] = testPoint2DInIndex[0]; p[1] = testPoint2DInIndex[1]; p[2] = testPoint2DInIndex[2]; slice->GetGeometry()->IndexToWorld(p, sliceIndexToWorld); p[0] = testPoint3DInIndex[0]; p[1] = testPoint3DInIndex[1]; p[2] = testPoint3DInIndex[2]; imageInMitk->GetGeometry()->IndexToWorld(p, imageIndexToWorld); itk::Index<2> testPoint2DIn2DIndex; testPoint2DIn2DIndex[0] = testPoint2DInIndex[0]; testPoint2DIn2DIndex[1] = testPoint2DInIndex[1]; typedef mitk::ImagePixelReadAccessor< unsigned short, 3 > VolumeReadAccessorType; typedef mitk::ImagePixelReadAccessor< unsigned short, 2 > SliceReadAccessorType; VolumeReadAccessorType VolumeReadAccessor( imageInMitk ); SliceReadAccessorType SliceReadAccessor( slice ); //compare the pixelvalues of the defined point in the 3D volume with the value of the resliced image unsigned short valueAt3DVolume = VolumeReadAccessor.GetPixelByIndex( testPoint3DInIndex ); unsigned short valueAtSlice = SliceReadAccessor.GetPixelByIndex( testPoint2DIn2DIndex ); //valueAt3DVolume == valueAtSlice is not always working. because of rounding errors //indices are shifted MITK_TEST_CONDITION(valueAt3DVolume == valueAtSlice, "comparing pixelvalues for orthogonal plane"); - vtkSmartPointer imageInVtk = vtkSmartPointer::New(); - imageInVtk = imageInMitk->GetVtkImageData(); - vtkSmartPointer sliceInVtk = vtkSmartPointer::New(); - sliceInVtk = slice->GetVtkImageData(); + vtkSmartPointer imageInVtk = imageInMitk->GetVtkImageData(); + vtkSmartPointer sliceInVtk = slice->GetVtkImageData(); double PixelvalueByMitkOutput = sliceInVtk->GetScalarComponentAsDouble(n1, n2, 0, 0); //double valueVTKinImage = imageInVtk->GetScalarComponentAsDouble(testPoint3DInIndex[0], testPoint3DInIndex[1], testPoint3DInIndex[2], 0); /* Test that everything is working equally if vtkoutput is used instead of the default output * from mitk ImageToImageFilter */ mitk::ExtractSliceFilter::Pointer slicerWithVtkOutput = mitk::ExtractSliceFilter::New(); slicerWithVtkOutput->SetInput(imageInMitk); slicerWithVtkOutput->SetWorldGeometry(plane); slicerWithVtkOutput->SetVtkOutputRequest(true); slicerWithVtkOutput->Update(); - vtkSmartPointer vtkImageByVtkOutput = vtkSmartPointer::New(); - vtkImageByVtkOutput = slicerWithVtkOutput->GetVtkOutput(); + vtkSmartPointer vtkImageByVtkOutput = slicerWithVtkOutput->GetVtkOutput(); double PixelvalueByVtkOutput = vtkImageByVtkOutput->GetScalarComponentAsDouble(n1, n2, 0, 0); MITK_TEST_CONDITION(PixelvalueByMitkOutput == PixelvalueByVtkOutput, "testing convertion of image output vtk->mitk by reslicer"); /*================ mbilog outputs ===========================*/ #ifdef EXTRACTOR_DEBUG MITK_INFO << "\n" << "TESTINFO index: " << sliceindex << " orientation: " << orientation << " frontside: " << isFrontside << " rotated: " << isRotated; MITK_INFO << "\n" << "slice index to world: " << sliceIndexToWorld; MITK_INFO << "\n" << "image index to world: " << imageIndexToWorld; MITK_INFO << "\n" << "vtk: slice: " << PixelvalueByMitkOutput << ", image: "<< valueVTKinImage; MITK_INFO << "\n" << "testPoint3D InWorld" << testPoint3DInWorld << " is " << testPoint2DInIndex << " in 2D"; MITK_INFO << "\n" << "randoms: " << ((int)n1) << ", " << ((int)n2); MITK_INFO << "\n" << "point is inside plane: " << plane->IsInside(testPoint3DInWorld) << " and volume: " << imageInMitk->GetGeometry()->IsInside(testPoint3DInWorld); MITK_INFO << "\n" << "volume idx: " << testPoint3DInIndex << " = " << valueAt3DVolume ; MITK_INFO << "\n" << "volume world: " << testPoint3DInWorld << " = " << valueAt3DVolumeByWorld ; MITK_INFO << "\n" << "slice idx: " << testPoint2DInIndex << " = " << valueAtSlice ; itk::Index<3> curr; curr[0] = curr[1] = curr[2] = 0; for( int i = 0; i < 32 ; ++i){ for( int j = 0; j < 32; ++j){ ++curr[1]; if(SliceReadAccessor.GetPixelByIndex( curr ) == valueAt3DVolume){ MITK_INFO << "\n" << valueAt3DVolume << " MATCHED mitk " << curr; } } curr[1] = 0; ++curr[0]; } typedef itk::Image Image2DType; Image2DType::Pointer img = Image2DType::New(); CastToItkImage(slice, img); typedef itk::ImageRegionConstIterator< Image2DType > Iterator2D; Iterator2D iter(img, img->GetLargestPossibleRegion()); iter.GoToBegin(); while( !iter.IsAtEnd() ){ if(img->GetPixel(iter.GetIndex()) == valueAt3DVolume) MITK_INFO << "\n" << valueAt3DVolume << " MATCHED itk " << iter.GetIndex(); ++iter; } #endif //EXTRACTOR_DEBUG } /* random a float value */ static float randFloat(){ return (((float)rand()+1.0) / ((float)RAND_MAX + 1.0)) + (((float)rand()+1.0) / ((float)RAND_MAX + 1.0)) / ((float)RAND_MAX + 1.0);} /* create a sphere with the size of the given testVolumeSize*/ static void InitializeTestVolume() { #ifdef CREATE_VOLUME //do sphere creation ItkVolumeGeneration(); #ifdef SAVE_VOLUME //save in file mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(TestVolume); std::string file; std::ostringstream filename; filename << "C:\\home\\schroedt\\MITK\\Modules\\ImageExtraction\\Testing\\Data\\sphere_"; filename << TestvolumeSize; filename << ".nrrd"; file = filename.str(); writer->SetFileName(file); writer->Update(); #endif//SAVE_VOLUME #endif #ifndef CREATE_VOLUME //read from file mitk::StandardFileLocations::Pointer locator = mitk::StandardFileLocations::GetInstance(); std::string filename = locator->FindFile("sphere_512.nrrd.mhd", "Modules/ImageExtraction/Testing/Data"); TestVolume = mitk::IOUtil::LoadImage(filename); #endif #ifdef CALC_TESTFAILURE_DEVIATION //get the TestFailureDeviation in % AccessFixedDimensionByItk(TestVolume, CalcTestFailureDeviation, 3); #endif } //the test result of the sphere reslice struct SliceProperties{ double planeDistanceToSphereCenter; double diameterInMM; double diameterInPixel; double diameterCalculated; double percentageRadiusToPixel; double areaCalculated; double areaInPixel; double percentageAreaCalcToPixel; }; static mitk::Image::Pointer TestVolume; static double TestvolumeSize; static mitk::PlaneGeometry::Pointer TestPlane; static std::string TestName; static unsigned char pixelValueSet; static SliceProperties testResults; static double TestFailureDeviation; private: /* * Generate a sphere with a radius of TestvolumeSize / 4.0 */ static void ItkVolumeGeneration () { typedef itk::Image TestVolumeType; typedef itk::ImageRegionConstIterator< TestVolumeType > ImageIterator; TestVolumeType::Pointer sphereImage = TestVolumeType::New(); TestVolumeType::IndexType start; start[0] = start[1] = start[2] = 0; TestVolumeType::SizeType size; size[0] = size[1] = size[2] = TestvolumeSize; TestVolumeType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); sphereImage->SetRegions(imgRegion); sphereImage->SetSpacing(1.0); sphereImage->Allocate(); sphereImage->FillBuffer(0); mitk::Vector3D center; center[0] = center[1] = center[2] = TestvolumeSize / 2.0; double radius = TestvolumeSize / 4.0; double pixelValue = pixelValueSet; - double distanceToCenter = 0.0; - ImageIterator imageIterator( sphereImage, sphereImage->GetLargestPossibleRegion() ); imageIterator.GoToBegin(); mitk::Vector3D currentVoxelInIndex; while ( !imageIterator.IsAtEnd() ) { currentVoxelInIndex[0] = imageIterator.GetIndex()[0]; currentVoxelInIndex[1] = imageIterator.GetIndex()[1]; currentVoxelInIndex[2] = imageIterator.GetIndex()[2]; - distanceToCenter = (center + ( currentVoxelInIndex * -1.0 )).GetNorm(); + double distanceToCenter = (center + ( currentVoxelInIndex * -1.0 )).GetNorm(); //if distance to center is smaller then the radius of the sphere if( distanceToCenter < radius) { sphereImage->SetPixel(imageIterator.GetIndex(), pixelValue); } ++imageIterator; } CastToMitkImage(sphereImage, TestVolume); } /* calculate the devation of the voxel object to the mathematical sphere object. * this is use to make a statement about the accuracy of the resliced image, eg. the circle's diameter or area. */ template static void CalcTestFailureDeviation (itk::Image* inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator< InputImageType > ImageIterator; ImageIterator iterator(inputImage, inputImage->GetLargestPossibleRegion()); iterator.GoToBegin(); int volumeInPixel = 0; while( !iterator.IsAtEnd() ) { if(inputImage->GetPixel(iterator.GetIndex()) == pixelValueSet) volumeInPixel++; ++iterator; } double diameter = TestvolumeSize / 2.0; double volumeCalculated = (1.0 / 6.0) * 3.14159265358979 * std::pow(diameter, 3); double volumeDeviation = std::abs( 100 - (100 / volumeCalculated * volumeInPixel) ); typename InputImageType::IndexType index; index[0] = index[1] = TestvolumeSize / 2.0; index[2] = 0; int sumpixels = 0; while (index[2] < TestvolumeSize ) { if(inputImage->GetPixel(index) == pixelValueSet) sumpixels++; index[2] += 1; } double diameterDeviation = std::abs( 100 - (100 / diameter * sumpixels) ); #ifdef DEBUG MITK_INFO << "volume deviation: " << volumeDeviation << " diameter deviation:" << diameterDeviation; #endif mitkExtractSliceFilterTestClass::TestFailureDeviation = (volumeDeviation + diameterDeviation) / 2.0; } }; /*================ #END class ================*/ /*================#BEGIN Instanciation of members ================*/ mitk::Image::Pointer mitkExtractSliceFilterTestClass::TestVolume = mitk::Image::New(); double mitkExtractSliceFilterTestClass::TestvolumeSize = 256.0; mitk::PlaneGeometry::Pointer mitkExtractSliceFilterTestClass::TestPlane = mitk::PlaneGeometry::New(); std::string mitkExtractSliceFilterTestClass::TestName = ""; unsigned char mitkExtractSliceFilterTestClass::pixelValueSet = 255; mitkExtractSliceFilterTestClass::SliceProperties mitkExtractSliceFilterTestClass::testResults = {-1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0}; double mitkExtractSliceFilterTestClass::TestFailureDeviation = 0.0; /*================ #END Instanciation of members ================*/ /*================ #BEGIN test main ================*/ int mitkExtractSliceFilterTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("mitkExtractSliceFilterTest") //pixelvalue based testing mitkExtractSliceFilterTestClass::PixelvalueBasedTest(); //initialize sphere test volume mitkExtractSliceFilterTestClass::InitializeTestVolume(); mitk::Vector3D spacing = mitkExtractSliceFilterTestClass::TestVolume->GetGeometry()->GetSpacing(); //the center of the sphere = center of image double sphereCenter = mitkExtractSliceFilterTestClass::TestvolumeSize / 2.0; double planeSize = mitkExtractSliceFilterTestClass::TestvolumeSize; /* axial plane */ mitk::PlaneGeometry::Pointer geometryAxial = mitk::PlaneGeometry::New(); geometryAxial->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Axial, sphereCenter, false, true); geometryAxial->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin = geometryAxial->GetOrigin(); mitk::Vector3D normal; normal = geometryAxial->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometryAxial->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryAxial, "Testing axial plane"); /* end axial plane */ /* sagittal plane */ mitk::PlaneGeometry::Pointer geometrySagital = mitk::PlaneGeometry::New(); geometrySagital->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); geometrySagital->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagital->GetOrigin(); normal = geometrySagital->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometrySagital->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagital, "Testing sagittal plane"); /* sagittal plane */ /* sagittal shifted plane */ mitk::PlaneGeometry::Pointer geometrySagitalShifted = mitk::PlaneGeometry::New(); geometrySagitalShifted->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, (sphereCenter - 14), true, false); geometrySagitalShifted->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagitalShifted->GetOrigin(); normal = geometrySagitalShifted->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometrySagitalShifted->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagitalShifted, "Testing sagittal plane shifted"); /* end sagittal shifted plane */ /* coronal plane */ mitk::PlaneGeometry::Pointer geometryCoronal = mitk::PlaneGeometry::New(); geometryCoronal->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Frontal, sphereCenter, true, false); geometryCoronal->ChangeImageGeometryConsideringOriginOffset(true); origin = geometryCoronal->GetOrigin(); normal = geometryCoronal->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //geometryCoronal->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryCoronal, "Testing coronal plane"); /* end coronal plane */ /* oblique plane */ mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); obliquePlane->InitializeStandardPlane(planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); obliquePlane->ChangeImageGeometryConsideringOriginOffset(true); origin = obliquePlane->GetOrigin(); normal = obliquePlane->GetNormal(); normal.Normalize(); origin += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 //obliquePlane->SetOrigin(origin); mitk::Vector3D rotationVector; rotationVector[0] = 0.2; rotationVector[1] = 0.4; rotationVector[2] = 0.62; float degree = 37.0; mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); obliquePlane->ExecuteOperation(op); delete op; mitkExtractSliceFilterTestClass::TestSlice(obliquePlane, "Testing oblique plane"); /* end oblique plane */ #ifdef SHOW_SLICE_IN_RENDER_WINDOW /*================ #BEGIN vtk render code ================*/ //set reslicer for renderwindow mitk::Image::Pointer pic = mitk::IOUtil::LoadImage(filename); vtkSmartPointer slicer = vtkSmartPointer::New(); slicer->SetInput(pic->GetVtkImageData()); mitk::PlaneGeometry::Pointer obliquePl = mitk::PlaneGeometry::New(); obliquePl->InitializeStandardPlane(pic->GetGeometry(), mitk::PlaneGeometry::Sagittal, pic->GetGeometry()->GetCenter()[0], true, false); obliquePl->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin2 = obliquePl->GetOrigin(); mitk::Vector3D n; n = obliquePl->GetNormal(); n.Normalize(); origin2 += n * 0.5;//pixelspacing is 1, so half the spacing is 0.5 obliquePl->SetOrigin(origin2); mitk::Vector3D rotation; rotation[0] = 0.534307; rotation[1] = 0.000439605; rotation[2] = 0.423017; MITK_INFO << rotation; - float rotateDegree = 70; - mitk::RotationOperation* operation = new mitk::RotationOperation(mitk::OpROTATE, obliquePl->GetCenter(), rotationVector, degree); obliquePl->ExecuteOperation(operation); delete operation; double origin[3]; origin[0] = obliquePl->GetOrigin()[0]; origin[1] = obliquePl->GetOrigin()[1]; origin[2] = obliquePl->GetOrigin()[2]; slicer->SetResliceAxesOrigin(origin); mitk::Vector3D right, bottom, normal; right = obliquePl->GetAxisVector( 0 ); bottom = obliquePl->GetAxisVector( 1 ); normal = obliquePl->GetNormal(); right.Normalize(); bottom.Normalize(); normal.Normalize(); double cosines[9]; mitk::vnl2vtk(right.GetVnlVector(), cosines);//x mitk::vnl2vtk(bottom.GetVnlVector(), cosines + 3);//y mitk::vnl2vtk(normal.GetVnlVector(), cosines + 6);//n slicer->SetResliceAxesDirectionCosines(cosines); slicer->SetOutputDimensionality(2); slicer->Update(); //set vtk renderwindow vtkSmartPointer vtkPlane = vtkSmartPointer::New(); vtkPlane->SetOrigin(0.0, 0.0, 0.0); //These two points define the axes of the plane in combination with the origin. //Point 1 is the x-axis and point 2 the y-axis. //Each plane is transformed according to the view (axial, coronal and saggital) afterwards. vtkPlane->SetPoint1(1.0, 0.0, 0.0); //P1: (xMax, yMin, depth) vtkPlane->SetPoint2(0.0, 1.0, 0.0); //P2: (xMin, yMax, depth) //these are not the correct values for all slices, only a square plane by now vtkSmartPointer imageMapper = vtkSmartPointer::New(); imageMapper->SetInputConnection(vtkPlane->GetOutputPort()); vtkSmartPointer lookupTable = vtkSmartPointer::New(); //built a default lookuptable lookupTable->SetRampToLinear(); lookupTable->SetSaturationRange( 0.0, 0.0 ); lookupTable->SetHueRange( 0.0, 0.0 ); lookupTable->SetValueRange( 0.0, 1.0 ); lookupTable->Build(); //map all black values to transparent lookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0); lookupTable->SetRange(-255.0, 255.0); //lookupTable->SetRange(-1022.0, 1184.0);//pic3D range vtkSmartPointer texture = vtkSmartPointer::New(); texture->SetInput(slicer->GetOutput()); texture->SetLookupTable(lookupTable); texture->SetMapColorScalarsThroughLookupTable(true); vtkSmartPointer imageActor = vtkSmartPointer::New(); imageActor->SetMapper(imageMapper); imageActor->SetTexture(texture); // Setup renderers vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->AddActor(imageActor); // Setup render window vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->AddRenderer(renderer); // Setup render window interactor vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); vtkSmartPointer style = vtkSmartPointer::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interaction renderWindowInteractor->SetRenderWindow(renderWindow); //renderer->AddViewProp(imageActor); renderWindow->Render(); renderWindowInteractor->Start(); // always end with this! /*================ #END vtk render code ================*/ #endif //SHOW_SLICE_IN_RENDER_WINDOW MITK_TEST_END() } diff --git a/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp b/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp index a835bfa5db..5375ad8c12 100644 --- a/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp +++ b/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp @@ -1,253 +1,252 @@ /*=================================================================== 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 "mitkPlaneGeometryDataToSurfaceFilter.h" #include "mitkSurface.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "vtkPolyData.h" #include template int testExpectedIndexBoundingBox(mitk::BaseGeometry* geometry, TScalarType expectedIndexBounds[6]) { mitk::BoundingBox* bb = const_cast(geometry->GetBoundingBox()); mitk::BoundingBox::BoundsArrayType bounds = bb->GetBounds(); int i; for(i=0;i<6;++i) { if( mitk::Equal(bounds[i], expectedIndexBounds[i]) == false) { std::cout<<"[FAILED]"< int testExpectedAxisParallelBoundingBox(mitk::BaseGeometry* geometry, TScalarType expectedAxisParallelBounds[6]) { mitk::BoundingBox::Pointer bb = geometry->CalculateBoundingBoxRelativeToTransform(nullptr); mitk::BoundingBox::BoundsArrayType bounds = bb->GetBounds(); int i; for(i=0;i<6;++i) { if( mitk::Equal(bounds[i], expectedAxisParallelBounds[i]) == false) { std::cout<<"[FAILED]"<GetVtkPolyData() == nullptr) || (surface->GetVtkPolyData()->GetNumberOfPoints() == 0 )) { std::cout<<"[FAILED]"<GetVtkPolyData(); polys->ComputeBounds(); polys->GetBounds( bounds ); - int i; if(expectIdentityTransform == false) { mitk::PlaneGeometry::Pointer geometry = mitk::PlaneGeometry::New(); geometry->SetFloatBounds(bounds); geometry->SetIndexToWorldTransform(surface->GetGeometry()->GetIndexToWorldTransform()); mitk::BoundingBox::BoundsArrayType bb = const_cast(geometry->GetBoundingBox())->GetBounds(); - for(i=0;i<6;++i) + for(int i=0;i<6;++i) bounds[i]=bb[i]; } std::cout << " Testing GetBoundingBox() "; - if((result=testExpectedIndexBoundingBox(surface->GetGeometry(), bounds)) != EXIT_SUCCESS) + if((testExpectedIndexBoundingBox(surface->GetGeometry(), bounds)) != EXIT_SUCCESS) { std::cout<<"[FAILED]"<GetOutput()->SetRequestedRegionToLargestPossibleRegion(); std::cout<<"[PASSED]"<UpdateOutputInformation(); std::cout<<"[PASSED]"<GetOutput()->GetGeometry(), expectedIndexBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing correctness of axis-parallel bounding-box after UpdateOutputInformation(): "; if((result=testExpectedAxisParallelBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedAxisParallelBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing Update(): "; geometryToSurfaceFilter->Update(); std::cout<<"[PASSED]"<GetOutput()->GetGeometry(), expectedIndexBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing correctness of axis-parallel bounding-box after UpdateOutputInformation(): "; if((result=testExpectedAxisParallelBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedAxisParallelBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing bounding-box consistency: "<GetOutput(), expectIdentityTransform)) != EXIT_SUCCESS) { std::cout<<"[FAILED]"<InitializeStandardPlane(50, 100); mitk::FillVector3D(origin, 1.0, 2.0, 3.0); plane->SetOrigin(origin); mitk::PlaneGeometryData::Pointer geometryData = mitk::PlaneGeometryData::New(); geometryData->SetPlaneGeometry(plane); std::cout << "Testing SetInput(): "; geometryToSurfaceFilter->SetInput(geometryData); std::cout<<"[PASSED]"<GetInput() != geometryData ) { std::cout<<"[FAILED]"<GetPlaceByGeometry() != false ) { std::cout<<"[FAILED]"<SetPlaceByGeometry(true); if (geometryToSurfaceFilter->GetPlaceByGeometry() != true ) { std::cout<<"[FAILED]"<InsertElement( 0, bbMin ); pointsContainer->InsertElement( 1, bbMax ); boundingBox->SetPoints( pointsContainer ); boundingBox->ComputeBoundingBox(); geometryToSurfaceFilter->SetPlaceByGeometry( true ); geometryToSurfaceFilter->SetBoundingBox( boundingBox ); mitk::ScalarType expectedIndexBoundsWithBB[6] = {9.0, 39.0, 8.0, 88.0, 0.0, 0.0}; mitk::ScalarType expectedAxisParallelBoundsWithBB[6] = {10.0, 40.0, 10.0, 90.0, 3.0, 3.0}; if((result=testGeometryDataToSurfaceFilter(geometryToSurfaceFilter, expectedIndexBoundsWithBB, expectedAxisParallelBoundsWithBB, true)) != EXIT_SUCCESS) { return result; } std::cout<<"[TEST DONE]"< #include "itkBarrier.h" #include #include #include #include #include struct ThreadData { itk::Barrier::Pointer m_Barrier; // holds a pointer to the used barrier mitk::Image::Pointer data; // some random data int m_NoOfThreads; // holds the number of generated threads bool m_Successful; // to check if everything worked }; itk::SimpleFastMutexLock testMutex; ITK_THREAD_RETURN_TYPE ThreadMethod(void* data) { /* extract data pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)data; // some data validity checking if (pInfo == NULL) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == NULL) { return ITK_THREAD_RETURN_VALUE; } // obtain user data for processing ThreadData* threadData = (ThreadData*) pInfo->UserData; srand( pInfo->ThreadID ); mitk::Image::Pointer im = threadData->data; int nrSlices = im->GetDimension(2); // Create randomly a PixelRead- or PixelWriteAccessor for a slice and access all pixels in it. try { if(rand() % 2) { testMutex.Lock(); mitk::ImageDataItem* iDi = im->GetSliceData(rand() % nrSlices); testMutex.Unlock(); while(!iDi->IsComplete()) {} //MITK_INFO << "pixeltype: " << im->GetPixelType().GetComponentTypeAsString(); if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3) ) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelReadAccessor readAccessor(im, iDi); itk::Index<2> idx; for(int i=0; iGetSliceData(rand() % nrSlices); testMutex.Unlock(); while(!iDi->IsComplete()) {} if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3) ) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelWriteAccessor writeAccessor(im, iDi); itk::Index<2> idx; for(int i=0; im_Successful = false; } } } } else { // use general accessor mitk::ImageWriteAccessor iB(im,iDi); void* pointer = iB.GetData(); *((char*) pointer) = 0; } } } - catch(mitk::MemoryIsLockedException e) + catch(mitk::MemoryIsLockedException &e) { threadData->m_Successful = false; e.Print(std::cout); } - catch(mitk::Exception e) + catch(mitk::Exception &e) { threadData->m_Successful = false; e.Print(std::cout); } // data processing end! threadData->m_Barrier->Wait(); return ITK_THREAD_RETURN_VALUE; } int mitkImageAccessorTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkImageAccessorTest"); std::cout << "Loading file: "; if(argc==0) { std::cout<<"no file specified [FAILED]"<GetGeometry()->Initialize(); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); unsigned int noOfThreads = 100; // initialize barrier itk::Barrier::Pointer barrier = itk::Barrier::New(); barrier->Initialize( noOfThreads + 1); // add one for we stop the base thread when the worker threads are processing ThreadData* threadData = new ThreadData; threadData->m_Barrier = barrier; threadData->m_NoOfThreads = noOfThreads; threadData->data = image; threadData->m_Successful = true; // spawn threads for(unsigned int i=0; i < noOfThreads; ++i) { threader->SpawnThread(ThreadMethod, threadData); } // stop the base thread during worker thread execution barrier->Wait(); // terminate threads for(unsigned int j=0; j < noOfThreads; ++j) { threader->TerminateThread(j); } bool TestSuccessful = threadData->m_Successful ; delete threadData; MITK_TEST_CONDITION_REQUIRED( TestSuccessful, "Testing image access from multiple threads"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp b/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp index a3140848bc..744f5c5ca3 100644 --- a/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp +++ b/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp @@ -1,181 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkLevelWindowManager.h" #include "mitkStandaloneDataStorage.h" #include #include #include #include #include class mitkLevelWindowManagerCppUnitTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLevelWindowManagerCppUnitTestSuite); MITK_TEST(TestMultiComponentRescaling); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer m_mitkMultiComponentImage; mitk::Image::Pointer m_mitkImageComponent1; mitk::Image::Pointer m_mitkImageComponent2; public: void setUp() { typedef itk::Image ImageType; typedef itk::VectorImage VectorImageType; typedef itk::ImageRegionIterator< ImageType > ImageIteratorType; typedef itk::ImageDuplicator< ImageType > DuplicatorType; typedef itk::ComposeImageFilter CompositeFilterType; // generate two images with one component ImageType::Pointer imageComponent1 = itk::Image::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size.Fill(5); ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); imageComponent1->SetRegions(region); imageComponent1->Allocate(); DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(imageComponent1); duplicator->Update(); ImageType::Pointer imageComponent2 = duplicator->GetOutput(); // give them differing data ImageIteratorType iterator1(imageComponent1, imageComponent1->GetLargestPossibleRegion()); iterator1.GoToBegin(); int i = 0; while(!iterator1.IsAtEnd()) { iterator1.Set((double) i); ++iterator1; ++i; } ImageIteratorType iterator2(imageComponent2, imageComponent2->GetLargestPossibleRegion()); iterator2.GoToBegin(); i = 2000; while(!iterator2.IsAtEnd()) { iterator2.Set((double) i); ++iterator2; ++i; } // copy into single VectorImage CompositeFilterType::Pointer compositeFilter = CompositeFilterType::New(); compositeFilter->SetInput( 0, imageComponent1 ); compositeFilter->SetInput( 1, imageComponent2 ); compositeFilter->Update(); itk::VectorImage< double, 3 >::Pointer multiComponentImage= compositeFilter->GetOutput(); // cast images to mitk mitk::CastToMitkImage(multiComponentImage, m_mitkMultiComponentImage); mitk::CastToMitkImage(imageComponent1, m_mitkImageComponent1); mitk::CastToMitkImage(imageComponent2, m_mitkImageComponent2); } static mitk::LevelWindow getLevelWindowForImage(mitk::Image* image, unsigned component) { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); try { manager->SetDataStorage(ds); } - catch(std::exception e) + catch(std::exception &e) { MITK_ERROR << "Exception: " << e.what(); } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(image); ds->Add(node); node->SetBoolProperty("selected", true); node->SetIntProperty("Image.Displayed Component", component); mitk::LevelWindow levelWindow; node->GetLevelWindow(levelWindow); return levelWindow; // node is an image node because of predicates } void TestMultiComponentRescaling() { // compute level windows for the three images mitk::LevelWindow imageComponent1LevelWindow = getLevelWindowForImage(m_mitkImageComponent1, 0); mitk::LevelWindow imageComponent2LevelWindow = getLevelWindowForImage(m_mitkImageComponent2, 0); // calculate level window for second component in multi-component image mitk::LevelWindow multiComponentImageLevelWindow = getLevelWindowForImage(m_mitkMultiComponentImage, 1); // compare level window boundaries. the multicomponent image level window // should match the second image, since the second component was selected CPPUNIT_ASSERT_EQUAL_MESSAGE("default lower bounds equal", imageComponent2LevelWindow.GetDefaultLowerBound(), multiComponentImageLevelWindow.GetDefaultLowerBound()); CPPUNIT_ASSERT_EQUAL_MESSAGE("default upper bounds equal", imageComponent2LevelWindow.GetDefaultUpperBound(), multiComponentImageLevelWindow.GetDefaultUpperBound()); CPPUNIT_ASSERT_EQUAL_MESSAGE("range equal", imageComponent2LevelWindow.GetRange(), multiComponentImageLevelWindow.GetRange()); CPPUNIT_ASSERT(imageComponent1LevelWindow.GetDefaultLowerBound() != multiComponentImageLevelWindow.GetDefaultLowerBound()); CPPUNIT_ASSERT(imageComponent1LevelWindow.GetDefaultUpperBound() != multiComponentImageLevelWindow.GetDefaultUpperBound()); } }; MITK_TEST_SUITE_REGISTRATION(mitkLevelWindowManagerCppUnit) diff --git a/Modules/Core/test/mitkLevelWindowManagerTest.cpp b/Modules/Core/test/mitkLevelWindowManagerTest.cpp index cb04abe8f5..316096eb43 100644 --- a/Modules/Core/test/mitkLevelWindowManagerTest.cpp +++ b/Modules/Core/test/mitkLevelWindowManagerTest.cpp @@ -1,625 +1,621 @@ /*=================================================================== 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 "mitkLevelWindowManager.h" #include "mitkStandaloneDataStorage.h" #include #include #include #include "mitkRenderingModeProperty.h" #include #include #include #include #include #include "itkMersenneTwisterRandomVariateGenerator.h" class mitkLevelWindowManagerTestClass { public: static void TestInstantiation() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); MITK_TEST_CONDITION_REQUIRED(manager.IsNotNull(),"Testing mitk::LevelWindowManager::New()"); } static void TestSetGetDataStorage() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); MITK_TEST_OUTPUT(<< "Creating DataStorage: "); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); bool success = true; try { manager->SetDataStorage(ds); } - catch(std::exception e) + catch(std::exception &e) { success = false; MITK_ERROR << "Exception: " << e.what(); } MITK_TEST_CONDITION_REQUIRED(success,"Testing mitk::LevelWindowManager SetDataStorage() "); MITK_TEST_CONDITION_REQUIRED(ds == manager->GetDataStorage(),"Testing mitk::LevelWindowManager GetDataStorage "); } static void TestMethodsWithInvalidParameters() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); bool success = false; mitk::LevelWindowProperty::Pointer levelWindowProperty = mitk::LevelWindowProperty::New(); try { manager->SetLevelWindowProperty(levelWindowProperty); } - catch(mitk::Exception e) + catch(mitk::Exception &e) { success = true; } MITK_TEST_CONDITION(success,"Testing mitk::LevelWindowManager SetLevelWindowProperty with invalid parameter"); } static void TestOtherMethods() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); MITK_TEST_CONDITION(manager->isAutoTopMost(),"Testing mitk::LevelWindowManager isAutoTopMost"); // It is not clear what the following code is supposed to test. The expression in // the catch(...) block does have no effect, so success is always true. // Related bugs are 13894 and 13889 /* bool success = true; try { mitk::LevelWindow levelWindow = manager->GetLevelWindow(); manager->SetLevelWindow(levelWindow); } catch (...) { success == false; } MITK_TEST_CONDITION(success,"Testing mitk::LevelWindowManager GetLevelWindow() and SetLevelWindow()"); */ manager->SetAutoTopMostImage(true); MITK_TEST_CONDITION(manager->isAutoTopMost(),"Testing mitk::LevelWindowManager isAutoTopMost()"); } static void TestRemoveObserver(std::string testImageFile) { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); //add multiple objects to the data storage => multiple observers should be created mitk::Image::Pointer image1 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node1 = mitk::DataNode::New(); node1->SetData(image1); mitk::Image::Pointer image2 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node2 = mitk::DataNode::New(); node2->SetData(image2); ds->Add(node1); ds->Add(node2); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Test if nodes have been added"); MITK_TEST_CONDITION_REQUIRED(static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), "Test if number of nodes is similar to number of observers"); mitk::Image::Pointer image3 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node3 = mitk::DataNode::New(); node3->SetData(image3); ds->Add(node3); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 3, "Test if another node have been added"); MITK_TEST_CONDITION_REQUIRED(static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), "Test if number of nodes is similar to number of observers"); ds->Remove(node1); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Deleted node 1 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 2, "Deleted node 1 (test GetNumberOfObservers())"); ds->Remove(node2); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 1, "Deleted node 2 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 1, "Deleted node 2 (test GetNumberOfObservers())"); ds->Remove(node3); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 0, "Deleted node 3 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 0, "Deleted node 3 (test GetNumberOfObservers())"); } static bool VerifyRenderingModes() { - bool ok = false; - - ok = ( mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR == 1 ) && + bool ok = ( mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR == 1 ) && (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR == 2 ) && (mitk::RenderingModeProperty::LOOKUPTABLE_COLOR == 3 ) && (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR == 4 ); return ok; } static void TestLevelWindowSliderVisibility(std::string testImageFile) { bool renderingModesValid = mitkLevelWindowManagerTestClass::VerifyRenderingModes(); if ( !renderingModesValid ) { MITK_ERROR << "Exception: Image Rendering.Mode property value types inconsistent."; } mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); //add multiple objects to the data storage => multiple observers should be created mitk::Image::Pointer image1 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node1 = mitk::DataNode::New(); node1->SetData(image1); ds->Add(node1); //mitk::DataNode::Pointer node1 = mitk::IOUtil::LoadDataNode( testImageFile ); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); std::vector< mitk::DataNode::Pointer > nodeVec; //nodeVec.resize( 3 ); nodeVec.push_back( node1 ); nodeVec.push_back( node2 ); nodeVec.push_back( node3 ); typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; RandomGeneratorType::Pointer rnd = RandomGeneratorType::New(); rnd->Initialize(); for( unsigned int i=0; i<8; ++i ) { unsigned int parity = i; for( unsigned int img = 0; img < 3; ++img ) { if ( parity & 1 ) { int mode = rnd->GetIntegerVariate() % 3; nodeVec[img]->SetProperty( "Image Rendering.Mode", mitk::RenderingModeProperty::New( mode ) ); } else { int mode = rnd->GetIntegerVariate() % 2; nodeVec[img]->SetProperty( "Image Rendering.Mode", mitk::RenderingModeProperty::New( 3 + mode ) ); } parity >>= 1; } MITK_TEST_CONDITION( renderingModesValid && ( (!manager->GetLevelWindowProperty() && !i) || (manager->GetLevelWindowProperty() && i) ), "Testing level window property member according to rendering mode"); } } static void TestSetLevelWindowProperty( std::string testImageFile ) { mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); //add multiple objects to the data storage => multiple observers should be created mitk::DataNode::Pointer node3 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); node3->SetIntProperty( "layer" , 1 ); node2->SetIntProperty( "layer" , 2 ); node1->SetIntProperty( "layer" , 3 ); manager->SetAutoTopMostImage( true ); bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 1."); manager->SetAutoTopMostImage( false ); mitk::LevelWindowProperty::Pointer prop = dynamic_cast(node2->GetProperty("levelwindow")); manager->SetLevelWindowProperty( prop ); node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( !isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 2."); prop = dynamic_cast(node3->GetProperty("levelwindow")); manager->SetLevelWindowProperty( prop ); node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( !isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); prop = dynamic_cast(node1->GetProperty("levelwindow")); manager->SetLevelWindowProperty( prop ); node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); } static void TestImageForLevelWindowOnVisibilityChange( std::string testImageFile ) { mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); //add multiple objects to the data storage => multiple observers should be created mitk::DataNode::Pointer node3 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); node3->SetIntProperty( "layer" , 1 ); node2->SetIntProperty( "layer" , 2 ); node1->SetIntProperty( "layer" , 3 ); manager->SetAutoTopMostImage( false ); bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing initial imageForLevelWindow setting."); node1->SetVisibility( false ); node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( !isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 2."); node2->SetVisibility( false ); node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( !isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); node3->SetVisibility( false ); node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( !isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); node1->SetVisibility( true ); node1->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow1 ); node2->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow2 ); node3->GetBoolProperty( "imageForLevelWindow", isImageForLevelWindow3 ); MITK_TEST_CONDITION( isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); } static void TestImageForLevelWindowOnRandomPropertyChange( std::string testImageFile ) { typedef std::vector< bool > BoolVecType; typedef BoolVecType::iterator IteratorType; typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; //initialize the data storage mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); node3->SetIntProperty( "layer" , 1 ); node2->SetIntProperty( "layer" , 2 ); node1->SetIntProperty( "layer" , 3 ); //node visibilities std::vector< bool > nodesVisible; nodesVisible.resize( 3 ); std::fill( nodesVisible.begin(), nodesVisible.end(), true ); //which node has the level window std::vector< bool > nodesForLevelWindow; nodesForLevelWindow.resize( 3 ); std::fill( nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false ); //the nodes themselves std::vector< mitk::DataNode::Pointer > nodes; nodes.push_back( node1 ); nodes.push_back( node2 ); nodes.push_back( node3 ); //status quo manager->SetAutoTopMostImage( false ); bool lvlWin1, lvlWin2, lvlWin3; node1->GetBoolProperty( "imageForLevelWindow", lvlWin1 ); node2->GetBoolProperty( "imageForLevelWindow", lvlWin2 ); node3->GetBoolProperty( "imageForLevelWindow", lvlWin3 ); MITK_TEST_CONDITION( lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; //prepare randomized visibility changes RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); ranGen->Initialize(); int ranCount = 100; int validCount = 0; int invalidCount = 0; int mustHaveLvlWindow = 4; for ( int run = 0; run < ranCount; ++run ) { //toggle node visibility int ran = ranGen->GetIntegerVariate( 2 ); nodes[ ran ]->SetBoolProperty( "imageForLevelWindow", !nodesForLevelWindow[ ran ] ); //one node must have the level window std::vector< bool >::const_iterator found = std::find( nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true ); if ( found == nodesForLevelWindow.end() ) { break; } - int ind = found - nodesForLevelWindow.begin(); //all invisible? found = std::find( nodesVisible.begin(), nodesVisible.end(), true ); - bool allInvisible = ( found == nodesVisible.end() ); if ( !nodesForLevelWindow[ ran ] ) { mustHaveLvlWindow = pow( 2, 2 - ran ); } else { mustHaveLvlWindow = 4; } //get the current status node1->GetBoolProperty( "imageForLevelWindow", lvlWin1 ); node2->GetBoolProperty( "imageForLevelWindow", lvlWin2 ); node3->GetBoolProperty( "imageForLevelWindow", lvlWin3 ); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; int hasLevelWindow = 0; for ( int i=0; i<3; ++i ) { if ( nodesForLevelWindow[ i ] ) { hasLevelWindow += pow( 2, 2 - i ); } } validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; //test sensitivity int falseran = 0; while ( falseran == 0 ) { falseran = ranGen->GetIntegerVariate( 7 ); } BoolVecType falseNodes; falseNodes.push_back( falseran & 1 == 1 ? !lvlWin1 : lvlWin1 ); falseran >>= 1; falseNodes.push_back( falseran & 1 == 1 ? !lvlWin2 : lvlWin2 ); falseran >>= 1; falseNodes.push_back( falseran & 1 == 1 ? !lvlWin3 : lvlWin3 ); int falseLevelWindow = 0; for ( int i=0; i<3; ++i ) { if ( falseNodes[ i ] ) { falseLevelWindow += pow( 2, 2 - i ); } } invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; //in case of errors proceed anyway mustHaveLvlWindow = hasLevelWindow; } MITK_TEST_CONDITION( validCount == ranCount, "Testing proper node for level window property."); MITK_TEST_CONDITION( invalidCount == ranCount, "Sensitivity test."); } static void TestImageForLevelWindowOnRandomVisibilityChange( std::string testImageFile ) { typedef std::vector< bool > BoolVecType; typedef BoolVecType::iterator IteratorType; typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; //initialize the data storage mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load( testImageFile, *ds )->GetElement(0); node3->SetIntProperty( "layer" , 1 ); node2->SetIntProperty( "layer" , 2 ); node1->SetIntProperty( "layer" , 3 ); //node visibilities std::vector< bool > nodesVisible; nodesVisible.resize( 3 ); std::fill( nodesVisible.begin(), nodesVisible.end(), true ); //which node has the level window std::vector< bool > nodesForLevelWindow; nodesForLevelWindow.resize( 3 ); std::fill( nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false ); //the nodes themselves std::vector< mitk::DataNode::Pointer > nodes; nodes.push_back( node1 ); nodes.push_back( node2 ); nodes.push_back( node3 ); //status quo manager->SetAutoTopMostImage( false ); bool lvlWin1, lvlWin2, lvlWin3; node1->GetBoolProperty( "imageForLevelWindow", lvlWin1 ); node2->GetBoolProperty( "imageForLevelWindow", lvlWin2 ); node3->GetBoolProperty( "imageForLevelWindow", lvlWin3 ); MITK_TEST_CONDITION( lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; //prepare randomized visibility changes RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); ranGen->Initialize(); int ranCount = 100; int validCount = 0; int invalidCount = 0; int mustHaveLvlWindow = 4; for ( int run = 0; run < ranCount; ++run ) { //toggle node visibility int ran = ranGen->GetIntegerVariate( 2 ); nodesVisible[ ran ] = !nodesVisible[ ran ]; nodes[ ran ]->SetVisibility( nodesVisible[ ran ] ); //one node must have the level window std::vector< bool >::const_iterator found = std::find( nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true ); if ( found == nodesForLevelWindow.end() ) { break; } int ind = found - nodesForLevelWindow.begin(); //all invisible? found = std::find( nodesVisible.begin(), nodesVisible.end(), true ); bool allInvisible = ( found == nodesVisible.end() ); //which node shall get the level window now if ( !allInvisible && !nodesVisible[ ind ] ) { int count = 0; for ( std::vector< bool >::const_iterator it = nodesVisible.begin(); it != nodesVisible.end(); ++it, ++count ) { if ( *it ) { mustHaveLvlWindow = pow( 2, 2 - count ); break; } } } //get the current status node1->GetBoolProperty( "imageForLevelWindow", lvlWin1 ); node2->GetBoolProperty( "imageForLevelWindow", lvlWin2 ); node3->GetBoolProperty( "imageForLevelWindow", lvlWin3 ); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; int hasLevelWindow = 0; for ( int i=0; i<3; ++i ) { if ( nodesForLevelWindow[ i ] ) { hasLevelWindow += pow( 2, 2 - i ); } } if ( hasLevelWindow != mustHaveLvlWindow ) { int n = 5; } validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; //test sensitivity int falseran = 0; while ( falseran == 0 ) { falseran = ranGen->GetIntegerVariate( 7 ); } BoolVecType falseNodes; falseNodes.push_back( falseran & 1 == 1 ? !lvlWin1 : lvlWin1 ); falseran >>= 1; falseNodes.push_back( falseran & 1 == 1 ? !lvlWin2 : lvlWin2 ); falseran >>= 1; falseNodes.push_back( falseran & 1 == 1 ? !lvlWin3 : lvlWin3 ); int falseLevelWindow = 0; for ( int i=0; i<3; ++i ) { if ( falseNodes[ i ] ) { falseLevelWindow += pow( 2, 2 - i ); } } invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; //in case of errors proceed anyway mustHaveLvlWindow = hasLevelWindow; } MITK_TEST_CONDITION( validCount == ranCount, "Testing proper node for level window property."); MITK_TEST_CONDITION( invalidCount == ranCount, "Sensitivity test."); } }; int mitkLevelWindowManagerTest(int argc, char* args[]) { MITK_TEST_BEGIN("mitkLevelWindowManager"); MITK_TEST_CONDITION_REQUIRED( argc >= 2, "Testing if test file is given."); std::string testImage = args[1]; mitkLevelWindowManagerTestClass::TestInstantiation(); mitkLevelWindowManagerTestClass::TestSetGetDataStorage(); mitkLevelWindowManagerTestClass::TestMethodsWithInvalidParameters(); mitkLevelWindowManagerTestClass::TestOtherMethods(); mitkLevelWindowManagerTestClass::TestRemoveObserver(testImage); mitkLevelWindowManagerTestClass::TestLevelWindowSliderVisibility(testImage); mitkLevelWindowManagerTestClass::TestSetLevelWindowProperty( testImage ); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnVisibilityChange( testImage ); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomVisibilityChange( testImage ); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomPropertyChange( testImage ); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkLevelWindowTest.cpp b/Modules/Core/test/mitkLevelWindowTest.cpp index c2ead8b2b3..b8bb367134 100644 --- a/Modules/Core/test/mitkLevelWindowTest.cpp +++ b/Modules/Core/test/mitkLevelWindowTest.cpp @@ -1,969 +1,970 @@ /*=================================================================== 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 "mitkLevelWindow.h" #include #include "mitkImageWriteAccessor.h" /* * Reseting the Levelwindow to default values: * i.e. Range = -1000..1000, Level = 0 and Window = 500 */ void resetLevelWindow(mitk::LevelWindow &lw) { //Default window bounds lw.SetRangeMinMax(-10000,10000); lw.SetLevelWindow(0,500,false); if (lw.GetRangeMin() != -10000 || lw.GetRangeMax() != 10000 || lw.GetLevel() != 0 || lw.GetWindow() != 500) { std::cout << "[Failed] To reset Levelwindow"<GetDefaultWindow(); if (!(defaultWindow == 500)) { std::cout<GetDefaultLevel(); if (!(defaultLevel == 256)) { std::cout<<"[FAILED]"<GetWindow(); if (!(window == 500)) { std::cout<<"[FAILED]"<GetLowerWindowBound() == 6)) { std::cout<<"[FAILED]"<GetUpperWindowBound() == 506)) { std::cout<<"[FAILED]"<GetLevel(); if (!(level == 256)) { std::cout<<"[FAILED]"<SetLevelWindow(20, 100); if (!(levWin->GetLevel() == 20)) { std::cout<<"[FAILED]"<GetWindow() == 100)) { std::cout<<"[FAILED]"<SetLevelWindow(levWin->GetDefaultLevel(), levWin->GetDefaultWindow()); if (!(levWin->GetLevel() == 256) && !(levWin->GetWindow() == 500)) { std::cout<<"[FAILED]"<SetDefaultLevelWindow(20, 200); if (!(levWin->GetDefaultLevel() == 20) && !(levWin->GetDefaultWindow() == 200)) { std::cout<<"[FAILED]"<SetLevelWindow(100, 50); levWin->ResetDefaultLevelWindow(); //double a = levWin->GetLevel(); //double d = levWin->GetWindow(); if (!((levWin->GetLevel() == 20) &&(levWin->GetWindow() == 200))) { std::cout<<"[FAILED]"<SetWindowBounds(0, 2); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2) && (levWin->GetLevel() == 1) && (levWin->GetWindow() == 2))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(2000, 2000); if (!(levWin->GetRangeMin() == 1999 && levWin->GetRangeMax() == 2000)) { std::cout<<"[FAILED]"< rangemax"; levWin->SetRangeMinMax(2100, 2000); if (!(levWin->GetRangeMin() == 2000 && levWin->GetRangeMax() == 2100)) { std::cout<<"[FAILED]"<SetRangeMinMax(-1000, 2000); std::cout<<"[PASSED]"<GetRangeMin() == -1000)) { std::cout<<"[FAILED]"<GetRangeMax() == 2000)) { std::cout<<"[FAILED]"<GetRangeMax() - levWin->GetRangeMin()) == levWin->GetRange())) { std::cout<<"[FAILED]"<SetDefaultBoundaries(2000, 2000); if (!(levWin->GetDefaultLowerBound() == 1999 && levWin->GetDefaultUpperBound() == 2000)) { std::cout<<"[FAILED]"< rangemax"; levWin->SetDefaultBoundaries(2100, 2000); if (!(levWin->GetDefaultLowerBound() == 2000 && levWin->GetDefaultUpperBound() == 2100)) { std::cout<<"[FAILED]"<SetDefaultBoundaries(-2000, 8000); std::cout<<"[PASSED]"<GetDefaultLowerBound() == -2000)) { std::cout<<"[FAILED]"<GetDefaultUpperBound() == 8000)) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); if (!((levWin->GetRangeMin() == levWin->GetDefaultLowerBound()) && (levWin->GetRangeMax() == levWin->GetDefaultUpperBound()))) { std::cout<<"[FAILED]"< maxRange "; levWin->SetRangeMinMax(2000, 1000); if (!((levWin->GetRangeMin() == 1000) && (levWin->GetRangeMax() == 2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(2000, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(-2000, -3000); if (!((levWin->GetRangeMin() == -3000) && (levWin->GetRangeMax() == -2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(0, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 0))) { std::cout<<"[FAILED]"<SetRangeMinMax(2000, 0); if (!((levWin->GetRangeMin() == 0) && (levWin->GetRangeMax() == 2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(-10000, 10000); std::cout<<"[PASSED]"< defaultMaxRange "; levWin->SetDefaultBoundaries(2000, 1000); if (!((levWin->GetDefaultLowerBound() == 1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(2000, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(-2000, -3000); if (!((levWin->GetDefaultLowerBound() == -3000) && (levWin->GetDefaultUpperBound() == -2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(0, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 0))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(2000, 0); if (!((levWin->GetDefaultLowerBound() == 0) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(-10000, 10000); std::cout<<"[PASSED]"< max "; levWin->SetWindowBounds(2000, 1000); if (!((levWin->GetLowerWindowBound() == 1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout<<"[FAILED]"<SetWindowBounds(2000, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout<<"[FAILED]"<SetWindowBounds(-2000, -3000); if (!((levWin->GetLowerWindowBound() == -3000) && (levWin->GetUpperWindowBound() == -2000))) { std::cout<<"[FAILED]"<SetWindowBounds(0, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 0))) { std::cout<<"[FAILED]"<SetWindowBounds(2000, 0); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange, minmax < minrange, minmaxrange, min < minrange & max > minrange // max < minrange & min > minrange, min > maxrange & max < maxrange, min < minrange & max > maxrange // min > maxrange & max < minrange std::cout << "Testing mitk::LevelWindow max > min > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<GetLowerWindowBound()<<","<GetUpperWindowBound()<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 12000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout<<"[FAILED]"<GetLowerWindowBound()<<","<GetUpperWindowBound()<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(12000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(12000, 11000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-12000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-12000, -11000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -12000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]: Expected (-10000, -9999): "<< levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound()<< std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -12000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = FALSE"; levWin->SetWindowBounds(9999, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = TRUE"; levWin->SetWindowBounds(9999, 12000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 12000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -9999, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -9999); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, 11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, 11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min = max > minrange autoexpand = FALSE"; levWin->SetWindowBounds(5000, 5000, false); if (!((levWin->GetLowerWindowBound() == 4999.5) && (levWin->GetUpperWindowBound() == 5000.5))) { std::cout<<"[FAILED]: Expected (4999.5, 5000.5): "<< levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound()<< std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min = max > minrange autoexpand = TRUE"; levWin->SetWindowBounds(5000, 5000); if (!((levWin->GetLowerWindowBound() == 4999.5) && (levWin->GetUpperWindowBound() == 5000.5))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-10000, -10000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]: Expected (-10000, -9999): "<< levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound() << " . " << levWin->GetRangeMin() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-10000, -10000); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(10000, 10000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(10000, 10000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 11000); if (!((levWin->GetLowerWindowBound() == 10999) && (levWin->GetUpperWindowBound() == 11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -10999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(-9000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(-9000, -11000, true); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange & maxrange < min < max "; levWin->ResetDefaultLevelWindow(); levWin->SetRangeMinMax(-15000, -20000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000,110, false); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000,110); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(1000,-80); levWin->SetWindowBounds(-1000,110, false ); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(1000,-80); levWin->SetWindowBounds(-1000,110 ); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(20, 110); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxRange & min < maxrange < max "; levWin->SetWindowBounds(-90,1000); levWin->SetRangeMinMax(100, -80); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 100))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxRange & min < minrange < maxrange SetRangeMinMax(20, 100); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 100))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange & min < max < minrange "; levWin->SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<DebugOn(); image->Initialize( mitk::MakePixelType(), 3, dim); mitk::ImageWriteAccessor imAccess(image); int *p = (int*)imAccess.GetData(); int size = dim[0]*dim[1]*dim[2]; int i; for(i=0; iGetRange() == levelwindow.GetRange())) { std::cout<<"[FAILED]"< #include #include #include #include /** Documentation * * @brief this class provides an accessible BackendCout to determine whether this backend was * used to process a message or not. * It is needed for the disable / enable backend test. */ class TestBackendCout : public mbilog::BackendCout { public: TestBackendCout() { m_Called = false; mbilog::BackendCout(); } void ProcessMessage(const mbilog::LogMessage &l) override { m_Called = true; mbilog::BackendCout::ProcessMessage(l); } bool WasCalled() { return m_Called; } private: bool m_Called; }; /** Documentation * * @brief Objects of this class can start an internal thread by calling the Start() method. * The thread is then logging messages until the method Stop() is called. The class * can be used to test if logging is thread-save by using multiple objects and let * them log simuntanously. */ class mitkTestLoggingThread : public itk::Object { public: mitkClassMacroItkParent(mitkTestLoggingThread,itk::Object); mitkNewMacro1Param(mitkTestLoggingThread,itk::MultiThreader::Pointer); int NumberOfMessages; protected: mitkTestLoggingThread(itk::MultiThreader::Pointer MultiThreader) { ThreadID = -1; NumberOfMessages = 0; m_MultiThreader = MultiThreader; + LoggingRunning = true; } bool LoggingRunning; int ThreadID; itk::MultiThreader::Pointer m_MultiThreader; void LogMessages() { while(LoggingRunning) { MITK_INFO << "Test info stream in thread" << ThreadID << "\n even with newlines"; MITK_WARN << "Test warning stream in thread " << ThreadID <<". " << "Even with a very long text, even without meaning or implied meaning or content, just a long sentence to see whether something has problems with long sentences or output in files or into windows or commandlines or whatever."; MITK_DEBUG << "Test debugging stream in thread " << ThreadID; MITK_ERROR << "Test error stream in thread " << ThreadID; MITK_FATAL << "Test fatal stream in thread " << ThreadID; NumberOfMessages += 5; } } static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; if (pInfo == nullptr) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == nullptr) { return ITK_THREAD_RETURN_VALUE; } mitkTestLoggingThread *thisthread = (mitkTestLoggingThread*)pInfo->UserData; if (thisthread != nullptr) thisthread->LogMessages(); return ITK_THREAD_RETURN_VALUE; } public: int Start() { LoggingRunning = true; this->ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); return ThreadID; } void Stop() { LoggingRunning = false; } }; /** Documentation * * @brief This class holds static test methods to sturcture the test of the mitk logging mechanism. */ class mitkLogTestClass { public: static void TestSimpleLog() { bool testSucceded = true; try { MITK_INFO << "Test info stream."; MITK_WARN << "Test warning stream."; MITK_DEBUG << "Test debugging stream."; //only activated if cmake variable is on! //so no worries if you see no output for this line MITK_ERROR << "Test error stream."; MITK_FATAL << "Test fatal stream."; } - catch(mitk::Exception e) + catch(mitk::Exception &e) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded,"Test logging streams."); } static void TestObjectInfoLogging() { bool testSucceded = true; try { int i = 123; float f = .32234; double d = 123123; std::string testString = "testString"; std::stringstream testStringStream; testStringStream << "test" << "String" << "Stream"; mitk::Point3D testMitkPoint; testMitkPoint.Fill(2); MITK_INFO << i; MITK_INFO << f; MITK_INFO << d; MITK_INFO << testString; MITK_INFO << testStringStream.str(); MITK_INFO << testMitkPoint; } - catch(mitk::Exception e) + catch(mitk::Exception &e) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded,"Test logging of object information."); } static void TestThreadSaveLog(bool toFile) { bool testSucceded = true; try { if (toFile) { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testthreadlog.log"; itksys::SystemTools::RemoveFile(filename.c_str()); // remove old file, we do not want to append to large files mitk::LoggingBackend::SetLogFile(filename.c_str()); } unsigned int numberOfThreads = 20; unsigned int threadRuntimeInMilliseconds = 2000; std::vector threadIDs; std::vector threads; itk::MultiThreader::Pointer multiThreader = itk::MultiThreader::New(); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //initialize threads... mitkTestLoggingThread::Pointer newThread = mitkTestLoggingThread::New(multiThreader); threads.push_back(newThread); std::cout << "Created " << threadIdx << ". thread." << std::endl; } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //start them std::cout << "Start " << threadIdx << ". thread." << std::endl; threadIDs.push_back( threads[threadIdx]->Start() ); std::cout << threadIdx << ". thread has ID " << threadIDs[threadIdx] << std::endl; } //wait for some time (milliseconds) itksys::SystemTools::Delay( threadRuntimeInMilliseconds ); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //stop them std::cout << "Stop " << threadIdx << ". thread." << std::endl; threads[threadIdx]->Stop(); } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { //Wait for all threads to end multiThreader->TerminateThread(threadIDs[threadIdx]); std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)." << std::endl; } } - catch(std::exception e) + catch(std::exception &e) { MITK_ERROR << "exception during 'TestThreadSaveLog': "<GetOptionDirectory() + "/testlog.log"; mitk::LoggingBackend::SetLogFile(filename.c_str()); MITK_INFO << "Test logging to default filename: " << mitk::LoggingBackend::GetLogFile(); MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(filename.c_str()),"Testing if log file exists."); //TODO delete log file? } static void TestAddAndRemoveBackends() { mbilog::BackendCout myBackend = mbilog::BackendCout(); mbilog::RegisterBackend(&myBackend); MITK_INFO << "Test logging"; mbilog::UnregisterBackend(&myBackend); //if no error occured until now, everything is ok MITK_TEST_CONDITION_REQUIRED(true,"Test add/remove logging backend."); } static void TestDefaultBackend() { //not possible now, because we cannot unregister the mitk logging backend in the moment. If such a method is added to mbilog utility one may add this test. } static void TestEnableDisableBackends() { TestBackendCout myCoutBackend = TestBackendCout(); mbilog::RegisterBackend(&myCoutBackend); mbilog::DisableBackends(mbilog::Console); MITK_INFO << "There should be no output!"; bool success = !myCoutBackend.WasCalled(); mbilog::EnableBackends(mbilog::Console); MITK_INFO << "Now there should be an output."; success &= myCoutBackend.WasCalled(); mbilog::UnregisterBackend(&myCoutBackend); MITK_TEST_CONDITION_REQUIRED(success, "Test disable / enable logging backends.") } }; int mitkLogTest(int /* argc */, char* /*argv*/[]) { // always start with this! MITK_TEST_BEGIN("Log") MITK_TEST_OUTPUT(<<"TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!") mitkLogTestClass::TestSimpleLog(); mitkLogTestClass::TestObjectInfoLogging(); mitkLogTestClass::TestLoggingToFile(); mitkLogTestClass::TestAddAndRemoveBackends(); mitkLogTestClass::TestThreadSaveLog( false ); // false = to console mitkLogTestClass::TestThreadSaveLog( true ); // true = to file mitkLogTestClass::TestEnableDisableBackends(); // TODO actually test file somehow? // always end with this! MITK_TEST_END() } diff --git a/Modules/Core/test/mitkMessageTest.cpp b/Modules/Core/test/mitkMessageTest.cpp index 0ef382cffe..7d4c4c4272 100644 --- a/Modules/Core/test/mitkMessageTest.cpp +++ b/Modules/Core/test/mitkMessageTest.cpp @@ -1,415 +1,415 @@ /*=================================================================== 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 "mitkMessage.h" #include "mitkTestingMacros.h" #include namespace mitk { class mitkMessageTestTestClass { public: // dummy class to send around class Package { public: Package(int content = 43) :m_Content(content) { } void Clear() { m_Content = 0; } bool operator==(const Package& other) { return m_Content == other.m_Content; } private: int m_Content; }; class MessageSenderClass { public: // message without any parameters, pure notification Message<> WaveHand; // message without any parameters, pure notification Message<> ShowFinger; // message with one parameter of type std::string Message1 Say; // message with one parameter of type double Message1 WalkMeters; // message with one parameter of class type Package Message1 GivePackage; // message with two parameters of type int and float Message2 ShoutAgeAndFootSize; void DoShowFinger() { ShowFinger.Send(); // ShowFinger() does the same } void DoWaveHand() { WaveHand(); // WaveHand.Send() does the same } void DoSay(const std::string& words) { Say(words); } void DoWalk(double meters) { WalkMeters(meters); } void DoGivePackage(const Package& package) { GivePackage.Send(package); } void DoShoutAgeAndFootSize(int age, float size) { ShoutAgeAndFootSize(age, size); } }; // Receiver class remembers events received. // Will tell about received events when asked. class MessageReceiverClass { public: MessageReceiverClass() { Amnesia(); } void OnWaveHand() { m_HandWaved = true; } bool HandWaved() { return m_HandWaved; } void OnSay(const std::string& words) { m_WordsSaid = words; } const std::string WordsSaid() { return m_WordsSaid; } void OnWalk(double meters) { m_MetersWalked = meters; } double MetersWalked() { return m_MetersWalked; } void OnGivePackage(const Package& p) { m_PackageReceived = p; } Package PackageReceived() { return m_PackageReceived; } void OnShoutAgeAndFootSize(int age, float footSize) { m_Age = age; m_FootSize = footSize; } int Age() { return m_Age; } float FootSize() { return m_FootSize; } void Amnesia() { m_HandWaved = false; m_WordsSaid.clear(); m_MetersWalked = 0.0; m_PackageReceived.Clear(); m_Age = 0; m_FootSize = 0.0; } void RegisterObservers(MessageSenderClass& sender) { sender.WaveHand += MessageDelegate( this, &MessageReceiverClass::OnWaveHand ); sender.ShowFinger += MessageDelegate( this, &MessageReceiverClass::OnWaveHand ); // we cannot see clearly, misinterpret this sender.Say += MessageDelegate1( this, &MessageReceiverClass::OnSay ); sender.WalkMeters += MessageDelegate1( this, &MessageReceiverClass::OnWalk ); sender.GivePackage += MessageDelegate1( this, &MessageReceiverClass::OnGivePackage ); sender.ShoutAgeAndFootSize += MessageDelegate2( this, &MessageReceiverClass::OnShoutAgeAndFootSize ); } private: bool m_HandWaved; std::string m_WordsSaid; double m_MetersWalked; Package m_PackageReceived; int m_Age; float m_FootSize; }; /* MessageMacro Test classes */ class Law { private: std::string m_Description; public: - Law(const std::string law) : m_Description(law) + Law(const std::string& law) : m_Description(law) { } std::string GetDescription() const { return m_Description; } }; // The NewtonMachine will issue specific events class NewtonMachine { mitkNewMessageMacro(AnalysisStarted); mitkNewMessage1Macro(AnalysisStopped, bool); mitkNewMessage1Macro(LawDiscovered, const Law&); //mitkNewMessageWithReturnMacro(PatentFiled, bool); public: void StartAnalysis() { // send the "started" signal m_AnalysisStartedMessage(); // we found a new law of nature by creating one :-) Law massLaw("Unit tests are mandatory!"); m_LawDiscoveredMessage(massLaw); } void StopAnalysis() { // send the "stop" message with true, indicating // that an error occurred m_AnalysisStoppedMessage(true); } bool PatentLaw() { //bool patentAccepted = m_PatentFiledMessage(); //return patentAccepted; //m_PatentFiledMessage(); return false; } }; class Observer { private: NewtonMachine* m_Machine; public: Observer(NewtonMachine* machine) : m_Machine(machine), m_MachineStarted(false), m_MachineStopped(false), m_Error(false), m_Law("NONE") { // Add "observers", i.e. function pointers to the machine m_Machine->AddAnalysisStartedListener( ::mitk::MessageDelegate(this, &Observer::MachineStarted)); m_Machine->AddAnalysisStoppedListener( ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); m_Machine->AddLawDiscoveredListener( ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); //m_Machine->AddPatentFiledListener( // ::mitk::MessageDelegate(this, &Observer::ReviewPatent)); } ~Observer() { // Always remove your observers when finished m_Machine->RemoveAnalysisStartedListener( ::mitk::MessageDelegate(this, &Observer::MachineStarted)); m_Machine->RemoveAnalysisStoppedListener( ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); m_Machine->RemoveLawDiscoveredListener( ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); //m_Machine->RemoveLawDiscoveredListener( // ::mitk::MessageDelegate(this, &Observer::ReviewPatent)); } void MachineStarted() { m_MachineStarted = true; } void MachineStopped(bool error) { m_MachineStopped = true; m_Error = error; } void LawDiscovered(const Law& law) { m_Law = law; } bool ReviewPatent() { m_PatentReviewed = true; return false; // laws of nature are not patentable. } bool m_MachineStarted; bool m_MachineStopped; bool m_Error; Law m_Law; bool m_PatentReviewed; }; }; // end test class } // end namespace int mitkMessageTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("Message") mitk::mitkMessageTestTestClass::MessageSenderClass sender; mitk::mitkMessageTestTestClass::MessageReceiverClass receiver; MITK_TEST_CONDITION_REQUIRED(true, "Testing instantiation"); receiver.RegisterObservers(sender); MITK_TEST_CONDITION_REQUIRED(true, "Testing registration to messages"); MITK_TEST_CONDITION_REQUIRED( (sender.DoWaveHand(), // This is called "comma operator". Don't ask, read! receiver.HandWaved()), "Message without parameters"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED( (sender.DoShowFinger(), receiver.HandWaved()), "Message without parameters"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED( (sender.DoSay("Blooodworsch"), receiver.WordsSaid() == "Blooodworsch"), "Message with std::string parameter"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED( (sender.DoWalk(2.67), (receiver.MetersWalked() - 2.67) < 0.0001 ), "Message with double parameter"); receiver.Amnesia(); mitk::mitkMessageTestTestClass::Package package(8); MITK_TEST_CONDITION_REQUIRED( (sender.DoGivePackage(package), receiver.PackageReceived() == package), "Message with class parameter"); receiver.Amnesia(); MITK_TEST_CONDITION_REQUIRED( (sender.DoShoutAgeAndFootSize(46, 30.5), (receiver.Age() == 46 && (receiver.FootSize() - 30.5 < 0.0001))), "Message with int AND float parameter"); receiver.Amnesia(); mitk::mitkMessageTestTestClass::NewtonMachine newtonMachine; mitk::mitkMessageTestTestClass::Observer observer1(&newtonMachine); mitk::mitkMessageTestTestClass::Observer observer2(&newtonMachine); // This will send two events to registered observers newtonMachine.StartAnalysis(); MITK_TEST_CONDITION(observer1.m_MachineStarted == true, "Message from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer2.m_MachineStarted == true, "Message from Message Macro send to receiver 2"); MITK_TEST_CONDITION(observer1.m_Law.GetDescription() == std::string("Unit tests are mandatory!"), "Message1 from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer2.m_Law.GetDescription() == std::string("Unit tests are mandatory!"), "Message1 from Message Macro send to receiver 2"); // This will send one event to registered observers newtonMachine.StopAnalysis(); MITK_TEST_CONDITION(observer1.m_MachineStopped == true, "Message1 from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer1.m_Error == true, "Message1 parameter from Message Macro send to receiver 1"); MITK_TEST_CONDITION(observer2.m_MachineStopped == true, "Message1 from Message Macro send to receiver 2"); MITK_TEST_CONDITION(observer2.m_Error == true, "Message1 parameter from Message Macro send to receiver 2"); /* Message with return type tests are work in progess... */ //bool patentSuccessful = newtonMachine.PatentLaw(); // what with return types from multiple observers? //MITK_TEST_CONDITION((observer1.m_PatentReviewed == true) && (patentSuccessful == false), // "Message with return type from Message Macro send to receiver 1"); // //MITK_TEST_CONDITION((observer2.m_PatentReviewed == true) && (patentSuccessful == false), // "Message with return type from Message Macro send to receiver 2"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkNodePredicateSourceTest.cpp b/Modules/Core/test/mitkNodePredicateSourceTest.cpp index 5d5312195d..0d75e7ef89 100644 --- a/Modules/Core/test/mitkNodePredicateSourceTest.cpp +++ b/Modules/Core/test/mitkNodePredicateSourceTest.cpp @@ -1,51 +1,50 @@ /*=================================================================== 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 "mitkNodePredicateSource.h" #include "mitkTestingMacros.h" #include "mitkStandaloneDataStorage.h" int mitkNodePredicateSourceTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("Testing nodePredicateSource") // Create a DataStorage mitk::DataStorage::Pointer myDataStorage(mitk::StandaloneDataStorage::New().GetPointer()); mitk::DataNode::Pointer godfather, grandMother, mother, daughter; godfather = mitk::DataNode::New(); grandMother = mitk::DataNode::New(); mother = mitk::DataNode::New(); daughter = mitk::DataNode::New(); myDataStorage->Add(godfather); myDataStorage->Add(grandMother); myDataStorage->Add(mother, grandMother); myDataStorage->Add(daughter, mother); mitk::NodePredicateSource::Pointer testPredicate = mitk::NodePredicateSource::New(grandMother, false, myDataStorage); ; MITK_TEST_CONDITION_REQUIRED(true, "Testing instantiation"); MITK_TEST_CONDITION_REQUIRED(testPredicate->CheckNode(mother), "Node is derivative"); MITK_TEST_CONDITION_REQUIRED(!testPredicate->CheckNode(godfather), "Node is not derivative"); MITK_TEST_CONDITION_REQUIRED(!testPredicate->CheckNode(daughter), "Node is derivative but only direct derivatives are wanted"); - testPredicate = nullptr; testPredicate = mitk::NodePredicateSource::New(grandMother, true, myDataStorage); MITK_TEST_CONDITION_REQUIRED(testPredicate->CheckNode(daughter), "Node is not direct derivative and all derivatives are wanted "); MITK_TEST_CONDITION_REQUIRED(!testPredicate->CheckNode(grandMother), "Self is not a derivative!"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPixelTypeTest.cpp b/Modules/Core/test/mitkPixelTypeTest.cpp index cc897162d1..7e37a8d6e4 100644 --- a/Modules/Core/test/mitkPixelTypeTest.cpp +++ b/Modules/Core/test/mitkPixelTypeTest.cpp @@ -1,139 +1,136 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include "mitkPixelType.h" #include #include #include struct MyObscurePixelType { typedef float ValueType; static const size_t Length = 2; - - float val1; - int val2; }; //## Documentation //## main testing method int mitkPixelTypeTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("PixelTypeTest"); MITK_INFO << "ptype = mitk::MakePixelType();"; MITK_INFO << "itkPtype = mitk::MakePixelType();\n with itk::Image, 3> ItkImageType"; mitk::PixelType ptype = mitk::MakePixelType(); typedef itk::Image, 3> ItkImageType; mitk::PixelType itkPtype = mitk::MakePixelType(); MITK_TEST_CONDITION_REQUIRED( ptype.GetComponentType() == itk::ImageIOBase::INT, "GetComponentType()"); // MITK_TEST_CONDITION( ptype.GetPixelTypeId() == typeid(ItkImageType), "GetPixelTypeId()"); MITK_INFO << ptype.GetPixelTypeAsString(); MITK_INFO << typeid(ItkImageType).name(); MITK_TEST_CONDITION_REQUIRED( ptype.GetBpe() == 8*sizeof(int)*5, "[ptype] GetBpe()"); MITK_TEST_CONDITION_REQUIRED( ptype.GetNumberOfComponents() == 5, "[ptype]GetNumberOfComponents()"); MITK_TEST_CONDITION_REQUIRED( ptype.GetBitsPerComponent() == 8*sizeof(int), "[ptype]GetBitsPerComponent()"); MITK_TEST_CONDITION_REQUIRED( itkPtype.GetBpe() == 8*sizeof(int)*5, "[itkPType] GetBpe()"); MITK_TEST_CONDITION_REQUIRED( itkPtype.GetNumberOfComponents() == 5, "[itkPType] GetNumberOfComponents()"); MITK_TEST_CONDITION_REQUIRED( itkPtype.GetBitsPerComponent() == 8*sizeof(int), "[itkPType] GetBitsPerComponent()"); // MITK_TEST_CONDITION_REQUIRED( itkPtype == ptype, "[itkPtype = ptype]"); //MITK_TEST_CONDITION( ptype.GetPixelTypeAsString().compare("unknown") == 0, "GetPixelTypeAsString()"); { { mitk::PixelType ptype2( ptype); MITK_TEST_CONDITION_REQUIRED( ptype2.GetComponentType() == itk::ImageIOBase::INT, "ptype2( ptype)- GetComponentType()"); MITK_TEST_CONDITION( ptype2.GetPixelType() == ptype.GetPixelType(), "ptype2( ptype)-GetPixelType("); MITK_TEST_CONDITION( ptype2.GetComponentType() == ptype.GetComponentType(), "ptype2( ptype)-GetComponentType("); MITK_TEST_CONDITION_REQUIRED( ptype2.GetBpe() == 8*sizeof(int)*5, "ptype2( ptype)-GetBpe()"); MITK_TEST_CONDITION_REQUIRED( ptype2.GetNumberOfComponents() == 5, "ptype2( ptype)-GetNumberOfComponents()"); MITK_TEST_CONDITION_REQUIRED( ptype2.GetBitsPerComponent() == 8*sizeof(int), "ptype2( ptype)-GetBitsPerComponent()"); // MITK_TEST_CONDITION_REQUIRED( ptype.GetPixelTypeAsString().compare("unknown") == 0, "ptype2( ptype)-GetPixelTypeAsString()"); } { mitk::PixelType ptype2 = ptype; MITK_TEST_CONDITION_REQUIRED( ptype2.GetComponentType() == itk::ImageIOBase::INT, "ptype2 = ptype- GetComponentType()"); MITK_TEST_CONDITION( ptype2.GetPixelType() == ptype.GetPixelType(), "ptype2 = ptype- GetPixelType("); MITK_TEST_CONDITION( ptype2.GetComponentType() == ptype.GetComponentType(), "ptype2( ptype)-GetComponentType("); MITK_TEST_CONDITION_REQUIRED( ptype2.GetBpe() == 8*sizeof(int)*5, "ptype2 = ptype- GetBpe()"); MITK_TEST_CONDITION_REQUIRED( ptype2.GetNumberOfComponents() == 5, "ptype2 = ptype- GetNumberOfComponents()"); MITK_TEST_CONDITION_REQUIRED( ptype2.GetBitsPerComponent() == 8*sizeof(int), "ptype2 = ptype- GetBitsPerComponent()"); // MITK_TEST_CONDITION_REQUIRED( ptype.GetPixelTypeAsString().compare("unknown") == 0, "ptype2 = ptype- GetPixelTypeAsString()"); } { mitk::PixelType ptype2 = ptype; MITK_TEST_CONDITION_REQUIRED( ptype == ptype2, "operator =="); //MITK_TEST_CONDITION_REQUIRED( ptype == typeid(int), "operator =="); //mitk::PixelType ptype3 = mitk::MakePixelType; //MITK_TEST_CONDITION_REQUIRED( ptype != ptype3, "operator !="); //MITK_TEST_CONDITION_REQUIRED( *ptype3 != typeid(int), "operator !="); } } // test instantiation typedef itk::Image< MyObscurePixelType > MyObscureImageType; mitk::PixelType obscurePixelType = mitk::MakePixelType< MyObscureImageType >(); MITK_TEST_CONDITION( obscurePixelType.GetPixelType() == itk::ImageIOBase::UNKNOWNPIXELTYPE, "PixelTypeId is 'UNKNOWN' "); MITK_TEST_CONDITION( obscurePixelType.GetNumberOfComponents() == MyObscurePixelType::Length, "Lenght was set correctly"); MITK_TEST_CONDITION( obscurePixelType.GetComponentType() == mitk::MapPixelComponentType::value, "ValueType corresponds." ); typedef itk::VectorImage< short, 3> VectorImageType; mitk::PixelType vectorPixelType = mitk::MakePixelType< VectorImageType >( 78 ); //vectorPixelType.SetVectorLength( 78 ); typedef itk::Image< itk::Vector< short, 7> > FixedVectorImageType; mitk::PixelType fixedVectorPixelType = mitk::MakePixelType< FixedVectorImageType >(); mitk::PixelType scalarPixelType = mitk::MakeScalarPixelType< float >(); // test ImageTypeTrait traits class MITK_TEST_CONDITION(typeid(mitk::ImageTypeTrait::ImageType) == typeid(itk::Image), "ImageTypeTrait"); MITK_TEST_CONDITION((mitk::ImageTypeTrait::IsVectorImage == false), "ImageTypeTrait"); MITK_TEST_CONDITION(typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::Image, 3>), "ImageTypeTrait"); MITK_TEST_CONDITION((mitk::ImageTypeTrait, 3>::IsVectorImage == false), "ImageTypeTrait"); MITK_TEST_CONDITION(typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::VectorImage), "ImageTypeTrait"); MITK_TEST_CONDITION((mitk::ImageTypeTrait, 3>::IsVectorImage == true), "ImageTypeTrait"); MITK_TEST_CONDITION(typeid(mitk::ImageTypeTrait >::ImageType) == typeid(itk::Image), "ImageTypeTrait"); MITK_TEST_CONDITION((mitk::ImageTypeTrait >::IsVectorImage == false), "ImageTypeTrait"); MITK_TEST_CONDITION(typeid(mitk::ImageTypeTrait, 3> >::ImageType) == typeid(itk::Image, 3>), "ImageTypeTrait"); MITK_TEST_CONDITION((mitk::ImageTypeTrait, 3> >::IsVectorImage == false), "ImageTypeTrait"); MITK_TEST_CONDITION(typeid(mitk::ImageTypeTrait >::ImageType) == typeid(itk::VectorImage), "ImageTypeTrait"); MITK_TEST_CONDITION((mitk::ImageTypeTrait >::IsVectorImage == true), "ImageTypeTrait"); // test CastableTo MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPlaneGeometryTest.cpp b/Modules/Core/test/mitkPlaneGeometryTest.cpp index 5d31893fc1..78b71f8ad7 100644 --- a/Modules/Core/test/mitkPlaneGeometryTest.cpp +++ b/Modules/Core/test/mitkPlaneGeometryTest.cpp @@ -1,944 +1,941 @@ /*=================================================================== 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 "mitkPlaneGeometry.h" #include "mitkRotationOperation.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkGeometry3D.h" #include "mitkThinPlateSplineCurvedGeometry.h" #include "mitkSlicedGeometry3D.h" #include "mitkAffineTransform3D.h" #include "mitkBaseGeometry.h" #include #include #include #include #include #include static const mitk::ScalarType testEps = 1E-9; // the epsilon used in this test == at least float precision. class mitkPlaneGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPlaneGeometryTestSuite); MITK_TEST(TestInitializeStandardPlane); MITK_TEST(TestProjectPointOntoPlane); MITK_TEST(TestPlaneGeometryCloning); MITK_TEST(TestInheritance); MITK_TEST(TestSetExtendInMM); MITK_TEST(TestRotate); MITK_TEST(TestClone); MITK_TEST(TestPlaneComparison); MITK_TEST(TestAxialInitialization); MITK_TEST(TestFrontalInitialization); MITK_TEST(TestSaggitalInitialization); MITK_TEST(TestLefthandedCoordinateSystem); // Currently commented out, see See bug 15990 // MITK_TEST(testPlaneGeometryInitializeOrder); MITK_TEST(TestIntersectionPoint); MITK_TEST(TestCase1210); CPPUNIT_TEST_SUITE_END(); private: // private test members that are initialized by setUp() mitk::PlaneGeometry::Pointer planegeometry; mitk::Point3D origin; mitk::Vector3D right, bottom, normal; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM, thicknessInMM; public: void setUp() override { planegeometry = mitk::PlaneGeometry::New(); width = 100; widthInMM = width; height = 200; heightInMM = height; thicknessInMM = 1.0; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); mitk::FillVector3D(normal, 0, 0, thicknessInMM); planegeometry->InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector()); planegeometry->SetOrigin(origin); } void tearDown() override { } // This test verifies inheritance behaviour, this test will fail if the behaviour changes in the future void TestInheritance() { mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); mitk::Geometry3D::Pointer g3d = dynamic_cast < mitk::Geometry3D* > ( plane.GetPointer() ); CPPUNIT_ASSERT_MESSAGE("Planegeometry should not be castable to Geometry 3D", g3d.IsNull()); mitk::BaseGeometry::Pointer base = dynamic_cast < mitk::BaseGeometry* > ( plane.GetPointer() ); CPPUNIT_ASSERT_MESSAGE("Planegeometry should be castable to BaseGeometry", base.IsNotNull()); - base = nullptr; g3d = mitk::Geometry3D::New(); base = dynamic_cast < mitk::BaseGeometry* > ( g3d.GetPointer() ); CPPUNIT_ASSERT_MESSAGE("Geometry3D should be castable to BaseGeometry", base.IsNotNull()); - g3d=nullptr; mitk::SlicedGeometry3D::Pointer sliced = mitk::SlicedGeometry3D::New(); g3d = dynamic_cast < mitk::Geometry3D* > ( sliced.GetPointer() ); CPPUNIT_ASSERT_MESSAGE("SlicedGeometry3D should not be castable to Geometry3D", g3d.IsNull()); - plane=nullptr; mitk::ThinPlateSplineCurvedGeometry::Pointer thin = mitk::ThinPlateSplineCurvedGeometry::New(); plane = dynamic_cast < mitk::PlaneGeometry* > ( thin.GetPointer() ); CPPUNIT_ASSERT_MESSAGE("AbstractTransformGeometry should be castable to PlaneGeometry", plane.IsNotNull()); plane = mitk::PlaneGeometry::New(); mitk::AbstractTransformGeometry::Pointer atg = dynamic_cast < mitk::AbstractTransformGeometry* > ( plane.GetPointer() ); CPPUNIT_ASSERT_MESSAGE("PlaneGeometry should not be castable to AbstractTransofrmGeometry", atg.IsNull()); } void TestLefthandedCoordinateSystem() { /** * @brief This method tests InitializeStandardPlane() and IndexToWorld() * with a left-handed coordinate orientation or indexToWorldMatrix. * * Of course this test does not use standard Parameters, which are right-handed. * See also discussion of bug #11477: http://bugs.mitk.org/show_bug.cgi?id=11477 */ planegeometry = mitk::PlaneGeometry::New(); width = 100; widthInMM = 5; height = 200; heightInMM = 3; thicknessInMM = 1.0; mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); // This one negative sign results in lefthanded coordinate orientation and det(matrix) < 0. mitk::FillVector3D(normal, 0, 0, -thicknessInMM); mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix; mitk::AffineTransform3D::MatrixType::InternalMatrixType& vnl_matrix = matrix.GetVnlMatrix(); vnl_matrix.set_column(0, right); vnl_matrix.set_column(1, bottom); vnl_matrix.set_column(2, normal); // making sure that we didn't screw up this special test case or else fail deadly: assert( vnl_determinant(vnl_matrix) < 0.0 ); transform->SetIdentity(); transform->SetMatrix(matrix); planegeometry->InitializeStandardPlane( width, height, transform ); // Crux of the matter. CPPUNIT_ASSERT_MESSAGE("Testing if IndexToWorldMatrix is correct after InitializeStandardPlane( width, height, transform ) ", mitk::MatrixEqualElementWise( planegeometry->GetIndexToWorldTransform()->GetMatrix(), matrix ) ); mitk::Point3D p_index; p_index[0] = 10.; p_index[1] = 10.; p_index[2] = 0.; mitk::Point3D p_world; mitk::Point3D p_expectedResult; p_expectedResult[0] = 50.; p_expectedResult[1] = 30.; p_expectedResult[2] = 0.; ((mitk::BaseGeometry::Pointer) planegeometry)->IndexToWorld(p_index, p_world); // Crux of the matter. CPPUNIT_ASSERT_MESSAGE( "Testing if IndexToWorld(a,b) function works correctly with lefthanded matrix ", mitk::Equal(p_world, p_expectedResult, testEps ) ); } // See bug 1210 // Test does not use standard Parameters void TestCase1210() { mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; mitk::Vector3D right, down, spacing; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, 1.015625, 1.015625, 1.1999969482421875 ); mitk::FillVector3D(down, 1.4012984643248170709237295832899161312802619418765e-45, 0, 0 ); mitk::FillVector3D(spacing, 0, 1.4713633875410579244699160624544119378442750389703e-43, 9.2806360452222355258639080851310540729807238879469e-32 ); std::cout << "Testing InitializeStandardPlane(rightVector, downVector, spacing = NULL): "<InitializeStandardPlane(right, down, &spacing)); /* std::cout << "Testing width, height and thickness (in units): "; if((mitk::Equal(planegeometry->GetExtent(0),width)==false) || (mitk::Equal(planegeometry->GetExtent(1),height)==false) || (mitk::Equal(planegeometry->GetExtent(2),1)==false) ) { std::cout<<"[FAILED]"<GetExtentInMM(0),widthInMM)==false) || (mitk::Equal(planegeometry->GetExtentInMM(1),heightInMM)==false) || (mitk::Equal(planegeometry->GetExtentInMM(2),thicknessInMM)==false) ) { std::cout<<"[FAILED]"< 0. * */ // Test does not use standard Parameters void TestIntersectionPoint() { //init plane with its parameter mitk::PlaneGeometry::Pointer myPlaneGeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; origin[0] = 0.0; origin[1] = 2.0; origin[2] = 0.0; mitk::Vector3D normal; normal[0] = 0.0; normal[1] = 1.0; normal[2] = 0.0; myPlaneGeometry->InitializePlane(origin,normal); //generate points and line for intersection testing //point distance of given line > 1 mitk::Point3D pointP1; pointP1[0] = 2.0; pointP1[1] = 1.0; pointP1[2] = 0.0; mitk::Point3D pointP2; pointP2[0] = 2.0; pointP2[1] = 4.0; pointP2[2] = 0.0; mitk::Vector3D lineDirection; lineDirection[0] = pointP2[0] - pointP1[0]; lineDirection[1] = pointP2[1] - pointP1[1]; lineDirection[2] = pointP2[2] - pointP1[2]; mitk::Line3D xingline( pointP1, lineDirection ); mitk::Point3D calcXingPoint; myPlaneGeometry->IntersectionPoint(xingline, calcXingPoint); //point distance of given line < 1 mitk::Point3D pointP3; pointP3[0] = 2.0; pointP3[1] = 2.2; pointP3[2] = 0.0; mitk::Point3D pointP4; pointP4[0] = 2.0; pointP4[1] = 1.7; pointP4[2] = 0.0; mitk::Vector3D lineDirection2; lineDirection2[0] = pointP4[0] - pointP3[0]; lineDirection2[1] = pointP4[1] - pointP3[1]; lineDirection2[2] = pointP4[2] - pointP3[2]; mitk::Line3D xingline2( pointP3, lineDirection2 ); mitk::Point3D calcXingPoint2; myPlaneGeometry->IntersectionPoint( xingline2, calcXingPoint2 ); //intersection points must be the same CPPUNIT_ASSERT_MESSAGE("Failed to calculate Intersection Point", calcXingPoint == calcXingPoint2); } /** * @brief This method tests method ProjectPointOntoPlane. * * See also bug #3409. */ // Test does not use standard Parameters void TestProjectPointOntoPlane() { mitk::PlaneGeometry::Pointer myPlaneGeometry = mitk::PlaneGeometry::New(); //create normal mitk::Vector3D normal; normal[0] = 0.0; normal[1] = 0.0; normal[2] = 1.0; //create origin mitk::Point3D origin; origin[0] = -27.582859; origin[1] = 50; origin[2] = 200.27742; //initialize plane geometry myPlaneGeometry->InitializePlane(origin,normal); //output to descripe the test std::cout << "Testing PlaneGeometry according to bug #3409" << std::endl; std::cout << "Our normal is: " << normal << std::endl; std::cout << "So ALL projected points should have exactly the same z-value!" << std::endl; //create a number of points mitk::Point3D myPoints[5]; myPoints[0][0] = -27.582859; myPoints[0][1] = 50.00; myPoints[0][2] = 200.27742; myPoints[1][0] = -26.58662; myPoints[1][1] = 50.00; myPoints[1][2] = 200.19026; myPoints[2][0] = -26.58662; myPoints[2][1] = 50.00; myPoints[2][2] = 200.33124; myPoints[3][0] = 104.58662; myPoints[3][1] = 452.12313; myPoints[3][2] = 866.41236; myPoints[4][0] = -207.58662; myPoints[4][1] = 312.00; myPoints[4][2] = -300.12346; //project points onto plane mitk::Point3D myProjectedPoints[5]; for ( unsigned int i = 0; i < 5; ++i ) { myProjectedPoints[i] = myPlaneGeometry->ProjectPointOntoPlane( myPoints[i] ); } //compare z-values with z-value of plane (should be equal) bool allPointsOnPlane = true; for (auto & myProjectedPoint : myProjectedPoints) { if ( fabs(myProjectedPoint[2] - origin[2]) > mitk::sqrteps ) { allPointsOnPlane = false; } } CPPUNIT_ASSERT_MESSAGE("All points lie not on the same plane", allPointsOnPlane); } void TestPlaneGeometryCloning() { mitk::PlaneGeometry::Pointer geometry2D = createPlaneGeometry(); try { mitk::PlaneGeometry::Pointer clone = geometry2D->Clone(); itk::Matrix matrix = clone->GetIndexToWorldTransform()->GetMatrix(); CPPUNIT_ASSERT_MESSAGE("Test if matrix element exists...", matrix[0][0] == 31); double origin = geometry2D->GetOrigin()[0]; CPPUNIT_ASSERT_MESSAGE("First Point of origin as expected...", mitk::Equal(origin, 8)); double spacing = geometry2D->GetSpacing()[0]; CPPUNIT_ASSERT_MESSAGE("First Point of spacing as expected...", mitk::Equal(spacing, 31)); } catch (...) { CPPUNIT_FAIL("Error during access on a member of cloned geometry"); } // direction [row] [coloum] MITK_TEST_OUTPUT( << "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same" ); } void TestPlaneGeometryInitializeOrder() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); mitk::PlaneGeometry::Pointer geometry2D1 = mitk::PlaneGeometry::New(); geometry2D1->SetIndexToWorldTransform(myTransform); geometry2D1->SetSpacing(mySpacing); geometry2D1->SetOrigin(myOrigin); mitk::PlaneGeometry::Pointer geometry2D2 = mitk::PlaneGeometry::New(); geometry2D2->SetSpacing(mySpacing); geometry2D2->SetOrigin(myOrigin); geometry2D2->SetIndexToWorldTransform(myTransform); mitk::PlaneGeometry::Pointer geometry2D3 = mitk::PlaneGeometry::New(); geometry2D3->SetIndexToWorldTransform(myTransform); geometry2D3->SetSpacing(mySpacing); geometry2D3->SetOrigin(myOrigin); geometry2D3->SetIndexToWorldTransform(myTransform); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 1 matches that of Geometry 2.", mitk::Equal(geometry2D1->GetOrigin(), geometry2D2->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 1 match those of Geometry 3.", mitk::Equal(geometry2D1->GetOrigin(), geometry2D3->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 2 match those of Geometry 3.", mitk::Equal(geometry2D2->GetOrigin(), geometry2D3->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 1 match those of Geometry 2.", mitk::Equal(geometry2D1->GetSpacing(), geometry2D2->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 1 match those of Geometry 3.", mitk::Equal(geometry2D1->GetSpacing(), geometry2D3->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 2 match those of Geometry 3.", mitk::Equal(geometry2D2->GetSpacing(), geometry2D3->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 1 match those of Geometry 2.", compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D2->GetIndexToWorldTransform()->GetMatrix())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 1 match those of Geometry 3.", compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 2 match those of Geometry 3.", compareMatrix(geometry2D2->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix())); } void TestInitializeStandardPlane() { CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: width", mitk::Equal(planegeometry->GetExtent(0),width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: height", mitk::Equal(planegeometry->GetExtent(1),height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: depth", mitk::Equal(planegeometry->GetExtent(2),1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: width in mm", mitk::Equal(planegeometry->GetExtentInMM(0),widthInMM, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: heght in mm", mitk::Equal(planegeometry->GetExtentInMM(1),heightInMM, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: depth in mm", mitk::Equal(planegeometry->GetExtentInMM(2),thicknessInMM, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorRight", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorBottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorNormal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps) ); mitk::Vector3D spacing; thicknessInMM = 1.5; normal.Normalize(); normal *= thicknessInMM; mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector(), &spacing); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: width", mitk::Equal(planegeometry->GetExtent(0),width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: height", mitk::Equal(planegeometry->GetExtent(1),height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: depth", mitk::Equal(planegeometry->GetExtent(2),1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: width in mm", mitk::Equal(planegeometry->GetExtentInMM(0),widthInMM, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: height in mm", mitk::Equal(planegeometry->GetExtentInMM(1),heightInMM, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: depth in mm", mitk::Equal(planegeometry->GetExtentInMM(2),thicknessInMM, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorRight", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorBottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorNormal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps) ); ; } void TestSetExtendInMM() { normal.Normalize(); normal *= thicknessInMM; planegeometry->SetExtentInMM(2, thicknessInMM); CPPUNIT_ASSERT_MESSAGE("Testing SetExtentInMM(2, ...), querying by GetExtentInMM(2): ", mitk::Equal(planegeometry->GetExtentInMM(2),thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetExtentInMM(2, ...), querying by GetAxisVector(2) and comparing to normal: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); planegeometry->SetOrigin(origin); CPPUNIT_ASSERT_MESSAGE("Testing SetOrigin", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Right", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps) ); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps) ); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestRotate() { // Changing the IndexToWorldTransform to a rotated version by SetIndexToWorldTransform() (keep origin): mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = planegeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::VnlVector axis(3); mitk::FillVector3D(axis, 1.0, 1.0, 1.0); axis.normalize(); vnl_quaternion rotation(axis, 0.223); vnlmatrix = rotation.rotation_matrix_transpose()*vnlmatrix; mitk::Matrix3D matrix; matrix = vnlmatrix; transform->SetMatrix(matrix); transform->SetOffset(planegeometry->GetIndexToWorldTransform()->GetOffset()); right.SetVnlVector( rotation.rotation_matrix_transpose()*right.GetVnlVector() ); bottom.SetVnlVector(rotation.rotation_matrix_transpose()*bottom.GetVnlVector()); normal.SetVnlVector(rotation.rotation_matrix_transpose()*normal.GetVnlVector()); planegeometry->SetIndexToWorldTransform(transform); //The origin changed,because m_Origin=m_IndexToWorldTransform->GetOffset()+GetAxisVector(2)*0.5 //and the AxisVector changes due to the rotation. In other words: the rotation was done around //the corner of the box, not around the planes origin. Now change it to a rotation around //the origin, simply by re-setting the origin to the original one: planegeometry->SetOrigin(origin); CPPUNIT_ASSERT_MESSAGE("Testing whether SetIndexToWorldTransform kept origin: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps) ); mitk::Point2D point; point[0] = 4; point[1] = 3; mitk::Point2D dummy; planegeometry->WorldToIndex(point, dummy); planegeometry->IndexToWorld(dummy, dummy); CPPUNIT_ASSERT_MESSAGE("Testing consistency of index and world coordinates.", dummy == point); CPPUNIT_ASSERT_MESSAGE("Testing width of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(0),widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(1),heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(2),thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: right ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(0).GetNorm(),planegeometry->GetExtentInMM(0), testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(1).GetNorm(),planegeometry->GetExtentInMM(1), testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(2).GetNorm(),planegeometry->GetExtentInMM(2), testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); width *= 2; height *= 3; planegeometry->SetSizeInUnits(width, height); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(0),width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(1),height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: " , mitk::Equal(planegeometry->GetExtent(2),1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: right ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(0).GetNorm(),planegeometry->GetExtentInMM(0), testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(1).GetNorm(),planegeometry->GetExtentInMM(1), testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(2).GetNorm(),planegeometry->GetExtentInMM(2), testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestClone() { mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); // Cave: Statement below is negated! CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", ! ((clonedplanegeometry.IsNull()) || (clonedplanegeometry->GetReferenceCount()!=1))); CPPUNIT_ASSERT_MESSAGE("Testing origin of cloned version: ", mitk::Equal(clonedplanegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(0),width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(1),height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing extent (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(2),1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: right", mitk::Equal(clonedplanegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: bottom", mitk::Equal(clonedplanegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: normal", mitk::Equal(clonedplanegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(clonedplanegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestSaggitalInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); // Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Sagittal, zPosition = 0, frontside=true): planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Sagittal); mitk::Vector3D newright, newbottom, newnormal; mitk::ScalarType newthicknessInMM; newright = bottom; newthicknessInMM = widthInMM/width*1.0; // extent in normal direction is 1; newnormal = right; newnormal.Normalize(); newnormal *= newthicknessInMM; newbottom = normal; newbottom.Normalize(); newbottom *= thicknessInMM; CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of sagitally initialized version:", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0, testEps)); //ok, corner was fine, so we can dare to believe the origin is ok. origin = planegeometry->GetOrigin(); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagitally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagitally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagitally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), newnormal, testEps)); mappingTests2D(planegeometry, height, 1, heightInMM, thicknessInMM, origin, newright, newbottom); // set origin back to the one of the axial slice: origin = clonedplanegeometry->GetOrigin(); // Testing backside initialization: InitializeStandardPlane(clonedplanegeometry, planeorientation = Axial, zPosition = 0, frontside=false, rotated=true): planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Axial, 0, false, true); mitk::Point3D backsideorigin; backsideorigin=origin+clonedplanegeometry->GetAxisVector(1);//+clonedplanegeometry->GetAxisVector(2); CPPUNIT_ASSERT_MESSAGE("Testing origin of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetOrigin(), backsideorigin, testEps)); mitk::Point3D backsidecornerpoint0; backsidecornerpoint0 = cornerpoint0+clonedplanegeometry->GetAxisVector(1);//+clonedplanegeometry->GetAxisVector(2); CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of sagitally initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), backsidecornerpoint0, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version (should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version (should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version (should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), -bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), -normal, testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, backsideorigin, right, -bottom); } void TestFrontalInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); //-------- mitk::Vector3D newright, newbottom, newnormal; mitk::ScalarType newthicknessInMM; // Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Frontal, zPosition = 0, frontside=true) planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Frontal); newright = right; newbottom = normal; newbottom.Normalize(); newbottom *= thicknessInMM; newthicknessInMM = heightInMM/height*1.0/*extent in normal direction is 1*/; newnormal = -bottom; newnormal.Normalize(); newnormal *= newthicknessInMM; CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of frontally initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0, testEps)); //ok, corner was fine, so we can dare to believe the origin is ok. origin = planegeometry->GetOrigin(); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in units) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), newnormal, testEps)); mappingTests2D(planegeometry, width, 1, widthInMM, thicknessInMM, origin, newright, newbottom); //Changing plane to in-plane unit spacing using SetSizeInUnits: planegeometry->SetSizeInUnits(planegeometry->GetExtentInMM(0), planegeometry->GetExtentInMM(1)); CPPUNIT_ASSERT_MESSAGE( "Testing origin of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), newnormal, testEps)); mappingTests2D(planegeometry, widthInMM, thicknessInMM, widthInMM, thicknessInMM, origin, newright, newbottom); // Changing plane to unit spacing also in normal direction using SetExtentInMM(2, 1.0): planegeometry->SetExtentInMM(2, 1.0); newnormal.Normalize(); CPPUNIT_ASSERT_MESSAGE("Testing origin of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), 1.0, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), newnormal, testEps)); mappingTests2D(planegeometry, widthInMM, thicknessInMM, widthInMM, thicknessInMM, origin, newright, newbottom); } void TestAxialInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); // Clone, move, rotate and test for 'IsParallel' and 'IsOnPlane' mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", ! ((clonedplanegeometry.IsNull()) || (clonedplanegeometry->GetReferenceCount()!=1)) ); std::cout << "Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Axial, zPosition = 0, frontside=true): " <InitializeStandardPlane(clonedplanegeometry); CPPUNIT_ASSERT_MESSAGE("Testing origin of axially initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin)); CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of axially initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0)); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of axially initialized version (should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of axially initialized version (should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in units) of axially initialized version (should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestPlaneComparison() { // Clone, move, rotate and test for 'IsParallel' and 'IsOnPlane' mitk::PlaneGeometry::Pointer clonedplanegeometry2 = dynamic_cast(planegeometry->Clone().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", ! ((clonedplanegeometry2.IsNull()) || (clonedplanegeometry2->GetReferenceCount()!=1)) ); CPPUNIT_ASSERT_MESSAGE("Testing wheter original and clone are at the same position", clonedplanegeometry2->IsOnPlane(planegeometry.GetPointer())); CPPUNIT_ASSERT_MESSAGE(" Asserting that origin is on the plane cloned plane:", clonedplanegeometry2->IsOnPlane(origin)); mitk::VnlVector newaxis(3); mitk::FillVector3D(newaxis, 1.0, 1.0, 1.0); newaxis.normalize(); vnl_quaternion rotation2(newaxis, 0.0); mitk::Vector3D clonednormal = clonedplanegeometry2->GetNormal(); mitk::Point3D clonedorigin = clonedplanegeometry2->GetOrigin(); auto planerot = new mitk::RotationOperation( mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector( 0 ), 180.0 ); clonedplanegeometry2->ExecuteOperation( planerot ); CPPUNIT_ASSERT_MESSAGE(" Asserting that a flipped plane is still on the original plane: ", clonedplanegeometry2->IsOnPlane(planegeometry.GetPointer())); clonedorigin += clonednormal; clonedplanegeometry2->SetOrigin( clonedorigin ); CPPUNIT_ASSERT_MESSAGE("Testing if the translated (cloned, flipped) plane is parallel to its origin plane: ", clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation( mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector( 0 ), 0.5 ); clonedplanegeometry2->ExecuteOperation( planerot ); CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as not paralell [rotation +0.5 degree] : ", ! clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation( mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector( 0 ), -1.0 ); clonedplanegeometry2->ExecuteOperation( planerot ); CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as not paralell [rotation -0.5 degree] : ", ! clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation( mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector( 0 ), 360.5 ); clonedplanegeometry2->ExecuteOperation( planerot ); CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as paralell [rotation 360 degree] : ", clonedplanegeometry2->IsParallel(planegeometry)); } private: // helper Methods for the Tests mitk::PlaneGeometry::Pointer createPlaneGeometry() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); mitk::PlaneGeometry::Pointer geometry2D = mitk::PlaneGeometry::New(); geometry2D->SetIndexToWorldTransform(myTransform); geometry2D->SetSpacing(mySpacing); geometry2D->SetOrigin(myOrigin); return geometry2D; } bool compareMatrix(itk::Matrix left, itk::Matrix right) { bool equal = true; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) equal &= mitk::Equal(left[i][j], right[i][j]); return equal; } /** * This function tests for correct mapping and is called several times from other tests **/ void mappingTests2D(const mitk::PlaneGeometry* planegeometry, const mitk::ScalarType& width, const mitk::ScalarType& height, const mitk::ScalarType& widthInMM, const mitk::ScalarType& heightInMM, const mitk::Point3D& origin, const mitk::Vector3D& right, const mitk::Vector3D& bottom) { std::cout << "Testing mapping Map(pt2d_mm(x=widthInMM/2.3,y=heightInMM/2.5), pt3d_mm) and compare with expected: "; mitk::Point2D pt2d_mm; mitk::Point3D pt3d_mm, expected_pt3d_mm; pt2d_mm[0] = widthInMM/2.3; pt2d_mm[1] = heightInMM/2.5; expected_pt3d_mm = origin+right*(pt2d_mm[0]/right.GetNorm())+bottom*(pt2d_mm[1]/bottom.GetNorm()); planegeometry->Map(pt2d_mm, pt3d_mm); CPPUNIT_ASSERT_MESSAGE("Testing mapping Map(pt2d_mm(x=widthInMM/2.3,y=heightInMM/2.5), pt3d_mm) and compare with expected", mitk::Equal(pt3d_mm, expected_pt3d_mm, testEps)); std::cout << "Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected: "; mitk::Point2D testpt2d_mm; planegeometry->Map(pt3d_mm, testpt2d_mm); std::cout << std::setprecision(12) << "Expected pt2d_mm " << pt2d_mm << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_mm " << testpt2d_mm << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10*mitk::eps << std::endl; //This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected", mitk::Equal(pt2d_mm, testpt2d_mm, 10*mitk::eps)); std::cout << "Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: "; mitk::Point2D pt2d_units; pt2d_units[0] = width/2.0; pt2d_units[1] = height/2.0; pt2d_mm[0] = widthInMM/2.0; pt2d_mm[1] = heightInMM/2.0; planegeometry->IndexToWorld(pt2d_units, testpt2d_mm); std::cout << std::setprecision(12) << "Expected pt2d_mm " << pt2d_mm << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_mm " << testpt2d_mm << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10*mitk::eps << std::endl; //This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: ", mitk::Equal(pt2d_mm, testpt2d_mm, 10*mitk::eps)); std::cout << "Testing WorldToIndex(pt2d_mm, pt2d_units) and compare with expected: "; mitk::Point2D testpt2d_units; planegeometry->WorldToIndex(pt2d_mm, testpt2d_units); std::cout << std::setprecision(12) << "Expected pt2d_units " << pt2d_units << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_units " << testpt2d_units << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10*mitk::eps << std::endl; //This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing WorldToIndex(pt2d_mm, pt2d_units) and compare with expected:", mitk::Equal(pt2d_units, testpt2d_units, 10*mitk::eps)); } }; MITK_TEST_SUITE_REGISTRATION(mitkPlaneGeometry) diff --git a/Modules/Core/test/mitkPlanePositionManagerTest.cpp b/Modules/Core/test/mitkPlanePositionManagerTest.cpp index a2b0c61fee..e54757682f 100644 --- a/Modules/Core/test/mitkPlanePositionManagerTest.cpp +++ b/Modules/Core/test/mitkPlanePositionManagerTest.cpp @@ -1,276 +1,273 @@ /*=================================================================== 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 "mitkRotationOperation.h" #include "mitkTestingMacros.h" #include "mitkPlanePositionManager.h" #include "mitkSliceNavigationController.h" #include "mitkGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkImage.h" #include "mitkSurface.h" #include "mitkStandaloneDataStorage.h" #include "mitkDataNode.h" #include "mitkStringProperty.h" #include "mitkBaseProperty.h" #include "mitkInteractionConst.h" #include "vnl/vnl_vector.h" #include #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usServiceReference.h" std::vector m_Geometries; std::vector m_SliceIndices; mitk::PlanePositionManagerService* m_Service; int SetUpBeforeTest() { //Getting Service us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); m_Service = us::GetModuleContext()->GetService(serviceRef); if (m_Service == 0) return EXIT_FAILURE; //Creating different Geometries m_Geometries.reserve(100); mitk::PlaneGeometry::PlaneOrientation views[] = {mitk::PlaneGeometry::Axial, mitk::PlaneGeometry::Sagittal, mitk::PlaneGeometry::Frontal}; for (unsigned int i = 0; i < 100; ++i) { mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); mitk::ScalarType width = 256+(0.01*i); mitk::ScalarType height = 256+(0.002*i); mitk::Vector3D right; mitk::Vector3D down; right[0] = 1; right[1] = i; right[2] = 0.5; down[0] = i*0.02; down[1] = 1; down[2] = i*0.03; mitk::Vector3D spacing; mitk::FillVector3D(spacing, 1.0*0.02*i, 1.0*0.15*i, 1.0); mitk::Vector3D rightVector; mitk::FillVector3D(rightVector, 0.02*(i+1), 0+(0.05*i), 1.0); mitk::Vector3D downVector; mitk::FillVector3D(downVector, 1, 3-0.01*i, 0.0345*i); vnl_vector normal = vnl_cross_3d(rightVector.GetVnlVector(), downVector.GetVnlVector()); normal.normalize(); normal *= 1.5; mitk::Vector3D origin; origin.Fill(1); origin[0] = 12 + 0.03*i; mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector.GetVnlVector()); matrix.GetVnlMatrix().set_column(1, downVector.GetVnlVector()); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(origin); plane->InitializeStandardPlane(width, height, transform, views[i%3], i, true, false); m_Geometries.push_back(plane); } return EXIT_SUCCESS; } int testAddPlanePosition() { MITK_TEST_OUTPUT(<<"Starting Test: ######### A d d P l a n e P o s i t i o n #########"); MITK_TEST_CONDITION(m_Service != NULL, "Testing getting of PlanePositionManagerService"); unsigned int currentID(m_Service->AddNewPlanePosition(m_Geometries.at(0),0)); bool error = ((m_Service->GetNumberOfPlanePositions() != 1)||(currentID != 0)); if(error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 1,"Checking for correct number of planepositions"); MITK_TEST_CONDITION(currentID == 0, "Testing for correct ID"); return EXIT_FAILURE; } //Adding new planes for(unsigned int i = 1; i < m_Geometries.size(); ++i) { unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i),i); error = ((m_Service->GetNumberOfPlanePositions() != i+1)||(newID != (currentID+1))); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == i+1,"Checking for correct number of planepositions"); MITK_TEST_CONDITION(newID == (currentID+1), "Testing for correct ID"); MITK_TEST_OUTPUT(<<"New: "<GetNumberOfPlanePositions(); //Adding existing planes -> nothing should change for(unsigned int i = 0; i < (m_Geometries.size()-1)*0.5; ++i) { unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i*2),i*2); error = ((m_Service->GetNumberOfPlanePositions() != numberOfPlanePos)||(newID != i*2)); if (error) { MITK_TEST_CONDITION( m_Service->GetNumberOfPlanePositions() == numberOfPlanePos, "Checking for correct number of planepositions"); MITK_TEST_CONDITION(newID == i*2, "Testing for correct ID"); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int testGetPlanePosition() { - mitk::PlaneGeometry* plane; - mitk::RestorePlanePositionOperation* op; bool error(true); MITK_TEST_OUTPUT(<<"Starting Test: ######### G e t P l a n e P o s i t i o n #########"); //Testing for existing planepositions for (unsigned int i = 0; i < m_Geometries.size(); ++i) { - plane = m_Geometries.at(i); - op = m_Service->GetPlanePosition(i); + auto plane = m_Geometries.at(i); + auto op = m_Service->GetPlanePosition(i); error = ( !mitk::Equal(op->GetHeight(),plane->GetExtent(1)) || !mitk::Equal(op->GetWidth(),plane->GetExtent(0)) || !mitk::Equal(op->GetSpacing(),plane->GetSpacing()) || !mitk::Equal(op->GetTransform()->GetOffset(),plane->GetIndexToWorldTransform()->GetOffset()) || !mitk::Equal(op->GetDirectionVector().GetVnlVector(),plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).normalize()) || !mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()) ); if( error ) { MITK_TEST_OUTPUT(<<"Iteration: "<GetHeight(),plane->GetExtent(1)) && mitk::Equal(op->GetWidth(),plane->GetExtent(0)), "Checking for correct extent"); MITK_TEST_CONDITION( mitk::Equal(op->GetSpacing(),plane->GetSpacing()), "Checking for correct spacing"); MITK_TEST_CONDITION( mitk::Equal(op->GetTransform()->GetOffset(),plane->GetIndexToWorldTransform()->GetOffset()), "Checking for correct offset"); MITK_INFO<<"Op: "<GetDirectionVector()<<" plane: "<GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)<<"\n"; MITK_TEST_CONDITION( mitk::Equal(op->GetDirectionVector().GetVnlVector(),plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)), "Checking for correct direction"); MITK_TEST_CONDITION( mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()), "Checking for correct matrix"); return EXIT_FAILURE; } } //Testing for not existing planepositions error = ( m_Service->GetPlanePosition(100000000) != 0 || m_Service->GetPlanePosition(-1) != 0 ); if (error) { MITK_TEST_CONDITION(m_Service->GetPlanePosition(100000000) == 0, "Trying to get non existing pos"); MITK_TEST_CONDITION(m_Service->GetPlanePosition(-1) == 0, "Trying to get non existing pos"); return EXIT_FAILURE; } return EXIT_SUCCESS; } int testRemovePlanePosition() { MITK_TEST_OUTPUT(<<"Starting Test: ######### R e m o v e P l a n e P o s i t i o n #########"); unsigned int size = m_Service->GetNumberOfPlanePositions(); - bool removed (true); //Testing for invalid IDs - removed = m_Service->RemovePlanePosition( -1 ); + bool removed = m_Service->RemovePlanePosition( -1 ); removed = m_Service->RemovePlanePosition( 1000000 ); unsigned int size2 = m_Service->GetNumberOfPlanePositions(); if (removed) { MITK_TEST_CONDITION(removed == false, "Testing remove not existing planepositions"); MITK_TEST_CONDITION(size == size2, "Testing remove not existing planepositions"); return EXIT_FAILURE; } //Testing for valid IDs for (unsigned int i = 0; i < m_Geometries.size()*0.5; i++) { removed = m_Service->RemovePlanePosition( i ); unsigned int size2 = m_Service->GetNumberOfPlanePositions(); removed = (size2 == (size-(i+1))); if (!removed) { MITK_TEST_CONDITION(removed == true, "Testing remove existing planepositions"); MITK_TEST_CONDITION(size == (size-i+1), "Testing remove existing planepositions"); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int testRemoveAll() { MITK_TEST_OUTPUT(<<"Starting Test: ######### R e m o v e A l l #########"); unsigned int numPos = m_Service->GetNumberOfPlanePositions(); MITK_INFO<RemoveAllPlanePositions(); bool error (true); error = (m_Service->GetNumberOfPlanePositions() != 0 || m_Service->GetPlanePosition(60) != 0); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 0, "Testing remove all pos"); MITK_TEST_CONDITION(m_Service->GetPlanePosition(60) == 0, "Testing remove all pos"); return EXIT_FAILURE; } return EXIT_SUCCESS; } int mitkPlanePositionManagerTest(int, char* []) { MITK_TEST_BEGIN("PlanePositionManager"); SetUpBeforeTest(); int result; MITK_TEST_CONDITION_REQUIRED( (result = testAddPlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED( (result = testGetPlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED( (result = testRemovePlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED( (result = testRemoveAll()) == EXIT_SUCCESS, ""); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPointSetLocaleTest.cpp b/Modules/Core/test/mitkPointSetLocaleTest.cpp index 9c41c4c9d9..e589452423 100644 --- a/Modules/Core/test/mitkPointSetLocaleTest.cpp +++ b/Modules/Core/test/mitkPointSetLocaleTest.cpp @@ -1,170 +1,168 @@ /*=================================================================== 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 "mitkPointSet.h" #include "mitkStandardFileLocations.h" #include "mitkIOUtil.h" #include "mitkTestingMacros.h" #include #include #include #include bool ChangeLocale(const std::string& locale) { try { MITK_TEST_OUTPUT(<< "\n** Changing locale from " << setlocale(LC_ALL, NULL) << " to '" << locale << "'"); setlocale(LC_ALL, locale.c_str()); std::locale l( locale.c_str() ); std::cin.imbue(l); std::cout.imbue(l); return true; } catch(...) { MITK_TEST_OUTPUT(<< "Could not activate locale " << locale << "\n"); return false; } } void ReaderLocaleTest(mitk::Point3D & refPoint, std::string filename) { MITK_TEST_OUTPUT(<< "---- Reader Test ---- "); mitk::PointSet::Pointer pointSet = mitk::IOUtil::LoadPointSet(filename); mitk::Point3D point; if (pointSet->GetPointIfExists(0, &point)) { MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[0] - point[0]) < 0.00001, "read x correct"); MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[1] - point[1]) < 0.00001, "read y correct"); MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[2] - point[2]) < 0.00001, "read z correct"); }else { MITK_TEST_FAILED_MSG(<< "File "<< filename << " can not be read - test will not applied." ); return; } } void WriterLocaleTest(mitk::Point3D & refPoint, std::string filename) { MITK_TEST_OUTPUT(<< "---- Writer Test---- "); //create pointset mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); refPointSet->InsertPoint(0,refPoint); //SetPoint(0, refPoint); std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile("testPointSet_XXXXXX.mps"); // write point set mitk::IOUtil::Save(refPointSet, tmpFilePath); std::ifstream stream(tmpFilePath.c_str()); //compare two .mps files std::ifstream refStream (filename.c_str()); MITK_TEST_CONDITION_REQUIRED(refStream,"Read reference point set"); MITK_TEST_CONDITION_REQUIRED(stream,"Read point set"); bool differ = false; if (stream.is_open() && refStream.is_open()) { std::string streamLine; std::string refStreamLine; while(!stream.eof() && ! refStream.eof()) { getline(stream, streamLine); getline(refStream, refStreamLine); if(streamLine.compare(refStreamLine) != 0) { differ = true; break; } } stream.close(); refStream.close(); } MITK_TEST_CONDITION_REQUIRED(!differ, "Write point set correct"); } int mitkPointSetLocaleTest(int, char*[]) { MITK_TEST_BEGIN("PointSetLocaleTest"); //create reference point set mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); mitk::Point3D refPoint; refPoint[0] = 32.2946; refPoint[1] = -17.7359; refPoint[2] = 29.6502; refPointSet->SetPoint(0, refPoint); //create locale list - std::ofstream stream; - std::locale previousLocale(stream.getloc()); typedef std::list StringList; StringList alllocales; alllocales.push_back("de_DE"); alllocales.push_back("de_DE.utf8"); alllocales.push_back("de_DE.UTF-8"); alllocales.push_back("de_DE@euro"); alllocales.push_back("German_Germany"); // QuickFix for MAC OS X // See for more the Bug #3894 comments #if defined (__APPLE__) || defined(MACOSX) alllocales.push_back("C"); #endif // write a reference file using the "C" locale once ChangeLocale("C"); std::string referenceFilePath = mitk::IOUtil::CreateTemporaryFile("refPointSet_XXXXXX.mps"); MITK_INFO << "Reference PointSet in " << referenceFilePath; // write point set mitk::IOUtil::Save(refPointSet, referenceFilePath); unsigned int numberOfTestedGermanLocales(0); for (StringList::iterator iter = alllocales.begin(); iter != alllocales.end(); ++iter) { if ( ChangeLocale(*iter) ) { ++numberOfTestedGermanLocales; WriterLocaleTest(refPoint, referenceFilePath); ReaderLocaleTest(refPoint, referenceFilePath); } } if(numberOfTestedGermanLocales == 0) { MITK_TEST_OUTPUT(<< "Warning: No German locale was found on the system."); } //MITK_TEST_CONDITION_REQUIRED( numberOfTestedGermanLocales > 0, "Verify that at least one German locale has been tested."); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPointTypeConversionTest.cpp b/Modules/Core/test/mitkPointTypeConversionTest.cpp index b6e69fd31e..39221e11f1 100644 --- a/Modules/Core/test/mitkPointTypeConversionTest.cpp +++ b/Modules/Core/test/mitkPointTypeConversionTest.cpp @@ -1,165 +1,160 @@ /*=================================================================== 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 "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkNumericConstants.h" #include "mitkNumericTypes.h" // for Equal method #include "mitkPoint.h" #include "itkPoint.h" #include "vtkPoints.h" #include "vtkSmartPointer.h" #include using namespace mitk; class mitkPointTypeConversionTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPointTypeConversionTestSuite); MITK_TEST(Vector2Point); MITK_TEST(Mitk2Itk_PointCompatibility); MITK_TEST(Itk2Mitk_PointCompatibility); MITK_TEST(Vtk2Mitk_PointCompatibility); MITK_TEST(Mitk2Pod_PointCompatibility); MITK_TEST(Pod2Mitk_PointCompatibility); CPPUNIT_TEST_SUITE_END(); private: vtkSmartPointer a_vtkPoints; ScalarType originalValues[3]; ScalarType valuesToCopy[3]; /** * @brief Convenience method to test if one vector has been assigned successfully to the other. * * More specifically, tests if v1 = v2 was performed correctly. * * @param v1 The vector v1 of the assignment v1 = v2 * @param v2 The vector v2 of the assignment v1 = v2 * @param v1Name The type name of v1 (e.g.: mitk::Vector3D). Necessary for the correct test output. * @param v2Name The type name of v2 (e.g.: mitk::Vector3D). Necessary for the correct test output. * @param eps defines the allowed tolerance when testing for equality. */ template void TestForEquality(T1 v1, T2 v2, std::string v1Name, std::string v2Name, ScalarType eps = mitk::eps) { CPPUNIT_ASSERT_EQUAL_MESSAGE("\nAssigning " + v2Name + " to " + v1Name + ":\n both are equal", true, EqualArray(v1, v2, 3, eps)); } public: void setUp(void) override { FillVector3D(originalValues, 1.0, 2.0, 3.0); FillVector3D(valuesToCopy, 4.0, 5.0, 6.0); a_vtkPoints = vtkSmartPointer::New(); a_vtkPoints->Initialize(); } void tearDown(void) override { // a_vtkPoints = NULL; } void Mitk2Itk_PointCompatibility() { - itk::Point itkPoint3D = originalValues; mitk::Point3D point3D = valuesToCopy; - itkPoint3D = point3D; + itk::Point itkPoint3D = point3D; TestForEquality(itkPoint3D, point3D, "itk::Point", "mitk:Point"); } void Itk2Mitk_PointCompatibility() { - mitk::Point3D point3D = originalValues; itk::Point itkPoint3D = valuesToCopy; - point3D = itkPoint3D; + mitk::Point3D point3D = itkPoint3D; TestForEquality(point3D, itkPoint3D, "mitk:Point", "itk::Point"); } void Vtk2Mitk_PointCompatibility() { - mitk::Point3D point3D = originalValues; a_vtkPoints->InsertNextPoint(valuesToCopy); double vtkPoint[3]; a_vtkPoints->GetPoint(0, vtkPoint); - point3D = vtkPoint; + mitk::Point3D point3D = vtkPoint; TestForEquality(point3D, vtkPoint, "mitk:Point", "vtkPoint"); } void Mitk2Pod_PointCompatibility() { ScalarType podPoint[] = {1.0, 2.0, 3.0}; mitk::Point3D point3D = valuesToCopy; point3D.ToArray(podPoint); TestForEquality(podPoint, point3D, "POD point", "mitk::Point"); } void Pod2Mitk_PointCompatibility() { - itk::Point point3D = originalValues; ScalarType podPoint[] = {4.0, 5.0, 6.0}; - point3D = podPoint; + itk::Point point3D = podPoint; TestForEquality(point3D, podPoint, "mitk::Point3D", "POD point"); } void Vector2Point() { - itk::Point point3D = valuesToCopy; itk::Vector vector3D = originalValues; - point3D = vector3D; + itk::Point point3D = vector3D; TestForEquality(point3D, vector3D, "mitk::Point", "mitk::Vector"); } }; MITK_TEST_SUITE_REGISTRATION(mitkPointTypeConversion) diff --git a/Modules/Core/test/mitkStateMachineContainerTest.cpp b/Modules/Core/test/mitkStateMachineContainerTest.cpp index bc582c4a93..5f6af144b8 100644 --- a/Modules/Core/test/mitkStateMachineContainerTest.cpp +++ b/Modules/Core/test/mitkStateMachineContainerTest.cpp @@ -1,75 +1,75 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include "mitkStateMachineContainer.h" #include "mitkStateMachineState.h" #include "mitkStateMachineAction.h" #include "mitkStateMachineTransition.h" #include int mitkStateMachineContainerTest(int /*argc*/, char* /*argv*/[]) { MITK_TEST_BEGIN("StateMachineContainer") /* * Loads a test StateMachine and checks if it is build up correctly. */ mitk ::StateMachineContainer* smc = mitk::StateMachineContainer::New(); MITK_TEST_CONDITION_REQUIRED( smc->LoadBehavior("Tests/StatemachineTest.xml") == true , "01 Check if file can be loaded" ); std::string sname = smc->GetStartState()->GetName(); MITK_TEST_CONDITION_REQUIRED( sname == "STATE1" , "02 Check if start state is correct" ); /* * Follow transitions and check if all actions have been loaded. */ mitk::StateMachineTransition::Pointer s1 = smc->GetStartState()->GetTransition("MouseMoveEvent","no1"); // transition exists mitk::StateMachineTransition::Pointer s2 = smc->GetStartState()->GetTransition("StdMouseNotMove","no1");// transition does not exist mitk::StateMachineState::Pointer st2 = s1->GetNextState(); mitk::StateMachineTransition::Pointer s3 = st2->GetTransition("MouseMoveEvent","no1");// transition from state2 to state3 typedef std::vector ActionCollectionType; ActionCollectionType actions = s3->GetActions(); ActionCollectionType::iterator it = actions.begin(); std::string action1 = (*it)->GetActionName(); - it++; + ++it; std::string action2 = (*it)->GetActionName(); - it++;// this should now point to the end of the vector, since only two actions are given + ++it;// this should now point to the end of the vector, since only two actions are given MITK_INFO << s2.IsNull(); MITK_TEST_CONDITION_REQUIRED( s1.IsNotNull() && s2.IsNull() && st2->GetName() == "STATE2" && action1 == "doaction7" && action2 == "doaction2" && (it == actions.end()) , "03 Check if transitions and Action identifier work. \n Expected STATE2 , doaction7, doction2 got:\n" <GetName() << "," << action1 << "," << action2); // always end with this! MITK_TEST_END() } diff --git a/Modules/Core/test/mitkSurfaceTest.cpp b/Modules/Core/test/mitkSurfaceTest.cpp index f9026bb679..dd26efdd40 100644 --- a/Modules/Core/test/mitkSurfaceTest.cpp +++ b/Modules/Core/test/mitkSurfaceTest.cpp @@ -1,150 +1,148 @@ /*=================================================================== 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 "mitkSurface.h" #include "mitkCommon.h" #include "mitkNumericTypes.h" #include "mitkTestingMacros.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::Surface::Pointer cloneSurface = surface->Clone(); MITK_TEST_CONDITION_REQUIRED( cloneSurface.GetPointer(), "Testing clone surface initialization!" ); vtkSphereSource* sphereSource = vtkSphereSource::New(); sphereSource->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() == nullptr, "Testing initial state of vtkPolyData"); surface->SetVtkPolyData( polys ); sphereSource->Delete(); MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData()!= nullptr, "Testing set vtkPolyData"); - cloneSurface= nullptr; cloneSurface = surface->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneSurface->GetVtkPolyData()!= nullptr, "Testing set vtkPolyData of cloned surface!"); cloneSurface = nullptr; double bounds[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; polys->ComputeBounds(); polys->GetBounds( bounds ); surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); mitk::BoundingBox* bb = const_cast(surface->GetGeometry()->GetBoundingBox()); mitk::BoundingBox::BoundsArrayType surfBounds = bb->GetBounds(); bool passed = false; 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] ) { passed = true; } MITK_TEST_CONDITION_REQUIRED(passed, "Testing GetBoundingBox()!"); surface->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 ): "); double 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( boundsMat[i] ); surface->SetVtkPolyData( sphereSource->GetOutput(),i ); sphereSource->Delete(); } surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); passed = true; for (int i=0;i<5;i++) { mitk::BoundingBox::BoundsArrayType surfBounds = (const_cast(surface->GetTimeGeometry()->GetGeometryForTimeStep(i)->GetBoundingBox()))->GetBounds(); 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; } } MITK_TEST_CONDITION_REQUIRED(passed, "Testing mitk::Surface::Testing 4D surface data creation!" ); const mitk::TimeGeometry* inputTimeGeometry = surface->GetUpdatedTimeGeometry(); int time = 3; int timestep=0; timestep = inputTimeGeometry->TimePointToTimeStep( time ); MITK_TEST_CONDITION_REQUIRED(time == timestep, "Testing correctness of geometry for surface->GetUpdatedTimeGeometry()!"); sphereSource = vtkSphereSource::New(); sphereSource->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->GetUpdatedTimeGeometry(); time = 3; - timestep=0; timestep = inputTimeGeometry->TimePointToTimeStep( time ); MITK_TEST_CONDITION_REQUIRED(time == timestep, "Explicitly changing the data of timestep 3 and checking for timebounds correctness of surface's geometry again!"); unsigned int numberoftimesteps = surface->GetTimeSteps(); mitk::Surface::Pointer dummy = mitk::Surface::New(); dummy->Graft(surface); MITK_TEST_CONDITION_REQUIRED( dummy->GetVtkPolyData() != nullptr, "Testing copying a Surface with Graft()!"); MITK_TEST_CONDITION_REQUIRED( dummy->GetTimeSteps() == numberoftimesteps, "orig-numberofTimeSteps:" << numberoftimesteps << " copy-numberofTimeSteps:" << dummy->GetTimeSteps()); surface = nullptr; MITK_TEST_CONDITION_REQUIRED( surface.IsNull(), "Testing destruction of surface!"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkVectorTypeConversionTest.cpp b/Modules/Core/test/mitkVectorTypeConversionTest.cpp index 5bf2504381..38808112a0 100644 --- a/Modules/Core/test/mitkVectorTypeConversionTest.cpp +++ b/Modules/Core/test/mitkVectorTypeConversionTest.cpp @@ -1,264 +1,259 @@ /*=================================================================== 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 "itkVector.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include "vnl/vnl_math.h" #include "mitkNumericConstants.h" #include "mitkVector.h" #include "mitkPoint.h" using namespace mitk; class mitkVectorTypeConversionTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkVectorTypeConversionTestSuite); MITK_TEST(Point2Vector); MITK_TEST(Pod2Mitk); MITK_TEST(Mitk2Pod); MITK_TEST(OneElement2Mitk); MITK_TEST(Itk2Mitk); MITK_TEST(Mitk2Itk); MITK_TEST(Vnlfixed2Mitk); MITK_TEST(Mitk2Vnlfixed); MITK_TEST(Vnl2Mitk); MITK_TEST(Mitk2Vnl); MITK_TEST(Vnl2Mitk_WrongVnlVectorSize); MITK_TEST(ToArray_DifferentType); MITK_TEST(Fill_DifferentType); CPPUNIT_TEST_SUITE_END(); private: /** * these variables are used in the test functions * * The variable which should be copied into is set to its original value. * The value which should be copied is set to valuesToCopy. * * Then the copying takes place. The test is successful, if the variable which * should be copied into holds the valuesToCopy afterwards and is equal to the * vector which should be copied. */ ScalarType originalValues[3]; ScalarType valuesToCopy[3]; float epsDouble2Float; /** * @brief Convenience method to test if one vector has been assigned successfully to the other. * * More specifically, tests if v1 = v2 was performed correctly. * * @param v1 The vector v1 of the assignment v1 = v2 * @param v2 The vector v2 of the assignment v1 = v2 * @param v1Name The type name of v1 (e.g.: mitk::Vector3D). Necessary for the correct test output. * @param v2Name The type name of v2 (e.g.: mitk::Vector3D). Necessary for the correct test output. * @param eps defines the allowed tolerance when testing for equality. */ template void TestForEquality(const T1& v1, const T2& v2, const std::string& v1Name, const std::string& v2Name, const ScalarType& eps = mitk::eps) const { CPPUNIT_ASSERT_EQUAL_MESSAGE("\nAssigning " + v2Name + " to " + v1Name + ":\n both are equal", true, EqualArray(v1, v2, 3, eps)); } public: void setUp(void) override { FillVector3D(originalValues, 1.123456789987, 2.789456321456, 3.123654789987456); FillVector3D(valuesToCopy, 4.654789123321, 5.987456789321, 6.321654987789546); epsDouble2Float = vnl_math::float_eps * 10.0; } void tearDown(void) override { } void Pod2Mitk(void) { mitk::Vector3D vector3D = valuesToCopy; TestForEquality(vector3D, valuesToCopy, "mitk::Vector3D", "double POD"); } void Mitk2Pod(void) { ScalarType podArray[3]; mitk::Vector3D vector3D = valuesToCopy; vector3D.ToArray(podArray); TestForEquality(podArray, vector3D, "double POD", "mitk::Vector3D"); } void OneElement2Mitk(void) { double twos[] = {2.0, 2.0, 2.0}; mitk::Vector vector3D(2.0); CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE( "\n one values initializes all elements to this value", EqualArray(vector3D, twos, 3)); } void Itk2Mitk(void) { - Vector3D vector3D = originalValues; itk::Vector itkVector = valuesToCopy; - vector3D = itkVector; + Vector3D vector3D = itkVector; TestForEquality(vector3D, itkVector, "mitk::Vector3D", "itk::Vector"); } void Mitk2Itk(void) { Vector3D vector3D = valuesToCopy; - itk::Vector itkVector = originalValues; - itkVector = vector3D; + itk::Vector itkVector = vector3D; TestForEquality(itkVector, vector3D, "itk::Vector", "mitk::Vector3D"); } void Vnlfixed2Mitk(void) { - mitk::Vector3D vector3D = originalValues; vnl_vector_fixed vnlVectorFixed(valuesToCopy); - vector3D = vnlVectorFixed; + mitk::Vector3D vector3D = vnlVectorFixed; TestForEquality(vector3D, vnlVectorFixed, "mitk::Vector3D", "vnl_vector_fixed"); } void Mitk2Vnlfixed(void) { vnl_vector_fixed vnlVectorFixed(originalValues); mitk::Vector3D vector3D = valuesToCopy; vnlVectorFixed = vector3D; TestForEquality(vnlVectorFixed, vector3D, "vnl_vector_fixed", "mitk::Vector3D"); } void Vnl2Mitk(void) { - mitk::Vector3D vector3D = originalValues; vnl_vector vnlVector(3); vnlVector.set(valuesToCopy); - vector3D = vnlVector; + mitk::Vector3D vector3D = vnlVector; TestForEquality(vector3D, vnlVector, "mitk::Vector3D", "vnl_vector"); } void Mitk2Vnl(void) { vnl_vector vnlVector(3); vnlVector.set(originalValues); mitk::Vector3D vector3D = valuesToCopy; vnlVector = vector3D; TestForEquality(vnlVector, vector3D, "vnl_vector", "mitk::Vector3D"); } /** * @brief Tests if an exception is thrown when constructing an mitk::Vector form a vnl_vector of not suited size. */ void Vnl2Mitk_WrongVnlVectorSize() { ScalarType largerValuesToCopy[] = {4.12345678910, 5.10987654321, 6.123456789132456, 7.123456987789456}; mitk::Vector3D vector3D = originalValues; vnl_vector vnlVector(4); vnlVector.set(largerValuesToCopy); CPPUNIT_ASSERT_THROW(vector3D = vnlVector, mitk::Exception); } void ToArray_DifferentType(void) { float podArray[3]; for (int var = 0; var < 3; ++var) { podArray[var] = originalValues[var]; } mitk::Vector3D vector3D = valuesToCopy; vector3D.ToArray(podArray); TestForEquality(podArray, vector3D, "float POD", "mitk::Vector3D", epsDouble2Float); } void Fill_DifferentType(void) { mitk::Vector3D vector3D = originalValues; float podArray[3]; for (int var = 0; var < 3; ++var) { podArray[var] = valuesToCopy[var]; } vector3D.FillVector(podArray); TestForEquality(vector3D, podArray, "mitk::Vector3D", "float POD", epsDouble2Float); } void Point2Vector() { mitk::Point3D point3D = originalValues; - mitk::Vector3D vector3D = valuesToCopy; - vector3D = point3D.GetVectorFromOrigin(); + mitk::Vector3D vector3D = point3D.GetVectorFromOrigin(); TestForEquality(point3D, vector3D, "mitk::Point3D", "mitk::Vector3D"); } }; MITK_TEST_SUITE_REGISTRATION(mitkVectorTypeConversion) diff --git a/Modules/Core/test/mitkVolumeCalculatorTest.cpp b/Modules/Core/test/mitkVolumeCalculatorTest.cpp index f2a1f62474..8f9622fa39 100644 --- a/Modules/Core/test/mitkVolumeCalculatorTest.cpp +++ b/Modules/Core/test/mitkVolumeCalculatorTest.cpp @@ -1,71 +1,71 @@ /*=================================================================== 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 "mitkVolumeCalculator.h" #include "mitkImage.h" #include "mitkTestingMacros.h" #include #include "mitkIOUtil.h" int mitkVolumeCalculatorTest(int /*argc*/, char* argv[]) { MITK_TEST_BEGIN("VolumeCalculator") const char * filename = argv[1]; const char * filename3D = argv[2]; mitk::VolumeCalculator::Pointer volumeCalculator = mitk::VolumeCalculator::New(); //********************************************************************* // Part I: Testing calculated volume. // The correct values have been manually calculated using external software. //********************************************************************* mitk::Image::Pointer image = mitk::IOUtil::LoadImage(filename); MITK_TEST_CONDITION_REQUIRED( image.IsNotNull() , "01 Check if test image could be loaded"); volumeCalculator->SetImage(image); volumeCalculator->SetThreshold(0); volumeCalculator->ComputeVolume(); float volume = volumeCalculator->GetVolume(); MITK_TEST_CONDITION_REQUIRED( volume == 1600 , "02 Test Volume Result. Expected 1600 actual value " << volume); volumeCalculator->SetThreshold(255); volumeCalculator->ComputeVolume(); volume = volumeCalculator->GetVolume(); MITK_TEST_CONDITION_REQUIRED( volume == 1272.50 , "03 Test Volume Result. Expected 1272.50 actual value " << volume); image = mitk::IOUtil::LoadImage(filename3D); volumeCalculator->SetImage(image); volumeCalculator->SetThreshold(-1023); volumeCalculator->ComputeVolume(); std::vector volumes = volumeCalculator->GetVolumes(); - for (std::vector::iterator it = volumes.begin(); it != volumes.end(); it++) + for (std::vector::iterator it = volumes.begin(); it != volumes.end(); ++it) { MITK_TEST_CONDITION_REQUIRED( (*it) == 24.576f , "04 Test Volume Result."); } MITK_TEST_END() } diff --git a/Modules/Core/test/mitkVtkPropRendererTest.cpp b/Modules/Core/test/mitkVtkPropRendererTest.cpp index 38217e209a..05d1182c22 100644 --- a/Modules/Core/test/mitkVtkPropRendererTest.cpp +++ b/Modules/Core/test/mitkVtkPropRendererTest.cpp @@ -1,213 +1,211 @@ /*=================================================================== 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 "vtkSphereSource.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkRenderWindow.h" #include "vtkRenderer.h" #include "vtkRenderWindowInteractor.h" #include "vtkProperty.h" #include "vtkUnsignedCharArray.h" #include #include #include #include #include #include #include #include #include #include int mitkVtkPropRendererTest(int argc, char* argv[]) { //independently read header of pic file mitkIpPicDescriptor *picheader = NULL; if (argc >= 1) { if(itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameExtension(argv[1])).find(".pic")!=std::string::npos) picheader = mitkIpPicGetHeader(argv[1], NULL); } if( picheader==NULL) { std::cout<<"file not found/not a pic-file - test not applied [PASSED]"<SetFileName(argv[1]); reader->Update(); std::cout<<"[PASSED]"<GetOutput(); std::cout << "Creating node: "; mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(image); std::cout<<"[PASSED]"<Add(node); std::cout<<"[PASSED]"<SetLevelWindow( levelwindow ); node->GetPropertyList()->SetProperty( "levelwindow", levWinProp ); std::cout<<"[PASSED]"<SetRadius(1.0); sphere->SetThetaResolution(18); sphere->SetPhiResolution(18); vtkPolyDataMapper *map = vtkPolyDataMapper::New(); map->SetInput(sphere->GetOutput()); sphere->Delete(); vtkActor *aSphere = vtkActor::New(); aSphere->SetMapper(map); map->Delete(); aSphere->GetProperty()->SetColor(0,0,1); // sphere color blue std::cout<<"[PASSED]"<AddActor(aSphere); aSphere->Delete(); //sphereRenderer->SetBackground(1,1,1); // Background color white std::cout<<"[PASSED]"<SetMapperID(2); std::cout<<"[PASSED]"<AddRenderer(sphereRenderer); //renderWindow->SetErase(0); std::cout << "BaseRenderer::SetData(iterator): "; propRenderer->SetDataStorage(ds); std::cout<<"[PASSED]"<SetPropRenderer(propRenderer); propRenderer->GetVtkRenderer()->AddViewProp(renderProp); std::cout<<"[PASSED]"<InsertForegroundRenderer(sphereRenderer,true); std::cout<<"[PASSED]"<SetRenderWindow(renderWindow); std::cout << "Setting and getting size of renderWindow: "; renderWindow->SetSize(400, 400); int *size = renderWindow->GetSize(); std::cout<<"[PASSED]"<Render(); std::cout<<"[PASSED]"<Start(); std::cout << "Testing to pick a world position: "; mitk::Point2D p; mitk::Point3D p_mm; p[0] = 10; p[1] = 10; propRenderer->PickWorldPoint(p, p_mm); std::cout << "returned world position: " << p_mm << "\n"; std::cout << "Creating vtkUnsignedCharArray: "; vtkUnsignedCharArray *vtkImage = vtkUnsignedCharArray::New(); std::cout<<"[PASSED]"<GetRGBACharPixelData(0, 0, size[0]-1, size[1]-1, 0, vtkImage); cout << "Read " << size[0]*size[1] << " data points\n"; cout << "Computing sum of all RGBA values..\n"; long int sum_now = 0; for(int i=0;iGetValue(i); std::cout << "Sum of all RGBA values: " << sum_now << "\n"; std::cout << "Sum should be: " << sum_orig_Pic3D_pic_gz << "\n"; //std::string Pic3d_pic_gz_str("Pic3D.pic.gz"); //std::cout << "pic3d " << Pic3d_pic_gz_str << "\n"; //std::cout << "argv " << argv_str << "\n"; //std::cout << "find " << (int) argv_str.find("Pic3D.pic.gz") << "\n"; //std::cout << "size " << argv_str.size() << "\n"; //if(argv_str.size() - ((int) argv_str.find("Pic3D.pic.gz")) == 12) //{ // std::cout << "Input image is Pic3D.pic.gz\n"; // std::cout << "Sum should be: " << sum_orig_Pic3D_pic_gz << "\n"; // if(sum_orig_Pic3D_pic_gz!=sum_now) // { // std::cout<<"[FAILED]"<GetVtkRenderer()->RemoveViewProp(renderProp); renderProp->Delete(); propRenderer = NULL; sphereRenderer->Delete(); renderWindow->Delete(); vtkImage->Delete(); ds = NULL; std::cout<<"[TEST DONE]"<