diff --git a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp index c3df456fad..fb77ebdb61 100644 --- a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp @@ -1,239 +1,240 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // VTK #include <vtkCamera.h> #include <vtkOpenGLRenderWindow.h> #include <vtkPNGWriter.h> #include <vtkRenderLargeImage.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> // MITK #include <mitkNodePredicateDataType.h> #include <mitkRenderWindow.h> #include <mitkRenderingTestHelper.h> #include <mitkSliceNavigationController.h> #include <mitkStandaloneDataStorage.h> #include <mitkException.h> #include <mitkTestNotRunException.h> #include <mitkTestingMacros.h> +#include <mitkUIDGenerator.h> // VTK Testing to compare the rendered image pixel-wise against a reference screen shot #include "vtkTesting.h" mitk::RenderingTestHelper::RenderingTestHelper(int width, int height, AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); } mitk::RenderingTestHelper::RenderingTestHelper( int width, int height, int argc, char *argv[], AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); this->SetInputFileNames(argc, argv); } void mitk::RenderingTestHelper::Initialize(int width, int height, AntiAliasing antiAliasing) { RenderingManager::GetInstance()->SetAntiAliasing(antiAliasing); mitk::UIDGenerator uidGen = mitk::UIDGenerator("UnnamedRenderer_"); m_RenderWindow = mitk::RenderWindow::New(nullptr, uidGen.GetUID().c_str()); auto renderWindow = m_RenderWindow->GetVtkRenderWindow(); if (0 == renderWindow->SupportsOpenGL()) { auto openGLRenderWindow = dynamic_cast<vtkOpenGLRenderWindow*>(renderWindow); auto message = nullptr != openGLRenderWindow ? openGLRenderWindow->GetOpenGLSupportMessage() : std::string("No details available."); mitkThrowException(mitk::TestNotRunException) << "OpenGL not supported: " << message; } m_DataStorage = mitk::StandaloneDataStorage::New(); m_RenderWindow->GetRenderer()->SetDataStorage(m_DataStorage); this->SetMapperIDToRender2D(); this->GetVtkRenderWindow()->SetSize(width, height); m_RenderWindow->GetRenderer()->Resize(width, height); } mitk::RenderingTestHelper::~RenderingTestHelper() { } void mitk::RenderingTestHelper::SetMapperID(mitk::BaseRenderer::StandardMapperSlot id) { m_RenderWindow->GetRenderer()->SetMapperID(id); } void mitk::RenderingTestHelper::SetMapperIDToRender3D() { this->SetMapperID(mitk::BaseRenderer::Standard3D); mitk::RenderingManager::GetInstance()->InitializeViews( this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll())); } void mitk::RenderingTestHelper::SetMapperIDToRender2D() { this->SetMapperID(mitk::BaseRenderer::Standard2D); } void mitk::RenderingTestHelper::Render() { // if the datastorage is initialized and at least 1 image is loaded render it if (m_DataStorage.IsNotNull() && m_DataStorage->GetAll()->Size() >= 1) { // Prepare the VTK camera before rendering. m_RenderWindow->GetRenderer()->PrepareRender(); this->GetVtkRenderWindow()->Render(); this->GetVtkRenderWindow()->WaitForCompletion(); if (m_AutomaticallyCloseRenderWindow == false) { // Use interaction to stop the test this->GetVtkRenderWindow()->GetInteractor()->Start(); } } else { MITK_ERROR << "No images loaded in data storage!"; } } mitk::DataStorage::Pointer mitk::RenderingTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::RenderingTestHelper::SetInputFileNames(int argc, char *argv[]) { // i is set 1, because 0 is the testname as string // parse parameters for (int i = 1; i < argc; ++i) { // add everything to a list but -T and -V std::string tmp = argv[i]; if ((tmp.compare("-T")) && (tmp.compare("-V"))) { this->AddToStorage(tmp); } else { break; } } } void mitk::RenderingTestHelper::SetViewDirection(mitk::AnatomicalPlane viewDirection) { mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow()) ->GetSliceNavigationController() ->SetDefaultViewDirection(viewDirection); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } void mitk::RenderingTestHelper::ReorientSlices(mitk::Point3D origin, mitk::Vector3D rotation) { mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow())->GetSliceNavigationController(); sliceNavigationController->ReorientSlices(origin, rotation); } vtkRenderer *mitk::RenderingTestHelper::GetVtkRenderer() { return m_RenderWindow->GetRenderer()->GetVtkRenderer(); } void mitk::RenderingTestHelper::SetImageProperty(const char *propertyKey, mitk::BaseProperty *property) { this->m_DataStorage->GetNode(mitk::NodePredicateDataType::New("Image"))->SetProperty(propertyKey, property); } vtkRenderWindow *mitk::RenderingTestHelper::GetVtkRenderWindow() { return m_RenderWindow->GetVtkRenderWindow(); } bool mitk::RenderingTestHelper::CompareRenderWindowAgainstReference(int argc, char *argv[], double threshold) { this->Render(); // retVal meanings: (see VTK/Rendering/vtkTesting.h) // 0 = test failed // 1 = test passed // 2 = test not run // 3 = something with vtkInteraction if (vtkTesting::Test(argc, argv, this->GetVtkRenderWindow(), threshold) == 1) return true; else return false; } // method to save a screenshot of the renderwindow (e.g. create a reference screenshot) void mitk::RenderingTestHelper::SaveAsPNG(std::string fileName) { vtkSmartPointer<vtkRenderer> renderer = this->GetVtkRenderer(); bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer()); renderer->GetRenderWindow()->DoubleBufferOff(); vtkSmartPointer<vtkRenderLargeImage> magnifier = vtkSmartPointer<vtkRenderLargeImage>::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(1); vtkSmartPointer<vtkImageWriter> fileWriter = vtkSmartPointer<vtkPNGWriter>::New(); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(fileName.c_str()); fileWriter->Write(); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void mitk::RenderingTestHelper::SetAutomaticallyCloseRenderWindow(bool automaticallyCloseRenderWindow) { m_AutomaticallyCloseRenderWindow = automaticallyCloseRenderWindow; } void mitk::RenderingTestHelper::SaveReferenceScreenShot(std::string fileName) { this->SaveAsPNG(fileName); } void mitk::RenderingTestHelper::AddToStorage(const std::string &filename) { try { mitk::IOUtil::Load(filename, *m_DataStorage.GetPointer()); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } catch ( const itk::ExceptionObject &e ) { MITK_ERROR << "Failed loading test data '" << filename << "': " << e.what(); } } void mitk::RenderingTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } diff --git a/Modules/Core/src/Rendering/mitkAnnotation.cpp b/Modules/Core/src/Rendering/mitkAnnotation.cpp index c9ac002908..9568fc0541 100644 --- a/Modules/Core/src/Rendering/mitkAnnotation.cpp +++ b/Modules/Core/src/Rendering/mitkAnnotation.cpp @@ -1,348 +1,349 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#include "mitkAnnotation.h" -#include "usGetModuleContext.h" +#include <mitkAnnotation.h> +#include <mitkUIDGenerator.h> +#include <usGetModuleContext.h> const std::string mitk::Annotation::US_INTERFACE_NAME = "org.mitk.services.Annotation"; const std::string mitk::Annotation::US_PROPKEY_AnnotationNAME = US_INTERFACE_NAME + ".name"; const std::string mitk::Annotation::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; const std::string mitk::Annotation::US_PROPKEY_MODIFIED = US_INTERFACE_NAME + ".modified"; const std::string mitk::Annotation::US_PROPKEY_RENDERER_ID = US_INTERFACE_NAME + ".rendererId"; const std::string mitk::Annotation::US_PROPKEY_AR_ID = US_INTERFACE_NAME + ".arId"; mitk::Annotation::Annotation() : m_PropertyListModifiedObserverTag(0) { m_PropertyList = mitk::PropertyList::New(); itk::MemberCommand<mitk::Annotation>::Pointer _PropertyListModifiedCommand = itk::MemberCommand<mitk::Annotation>::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::Annotation::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); this->SetName(this->GetNameOfClass()); this->SetVisibility(true); this->SetOpacity(1.0); } void mitk::Annotation::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { AnnotationModified(); } mitk::Annotation::~Annotation() { this->UnRegisterMicroservice(); } void mitk::Annotation::SetUSProperty(const std::string &propertyKey, us::Any value) { if (this->m_ServiceRegistration) { us::ServiceProperties props; std::vector<std::string> propertyKeys; m_ServiceRegistration.GetReference().GetPropertyKeys(propertyKeys); for (const std::string &key : propertyKeys) { props[key] = m_ServiceRegistration.GetReference().GetProperty(key); } props[propertyKey] = value; m_ServiceRegistration.SetProperties(props); } } void mitk::Annotation::SetProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::Annotation::ReplaceProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->ReplaceProperty(propertyKey, propertyValue); } void mitk::Annotation::AddProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey) == nullptr)) { SetProperty(propertyKey, propertyValue); } } void mitk::Annotation::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::Annotation::GetProperty(const std::string &propertyKey) const { mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty(propertyKey); if (property.IsNotNull()) return property; // only to satisfy compiler! return nullptr; } bool mitk::Annotation::GetBoolProperty(const std::string &propertyKey, bool &boolValue) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast<mitk::BoolProperty *>(GetProperty(propertyKey)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::Annotation::GetIntProperty(const std::string &propertyKey, int &intValue) const { mitk::IntProperty::Pointer intprop = dynamic_cast<mitk::IntProperty *>(GetProperty(propertyKey)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::Annotation::GetFloatProperty(const std::string &propertyKey, float &floatValue) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast<mitk::FloatProperty *>(GetProperty(propertyKey)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::Annotation::GetStringProperty(const std::string &propertyKey, std::string &string) const { mitk::StringProperty::Pointer stringProp = dynamic_cast<mitk::StringProperty *>(GetProperty(propertyKey)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } void mitk::Annotation::SetIntProperty(const std::string &propertyKey, int intValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); Modified(); } void mitk::Annotation::SetBoolProperty(const std::string &propertyKey, bool boolValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); Modified(); } void mitk::Annotation::SetFloatProperty(const std::string &propertyKey, float floatValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); Modified(); } void mitk::Annotation::SetDoubleProperty(const std::string &propertyKey, double doubleValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); Modified(); } void mitk::Annotation::SetStringProperty(const std::string &propertyKey, const std::string &stringValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); Modified(); } std::string mitk::Annotation::GetName() const { mitk::StringProperty *sp = dynamic_cast<mitk::StringProperty *>(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } void mitk::Annotation::SetName(const std::string &name) { this->SetStringProperty("name", name); } bool mitk::Annotation::GetName(std::string &nodeName, const std::string &propertyKey) const { return GetStringProperty(propertyKey, nodeName); } void mitk::Annotation::SetText(std::string text) { SetStringProperty("Text", text.c_str()); } std::string mitk::Annotation::GetText() const { std::string text; GetStringProperty("Text", text); return text; } void mitk::Annotation::SetFontSize(int fontSize) { SetIntProperty("FontSize", fontSize); } int mitk::Annotation::GetFontSize() const { int fontSize = 1; GetIntProperty("FontSize", fontSize); return fontSize; } bool mitk::Annotation::GetVisibility(bool &visible, const std::string &propertyKey) const { return GetBoolProperty(propertyKey, visible); } bool mitk::Annotation::IsVisible(const std::string &propertyKey, bool defaultIsOn) const { return IsOn(propertyKey, defaultIsOn); } bool mitk::Annotation::GetColor(float rgb[], const std::string &propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast<mitk::ColorProperty *>(GetProperty(propertyKey)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } void mitk::Annotation::SetColor(const mitk::Color &color, const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetColor(float red, float green, float blue, const std::string &propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, propertyKey); } void mitk::Annotation::SetColor(const float rgb[], const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); this->m_PropertyList->SetProperty(propertyKey, prop); } bool mitk::Annotation::GetOpacity(float &opacity, const std::string &propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast<mitk::FloatProperty *>(GetProperty(propertyKey)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } void mitk::Annotation::SetOpacity(float opacity, const std::string &propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetVisibility(bool visible, const std::string &propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); this->m_PropertyList->SetProperty(propertyKey, prop); Modified(); } bool mitk::Annotation::BaseLocalStorage::IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Annotation *Annotation) { if (m_LastGenerateDataTime < Annotation->GetMTime()) return true; if (m_LastGenerateDataTime < Annotation->GetPropertyList()->GetMTime()) return true; if (renderer && m_LastGenerateDataTime < renderer->GetTimeStepUpdateTime()) return true; return false; } mitk::Annotation::Bounds mitk::Annotation::GetBoundsOnDisplay(mitk::BaseRenderer *) const { mitk::Annotation::Bounds bounds; bounds.Position[0] = bounds.Position[1] = bounds.Size[0] = bounds.Size[1] = 0; return bounds; } void mitk::Annotation::SetBoundsOnDisplay(mitk::BaseRenderer *, const mitk::Annotation::Bounds &) { } void mitk::Annotation::SetForceInForeground(bool forceForeground) { m_ForceInForeground = forceForeground; } bool mitk::Annotation::IsForceInForeground() const { return m_ForceInForeground; } mitk::PropertyList *mitk::Annotation::GetPropertyList() const { return m_PropertyList; } std::string mitk::Annotation::GetMicroserviceID() { return this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID).ToString(); } void mitk::Annotation::RegisterAsMicroservice(us::ServiceProperties props) { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); us::ModuleContext *context = us::GetModuleContext(); // Define ServiceProps mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.Annotation.id_"); props[US_PROPKEY_ID] = uidGen.GetUID(); m_ServiceRegistration = context->RegisterService(this, props); } void mitk::Annotation::UnRegisterMicroservice() { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } void mitk::Annotation::AnnotationModified() { Modified(); this->SetUSProperty(US_PROPKEY_MODIFIED, this->GetMTime()); } diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h index 0d64a124df..968452be5d 100644 --- a/Modules/Multilabel/mitkLabel.h +++ b/Modules/Multilabel/mitkLabel.h @@ -1,106 +1,107 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkLabel_h #define mitkLabel_h #include "MitkMultilabelExports.h" #include <mitkColorProperty.h> #include <mitkPropertyList.h> +#include <mitkPoint.h> #include <mitkVector.h> namespace mitk { //## //##Documentation //## @brief A data structure describing a label. //## @ingroup Data //## class MITKMULTILABEL_EXPORT Label : public PropertyList { public: mitkClassMacro(Label, mitk::PropertyList); typedef unsigned short PixelType; itkNewMacro(Self); mitkNewMacro2Param(Self, PixelType, const std::string&); /// The maximum value a label can get: Since the value is of type unsigned short MAX_LABEL_VALUE = 65535 static const PixelType MAX_LABEL_VALUE; void SetLocked(bool locked); bool GetLocked() const; void SetVisible(bool visible); bool GetVisible() const; void SetOpacity(float opacity); float GetOpacity() const; void SetName(const std::string &name); std::string GetName() const; void SetCenterOfMassIndex(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassIndex() const; void SetCenterOfMassCoordinates(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassCoordinates() const; void SetColor(const mitk::Color &); const mitk::Color &GetColor() const; void SetValue(PixelType pixelValue); PixelType GetValue() const; void SetLayer(unsigned int layer); unsigned int GetLayer() const; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; using itk::Object::Modified; void Modified() { Superclass::Modified(); } Label(); Label(PixelType value, const std::string& name); ~Label() override; protected: void PrintSelf(std::ostream &os, itk::Indent indent) const override; Label(const Label &other); private: itk::LightObject::Pointer InternalClone() const override; }; /** * @brief Equal A function comparing two labels for beeing equal in data * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - Lebel equality via Equal-PropetyList * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKMULTILABEL_EXPORT bool Equal(const mitk::Label &leftHandSide, const mitk::Label &rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif diff --git a/Modules/SceneSerialization/src/mitkSceneIO.cpp b/Modules/SceneSerialization/src/mitkSceneIO.cpp index c96aa37a3e..6072a7bc3f 100644 --- a/Modules/SceneSerialization/src/mitkSceneIO.cpp +++ b/Modules/SceneSerialization/src/mitkSceneIO.cpp @@ -1,577 +1,578 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include <Poco/Delegate.h> #include <Poco/Path.h> #include <Poco/TemporaryFile.h> #include <Poco/Zip/Compress.h> #include <Poco/Zip/Decompress.h> #include "mitkBaseDataSerializer.h" #include "mitkPropertyListSerializer.h" #include "mitkSceneIO.h" #include "mitkSceneReader.h" #include "mitkBaseRenderer.h" #include "mitkProgressBar.h" #include "mitkRenderingManager.h" #include "mitkStandaloneDataStorage.h" #include <mitkLocaleSwitch.h> #include <mitkStandardFileLocations.h> +#include <mitkUIDGenerator.h> #include <itkObjectFactoryBase.h> #include <fstream> #include <mitkIOUtil.h> #include <sstream> #include "itksys/SystemTools.hxx" #include <tinyxml2.h> mitk::SceneIO::SceneIO() : m_WorkingDirectory(""), m_UnzipErrors(0) { } mitk::SceneIO::~SceneIO() { } std::string mitk::SceneIO::CreateEmptyTempDirectory() { mitk::UIDGenerator uidGen; // std::string returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + // Poco::Path::separator() + "SceneIOTemp" + uidGen.GetUID(); std::string returnValue = Poco::Path::temp() + "SceneIOTemp" + uidGen.GetUID(); std::string uniquename = returnValue + Poco::Path::separator(); Poco::File tempdir(uniquename); try { bool existsNot = tempdir.createDirectory(); if (!existsNot) { MITK_ERROR << "Warning: Directory already exitsts: " << uniquename << " (choosing another)"; returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + Poco::Path::separator() + "SceneIOTempDirectory" + uidGen.GetUID(); uniquename = returnValue + Poco::Path::separator(); Poco::File tempdir2(uniquename); if (!tempdir2.createDirectory()) { MITK_ERROR << "Warning: Second directory also already exitsts: " << uniquename; } } } catch (std::exception &e) { MITK_ERROR << "Could not create temporary directory " << uniquename << ":" << e.what(); return ""; } return returnValue; } mitk::DataStorage::Pointer mitk::SceneIO::LoadScene(const std::string &filename, DataStorage *pStorage, bool clearStorageFirst) { mitk::LocaleSwitch localeSwitch("C"); // prepare data storage DataStorage::Pointer storage = pStorage; if (storage.IsNull()) { storage = StandaloneDataStorage::New().GetPointer(); } // test input filename if (filename.empty()) { MITK_ERROR << "No filename given. Not possible to load scene."; return storage; } // test if filename can be read std::ifstream file(filename.c_str(), std::ios::binary); if (!file.good()) { MITK_ERROR << "Cannot open '" << filename << "' for reading"; return storage; } // get new temporary directory m_WorkingDirectory = CreateEmptyTempDirectory(); if (m_WorkingDirectory.empty()) { MITK_ERROR << "Could not create temporary directory. Cannot open scene files."; return storage; } // unzip all filenames contents to temp dir m_UnzipErrors = 0; Poco::Zip::Decompress unzipper(file, Poco::Path(m_WorkingDirectory)); unzipper.EError += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>( this, &SceneIO::OnUnzipError); unzipper.EOk += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>( this, &SceneIO::OnUnzipOk); unzipper.decompressAllFiles(); unzipper.EError -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>( this, &SceneIO::OnUnzipError); unzipper.EOk -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>( this, &SceneIO::OnUnzipOk); if (m_UnzipErrors) { MITK_ERROR << "There were " << m_UnzipErrors << " errors unzipping '" << filename << "'. Will attempt to read whatever could be unzipped."; } // transcode locale-dependent string m_WorkingDirectory = Poco::Path::transcode (m_WorkingDirectory); auto indexFile = m_WorkingDirectory + mitk::IOUtil::GetDirectorySeparator() + "index.xml"; storage = LoadSceneUnzipped(indexFile, storage, clearStorageFirst); // delete temp directory try { Poco::File deleteDir(m_WorkingDirectory); deleteDir.remove(true); // recursive } catch (...) { MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory; } // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method) return storage; } mitk::DataStorage::Pointer mitk::SceneIO::LoadSceneUnzipped(const std::string &indexfilename, DataStorage *pStorage, bool clearStorageFirst) { mitk::LocaleSwitch localeSwitch("C"); // prepare data storage DataStorage::Pointer storage = pStorage; if (storage.IsNull()) { storage = StandaloneDataStorage::New().GetPointer(); } if (clearStorageFirst) { try { storage->Remove(storage->GetAll()); } catch (...) { MITK_ERROR << "DataStorage cannot be cleared properly."; } } // test input filename if (indexfilename.empty()) { MITK_ERROR << "No filename given. Not possible to load scene."; return storage; } // transcode locale-dependent string std::string tempfilename; std::string workingDir; itksys::SystemTools::SplitProgramPath(indexfilename, workingDir, tempfilename); // test if index.xml exists // parse index.xml with TinyXML tinyxml2::XMLDocument document; if (tinyxml2::XML_SUCCESS != document.LoadFile(indexfilename.c_str())) { MITK_ERROR << "Could not open/read/parse " << workingDir << mitk::IOUtil::GetDirectorySeparator() << "index.xml\nTinyXML reports: " << document.ErrorStr() << std::endl; return storage; } SceneReader::Pointer reader = SceneReader::New(); if (!reader->LoadScene(document, workingDir, storage)) { MITK_ERROR << "There were errors while loading scene file " << indexfilename << ". Your data may be corrupted"; } // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method) return storage; } bool mitk::SceneIO::SaveScene(DataStorage::SetOfObjects::ConstPointer sceneNodes, const DataStorage *storage, const std::string &filename) { if (!sceneNodes) { MITK_ERROR << "No set of nodes given. Not possible to save scene."; return false; } if (!storage) { MITK_ERROR << "No data storage given. Not possible to save scene."; // \TODO: Technically, it would be possible to // save the nodes without their relation return false; } if (filename.empty()) { MITK_ERROR << "No filename given. Not possible to save scene."; return false; } mitk::LocaleSwitch localeSwitch("C"); try { m_FailedNodes = DataStorage::SetOfObjects::New(); m_FailedProperties = PropertyList::New(); // start XML DOM tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); auto *version = document.NewElement("Version"); version->SetAttribute("Writer", __FILE__); version->SetAttribute("Revision", "$Revision: 17055 $"); version->SetAttribute("FileVersion", 1); document.InsertEndChild(version); // DataStorage::SetOfObjects::ConstPointer sceneNodes = storage->GetSubset( predicate ); if (sceneNodes.IsNull()) { MITK_WARN << "Saving empty scene to " << filename; } else { if (sceneNodes->size() == 0) { MITK_WARN << "Saving empty scene to " << filename; } MITK_INFO << "Storing scene with " << sceneNodes->size() << " objects to " << filename; m_WorkingDirectory = CreateEmptyTempDirectory(); if (m_WorkingDirectory.empty()) { MITK_ERROR << "Could not create temporary directory. Cannot create scene files."; return false; } ProgressBar::GetInstance()->AddStepsToDo(sceneNodes->size()); // find out about dependencies typedef std::map<DataNode *, std::string> UIDMapType; typedef std::map<DataNode *, std::list<std::string>> SourcesMapType; UIDMapType nodeUIDs; // for dependencies: ID of each node SourcesMapType sourceUIDs; // for dependencies: IDs of a node's parent nodes UIDGenerator nodeUIDGen("OBJECT_"); for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter) { DataNode *node = iter->GetPointer(); if (!node) continue; // unlikely event that we get a nullptr pointer as an object for saving. just ignore // generate UIDs for all source objects DataStorage::SetOfObjects::ConstPointer sourceObjects = storage->GetSources(node); for (auto sourceIter = sourceObjects->begin(); sourceIter != sourceObjects->end(); ++sourceIter) { if (std::find(sceneNodes->begin(), sceneNodes->end(), *sourceIter) == sceneNodes->end()) continue; // source is not saved, so don't generate a UID for this source // create a uid for the parent object if (nodeUIDs[*sourceIter].empty()) { nodeUIDs[*sourceIter] = nodeUIDGen.GetUID(); } // store this dependency for writing sourceUIDs[node].push_back(nodeUIDs[*sourceIter]); } if (nodeUIDs[node].empty()) { nodeUIDs[node] = nodeUIDGen.GetUID(); } } // write out objects, dependencies and properties for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter) { DataNode *node = iter->GetPointer(); if (node) { auto *nodeElement = document.NewElement("node"); std::string filenameHint(node->GetName()); filenameHint = itksys::SystemTools::MakeCindentifier( filenameHint.c_str()); // escape filename <-- only allow [A-Za-z0-9_], replace everything else with _ // store dependencies auto searchUIDIter = nodeUIDs.find(node); if (searchUIDIter != nodeUIDs.end()) { // store this node's ID nodeElement->SetAttribute("UID", searchUIDIter->second.c_str()); } auto searchSourcesIter = sourceUIDs.find(node); if (searchSourcesIter != sourceUIDs.end()) { // store all source IDs for (auto sourceUIDIter = searchSourcesIter->second.begin(); sourceUIDIter != searchSourcesIter->second.end(); ++sourceUIDIter) { auto *uidElement = document.NewElement("source"); uidElement->SetAttribute("UID", sourceUIDIter->c_str()); nodeElement->InsertEndChild(uidElement); } } // store basedata if (BaseData *data = node->GetData()) { // std::string filenameHint( node->GetName() ); bool error(false); auto *dataElement = SaveBaseData(document, data, filenameHint, error); // returns a reference to a file if (error) { m_FailedNodes->push_back(node); } // store basedata properties PropertyList *propertyList = data->GetPropertyList(); if (propertyList && !propertyList->IsEmpty()) { auto *baseDataPropertiesElement = SavePropertyList(document, propertyList, filenameHint + "-data"); // returns a reference to a file dataElement->InsertEndChild(baseDataPropertiesElement); } nodeElement->InsertEndChild(dataElement); } // store all renderwindow specific propertylists mitk::DataNode::PropertyListKeyNames propertyListKeys = node->GetPropertyListNames(); for (const auto &renderWindowName : propertyListKeys) { PropertyList *propertyList = node->GetPropertyList(renderWindowName); if (propertyList && !propertyList->IsEmpty()) { auto *renderWindowPropertiesElement = SavePropertyList(document, propertyList, filenameHint + "-" + renderWindowName); // returns a reference to a file renderWindowPropertiesElement->SetAttribute("renderwindow", renderWindowName.c_str()); nodeElement->InsertEndChild(renderWindowPropertiesElement); } } // don't forget the renderwindow independent list PropertyList *propertyList = node->GetPropertyList(); if (propertyList && !propertyList->IsEmpty()) { auto *propertiesElement = SavePropertyList(document, propertyList, filenameHint + "-node"); // returns a reference to a file nodeElement->InsertEndChild(propertiesElement); } document.InsertEndChild(nodeElement); } else { MITK_WARN << "Ignoring nullptr node during scene serialization."; } ProgressBar::GetInstance()->Progress(); } // end for all nodes } // end if sceneNodes std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); auto xmlFilename = defaultLocale_WorkingDirectory + Poco::Path::separator() + "index.xml"; if (tinyxml2::XML_SUCCESS != document.SaveFile(xmlFilename.c_str())) { MITK_ERROR << "Could not write scene to " << defaultLocale_WorkingDirectory << Poco::Path::separator() << "index.xml" << "\nTinyXML reports '" << document.ErrorStr() << "'"; return false; } else { try { Poco::File deleteFile(filename.c_str()); if (deleteFile.exists()) { deleteFile.remove(); } // create zip at filename std::ofstream file(filename.c_str(), std::ios::binary | std::ios::out); if (!file.good()) { MITK_ERROR << "Could not open a zip file for writing: '" << filename << "'"; return false; } else { Poco::Zip::Compress zipper(file, true); Poco::Path tmpdir(m_WorkingDirectory); zipper.addRecursive(tmpdir); zipper.close(); } try { Poco::File deleteDir(m_WorkingDirectory); deleteDir.remove(true); // recursive } catch (...) { MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory; return false; // ok? } } catch (std::exception &e) { MITK_ERROR << "Could not create ZIP file from " << m_WorkingDirectory << "\nReason: " << e.what(); return false; } return true; } } catch (std::exception &e) { MITK_ERROR << "Caught exception during saving temporary files to disk. Error description: '" << e.what() << "'"; return false; } } tinyxml2::XMLElement *mitk::SceneIO::SaveBaseData(tinyxml2::XMLDocument &doc, BaseData *data, const std::string &filenamehint, bool &error) { assert(data); error = true; // find correct serializer // the serializer must // - create a file containing all information to recreate the BaseData object --> needs to know where to put this // file (and a filename?) // - TODO what to do about writers that creates one file per timestep? auto *element = doc.NewElement("data"); element->SetAttribute("type", data->GetNameOfClass()); // construct name of serializer class std::string serializername(data->GetNameOfClass()); serializername += "Serializer"; std::list<itk::LightObject::Pointer> thingsThatCanSerializeThis = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (thingsThatCanSerializeThis.size() < 1) { MITK_ERROR << "No serializer found for " << data->GetNameOfClass() << ". Skipping object"; } for (auto iter = thingsThatCanSerializeThis.begin(); iter != thingsThatCanSerializeThis.end(); ++iter) { if (auto *serializer = dynamic_cast<BaseDataSerializer *>(iter->GetPointer())) { serializer->SetData(data); serializer->SetFilenameHint(filenamehint); std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory); try { std::string writtenfilename = serializer->Serialize(); element->SetAttribute("file", writtenfilename.c_str()); if (!writtenfilename.empty()) error = false; } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } element->SetAttribute("UID", data->GetUID().c_str()); return element; } tinyxml2::XMLElement *mitk::SceneIO::SavePropertyList(tinyxml2::XMLDocument &doc, PropertyList *propertyList, const std::string &filenamehint) { assert(propertyList); // - TODO what to do about shared properties (same object in two lists or behind several keys)? auto *element = doc.NewElement("properties"); // construct name of serializer class PropertyListSerializer::Pointer serializer = PropertyListSerializer::New(); serializer->SetPropertyList(propertyList); serializer->SetFilenameHint(filenamehint); std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory); try { std::string writtenfilename = serializer->Serialize(); element->SetAttribute("file", writtenfilename.c_str()); PropertyList::Pointer failedProperties = serializer->GetFailedProperties(); if (failedProperties.IsNotNull()) { // move failed properties to global list m_FailedProperties->ConcatenatePropertyList(failedProperties, true); } } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } return element; } const mitk::SceneIO::FailedBaseDataListType *mitk::SceneIO::GetFailedNodes() { return m_FailedNodes.GetPointer(); } const mitk::PropertyList *mitk::SceneIO::GetFailedProperties() { return m_FailedProperties; } void mitk::SceneIO::OnUnzipError(const void * /*pSender*/, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> &info) { ++m_UnzipErrors; MITK_ERROR << "Error while unzipping: " << info.second; } void mitk::SceneIO::OnUnzipOk(const void * /*pSender*/, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path> & /*info*/) { // MITK_INFO << "Unzipped ok: " << info.second.toString(); } diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.h b/Modules/SceneSerialization/src/mitkSceneReaderV1.h index ba69c6081e..2c370b1ad8 100644 --- a/Modules/SceneSerialization/src/mitkSceneReaderV1.h +++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.h @@ -1,76 +1,77 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSceneReaderV1_h #define mitkSceneReaderV1_h -#include "mitkSceneReader.h" +#include <mitkSceneReader.h> +#include <mitkUIDGenerator.h> namespace tinyxml2 { class XMLElement; } namespace mitk { class SceneReaderV1 : public SceneReader { public: mitkClassMacro(SceneReaderV1, SceneReader); itkFactorylessNewMacro(Self); itkCloneMacro(Self); bool LoadScene(tinyxml2::XMLDocument &document, const std::string &workingDirectory, DataStorage *storage) override; protected: /** \brief tries to create one DataNode from a given XML \<node\> element */ DataNode::Pointer LoadBaseDataFromDataTag(const tinyxml2::XMLElement *dataElement, const PropertyList *properties, const std::string &workingDirectory, bool &error); /** \brief reads all the properties from the XML document and recreates them in node */ bool DecorateNodeWithProperties(DataNode *node, const tinyxml2::XMLElement *nodeElement, const std::string &workingDirectory); /** \brief Clear a default property list and handle some exceptions. Called after assigning a BaseData object to a fresh DataNode via SetData(). This call to SetData() would create default properties that have not been there when saving the scene. Since they can produce problems, we clear the list and use only those properties that we read from the scene file. This method also handles some exceptions for backwards compatibility. Those exceptions are documented directly in the code of the method. */ void ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList); typedef std::pair<DataNode::Pointer, std::list<std::string>> NodesAndParentsPair; typedef std::list<NodesAndParentsPair> OrderedNodesList; typedef std::map<std::string, DataNode *> IDToNodeMappingType; typedef std::map<DataNode *, std::string> NodeToIDMappingType; OrderedNodesList m_OrderedNodePairs; IDToNodeMappingType m_NodeForID; NodeToIDMappingType m_IDForNode; UIDGenerator m_UIDGen; }; } #endif