diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtWidgetsTweakletImpl.cpp b/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtWidgetsTweakletImpl.cpp index 3a13edec67..63a520a898 100755 --- a/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtWidgetsTweakletImpl.cpp +++ b/BlueBerry/Bundles/org.blueberry.ui.qt/src/internal/berryQtWidgetsTweakletImpl.cpp @@ -1,446 +1,450 @@ /*=================================================================== BlueBerry Platform 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 "berryLog.h" #include "berryQtWidgetsTweakletImpl.h" #include "berryQtSash.h" #include "berryQtShell.h" #include #include #include #include #include #include namespace berry { QtSelectionListenerWrapper::QtSelectionListenerWrapper(QWidget* w) : widget(w) { } void QtSelectionListenerWrapper::AddListener(GuiTk::ISelectionListener::Pointer listener) { QAbstractButton* button = qobject_cast(widget); if (button != 0) { this->connect(button, "clicked(bool)", this, "QAbstractButtonClicked(bool)"); selectionEvents.AddListener(listener); } BERRY_WARN << "WARNING: QtWidgetsTweaklet: no suitable type for listening for selections found!\n"; } int QtSelectionListenerWrapper::RemoveListener(GuiTk::ISelectionListener::Pointer listener) { selectionEvents.RemoveListener(listener); return static_cast(std::max(selectionEvents.selected.GetListeners().size(), selectionEvents.defaultSelected.GetListeners().size())); } void QtSelectionListenerWrapper::QAbstractButtonClicked(bool /*checked*/) { GuiTk::SelectionEvent::Pointer event(new GuiTk::SelectionEvent(widget)); selectionEvents.selected(event); } void QtWidgetsTweakletImpl::AddSelectionListener(QWidget* widget, GuiTk::ISelectionListener::Pointer listener) { if (widget == 0) return; // special handling for berry::QtSash QtSash* sash = qobject_cast(widget); if (sash != 0) { sash->AddSelectionListener(listener); return; } // "normal" Qt widgets get wrapped QtSelectionListenerWrapper* wrapper = selectionListenerMap[widget]; if (wrapper == 0) { wrapper = new QtSelectionListenerWrapper(widget); selectionListenerMap[widget] = wrapper; } wrapper->AddListener(listener); } void QtWidgetsTweakletImpl::RemoveSelectionListener(QWidget* widget, GuiTk::ISelectionListener::Pointer listener) { if (widget == 0) return; // special handling for berry::QtSash QtSash* sash = qobject_cast(widget); if (sash != 0) { sash->RemoveSelectionListener(listener); return; } QtSelectionListenerWrapper* wrapper = selectionListenerMap[widget]; if (wrapper == 0) return; if (wrapper->RemoveListener(listener) == 0) { selectionListenerMap.erase(wrapper); delete wrapper; } } Rectangle QtWidgetsTweakletImpl::GetScreenSize(int i) { QDesktopWidget *desktop = QApplication::desktop(); QRect screenGeometry; if (i < 0) screenGeometry = desktop->screen()->geometry(); else screenGeometry = desktop->screenGeometry(i); return (Rectangle(screenGeometry.x(), screenGeometry.y() , screenGeometry.width(), screenGeometry.height())); } unsigned int QtWidgetsTweakletImpl::GetScreenNumber() { QDesktopWidget *desktop = QApplication::desktop(); // get the primary screen unsigned int numScreens = desktop->numScreens(); return numScreens; } int QtWidgetsTweakletImpl::GetPrimaryScreenNumber() { QDesktopWidget *desktop = QApplication::desktop(); // get the primary screen int primaryScreenNr = desktop->primaryScreen(); return primaryScreenNr; } Rectangle QtWidgetsTweakletImpl::GetAvailableScreenSize(int i) { QDesktopWidget *desktop = QApplication::desktop(); QRect screenGeometry; if (i < 0) screenGeometry = desktop->screen()->geometry(); else screenGeometry = desktop->availableGeometry(i); return (Rectangle(screenGeometry.x(), screenGeometry.y() , screenGeometry.width(), screenGeometry.height())); } int QtWidgetsTweakletImpl::GetClosestScreenNumber(const Rectangle& r) { QDesktopWidget *desktop = QApplication::desktop(); return desktop->screenNumber(QPoint(r.x + r.width/2, r.y + r.height/2)); } void QtWidgetsTweakletImpl::AddControlListener(QtWidgetController* controller, GuiTk::IControlListener::Pointer listener) { controller->AddControlListener(listener); } void QtWidgetsTweakletImpl::RemoveControlListener(QtWidgetController* controller, GuiTk::IControlListener::Pointer listener) { controller->RemoveControlListener(listener); } bool QtWidgetsTweakletImpl::GetEnabled(QWidget* widget) { return widget->isEnabled(); } void QtWidgetsTweakletImpl::SetEnabled(QWidget* widget, bool enabled) { widget->setEnabled(enabled); } void QtWidgetsTweakletImpl::SetBounds(QWidget* widget, const Rectangle& bounds) { widget->setGeometry(bounds.x, bounds.y, bounds.width, bounds.height); } Rectangle QtWidgetsTweakletImpl::GetBounds(QWidget* widget) { const QRect& geometry = widget->geometry(); Rectangle rect(geometry.x(), geometry.y(), geometry.width(), geometry.height()); return rect; } void QtWidgetsTweakletImpl::SetVisible(QWidget* widget, bool visible) { widget->setVisible(visible); } bool QtWidgetsTweakletImpl::GetVisible(QWidget* widget) { return !widget->isHidden(); } bool QtWidgetsTweakletImpl::IsVisible(QWidget* widget) { return widget->isVisible(); } Rectangle QtWidgetsTweakletImpl::GetClientArea(QWidget* widget) { const QRect& contentsRect = widget->contentsRect(); Rectangle rect(contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height()); return rect; } void* QtWidgetsTweakletImpl::GetParent(QWidget* widget) { return widget->parentWidget(); } bool QtWidgetsTweakletImpl::SetParent(QWidget* widget, QWidget* parent) { if (parent != widget->parentWidget()) { widget->setParent(parent); return true; } return false; } void QtWidgetsTweakletImpl::SetData(QWidget* object, const std::string& id, Object::Pointer data) { if (object == 0) return; QVariant variant; if (data != 0) variant.setValue(data); object->setProperty(id.c_str(), variant); } Object::Pointer QtWidgetsTweakletImpl::GetData(QWidget* object, const std::string& id) { if (object == 0) return Object::Pointer(0); QVariant variant = object->property(id.c_str()); if (variant.isValid()) { return variant.value(); } return Object::Pointer(0); } Point QtWidgetsTweakletImpl::GetCursorLocation() { QPoint qpoint = QCursor::pos(); return Point(qpoint.x(), qpoint.y()); } QWidget* QtWidgetsTweakletImpl::GetCursorControl() { return QApplication::widgetAt(QCursor::pos()); } QWidget* QtWidgetsTweakletImpl::FindControl(const std::vector& shells, const Point& location) { for (std::vector::const_iterator iter = shells.begin(); iter != shells.end(); ++iter) { QWidget* shellWidget = static_cast((*iter)->GetControl()); QWidget* control = shellWidget->childAt(location.x, location.y); if (control) return control; } return 0; } bool QtWidgetsTweakletImpl::IsChild(QObject* parentToTest, QObject* childToTest) { bool found = false; QObject* parent = childToTest->parent(); while (!found && parent != 0) { if (parent == parentToTest) found = true; parent = parent->parent(); } return found; } QWidget* QtWidgetsTweakletImpl::GetFocusControl() { return QApplication::focusWidget(); } bool QtWidgetsTweakletImpl::IsReparentable(QWidget* /*widget*/) { return true; } void QtWidgetsTweakletImpl::MoveAbove(QWidget* widgetToMove, QWidget* /*widget*/) { widgetToMove->raise(); } void QtWidgetsTweakletImpl::MoveBelow(QWidget* widgetToMove, QWidget* /*widget*/) { widgetToMove->lower(); } void QtWidgetsTweakletImpl::Dispose(QWidget* widget) { delete widget; widget = 0; } Shell::Pointer QtWidgetsTweakletImpl::CreateShell(Shell::Pointer parent, int style) { +#ifdef __APPLE__ + Qt::WindowFlags qtFlags(0); +#else Qt::WindowFlags qtFlags(Qt::CustomizeWindowHint); +#endif if (style & Constants::MAX) qtFlags |= Qt::WindowMaximizeButtonHint; if (style & Constants::MIN) qtFlags |= Qt::WindowMinimizeButtonHint; if (style & Constants::CLOSE) { qtFlags |= Qt::WindowSystemMenuHint; #if QT_VERSION >= 0x040500 qtFlags |= Qt::WindowCloseButtonHint; #endif } if (!(style & Constants::BORDER)) qtFlags |= Qt::FramelessWindowHint; if (style & Constants::TITLE) qtFlags |= Qt::WindowTitleHint; if (style & Constants::TOOL) qtFlags |= Qt::Tool; QWidget* parentWidget = 0; if (parent != 0) parentWidget = static_cast(parent->GetControl()); QtShell* qtshell = new QtShell(parentWidget, qtFlags); Shell::Pointer shell(qtshell); shellList.push_back(shell); if ((style & Constants::APPLICATION_MODAL) || (style & Constants::SYSTEM_MODAL)) qtshell->GetWidget()->setWindowModality(Qt::ApplicationModal); if (style & Constants::PRIMARY_MODAL) qtshell->GetWidget()->setWindowModality(Qt::WindowModal); return shell; } QWidget* QtWidgetsTweakletImpl::CreateComposite(QWidget* parent) { QWidget* composite = new QtControlWidget(parent, 0); composite->setObjectName("created composite"); return composite; } void QtWidgetsTweakletImpl::DisposeShell(Shell::Pointer shell) { shellList.remove(shell); } std::vector QtWidgetsTweakletImpl::GetShells() { std::vector shells(shellList.begin(), shellList.end()); return shells; } Shell::Pointer QtWidgetsTweakletImpl::GetShell(QWidget* widget) { QWidget* qwindow = widget->window(); QVariant variant = qwindow->property(QtWidgetController::PROPERTY_ID); if (variant.isValid()) { QtWidgetController::Pointer controller = variant.value(); poco_assert(controller != 0); return controller->GetShell(); } return Shell::Pointer(0); } Shell::Pointer QtWidgetsTweakletImpl::GetActiveShell() { QWidget* qwidget = QApplication::activeWindow(); if (qwidget == 0) return Shell::Pointer(0); QVariant variant = qwidget->property(QtWidgetController::PROPERTY_ID); if (variant.isValid()) { return variant.value()->GetShell(); } return Shell::Pointer(0); } Rectangle QtWidgetsTweakletImpl::ToControl(QWidget* coordinateSystem, const Rectangle& toConvert) { QPoint globalUpperLeft(toConvert.x, toConvert.y); QPoint globalLowerRight(toConvert.x + toConvert.width, toConvert.y + toConvert.height); QPoint upperLeft = coordinateSystem->mapFromGlobal(globalUpperLeft); QPoint lowerRight = coordinateSystem->mapFromGlobal(globalLowerRight); return Rectangle(upperLeft.x(), upperLeft.y(), lowerRight.x() - upperLeft.x(), lowerRight.y() - upperLeft.y()); } Point QtWidgetsTweakletImpl::ToControl(QWidget* coordinateSystem, const Point& toConvert) { QPoint displayPoint(toConvert.x, toConvert.y); QPoint localPoint = coordinateSystem->mapFromGlobal(displayPoint); return Point(localPoint.x(), localPoint.y()); } Rectangle QtWidgetsTweakletImpl::ToDisplay(QWidget* coordinateSystem, const Rectangle& toConvert) { QPoint upperLeft(toConvert.x, toConvert.y); QPoint lowerRight(toConvert.x + toConvert.width, toConvert.y + toConvert.height); QPoint globalUpperLeft = coordinateSystem->mapToGlobal(upperLeft); QPoint globalLowerRight = coordinateSystem->mapToGlobal(lowerRight); return Rectangle(globalUpperLeft.x(), globalUpperLeft.y(), globalLowerRight.x() - globalUpperLeft.x(), globalLowerRight.y() - globalUpperLeft.y()); } Point QtWidgetsTweakletImpl::ToDisplay(QWidget* coordinateSystem, const Point& toConvert) { QPoint localPoint(toConvert.x, toConvert.y); QPoint displayPoint = coordinateSystem->mapToGlobal(localPoint); return Point(displayPoint.x(), displayPoint.y()); } } diff --git a/CMake/mitkSetupVariables.cmake b/CMake/mitkSetupVariables.cmake index b70bd66f70..efed74fc82 100644 --- a/CMake/mitkSetupVariables.cmake +++ b/CMake/mitkSetupVariables.cmake @@ -1,161 +1,159 @@ if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() set(LIBPOSTFIX "") # MITK_VERSION set(MITK_VERSION_MAJOR "2012") set(MITK_VERSION_MINOR "06") set(MITK_VERSION_PATCH "00") set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------- # Configuration of module system #----------------------------------- set(MODULES_CONF_DIRNAME modulesConf) set(MODULES_CONF_DIRS ${MITK_BINARY_DIR}/${MODULES_CONF_DIRNAME}) if(NOT UNIX AND NOT MINGW) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() # build the MITK_INCLUDE_DIRS variable set(MITK_INCLUDE_DIRS ${ITK_INCLUDE_DIRS} ${VTK_INCLUDE_DIRS} ${PROJECT_BINARY_DIR} # contains mitkConfig.h and similar files ${MODULES_CONF_DIRS} # contains module *Exports.h files ) set(CORE_DIRECTORIES Common DataManagement Algorithms IO Rendering Interactions Controllers Service) foreach(d ${CORE_DIRECTORIES}) list(APPEND MITK_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/Core/Code/${d}) endforeach() #list(APPEND MITK_INCLUDE_DIRS #${ITK_INCLUDE_DIRS} #${VTK_INCLUDE_DIRS} # ) -#foreach(d Utilities Utilities/ipPic Utilities/IIL4MITK Utilities/pic2vtk Utilities/tinyxml Utilities/mbilog) foreach(d Utilities Utilities/ipPic Utilities/pic2vtk Utilities/tinyxml Utilities/mbilog) list(APPEND MITK_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/${d}) endforeach() list(APPEND MITK_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/Utilities/mbilog) if(WIN32) list(APPEND MITK_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipPic/win32) endif() # additional include dirs variables set(ANN_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ann/include) set(IPSEGMENTATION_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipSegmentation) # variables containing librariy names set(MITK_CORE_LIBRARIES Mitk) set(VTK_FOR_MITK_LIBRARIES vtkGraphics vtkCommon vtkFiltering vtkftgl vtkGraphics vtkHybrid vtkImaging vtkIO vtkParallel vtkRendering vtkVolumeRendering vtkWidgets ${VTK_JPEG_LIBRARIES} ${VTK_PNG_LIBRARIES} ${VTK_ZLIB_LIBRARIES} ${VTK_EXPAT_LIBRARIES} ${VTK_FREETYPE_LIBRARIES} ) # TODO: maybe solve this with lib depends mechanism of CMake set(UTIL_FOR_MITK_LIBRARIES mitkIpPic mitkIpFunc mbilog) set(LIBRARIES_FOR_MITK_CORE ${UTIL_FOR_MITK_LIBRARIES} ${VTK_FOR_MITK_LIBRARIES} ${ITK_LIBRARIES} ) set(MITK_LIBRARIES ${MITK_CORE_LIBRARIES} ${LIBRARIES_FOR_MITK_CORE} pic2vtk - #IIL4MITK ipSegmentation ann ) # variables used in CMake macros which are called from external projects set(MITK_VTK_LIBRARY_DIRS ${VTK_LIBRARY_DIRS}) set(MITK_ITK_LIBRARY_DIRS ${ITK_LIBRARY_DIRS}) # variables containing link directories set(MITK_LIBRARY_DIRS ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) set(MITK_LINK_DIRECTORIES ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${ITK_LIBRARY_DIRS} ${VTK_LIBRARY_DIRS} ${GDCM_LIBRARY_DIRS}) # Qt support if(MITK_USE_QT) find_package(Qt4 REQUIRED) set(QMITK_INCLUDE_DIRS ${MITK_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/Modules/Qmitk ${PROJECT_BINARY_DIR}/Modules/Qmitk ) set(QMITK_LIBRARIES Qmitk ${MITK_LIBRARIES}) set(QMITK_LINK_DIRECTORIES ${MITK_LINK_DIRECTORIES}) endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # create a list of types for template instantiations of itk image access functions function(_create_type_seq TYPES seq_var seqdim_var) set(_seq ) set(_seq_dim ) string(REPLACE "," ";" _pixeltypes "${TYPES}") foreach(_pixeltype ${_pixeltypes}) set(_seq "${_seq}(${_pixeltype})") set(_seq_dim "${_seq_dim}((${_pixeltype},dim))") endforeach() set(${seq_var} "${_seq}" PARENT_SCOPE) set(${seqdim_var} "${_seq_dim}" PARENT_SCOPE) endfunction() set(MITK_ACCESSBYITK_PIXEL_TYPES ) set(MITK_ACCESSBYITK_PIXEL_TYPES_SEQ ) set(MITK_ACCESSBYITK_TYPES_DIMN_SEQ ) foreach(_type INTEGRAL FLOATING COMPOSITE) set(_typelist "${MITK_ACCESSBYITK_${_type}_PIXEL_TYPES}") if(_typelist) if(MITK_ACCESSBYITK_PIXEL_TYPES) set(MITK_ACCESSBYITK_PIXEL_TYPES "${MITK_ACCESSBYITK_PIXEL_TYPES},${_typelist}") else() set(MITK_ACCESSBYITK_PIXEL_TYPES "${_typelist}") endif() endif() _create_type_seq("${_typelist}" MITK_ACCESSBYITK_${_type}_PIXEL_TYPES_SEQ MITK_ACCESSBYITK_${_type}_TYPES_DIMN_SEQ) set(MITK_ACCESSBYITK_PIXEL_TYPES_SEQ "${MITK_ACCESSBYITK_PIXEL_TYPES_SEQ}${MITK_ACCESSBYITK_${_type}_PIXEL_TYPES_SEQ}") set(MITK_ACCESSBYITK_TYPES_DIMN_SEQ "${MITK_ACCESSBYITK_TYPES_DIMN_SEQ}${MITK_ACCESSBYITK_${_type}_TYPES_DIMN_SEQ}") endforeach() set(MITK_ACCESSBYITK_DIMENSIONS_SEQ ) string(REPLACE "," ";" _dimensions "${MITK_ACCESSBYITK_DIMENSIONS}") foreach(_dimension ${_dimensions}) set(MITK_ACCESSBYITK_DIMENSIONS_SEQ "${MITK_ACCESSBYITK_DIMENSIONS_SEQ}(${_dimension})") endforeach() diff --git a/CMakeExternals/VTK.cmake b/CMakeExternals/VTK.cmake index b25a27cd58..9a3aab40ed 100644 --- a/CMakeExternals/VTK.cmake +++ b/CMakeExternals/VTK.cmake @@ -1,104 +1,104 @@ #----------------------------------------------------------------------------- # VTK #----------------------------------------------------------------------------- if(WIN32) option(VTK_USE_SYSTEM_FREETYPE OFF) else(WIN32) option(VTK_USE_SYSTEM_FREETYPE ON) endif(WIN32) # Sanity checks if(DEFINED VTK_DIR AND NOT EXISTS ${VTK_DIR}) message(FATAL_ERROR "VTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj VTK) set(proj_DEPENDENCIES ) set(VTK_DEPENDS ${proj}) if(NOT DEFINED VTK_DIR) set(additional_cmake_args ) if(MINGW) set(additional_cmake_args -DCMAKE_USE_WIN32_THREADS:BOOL=ON -DCMAKE_USE_PTHREADS:BOOL=OFF -DVTK_USE_VIDEO4WINDOWS:BOOL=OFF # no header files provided by MinGW ) endif() if(MITK_USE_Python) list(APPEND additional_cmake_args -DVTK_WRAP_PYTHON:BOOL=ON -DVTK_USE_TK:BOOL=OFF -DVTK_WINDOWS_PYTHON_DEBUGGABLE:BOOL=OFF ) endif() if(MITK_USE_QT) list(APPEND additional_cmake_args -DDESIRED_QT_VERSION:STRING=4 -DVTK_USE_GUISUPPORT:BOOL=ON - -DVTK_USE_QVTK_QTOPENGL:BOOL=ON + -DVTK_USE_QVTK_QTOPENGL:BOOL=OFF -DVTK_USE_QT:BOOL=ON -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} ) endif() option(MITK_USE_VTK_5_8_IN_SUPERBUILD "Use VTK 5.8 in MITK superbuild" OFF) include(mitkFunctionGetGccVersion) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) if(GCC_VERSION AND NOT ${GCC_VERSION} VERSION_LESS "4.6") if(NOT MITK_USE_VTK_5_8_IN_SUPERBUILD) message("Forcing VTK 5.8 since we're using gcc ${GCC_VERSION}") endif() set(MITK_USE_VTK_5_8_IN_SUPERBUILD ON CACHE BOOL "Use VTK 5.8 in MITK superbuild" FORCE) endif() if(CMAKE_CXX_COMPILER MATCHES clang) if(NOT MITK_USE_VTK_5_8_IN_SUPERBUILD) message("Forcing VTK 5.8 since we're using clang") endif() set(MITK_USE_VTK_5_8_IN_SUPERBUILD ON CACHE BOOL "Use VTK 5.8 in MITK superbuild" FORCE) endif() if(MITK_USE_VTK_5_8_IN_SUPERBUILD) set(VTK_URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/vtk-5.8.0.tar.gz) set(VTK_URL_MD5 37b7297d02d647cc6ca95b38174cb41f) else() set(VTK_URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/vtk-5.6.1.tar.gz) set(VTK_URL_MD5 b80a76435207c5d0f74dfcab15b75181) endif() ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake URL ${VTK_URL} URL_MD5 ${VTK_URL_MD5} INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} -DVTK_WRAP_TCL:BOOL=OFF -DVTK_WRAP_PYTHON:BOOL=OFF -DVTK_WRAP_JAVA:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=ON -DVTK_USE_PARALLEL:BOOL=ON -DVTK_USE_CHARTS:BOOL=OFF -DVTK_USE_QTCHARTS:BOOL=ON -DVTK_USE_GEOVIS:BOOL=OFF -DVTK_USE_SYSTEM_FREETYPE:BOOL=${VTK_USE_SYSTEM_FREETYPE} -DVTK_USE_QVTK_QTOPENGL:BOOL=OFF -DVTK_LEGACY_REMOVE:BOOL=ON ${additional_cmake_args} DEPENDS ${proj_DEPENDENCIES} ) set(VTK_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-build) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/Core/Code/CMakeLists.txt b/Core/Code/CMakeLists.txt index 64c31b715b..34d1d46016 100644 --- a/Core/Code/CMakeLists.txt +++ b/Core/Code/CMakeLists.txt @@ -1,81 +1,81 @@ #FIND_PACKAGE(OpenGL) #IF(NOT OPENGL_FOUND) # MESSAGE("GL is required for MITK rendering") #ENDIF(NOT OPENGL_FOUND ) # Configure the C++ Micro Services Code for MITK find_package(ITK REQUIRED) include(${ITK_USE_FILE}) set(US_NAMESPACE "mitk") set(US_HEADER_PREFIX "mitk") set(US_BASECLASS_NAME "itk::LightObject") set(US_BASECLASS_HEADER "itkLightObject.h") set(US_BASECLASS_PACKAGE "ITK") set(US_ENABLE_THREADING_SUPPORT 1) set(US_EMBEDDING_LIBRARY Mitk) set(US_BUILD_TESTING ${BUILD_TESTING}) if(BUILD_TESTING) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Testing) set(US_BASECLASS_HEADER "uServicesBaseObject.h") set(US_TEST_LABELS MITK-Core) endif() add_subdirectory(CppMicroServices) include(${CMAKE_CURRENT_BINARY_DIR}/CppMicroServices/CppMicroServicesConfig.cmake) set(TOOL_CPPS "") MITK_CREATE_MODULE( Mitk INCLUDE_DIRS ${CppMicroServices_INCLUDE_DIRS} Algorithms Common DataManagement Controllers Interactions Interfaces IO Rendering ${MITK_BINARY_DIR} INTERNAL_INCLUDE_DIRS ${OPENGL_INCLUDE_DIR} ${IPSEGMENTATION_INCLUDE_DIR} ${ANN_INCLUDE_DIR} ${CppMicroServices_INTERNAL_INCLUDE_DIRS} DEPENDS mbilog tinyxml - DEPENDS_INTERNAL pic2vtk #IIL4MITK + DEPENDS_INTERNAL pic2vtk PACKAGE_DEPENDS ITK VTK EXPORT_DEFINE MITK_CORE_EXPORT WARNINGS_AS_ERRORS ) # this is needed for libraries which link to Mitk and need # symbols from explicitly instantiated templates like # mitk::SurfaceVtkWriter which is referenced in # QmitkCommonFunctionality in the QmitkExt library. if(MINGW) get_target_property(_mitkCore_MINGW_linkflags Mitk LINK_FLAGS) if(NOT _mitkCore_MINGW_linkflags) set(_mitkCore_MINGW_linkflags "") endif(NOT _mitkCore_MINGW_linkflags) set_target_properties(Mitk PROPERTIES LINK_FLAGS "${_mitkCore_MINGW_linkflags} -Wl,--export-all-symbols") endif(MINGW) # replacing Mitk by Mitk [due to removing the PROVIDES macro TARGET_LINK_LIBRARIES(Mitk ${LIBRARIES_FOR_${KITNAME}_CORE} ${IPFUNC_LIBRARY} ipSegmentation ann) #TARGET_LINK_LIBRARIES(Mitk ${OPENGL_LIBRARIES} ) TARGET_LINK_LIBRARIES(Mitk gdcmCommon gdcmIOD gdcmDSED) if(MSVC_IDE OR MSVC_VERSION OR MINGW) target_link_libraries(Mitk psapi.lib) endif(MSVC_IDE OR MSVC_VERSION OR MINGW) # verify ITK has been built with GDCM >= 2.0.14 set(GDCM_FULL_VERSION "${GDCM_MAJOR_VERSION}.${GDCM_MINOR_VERSION}.${GDCM_BUILD_VERSION}") set(MITK_REQUIRED_GDCM_VERSION "2.0.14") if(GDCM_FULL_VERSION VERSION_LESS MITK_REQUIRED_GDCM_VERSION) message(SEND_ERROR "Mitk: MITK requires ITK with at least GDCM version ${MITK_REQUIRED_GDCM_VERSION}.\nFound version ${GDCM_FULL_VERSION} (GDCM NOT found if you don't see a version here)") else(GDCM_FULL_VERSION VERSION_LESS MITK_REQUIRED_GDCM_VERSION) message(STATUS "Mitk: Found GDCM version ${GDCM_FULL_VERSION}") endif(GDCM_FULL_VERSION VERSION_LESS MITK_REQUIRED_GDCM_VERSION) # build tests? OPTION(BUILD_TESTING "Build the MITK Core tests." ON) IF(BUILD_TESTING) ENABLE_TESTING() ADD_SUBDIRECTORY(Testing) -ENDIF(BUILD_TESTING) \ No newline at end of file +ENDIF(BUILD_TESTING) diff --git a/Core/Code/Controllers/mitkSliceNavigationController.cpp b/Core/Code/Controllers/mitkSliceNavigationController.cpp index fdc3b2fd2d..149ddf05b5 100644 --- a/Core/Code/Controllers/mitkSliceNavigationController.cpp +++ b/Core/Code/Controllers/mitkSliceNavigationController.cpp @@ -1,734 +1,726 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSliceNavigationController.h" #include "mitkBaseRenderer.h" #include "mitkSlicedGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkStateEvent.h" #include "mitkCrosshairPositionEvent.h" #include "mitkPositionEvent.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkGlobalInteraction.h" #include "mitkEventMapper.h" #include "mitkFocusManager.h" #include "mitkVtkPropRenderer.h" #include "mitkRenderingManager.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include "mitkPlaneOperation.h" #include "mitkUndoController.h" #include "mitkOperationEvent.h" #include "mitkNodePredicateDataType.h" #include "mitkStatusBar.h" #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController( const char *type ) : BaseController( type ), m_InputWorldGeometry( NULL ), m_CreatedWorldGeometry( NULL ), m_ViewDirection( Transversal ), m_DefaultViewDirection( Transversal ), m_RenderingManager( NULL ), m_Renderer( NULL ), m_Top( false ), m_FrontSide( false ), m_Rotated( false ), m_BlockUpdate( false ), m_SliceLocked( false ), m_SliceRotationLocked( false ), m_OldPos(0) { typedef itk::SimpleMemberCommand< SliceNavigationController > SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendSlice ); timeStepperChangedCommand->SetCallbackFunction( this, &SliceNavigationController::SendTime ); m_Slice->AddObserver( itk::ModifiedEvent(), sliceStepperChangedCommand ); m_Time->AddObserver( itk::ModifiedEvent(), timeStepperChangedCommand ); m_Slice->SetUnitName( "mm" ); m_Time->SetUnitName( "ms" ); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() { } void SliceNavigationController::SetInputWorldGeometry( const Geometry3D *geometry ) { if ( geometry != NULL ) { if ( const_cast< BoundingBox * >( geometry->GetBoundingBox()) ->GetDiagonalLength2() < eps ) { itkWarningMacro( "setting an empty bounding-box" ); geometry = NULL; } } if ( m_InputWorldGeometry != geometry ) { m_InputWorldGeometry = geometry; this->Modified(); } } RenderingManager * SliceNavigationController::GetRenderingManager() const { mitk::RenderingManager* renderingManager = m_RenderingManager.GetPointer(); if (renderingManager != NULL) return renderingManager; if ( m_Renderer != NULL ) { renderingManager = m_Renderer->GetRenderingManager(); if (renderingManager != NULL) return renderingManager; } return mitk::RenderingManager::GetInstance(); } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } void SliceNavigationController::Update() { if ( !m_BlockUpdate ) { if ( m_ViewDirection == Transversal ) { this->Update( Transversal, false, false, true ); } else { this->Update( m_ViewDirection ); } } } void SliceNavigationController::Update( SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated ) { const TimeSlicedGeometry* worldTimeSlicedGeometry = dynamic_cast< const TimeSlicedGeometry * >( m_InputWorldGeometry.GetPointer() ); if( m_BlockUpdate || m_InputWorldGeometry.IsNull() || ( (worldTimeSlicedGeometry != NULL) && (worldTimeSlicedGeometry->GetTimeSteps() == 0) ) ) { return; } m_BlockUpdate = true; if ( m_LastUpdateTime < m_InputWorldGeometry->GetMTime() ) { Modified(); } this->SetViewDirection( viewDirection ); this->SetTop( top ); this->SetFrontSide( frontside ); this->SetRotated( rotated ); if ( m_LastUpdateTime < GetMTime() ) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = NULL; m_CreatedWorldGeometry = NULL; switch ( viewDirection ) { case Original: if ( worldTimeSlicedGeometry != NULL ) { m_CreatedWorldGeometry = static_cast< TimeSlicedGeometry * >( m_InputWorldGeometry->Clone().GetPointer() ); worldTimeSlicedGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometry3D( this->GetTime()->GetPos() ) ); if ( slicedWorldGeometry.IsNotNull() ) { break; } } else { const SlicedGeometry3D *worldSlicedGeometry = dynamic_cast< const SlicedGeometry3D * >( m_InputWorldGeometry.GetPointer()); if ( worldSlicedGeometry != NULL ) { slicedWorldGeometry = static_cast< SlicedGeometry3D * >( m_InputWorldGeometry->Clone().GetPointer()); break; } } //else: use Transversal: no "break" here!! case Transversal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( m_InputWorldGeometry, PlaneGeometry::Transversal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( m_InputWorldGeometry, PlaneGeometry::Frontal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes( m_InputWorldGeometry, PlaneGeometry::Sagittal, top, frontside, rotated ); slicedWorldGeometry->SetSliceNavigationController( this ); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos( 0 ); m_Slice->SetSteps( (int)slicedWorldGeometry->GetSlices() ); if ( m_CreatedWorldGeometry.IsNull() ) { // initialize TimeSlicedGeometry m_CreatedWorldGeometry = TimeSlicedGeometry::New(); } if ( worldTimeSlicedGeometry == NULL ) { m_CreatedWorldGeometry->InitializeEvenlyTimed( slicedWorldGeometry, 1 ); m_Time->SetSteps( 0 ); m_Time->SetPos( 0 ); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps( worldTimeSlicedGeometry->GetTimeSteps() ); m_Time->SetPos( 0 ); const TimeBounds &timeBounds = worldTimeSlicedGeometry->GetTimeBounds(); m_Time->SetRange( timeBounds[0], timeBounds[1] ); m_BlockUpdate = false; assert( worldTimeSlicedGeometry->GetGeometry3D( this->GetTime()->GetPos() ) != NULL ); slicedWorldGeometry->SetTimeBounds( worldTimeSlicedGeometry->GetGeometry3D( this->GetTime()->GetPos() )->GetTimeBounds() ); //@todo implement for non-evenly-timed geometry! m_CreatedWorldGeometry->InitializeEvenlyTimed( slicedWorldGeometry, worldTimeSlicedGeometry->GetTimeSteps() ); } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if ( !m_BlockUpdate ) { this->InvokeEvent( GeometrySendEvent(m_CreatedWorldGeometry, 0) ); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if ( !m_BlockUpdate ) { this->InvokeEvent( GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); } } void SliceNavigationController::SendSlice() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos()) ); // send crosshair event crosshairPositionEvent.Send(); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if ( !m_BlockUpdate ) { if ( m_CreatedWorldGeometry.IsNotNull() ) { this->InvokeEvent( GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos()) ); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry( const itk::EventObject & ) { } void SliceNavigationController ::SetGeometryTime( const itk::EventObject &geometryTimeEvent ) { const SliceNavigationController::GeometryTimeEvent *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >( &geometryTimeEvent); assert( timeEvent != NULL ); TimeSlicedGeometry *timeSlicedGeometry = timeEvent->GetTimeSlicedGeometry(); assert( timeSlicedGeometry != NULL ); if ( m_CreatedWorldGeometry.IsNotNull() ) { int timeStep = (int) timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeSlicedGeometry->TimeStepToMS( timeStep ); timeStep = m_CreatedWorldGeometry->MSToTimeStep( timeInMS ); this->GetTime()->SetPos( timeStep ); } } void SliceNavigationController ::SetGeometrySlice(const itk::EventObject & geometrySliceEvent) { const SliceNavigationController::GeometrySliceEvent* sliceEvent = dynamic_cast( &geometrySliceEvent); assert(sliceEvent!=NULL); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint( const Point3D &point ) { //@todo add time to PositionEvent and use here!! SlicedGeometry3D* slicedWorldGeometry = dynamic_cast< SlicedGeometry3D * >( m_CreatedWorldGeometry->GetGeometry3D( this->GetTime()->GetPos() ) ); if ( slicedWorldGeometry ) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if ( slicedWorldGeometry->GetEvenlySpaced() ) { mitk::Geometry2D *plane = slicedWorldGeometry->GetGeometry2D( 0 ); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project( point, projectedPoint ); // Check whether the point is somewhere within the slice stack volume; // otherwise, the defualt slice (0) will be selected if ( direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0 ) { bestSlice = (int)(plane->Distance( point ) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for ( s = 0; s < slices; ++s ) { slicedWorldGeometry->GetGeometry2D( s )->Project( point, projectedPoint ); Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if ( currentDistance < bestDistance ) { bestDistance = currentDistance; bestSlice = s; } } } if ( bestSlice >= 0 ) { this->GetSlice()->SetPos( bestSlice ); } else { this->GetSlice()->SetPos( 0 ); } this->SendCreatedWorldGeometryUpdate(); } } void SliceNavigationController::ReorientSlices( const Point3D &point, const Vector3D &normal ) { PlaneOperation op( OpORIENT, point, normal ); m_CreatedWorldGeometry->ExecuteOperation( &op ); this->SendCreatedWorldGeometryUpdate(); } const mitk::TimeSlicedGeometry * SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::Geometry3D * SliceNavigationController::GetCurrentGeometry3D() { if ( m_CreatedWorldGeometry.IsNotNull() ) { return m_CreatedWorldGeometry->GetGeometry3D( this->GetTime()->GetPos() ); } else { return NULL; } } const mitk::PlaneGeometry * SliceNavigationController::GetCurrentPlaneGeometry() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); if ( slicedGeometry ) { const mitk::PlaneGeometry *planeGeometry = dynamic_cast< mitk::PlaneGeometry * > ( slicedGeometry->GetGeometry2D(this->GetSlice()->GetPos()) ); return planeGeometry; } else { return NULL; } } void SliceNavigationController::SetRenderer( BaseRenderer *renderer ) { m_Renderer = renderer; } BaseRenderer * SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { const mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast< const mitk::SlicedGeometry3D * > ( this->GetCurrentGeometry3D() ); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for ( i = 0; i < 3; ++i ) { if ( fabs( (float) direction[i] ) < 0.000000001 ) { ++c; } else { k = i; } } if ( c == 2 ) { ScalarType min = m_InputWorldGeometry->GetOrigin()[k]; ScalarType max = min + m_InputWorldGeometry->GetExtentInMM( k ); m_Slice->SetRange( min, max ); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation( Operation *operation ) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if ( !operation ) { return; } switch ( operation->GetOperationType() ) { case OpMOVE: // should be a point operation { if ( !m_SliceLocked ) //do not move the cross position { // select a slice PointOperation *po = dynamic_cast< PointOperation * >( operation ); if ( po && po->GetIndex() == -1 ) { this->SelectSliceByPoint( po->GetPoint() ); } else if ( po && po->GetIndex() != -1 ) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos( po->GetIndex() ); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation( operation ); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } // Relict from the old times, when automous decisions were accepted // behavior. Remains in here, because some RenderWindows do exist outside // of StdMultiWidgets. bool SliceNavigationController ::ExecuteAction( Action* action, StateEvent const* stateEvent ) { bool ok = false; const PositionEvent* posEvent = dynamic_cast< const PositionEvent * >( stateEvent->GetEvent() ); if ( posEvent != NULL ) { if ( m_CreatedWorldGeometry.IsNull() ) { return true; } switch (action->GetActionId()) { case AcMOVE: { BaseRenderer *baseRenderer = posEvent->GetSender(); if ( !baseRenderer ) { baseRenderer = const_cast( GlobalInteraction::GetInstance()->GetFocus() ); } if ( baseRenderer ) if ( baseRenderer->GetMapperID() == 1 ) { PointOperation* doOp = new mitk::PointOperation(OpMOVE, posEvent->GetWorldPosition()); - if (m_UndoEnabled) - { - m_OldPos = this->GetSlice()->GetPos(); - // m_OldPos holds the old slice position. For the undo controller this old position will be stored as index in mitk::PointOperation - PointOperation* undoOp = new mitk::PointOperation(OpMOVE, posEvent->GetWorldPosition(), m_OldPos); - OperationEvent *operationEvent = new mitk::OperationEvent(this, doOp, undoOp, "Move slices"); - m_UndoController->SetOperationEvent(operationEvent); - } this->ExecuteOperation( doOp ); // If click was performed in this render window than we have to update the status bar information about position and pixel value. if(baseRenderer == m_Renderer) { { std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); mitk::Point3D worldposition = posEvent->GetWorldPosition(); int maxlayer = -32768; mitk::Image::Pointer image3D; // find image with largest layer, that is the image shown on top in the render window for (unsigned int x = 0; x < nodes->size(); x++) { //Just consider image data that is no helper object. E.g. do not consider nodes created for the slice interpolation bool isHelper (false); 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(m_Renderer)) { image3D = dynamic_cast(nodes->at(x)->GetData()); maxlayer = layer; } } } } 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()) { Index3D p; image3D->GetGeometry()->WorldToIndex(worldposition, p); stream.precision(2); stream<<"Position: <" << std::fixed < mm"; stream<<"; Index: <"< "; mitk::ScalarType pixelValue = image3D->GetPixelValueByIndex(p, baseRenderer->GetTimeStep()); if (fabs(pixelValue)>1000000) { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<GetPixelValueByIndex(p, baseRenderer->GetTimeStep())<<" "; } else { stream<<"; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: "<GetPixelValueByIndex(p, baseRenderer->GetTimeStep())<<" "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } } ok = true; break; } } default: ok = true; break; } return ok; } const DisplayPositionEvent *displPosEvent = dynamic_cast< const DisplayPositionEvent * >( stateEvent->GetEvent() ); if ( displPosEvent != NULL ) { return true; } return false; } } // namespace diff --git a/Core/Code/CppMicroServices/src/service/usServiceRegistration.cpp b/Core/Code/CppMicroServices/src/service/usServiceRegistration.cpp index 3a10639437..c945abe64d 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceRegistration.cpp +++ b/Core/Code/CppMicroServices/src/service/usServiceRegistration.cpp @@ -1,232 +1,244 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT #include US_BASECLASS_HEADER #endif #include "usServiceRegistration.h" #include "usServiceRegistrationPrivate.h" #include "usServiceListenerEntry_p.h" #include "usServiceRegistry_p.h" #include "usServiceFactory.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" #include US_BEGIN_NAMESPACE typedef ServiceRegistrationPrivate::MutexLocker MutexLocker; ServiceRegistration::ServiceRegistration() : d(0) { } ServiceRegistration::ServiceRegistration(const ServiceRegistration& reg) : d(reg.d) { if (d) d->ref.Ref(); } ServiceRegistration::ServiceRegistration(ServiceRegistrationPrivate* registrationPrivate) : d(registrationPrivate) { if (d) d->ref.Ref(); } ServiceRegistration::ServiceRegistration(ModulePrivate* module, US_BASECLASS_NAME* service, const ServiceProperties& props) : d(new ServiceRegistrationPrivate(module, service, props)) { } ServiceRegistration::operator bool() const { return d != 0; } ServiceRegistration& ServiceRegistration::operator=(int null) { if (null == 0) { if (d && !d->ref.Deref()) { delete d; } d = 0; } return *this; } ServiceRegistration::~ServiceRegistration() { if (d && !d->ref.Deref()) delete d; } ServiceReference ServiceRegistration::GetReference() const { if (!d) throw std::logic_error("ServiceRegistration object invalid"); if (!d->available) throw std::logic_error("Service is unregistered"); return d->reference; } void ServiceRegistration::SetProperties(const ServiceProperties& props) { if (!d) throw std::logic_error("ServiceRegistration object invalid"); MutexLocker lock(d->eventLock); ServiceListeners::ServiceListenerEntries before; // TBD, optimize the locking of services { //MutexLocker lock2(d->module->coreCtx->globalFwLock); - MutexLocker lock3(d->propsLock); if (d->available) { // NYI! Optimize the MODIFIED_ENDMATCH code - int old_rank = any_cast(d->properties[ServiceConstants::SERVICE_RANKING()]); - d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, before, false); - const std::list& classes = ref_any_cast >(d->properties[ServiceConstants::OBJECTCLASS()]); - long int sid = any_cast(d->properties[ServiceConstants::SERVICE_ID()]); - d->properties = ServiceRegistry::CreateServiceProperties(props, classes, sid); - int new_rank = any_cast(d->properties[ServiceConstants::SERVICE_RANKING()]); + int old_rank = 0; + int new_rank = 0; + + std::list classes; + { + MutexLocker lock3(d->propsLock); + + Any any = d->properties[ServiceConstants::SERVICE_RANKING()]; + if (any.Type() == typeid(int)) old_rank = any_cast(any); + + d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, before, false); + classes = ref_any_cast >(d->properties[ServiceConstants::OBJECTCLASS()]); + long int sid = any_cast(d->properties[ServiceConstants::SERVICE_ID()]); + d->properties = ServiceRegistry::CreateServiceProperties(props, classes, sid); + + any = d->properties[ServiceConstants::SERVICE_RANKING()]; + if (any.Type() == typeid(int)) new_rank = any_cast(any); + } + if (old_rank != new_rank) { d->module->coreCtx->services.UpdateServiceRegistrationOrder(*this, classes); } } else { throw std::logic_error("Service is unregistered"); } } ServiceListeners::ServiceListenerEntries matchingListeners; d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, matchingListeners); d->module->coreCtx->listeners.ServiceChanged(matchingListeners, ServiceEvent(ServiceEvent::MODIFIED, d->reference), before); d->module->coreCtx->listeners.ServiceChanged(before, ServiceEvent(ServiceEvent::MODIFIED_ENDMATCH, d->reference)); } void ServiceRegistration::Unregister() { if (!d) throw std::logic_error("ServiceRegistration object invalid"); if (d->unregistering) return; // Silently ignore redundant unregistration. { MutexLocker lock(d->eventLock); if (d->unregistering) return; d->unregistering = true; if (d->available) { if (d->module) { d->module->coreCtx->services.RemoveServiceRegistration(*this); } } else { throw std::logic_error("Service is unregistered"); } } if (d->module) { ServiceListeners::ServiceListenerEntries listeners; d->module->coreCtx->listeners.GetMatchingServiceListeners(d->reference, listeners); d->module->coreCtx->listeners.ServiceChanged( listeners, ServiceEvent(ServiceEvent::UNREGISTERING, d->reference)); } { MutexLocker lock(d->eventLock); { MutexLocker lock2(d->propsLock); d->available = false; #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT if (d->module) { ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator end = d->serviceInstances.end(); for (ServiceRegistrationPrivate::ModuleToServicesMap::const_iterator i = d->serviceInstances.begin(); i != end; ++i) { US_BASECLASS_NAME* obj = i->second; try { // NYI, don't call inside lock dynamic_cast(d->service)->UngetService(i->first, *this, obj); } catch (const std::exception& /*ue*/) { US_WARN << "ServiceFactory UngetService implementation threw an exception"; } } } #endif d->module = 0; d->dependents.clear(); d->service = 0; d->serviceInstances.clear(); d->reference = 0; d->unregistering = false; } } } bool ServiceRegistration::operator<(const ServiceRegistration& o) const { if (!d) return true; return d->reference <(o.d->reference); } bool ServiceRegistration::operator==(const ServiceRegistration& registration) const { return d == registration.d; } ServiceRegistration& ServiceRegistration::operator=(const ServiceRegistration& registration) { ServiceRegistrationPrivate* curr_d = d; d = registration.d; if (d) d->ref.Ref(); if (curr_d && !curr_d->ref.Deref()) delete curr_d; return *this; } US_END_NAMESPACE diff --git a/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp b/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp index 6ae3263cbe..5a49cdc7dd 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp +++ b/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp @@ -1,335 +1,327 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT #include US_BASECLASS_HEADER #endif #include "usServiceRegistry_p.h" #include "usServiceFactory.h" #include "usServiceRegistry_p.h" #include "usServiceRegistrationPrivate.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" US_BEGIN_NAMESPACE typedef MutexLock MutexLocker; -struct ServiceRegistrationComparator -{ - bool operator()(const ServiceRegistration& a, const ServiceRegistration& b) const - { - return a < b; - } -}; - ServiceProperties ServiceRegistry::CreateServiceProperties(const ServiceProperties& in, const std::list& classes, long sid) { static long nextServiceID = 1; ServiceProperties props(in); if (!classes.empty()) { props.insert(std::make_pair(ServiceConstants::OBJECTCLASS(), classes)); } props.insert(std::make_pair(ServiceConstants::SERVICE_ID(), sid != -1 ? sid : nextServiceID++)); return props; } ServiceRegistry::ServiceRegistry(CoreModuleContext* coreCtx) : core(coreCtx) { } ServiceRegistry::~ServiceRegistry() { Clear(); } void ServiceRegistry::Clear() { services.clear(); serviceRegistrations.clear(); classServices.clear(); core = 0; } ServiceRegistration ServiceRegistry::RegisterService(ModulePrivate* module, const std::list& classes, US_BASECLASS_NAME* service, const ServiceProperties& properties) { if (service == 0) { throw std::invalid_argument("Can't register 0 as a service"); } // Check if service implements claimed classes and that they exist. for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { if (i->empty()) { throw std::invalid_argument("Can't register as null class"); } #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT if (!(dynamic_cast(service))) #endif { if (!CheckServiceClass(service, *i)) { std::string msg; std::stringstream ss(msg); ss << "Service class " << us_service_impl_name(service) << " is not an instance of " << (*i) << ". Maybe you forgot to export the RTTI information for the interface."; throw std::invalid_argument(msg); } } } ServiceRegistration res(module, service, CreateServiceProperties(properties, classes)); { MutexLocker lock(mutex); services.insert(std::make_pair(res, classes)); serviceRegistrations.push_back(res); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; std::list::iterator ip = - std::lower_bound(s.begin(), s.end(), res, ServiceRegistrationComparator()); + std::lower_bound(s.begin(), s.end(), res); s.insert(ip, res); } } ServiceReference r = res.GetReference(); ServiceListeners::ServiceListenerEntries listeners; module->coreCtx->listeners.GetMatchingServiceListeners(r, listeners); module->coreCtx->listeners.ServiceChanged(listeners, ServiceEvent(ServiceEvent::REGISTERED, r)); return res; } void ServiceRegistry::UpdateServiceRegistrationOrder(const ServiceRegistration& sr, const std::list& classes) { MutexLocker lock(mutex); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; - std::remove(s.begin(), s.end(), sr); - s.insert(std::lower_bound(s.begin(), s.end(), sr, ServiceRegistrationComparator()), sr); + s.erase(std::remove(s.begin(), s.end(), sr), s.end()); + s.insert(std::lower_bound(s.begin(), s.end(), sr), sr); } } bool ServiceRegistry::CheckServiceClass(US_BASECLASS_NAME* , const std::string& ) const { //return service->inherits(cls.toAscii()); // No possibility to check inheritance based on string literals. return true; } void ServiceRegistry::Get(const std::string& clazz, std::list& serviceRegs) const { MutexLocker lock(mutex); MapClassServices::const_iterator i = classServices.find(clazz); if (i != classServices.end()) { serviceRegs = i->second; } } ServiceReference ServiceRegistry::Get(ModulePrivate* module, const std::string& clazz) const { MutexLocker lock(mutex); try { std::list srs; Get_unlocked(clazz, "", module, srs); US_DEBUG << "get service ref " << clazz << " for module " << module->info.name << " = " << srs.size() << " refs"; if (!srs.empty()) { - return srs.front(); + return srs.back(); } } catch (const std::invalid_argument& ) { } return ServiceReference(); } void ServiceRegistry::Get(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::list& res) const { MutexLocker lock(mutex); Get_unlocked(clazz, filter, module, res); } void ServiceRegistry::Get_unlocked(const std::string& clazz, const std::string& filter, ModulePrivate* /*module*/, std::list& res) const { std::list::const_iterator s; std::list::const_iterator send; std::list v; LDAPExpr ldap; if (clazz.empty()) { if (!filter.empty()) { ldap = LDAPExpr(filter); LDAPExpr::ObjectClassSet matched; if (ldap.GetMatchedObjectClasses(matched)) { v.clear(); for(LDAPExpr::ObjectClassSet::const_iterator className = matched.begin(); className != matched.end(); ++className) { MapClassServices::const_iterator i = classServices.find(*className); if (i != classServices.end()) { std::copy(i->second.begin(), i->second.end(), std::back_inserter(v)); } } if (!v.empty()) { s = v.begin(); send = v.end(); } else { return; } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { MapClassServices::const_iterator it = classServices.find(clazz); if (it != classServices.end()) { s = it->second.begin(); send = it->second.end(); } else { return; } if (!filter.empty()) { ldap = LDAPExpr(filter); } } for (; s != send; ++s) { ServiceReference sri = s->GetReference(); if (filter.empty() || ldap.Evaluate(s->d->properties, false)) { res.push_back(sri); } } } void ServiceRegistry::RemoveServiceRegistration(const ServiceRegistration& sr) { MutexLocker lock(mutex); const std::list& classes = ref_any_cast >( sr.d->properties[ServiceConstants::OBJECTCLASS()]); services.erase(sr); serviceRegistrations.remove(sr); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; if (s.size() > 1) { - std::remove(s.begin(), s.end(), sr); + s.erase(std::remove(s.begin(), s.end(), sr), s.end()); } else { classServices.erase(*i); } } } void ServiceRegistry::GetRegisteredByModule(ModulePrivate* p, std::list& res) const { MutexLocker lock(mutex); for (std::list::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->module == p) { res.push_back(*i); } } } void ServiceRegistry::GetUsedByModule(Module* p, std::list& res) const { MutexLocker lock(mutex); for (std::list::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->IsUsedByModule(p)) { res.push_back(*i); } } } US_END_NAMESPACE diff --git a/Core/Code/CppMicroServices/src/util/usUtils.cpp b/Core/Code/CppMicroServices/src/util/usUtils.cpp index ffd8b2e38d..01b2aa1322 100644 --- a/Core/Code/CppMicroServices/src/util/usUtils.cpp +++ b/Core/Code/CppMicroServices/src/util/usUtils.cpp @@ -1,108 +1,108 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usUtils_p.h" #include #ifdef US_PLATFORM_POSIX #include #include #else #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #endif US_BEGIN_NAMESPACE 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 } static MsgHandler handler = 0; MsgHandler installMsgHandler(MsgHandler h) { MsgHandler old = handler; handler = h; return old; } void message_output(MsgType msgType, const char *buf) { if (handler) { (*handler)(msgType, buf); } else { fprintf(stderr, "%s\n", buf); fflush(stderr); } if (msgType == ErrorMsg) { - #if defined(_MSC_VER) && !defined(NDEBUG) && defined(_CRT_ERROR) + #if defined(_MSC_VER) && !defined(NDEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) // get the current report mode int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_ERROR, reportMode); int ret = _CrtDbgReport(_CRT_ERROR, __FILE__, __LINE__, CppMicroServices_VERSION_STR, buf); if (ret == 0 && reportMode & _CRTDBG_MODE_WNDW) return; // ignore else if (ret == 1) _CrtDbgBreak(); #endif #ifdef US_PLATFORM_POSIX abort(); // trap; generates core dump #else exit(1); // goodbye cruel world #endif } } US_END_NAMESPACE diff --git a/Core/Code/CppMicroServices/test/CMakeLists.txt b/Core/Code/CppMicroServices/test/CMakeLists.txt index 27d10d085e..2c26a08f99 100644 --- a/Core/Code/CppMicroServices/test/CMakeLists.txt +++ b/Core/Code/CppMicroServices/test/CMakeLists.txt @@ -1,81 +1,82 @@ #----------------------------------------------------------------------------- # Configure files, include dirs, etc. #----------------------------------------------------------------------------- configure_file("${CMAKE_CURRENT_SOURCE_DIR}/usTestingConfig.h.in" "${PROJECT_BINARY_DIR}/include/usTestingConfig.h") include_directories(${US_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}) if(NOT US_ENABLE_SERVICE_FACTORY_SUPPORT) include_directories(${US_BASECLASS_INCLUDE_DIRS}) endif() link_directories(${US_LINK_DIRS}) if(NOT US_ENABLE_SERVICE_FACTORY_SUPPORT) link_directories(${US_BASECLASS_LIBRARY_DIRS}) endif() #----------------------------------------------------------------------------- # Create test modules #----------------------------------------------------------------------------- include(usFunctionCreateTestModule) set(_us_test_module_libs "" CACHE INTERNAL "" FORCE) add_subdirectory(modules) #----------------------------------------------------------------------------- # Add unit tests #----------------------------------------------------------------------------- set(_tests usDebugOutputTest usLDAPFilterTest usModuleTest usServiceListenerTest + usServiceRegistryTest usServiceTrackerTest ) set(_test_driver ${PROJECT_NAME}TestDriver) create_test_sourcelist(_srcs ${_test_driver}.cpp ${_tests}) # Generate a custom "module init" file for the test driver executable set(MODULE_DEPENDS_STR ) foreach(_dep ${US_LINK_LIBRARIES}) set(MODULE_DEPENDS_STR "${MODULE_DEPENDS_STR} ${_dep}") endforeach() usFunctionGenerateModuleInit(_srcs NAME ${_test_driver} DEPENDS "${MODULE_DEPENDS_STR}" VERSION "0.1.0" EXECUTABLE ) add_executable(${_test_driver} ${_srcs} usTestManager.cpp) if(NOT US_BUILD_SHARED_LIBS) target_link_libraries(${_test_driver} ${_us_test_module_libs}) endif() target_link_libraries(${_test_driver} ${US_LINK_LIBRARIES}) if(NOT US_ENABLE_SERVICE_FACTORY_SUPPORT) target_link_libraries(${_test_driver} ${US_BASECLASS_LIBRARIES}) endif() # Register tests foreach(_test ${_tests}) add_test(NAME ${_test} COMMAND ${_test_driver} ${_test}) endforeach() if(US_TEST_LABELS) set_tests_properties(${_tests} PROPERTIES LABELS "${US_TEST_LABELS}") endif() #----------------------------------------------------------------------------- # Add dependencies for shared libraries #----------------------------------------------------------------------------- if(US_BUILD_SHARED_LIBS) foreach(_test_module ${_us_test_module_libs}) add_dependencies(${_test_driver} ${_test_module}) endforeach() endif() diff --git a/Core/Code/CppMicroServices/test/usServiceRegistryTest.cpp b/Core/Code/CppMicroServices/test/usServiceRegistryTest.cpp new file mode 100644 index 0000000000..a2cbdf3507 --- /dev/null +++ b/Core/Code/CppMicroServices/test/usServiceRegistryTest.cpp @@ -0,0 +1,138 @@ +/*============================================================================= + + Library: CppMicroServices + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=============================================================================*/ + +#include + +#include "usTestingMacros.h" +#include +#include +#include + +#include US_BASECLASS_HEADER + +#include + +US_USE_NAMESPACE + +struct ITestServiceA +{ + virtual ~ITestServiceA() {} +}; + +US_DECLARE_SERVICE_INTERFACE(ITestServiceA, "org.cppmicroservices.testing.ITestServiceA") + +int TestMultipleServiceRegistrations() +{ + struct TestServiceA : public US_BASECLASS_NAME, public ITestServiceA + { + }; + + ModuleContext* context = GetModuleContext(); + + TestServiceA s1; + TestServiceA s2; + + ServiceRegistration reg1 = context->RegisterService(&s1); + ServiceRegistration reg2 = context->RegisterService(&s2); + + std::list refs = context->GetServiceReferences(); + US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Testing for two registered ITestServiceA services") + + reg2.Unregister(); + refs = context->GetServiceReferences(); + US_TEST_CONDITION_REQUIRED(refs.size() == 1, "Testing for one registered ITestServiceA services") + + reg1.Unregister(); + refs = context->GetServiceReferences(); + US_TEST_CONDITION_REQUIRED(refs.empty(), "Testing for no ITestServiceA services") + + return EXIT_SUCCESS; +} + +int TestServicePropertiesUpdate() +{ + struct TestServiceA : public US_BASECLASS_NAME, public ITestServiceA + { + }; + + ModuleContext* context = GetModuleContext(); + + TestServiceA s1; + ServiceProperties props; + props["string"] = std::string("A std::string"); + props["bool"] = false; + const char* str = "A const char*"; + props["const char*"] = str; + + ServiceRegistration reg1 = context->RegisterService(&s1, props); + ServiceReference ref1 = context->GetServiceReference(); + + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences().size() == 1, "Testing service count") + US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty("bool")) == false, "Testing bool property") + + // register second service with higher rank + TestServiceA s2; + ServiceProperties props2; + props2[ServiceConstants::SERVICE_RANKING()] = 50; + + ServiceRegistration reg2 = context->RegisterService(&s2, props2); + + // Get the service with the highest rank, this should be s2. + ServiceReference ref2 = context->GetServiceReference(); + TestServiceA* service = dynamic_cast(context->GetService(ref2)); + US_TEST_CONDITION_REQUIRED(service == &s2, "Testing highest service rank") + + props["bool"] = true; + // change the service ranking + props[ServiceConstants::SERVICE_RANKING()] = 100; + reg1.SetProperties(props); + + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences().size() == 2, "Testing service count") + US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty("bool")) == true, "Testing bool property") + US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty(ServiceConstants::SERVICE_RANKING())) == 100, "Testing updated ranking") + + // Service with the highest ranking should now be s1 + service = dynamic_cast(context->GetService(ref1)); + US_TEST_CONDITION_REQUIRED(service == &s1, "Testing highest service rank") + + reg1.Unregister(); + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences("").size() == 1, "Testing service count") + + service = dynamic_cast(context->GetService(ref2)); + US_TEST_CONDITION_REQUIRED(service == &s2, "Testing highest service rank") + + reg2.Unregister(); + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences().empty(), "Testing service count") + + return EXIT_SUCCESS; +} + + +int usServiceRegistryTest(int /*argc*/, char* /*argv*/[]) +{ + US_TEST_BEGIN("ServiceRegistryTest"); + + US_TEST_CONDITION(TestMultipleServiceRegistrations() == EXIT_SUCCESS, "Testing service registrations: ") + US_TEST_CONDITION(TestServicePropertiesUpdate() == EXIT_SUCCESS, "Testing service property update: ") + + US_TEST_END() +} + diff --git a/Core/Code/DataManagement/mitkImage.cpp b/Core/Code/DataManagement/mitkImage.cpp index 397e027b78..bb3cf16547 100644 --- a/Core/Code/DataManagement/mitkImage.cpp +++ b/Core/Code/DataManagement/mitkImage.cpp @@ -1,1280 +1,1278 @@ /*=================================================================== 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 "mitkImage.h" #include "mitkImageStatisticsHolder.h" #include "mitkPixelTypeMultiplex.h" #include #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(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { 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(NULL), m_ImageDescriptor(NULL), m_OffsetTable(NULL), m_CompleteData(NULL), m_ImageStatistics(NULL) { 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 this->SetGeometry(dynamic_cast(other.GetGeometry()->Clone().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() { Clear(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 3; m_ReferenceCountLock.Unlock(); m_ReferenceCountLock.Lock(); m_ReferenceCount = 0; m_ReferenceCountLock.Unlock(); if(m_OffsetTable != NULL) delete [] m_OffsetTable; if(m_ImageStatistics != NULL) 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 NULL; 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 == NULL ) return; if(ptype.GetBpe() != 24) { value = (double) (((T*) data)[ offset ]); } else { const unsigned int rgboffset = 3 * offset; double returnvalue = (((T*) data)[rgboffset ]); returnvalue += (((T*) data)[rgboffset + 1]); returnvalue += (((T*) data)[rgboffset + 2]); value = returnvalue; } } double mitk::Image::GetPixelValueByIndex(const mitk::Index3D &position, unsigned int timestep) { 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) - // && - if ( (unsigned int)position[0] < imageDims[0] && (unsigned int)position[1] < imageDims[1] && - ( (imageDims[2] == 0) || (unsigned int)position[2] < imageDims[2]) // in case a 2D Image passed in, the third dimension could be set to 0 causing the if() to fail - /*&& (unsigned int)timestep < imageDims[3]*/ ) + // 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 = 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) { double value = 0.0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } Index3D itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); - const unsigned int* imageDims = this->m_ImageDescriptor->GetDimensions(); - const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); - - //if ( (itkIndex[0]>=0 && itkIndex[1] >=0 && itkIndex[2]>=0 && timestep>=0) - // && - // lines above taken from comparison since always true due to unsigned type - if (((unsigned int)itkIndex[0] < imageDims[0] && - (unsigned int)itkIndex[1] < imageDims[1] && - (imageDims[2] == 0) ) || ((unsigned int)itkIndex[2] < imageDims[2])) // in case a 2D Image passed in, the third dimension could be set to 0 causing the if() to fail - { - const unsigned int offset = itkIndex[0] + itkIndex[1]*imageDims[0] + itkIndex[2]*imageDims[0]*imageDims[1] + timestep*imageDims[0]*imageDims[1]*imageDims[2]; - - mitkPixelTypeMultiplex3( AccessPixel, ptype, this->GetData(), offset, value ); - } + value = this->GetPixelValueByIndex( itkIndex, timestep); return value; } vtkImageData* mitk::Image::GetVtkImageData(int t, int n) { if(m_Initialized==false) { if(GetSource().IsNull()) return NULL; if(GetSource()->Updating()==false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume=GetVolumeData(t, n); if(volume.GetPointer()==NULL || volume->GetVtkImageData() == NULL) return NULL; float *fspacing = const_cast(GetSlicedGeometry(t)->GetFloatSpacing()); double dspacing[3] = {fspacing[0],fspacing[1],fspacing[2]}; volume->GetVtkImageData()->SetSpacing( dspacing ); return volume->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData(int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return NULL; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos=GetSliceIndex(s,t,n); if(m_Slices[pos].GetPointer()!=NULL) return m_Slices[pos]; // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 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()!=NULL) && (ch->IsComplete())) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 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(s,t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData(s,t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateSliceData(s,t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidVolume(t,n)==false) return NULL; ImageDataItemPointer ch, vol; // volume directly available? int pos=GetVolumeIndex(t,n); vol=m_Volumes[pos]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return vol; 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()!=NULL) && (ch->IsComplete())) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 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()==NULL) vol=new ImageDataItem( chPixelType, 3, m_Dimensions, NULL, 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, 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(t,n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData(t,n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateVolumeData(t,n,data,importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { if(IsValidChannel(n)==false) return NULL; ImageDataItemPointer ch, vol; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if(IsChannelSet(n)) { // if there is only one time frame we do not need to combine anything if(m_Dimensions[3]<=1) { vol=GetVolumeData(0,n,data,importMemoryManagement); ch=new ImageDataItem(*vol, m_ImageDescriptor, 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()==NULL) ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); ch->SetComplete(true); size_t size=m_OffsetTable[m_Dimension-1]*(ptypeSize); unsigned int t; ImageDataItemPointerArray::iterator slicesIt = m_Slices.begin()+n*m_Dimensions[2]*m_Dimensions[3]; for(t=0;tGetParent()!=ch) { // copy data of volume in channel size_t offset = ((size_t) t)*m_OffsetTable[3]*(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, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); //mitkIpFuncCopyTags(vol->GetPicDescriptor(), pic); m_Volumes[posVol]=vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull=NULL; for(unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } // 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(n)) //yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData(n,data,importMemoryManagement); else return NULL; } else { ImageDataItemPointer item = AllocateChannelData(n,data,importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { if(IsValidSlice(s,t,n)==false) return false; if(m_Slices[GetSliceIndex(s,t,n)].GetPointer()!=NULL) return true; ImageDataItemPointer ch, vol; vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { if(IsValidVolume(t,n)==false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol=m_Volumes[GetVolumeIndex(t,n)]; if((vol.GetPointer()!=NULL) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch=m_Channels[n]; if((ch.GetPointer()!=NULL) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for(s=0;sIsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for(t=0;t(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if(IsValidSlice(s,t,n)==false) return false; ImageDataItemPointer sl; 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()==NULL) 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()==NULL) 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()==NULL) 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()==NULL) 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()==NULL) 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()==NULL) 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)=NULL; } for( it=m_Volumes.begin(), end=m_Volumes.end(); it!=end; ++it ) { (*it)=NULL; } for( it=m_Channels.begin(), end=m_Channels.end(); it!=end; ++it ) { (*it)=NULL; } m_CompleteData = NULL; if( m_ImageStatistics == NULL) { 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 = NULL; 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]); if(dimension>=4) { TimeBounds timebounds; timebounds[0] = 0.0; timebounds[1] = 1.0; slicedGeometry->SetTimeBounds(timebounds); } TimeSlicedGeometry::Pointer timeSliceGeometry = TimeSlicedGeometry::New(); timeSliceGeometry->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); timeSliceGeometry->ImageGeometryOn(); SetGeometry(timeSliceGeometry); ImageDataItemPointer dnull=NULL; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels()*m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels()*m_Dimensions[3]*m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int channels, int tDim ) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetExtent(0)+0.5); dimensions[1] = (unsigned int)(geometry.GetExtent(1)+0.5); dimensions[2] = (unsigned int)(geometry.GetExtent(2)+0.5); dimensions[3] = 0; dimensions[4] = 0; unsigned int dimension = 2; if ( dimensions[2] > 1 ) dimension = 3; if ( tDim > 0) { dimensions[3] = tDim; } else { const mitk::TimeSlicedGeometry* timeGeometry = dynamic_cast(&geometry); if ( timeGeometry != NULL ) { dimensions[3] = timeGeometry->GetTimeSteps(); } } if ( dimensions[3] > 1 ) dimension = 4; Initialize( type, dimension, dimensions, channels ); SetGeometry(static_cast(geometry.Clone().GetPointer())); mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBox()->GetBounds(); if( (bounds[0] != 0.0) || (bounds[2] != 0.0) || (bounds[4] != 0.0) ) { SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); mitk::Point3D origin; origin.Fill(0.0); slicedGeometry->IndexToWorld(origin, origin); bounds[1]-=bounds[0]; bounds[3]-=bounds[2]; bounds[5]-=bounds[4]; bounds[0] = 0.0; bounds[2] = 0.0; bounds[4] = 0.0; this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.Get_vnl_vector().data_block()); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); } } void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(static_cast(geometry2d.Clone().GetPointer()), sDim, flipped); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image* image) { Initialize(image->GetPixelType(), *image->GetTimeSlicedGeometry()); } void mitk::Image::Initialize(vtkImageData* vtkimagedata, int channels, int tDim, int sDim) { if(vtkimagedata==NULL) return; m_Dimension=vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions=new unsigned int[m_Dimension>4?m_Dimension:4]; for(i=0;iGetDimensions()[i]; if(m_Dimension<4) { unsigned int *p; for(i=0,p=tmpDimensions+m_Dimension;i<4-m_Dimension;++i, ++p) *p=1; } if(sDim>=0) { tmpDimensions[2]=sDim; if(m_Dimension < 3) m_Dimension = 3; } if(tDim>=0) { tmpDimensions[3]=tDim; if(m_Dimension < 4) m_Dimension = 4; } switch ( vtkimagedata->GetScalarType() ) { case VTK_BIT: case VTK_CHAR: //pixelType.Initialize(typeid(char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_CHAR: //pixelType.Initialize(typeid(unsigned char), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_SHORT: //pixelType.Initialize(typeid(short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_SHORT: //pixelType.Initialize(typeid(unsigned short), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_INT: //pixelType.Initialize(typeid(int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_INT: //pixelType.Initialize(typeid(unsigned int), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_LONG: //pixelType.Initialize(typeid(long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_UNSIGNED_LONG: //pixelType.Initialize(typeid(unsigned long), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_FLOAT: //pixelType.Initialize(typeid(float), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; case VTK_DOUBLE: //pixelType.Initialize(typeid(double), vtkimagedata->GetNumberOfScalarComponents()); Initialize(mitk::MakeScalarPixelType(), m_Dimension, tmpDimensions, channels); break; default: break; } /* 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; vtkFloatingPointType vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if(m_Dimension>=2) origin[1]=vtkorigin[1]; if(m_Dimension>=3) origin[2]=vtkorigin[2]; SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast(slicedGeometry->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, m_Dimensions[3]); delete [] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if(m_Initialized) return ((s>=0) && (s<(int)m_Dimensions[2]) && (t>=0) && (t< (int) m_Dimensions[3]) && (n>=0) && (n< (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if(m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if(m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if(m_OffsetTable!=NULL) delete [] m_OffsetTable; m_OffsetTable=new size_t[m_Dimension>4 ? m_Dimension+1 : 4+1]; unsigned int i; size_t num=1; m_OffsetTable[0] = 1; for (i=0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i+1] = num; } for (;i < 4; ++i) m_OffsetTable[i+1] = num; } 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) { 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()!=NULL) { sl=new ImageDataItem(*vol, m_ImageDescriptor, 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()!=NULL) { sl=new ImageDataItem(*ch, m_ImageDescriptor, 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(t,n,NULL,importMemoryManagement); sl=new ImageDataItem(*vol, m_ImageDescriptor, 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) { 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()!=NULL) { vol=new ImageDataItem(*ch, m_ImageDescriptor, 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, 3, m_Dimensions, NULL, true); if(data != NULL) std::memcpy(vol->GetData(), data, m_OffsetTable[3]*(ptypeSize)); } else { vol=new ImageDataItem( chPixelType, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos]=vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) { ImageDataItemPointer ch; // allocate new channel if(importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch=new ImageDataItem(this->m_ImageDescriptor, NULL, true); if(data != NULL) std::memcpy(ch->GetData(), data, m_OffsetTable[4]*(ptypeSize)); } else { ch=new ImageDataItem(this->m_ImageDescriptor, data, importMemoryManagement == ManageMemory); } m_Channels[n]=ch; return ch; } unsigned int* mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete [] m_Dimensions; m_Dimensions = NULL; } void mitk::Image::SetGeometry(Geometry3D* aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if(aGeometry3D->GetImageGeometry()==false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is pixel-center-based! If it is not, you need to call Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); GetTimeSlicedGeometry()->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream& os, itk::Indent indent) const { unsigned char i; if(m_Initialized) { 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.GetTypeId().name() << std::endl; os << indent << " BitsPerElement: " << chPixelType.GetSize() << 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::Geometry3D* geo = this->GetGeometry(); bool ret = false; if(geo) { const vnl_matrix_fixed & mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); float ref = 0; for(short k = 0; k < 3; ++k) ref += mx[k][k]; ref/=1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for(short i = 0; i < 3; ++i) { for(short j = 0; j < 3; ++j) { if(i != j) { if(std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } #include "mitkImageStatisticsHolder.h" //##Documentation mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } //##Documentation //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } //##Documentation //## \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); } diff --git a/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp b/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp index 64b16a3de0..909afd859a 100644 --- a/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp +++ b/Core/Code/Interactions/mitkDisplayVectorInteractor.cpp @@ -1,171 +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 "mitkDisplayVectorInteractor.h" #include "mitkOperation.h" #include "mitkDisplayCoordinateOperation.h" #include "mitkDisplayPositionEvent.h" #include "mitkUndoController.h" #include "mitkStateEvent.h" #include "mitkInteractionConst.h" #include "mitkAction.h" void mitk::DisplayVectorInteractor::ExecuteOperation(Operation* itkNotUsed( operation ) ) { /*DisplayCoordinateOperation* dcOperation = static_cast(operation); if(dcOperation==NULL) return; switch(operation->GetOperationType()) { case OpSELECTPOINT: m_Sender=dcOperation->GetRenderer(); m_StartDisplayCoordinate=dcOperation->GetStartDisplayCoordinate(); m_LastDisplayCoordinate=dcOperation->GetLastDisplayCoordinate(); m_CurrentDisplayCoordinate=dcOperation->GetCurrentDisplayCoordinate(); // MITK_INFO << m_CurrentDisplayCoordinate << std::endl; MITK_INFO<<"Message from DisplayVectorInteractor.cpp::ExecuteOperation() : " << "StartDisplayCoordinate:" << m_StartDisplayCoordinate << "LastDisplayCoordinate:" << m_LastDisplayCoordinate << "CurrentDisplayCoordinate:" << m_CurrentDisplayCoordinate << std::endl; break; }*/ } bool mitk::DisplayVectorInteractor::ExecuteAction(Action* action, mitk::StateEvent const* stateEvent) { bool ok=false; const DisplayPositionEvent* posEvent=dynamic_cast(stateEvent->GetEvent()); if(posEvent==NULL) return false; int actionId = action->GetActionId(); //initzoom and initmove is the same! if (actionId == AcINITZOOM) actionId = AcINITMOVE; switch(actionId) { //case 0: // { // DisplayCoordinateOperation* doOp = new mitk::DisplayCoordinateOperation(OpTEST, posEvent->GetSender(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition()); - // if (m_UndoEnabled) //write to UndoMechanism - // { - // DisplayCoordinateOperation* undoOp = new DisplayCoordinateOperation(OpTEST, m_Sender, m_StartDisplayCoordinate, m_LastDisplayCoordinate, m_CurrentDisplayCoordinate); - // - // - // OperationEvent *operationEvent = new OperationEvent(this, doOp, undoOp); - // m_UndoController->SetOperationEvent(operationEvent); - // } - // + // // //execute the Operation // m_Destination->ExecuteOperation(doOp); // ok = true; // break; // } case AcSENDCOORDINATES: { DisplayCoordinateOperation* doOp = new mitk::DisplayCoordinateOperation(OpSENDCOORDINATES, posEvent->GetSender(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition()); m_Destination->ExecuteOperation(doOp); ok = true; break; } case AcINITMOVE: { m_Sender=posEvent->GetSender(); mitk::Vector2D origin = m_Sender->GetDisplayGeometry()->GetOriginInMM(); double scaleFactorMMPerDisplayUnit = m_Sender->GetDisplayGeometry()->GetScaleFactorMMPerDisplayUnit(); m_StartDisplayCoordinate=posEvent->GetDisplayPosition(); m_LastDisplayCoordinate=posEvent->GetDisplayPosition(); m_CurrentDisplayCoordinate=posEvent->GetDisplayPosition(); m_StartCoordinateInMM=mitk::Point2D( ( origin+m_StartDisplayCoordinate.GetVectorFromOrigin()*scaleFactorMMPerDisplayUnit ).GetDataPointer() ); ok = true; break; } case AcMOVE: { DisplayCoordinateOperation* doOp = new DisplayCoordinateOperation(OpMOVE, m_Sender, m_StartDisplayCoordinate, m_CurrentDisplayCoordinate, posEvent->GetDisplayPosition()); //make Operation m_LastDisplayCoordinate=m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate=posEvent->GetDisplayPosition(); //execute the Operation m_Destination->ExecuteOperation(doOp); ok = true; break; } case AcFINISHMOVE: - { - if (m_UndoEnabled) //write to UndoMechanism - { - DisplayCoordinateOperation* doOp = new mitk::DisplayCoordinateOperation(OpMOVE, m_Sender, m_StartDisplayCoordinate, m_StartDisplayCoordinate, posEvent->GetDisplayPosition()); - DisplayCoordinateOperation* undoOp = new mitk::DisplayCoordinateOperation(OpMOVE, posEvent->GetSender(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition(), m_StartDisplayCoordinate); - - OperationEvent *operationEvent = new OperationEvent(m_Destination, doOp, undoOp, "Move view"); - m_UndoController->SetOperationEvent(operationEvent); - } + { ok = true; break; } case AcZOOM: { DisplayCoordinateOperation* doOp = new DisplayCoordinateOperation(OpZOOM, m_Sender, m_StartDisplayCoordinate, m_LastDisplayCoordinate, posEvent->GetDisplayPosition(),m_StartCoordinateInMM); - - if (m_UndoEnabled) //write to UndoMechanism - { - DisplayCoordinateOperation* undoOp = new mitk::DisplayCoordinateOperation(OpZOOM, posEvent->GetSender(), posEvent->GetDisplayPosition(), posEvent->GetDisplayPosition(), m_LastDisplayCoordinate); - - OperationEvent *operationEvent = new OperationEvent(m_Destination, doOp, undoOp, "Zoom view"); - m_UndoController->SetOperationEvent(operationEvent); - } + //make Operation m_LastDisplayCoordinate=m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate=posEvent->GetDisplayPosition(); //MITK_INFO << m_CurrentDisplayCoordinate << std::endl; //execute the Operation m_Destination->ExecuteOperation(doOp); ok = true; break; } default: ok = false; break; } return ok; } mitk::DisplayVectorInteractor::DisplayVectorInteractor(const char * type, mitk::OperationActor* destination) : mitk::StateMachine(type), m_Sender(NULL), m_Destination(destination) { m_StartDisplayCoordinate.Fill(0); m_LastDisplayCoordinate.Fill(0); m_CurrentDisplayCoordinate.Fill(0); if(m_Destination==NULL) m_Destination=this; } mitk::DisplayVectorInteractor::~DisplayVectorInteractor() { } diff --git a/Core/Code/Testing/CMakeLists.txt b/Core/Code/Testing/CMakeLists.txt index 2eb2a32c77..8f21bab6c0 100644 --- a/Core/Code/Testing/CMakeLists.txt +++ b/Core/Code/Testing/CMakeLists.txt @@ -1,63 +1,63 @@ MITK_CREATE_MODULE_TESTS(LABELS MITK-Core) # MITK_INSTALL_TARGETS(EXECUTABLES MitkTestDriver) mitkAddCustomModuleTest(mitkDICOMLocaleTest_spacingOk_CT mitkDICOMLocaleTest ${MITK_DATA_DIR}/spacing-ok-ct.dcm) mitkAddCustomModuleTest(mitkDICOMLocaleTest_spacingOk_MR mitkDICOMLocaleTest ${MITK_DATA_DIR}/spacing-ok-mr.dcm) mitkAddCustomModuleTest(mitkDICOMLocaleTest_spacingOk_SC mitkDICOMLocaleTest ${MITK_DATA_DIR}/spacing-ok-sc.dcm) mitkAddCustomModuleTest(mitkEventMapperTest_Test1And2 mitkEventMapperTest ${MITK_DATA_DIR}/TestStateMachine1.xml ${MITK_DATA_DIR}/TestStateMachine2.xml) #mitkAddCustomModuleTest(mitkNodeDependentPointSetInteractorTest mitkNodeDependentPointSetInteractorTest ${MITK_DATA_DIR}/Pic3D.pic.gz ${MITK_DATA_DIR}/BallBinary30x30x30.pic.gz) mitkAddCustomModuleTest(mitkNodeDependentPointSetInteractorTest mitkNodeDependentPointSetInteractorTest ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/BallBinary30x30x30.nrrd) mitkAddCustomModuleTest(mitkDataStorageTest_US4DCyl mitkDataStorageTest ${MITK_DATA_DIR}/US4DCyl.nrrd) mitkAddCustomModuleTest(mitkStateMachineFactoryTest_TestStateMachine1_2 mitkStateMachineFactoryTest ${MITK_DATA_DIR}/TestStateMachine1.xml ${MITK_DATA_DIR}/TestStateMachine2.xml) mitkAddCustomModuleTest(mitkDicomSeriesReaderTest_CTImage mitkDicomSeriesReaderTest ${MITK_DATA_DIR}/TinyCTAbdomen) mitkAddCustomModuleTest(mitkPointSetReaderTest mitkPointSetReaderTest ${MITK_DATA_DIR}/PointSetReaderTestData.mps) mitkAddCustomModuleTest(mitkImageTest_4DImageData mitkImageTest ${MITK_DATA_DIR}/US4DCyl.nrrd) mitkAddCustomModuleTest(mitkImageTest_2D+tImageData mitkImageTest ${MITK_DATA_DIR}/Pic2DplusT.nrrd) mitkAddCustomModuleTest(mitkImageTest_3DImageData mitkImageTest ${MITK_DATA_DIR}/Pic3D.nrrd) mitkAddCustomModuleTest(mitkImageTest_brainImage mitkImageTest ${MITK_DATA_DIR}/brain.mhd) #mitkAddCustomModuleTest(mitkImageTest_color2DImage mitkImageTest ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg) -if(MITK_ENABLE_GUI_TESTING) +if(WIN32 OR APPLE OR MITK_ENABLE_GUI_TESTING) ### since the rendering test's do not run in ubuntu, yet, we build them only for other systems or if the user explicitly sets the variable MITK_ENABLE_GUI_TESTING mitkAddCustomModuleTest(mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2DTest ${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/rgbaImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3d640x480 mitkImageVtkMapper2DTest #test for standard Pic3D transversal slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3d640x480REF.png #corresponding reference screenshot ) -mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dColorBlue640x480 mitkImageVtkMapper2DColorTest #test for color property (=blue) Pic3D sagittal slice - ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage - -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dColorBlue640x480REF.png #corresponding reference screenshot -) -mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dLevelWindow640x480 mitkImageVtkMapper2DLevelWindowTest #test for levelwindow property (=blood) Pic3D sagittal slice - ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage - -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dLevelWindowBlood640x480REF.png #corresponding reference screenshot -) -mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dOpacity640x480 mitkImageVtkMapper2DOpacityTest #test for opacity (=0.5) Pic3D coronal slice - ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage - -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dOpacity640x480REF.png #corresponding reference screenshot -) +#mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dColorBlue640x480 mitkImageVtkMapper2DColorTest #test for color property (=blue) Pic3D sagittal slice +# ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage +# -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dColorBlue640x480REF.png #corresponding reference screenshot +#) +#mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dLevelWindow640x480 mitkImageVtkMapper2DLevelWindowTest #test for levelwindow property (=blood) Pic3D sagittal slice +# ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage +# -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dLevelWindowBlood640x480REF.png #corresponding reference screenshot +#) +#mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dOpacity640x480 mitkImageVtkMapper2DOpacityTest #test for opacity (=0.5) Pic3D coronal slice +# ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage +# -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dOpacity640x480REF.png #corresponding reference screenshot +#) endif() # see bug 9882 if(NOT APPLE) add_test(mitkPointSetLocaleTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkPointSetLocaleTest ${MITK_DATA_DIR}/pointSet.mps) set_property(TEST mitkPointSetLocaleTest PROPERTY LABELS MITK-Core) endif() add_test(mitkImageWriterTest_nrrdImage ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkImageWriterTest ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg) add_test(mitkImageWriterTest_2DPNGImage ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkImageWriterTest ${MITK_DATA_DIR}/Png2D-bw.png) set_property(TEST mitkImageWriterTest_nrrdImage PROPERTY LABELS MITK-Core) set_property(TEST mitkImageWriterTest_2DPNGImage PROPERTY LABELS MITK-Core) add_subdirectory(DICOMTesting) diff --git a/Core/Code/Testing/mitkImageTest.cpp b/Core/Code/Testing/mitkImageTest.cpp index 3fa1c680fa..c35048356e 100644 --- a/Core/Code/Testing/mitkImageTest.cpp +++ b/Core/Code/Testing/mitkImageTest.cpp @@ -1,341 +1,351 @@ /*=================================================================== 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 includes #include #include #include #include "mitkItkImageFileReader.h" #include #include // itk includes #include #include // stl includes #include // vtk includes #include int mitkImageTest(int argc, char* argv[]) { MITK_TEST_BEGIN(mitkImageTest); //Create Image out of nowhere mitk::Image::Pointer imgMem = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED( imgMem.IsNotNull(), "An image was created. "); // Initialize image imgMem->Initialize( pt, 3, dim); MITK_TEST_CONDITION_REQUIRED( imgMem->IsInitialized(), "Image::IsInitialized() ?"); MITK_TEST_CONDITION_REQUIRED( imgMem->GetPixelType() == pt, "PixelType was set correctly."); int *p = (int*)imgMem->GetData(); MITK_TEST_CONDITION( p != NULL, "GetData() returned not-NULL pointer."); // FIXME: this is directly changing the image data // filling image const unsigned int size = dim[0]*dim[1]*dim[2]; for(unsigned int i=0; iGetData(); MITK_TEST_CONDITION( p2 != NULL, "GetData() returned not-NULL pointer."); bool isEqual = true; for(unsigned int i=0; iGetSliceData(dim[2]/2)->GetData(); MITK_TEST_CONDITION_REQUIRED( p2 != NULL, "Valid slice data returned"); unsigned int xy_size = dim[0]*dim[1]; unsigned int start_mid_slice = (dim[2]/2)*xy_size; isEqual = true; for(unsigned int i=0; i(); imgMem->Initialize( pType , 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension()== 3, "Testing initialization parameter dimension!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pType, "Testing initialization parameter pixeltype!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension(0) == dim[0] && imgMem->GetDimension(1)== dim[1] && imgMem->GetDimension(2)== dim[2], "Testing initialization of dimensions!"); MITK_TEST_CONDITION( imgMem->IsInitialized(), "Image is initialized."); // Setting volume again: imgMem->SetVolume(imgMem->GetData()); //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); //InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); // Testing Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int slices) with PlaneGeometry and GetData(): "; imgMem->Initialize( mitk::MakePixelType(), *planegeometry); MITK_TEST_CONDITION_REQUIRED( imgMem->GetGeometry()->GetOrigin() == static_cast(planegeometry)->GetOrigin(), "Testing correct setting of geometry via initialize!"); p = (int*)imgMem->GetData(); MITK_TEST_CONDITION_REQUIRED( p!=NULL, "GetData() returned valid pointer."); // Testing Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry) and GetData(): "; imgMem->Initialize( mitk::MakePixelType() , 40, *planegeometry); p = (int*)imgMem->GetData(); MITK_TEST_CONDITION_REQUIRED( p!=NULL, "GetData() returned valid pointer."); //----------------- // testing origin information and methods MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetTimeSlicedGeometry()->GetOrigin(): "); // Setting origin via SetOrigin(origin): "; mitk::FillVector3D(origin, 37.0, 17.92, 27.83); imgMem->SetOrigin(origin); // Test origin MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetTimeSlicedGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(), origin), "Testing correctness of changed origin via GetSlicedGeometry()->GetGeometry2D(0)->GetOrigin(): "); //----------------- // testing spacing information and methods MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correct spacing from Geometry3D!"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetSpacing(), spacing), "Testing correctspacing from TimeSlicedGeometry!"); mitk::FillVector3D(spacing, 7.0, 0.92, 1.83); imgMem->SetSpacing(spacing); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetTimeSlicedGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetTimeSlicedGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(), spacing), "Testing correctness of changed spacing via GetSlicedGeometry()->GetGeometry2D(0)->GetSpacing(): "); mitk::Image::Pointer vecImg = mitk::Image::New(); vecImg->Initialize( imgMem->GetPixelType(), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/ ); vecImg->SetImportChannel(imgMem->GetData(), 0, mitk::Image::CopyMemory ); vecImg->SetImportChannel(imgMem->GetData(), 1, mitk::Image::CopyMemory ); MITK_TEST_CONDITION_REQUIRED(vecImg->GetChannelData(0)->GetData() != NULL && vecImg->GetChannelData(1)->GetData() != NULL, "Testing set and return of channel data!"); MITK_TEST_CONDITION_REQUIRED( vecImg->IsValidSlice(0,0,1) , ""); MITK_TEST_OUTPUT(<< " Testing whether CopyMemory worked"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetData() != vecImg->GetData(), ""); MITK_TEST_OUTPUT(<< " Testing destruction after SetImportChannel"); vecImg = NULL; MITK_TEST_CONDITION_REQUIRED(vecImg.IsNull() , "testing destruction!"); //----------------- MITK_TEST_OUTPUT(<< "Testing initialization via vtkImageData"); MITK_TEST_OUTPUT(<< " Setting up vtkImageData"); vtkImageData* vtkimage = vtkImageData::New(); vtkimage->Initialize(); vtkimage->SetDimensions( 2, 3, 4); double vtkorigin[] = {-350,-358.203, -1363.5}; vtkimage->SetOrigin(vtkorigin); mitk::Point3D vtkoriginAsMitkPoint; mitk::vtk2itk(vtkorigin, vtkoriginAsMitkPoint); double vtkspacing[] = {1.367, 1.367, 2}; vtkimage->SetSpacing(vtkspacing); vtkimage->SetScalarType( VTK_SHORT ); vtkimage->AllocateScalars(); std::cout<<"[PASSED]"<Initialize(vtkimage); MITK_TEST_CONDITION_REQUIRED(mitkByVtkImage->IsInitialized(), ""); vtkimage->Delete(); MITK_TEST_OUTPUT(<< " Testing whether spacing has been correctly initialized from vtkImageData"); mitk::Vector3D spacing2 = mitkByVtkImage->GetGeometry()->GetSpacing(); mitk::Vector3D vtkspacingAsMitkVector; mitk::vtk2itk(vtkspacing, vtkspacingAsMitkVector); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(spacing2,vtkspacingAsMitkVector), ""); MITK_TEST_OUTPUT(<< " Testing whether GetSlicedGeometry(0)->GetOrigin() has been correctly initialized from vtkImageData"); mitk::Point3D origin2 = mitkByVtkImage->GetSlicedGeometry(0)->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetTimeSlicedGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetTimeSlicedGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2,vtkoriginAsMitkPoint), ""); // TODO test the following initializers on channel-incorporation // void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, unsigned int *dimensions, unsigned int channels) // void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::Geometry2D& geometry2d, bool flipped, unsigned int channels, int tDim ) // void mitk::Image::Initialize(const mitk::Image* image) // void mitk::Image::Initialize(const mitkIpPicDescriptor* pic, int channels, int tDim, int sDim) //mitk::Image::Pointer vecImg = mitk::Image::New(); //vecImg->Initialize(PixelType(typeid(float), 6, itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); //vecImg->Initialize(PixelType(typeid(itk::Vector)), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); // testing access by index coordinates and by world coordinates MITK_TEST_CONDITION_REQUIRED(argc == 2, "Check if test image is accessible!"); const std::string filename = std::string(argv[1]); mitk::ItkImageFileReader::Pointer imageReader = mitk::ItkImageFileReader::New(); try { imageReader->SetFileName(filename); imageReader->Update(); } catch(...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return 0; } mitk::Image::Pointer image = imageReader->GetOutput(); // generate a random point in world coordinates mitk::Point3D xMax, yMax, zMax, xMaxIndex, yMaxIndex, zMaxIndex; xMaxIndex.Fill(0.0f); yMaxIndex.Fill(0.0f); zMaxIndex.Fill(0.0f); xMaxIndex[0] = image->GetLargestPossibleRegion().GetSize()[0]; yMaxIndex[1] = image->GetLargestPossibleRegion().GetSize()[1]; zMaxIndex[2] = image->GetLargestPossibleRegion().GetSize()[2]; image->GetGeometry()->IndexToWorld(xMaxIndex, xMax); image->GetGeometry()->IndexToWorld(yMaxIndex, yMax); image->GetGeometry()->IndexToWorld(zMaxIndex, zMax); MITK_INFO << "Origin " << image->GetGeometry()->GetOrigin()[0] << " "<< image->GetGeometry()->GetOrigin()[1] << " "<< image->GetGeometry()->GetOrigin()[2] << ""; MITK_INFO << "MaxExtend " << xMax[0] << " "<< yMax[1] << " "<< zMax[2] << ""; mitk::Point3D point; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize( std::rand() ); // initialize with random value, to get sensible random points for the image point[0] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[0], xMax[0]); point[1] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[1], yMax[1]); point[2] = randomGenerator->GetUniformVariate( image->GetGeometry()->GetOrigin()[2], zMax[2]); MITK_INFO << "RandomPoint " << point[0] << " "<< point[1] << " "<< point[2] << ""; // test values and max/min mitk::ScalarType imageMin = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType imageMax = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType value = image->GetPixelValueByWorldCoordinate(point); MITK_INFO << imageMin << " "<< imageMax << " "<< value << ""; MITK_TEST_CONDITION( (value >= imageMin && value <= imageMax), "Value returned is between max/min"); + // test accessing PixelValue with coordinate leading to a negative index + const mitk::Point3D geom_origin = image->GetGeometry()->GetOrigin(); + const mitk::Point3D geom_center = image->GetGeometry()->GetCenter(); + const unsigned int timestep = 0; + + // shift position from origin outside of the image ( in the opposite direction to [center-origin] vector which points in the inside) + mitk::Point3D position = geom_origin + (geom_origin - geom_center); + MITK_TEST_CONDITION_REQUIRED( image->GetPixelValueByWorldCoordinate(position, timestep) == 0, "Test access to the outside of the image") + + // testing the clone method of mitk::Image mitk::Image::Pointer cloneImage = image->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension() == image->GetDimension(), "Clone (testing dimension)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetPixelType() == image->GetPixelType(), "Clone (testing pixel type)"); // After cloning an image the geometry of both images should be equal too MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetOrigin() == image->GetGeometry()->GetOrigin(), "Clone (testing origin)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetSpacing() == image->GetGeometry()->GetSpacing(), "Clone (testing spacing)"); MITK_TEST_CONDITION_REQUIRED(mitk::MatrixEqualElementWise(cloneImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(), image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix()), "Clone (testing transformation matrix)"); for (unsigned int i = 0u; i < cloneImage->GetDimension(); ++i) { MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension(i) == image->GetDimension(i), "Clone (testing dimension " << i << ")"); } //access via itk if(image->GetDimension()> 3) // CastToItk only works with 3d images so we need to check for 4d images { mitk::ImageTimeSelector::Pointer selector = mitk::ImageTimeSelector::New(); selector->SetTimeNr(0); selector->SetInput(image); selector->Update(); image = selector->GetOutput(); } if(image->GetDimension()==3) { typedef itk::Image ItkFloatImage3D; ItkFloatImage3D::Pointer itkimage; mitk::CastToItkImage(image, itkimage); MITK_TEST_CONDITION_REQUIRED(itkimage.IsNotNull(), "Test conversion to itk::Image!"); mitk::Point3D itkPhysicalPoint; image->GetGeometry()->WorldToItkPhysicalPoint(point, itkPhysicalPoint); MITK_INFO << "ITKPoint " << itkPhysicalPoint[0] << " "<< itkPhysicalPoint[1] << " "<< itkPhysicalPoint[2] << ""; mitk::Point3D backTransformedPoint; image->GetGeometry()->ItkPhysicalPointToWorld(itkPhysicalPoint, backTransformedPoint); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(point,backTransformedPoint), "Testing world->itk-physical->world consistency"); itk::Index<3> idx; bool status = itkimage->TransformPhysicalPointToIndex(itkPhysicalPoint, idx); MITK_INFO << "ITK Index " << idx[0] << " "<< idx[1] << " "<< idx[2] << ""; if(status) { float valByItk = itkimage->GetPixel(idx); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(valByItk, value), "Compare value of pixel returned by mitk in comparison to itk"); } else { MITK_WARN<< "Index is out buffered region!"; } } else { MITK_INFO << "Image does not contain three dimensions, some test cases are skipped!"; } // clone generated 3D image with one slice in z direction (cf. bug 11058) unsigned int* threeDdim = new unsigned int[3]; threeDdim[0] = 100; threeDdim[1] = 200; threeDdim[2] = 1; mitk::Image::Pointer threeDImage = mitk::Image::New(); threeDImage->Initialize(mitk::MakeScalarPixelType(), 3, threeDdim); mitk::Image::Pointer cloneThreeDImage = threeDImage->Clone(); // check that the clone image has the same dimensionality as the source image MITK_TEST_CONDITION_REQUIRED( cloneThreeDImage->GetDimension() == 3, "Testing if the clone image initializes with 3D!"); MITK_TEST_END(); } diff --git a/Core/Documentation/Doxygen/Concepts/CodingGeneral.dox b/Core/Documentation/Doxygen/Concepts/CodingGeneral.dox new file mode 100644 index 0000000000..2dab6af5f9 --- /dev/null +++ b/Core/Documentation/Doxygen/Concepts/CodingGeneral.dox @@ -0,0 +1,53 @@ +/** +\page CodingPage Coding + +\section CodingPageGeneral General Information +Conceptually MITK is mainly geared to ITK (The Insight Toolkit). +Thus, most of the ITK coding concepts can be transferred to MITK. + +The ITK coding concepts can be found in the free ITK software guide, downloadable at ITKs homepage. + +The main ITK concepts which one should look into before using MITK are: +
    +
  • SmartPointers +
  • Pipelining and Filters +
+ +\section CodingPageStyle Coding Style +MITK provides coding style guidelines. When implementing code which should be added to the project, +these guidelines should be followed in order to keep a unified coding style all over MITK. The style +is geared to a common c++ coding style combined with some extension of used libraries, e.g. the use of +itk smart pointers. The MITK style guide is provided on the page \ref StyleGuideAndNotesPage . +If you are new to coding with MITK please also consider that MITK is using different pre defined macros, +which might look confusing. An overview on these macros is given in section \ref CodingPageMITKMacros. + +\section CodingPageMITKMacros Macros in MITK +MITK uses different macros to simplify implementation, but these macros might look confusing at first. Some +of these macros are derived from ITK, others are defined in MITK itself. The most important macros are defined +in the file mitkCommon.h, but there are some other headers which also define macros, e.g. mitkTestingMacros.h +and mitkExceptionMacros.h. + +In the following the most important macros are shown for overview, more details are available in the corresponding header files. +\code +//These macros come from ITK: + +itkNewMacro(Class);//this macro creates the constructor for smart pointers + //it calls the default c++ constructor of the class + //the default constructor should be declared protected + +itkGetMacro(Name,Class); //these macros create getters and setters +itkSetMacro(Name,Class); //automatically, but you need the corresponding +itkGetConstMacro(Name,Class); //member variable m_Name in your class +itkSetConstMacro(Name,Class); + +//The following macros are defined in MITK itself: + +mitkClassMacro(Class,Superclass); //macro is needed in every header of a MITK class + +mitkNewMacro1Param(Class,ParamType); //like the ITK new macro, but with one parameter + //you need a constructor with one parameter as well + //the same macro exists for 2,3 and 4 parameters + +mitkExceptionClassMacro(Class,Superclass); //special macro for MITK exception classes +\endcode +*/ diff --git a/Core/Documentation/Doxygen/Concepts/Concepts.dox b/Core/Documentation/Doxygen/Concepts/Concepts.dox index 4caf8cec74..ed3c3a2d15 100644 --- a/Core/Documentation/Doxygen/Concepts/Concepts.dox +++ b/Core/Documentation/Doxygen/Concepts/Concepts.dox @@ -1,17 +1,30 @@ /** \page Concepts MITK concepts The following items describe some issues about MITK on a more abstract level. -If you want to start using MITK, you also want to see \ref Development +-# \subpage OverviewPage +-# \subpage CodingPage "Coding Concepts" + -# \ref CodingPageGeneral + -# \ref CodingPageStyle + -# \ref CodingPageMITKMacros + -# \subpage MicroServices_Overview +-# Data Concepts + -# \subpage DataManagementPage + -# \subpage MitkImagePage + -# \subpage PropertiesPage + -# \subpage GeometryOverviewPage + -# \subpage PipelineingConceptPage +-# \subpage QVTKRendering +-# \subpage InteractionPage +-# \subpage LoggingPage +-# \subpage ExceptionPage +-# Testing Concept + -# \subpage GeneralTests + -# \subpage RenderingTests +-# \subpage ModularizationPage "Modularization Concept" + -# \ref ModularizationPageOverview + -# \ref ModularizationPageHowTo -\li \subpage OverviewPage -\li \subpage QVTKRendering -\li \subpage InteractionPage -\li \subpage ExceptionPage -\li \subpage LoggingPage -\li \subpage PropertiesPage -\li \subpage GeometryOverviewPage -\li \subpage MicroServices_Overview -\li \subpage RenderingTests +If you want to start using MITK, you also want to see the chapter \ref Development. */ diff --git a/Core/Documentation/Doxygen/Concepts/DataManagement.dox b/Core/Documentation/Doxygen/Concepts/DataManagement.dox new file mode 100644 index 0000000000..cfae3c848f --- /dev/null +++ b/Core/Documentation/Doxygen/Concepts/DataManagement.dox @@ -0,0 +1,56 @@ +/** + +\page DataManagementPage The Data Management Concept + +As MITK is used to process large and diverse data the management thereof becomes an important issue. +In order to abstract the management of data from the actual format of the data it is encapsulated. + +\section DataManagementPageEncapsulation Data Encapsulation + +All data objects, like images or surfaces, are contained in DataNodes. These DataNodes describe the data +itself (mitk::BaseData and derived classes), how they can be rendered in 2D or 3D (a list of mitk::Mapper), +what mitk::Interactor is associated with it, and a list of arbitrary properties (name, visibility, opacity, etc.). +Information about the position of a data object in space/time is stored in a Geometry, which is attached to the +data object itself, not to the node. + +\subsection DataManagementPageBaseData BaseData + +mitk::BaseData is the base class for all data objects. It itself inherits from itk::DataObject. + +\subsection DataManagementPageDataNode DataNode + +This class encapsulates a BaseData object and provides uniform handling. + +\subsection DataManagementPageDataStorage DataStorage + +The MITK DataStorage manages and stores DataNodes. Besides adding and removing nodes it provides advanced +functionality such as getting a list of all DataNodes currently in the DataStorage which match a specific +type (e.g. image, surface ) or have a specific name or other property. + +There can be different DataStorages. + +A usage example for a class deriving from QmitkAbstractView: +\code + mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); // create data node + std::string nameOfResultImage = node->GetName(); // get property "name" of the original node,this is a convenience method + nameOfResultImage.append("Otsu"); // extend the "name" to "nameOtsu" + resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); // write name property to new node + resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); // set binary property of new node true + resultNode->SetData( mitk::ImportItkImage ( filter->GetOutput() ) ); // set data of new node + + this->GetDataStorage()->Add(resultNode, node); // add new node to datastorage +\endcode + +\section DataManagementPageOwnType Adding an own data type + +To extent MITKs capabilities by adding a custom data type there are several things which should be done. +Depending on whether the data type is completely unlike any of the existing ones, or derived from one of +them some steps can possibly be omitted. + +
    +
  1. Add your data type +
  2. Add reader and writer and register them +
  3. Add mapper +
+ +*/ diff --git a/Core/Documentation/Doxygen/Concepts/Exceptions.dox b/Core/Documentation/Doxygen/Concepts/Exceptions.dox index e3f3149380..5bc0ca762d 100644 --- a/Core/Documentation/Doxygen/Concepts/Exceptions.dox +++ b/Core/Documentation/Doxygen/Concepts/Exceptions.dox @@ -1,92 +1,92 @@ /** \page ExceptionPage Error Handling and Exception Concept \ref ExceptionHandling "1. General Exception Handling" \ref SpecializedExceptionHandling "2. Defining and Using Specialized Exceptions" \section ExceptionHandling General Exception Handling In MITK, errors during program execution are handled by the well known exception handling concept which is part of the C++ language. In case of unexpected exceptional behaviour or errors during program execution MITK classes throw exceptions. MITK exceptions are always objects of the class mitk::Exception or of one of its subclasses. \subsection Throw Throwing of exceptions Exceptions should always be thrown by using the predefined exception macros. If you want to throw a mitk::Exception in your code, simply use the following macro. \verbatim //This command will throw a mitk::Exception and add a message. //The macro will also add filename and line number to the exception //object. mitkThrow() << "Here comes your exception message"; \endverbatim You can also stream more complex messages, e.g. adding integers or other variables to your exception messages, like shown in the following example. \verbatim mitkThrow() << "This time we show the values of some variables:" << m_MyObject->GetSize() << m_MyInteger; \endverbatim \subsection Doc Documentation of exceptions If you throw exceptions in your code, please also document this in your doxygen comments by using the "@throws " tag. This will help users of your class to catch and handle the exceptions in a proper way. An example how to document exception throwing is given below. \verbatim class myExampleClass { /** Documentation * @brief This method does [...] * @throws mitk::Exception This exception is thrown, when the following error occures: [...] */ void MyExampleMethod() { //here comes your code //here happens an exception mitkThrow() << "An exception occured because [...], method can't continue."; } } \endverbatim In general, exceptions emit no logging messages by default because they are intended to be catched by overlying classes. This classes should then decide what to do, e.g. to log an error message or handle the exception in another way. See the logging documentation for more details on error logging. \subsection Catch Catching exceptions -Exceptions should be catched by overlying classes, when they can handle them in a proper way. Catching exceptions is very simple, use the standard try-catch block to do so. An example is given below. +Exceptions should be caught by overlying classes, if they can handle them in a proper way. Catching exceptions is very simple, use the standard try-catch block to do so. An example is given below. \verbatim try { //call of a method which may throw an exception myObject->MyExampleMethod(); } catch (mitk::Exception e) { //This code is executed if an exception of the given type was thrown above. //For example log an error message here or do some other proper handling of the exception. } \endverbatim -Please do not use "catch (...)" because normally your class can't garantee to handle all exceptions in a proper way without differentiate them. +Please do not use "catch (...)" because normally your class can't garantee to handle all exceptions in a proper way without differentiating them. \section SpecializedExceptionHandling Defining and Using more Specialized Exceptions The basic MITK exception concept was kept very simple and should suffice in many cases. But you can also use more specialized exceptions, if needed. Nevertheless all MITK exceptions should be subclasses of mitk::exception. You can define your own exception classes by simply implementing new classes which derive from mitk::exception. Thus, you can catch your exception seperately when needed. The mitkExceptionClassMacro helps to keep implementing new exception classes as simple as possible, like shown in the following code example. \verbatim #include class mitk::MySpecializedException : public mitk::Exception { public: mitkExceptionClassMacro(mitk::MySpecializedException,mitk::Exception); }; \endverbatim To throw your specialized exception you should use the corresponing macro, which is shown in the next code snippet. \verbatim mitkThrowException(mitk::MySpecializedException) << "this is error info"; \endverbatim */ \ No newline at end of file diff --git a/Core/Documentation/Doxygen/Concepts/Interaction.dox b/Core/Documentation/Doxygen/Concepts/Interaction.dox index 99cf22ec85..1eb2bd879f 100644 --- a/Core/Documentation/Doxygen/Concepts/Interaction.dox +++ b/Core/Documentation/Doxygen/Concepts/Interaction.dox @@ -1,122 +1,120 @@ /** \page InteractionPage Interaction and Undo/Redo Concepts \section InteractionPage_Introduction Interaction in MITK \b Interaction is one of the most important tasks in clinically useful image processing software. Due to that, MITK has a special interaction concept, with which the developer can map the desired interaction. For a simple change in interaction he doesn't have to change the code. All information about the sequence of the interaction is stored in an XML-file that is loaded by the application during startup procedure at runtime. That even allows the storage of different interaction patterns, e.g. an interaction behaviour like in MS PowerPoint, in Adobe Photoshop or like the interaction behaviour on a medical image retrieval system. \section InteractionPage_Statemachines_Implementation Statemachines to implement Interaction The interaction in MITK is implemented with the concept of state machines (by Mealy). This concept allows to build the steps of interaction with different states, which each have different conditions, very alike the different interactions that may have to be build to develop medical imaging applications. Furthermore state machines can be implemented using object oriented programming (OOP). Due to that we can abstract from the section of code, that implements the interaction and focus on the sequence of interaction. What steps must the user do first before the program can compute a result? For example he has to declare three points in space first and these points are the input of a filter so only after the definition of the points, the filter can produce a result. The according interaction sequence will inform the filter after the third point is set and not before that. Now the filter after an adaption only needs two points as an input. The sequence of the interaction can be easily changed if it is build up as a sequence of objects and not hard implemented in a e.g. switch/case block. Or the user wants to add a point in the scene with the right mouse button instead of the left. Wouldn't it be nice to only change the definition of an interaction sequence rather than having to search through the code and changing every single if/else condition? \subsection InteractionPage_Statemachine State Machine -So a separation of the definition of a sequence in interaction and its implementation is a useful step in the development of an interactive application. +So a separation of the definition of a sequence in interaction and its implementation is a useful step in the development of an +interactive application. To be able to do that, we implemented the concept of state machines with several classes: States, Transitions and Actions define the interaction pattern. The state machine itself adds the handling of events, that are sent to it. \image html statemachine.jpg \subsubsection InteractionPage_ExampleA Example A: -A deterministic Mealy state machine has always one current state (here state 1). If an event 1 is sent to the state machine, it searches in its current state for a transition that waits for event 1 (here transition 1). The state machine finds transition 1, changes the current state to state2, cause the transition points to it and executes actions 1 and 2. Now state 2 is the current state. The state machine receives an event 2 and searches for an according transition. Transition 2 waits for event 2, and since the transition leads to state 2 the current state is not changed. Action 3 and 4 are executed. Now Event 3 gets send to the state machine but the state machine can't find an according transition in state 2. Only transition 2 , that waits for event 2 and transition 4, that waits for event 4 are defined in that state. So the state machine ignores the event and doesn't change the state or execute an action. Now the state machine receives an event 4 and finds transition 3. So now the current state changes from state 2 to state 1 and actions 5 and 1 are executed. +A deterministic Mealy state machine has always one current state (here state 1). If an event 1 is sent to the state machine, it searches in its current state for a transition that waits for event 1 (here transition 1). The state machine finds transition 1, changes the current state to state2, as the transition points to it and executes actions 1 and 2. Now state 2 is the current state. The state machine receives an event 2 and searches for an according transition. Transition 2 waits for event 2, and since the transition leads to state 2 the current state is not changed. Action 3 and 4 are executed. Now Event 3 gets send to the state machine but the state machine can't find an according transition in state 2. Only transition 2 , that waits for event 2 and transition 4, that waits for event 4 are defined in that state. So the state machine ignores the event and doesn't change the state or execute an action. Now the state machine receives an event 4 and finds transition 3. So now the current state changes from state 2 to state 1 and actions 5 and 1 are executed. Several actions can be defined in one transition. The execution of an action is the active part of the state machine. Here is where the state machine can make changes in data, e.g. add a Point into a list. See mitk::StateMachine, mitk::State, mitk::Event, mitk::Action, mitk::Transition, mitk::Interactor \subsection InteractionPage_GuardState Guard States -Guard States are a special kind of states. The action, that is executed after the state is set as current state, sends a new event to the state machine, which leads out of the guard state. So the state machine will only stay in a guard state for a short time. This kind of state is used to check different conditions, e.g. is an Object is picked of a set of points will be full after the addition of one point. +Guard States are a special kind of states. The action, that is executed after the state is set as current state, sends a new event to the state machine, which leads out of the guard state. So the state machine will only stay in a guard state for a short time. This kind of state is used to check different conditions, e.g. if an Object is picked or whether a set of points will be full after the addition of one point. \image html statemachine_guard.jpg \subsubsection InteractionPage_ExampleB Example B: -Event 1 is sent to the state machine. This leads the current state from state 1 into state check. The action 1 is executed. This action checks a condition and puts the result into a new event, that is sent and handled by the same (this) state machine. E.g. is the the object, the state machine handles the interaction, picked with the received mouse-coordinate? The event, that is generated, will be Yes or No. In case of event No, the state machine sets the current state back to state 1 and executes action 2. In case of event Yes, the state machine changes the state from state check into state 2 and executes action 3, which e.g. can select the object taken care of. +Event 1 is sent to the state machine. This leads the current state from state 1 into state check. The action 1 is executed. This action checks a condition and puts the result into a new event, that is sent and handled by the same (this) state machine. E.g. is the object picked with the received mouse-coordinate? The event, that is generated, will be Yes or No. In case of event No, the state machine sets the current state back to state 1 and executes action 2. In case of event Yes, the state machine changes the state from state check into state 2 and executes action 3, which e.g. can select said object. \subsection InteractionPage_XMLDefinitionStatemachine Definition of a State machine -Due to the separation of the definition of an interaction sequence and its implementation, the definition has to be archived somewhere, where the application can reach it during startup and build up all the objects (states, transitions and actions) that represent the sequence of a special interaction. In MITK, these informations are defined in an XML-file (usually in Interaction/mitkBaseInteraction/StateMachine.xml). +Due to the separation of the definition of an interaction sequence and its implementation, the definition has to be archived somewhere, where the application can reach it during startup and build up all the objects (states, transitions and actions) that represent the sequence of a special interaction. In MITK, these informations are defined in an XML-file (usually in Core/Code/Interactions/StateMachine.xml and Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml, the duplication is currently needed due to design decisions). The structure is the following (from \ref InteractionPage_ExampleA) : \code - - - - - - - - + + + + + + + + \endcode -The identification numbers (ID) inside a state machine has to be unique. Each state machine has to have one state, that is defined as the start-state of that state machine. This means, initially, the current state of the state machine is the start-state. +The identification numbers (ID) inside a state machine have to be unique. Each state machine has to have one state, that is defined as the start-state of that state machine. This means, initially, the current state of the state machine is the start-state. The Event-Ids seen above are also defined in the statemachine.xml file. They specify a unique number for a combination of input-conditions (key, mouse and so on). See \ref InteractionPage_InteractionEvents for further informations. -So a state machine is defined through a few lines of xml-code, which is loaded at runtime. This allows us to change the sequence in interaction in the xml-file and restart the application, where the changes are applied right away. +The statemachine is compiled into an application at compile time. The definition of one single state machine is called the \a statemachine-pattern. Since this pattern is build up during startup with objects (states, transitions and actions) and these objects only hold information about what interaction may be done at the current state, we can also reuse the pattern. -Note, that you as a developer don't necessarily have to implement your own XML-File! We already have defined some interaction-patterns (e.g. for setting Points in 2D or 3D) which you can use and adapt. +\note You as a developer don't necessarily have to implement your own XML-File! We already have defined some interaction-patterns (e.g. for setting Points in 2D or 3D) which you can use and adapt. \subsubsection InteractionPage_ReusePattern Reuse of Interaction Patterns If we for example have a pattern called "pointset", which defines how the user can set different points into the scene and there is an instance of a state machine called "PointSetInteractor". This state machine has a pointer pointing to the current state in its assigned state machine pattern. Several events are send to the state machine, which moves the pointer from one state to the next, according to the transitions, and executes the actions, referenced in the transitions. But now a new instance of the class "PointSetInteractor" has to be build. So we reuse the pattern and let the current state pointer of the new object point to the start state of the pattern "pointset". The implementation of the actions is \b not done inside a class of the pattern (\a state, \a transition, \a action), it is done inside a state machine class (see the reference for mitkStatemachine). -\subsection InteractionPage_StartupInteraction Loading Statemachines at Runtime -During startup all state machine-patterns are checked for not having states, that have no transitions leading out of it (dead states), or states, that have no transitions leading into it (magic states) [Bin99 p. 183]. If an error is found, an error-message will be displayed \b and the state machine will be loaded. - -See mitk::StateMachineFactory - \subsection InteractionPage_InteractionEvents Events -During runtime, events are thrown from e.g. the mouse to the operation system, are then send to your graphical user interface and from there it has to be send to the MITK-object called \a mitkEventMapper. This class maps the events received with an internal list of all events that can be understood in MITK. The definition of all understandable events is also located in the XML-File the state machines are defined in. If the received event can be found in the list, an internal mitk-eventnumber is added to the event and send to the object \a mitkGlobalInteraction. +During runtime, events are thrown from e.g. the mouse to the operating system, are then send to your graphical user interface and from there it has to be send to the MITK-object called \a mitkEventMapper. This class maps the events received with an internal list of all events that can be understood in MITK. The definition of all understandable events is also located in the XML-File the state machines are defined in. If the received event can be found in the list, an internal mitk-eventnumber is added to the event and send to the object \a mitkGlobalInteraction. See mitk::Event, mitk::GlobalInteraction \subsection InteractionPage_GlobalInteraction GlobalInteraction -This object administers the transmission of events to registered state machines. There can be two kinds of state machines, the ones that are only listening and ones that also change data. Listening state machines are here called Listeners and state machines that also change data are called Interactors. \b Note that the discrimination between \a Listener and \a Interactor is only made in mitkGlobalInteraction. As Listener an object derived from class StateMachine can be added and removed from GlobalInteraction and as Interactor an object derived from class Interactor can be added and removed. See the interaction class diagram for further information. +This object administers the transmission of events to registered state machines. There can be two kinds of state machines, the ones that are only listening and ones that also change data. Listening state machines are here called Listeners and state machines that also change data are called Interactors. +\note The discrimination between \a Listener and \a Interactor is only made in mitkGlobalInteraction. +As Listener an object derived from class StateMachine can be added and removed from GlobalInteraction and as Interactor an object derived from class Interactor can be added and removed. See the interaction class diagram for further information. -To add or remove a state machine to the list of registered interactors, call \a AddInteractor or \a RemoveInteractor of \a GlobalInteraction or to add or remove a listener call \a AddListener of \a RemoveListener. Listeners are always provided with the events. Interactors shall only be provided with an event, if they can handle the event. Because of that the method CanHandleEvent is called, which is implemented in each Interactor. This method analyses the event and returns a value between 0 (can't handle event) and 1 (can handle the event). Information, that can help to calculate this jurisdiction can be the bounding box of the interacted data and the picked mouse-position stored in the event. +To add or remove a state machine to the list of registered interactors, call \a AddInteractor or \a RemoveInteractor of \a GlobalInteraction or to add or remove a listener call \a AddListener of \a RemoveListener. Listeners are always provided with the events. Interactors shall only be provided with an event, if they can handle the event. Because of that the method CanHandleEvent is called, which is implemented in each Interactor. This method analyses the event and returns a value between 0 (can't handle event) and 1 (Best choice to handle the event). Information, that can help to calculate this jurisdiction can be the bounding box of the interacted data and the picked mouse-position stored in the event. So after the object \a GlobalInteraction has received an event, it sends this event to all registered Listeners and then asks all registered Interactors through the method \a CanHandleEvent how good each Interactor can handle this event. The Interactor which can handle the event the best receives the event. Also see the documented code in \a mitkGlobalInteraction. To not ask all registered interactors on a new event, the class \a Interactor also has a mode, which can be one of the following: deselected, subselected (deprecated since HierarchicalInteraction has been removed), selected. These modes are also used for the event mechanism. If an interactor is in a state, where the user builds up a graphical object, it is likely that the following events are also for the build of the object. Here the interactor is in mode selected as long as the interactor couldn't handle an event. Then it changes to mode deselected. The mode changes are done in the actions through operations (described further down) and so declared inside the interaction pattern. See mitk::GlobalInteraction \subsection InteractionPage_Interactors Interactors The class \a Interactor is the superclass for all state machines, that solve the interaction for a single data-object. An example is the class \a mitkPointSetInteractor which handles the interaction of the data \a mitkPointSet. Inside the class \a mitkPointSetInteractor all actions, defined in the interaction-pattern "pointsetinteractor", are implemented. Inside the implementation of these actions (\a ExecuteAction(...) ), so called \a mitkOperations are created, filled with information and send to the \a mitkUndoController and to \a mitkOperactionActor (the data, the interaction is handled for). See mitk::Interactor \subsection InteractionPage_ExecOperations Executing Operations The class mitkOperation and its subclasses basically holds all information needed to execute a certain change of data. This change of data is only done inside the data-class itself, which is derived from the interface \a mitkOperationActor. Interactors handle the interaction through state-differentiation and combine all informations about the change in a \a mitkOperation and send this operation-object to the method ExecuteOperation (of data-class). Here the necessary data is extracted and then the change of data is performed. When the operation-object, here called do-operation, is created inside the method \a ExecuteAction (in class \a mitkInteractor), an undo-operation is also created and together with the do-operation stored in an object called \a OperationEvent. After the Interactor has sent the do-operation to the data, the operation-event-object then is sent to the instance of class \a mitkUndoController, which administrates the undo-mechanism. See mitk::Operation, mitk::OperationActor \subsection InteractionPage_UndoController UndoController The instance of class \a mitkUndoController administrates different Undo-Models. Currently implemented is a limited linear Undo. Only one Undo-Model can be activated at a time. The UndoController sends the received operation events further to the current Undo-Model, which then stores it according to the model. If the method \a Undo() of UndoController is called (e.g. Undo-Button pressed from ) the call is send to the current Undo-Model. Here the undo-operation from the last operation event in list is taken and send to the data, referenced in a pointer which is also stored in the operation-event. A call of the method \a Redo() is handled accordingly. See mitk::UndoController, mitk::LimitedLinearUndo \subsection InteractionPage_references References [Bin99] Robert V. Binder. Testing Object-Oriented Systems: Models, Patterns, and Tools. Addison-Wesley, 1999 */ diff --git a/Core/Documentation/Doxygen/Concepts/Logging.dox b/Core/Documentation/Doxygen/Concepts/Logging.dox index de6aef863d..fb722220b6 100644 --- a/Core/Documentation/Doxygen/Concepts/Logging.dox +++ b/Core/Documentation/Doxygen/Concepts/Logging.dox @@ -1,78 +1,78 @@ /** \page LoggingPage Logging Concept Available sections: --# \ref Sec1 "Basic Information on Logging" --# \ref Sec2 "Categorize your Logging Messages" --# \ref Sec3 "Logging Levels" --# \ref Sec4 "Conditional Logging" +-# \ref LoggingPageSection1 "Basic Information on Logging" +-# \ref LoggingPageSection2 "Categorize your Logging Messages" +-# \ref LoggingPageSection3 "Logging Levels" +-# \ref LoggingPageSection4 "Conditional Logging" -\section Sec1 Basic Information on Logging +\section LoggingPageSection1 Basic Information on Logging To use logging in MITK you can stream your messages into a logging stream, similar to the "std::cout" stream in standard c++. A simple example is shown next. \code MITK_INFO << "Here comes my message"; \endcode -Please only use the MITK_INFO (respectively MITK_WARN, MITK_ERROR, MITK_FATAL, MITK_DEBUG, see section "Logging levels" for more details) in MITK, the std::cout stream should not be used. +Please only use the MITK_INFO (respectively MITK_WARN, MITK_ERROR, MITK_FATAL, MITK_DEBUG, see \ref LoggingPageSection3 for more details) in MITK, the std::cout stream should not be used. You can also log object information, like shown in the next example. \code MITK_INFO << "I want to log my vector size: " << m_vector.getSize(); \endcode All logged information will be displayed in the console and be written to a logging file in the MITK binary folder. Advanced users may want to know that this behavior is controlled by the logging backend class, which can be adapted if you want to change the behavior. This is everything you need to know about simple logging. Further reading will show you how you can categorize your logging message and what logging levels are. -\section Sec2 Categorize your Logging Messages +\section LoggingPageSection2 Categorize your Logging Messages You may also want to categorize your logging messages, so you can assign messages to your specific topic. You can simply add classes and subclasses to your MITK logging messages by using brackets, like shown in the next example. \code MITK_INFO << "no class"; MITK_INFO("MyClass") << "single class"; MITK_INFO("MyClass")("MySubClass") << "class with subclass"; \endcode This classes makes it easy to e.g. simply filter all logging messages only for relevant information. -\section Sec3 Logging Levels +\section LoggingPageSection3 Logging Levels MITK offers different logging levels. You may mostly want to use MITK_INFO, but please consider using the other levels, e.g. when logging debug information or errors. Debug (MITK_DEBUG): These messages are designed for debug output, used to debug your source code. They are only displayed if you turn the CMake-Variable MBILOG_ENABLE_DEBUG_MESSAGES on. You can also use the debug message in release mode, output only depends on the CMake-Variable. Example: \code MITK_DEBUG << "Result of method LoadPicture(): true." \endcode Info (MITK_INFO): For standard information messages that inform about expected changes/results in the program flow. Info messages should be important and understandable for the users of the program. Example: \code MITK_INFO << "The picture test.pic has been loaded successfully." \endcode Warning (MITK_WARN): Warning messages should inform about unexpected or potentially problematic states in the program flow that do not lead directly to an error. Thus, after a warning the program should be able continue without errors and in a clear state. Example: \code MITK_WARN << "The picture test.pic was not loaded because access was denied." \endcode Error (MITK_ERROR): Error messages notify the user about corrupt states in the program flow. Such states may occur after unexpected behavior of source code. Example: \code MITK_ERROR << "Error while adding test.pic to the data storage, aborting." \endcode Fatal (MITK_FATAL): Fatal messages report corrupt states in the program flow that lead directly to a crash of the program. Example: \code MITK_FATAL << "Memory allocation error during instantiation of image object." \endcode -\section Sec4 Conditional Logging -Another feature of the logging mechanism is that you can directly give conditions if your message should be logged. These bool values or expressions can be defined in brackets. An example is shown next. +\section LoggingPageSection4 Conditional Logging +Another feature of the logging mechanism is that you can directly give conditions for logging your message. These bool values or expressions can be defined in brackets. An example is shown next. \code MITK_INFO(x>1000) << "x is too large"; //logs only if x > 1000 \endcode */ \ No newline at end of file diff --git a/Core/Documentation/Doxygen/Concepts/MitkImage.dox b/Core/Documentation/Doxygen/Concepts/MitkImage.dox new file mode 100644 index 0000000000..fb5f91314e --- /dev/null +++ b/Core/Documentation/Doxygen/Concepts/MitkImage.dox @@ -0,0 +1,71 @@ +/** +\page MitkImagePage MITK Image + +Available Sections: + +-# \ref MitkImagePage_Introduction "Introduction to MITK Image" + -# \ref MitkImagePage_SlicedData "SlicedData and Geometry" + -# \ref MitkImagePage_ImageData "ImageData" + -# \ref MitkImagePage_Properties "Image Properties" +-# \ref MitkImagePage_WorkingWith "Working with MITK Image" + -# \ref MitkImagePage_Cloning "Cloning a MITK Image" + -# \ref MitkImagePage_Inheriting "Inheriting from MITK Image" + + + +\section MitkImagePage_Introduction Introduction to MITK Image + +The MITK Image obviously is a very central class to MITK and one of those you are most likely to work with. This section will get you up and running with the basics. Consider this document a prerequisite for the Pipelining Introduction and the \ref GeometryOverviewPage. + +\image html mitkimagehierarchy.png + +Image is a direct descendant of SlicedData which itself inherits from Basedata. In MITK, Basedata is the common DataType from which all other Datatypes stem. SlicedData specifies this class to contain image slices, a typical example being a CT scan, and introduces properties and methods necessary to give the data a well defined geometry. Image further specializes the concept to allow for multiple channels, volumes and slices as well as additional information like image properties. + +For the sake of this introduction, we will have a look at three different aspects: + +1. SlicedData and Geometry +2. ImageData +3. Image Properties + +\subsection MitkImagePage_SlicedData SlicedData and Geometry +The mother class of Image introduces a fundamental aspect: Image geometry. It defines the image's spatial context: +Dimension and orientation. A more in depth introduction is given here: \ref GeometryOverviewPage + +\subsection MitkImagePage_ImageData ImageData + +Objects of the class Image store the actual image data. It is important to discern four different concepts: + +1. Channels, which can be of a specific data type e.g. an intensity image or a vector field. Each channel consists of one or more... +2. Volumes, which contain data of a certain type. A volume is represented by ImageDataItems that define volume properties. Inside of a channel, each volume must be of the same type (float, int, etc.). Each volume consists of several... +3. Slices, which each contain a two-dimensional image slice. + +There is also the pointer m_CompleteData that references all of the data (i.e. all volumes) as a singular array. This member is helpful, when one wants to copy image data from one image to another. + +\image html mitkimagememory.png + +\subsection MitkImagePage_Properties Image Properties + +Lastly, we'll talk about properties. Properties are a set of additional information mainly used to save DICOM information. The functionality is introduced very early in the image's lineage, in BaseData. The system works quite similar to a hashmap by using property keys and properties. For further reference, see BaseData::GetProperty() or, for a simple example implementation, USImage::GetMetadata(). + +\section MitkImagePage_WorkingWith Working with MITK Image + +\subsection MitkImagePage_Cloning Cloning a MITK Image +When duplicating an image, be sure to duplicate all data that you want to transfer. This includes Geometry, the visual Data and any properties necessary. The simplest way to achieve this is to first call Image::Initialize(const Image * image). This will copy the geometry information, but not the data or the properties. Afterwards, copy the image's data and, if necessary, it's properties. + +\verbatim +mitk::Image::Pointer new = mitk::Image::New(); // Create new, empty image +new->Initialize(old); // new no has the geometry information from old image +new->SetVolume(old->GetData()); // new now additionally contains the old images visual data +new->SetPropertyList(old->GetPropertyList()) // new now additionally contains the old image's properties +\endverbatim + + +\subsection MitkImagePage_Inheriting Inheriting from MITK Image +In general, one should try to avoid inheriting from mitk Image. The simple reason for this is that your derived class will not +cleanly work together with the Filters already implemented (See the chapter on Pipelining for Details). If however, mitk Image +does not offer the functionality you require it is possible to do so. See the documentation for various examples of classes +that inherit from image. + + + +*/ \ No newline at end of file diff --git a/Core/Documentation/Doxygen/Concepts/Modularization.dox b/Core/Documentation/Doxygen/Concepts/Modularization.dox new file mode 100644 index 0000000000..58d0f02c4e --- /dev/null +++ b/Core/Documentation/Doxygen/Concepts/Modularization.dox @@ -0,0 +1,44 @@ +/** + +\page ModularizationPage Modular MITK + +MITK has been designed to be modular and flexible, to facilitate reuse of existing code and functionality if possible. As such there are several levels of modularization which can be chosen when working with MITK, depending on the use case. + +\section ModularizationPageOverview Overview + +The general hierarchy of modularization runs micro service, module, view, plug-in, perspective and finally application. + +\subsection ModularizationPageMicroServices Micro Services + +A \ref MicroServices_Overview "micro service" is a reusable component provided by MITK modules. It can be accessed by other MITK modules or MITK plug-ins via its declared interface. + +\subsection ModularizationPageModules Modules + +Modules can be found in the MITK/Modules directory (with the exception of the core module in MITK/Core). Each module is a shared library that provides algorithms, data structures and similar code. Many modules are gui independent. A module is only build if it is required by other code, e.g. if a plug-ins is activated that requires it. + +\subsection ModularizationPageViews Views + +One of the smallest units in the MITK application framework (\ref FurtherReading "Blueberry") is a \e view. A view is always part of a plug-in and provides one specific function, such as grouping all gui elements needed for providing segmentation algorithms or loading dicom images. Views usually contain any code which communicates with the user, performs input checking and similar, but no advanced algorithms. + +\subsection ModularizationPagePlugInBundles Plugins + +The next smallest unit is a CTK Plug-in (the term \e Bundle is used interchangeably). They can be found in MITK/Plugins or MITK/BlueBerry/Bundles. Plugins can be individually switched on and off during the CMake configuration. + +A plugin usually represents a solution to a specific problem, such as segmentation or data management. As such they may provide any number of views and other contributions to the MITK application framework. + +If you want to create your own MITK plugin you find further information \ref NewPluginPage "here". + +\subsection ModularizationPagePerspectives Perspectives + +Perspectives represent a configuration for the MITK application framework (specifically, for the \e Workbench) needed for a specific workflow. They allow the arrangement of different views (contributed by different plug-ins) to reflect the usage scenario, such as a data loading view in the top right, a segmentation view to the right and a visualization view to the bottom right. A perspective is provided by arbitrary plug-ins as an extension (contribution) to the application framework. + +\subsection ModularizationPageApplications Applications + +A specific selection of plug-ins together with custom configuration data defines the functionality for an application. The application corresponds to an executable and enables very specific program behaviour and configuration. + +Example applications can be found in the MITK/Applications directory. + +\section ModularizationPageHowTo How to create your own application + +It is suggested to use the project generator provided by MITK unless one knows what one is doing. See \ref NewPluginWithProject "here" for more information. +*/ diff --git a/Core/Documentation/Doxygen/Concepts/Overview.dox b/Core/Documentation/Doxygen/Concepts/Overview.dox index 1891b083fb..f5def8bb90 100644 --- a/Core/Documentation/Doxygen/Concepts/Overview.dox +++ b/Core/Documentation/Doxygen/Concepts/Overview.dox @@ -1,52 +1,47 @@ /** -\page OverviewPage Overview on the Medical Imaging Interaction Toolkit (MITK) +\page OverviewPage Introduction: Overview on the Medical Imaging Interaction Toolkit (MITK) -Four issues are important for advanced interactive medical imaging software: -
    -
  • the data, not only the original images, but also other data like segmentation results, -surfaces, vessel-trees, etc., -
  • the algorithms to extract information from the data or to generate new data, -
  • the visualization of the data, which requires information about the position of the data -in space and time and -
  • to allow the user to conveniently interact with the data. -
+MITK is an open source software toolkit for medical image processing, subsequent data analysis and integration of medical hardware. +It is designed with the aim of providing a modular and heavily reusable code base to enable rapid development of new features. Following +this design philosophy MITK includes many different specialized modules e.g. the Segmentation Module. -Today, there are two major open-source toolkits for visualization and image processing: -
    -
  • the Visualization Toolkit (VTK), which provides "a wide variety -of visualization algorithms including scalar, vector, tensor, texture, and volumetric methods; -and advanced modeling techniques such as implicit modelling, polygon reduction, mesh smoothing, -cutting, contouring, and Delaunay triangulation. In addition, dozens of imaging algorithms have -been directly integrated to allow the user to mix 2D imaging / 3D graphics algorithms and data." -(from the Visualization Toolkit (VTK) ). -
  • the Insight Toolkit (ITK), which provides registration and -segmentation algorithms. -
+This document is aimed at giving an overview of the general structure of MITK. Furthermore it will give an introduction into the coding +and design concepts behind this toolkit. -ITK provides powerful algorithms, but is not designed for visualization or interaction. VTK has -powerful visualization capabilities, but only low-level support for interaction such as picking -methods, rotation, movement and scaling of objects. Support for high level interactions with data -as, for example, the interactive construction and modification of deformable models, and -undo-capabilities is outside the scope of VTK. Furthermore, it is designed to create \em one -\em kind of view on the data. There is no special assistance to realized multiple, different -views of the data (as a multiplanar reconstruction and a 3D rendering). Finally, VTK supports only -2D and 3D data, not 3D+t data, which are required for some medical applications, and there is -currently no convenient possibility to combine VTK with ITK. +\section OverviewPage_DesignOverview Design Overview -The aim of MITK is to use VTK and ITK, allow an easy combination of both and extend them with those -features, which are outside the scope of both. +MITK is designed to be used as a pure software library or as a complete application framework. Thus, a user +of MITK can decide if he simply wants to add a new plug-in to the existing application framework or if he needs to implement his +own application and wants to use MITK as a software library. Depending on the type of use MITK uses different software libraries, which is +shown in the next figure for overview. -\section OverviewPage_DesignOverview Design Overview +\image html MitkOverview.png "Overview of MITK" + +Like shown above, MITK uses the following libraries. +
    +
  • The Insight Toolkit (ITK), which provides registration and +segmentation algorithms, but is not designed for visualization or interaction. + +
  • The Visualization Toolkit (VTK), which provides powerful visualization capabilities + and low-level support for interaction such as picking methods, rotation, movement and scaling of objects. -The basic design concept of MITK is model-view-controller (MVC). Although some people think MVC is -out-of-date, it is useful in this case (and also we do not really use pure MVC): we have data -(\em model), on which we want to have different @em views and we want to interact with the data -(\em controller), which should result in a simultaneous and consistent update of all views. +
  • The Common Toolkit (CTK), which focuses on DICOM support and a plug-in framework. + +
  • The Qt Cross-platform application and UI framework (Qt) as a framework for UI and application + support. +
+These are the main libraries MITK is based on. For further functionality you can optionally include others, a list can be found \ref thirdpartylibs "here" . + +Based on these libraries, MITK includes the following features:
    -
  • data management classes -
  • -
-... - */ +
  • High level interactions with data. +
  • Specialized medical imaging algorithms (e.g. segmentation) +
  • Support of 3D + t data. +
  • Complete application framework, expandable by plug-ins +
  • Standard tools for medical imaging as default plug-ins (e.g. measurement, segmentation) +
  • Many specialized module for different topics on medical imaging (e.g. diffusion imaging, image guided therapy, live image/ultrasound data processing) + + +*/ diff --git a/Core/Documentation/Doxygen/Concepts/Pipelining.dox b/Core/Documentation/Doxygen/Concepts/Pipelining.dox new file mode 100644 index 0000000000..2f936b0fab --- /dev/null +++ b/Core/Documentation/Doxygen/Concepts/Pipelining.dox @@ -0,0 +1,94 @@ +/** +\page PipelineingConceptPage The Pipelining Concept + +Available Sections: + +-# \ref PipelineingConceptPage_Introduction "Introduction to Pipelining" +-# \ref PipelineingConceptPage_InMITK "Pipelining in MITK" + -# \ref PipelineingConceptPage_Update "The Update() Mechanism" + -# \ref PipelineingConceptPage_Hierarchy "Pipeline Hierarchy" +-# \ref PipelineingConceptPage_WorkWith "Working with Filter" + -# \ref PipelineingConceptPage_Setup "Setting Up a Pipeline" + -# \ref PipelineingConceptPage_Implement "Writing your own Filter" + + +\section PipelineingConceptPage_Introduction Introduction to Pipelining + +Image processing in MITK draws heavily from the pipelining concept, and a clear understaning of it is crucial when developing with MITK. This document will first clarify the general idea behind pipelining and then discuss some MITK specifics that you should know about. + +In the real world, a pipeline connects a source of some kind with a consumer of another. So we identify three key concepts: + +1. The source, which generates data of some kind. +2. The pipeline, which transports the data. Many different pipeline segements can be switched in line to achieve this. +3. The consumer, which uses the data to do something of interest. + +The analogy to real pipelines falls a little short in one point: A physical pipeline would never process it's contents, while in software development a pipeline usually does (this is why they are often dubbed filters as well). One might ask why one shouldn't just implement the processing logic in the consumer onject itself, since it onviously knows best what to do with it's data. The two main reasons for this are reusability and flexibility. Say, one wants to display a bone segmentation from a CT-image. Let's also assume for the sake of this introduction, that this is a simple task. One could build a monolithic class that solves the problem. Or one builds a pipeline between the displaying class and the source. We know that bones are very bright in a CT Scan, so we use a treshold filter, and then a segmentation Filter to solve the problem. + +\image html pipelining_example_ct.png + +Now let's further assume that after successfully selling this new technology to a large firm, we plan to do the same with ultrasound imaging technology. The brithness relations in Ultrasound images are basically the same, but ultrasound images are very noisy, and the contrast is significantly lower. Since we used pipelining, this is no problem: We don't need to change our old segmentation class - we just plug two new filters in front of the pipeline: + +\image html pipelining_example_us.png + +This may seem trivial, but when working with several input streams from many different devices that themselves stem from many different vendors, pipelining can save the day when it comes to broad range support of different specifications. + + + + + + +\section PipelineingConceptPage_InMITK Pipelining in MITK + +\subsection PipelineingConceptPage_Update The Update() Mechanism + +The flow of data inside a pipeline is triggered by only one function call to the consumer, which is Update(). Each part of the pipeline then triggers the Update() method of it's antecessor. Finally, the source creates a new batch of data using it's own GenerateData() method, and notifies its successor that new data is available. The pipeline can then start to process the data until the finished data batch is available as an output of the last Filter. + +\image html pipelining_update.png + +\subsection PipelineingConceptPage_Hierarchy The Pipeline Hierarchy + +Tha base class for all parts of the pipeline except the consumer (which can be of any class) is mitk::Baseprocess. This class introduces the ability to process data, has an output and may have an input as well. You will however rarly work with this class directly. + +\image html pipelining_hierarchy.png + +Several source classes extend BaseProcess. Depending on the type of data they deliver, these are ImageSource, PointSetSource and SurfaceSource. All of these mark the start of a pipeline. + +The filters themselves extend one of the source classes. This may not immediately make sense, but remember that a filter basically is a source with an additional input. + + + +\section PipelineingConceptPage_WorkWith Working with Filter + +\subsection PipelineingConceptPage_Setup Setting Up a Pipeline +\verbatim + // Create Participants + mitk::USVideoDevice::Pointer videoDevice = mitk::USVideoDevice::New("-1", "Manufacturer", "Model"); + TestUSFilter::Pointer filter = TestUSFilter::New(); + // Make Videodevice produce it's first set of Data, so it's output isn't empty + videoDevice->Update(); + // attacht filter input to device output + filter->SetInput(videoDevice->GetOutput()); + // Pipeline is now functional + filter->Update(); +\endverbatim + +\subsection PipelineingConceptPage_Implement Writing Your Own Filter +When writing your first Filter, this is the recommended way to go about: + +- Identify which kinds of Data you require for input, and which for output +- According to the information from step one, extend the most specific subclass of BaseProcess available. E.g. a filter that processes images, should extend ImageToImageFilter. +- Identify how many inputs and how many outputs you require. +- In the constructor, define the number of outputs, and create an output. +\verbatim +//set number of outputs +this->SetNumberOfOutputs(1); + +//create a new output +mitk::Image::Pointer newOutput = mitk::Image::New(); +this->SetNthOutput(0, newOutput); +\endverbatim + +- Implement MakeOutput(). This Method creats a new, clean Output that can be written to. Refer to Filters with similiar task for this. +- Implement GenerateData(). This Method will generate the output based on the input it. At time of execution you can assume that the Data in input is a new set. + +*/ \ No newline at end of file diff --git a/Core/Documentation/Doxygen/Concepts/Properties.dox b/Core/Documentation/Doxygen/Concepts/Properties.dox index 1a9cb7236d..ead012076b 100644 --- a/Core/Documentation/Doxygen/Concepts/Properties.dox +++ b/Core/Documentation/Doxygen/Concepts/Properties.dox @@ -1,226 +1,233 @@ /** -\page PropertiesPage The MITK Property Concept +\page PropertiesPage Properties \section PropertyConcept The Concept Behind MITK Properties Properties belong to a datanode and contain information relevant to the handling of the node by MITK. They provide a place to store additional information which is not part of the actual data, and as such have no reason to be contained within the data/file itself, but might be needed for such things as rendering (e.g. transfer functions) or interaction (e.g. the name of the node). -Propteries can be read an set: +Propteries can be read and set: \code -mitk::ColorProperty::Pointer colorProperty = dynamic_cast(node->GetProperty("color")); +//1: Read a property +mitk::Property::Pointer readProperty = node->GetProperty("color"); //read the property "color" from the data node +mitk::ColorProperty::Pointer colorProperty = dynamic_cast(readProperty); //cast this property to the subtype ColorProperty -node->SetProperty( "IsTensorVolume", mitk::BoolProperty::New( true ) ); +//2: Write a property +mitk::BoolProperty::Pointer newProperty = mitk::BoolProperty::New( true ); //create a new property, in this case a boolean property +node->SetProperty( "IsTensorVolume", newProperty ); //add this property to node with the key "IsTensorVolume" \endcode \section ListOfIndependentProperty A List Of Module Independent Properties +This section lists most of the known properties in MITK according to where they are used. Not every node needs each +(or even close to) of these properties. + \subsection FileManagement File Management
    • path - The physical path the file was loaded from
    • name - The node name in the datamanager
    • selected - Whether the node is selected in the datamanager
    \subsection GenericRenderingProperty Generic Rendering Properties
    • color - Color the surface, grey value image, whatever should be rendered in (default is usually white). There is a special mitk::ColorProperty and you can use the Getter/Setter methods to access it. The color is defined with three values (RGB) in the range between 0.0 and 1.0. \remark If you are inside a mapper you can use the following code to access the color: \code float rgb[3]={1.0f, 1.0f, 1.0f}; GetColor( rgb, BaseRenderer ); \endcode (The BaseRenderer is usually known inside a mapper). \warning -This property will not effect images if you set the property "use color" +This property will not affect images if you set the property "use color" to false. In that case a user-defined lookuptable will be used.
    • in plane resample extent by geometry - Toggles: 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.
    • 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.
    • layer - Controls which image is considered "on top" of another. In the case that two should inhabit the same space, the higher layer occludes lower layer. So far it works for images and pointsets. The layer property applies only for similar datatypes. Pointsets are always rendered in front of images and the layer will not have any effect.
    • levelwindow - In general, grayscale images contain values between 0 and 255. Therefore, the default window boundaries are set respectively. For displaying the image within a certain range, ie. 50 - 200, this property can be used to adjust the minimum and maximum boundary.
    • LookupTable - This property contains a user defined lookuptable, which can be used to map scalar values to color values. Example: If an image contains a value of 128, in the resulting image the passed lookuptable could map this value to red (255, 0, 0). \warning This property will not effect images if you set the property "use color" to true. In that case color properties and a default lookuptable are used. Example for setting up a lookuptable in MITK: \code #include #include #include #include [...] vtkSmartPointer vtkLUT = vtkSmartPointer::New(); vtkLUT->SetRange(100,200); //define your table here vtkLUT->Build(); //pass the table to MITK mitk::LookupTable::Pointer mitkLookupTable = mitk::LookupTable::New(); mitkLookupTable->SetVtkLookupTable(vtkLUT); mitk::LookupTableProperty::Pointer LookupTableProp = mitk::LookupTableProperty::New( mitkLookupTable ); result->SetProperty( "LookupTable", LookupTableProp ); result->SetBoolProperty("use color", false); result->Update(); \endcode
    • opacity - Alpha (or transparency) value of the node/image/surface etc.. The range of the opacity is between 0.0 and 1.0. \remark If you are inside a mapper you can use the following code to access the opacity: \code float opacity=1.0f; GetOpacity( opacity, BaseRenderer ); \endcode (The BaseRenderer is usually known inside a mapper).
    • reslice interpolation - This property takes effect in swivel mode or crosshair rotaiton only. The interpolation modes "Nearest", "Linear", and "Cubic" are available and effect the pixel outcome along the rotated plane.
    • texture interpolation - This property toggles interpolation of the texture. If enabled, edges between image pixels are blurred. If disabled, edges remain sharp.
    • use color - This property toggles the use of a user-defined lookuptable for the rendering. True: use the LUT; False: use the color propery. Example for setting up a lookuptable in MITK: \code #include #include #include #include [...] vtkSmartPointer vtkLUT = vtkSmartPointer::New(); vtkLUT->SetRange(100,200); //define your table here vtkLUT->Build(); //pass the table to MITK mitk::LookupTable::Pointer mitkLookupTable = mitk::LookupTable::New(); mitkLookupTable->SetVtkLookupTable(vtkLUT); mitk::LookupTableProperty::Pointer LookupTableProp = mitk::LookupTableProperty::New( mitkLookupTable ); result->SetProperty( "LookupTable", LookupTableProp ); result->SetBoolProperty("use color", false); result->Update(); \endcode
    • visible - toggle node/image/surface being rendered at all
    \subsection SurfaceRenderingProperties Surface Rendering Properties
    • back color - in 2D, color of the normals outside the surface
    • back normal lenth (px) - in 2D, length of the normals in pixels (When decreasing it the color using the front color is shorter?)
    • color mode - (From VTK) Control how the scalar data is mapped to colors. By default (ColorModeToDefault), unsigned char scalars are treated as colors, and NOT mapped through the lookup table, while everything else is. Setting ColorModeToMapScalars means that all scalar data will be mapped through the lookup table.
    • draw normals 2d - in 2D, toggles the presence of normals
    • front color - in 2D, color of the normals inside the surface
    • front normal lenth (px) - in 2D, length of the normals in pixels (When decreasing it the color using the back color is shorter?)
    • invert normals - in 2D, switch front/back normals
    • line width - in 2D, controls the thickness of the line where the surface intersects the plane (and normals)
    • material.ambientCoefficient - in 3D ambient lighting
    • material.diffuseCoefficient - in 3D scattering of light
    • material.interpolation - Choose which interpolation algorithm to use for surface construction
    • material.representation - Choose the representation to draw the mesh in (Surface, Wireframe, Point Cloud)
    • material.specularCoefficient - in-/decrease non-scattered reflection
    • material.specularPower - control percentage of non-scattered reflection
    • material.wireframeLineWidth - width of the wires if wireframe representation is
    • scalar mode - (From VTK) Control how the filter works with scalar point data and cell attribute data. By default (ScalarModeToDefault), the filter will use point data, and if no point data is available, then cell data is used. Alternatively you can explicitly set the filter to use point data (ScalarModeToUsePointData) or cell data (ScalarModeToUseCellData). You can also choose to get the scalars from an array in point field data (ScalarModeToUsePointFieldData) or cell field data (ScalarModeToUseCellFieldData). If scalars are coming from a field data array, you must call SelectColorArray before you call GetColors. When ScalarMode is set to use Field Data (ScalarModeToFieldData), you must call SelectColorArray to choose the field data array to be used to color cells. In this mode, if the poly data has triangle strips, the field data is treated as the celldata for each mini-cell formed by a triangle in the strip rather than the entire strip.
    • scalar visibility - (From VTK) Turn on/off flag to control whether scalar data is used to color objects.
    • selected - whether the node is selected
    • shader - which shader to use for surface rendering, currently the options are "fixed" and "mitkShaderLightning"
    \subsection VolumeRenderingProperties Volume Rendering Properties
    • TransferFunction - contains transfer function for use in coloring image
    • volumerendering - Should the volume be rendered or not
    • volumerendering configuration - Choice between Composite und MIP
    • volumerendering.cpu.ambient - ambient lighting
    • volumerendering.cpu.diffuse - in-/decrease light dispersion
    • volumerendering.cpu.specular - in-/decrease non-scattered reflection
    • volumerendering.cpu.specular.power - control percentage of non-scattered reflection
    • volumerendering.gpu.ambient - same as cpu with gpu
    • volumerendering.gpu.diffuse - same as cpu with gpu
    • volumerendering.gpu.reducesliceartifacts - Reduce slice artifacts
    • volumerendering.gpu.specular - same as cpu with gpu
    • volumerendering.gpu.specular.power - same as cpu with gpu
    • volumerendering.gpu.usetexturecompression - use texture compression
    • volumerendering.ray.ambient - same as cpu with ray
    • volumerendering.ray.diffuse - same as cpu with ray
    • volumerendering.ray.specular - same as cpu with ray
    • volumerendering.ray.specular.power - same as cpu with ray
    • volumerendering.usegpu - Whether to use the GPU for rendering or not
    • volumerendering.uselod - Whether to use the Level Of Detail mechanism or not
    • volumerendering.usemip - Whether to utilize maximum intensity projection
    • volumerendering.useray - Whether to use raycasting or not
    \remark Uselod can be active with usegpu, usemip, useray, but any of the latter can not be used with another one of them. \subsection PointSetProperties Point Set Properties
    • close contour - Toggles whether the first and the last point of a contour (connecting pieces between following points of a pointset) are connected.
    • contourcolor - Determines the color of the contour (connecting pieces between following points of a pointset). Visible only if "show contour" is active.
    • contoursize - Represents the diameter of the contour (which is kind of a tube between the following points of a pointset). Visible only if "show contour" is active.
    • distance decimal digits - Sets the number of decimal places for the euclidean point to point distance which can be displayed by activating "show distances".
    • point 2D size - The positions of points in the 2D view are represented by crosses. "point 2D size" determines the size of this crosses.
    • point line width - The positions of points in the 2D view are represented by crosses. "point line width" determines the thickness of this crosses.
    • pointsize - The positions of points in the 3D view are represented by spheres. "pointsize" determines the diameter (size) of this spheres.
    • selectedcolor - Sets the color for selected points from a pointset.
    • show angles - If "show contour" is active the angles between two contour parts can be shown.
    • show contour - Connects following points of a pointset by drawing connecting pieces between this points.
    • show distance lines - Shows all angles and lines of the contour (in 2D views) even if they are not on the view's current slice.
    • show distances - Draws lines between following points (in 2D views) and displays the euclidean distance between this points.
    • show points - Toggles if the points are visible or not in the view.
    • updateDataOnRender - If "true" the pointset is updated before rendering. If the pointset is part of a filter pipeline this also causes an update to the pipeline which sometimes may be not desired so it can be switched of by setting it to false.
    Information on properties not in this list can be found in the appropriate module. \subsection PropertiesPageSeeAlso See Also
    • \subpage PlanarPropertiesPage
    • \subpage SegmentationPropertiesPage
    */ diff --git a/Core/Documentation/Doxygen/Concepts/QVTKRendering.dox b/Core/Documentation/Doxygen/Concepts/QVTKRendering.dox index 1f1f070925..6b59d650e6 100644 --- a/Core/Documentation/Doxygen/Concepts/QVTKRendering.dox +++ b/Core/Documentation/Doxygen/Concepts/QVTKRendering.dox @@ -1,107 +1,83 @@ /** -\page QVTKRendering Rendering in MITK by means of the QT-VTK widget - -\brief This page describes the MITK rendering mechanism switching to the QTVTK widget. MITK releases with version > 0.8 use this new rendering pipeline. Several changes in contrast to the established old rendering pipeline are explained in the following. - - +\page QVTKRendering Rendering Concept +The MITK rendering pipeline is derived from the VTK rendering pipeline. \section QVTKRendering_Pipeline_VTK VTK Rendering Pipeline \image html RenderingOverviewVTK.png "Rendering in VTK" -\li In VTK, the vtkRenderWindow coordinates the rendering process. Several vtkRenderers may be associated to one vtkRenderWindow. -\li All visible objects, which can exist in a rendered scene (2D and 3D scene), inherit from vtkProp. -\li A vtkPropAssembly is an assembly of several vtkProps, which appears like one single vtkProp. -\li MITK uses a new interface class, the "vtkMitkRenderProp", which is inherited from vtkProp. Similar to a vtkPropAssembly, all MITK rendering stuff is performed via this interface class. -\li Thus, the MITK rendering process is completely integrated into the VTK rendering pipeline. From VTK point of view, MITK renders like a custom vtkProp object. +In VTK, the vtkRenderWindow coordinates the rendering process. Several vtkRenderers may be associated to one vtkRenderWindow. +All visible objects, which can exist in a rendered scene (2D and 3D scene), inherit from vtkProp (or any subclass e.g. vtkActor). +A vtkPropAssembly is an assembly of several vtkProps, which appears like one single vtkProp. +MITK uses a new interface class, the "vtkMitkRenderProp", which is inherited from vtkProp. Similar to a vtkPropAssembly, all MITK rendering stuff is performed via this interface class. +Thus, the MITK rendering process is completely integrated into the VTK rendering pipeline. From VTK point of view, MITK renders like a custom vtkProp object. More information about the VTK rendering pipeline can be found at http://www.vtk.org and in the several VTK books. \section QVTKRendering_Pipeline_MITK MITK Rendering Pipeline -In contrast to the former MITK rendering pipeline, the new process is tightly connected to VTK, which makes it straight forward and simple. -In consequence, several MITK classes have been dropped out: - -\li Qmitk::SelectableGLWidget and all inheritors -\li mitk::RenderWindow -\li mitk::VtkRenderWindow and all inheritors -\li mitk::OpenGLRenderer -\li mitk::SimpleTextRendering - -Instead, we use the above mentioned "vtkMitkRenderProp" in conjunction with a new mitk::VtkPropRenderer for integration into the VTK pipeline. Also, the QmitkRenderWindow does not inherit -from mitk::RenderWindow, but from the QVTKWidget, which is provided by VTK. +This process is tightly connected to VTK, which makes it straight forward and simple. We use the above mentioned "vtkMitkRenderProp" in conjunction with the mitk::VtkPropRenderer for integration into the VTK pipeline. The QmitkRenderWindow does not inherit from mitk::RenderWindow, but from the QVTKWidget, which is provided by VTK. The main classes of the MITK rendering process can be illustrated like this: \image html qVtkRenderingClassOverview.png "Rendering in MITK" -A render request to the vtkRenderWindow does not only update the VTK pipeline, but also the MITK pipeline. However, the mitk::RenderingManager still coordinates the rendering update behaviour. -Update requests should be sent to the RenderingManager, which then, if needed, will request an update of the overall vtkRenderWindow. The vtkRenderWindow then starts to call the Render() function -of all vtkRenderers, which are associated to the vtkRenderWindow. Currently, MITK uses specific vtkRenderers (outside the standard MITK rendering pipeline) for purposes, like displaying a gradient -background (mitk::GradientBackground), displaying video sources (QmitkVideoBackround and mitk::VideoSource), or displaying a (department) logo (mitk::ManufacturerLogo), etc. Despite these specific -renderers, a kind of "SceneRenderer" is member of each QmitkRenderWindow. This vtkRenderer is associated with the custom vtkMitkRenderProp and is responsible for the MITK rendering. +A render request to the vtkRenderWindow does not only update the VTK pipeline, but also the MITK pipeline. However, the mitk::RenderingManager still coordinates the rendering update behavior. +Update requests should be sent to the RenderingManager, which then, if needed, will request an update of the overall vtkRenderWindow. The vtkRenderWindow then starts to call the Render() function of all vtkRenderers, which are associated to the vtkRenderWindow. Currently, MITK uses specific vtkRenderers (outside the standard MITK rendering pipeline) for purposes, like displaying a gradient background (mitk::GradientBackground), displaying video sources (QmitkVideoBackround and mitk::VideoSource), or displaying a (department) logo (mitk::ManufacturerLogo), etc.. +Despite these specific renderers, a kind of "SceneRenderer" is member of each QmitkRenderWindow. This vtkRenderer is associated with the custom vtkMitkRenderProp and is responsible for the MITK rendering. -A sequence diagramm, which illustrates the actions after calling the Render() function of the MITK-Scene vtkRenderer is shown below: +A sequence diagram, which illustrates the actions after calling the Render() function of the MITK-Scene vtkRenderer is shown below: \image html qVtkRenderingSequence.png "Sequence overview MITK scene rendering" +\section QVTKRendering_programmerGuide User Guide: Programming hints for rendering related stuff (in plugins) +\li The QmitkRenderWindow can be accessed like this: this->GetRenderWindowPart()->GetRenderWindow("transversal"); +\li The vtkRenderWindow can be accessed like this: this->GetRenderWindowPart()->GetRenderWindow("transversal")->GetVtkRenderWindow(); +\li The mitkBaseRenderer can be accessed like this: mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart()->GetRenderWindow("sagittal")->GetRenderWindow()); +\li An update request of the overall QmitkStdMultiWidget can be performed with: this->GetRenderWindowPart()->GetRenderingManager()->RequestUpdateAll(); +\li A single QmitkRenderWindow update request can be done like this: this->GetRenderWindowPart()->GetRenderingManager()->RequestUpdate(this->GetRenderWindowPart()->GetRenderWindow("transversal")->GetVtkRenderWindow()); - -\section QVTKRendering_programmerGuide User Guide: Changes in programming of rendering related stuff - -\li Within a functionality the vtkRenderWindow can be accessed like this: vtkRenderWindow* vtkRenWin = m_MultiWidget->mitkWidget4->GetRenderWindow(); -\li Within a functionality the mitkBaseRenderer can be accessed like this: mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget4->GetRenderWindow()); -\li An update request of the overall QmitkStdMultiWidget can be performed with: m_MultiWidget->RequestUpdate(); -\li An update of the overall QmitkStdMultiWidget can be forced with: m_MultiWidget->ForceImmediateUpdate(); -\li A single QmitkRenderWindow update request can be done like this: mitk::RenderingManager::GetInstance()->RequestUpdate(m_MultiWidget->mitkWidget4->GetRenderWindow()); -\li A single QmitkRenderWindow update can be forced like this: mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(m_MultiWidget->mitkWidget4->GetRenderWindow()); -\li Getting a BaseRenderer by the widget name can be done like this: mitk::BaseRenderer::GetByName("mitkWidget1"); +\note The usage of ForceImmediateUpdateAll() is not desired in most common use-cases. \subsection QVTKRendering_distinctRenderWindow Setting up a distinct Rendering-Pipeline It is sometimes desired to have one (or more) QmitkRenderWindows that are managed totally independent of the 'usual' renderwindows defined by the QmitkStdMultiWidget. This may include the data that is rendered as well as possible interactions. In order to achieve this, a set of objects is needed: \li mitk::RenderingManager -> Manages the rendering \li mitk::DataStorage -> Manages the data that is rendered \li mitk::GlobalInteraction -> Manages all interaction \li QmitkRenderWindow -> Actually visualizes the data The actual setup, respectively the connection, of these classes is rather simple: \code // create a new instance of mitk::RenderingManager mitk::RenderingManager::Pointer renderingManager = mitk::RenderingManager::New(); // create new instances of DataStorage and GlobalInteraction mitk::DataStorage::Pointer dataStorage = mitk::DataStorage::New(); mitk::GlobalInteraction::Pointer globalInteraction = mitk::GlobalInteraction::New(); // add both to the RenderingManager renderingManager->SetDataStorage( dataStorage ); renderingManager->SetGlobalInteraction( globalInteraction ); // now create a new QmitkRenderWindow with this renderingManager as parameter QmitkRenderWindow* renderWindow = new QmitkRenderWindow( parent, "name", renderer, renderingManager ); \endcode That is basically all you need to setup your own rendering pipeline. Obviously you have to add all data you want to render to your new DataStorage. If you want to interact with this renderwindow, you will also have to add additional Interactors/Listeners. -Note: - -\li Dynamic casts of a mitk::BaseRenderer class to an OpenGLRenderer (or now, to an VtkPropRenderer) should be avoided. The "MITK Scene" vtkRenderer and the vtkRenderWindow as well, are -therefore now included in the mitk::BaseRenderer. - - - +\note Dynamic casts of a mitk::BaseRenderer class to an OpenGLRenderer (or now, to an VtkPropRenderer) should be avoided. The "MITK Scene" vtkRenderer and the vtkRenderWindow as well, are therefore now included in the mitk::BaseRenderer. */ diff --git a/Core/Documentation/Doxygen/Concepts/RenderingTests.dox b/Core/Documentation/Doxygen/Concepts/RenderingTests.dox index 2c60491f2a..2dbe0bd4ec 100644 --- a/Core/Documentation/Doxygen/Concepts/RenderingTests.dox +++ b/Core/Documentation/Doxygen/Concepts/RenderingTests.dox @@ -1,97 +1,97 @@ namespace mitk{ /** \page RenderingTests Automatic Rendering Tests Available sections: -# \ref RenderingTests_WhatIsARenderingTest "What is an automatic rendering test?" -# \ref RenderingTests_HowToCreateATest "How to create a rendering test" \section RenderingTests_WhatIsARenderingTest What is an automatic rendering test? An automatic rendering test is a powerful tool to test rendering results automatically via dashboard. Regarding rendering lots of different sources influence the output on the screen (e.g. different mappers, renderes, camera settings or the algorithm creating the data). Thus, during the rendering process of an image many different classes are involved and can have impact on the output. A minor change in an important class (e.g. mitkVtkPropRenderer) can have major impact on the actual rendering. An automatic rendering test takes an arbitrary object as input (e.g. image, surface, point set), renders this into an mitkRenderWindow, makes a screen shot of that renderwindow and finally compares that screen shot to a given reference. Of course, the reference has to be defined by the user. Internally, a VTK test method is used to compare both screen shots and measure differences. In case of failure, a difference can be generated to show exactly which pixels are rendered incorrectly. Implementing automatic rendering tests for algorithms ensures that algorithms deliver the same output as they used to do in previous version of MITK. \section RenderingTests_HowToCreateATest How to create your own automatic rendering test To create an automatic rendering test you should use an existing test as example (e.g. mitkImageVtkMapper2DTest). -1. Adding the test to CMake +\subsection RenderingTests_HowToCreateATest_Sub1 Adding the test to CMake Like adding any test with parameters to CMake, you have to add a custom test to the files.cmake and the corresponding CMakeLists.txt: For instance a test for the mitkImageVtkMapper2D has to be added like this: files.cmake \code set(MODULE_CUSTOM_TESTS ... mitkImageVtkMapper2D.cpp ) \endcode CMakeLists.txt \code mitkAddCustomModuleTest(mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2D #custom name of the test and executable ${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/rgbaImage640x480REF.png #corresponding reference screenshot ) \endcode -The first parameter defines a name for the test on the dashboard. This is a feature to distinguish +The first parameter defines a custom name for the test. This is a feature to distinguish between tests with different inputs. In this example the test is named mitkImageVtkMapper2D_rgbaImage640x480 to show that this test is using the test image rbgaImage640x480 as input. -The next parameters sets test name (i.e. the name of the test class). Here: mitkImageVtkMapper2D. +The next parameters sets test executable name (i.e. the name of the test class). Here: mitkImageVtkMapper2D. The next parameter(s) are used to pass the input to the test. For instance, it is possible to set multiple objects as input for a test (e.g. /path/to/img1.jpg /path/to/img2.pic /path/to/pointset.mps). All test data for core tests should be placed into the MITK-DATA repository inside the folder: -${MITK_DATA_DIR}/RenderingTestData/ -It is possible to create another folders for other modules/bundles. +${MITK_DATA_DIR}/RenderingTestData/. It is possible to create other folders for different modules or bundles. The option -V defines the path to the reference screen shot and is internally used by VTK. The reference -screen shot is highly important and has to be triple-checked if is correct!!! The +screen shot is highly important and has to be proven if is correct. The mitkRenderingTestHelper offers means to capture a screen shot of a renderwindow. Capturing a reference screen shot should happen just once and NOT be a permanent part of the test. It is also possible to set the option -T /path/to/directory/. This option is internally used by VTK -to save a difference image. This is meant for debugging and should not be used on the dashboard. +to save a difference image. This is meant for debugging and should not be used for the final implemenation of a test. -2. Coding the test +\subsection RenderingTests_HowToCreateATest_Sub2 Coding the test + Writing the test code is pretty straight forward. In the example of the mitkImageVtkMapper2DTest the input parameters are added to a datastorage and rendered into a render window via the mitkRenderingTestHelper. Last, the vtkTesting macro is called to compare the given reference to the data rendered in the renderwindow: \code int retVal = vtkRegressionTestImage( renderingHelper.GetVtkRenderWindow() ); //retVal meanings: (see VTK/Rendering/vtkTesting.h) //0 = test failed //1 = test passed //2 = test not run //3 = something with vtkInteraction MITK_TEST_CONDITION( retVal == 1, "VTK test result positive" ); \endcode If the content of the previously filled renderwindow does not equal the reference, the test will fail. Feel free to modify the data before rendering. E.g. create a surface from the loaded image and render that surface afterwards or add compute QBalls for an image and render those. Happy testing! */ } diff --git a/Core/Documentation/Doxygen/Concepts/TestsGeneral.dox b/Core/Documentation/Doxygen/Concepts/TestsGeneral.dox new file mode 100644 index 0000000000..4e0992f1a5 --- /dev/null +++ b/Core/Documentation/Doxygen/Concepts/TestsGeneral.dox @@ -0,0 +1,93 @@ +/** +\page GeneralTests General: Tests in MITK + +Two types of standardizes automated tests are provided by MITK. These types are unit tests and rendering tests . This section will describe unit testing in MITK, while more information on rendering tests can be found in the section \ref RenderingTests. + +-# \ref GeneralTestsSection1 "Basic Information on Unit Testing" +-# \ref GeneralTestsSection2 "Adding a Unit Test to MITK" + -# \ref GeneralTestsSection2_1 "Structure your test method" +-# \ref GeneralTestsSection3 "Run a Unit Test with MITK" +-# \ref GeneralTestsSection4 "MITK Testing Macros" + +\section GeneralTestsSection1 Basic Information on Unit Testing + +The basic idea about unit testing is to write a test for every class (unit) of your software. The test should then run all methods of the tested class and check if the results are correct. Thus, the testing environment for MITK allows for simple implementation of corresponding test methods for MITK classes. + +The build system of MITK generates a test driver which includes all tests that have been added to the project. Alternativly you can run MITK tests by using the program ctest. This is the way all MITK tests run on the continous dart clients of MITK. The results of these runs can be found at http://cdash.mitk.org. + +The following sections will explain how to write your own tests with MITK and how to run them. The last section will describe the testing macros of MITK in detail. + +\section GeneralTestsSection2 Adding a Unit Test to MITK + +To add a test, you simply need to create a new file "mitkMyTest.cpp" in the folder "Testing" of the software module to which you want to add the test. The file must contain such a method: \code int mitkMyTest(int argc, char* argv[]) \endcode This method is automatically called by the test driver. A header file to this cpp file is not needed. An example for a simple test method is shown next. + +\code +int mitkMyTest(int argc, char* argv[]) +{ + MITK_TEST_BEGIN("MyTest"); + MITK_TEST_CONDITION_REQUIRED(true,"Here we test our condition"); + MITK_TEST_END(); +} +\endcode + +Additionaly you've to add the test file to the files.cmake of your testing directory. In the files.cmake you'll find a command "SET(MODULE_TESTS [...])", this is where you've to add the filename of your test. This will add your test to the test driver. A possible files.cmake where a test have already been added is shown next. +\code +SET(MODULE_TESTS + mitkMyTest.cpp + [...] +) +\endcode +Finally you only have to run cmake one your project and then compile the test driver. Don't forget to turn "BUILD_TESTING" on, when running cmake. + +\subsection GeneralTestsSection2_1 Structure your test method + +When implementing more complex test methods you might want to structure them, e.g. by using sub methods. This is also possible. You can create a test class and add static methods which can then be called in your main test method. This is a simple way to keep a clear structure in your test file. An example for such a structured test file is shown next. + +\code +//first: your test class with static methods, if it comes before the main test method +// like shown here, you still don't need a header and you can keep your code as +// simple as possible +class mitkMyTestClass +{ +public: + + static void TestInstantiation() + { + //do your instantiation test here + } + + static void TestMethod1() + { + //do a test of method 1 here + } +};//do not forget the semicolon at this place! + +//secondly: your main test method +int mitkMyTest(int argc, char* argv[]) +{ + MITK_TEST_BEGIN("MyTest"); + mitkMyTestClass.TestInstantiation(); + mitkMyTestClass.TestMethod1(); + MITK_TEST_END(); +} +\endcode + +\section GeneralTestsSection3 Run a Unit Test with MITK + +After building and compiling MITK, there are two ways to run your test. Firstly, you can run your test using the executable test driver. Secondly, you can use the external program ctest. + +If you use the test driver, you only need to start the executable. If you start it without parameters, it will then give you an overview of all tests which are included in this test driver and you can choose one by typing a number. + +\note This way you can not set additional input, such as images. + +Alternatively you can give your test driver the name of your test as parameter. Then this test will be started directly. You are also allowed to give more parameters which are then given to the main test method as parameters (argv[]). + +If you want to use ctest instead of the test driver you need to start a command line, go to the binary directory of MITK and call ctest. To avoid errors, check if your path variable contains all relevant paths to start MITK. + +\section GeneralTestsSection4 MITK Testing Macros + +MITK offers testing macros to simplify testing, e.g. to test your testing conditions. These macros can be found in the header mitkTestingMacros.h . + + + +*/ diff --git a/Core/Documentation/Doxygen/Concepts/images/introduction/MitkOverview.png b/Core/Documentation/Doxygen/Concepts/images/introduction/MitkOverview.png new file mode 100644 index 0000000000..e9dd638ac6 Binary files /dev/null and b/Core/Documentation/Doxygen/Concepts/images/introduction/MitkOverview.png differ diff --git a/Core/Documentation/Doxygen/Concepts/images/mitkimage/mitkimagehierarchy.png b/Core/Documentation/Doxygen/Concepts/images/mitkimage/mitkimagehierarchy.png new file mode 100644 index 0000000000..b73b652c63 Binary files /dev/null and b/Core/Documentation/Doxygen/Concepts/images/mitkimage/mitkimagehierarchy.png differ diff --git a/Core/Documentation/Doxygen/Concepts/images/mitkimage/mitkimagememory.png b/Core/Documentation/Doxygen/Concepts/images/mitkimage/mitkimagememory.png new file mode 100644 index 0000000000..706532efcc Binary files /dev/null and b/Core/Documentation/Doxygen/Concepts/images/mitkimage/mitkimagememory.png differ diff --git a/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_example_ct.png b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_example_ct.png new file mode 100644 index 0000000000..1583a91853 Binary files /dev/null and b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_example_ct.png differ diff --git a/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_example_us.png b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_example_us.png new file mode 100644 index 0000000000..00f1c88427 Binary files /dev/null and b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_example_us.png differ diff --git a/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_hierarchy.png b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_hierarchy.png new file mode 100644 index 0000000000..f469bcd0fa Binary files /dev/null and b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_hierarchy.png differ diff --git a/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_update.png b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_update.png new file mode 100644 index 0000000000..103c5f8e0e Binary files /dev/null and b/Core/Documentation/Doxygen/Concepts/images/pipelining/pipelining_update.png differ diff --git a/Documentation/Doxygen/DeveloperManual/SupportedPlatforms.md b/Documentation/Doxygen/DeveloperManual/SupportedPlatforms.md index 3a59b5239e..2b74733e8a 100644 --- a/Documentation/Doxygen/DeveloperManual/SupportedPlatforms.md +++ b/Documentation/Doxygen/DeveloperManual/SupportedPlatforms.md @@ -1,51 +1,52 @@ Supported Platforms {#SupportedPlatformsPage} =================== MITK is a cross-platform framework and is available for the following platforms: - Windows - Linux/X11 - Mac OS X Supported Platforms Details --------------------------- The MITK team provides support for the most frequently used platforms and continuously runs testing procedures to ensure compatibility. Due to the large amount of possible combinations of operating systems and compiler versions, we divide platform support into two test categories (Tier 1 and Tier 2). Although MITK may be built on a range of platform-compiler combinations, only a subset of these are actively support by the MITK development team. Tier 1 Platforms ---------------- All Tier 1 platforms are continuously tested by our unit test suite and other internal testing procedures. Errors or bugs discovered in these platforms are prioritized and corrected as soon as possible. | Platform | Compilers | ----------------------------------- | ----------------------------- -| Ubuntu Linux 11.04 (64-bit) | As provided by Ubuntu -| Microsoft Windows 7 (32 and 64-bit) | MSVC 2008 SP1 +| Ubuntu Linux 12.04 (64-bit) | gcc as provided by Ubuntu +| Microsoft Windows 7 (64-bit) | MSVC 2008 SP1 | Apple Mac OS X 10.7 "Lion" | LLVM-GCC as provided by Apple Tier 2 Platforms ---------------- Tier 2 platforms may or may not be tested on a regular basis. Some Tier 2 platforms are used by individual members of the MITK development team on a daily basis and some only receive occasional testing. While we strive to support these platforms, MITK users should note that errors may be present in released versions as well as in the current master branch. | Platform | Compilers | ---------------------------------- | ----------------------------- -| Ubuntu Linux 11.04 (32-bit) | As provided by Ubuntu -| Microsoft Windows XP (32-bit) | MSVC 2008 SP1 -| Apple Mac OS X 10.6 "Snow Leopard" | LLVM-GCC as provided by Apple +| Ubuntu Linux 11.04 (64-bit) | gcc as provided by Ubuntu +| Ubuntu Linux 12.04 (64-bit) | Clang as provided by Ubuntu +| Microsoft Windows 7 (32-bit) | MSVC 2008 SP1 +| Apple Mac OS X 10.7 "Lion" | Clang as provided by Apple (XCode) All platforms not listed above are not officially supported by the MITK team. However, we will happily accept contributions to improve support for other platforms. diff --git a/Documentation/Doxygen/Modules/DataTreeAndIterators.dia b/Documentation/Doxygen/Modules/DataTreeAndIterators.dia deleted file mode 100644 index 36154f9f6b..0000000000 Binary files a/Documentation/Doxygen/Modules/DataTreeAndIterators.dia and /dev/null differ diff --git a/Documentation/Doxygen/Modules/DataTreeAndIterators.png b/Documentation/Doxygen/Modules/DataTreeAndIterators.png deleted file mode 100644 index ed59e55435..0000000000 Binary files a/Documentation/Doxygen/Modules/DataTreeAndIterators.png and /dev/null differ diff --git a/Documentation/Doxygen/Modules/DataTreeNodeFactoryMechanism.png b/Documentation/Doxygen/Modules/DataTreeNodeFactoryMechanism.png deleted file mode 100644 index aaa32efca7..0000000000 Binary files a/Documentation/Doxygen/Modules/DataTreeNodeFactoryMechanism.png and /dev/null differ diff --git a/Documentation/Doxygen/Modules/DataTreeNodeFactoryMechanism.vsd b/Documentation/Doxygen/Modules/DataTreeNodeFactoryMechanism.vsd deleted file mode 100644 index 1a52334470..0000000000 Binary files a/Documentation/Doxygen/Modules/DataTreeNodeFactoryMechanism.vsd and /dev/null differ diff --git a/Documentation/Doxygen/Modules/ModuleApplications.dox b/Documentation/Doxygen/Modules/ModuleApplications.dox deleted file mode 100644 index f40b1724ce..0000000000 --- a/Documentation/Doxygen/Modules/ModuleApplications.dox +++ /dev/null @@ -1,14 +0,0 @@ -/** - \defgroup Application Application-level Classes - - This category includes classes on the application-level, e.g., pre-defined widgets. - - It also includes the concept to organize an application in so-called "functionalities" (subclasses of - QmitkFunctionality). Each functionality can have its own workspace area (or share one workspace - with other functionalities) and a control area. Functionalities are added to an instance of - QmitkFctMediator, which organizes the switching between the functionalities. This is a - "toolbox-in-the-large", e.g., for combining and switching between one functionality for - segmentation and another for registration. - -*/ - diff --git a/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.dia b/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.dia deleted file mode 100644 index 97e71347e2..0000000000 Binary files a/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.dia and /dev/null differ diff --git a/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.dox b/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.dox deleted file mode 100644 index 22b31a6e07..0000000000 --- a/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.dox +++ /dev/null @@ -1,119 +0,0 @@ -/** - \defgroup Functionalities Functionalities - \ingroup Application - - - -This is a preliminary version and not yet official documentation. - - - -Available sections: - - \ref overviewApplicationFunctionalities - - \ref inthisgroupApplicationFunctionalities - - \ref notinthisgroupApplicationFunctionalities - - \ref implementApplicationFunctionalities - - \ref seealsoApplicationFunctionalities - - \ref futureApplicationFunctionalities - - - \section overviewApplicationFunctionalities Rationale and overview - -Functionalities facilitate the structured combination of modules for specific tasks. -They combine a user interface with algorithmic function. A functionality consists of (compare screen-shot) - -\li an identification (name of the functionality, icon, tool-tip, ...) -\li a workspace area, where the main interaction with data objects is taking place -\li a control area containing GUI elements to set parameters of the used algorithms -\li the algorithmic implementation (not visible to the user) - -Communication between functionalities is largely based on the data tree. Each -functionality accesses the data objects contained in the tree, changes them and/or -creates new data objects. Other functionalities can continue to work on the changed -and/or newly created data tree entries. By that, functionalities can communicate -without becoming dependent on each other. - -\image html PartsOfAFunctionaliy.jpg "The visible parts of a functionality. The algorithmic implementation, which is also part of a functionality has no visual representation." - - - \subsection inthisgroupApplicationFunctionalities What belongs into this group - -A functionality should comprise a complete set of controls that are necessary to -complete a certain task, where the task should be on a reasonably high level that makes -sense for the user of the application. -Before you consider a new functionality, check if the feature you want to implement could -instead be implemented as a \ref Widgets "widget" that can be used in different contexts. -Possible examples for functionalities are: - - - "Segmentation": a tool for segmentation that comprises several controls (selection of the - image to be segmented, list of available segmentations, selection of tools for segmentation) - - "Movie generation": when you have a scene ready for presentation and want to generate - a movie from it, with animation through slices, rotation in 3D, stepping through time, etc. - - "Vessel editing": a complete set of tools for working with a vessel tree (creating from segmentation, - editing, annotation, searching, etc. - - \subsection notinthisgroupApplicationFunctionalities What does not belong into this group - -A set of controls that could possibly be of use in more than one context should not -be a functionality, but rather a widget. Widgets are good for sub-tasks from the user's -point of view. -Some examples for things that should not be implemented as functionality - - - a control that allows to select an image - - a control that lists available segmentations - - a control that shows information about certain scene elements - - \section implementApplicationFunctionalities Practical issues - -Technically a functionality is a derivative class of QmitkFunctionality. The -functionality object gets an mitk::DataTreeIteratorBase when it is created and -has to return on request QWidgets for its main widget and its control area, and -a QAction (Combination of icon, description and keyboard shortcut) for its identification. - -The functionality can own and use arbitrary objects to create and manipulate -objects of the data tree. - -A functionality is meant to exist only with the context of an application. It is -the application's task to create an environment, where it can place the main and -control widgets of functionalities, and to create a data tree. - -Since the intention is to combine several functionalities in one application, -there is a "functionality mediator" of class QmitkFctMediator. A single instance -of this object is created by the application and it is told about every -functionality that should be available for the application. It is then the -mediator's job to arrange the correct switching between functionalities, which -involves calling the functionalities' Activated() and Deactivated() methods and -hiding/showing the correct main and control widgets for each functionality. - -The diagram below shows the important classes related to a functionality. - -\image html ModuleApplicationsFunctionalities.png "Classes related to a functionality" - - \section seealsoApplicationFunctionalities See also - - Documentation you should read - - \li \ref Widgets - \li \ref SampleApp - - Classes you should be aware of: - - \li QmitkFunctionality - \li QmitkFctMediator - \li mitk::DataTree - - If you want to enhance the SampleApp, you should read the directions on \ref NewFunctionalityPage "how to create a new functionality". - - \section futureApplicationFunctionalities Plans for the future - -One idea for future development is to change the current use of functionalities in a way that -different functionalities can be active at the same time. This would allow the user to select -and arrange a sensible set of functionalities for his or her specific task at run-time. This would -allow for more flexible applications. - -Implementing this idea would probably require changes to existing functionalities, since currently -authors of functionalities can assume that nothing else is manipulating the data tree when their -functionality is active. - -*/ - diff --git a/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.png b/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.png deleted file mode 100644 index 43488f8500..0000000000 Binary files a/Documentation/Doxygen/Modules/ModuleApplicationsFunctionalities.png and /dev/null differ diff --git a/Documentation/Doxygen/Modules/ModuleApplicationsWidgets.dox b/Documentation/Doxygen/Modules/ModuleApplicationsWidgets.dox deleted file mode 100644 index c1548da105..0000000000 --- a/Documentation/Doxygen/Modules/ModuleApplicationsWidgets.dox +++ /dev/null @@ -1,7 +0,0 @@ -/** - \defgroup Widgets Widgets - \ingroup Application - - This category includes widget classes (mainly for Qt). - -*/ diff --git a/Documentation/Doxygen/Modules/ModuleDataManagement.dox b/Documentation/Doxygen/Modules/ModuleDataManagement.dox deleted file mode 100644 index e6f919743f..0000000000 --- a/Documentation/Doxygen/Modules/ModuleDataManagement.dox +++ /dev/null @@ -1,27 +0,0 @@ -/** - -\defgroup DataManagement Data Management Classes -\ingroup Core - -\brief This category includes the data classes themselves (images, surfaces, -etc.) as well as management classes to organize the data during run-time in a -tree structure, called the data tree (mitk::DataTree). - -\section DataManagementOverview Overview - -There are two things in MITK: data itself and the organization of data in a -tree. The data tree is used for communication between different parts of an -application: The renderer uses it to find the things to render, functionalities -use it to find "all images" or "all surfaces", and the event handling uses it to -distribute events to interactors. - -For a list of all possible kind of data, look at \ref Data. \ref IO lists -classes that help to read and write data sets. - -For an explanation of the data tree, iterators and tree nodes, refer to -\ref DataTree. - -\ref Geometry groups all classes that describe the spatial/temporal position and -orientation of data sets. - -*/ diff --git a/Documentation/Doxygen/Modules/ModuleDataTree.dox b/Documentation/Doxygen/Modules/ModuleDataTree.dox deleted file mode 100644 index 2db8d6bf83..0000000000 --- a/Documentation/Doxygen/Modules/ModuleDataTree.dox +++ /dev/null @@ -1,58 +0,0 @@ -/** - -\defgroup DataTree Data Tree Classes -\ingroup DataManagement - -\brief This subcategory includes the data tree classes, which organizes the data during run-time in a tree structure. - -\section overviewDataTree Overview - -All data objects, like images or surfaces, are contained in DataNodes. These -DataNodes describe the data itself (mitk::BaseData), how they can be rendered in -2D or 3D (a list of mitk::Mapper), what mitk::Interactor is associated with it, and a list -of arbirary properties (name, visibility, opacity, etc.). Information about the -position of a data object in space/time is stored in a Geometry, which is -attached to the data object itself, not to the node. - -Tree nodes are organized in a data tree for communication between different -parts of an application, e.g. functionalities and the rendering process. -mitk::DataTree is just a typedef to the ITK tree container class. To access and -modify data trees, we use iterators (mitk::DataTreeIteratorBase). - -To clarify the relation between the tree, its nodes and iterators, have a look -at the following diagram. The main point of this diagram is, that you need an -iterator to change the tree. A tree node does not know about the tree! -Once again: a tree node does not know about the tree! - -\image html DataTreeAndIterators.png "The data tree, its nodes and iterators" - -To modify a tree, you can use an iterator's Add(), Set() or Remove() methods -(among others): - -\code - mitk::DataTreeIteratorBase* iterator = GetAnIterator(); //whatever - mitk::DataNode* node = GetMyNode(); - mitk::DataNode* node2 = GetMyNode(); - - if ( iterator.IsNotNull() ) - { - iterator->Add( node ); // add as child to the current position - - iterator->Set( node2 ); // set the node of the current position - - iterator->Remove(); // remove the current position (and all its children) - } - -\endcode - -To access the data behind an iterator, use Get(): - -\code - if ( iterator.IsNotNull() ) - { - mitk::DataNode* datanode = iterator->Get(); - mitk::BaseData* = datanode->GetData(); - } -\endcode - -*/ diff --git a/Documentation/Doxygen/Modules/ModuleDatamanagementIO.dox b/Documentation/Doxygen/Modules/ModuleDatamanagementIO.dox deleted file mode 100644 index c0c32af17a..0000000000 --- a/Documentation/Doxygen/Modules/ModuleDatamanagementIO.dox +++ /dev/null @@ -1,258 +0,0 @@ -/** - \defgroup IO IO Classes - \ingroup DataManagement - - \brief This subcategory includes the IO classes to read or write data objects. - - Available sections: - - \ref DataNodeFactory - - \ref XMLWriterAndXMLReader - - \ref XMLFile - - \ref XMLWriter - - \ref XMLWriterCodeSnippets - - \ref XMLReader - - \ref XMLReaderCodeSnippets - - \section DataNodeFactory DataNodeFactory - - The mitk::DataNodeFactory creates instances of mitk::DataNodes filled with data read from a given file. - This class reads files, creates an appropriate mitk::BaseData and adds the BaseData to a mitk::DataNode. - This filter may produce one or more outputs (i.e. mitk::DataNodes). The number of generated nodes can be retrieved by a call of GetNumberOfOutputs(). - If you want to add a new file type, you have to register the factory of the file reader in the class mitk::BaseDataIO. - - \image html DataNodeFactoryMechanism.png "A short overview of the factory mechanism of the DataNodeFactory." - - The mitk::DataNodeFactory calls the mitk::BaseDataIO. - BaseDataIO registers the defined factories of the file readers. - After registration each registered factory will be asked if it can read the file specified by the filename. - If the asked factory can read the file it instantiates the adequate reader. The reader reads the file and returns as output a pointer of BaseData. - It is possible that a reader has more than one output. The BaseData is stored in a vector of BaseData. - The pointer of this vector is returned to the DataNodeFactory. A node is created for each element of the vector. - - Between BaseDataIO and the ObjectFactoryBase there is still an adapter (mitk::IOAdapter). - The instantiated reader returns a pointer of mitk::BaseProcess. In the class BaseProcess there couldn't be defined the necessary function CanReadFile(). - So we need an adapter class to handle this. - - \section XMLWriterAndXMLReader XMLWriter and XMLReader - - The mitk::XMLWriter and mitk::XMLReader give the possibility to write and to read XML files. - These classes were developed with the aim to store and to load the MITK datatree (mitk::DataTree). - To avoid external dependencies of the MITK these classes are realized without XML parsers like DOM or SAX. - Only dependencies to ITK and VTK are existing. - - \subsection XMLFile XML file - - Mentioned before, the focus of this classes is to store and to load the MITK datatree (mitk::DataTree) with XML. - In the following you can see the body of a typical XML file of a MITK image that was kept as a node in the datatree. - - \image html SceneXML.png "A typical XML file of a MITK image." - - Initial point is the DataTree class. So \ is the root element. - The hierarchy of the datatree is reflected again in the hierarchy of the XML nodes. - At large the XML nodes represent classes. The name of the class is stored with the XML attribute "CLASS_NAME". - Generally data is stored with attributes. - - \subsection XMLWriter XMLWriter - - Technically the XMLWriter class is derived from mitkBaseXMLWriter class. The constructor needs the filename of the XML file at least. - - The following diagram displays the main components: - \image html XMLWriterOverview.png "Classes related to XMLWriter" - - For writing a XML file the following funtions are important. - - \li mitk::XMLIO::WriteXML() - \li mitk::XMLIO::WriteXMLData() - \li mitk::BaseXMLWriter::BeginNode() - \li mitk::BaseXMLWriter::EndNode() - \li mitk::XMLWriter::WriteProperty() - - Easily speaking the mitk::XMLIO class manages the input and the output of the XML file. - New classes that should be written to the XML file have to be derived from the XMLIO class. - New classes should also be declared in the mitk::MapClassIDToClassName class. - Calling the function WriteXML() writes automatically a complete node of the referenced class to the XML file. - The function WriteXML() calls sequential the functions BeginNode(), WriteProperty(), WriteXMLData() and EndNode(). - A new XML node is initialized with the function BeginNode("nameOfNewNode"). - The function WriteXMLData() writes the data of the referenced class. - This function has to be overwritten in new classes. The data is stored with XML attributes. - These attributes can be written with the function WriteProperty("key", value). - The parameter "key" specifies the name of the attribute and the parameter "value" the data. - Different datatypes are supported. For details you can look in the mitk::BaseXMLWriter and mitk::XMLWriter class. - The function EndNode() writes the end tag of the started XML node to the XML file. - - It is also possible to write custom data apart from classes to the XML file (e.g. data of complex datatypes). - In this case you can call BeginNode(),(WriteProperty()), (WriteXMLData()) and EndNode() manually to write XML nodes and attributes. - But watch out for the closing tag of each XML node. Each start tag needs an end tag. - - It is prefered to define common key words (e.g. name of attriubtes) in the XMLNodes class. - Class specific key words should be defined in the class itself (e.g. XML_NODE_NAME). - - The nodes of the datatree contain different data like images, surfaces or vessels. - These base data is stored in the respectively fitting dataformat. - Such files are called source files and are referenced in the XML file. - - To manage these references there are different functions implemented in the mitk::XMLWriter class. - - \li mitk::XMLWriter::SetSaveSourceFiles() - \li mitk::XMLWriter::SetSourceFileName() - \li mitk::XMLWriter::SetSubFolder() - \li mitk::XMLWriter::SetOriginPath() - \li mitk::XMLWriter::GetNewFileName() - \li mitk::XMLWriter::GetRelativePath() - \li mitk::XMLWriter::GetAbsolutePath() - \li mitk::XMLWriter::GetSubFolder() - - A special feature of the XMLWriter is to store the source files relative to the XML file. - If no filename is set a new filename is generated. - - \subsection XMLWriterCodeSnippets XMLWriter code snippets - - Initialize and start the XMLWriter - - \code - #include - #include - - //... - - const *char filename = "Scene.xml"; - mitk::XMLWriter xmlWriter(filename); - mitk::myClass::Pointer myClass; - - xmlWriter.BeginNode("root"); - myClass->WriteXML(xmlWriter); - xmlWriter.EndNode(); - \endcode - - - Overwrite the function WriteXMLData of a new class - - myClass.h - - \code - #include - - class myClass: public XMLIO - - public - - //... - - static const std::string XML_NODE_NAME; - - virtual bool WriteXMLData(XMLWriter &xmlWriter); - \endcode - - myClass.cpp - - \code - #include - - //... - - static const std::string XML_NODE_NAME = "myClass"; - // defines the XML nodename - - bool mitk::myClass::WriteXMLData(XMLWriter &xmlWriter); - { - xmlWriter.WriteProperty("MY_NUMBER", this->GetNumber()); - xmlWriter.BeginNode("uselessNode"); - xmlWriter.WriteProperty("USELESS_INFO", "useless info"); - xmlWriter.EndNode(); - return true; - } - \endcode - - \subsection XMLReader XMLReader - - The mitk::XMLReader reads a stream and parses XML element tags and corresponding attributes. - Technically the XMLReader class is derived from vtkXMLParser class. - The constructor needs the filename ((path)+filename) of the XML file and a datatree iterator (mitk::DataTreeIteratorBase) of the datatree. - - The following diagram displays the main components: - \image html XMLReaderOverview.png "Classes related to XMLReader" - - For reading a XML file the following funtions are important. - - \li mitk::XMLIO::ReadXMLData() - \li mitk::XMLReader::Goto() - \li mitk::XMLReader::GotoNext() - \li mitk::XMLReader::GotoParent() - \li mitk::XMLReader::CreateObject() - \li mitk::XMLReader::GetAttribute() - - As mentioned in the section before the mitk::XMLIO class manages the input and the output of the XML file. - New classes that should be read data from the XML file have to be derived from the mitkXMLIO class. - In new classes the function ReadXMLData() have to be overwritten. - Easily speaking this function reads the data from the XML file, provides for instantiation of new objects and sets the data. - With the Goto functions it is possible to navigate the parser through the XML document. - To get the root XML node call the function Goto("root"). Be careful with the hierarchy of the XML nodes and the actual position of the parser. - The function Goto() is looking always for child nodes and function GotoNext() is looking for further XML nodes in the same hierarchy. - For creation of new objects of the read class (the class is specified in the XML attribute "CLASS_NAME") the function CreateObject() has to be called. - The creation of the asked object is only successful when the new class has already be defined in the mitk::ObjectFactory class. - If the asked XML node is reached, the data stored in XML attributes, can be read by the function GetAttribute("key", value). - The parameter "key" specifies the name of the attribute and the parameter "value" keeps the data. - Different datatypes are supported. For details you can look in the mitk::XMLReader class. - When the parser should climb up in the XML tree hierarchy the function GotoParent() does a good job. - - The source file management - - If the reference to the source file is stored relative to the XML file you need the path of the XML file to calculate the absolute path. - This operation is done automatically and you get the absolute path when calling the attribute "FILENAME". - It is also possible to get the path of the source file by calling the function GetSourceFilePath(). - - \subsection XMLReaderCodeSnippets XMLReader code snippets - - Initialize and start the XMLReader - - \code - #include - - //... - - std::string filename = "Scene.xml"; - mitk::DataTreePreOrderIterator it(m_Tree); - mitk::XMLReader::Load(&it, filename); - \endcode - - - Overwrite the function ReadXMLData of a new class - - myClass.h - - \code - #include - - class myClass: public XMLIO - - public - - //... - - virtual bool ReadXMLData(XMLReader &xmlReader); - \endcode - - myClass.cpp - - \code - #include - #include - - //... - - bool mitk::myClass::ReadXMLData(XMLReader &xmlReader); - { - int value; - - if(xmlReader.Goto("myClass")){ - mitk::fictiveClass::Pointer myFictiveObject = dynamic_cast(xmlReader.CreateObject().GetPointer()); - if (xmlReader.GetAttribute("myNumber", value)) - this->SetNumber(value); - myFictiveObject->ReadXMLData(xmlReader); // further elements can be read - xmlReader.GotoParent(); // now we are back on parent tag - } - return true; - } - \endcode - -*/ diff --git a/Documentation/Doxygen/Modules/PartsOfAFunctionaliy.jpg b/Documentation/Doxygen/Modules/PartsOfAFunctionaliy.jpg deleted file mode 100644 index 876772b43f..0000000000 Binary files a/Documentation/Doxygen/Modules/PartsOfAFunctionaliy.jpg and /dev/null differ diff --git a/Documentation/Doxygen/Modules/PartsOfAFunctionaliy.xcf b/Documentation/Doxygen/Modules/PartsOfAFunctionaliy.xcf deleted file mode 100644 index 1b2b6b026f..0000000000 Binary files a/Documentation/Doxygen/Modules/PartsOfAFunctionaliy.xcf and /dev/null differ diff --git a/Documentation/Doxygen/Modules/SceneXML.png b/Documentation/Doxygen/Modules/SceneXML.png deleted file mode 100644 index 42136c722d..0000000000 Binary files a/Documentation/Doxygen/Modules/SceneXML.png and /dev/null differ diff --git a/Documentation/Doxygen/Modules/XMLReaderOverview.png b/Documentation/Doxygen/Modules/XMLReaderOverview.png deleted file mode 100644 index 8d5789d4c1..0000000000 Binary files a/Documentation/Doxygen/Modules/XMLReaderOverview.png and /dev/null differ diff --git a/Documentation/Doxygen/Modules/XMLReaderOverview.vsd b/Documentation/Doxygen/Modules/XMLReaderOverview.vsd deleted file mode 100644 index 474dedbcc0..0000000000 Binary files a/Documentation/Doxygen/Modules/XMLReaderOverview.vsd and /dev/null differ diff --git a/Documentation/Doxygen/Modules/XMLWriterOverview.png b/Documentation/Doxygen/Modules/XMLWriterOverview.png deleted file mode 100644 index a8b62f592e..0000000000 Binary files a/Documentation/Doxygen/Modules/XMLWriterOverview.png and /dev/null differ diff --git a/Documentation/Doxygen/Modules/XMLWriterOverview.vsd b/Documentation/Doxygen/Modules/XMLWriterOverview.vsd deleted file mode 100644 index 54a382ef99..0000000000 Binary files a/Documentation/Doxygen/Modules/XMLWriterOverview.vsd and /dev/null differ diff --git a/Modules/MitkExt/CMakeLists.txt b/Modules/MitkExt/CMakeLists.txt index 7d60b5a2eb..6a6a06c49d 100644 --- a/Modules/MitkExt/CMakeLists.txt +++ b/Modules/MitkExt/CMakeLists.txt @@ -1,28 +1,28 @@ #if(WIN32) # option(MITK_USE_TD_MOUSE "Enable support for 3D Connexion SpaceNavigator" OFF) #endif(WIN32) configure_file(${PROJECT_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${PROJECT_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${PROJECT_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${PROJECT_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) MITK_CREATE_MODULE( MitkExt INCLUDE_DIRS Algorithms Controllers DataManagement Interactions IO Rendering DEPENDS Mitk LegacyAdaptors IpPicSupport ipSegmentation mitkIpFunc PlanarFigure ImageStatistics - DEPENDS_INTERNAL pic2vtk #IIL4MITK + DEPENDS_INTERNAL pic2vtk ) # this is needed for libraries which link to mitkCoreExt and need # symbols from explicitly instantiated templates like # mitk::UnstructuredGridVtkWriter which is referenced in # mitk::UnstructuredGridVtkWriterTest in the MitkExtTestDriver executable. if(MINGW) get_target_property(_mitkCoreExt_MINGW_linkflags mitkCoreExt LINK_FLAGS) if(NOT _mitkCoreExt_MINGW_linkflags) set(_mitkCoreExt_MINGW_linkflags "") endif(NOT _mitkCoreExt_MINGW_linkflags) set_target_properties(mitkCoreExt PROPERTIES LINK_FLAGS "${_mitkCoreExt_MINGW_linkflags} -Wl,--export-all-symbols") endif(MINGW) add_subdirectory(Testing) diff --git a/Modules/Overlays/QmitkCustomWidgetOverlay.cpp b/Modules/Overlays/QmitkCustomWidgetOverlay.cpp index fa233291dd..def3dc8bfe 100644 --- a/Modules/Overlays/QmitkCustomWidgetOverlay.cpp +++ b/Modules/Overlays/QmitkCustomWidgetOverlay.cpp @@ -1,41 +1,41 @@ -/*========================================================================= +/*=================================================================== -Program: Medical Imaging & Interaction Toolkit -Language: C++ -Date: $Date: 2010-01-14 14:20:26 +0100 (Thu, 14 Jan 2010) $ -Version: $Revision: 21047 $ +The Medical Imaging Interaction Toolkit (MITK) -Copyright (c) German Cancer Research Center, Division of Medical and -Biological Informatics. All rights reserved. -See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. +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 the above copyright notices for more information. +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 "QmitkCustomWidgetOverlay.h" QmitkCustomWidgetOverlay::QmitkCustomWidgetOverlay( const char* id ) : QmitkOverlay(id) { QmitkOverlay::AddDropShadow( m_Widget ); } QmitkCustomWidgetOverlay::~QmitkCustomWidgetOverlay() { } void QmitkCustomWidgetOverlay::SetWidget( QWidget* widget ) { if ( widget != NULL ) { m_Widget = widget; m_WidgetIsCustom = true; } } diff --git a/Modules/Overlays/QmitkCustomWidgetOverlay.h b/Modules/Overlays/QmitkCustomWidgetOverlay.h index e9c50220e3..40230ead31 100644 --- a/Modules/Overlays/QmitkCustomWidgetOverlay.h +++ b/Modules/Overlays/QmitkCustomWidgetOverlay.h @@ -1,65 +1,64 @@ -/*========================================================================= +/*=================================================================== -Program: Medical Imaging & Interaction Toolkit -Language: C++ -Date: $Date: 2009-05-28 17:19:30 +0200 (Thu, 28 May 2009) $ -Version: $Revision: 17495 $ +The Medical Imaging Interaction Toolkit (MITK) -Copyright (c) German Cancer Research Center, Division of Medical and -Biological Informatics. All rights reserved. -See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. +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 the above copyright notices for more information. +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 QmitkCustomWidgetOverlay_H_HEADER_INCLUDED_C10DC4EB #define QmitkCustomWidgetOverlay_H_HEADER_INCLUDED_C10DC4EB // MITK #include "QmitkOverlay.h" #include "OverlaysExports.h" /** \class QmitkCustomWidgetOverlay * \brief object representing a custom widget that is handled and positioned * as an overlay. * * A QmitkCustomWidgetOverlay is a generic sub-class of QmitkOverlay. It * offers the possibility to set the internal m_Widget from the outside. * * This offers the possibility to position custom widgets 'on top of' other * widgets using the positioning mechanism of all overlays. * * \warn The custom widgets need to be configured and connected manually. * Properties cannot be set. * * \ingroup Overlays */ class Overlays_EXPORT QmitkCustomWidgetOverlay : public QmitkOverlay { public: /** * @brief Default Constructor **/ QmitkCustomWidgetOverlay( const char* id ); /** * @brief Default Destructor **/ virtual ~QmitkCustomWidgetOverlay(); void SetWidget( QWidget* widget ); }; #endif /* QmitkCustomWidgetOverlay_H_HEADER_INCLUDED_C10DC4EB */ diff --git a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp index 2b5a8569fb..c5bf8fd365 100644 --- a/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp +++ b/Modules/PlanarFigure/Interactions/mitkPlanarFigureInteractor.cpp @@ -1,979 +1,999 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ +#define PLANARFIGUREINTERACTOR_DBG MITK_DEBUG("PlanarFigureInteractor") << __LINE__ << ": " + #include "mitkPlanarFigureInteractor.h" #include "mitkPointOperation.h" #include "mitkPositionEvent.h" #include "mitkPlanarFigure.h" #include "mitkStatusBar.h" #include "mitkDataNode.h" #include "mitkInteractionConst.h" #include "mitkAction.h" #include "mitkStateEvent.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkStateMachineFactory.h" #include "mitkStateTransitionOperation.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" //how precise must the user pick the point //default value mitk::PlanarFigureInteractor ::PlanarFigureInteractor(const char * type, DataNode* dataNode, int /* n */ ) : Interactor( type, dataNode ), m_Precision( 6.5 ), m_MinimumPointDistance( 25.0 ), m_IsHovering( false ), m_LastPointWasValid( false ) { } mitk::PlanarFigureInteractor::~PlanarFigureInteractor() { } void mitk::PlanarFigureInteractor::SetPrecision( mitk::ScalarType precision ) { m_Precision = precision; } void mitk::PlanarFigureInteractor::SetMinimumPointDistance( ScalarType minimumDistance ) { m_MinimumPointDistance = minimumDistance; } // Overwritten since this class can handle it better! float mitk::PlanarFigureInteractor ::CanHandleEvent(StateEvent const* stateEvent) const { float returnValue = 0.5; // If it is a key event that can be handled in the current state, // then return 0.5 mitk::DisplayPositionEvent const *disPosEvent = dynamic_cast (stateEvent->GetEvent()); // Key event handling: if (disPosEvent == NULL) { // Check if the current state has a transition waiting for that key event. if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { return 0.5; } else { return 0.0; } } mitk::PlanarFigure *planarFigure = dynamic_cast( m_DataNode->GetData() ); if ( planarFigure != NULL ) { // Give higher priority if this figure is currently selected if ( planarFigure->GetSelectedControlPoint() >= 0 ) { return 1.0; } } return returnValue; } bool mitk::PlanarFigureInteractor ::ExecuteAction( Action *action, mitk::StateEvent const *stateEvent ) { bool ok = false; // Check corresponding data; has to be sub-class of mitk::PlanarFigure mitk::PlanarFigure *planarFigure = dynamic_cast< mitk::PlanarFigure * >( m_DataNode->GetData() ); if ( planarFigure == NULL ) { return false; } // Get the timestep to also support 3D+t const mitk::Event *theEvent = stateEvent->GetEvent(); int timeStep = 0; //mitk::ScalarType timeInMS = 0.0; if ( theEvent ) { if (theEvent->GetSender() != NULL) { timeStep = theEvent->GetSender()->GetTimeStep( planarFigure ); //timeInMS = theEvent->GetSender()->GetTime(); } } // Get Geometry2D of PlanarFigure mitk::Geometry2D *planarFigureGeometry = dynamic_cast< mitk::Geometry2D * >( planarFigure->GetGeometry( timeStep ) ); // Get the Geometry2D of the window the user interacts with (for 2D point // projection) mitk::BaseRenderer *renderer = NULL; const Geometry2D *projectionPlane = NULL; if ( theEvent ) { renderer = theEvent->GetSender(); projectionPlane = renderer->GetCurrentWorldGeometry2D(); } // TODO: Check if display and PlanarFigure geometries are parallel (if they are PlaneGeometries) switch (action->GetActionId()) { case AcDONOTHING: + PLANARFIGUREINTERACTOR_DBG << "AcDONOTHING"; ok = true; break; case AcCHECKOBJECT: { + PLANARFIGUREINTERACTOR_DBG << "AcCHECKOBJECT"; if ( planarFigure->IsPlaced() ) { this->HandleEvent( new mitk::StateEvent( EIDYES, NULL ) ); } else { this->HandleEvent( new mitk::StateEvent( EIDNO, NULL ) ); } ok = false; break; } case AcADD: { + PLANARFIGUREINTERACTOR_DBG << "AcADD"; // Invoke event to notify listeners that placement of this PF starts now planarFigure->InvokeEvent( StartPlacementPlanarFigureEvent() ); // Use Geometry2D of the renderer clicked on for this PlanarFigure mitk::PlaneGeometry *planeGeometry = const_cast< mitk::PlaneGeometry * >( dynamic_cast< const mitk::PlaneGeometry * >( renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry() ) ); if ( planeGeometry != NULL ) { planarFigureGeometry = planeGeometry; planarFigure->SetGeometry2D( planeGeometry ); } else { ok = false; break; } // Extract point in 2D world coordinates (relative to Geometry2D of // PlanarFigure) Point2D point2D; if ( !this->TransformPositionEventToPoint2D( stateEvent, point2D, planarFigureGeometry ) ) { ok = false; break; } // Place PlanarFigure at this point planarFigure->PlaceFigure( point2D ); // Re-evaluate features planarFigure->EvaluateFeatures(); //this->LogPrintPlanarFigureQuantities( planarFigure ); // Set a bool property indicating that the figure has been placed in // the current RenderWindow. This is required so that the same render // window can be re-aligned to the Geometry2D of the PlanarFigure later // on in an application. m_DataNode->SetBoolProperty( "PlanarFigureInitializedWindow", true, renderer ); // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); ok = true; break; } case AcMOVEPOINT: { + PLANARFIGUREINTERACTOR_DBG << "AcMOVEPOINT"; bool isEditable = true; m_DataNode->GetBoolProperty( "planarfigure.iseditable", isEditable ); // Extract point in 2D world coordinates (relative to Geometry2D of // PlanarFigure) Point2D point2D; if ( !this->TransformPositionEventToPoint2D( stateEvent, point2D, planarFigureGeometry ) || !isEditable ) { ok = false; break; } // check if the control points shall be hidden during interaction bool hidecontrolpointsduringinteraction = false; m_DataNode->GetBoolProperty( "planarfigure.hidecontrolpointsduringinteraction", hidecontrolpointsduringinteraction ); // hide the control points if necessary m_DataNode->SetBoolProperty( "planarfigure.drawcontrolpoints", !hidecontrolpointsduringinteraction ); // Move current control point to this point planarFigure->SetCurrentControlPoint( point2D ); // Re-evaluate features planarFigure->EvaluateFeatures(); //this->LogPrintPlanarFigureQuantities( planarFigure ); // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); ok = true; break; } case AcCHECKNMINUS1: { + PLANARFIGUREINTERACTOR_DBG << "AcCHECKNMINUS1"; + if ( planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints() ) { // Initial placement finished: deselect control point and send an // event to notify application listeners planarFigure->Modified(); planarFigure->DeselectControlPoint(); planarFigure->InvokeEvent( EndPlacementPlanarFigureEvent() ); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); planarFigure->SetProperty( "initiallyplaced", mitk::BoolProperty::New( true ) ); m_DataNode->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); m_DataNode->Modified(); this->HandleEvent( new mitk::StateEvent( EIDYES, stateEvent->GetEvent() ) ); } else { this->HandleEvent( new mitk::StateEvent( EIDNO, stateEvent->GetEvent() ) ); } // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); ok = true; break; } case AcCHECKEQUALS1: { + PLANARFIGUREINTERACTOR_DBG << "AcCHECKEQUALS1"; + // NOTE: Action name is a bit misleading; this action checks whether // the figure has already the minimum number of required points to // be finished (by double-click) const mitk::PositionEvent *positionEvent = dynamic_cast< const mitk::PositionEvent * > ( stateEvent->GetEvent() ); if ( positionEvent == NULL ) { ok = false; break; } if ( planarFigure->GetNumberOfControlPoints() > planarFigure->GetMinimumNumberOfControlPoints() ) { // Initial placement finished: deselect control point and send an // event to notify application listeners planarFigure->Modified(); planarFigure->DeselectControlPoint(); planarFigure->RemoveLastControlPoint(); planarFigure->SetProperty( "initiallyplaced", mitk::BoolProperty::New( true ) ); m_DataNode->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); m_DataNode->Modified(); planarFigure->InvokeEvent( EndPlacementPlanarFigureEvent() ); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); this->HandleEvent( new mitk::StateEvent( EIDYES, NULL ) ); } else { this->HandleEvent( new mitk::StateEvent( EIDNO, NULL ) ); } // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); ok = true; break; } case AcCHECKPOINT: { + PLANARFIGUREINTERACTOR_DBG << "AcCHECKPOINT"; // Check if the distance of the current point to the previously set point in display coordinates // is sufficient (if a previous point exists) // Extract display position const mitk::PositionEvent *positionEvent = dynamic_cast< const mitk::PositionEvent * > ( stateEvent->GetEvent() ); if ( positionEvent == NULL ) { ok = false; break; } m_LastPointWasValid = IsMousePositionAcceptableAsNewControlPoint( positionEvent, planarFigure ); if (m_LastPointWasValid) { this->HandleEvent( new mitk::StateEvent( EIDYES, stateEvent->GetEvent() ) ); } else { this->HandleEvent( new mitk::StateEvent( EIDNO, stateEvent->GetEvent() ) ); } ok = true; break; } case AcADDPOINT: { + PLANARFIGUREINTERACTOR_DBG << "AcADDPOINT"; bool selected = false; bool isEditable = true; m_DataNode->GetBoolProperty("selected", selected); m_DataNode->GetBoolProperty( "planarfigure.iseditable", isEditable ); if ( !selected || !isEditable ) { ok = false; break; } // Extract point in 2D world coordinates (relative to Geometry2D of // PlanarFigure) Point2D point2D, projectedPoint; if ( !this->TransformPositionEventToPoint2D( stateEvent, point2D, planarFigureGeometry ) ) { ok = false; break; } // TODO: check segement of polyline we clicked in int nextIndex = this->IsPositionOverFigure( stateEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry(), projectedPoint ); // Add point as new control point renderer->GetDisplayGeometry()->DisplayToWorld( projectedPoint, projectedPoint ); if ( planarFigure->IsPreviewControlPointVisible() ) { point2D = planarFigure->GetPreviewControlPoint(); } planarFigure->AddControlPoint( point2D, nextIndex ); if ( planarFigure->IsPreviewControlPointVisible() ) { planarFigure->SelectControlPoint( nextIndex ); planarFigure->ResetPreviewContolPoint(); } // Re-evaluate features planarFigure->EvaluateFeatures(); //this->LogPrintPlanarFigureQuantities( planarFigure ); // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); ok = true; break; } case AcDESELECTPOINT: { + PLANARFIGUREINTERACTOR_DBG << "AcDESELECTPOINT"; planarFigure->DeselectControlPoint(); // Issue event so that listeners may update themselves planarFigure->Modified(); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); m_DataNode->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); m_DataNode->SetBoolProperty( "planarfigure.ishovering", false ); m_DataNode->Modified(); // falls through break; } case AcCHECKHOVERING: { + PLANARFIGUREINTERACTOR_DBG << "AcCHECKHOVERING"; mitk::Point2D pointProjectedOntoLine; int previousControlPoint = mitk::PlanarFigureInteractor::IsPositionOverFigure( stateEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry(), pointProjectedOntoLine ); bool isHovering = ( previousControlPoint != -1 ); int pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( stateEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry() ); int initiallySelectedControlPoint = planarFigure->GetSelectedControlPoint(); if ( pointIndex >= 0 ) { // If mouse is above control point, mark it as selected planarFigure->SelectControlPoint( pointIndex ); // If mouse is hovering above a marker, it is also hovering above the figure isHovering = true; } else { // Mouse in not above control point --> deselect point planarFigure->DeselectControlPoint(); } bool renderingUpdateNeeded = true; if ( isHovering ) { if ( !m_IsHovering ) { // Invoke hover event once when the mouse is entering the figure area m_IsHovering = true; planarFigure->InvokeEvent( StartHoverPlanarFigureEvent() ); // Set bool property to indicate that planar figure is currently in "hovering" mode m_DataNode->SetBoolProperty( "planarfigure.ishovering", true ); renderingUpdateNeeded = true; } bool selected = false; bool isExtendable = false; bool isEditable = true; m_DataNode->GetBoolProperty("selected", selected); m_DataNode->GetBoolProperty("planarfigure.isextendable", isExtendable); m_DataNode->GetBoolProperty( "planarfigure.iseditable", isEditable ); if ( selected && isHovering && isExtendable && pointIndex == -1 && isEditable ) { const mitk::PositionEvent *positionEvent = dynamic_cast< const mitk::PositionEvent * > ( stateEvent->GetEvent() ); if ( positionEvent != NULL ) { renderer->GetDisplayGeometry()->DisplayToWorld( pointProjectedOntoLine, pointProjectedOntoLine ); planarFigure->SetPreviewControlPoint( pointProjectedOntoLine ); renderingUpdateNeeded = true; } } else { planarFigure->ResetPreviewContolPoint(); } if ( planarFigure->GetSelectedControlPoint() != initiallySelectedControlPoint ) { // the selected control point has changed -> rendering update necessary renderingUpdateNeeded = true; } this->HandleEvent( new mitk::StateEvent( EIDYES, stateEvent->GetEvent() ) ); // Return true: only this interactor is eligible to react on this event ok = true; } else { if ( m_IsHovering ) { planarFigure->ResetPreviewContolPoint(); // Invoke end-hover event once the mouse is exiting the figure area m_IsHovering = false; planarFigure->InvokeEvent( EndHoverPlanarFigureEvent() ); // Set bool property to indicate that planar figure is no longer in "hovering" mode m_DataNode->SetBoolProperty( "planarfigure.ishovering", false ); renderingUpdateNeeded = true; } this->HandleEvent( new mitk::StateEvent( EIDNO, NULL ) ); // Return false so that other (PlanarFigure) Interactors may react on this // event as well ok = false; } // Update rendered scene if necessray if ( renderingUpdateNeeded ) { renderer->GetRenderingManager()->RequestUpdateAll(); } break; } case AcCHECKSELECTED: { + PLANARFIGUREINTERACTOR_DBG << "AcCHECKSELECTED"; bool selected = false; m_DataNode->GetBoolProperty("selected", selected); if ( selected ) { this->HandleEvent( new mitk::StateEvent( EIDYES, stateEvent->GetEvent() ) ); } else { // Invoke event to notify listeners that this planar figure should be selected planarFigure->InvokeEvent( SelectPlanarFigureEvent() ); this->HandleEvent( new mitk::StateEvent( EIDNO, NULL ) ); } } case AcSELECTPICKEDOBJECT: { + PLANARFIGUREINTERACTOR_DBG << "AcSELECTPICKEDOBJECT"; //// Invoke event to notify listeners that this planar figure should be selected //planarFigure->InvokeEvent( SelectPlanarFigureEvent() ); + //planarFigure->InvokeEvent( StartInteractionPlanarFigureEvent() ); // Check if planar figure is marked as "editable" bool isEditable = true; m_DataNode->GetBoolProperty( "planarfigure.iseditable", isEditable ); int pointIndex = -1; if ( isEditable ) { // If planar figure is editable, check if mouse is over a control point pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( stateEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry() ); } // If editing is enabled and the mouse is currently over a control point, select it if ( pointIndex >= 0 ) { this->HandleEvent( new mitk::StateEvent( EIDYES, stateEvent->GetEvent() ) ); // Return true: only this interactor is eligible to react on this event ok = true; } else { this->HandleEvent( new mitk::StateEvent( EIDNO, stateEvent->GetEvent() ) ); // Return false so that other (PlanarFigure) Interactors may react on this // event as well ok = false; } ok = true; break; } case AcENTEROBJECT: { + PLANARFIGUREINTERACTOR_DBG << "AcENTEROBJECT"; bool selected = false; m_DataNode->GetBoolProperty("selected", selected); // no need to invoke this if the figure is already selected if ( !selected ) { planarFigure->InvokeEvent( SelectPlanarFigureEvent() ); } // if this was a right mouse button click, invoke the event if ( theEvent->GetButton() == 2 ) { planarFigure->InvokeEvent( ContextMenuPlanarFigureEvent() ); ok = true; } else { ok = false; } // we HAVE TO proceed with 'EIDNO' here to ensure correct states // and convenient application behaviour this->HandleEvent( new mitk::StateEvent( EIDNO, stateEvent->GetEvent() ) ); break; } case AcSELECTPOINT: { + PLANARFIGUREINTERACTOR_DBG << "AcSELECTPOINT"; // Invoke event to notify listeners that interaction with this PF starts now planarFigure->InvokeEvent( StartInteractionPlanarFigureEvent() ); // Reset the PlanarFigure if required if ( planarFigure->ResetOnPointSelect() ) { this->HandleEvent( new mitk::StateEvent( EIDYES, stateEvent->GetEvent() ) ); } else { this->HandleEvent( new mitk::StateEvent( EIDNO, stateEvent->GetEvent() ) ); } ok = true; break; } case AcREMOVEPOINT: { + PLANARFIGUREINTERACTOR_DBG << "AcREMOVEPOINT"; bool isExtendable = false; m_DataNode->GetBoolProperty("planarfigure.isextendable", isExtendable); if ( isExtendable ) { int selectedControlPoint = planarFigure->GetSelectedControlPoint(); planarFigure->RemoveControlPoint( selectedControlPoint ); // Re-evaluate features planarFigure->EvaluateFeatures(); planarFigure->Modified(); m_DataNode->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); renderer->GetRenderingManager()->RequestUpdateAll(); this->HandleEvent( new mitk::StateEvent( EIDYES, NULL ) ); } else { this->HandleEvent( new mitk::StateEvent( EIDNO, NULL ) ); } } //case AcMOVEPOINT: //case AcMOVESELECTED: // { // // Update the display // renderer->GetRenderingManager()->RequestUpdateAll(); // ok = true; // break; // } //case AcFINISHMOVE: // { // ok = true; // break; // } default: return Superclass::ExecuteAction( action, stateEvent ); } return ok; } bool mitk::PlanarFigureInteractor::TransformPositionEventToPoint2D( const StateEvent *stateEvent, Point2D &point2D, const Geometry2D *planarFigureGeometry ) { // Extract world position, and from this position on geometry, if // available const mitk::PositionEvent *positionEvent = dynamic_cast< const mitk::PositionEvent * > ( stateEvent->GetEvent() ); if ( positionEvent == NULL ) { return false; } mitk::Point3D worldPoint3D = positionEvent->GetWorldPosition(); // TODO: proper handling of distance tolerance if ( planarFigureGeometry->Distance( worldPoint3D ) > 0.1 ) { return false; } // Project point onto plane of this PlanarFigure planarFigureGeometry->Map( worldPoint3D, point2D ); return true; } bool mitk::PlanarFigureInteractor::TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::Geometry2D *objectGeometry, const mitk::Geometry2D *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) const { mitk::Point3D point3D; // Map circle point from local 2D geometry into 3D world space objectGeometry->Map( point2D, point3D ); // TODO: proper handling of distance tolerance if ( displayGeometry->Distance( point3D ) < 0.1 ) { // Project 3D world point onto display geometry rendererGeometry->Map( point3D, displayPoint ); displayGeometry->WorldToDisplay( displayPoint, displayPoint ); return true; } return false; } bool mitk::PlanarFigureInteractor::IsPointNearLine( const mitk::Point2D& point, const mitk::Point2D& startPoint, const mitk::Point2D& endPoint, mitk::Point2D& projectedPoint ) const { mitk::Vector2D n1 = endPoint - startPoint; n1.Normalize(); // Determine dot products between line vector and startpoint-point / endpoint-point vectors double l1 = n1 * (point - startPoint); double l2 = -n1 * (point - endPoint); // Determine projection of specified point onto line defined by start / end point mitk::Point2D crossPoint = startPoint + n1 * l1; projectedPoint = crossPoint; // Point is inside encompassing rectangle IF // - its distance to its projected point is small enough // - it is not further outside of the line than the defined tolerance if (((crossPoint.SquaredEuclideanDistanceTo(point) < 20.0) && (l1 > 0.0) && (l2 > 0.0)) || endPoint.SquaredEuclideanDistanceTo(point) < 20.0 || startPoint.SquaredEuclideanDistanceTo(point) < 20.0) { return true; } return false; } int mitk::PlanarFigureInteractor::IsPositionOverFigure( const StateEvent *stateEvent, PlanarFigure *planarFigure, const Geometry2D *planarFigureGeometry, const Geometry2D *rendererGeometry, const DisplayGeometry *displayGeometry, Point2D& pointProjectedOntoLine ) const { // Extract display position const mitk::PositionEvent *positionEvent = dynamic_cast< const mitk::PositionEvent * > ( stateEvent->GetEvent() ); if ( positionEvent == NULL ) { return -1; } mitk::Point2D displayPosition = positionEvent->GetDisplayPosition(); // Iterate over all polylines of planar figure, and check if // any one is close to the current display position typedef mitk::PlanarFigure::PolyLineType VertexContainerType; mitk::Point2D worldPoint2D, displayControlPoint; mitk::Point3D worldPoint3D; for ( unsigned short loop = 0; loop < planarFigure->GetPolyLinesSize(); ++loop ) { const VertexContainerType polyLine = planarFigure->GetPolyLine( loop ); Point2D polyLinePoint; Point2D firstPolyLinePoint; Point2D previousPolyLinePoint; bool firstPoint = true; for ( VertexContainerType::const_iterator it = polyLine.begin(); it != polyLine.end(); ++it ) { // Get plane coordinates of this point of polyline (if possible) if ( !this->TransformObjectToDisplay( it->Point, polyLinePoint, planarFigureGeometry, rendererGeometry, displayGeometry ) ) { break; // Poly line invalid (not on current 2D plane) --> skip it } if ( firstPoint ) { firstPolyLinePoint = polyLinePoint; firstPoint = false; } else if ( this->IsPointNearLine( displayPosition, previousPolyLinePoint, polyLinePoint, pointProjectedOntoLine ) ) { // Point is close enough to line segment --> Return index of the segment return it->Index; } previousPolyLinePoint = polyLinePoint; } // For closed figures, also check last line segment if ( planarFigure->IsClosed() && this->IsPointNearLine( displayPosition, polyLinePoint, firstPolyLinePoint, pointProjectedOntoLine ) ) { return 0; // Return index of first control point } } return -1; } int mitk::PlanarFigureInteractor::IsPositionInsideMarker( const StateEvent *stateEvent, const PlanarFigure *planarFigure, const Geometry2D *planarFigureGeometry, const Geometry2D *rendererGeometry, const DisplayGeometry *displayGeometry ) const { // Extract display position const mitk::PositionEvent *positionEvent = dynamic_cast< const mitk::PositionEvent * > ( stateEvent->GetEvent() ); if ( positionEvent == NULL ) { return -1; } mitk::Point2D displayPosition = positionEvent->GetDisplayPosition(); // Iterate over all control points of planar figure, and check if // any one is close to the current display position mitk::Point2D worldPoint2D, displayControlPoint; mitk::Point3D worldPoint3D; int numberOfControlPoints = planarFigure->GetNumberOfControlPoints(); for ( int i=0; iTransformObjectToDisplay( planarFigure->GetControlPoint(i), displayControlPoint, planarFigureGeometry, rendererGeometry, displayGeometry ) ) { // TODO: variable size of markers if ( displayPosition.SquaredEuclideanDistanceTo( displayControlPoint ) < 20.0 ) { return i; } } } //for ( it = controlPoints.begin(); it != controlPoints.end(); ++it ) //{ // Point2D displayControlPoint; // if ( this->TransformObjectToDisplay( it->Point, displayControlPoint, // planarFigureGeometry, rendererGeometry, displayGeometry ) ) // { // // TODO: variable size of markers // if ( (abs(displayPosition[0] - displayControlPoint[0]) < 4 ) // && (abs(displayPosition[1] - displayControlPoint[1]) < 4 ) ) // { // return index; // } // } //} return -1; } void mitk::PlanarFigureInteractor::LogPrintPlanarFigureQuantities( const PlanarFigure *planarFigure ) { MITK_INFO << "PlanarFigure: " << planarFigure->GetNameOfClass(); for ( unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i ) { MITK_INFO << "* " << planarFigure->GetFeatureName( i ) << ": " << planarFigure->GetQuantity( i ) << " " << planarFigure->GetFeatureUnit( i ); } } bool mitk::PlanarFigureInteractor::IsMousePositionAcceptableAsNewControlPoint( const PositionEvent* positionEvent, const PlanarFigure* planarFigure ) { assert(positionEvent && planarFigure); BaseRenderer* renderer = positionEvent->GetSender(); assert(renderer); // Get the timestep to support 3D+t int timeStep( renderer->GetTimeStep( planarFigure ) ); // Get current display position of the mouse Point2D currentDisplayPosition = positionEvent->GetDisplayPosition(); // Check if a previous point has been set bool tooClose = false; for( int i=0; i < (int)planarFigure->GetNumberOfControlPoints(); i++ ) { if ( i != planarFigure->GetSelectedControlPoint() ) { // Try to convert previous point to current display coordinates mitk::Geometry2D *planarFigureGeometry = dynamic_cast< mitk::Geometry2D * >( planarFigure->GetGeometry( timeStep ) ); const Geometry2D *projectionPlane = renderer->GetCurrentWorldGeometry2D(); mitk::Point3D previousPoint3D; planarFigureGeometry->Map( planarFigure->GetControlPoint( i ), previousPoint3D ); if ( renderer->GetDisplayGeometry()->Distance( previousPoint3D ) < 0.1 ) // ugly, but assert makes this work { mitk::Point2D previousDisplayPosition; projectionPlane->Map( previousPoint3D, previousDisplayPosition ); renderer->GetDisplayGeometry()->WorldToDisplay( previousDisplayPosition, previousDisplayPosition ); double a = currentDisplayPosition[0] - previousDisplayPosition[0]; double b = currentDisplayPosition[1] - previousDisplayPosition[1]; // If point is to close, do not set a new point tooClose = (a * a + b * b < m_MinimumPointDistance ); } if ( tooClose ) return false; // abort loop early } } return !tooClose; // default } diff --git a/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.cpp b/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.cpp index 9e93c0a7df..19a39cc1d2 100644 --- a/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.cpp +++ b/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.cpp @@ -1,806 +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 "QmitkAdaptiveRegionGrowingWidget.h" #include "QmitkStdMultiWidget.h" #include #include "mitkNodePredicateDataType.h" #include "mitkGlobalInteraction.h" #include "mitkPointSetInteractor.h" #include "mitkProperties.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageTimeSelector.h" +#include "mitkImageStatisticsHolder.h" + #include #include #include QmitkAdaptiveRegionGrowingWidget::QmitkAdaptiveRegionGrowingWidget(QWidget * parent) : QWidget(parent), m_MultiWidget(NULL), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0) { m_Controls.setupUi(this); m_NAMEFORSEEDPOINT = "Seed Point"; this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation","RGResult","RGFeedbackSurface","RGSeedpoint"); } QmitkAdaptiveRegionGrowingWidget::~QmitkAdaptiveRegionGrowingWidget() { //Removing the observer of the PointSet node mitk::DataNode* node = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); if (node != NULL) { this->DeactivateSeedPointMode(); dynamic_cast(node->GetData())->RemoveObserver(m_PointSetAddObserverTag); } + + this->RemoveHelperNodes(); + +} + +void QmitkAdaptiveRegionGrowingWidget::RemoveHelperNodes() +{ + mitk::DataNode::Pointer seedNode = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); + if( seedNode.IsNotNull() ) + { + m_DataStorage->Remove(seedNode); + } + + mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); + if( imageNode.IsNotNull() ) + { + m_DataStorage->Remove(imageNode); + } } void QmitkAdaptiveRegionGrowingWidget::CreateConnections() { //Connecting GUI components connect( (QObject*) (m_Controls.m_pbDefineSeedPoint), SIGNAL( toggled(bool) ), this, SLOT( SetSeedPointToggled(bool)) ); connect( (QObject*) (m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect( (QObject*) (m_Controls.m_Slider), SIGNAL(valueChanged(int)), this, SLOT(ChangeLevelWindow(int)) ); connect( (QObject*) (m_Controls.m_DecreaseTH), SIGNAL(clicked()), this, SLOT(DecreaseSlider())); connect( (QObject*) (m_Controls.m_IncreaseTH), SIGNAL(clicked()), this,SLOT(IncreaseSlider())); connect( (QObject*) (m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect( (QObject*) (m_Controls.m_cbVolumeRendering), SIGNAL(toggled(bool)), this, SLOT(UseVolumeRendering(bool) )); connect( m_Controls.m_LowerThresholdSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); connect( m_Controls.m_UpperThresholdSpinBox, SIGNAL(valueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); } void QmitkAdaptiveRegionGrowingWidget::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface, std::string seedPoint) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; m_NAMEFORSEEDPOINT = seedPoint; } void QmitkAdaptiveRegionGrowingWidget::SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingWidget::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { m_MultiWidget = multiWidget; } void QmitkAdaptiveRegionGrowingWidget::SetInputImageNode(mitk::DataNode* node) { m_InputImageNode = node; } void QmitkAdaptiveRegionGrowingWidget::SetSeedPointToggled(bool toggled) { if (m_InputImageNode.IsNull()) { if (m_Controls.m_pbDefineSeedPoint->isChecked()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); m_Controls.m_pbDefineSeedPoint->toggle(); } return; } //check that a pointset node with the given name exists in data storage mitk::DataNode* node = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); if (node == NULL) //no pointSet present { // new node and data item mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New(); pointSetNode->GetPropertyList()->SetProperty("name", mitk::StringProperty::New(m_NAMEFORSEEDPOINT)); pointSetNode->GetPropertyList()->SetProperty("helper object", mitk::BoolProperty::New(true)); pointSetNode->GetPropertyList()->SetProperty("layer", mitk::IntProperty::New(2)); mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); pointSetNode->SetData(pointSet); //Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingWidget::OnPointAdded); m_PointSetAddObserverTag = pointSet->AddObserver( mitk::PointSetAddEvent(), pointAddedCommand); //add to DataStorage m_DataStorage->Add(pointSetNode, m_InputImageNode); } mitk::NodePredicateDataType::Pointer imagePred = mitk::NodePredicateDataType::New("Image"); mitk::DataStorage::SetOfObjects::ConstPointer setOfImages = m_DataStorage->GetSubset(imagePred); //no image node found if (setOfImages->Size() < 1) { QMessageBox::information(NULL, "Segmentation functionality", "Please load an image before setting a seed point."); return; } else { //get PointSet node from DataStorage mitk::DataNode* node = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); if (node == NULL) { QMessageBox::critical(NULL, "QmitkAdaptiveRegionGrowingWidget", "No seed point node found!"); return; } if (toggled == true) // button is down { this->ActivateSeedPointMode(); //add pointSet Interactor if there is none } else { this->DeactivateSeedPointMode(); //disable pointSet Interactor } //enable the Run Segmentation button once the set seed point button is pressed m_Controls.m_pbRunSegmentation->setEnabled(true); } } void QmitkAdaptiveRegionGrowingWidget::OnPointAdded() { mitk::DataNode* node = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); if (node != NULL) //no pointSet present { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(NULL, "QmitkAdaptiveRegionGrowingWidget", "PointSetNode does not contain a pointset"); return; } mitk::Image* image = dynamic_cast(m_InputImageNode->GetData()); mitk::Point3D seedPoint = pointSet->GetPointSet(m_MultiWidget->GetTimeNavigationController()->GetTime()->GetPos())->GetPoints()->ElementAt(0); m_SeedpointValue = image->GetPixelValueByWorldCoordinate(seedPoint); /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } m_SeedPointValueMean = 0; mitk::Index3D currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos (0); image->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for(int i = runningIndex[0]-2; i <= runningIndex[0]+2; i++) { for(int j = runningIndex[1]-2; j <= runningIndex[1]+2; j++) { for(int k = runningIndex[2]-2; k <= runningIndex[2]+2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if(image->GetGeometry()->IsIndexInside(currentIndex)) { pixelValues[pos] = image->GetPixelValueByIndex(currentIndex); pos++; } else { pixelValues[pos] = -10000000; pos++; } } } } //Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (unsigned int i = 0; i < 125; i++) { if(pixelValues[i] > -10000000) { m_SeedPointValueMean += pixelValues[i]; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean/numberOfValues; /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is meanSeedValue+0.15*windowsSize */ - mitk::ScalarType min = image->GetScalarValueMin(); - mitk::ScalarType max = image->GetScalarValueMax(); + mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(); + mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType windowSize = max - min; windowSize = 0.15*windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue < m_SeedPointValueMean) m_LOWERTHRESHOLD = m_SeedpointValue; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; } this->m_Controls.m_LowerThresholdSpinBox->setValue(m_LOWERTHRESHOLD); this->m_Controls.m_UpperThresholdSpinBox->setValue(m_UPPERTHRESHOLD); } } void QmitkAdaptiveRegionGrowingWidget::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } //release the "define seed point"-button if it is still pressed if (m_Controls.m_pbDefineSeedPoint->isChecked()) m_Controls.m_pbDefineSeedPoint->toggle(); mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); if (node.IsNull()) { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Please insert a seed point inside the image.\n\nFirst press the \"Define Seed Point\" button,\nthen click left mouse button inside the image."); return; } //safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast (node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } if (!(seedPointSet->GetSize(m_MultiWidget->GetTimeNavigationController()->GetTime()->GetPos()) > 0)) { m_Controls.m_pbRunSegmentation->setEnabled(true); m_Controls.m_pbDefineSeedPoint->setHidden(false); QMessageBox::information( NULL, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } int timeStep = m_MultiWidget->GetTimeNavigationController()->GetTime()->GetPos(); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr( timeStep ); timeSelector->UpdateLargestPossibleRegion(); mitk::Image* timedImage = timeSelector->GetOutput(); AccessByItk_2( timedImage , StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); QApplication::restoreOverrideCursor();//reset cursor } else { QMessageBox::information( NULL, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } } template void QmitkAdaptiveRegionGrowingWidget::StartRegionGrowing(itk::Image* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::MinimumMaximumImageCalculator MinMaxValueFilterType; if ( !imageGeometry->IsInside(seedPoint) ) { QApplication::restoreOverrideCursor();//reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( NULL, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex( seedPoint, seedIndex);// convert world coordinates to image indices if (m_SeedpointValue>m_UPPERTHRESHOLD || m_SeedpointValueSetGrowingDirectionIsUpwards( m_CurrentRGDirectionIsUpwards ); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); //In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. //Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter regionGrower->SetLower( m_LOWERTHRESHOLD-1 ); regionGrower->SetUpper( m_UPPERTHRESHOLD+1); try { regionGrower->Update(); } catch(itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch( ... ) { QMessageBox::critical( NULL, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } this->m_SeedpointValue = regionGrower->GetSeedpointValue(); //initialize slider if(m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_Slider->setMinimum(m_LOWERTHRESHOLD); this->m_Controls.m_Slider->setMaximum(m_UPPERTHRESHOLD); } else { m_Controls.m_Slider->setMinimum(m_LOWERTHRESHOLD); m_Controls.m_Slider->setMaximum(m_UPPERTHRESHOLD); } this->m_SliderInitialized = true; this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); mitk::Image::Pointer resultImage = mitk::ImportItkImage( regionGrower->GetOutput() ); //create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData( resultImage ); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(1.0,0.0,0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); //delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add( newNode, m_InputImageNode ); this->InitializeLevelWindow(); if(m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true;// reset first stored threshold value //Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); } void QmitkAdaptiveRegionGrowingWidget::InitializeLevelWindow() { //get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); mitk::ScalarType* level = new mitk::ScalarType(0.0); mitk::ScalarType* window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); //get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_Slider->setValue(this->m_SeedpointValue + this->m_DetectedLeakagePoint -1); *level = m_UPPERTHRESHOLD - (this->m_SeedpointValue + this->m_DetectedLeakagePoint -1) + 0.5; } else { this->m_Controls.m_Slider->setValue(this->m_SeedpointValue - this->m_DetectedLeakagePoint +1); *level = (this->m_SeedpointValue + this->m_DetectedLeakagePoint +1) - m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; //inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint-1; this->m_MultiWidget->levelWindowWidget->GetManager()->SetAutoTopMostImage(false); this->m_MultiWidget->levelWindowWidget->GetManager()->SetLevelWindowProperty(static_cast(newNode->GetProperty("levelwindow"))); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (*level + 0.5));//lower threshold for labeled image } void QmitkAdaptiveRegionGrowingWidget::ChangeLevelWindow(int newValue) { if (m_SliderInitialized) { //do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //get the levelWindow associated with the preview mitk::ScalarType level;// = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType* window = new mitk::ScalarType(1); //adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UPPERTHRESHOLD - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD +0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, NULL, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int) (level - 0.5));//lower threshold for labeled image this->m_Controls.m_SliderValueLabel->setNum(newValue); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingWidget::DecreaseSlider() { //moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_Slider->value() != this->m_Controls.m_Slider->minimum()) { int newValue = this->m_Controls.m_Slider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_Slider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingWidget::IncreaseSlider() { //moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_Slider->value() != this->m_Controls.m_Slider->maximum()) { int newValue = this->m_Controls.m_Slider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_Slider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingWidget::ConfirmSegmentation() { //get image node if(m_InputImageNode.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } //get image data mitk::Image::Pointer orgImage = dynamic_cast (m_InputImageNode->GetData()); if(orgImage.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Image found!"); return; } //get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image*)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(labeledSeg.IsNull()) { QMessageBox::critical( NULL, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::Image* img = dynamic_cast(newNode->GetData()); AccessByItk(img, ITKThresholding); + // disable volume rendering preview after the segmentation node was created + this->EnableVolumeRendering(false); + m_Controls.m_cbVolumeRendering->setChecked(false); } template void QmitkAdaptiveRegionGrowingWidget::ITKThresholding(itk::Image* itkImage) { typedef itk::Image InputImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(itkImage); filter->SetInsideValue(1); filter->SetOutsideValue(0); mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, NULL, "levelwindow"); //get the levelWindow associated with the preview filter->SetUpperThreshold(tempLevelWindow.GetRangeMax()); if (m_CurrentRGDirectionIsUpwards) { filter->SetLowerThreshold(tempLevelWindow.GetLevel()+0.5); } else { filter->SetLowerThreshold(tempLevelWindow.GetLevel()+0.5); } filter->Update(); mitk::Image::Pointer new_image = mitk::Image::New(); mitk::CastToMitkImage(filter->GetOutput(), new_image); mitk::DataNode::Pointer segNode = mitk::DataNode::New(); segNode->SetData(new_image); segNode->SetName("RegionGrowing_Result"); segNode->SetBoolProperty("binary", mitk::BoolProperty::New(true)); //delete the old image, if there was one: mitk::DataNode::Pointer prevSegNode = m_DataStorage->GetNamedNode("RegionGrowing_Result"); m_DataStorage->Remove(prevSegNode); m_DataStorage->Add(segNode, m_InputImageNode); } void QmitkAdaptiveRegionGrowingWidget::EnableControls(bool enable) { //set the labels below the slider this->m_Controls.m_RSliderLabelLower->setText("Shrink"); this->m_Controls.m_RGSliderLaberUpper->setText("Expand"); this->m_Controls.m_RSliderLabelLower->setEnabled(enable); this->m_Controls.m_RGSliderLaberUpper->setEnabled(enable); this->m_Controls.m_pbRunSegmentation->setEnabled(enable); this->m_Controls.m_DecreaseTH->setEnabled(enable); this->m_Controls.m_IncreaseTH->setEnabled(enable); this->m_Controls.m_Slider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } void QmitkAdaptiveRegionGrowingWidget::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); if(node.IsNull()) return; if(m_MultiWidget) m_MultiWidget->SetWidgetPlanesVisibility(!enable); if (enable) { node->SetBoolProperty("volumerendering", enable); node->SetBoolProperty("volumerendering.uselod", true); } else { node->SetBoolProperty("volumerendering", enable); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingWidget::UpdateVolumeRenderingThreshold(int thValue) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); if (m_UpdateSuggestedThreshold) { m_SuggestedThValue = thValue; m_UpdateSuggestedThreshold = false; } // grayvalue->opacity { vtkPiecewiseFunction *f = tf->GetScalarOpacityFunction(); f->RemoveAllPoints(); f->AddPoint(0, 0); f->AddPoint(thValue+0.5, 0); f->AddPoint(thValue+1.5, 1); f->AddPoint(1000, 1); f->ClampingOn(); f->Modified(); } // grayvalue->color { float a = 255.0; vtkColorTransferFunction *ctf = tf->GetColorTransferFunction(); ctf->RemoveAllPoints(); //ctf->AddRGBPoint(-1000, 0.0, 0.0, 0.0); ctf->AddRGBPoint(m_SuggestedThValue+1, 203/a, 104/a, 102/a); ctf->AddRGBPoint(m_SuggestedThValue, 255/a, 0/a, 0/a); ctf->ClampingOn(); ctf->Modified(); } // GradientOpacityFunction { vtkPiecewiseFunction *gof = tf->GetGradientOpacityFunction(); gof->RemoveAllPoints(); gof->AddPoint(-10000, 1); gof->AddPoint(10000, 1); gof->ClampingOn(); gof->Modified(); } mitk::TransferFunctionProperty::Pointer tfp = mitk::TransferFunctionProperty::New(); tfp->SetValue(tf); node->SetProperty("TransferFunction", tfp); } void QmitkAdaptiveRegionGrowingWidget::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingWidget::OnDefineThresholdBoundaries(bool status) { m_Controls.m_LowerThresholdSpinBox->setEnabled(status); m_Controls.m_UpperThresholdSpinBox->setEnabled(status); m_Controls.lb_LowerTh->setEnabled(status); m_Controls.lb_UpperTh->setEnabled(status); } void QmitkAdaptiveRegionGrowingWidget::SetLowerThresholdValue( double lowerThreshold ) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingWidget::SetUpperThresholdValue( double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingWidget::Deactivated() { this->DeactivateSeedPointMode(); + + // make the segmentation preview node invisible + mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode( m_NAMEFORLABLEDSEGMENTATIONIMAGE); + if( node.IsNotNull() ) + { + node->SetVisibility(false); + } + + // disable volume rendering preview after the segmentation node was created + this->EnableVolumeRendering(false); + m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingWidget::Activated() { - if(m_Controls.m_pbDefineSeedPoint->isChecked()) - { - this->ActivateSeedPointMode(); - } + } void QmitkAdaptiveRegionGrowingWidget::ActivateSeedPointMode() { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); if(node.IsNotNull()) { //set the cursor to cross QApplication::setOverrideCursor(QCursor(Qt::CrossCursor)); mitk::PointSetInteractor::Pointer interactor = dynamic_cast (node->GetInteractor()); if (interactor.IsNull()) { //create a new interactor and add it to node interactor = mitk::PointSetInteractor::New("singlepointinteractorwithoutshiftclick", node, 1); } mitk::GlobalInteraction::GetInstance()->AddInteractor(interactor); } } void QmitkAdaptiveRegionGrowingWidget::DeactivateSeedPointMode() { //set the cursor to default again QApplication::restoreOverrideCursor(); mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORSEEDPOINT); if(node.IsNotNull()) { mitk::PointSetInteractor::Pointer interactor = dynamic_cast (node->GetInteractor()); if (interactor.IsNotNull()) { mitk::GlobalInteraction::GetInstance()->RemoveInteractor(interactor); } } } diff --git a/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.h b/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.h index ebf6562256..f87d323aef 100644 --- a/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.h +++ b/Modules/QmitkExt/QmitkAdaptiveRegionGrowingWidget.h @@ -1,158 +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. ===================================================================*/ #ifndef QMITK_AdaptiveRegionGrowingWidget_H #define QMITK_AdaptiveRegionGrowingWidget_H #include "mitkDataStorage.h" #include "itkImage.h" #include "mitkGeometry3D.h" #include "mitkPointSet.h" #include "qwidget.h" #include "ui_QmitkAdaptiveRegionGrowingWidgetControls.h" #include "QmitkExtExports.h" class QmitkStdMultiWidget; class DataNode; class QmitkAdaptiveRegionGrowingWidgetControls; /*! * * \brief QmitkAdaptiveRegionGrowingWidget * * Adaptive Region Growing View class of the segmentation. * */ class QmitkExt_EXPORT QmitkAdaptiveRegionGrowingWidget : public QWidget { Q_OBJECT public: typedef QmitkAdaptiveRegionGrowingWidget Self; /** * @brief Constructor. **/ QmitkAdaptiveRegionGrowingWidget(QWidget *parent=0); /** \brief Destructor. */ virtual ~QmitkAdaptiveRegionGrowingWidget(); /** \brief Method to create the connections for the component. This Method is obligatory even if no connections is needed*/ virtual void CreateConnections(); ///** \brief Method to set the default data storage.*/ virtual void SetDataStorage(mitk::DataStorage* dataStorage); void SetMultiWidget(QmitkStdMultiWidget* multiWidget); void SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, /*std::string vesselTree,*/ std::string surface, std::string seedPoint); void EnableControls(bool enable); void SetInputImageNode(mitk::DataNode* node); void Deactivated(); void Activated(); /** * @brief The created GUI from the .ui-File. This Attribute is obligatory */ Ui::QmitkAdaptiveRegionGrowingWidgetControls m_Controls; protected slots: void SetSeedPointToggled(bool toggled); void RunSegmentation(); void ChangeLevelWindow(int newValue);//called, when the Level Window is changed via the slider in the ControlWidget //****called, when the slider-position is modified via the +/- buttons void IncreaseSlider(); void DecreaseSlider(); //*** void ConfirmSegmentation(); void UseVolumeRendering(bool on); void OnDefineThresholdBoundaries(bool); void SetLowerThresholdValue(double lowerThreshold); void SetUpperThresholdValue(double upperThreshold); protected: //Pointer to the main widget to be able to reach the renderer QmitkStdMultiWidget* m_MultiWidget; mitk::DataStorage* m_DataStorage; mitk::DataNode::Pointer m_InputImageNode; void DeactivateSeedPointMode(); void ActivateSeedPointMode(); void OnPointAdded(); private: std::string m_NAMEFORORGIMAGE; std::string m_NAMEFORSEEDPOINT; std::string m_NAMEFORLABLEDSEGMENTATIONIMAGE; std::string m_NAMEFORBINARYIMAGE; std::string m_NAMEFORSURFACE; mitk::ScalarType m_LOWERTHRESHOLD; //Hounsfield value mitk::ScalarType m_UPPERTHRESHOLD; //Hounsfield value mitk::ScalarType m_SeedPointValueMean; + void RemoveHelperNodes(); + int m_DetectedLeakagePoint; bool m_CurrentRGDirectionIsUpwards; // defines fixed threshold (true = LOWERTHRESHOLD fixed, false = UPPERTHRESHOLD fixed) int m_SeedpointValue; bool m_SliderInitialized; bool m_UseVolumeRendering; bool m_UpdateSuggestedThreshold; float m_SuggestedThValue; long m_PointSetAddObserverTag; template < typename TPixel, unsigned int VImageDimension > void StartRegionGrowing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Geometry3D* imageGeometry, mitk::PointSet::PointType seedPoint ); template < typename TPixel, unsigned int VImageDimension > void ITKThresholding( itk::Image< TPixel, VImageDimension >* inputImage ); void InitializeLevelWindow(); void EnableVolumeRendering(bool enable); void UpdateVolumeRenderingThreshold(int thValue); }; #endif diff --git a/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.cpp b/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.cpp index 15497af410..2a800ee5eb 100644 --- a/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.cpp +++ b/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.cpp @@ -1,210 +1,212 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkBinaryThresholdToolGUI.h" #include "QmitkNewSegmentationDialog.h" #include #include #include #include MITK_TOOL_GUI_MACRO(QmitkExt_EXPORT, QmitkBinaryThresholdToolGUI, "") QmitkBinaryThresholdToolGUI::QmitkBinaryThresholdToolGUI() :QmitkToolGUI(), m_Slider(NULL), m_Spinner(NULL), m_isFloat(false), m_RangeMin(0), m_RangeMax(0), m_ChangingSlider(false), m_ChangingSpinner(false) { // create the visible widgets QBoxLayout* mainLayout = new QVBoxLayout(this); QLabel* label = new QLabel( "Threshold :", this ); QFont f = label->font(); f.setBold(false); label->setFont( f ); mainLayout->addWidget(label); QBoxLayout* layout = new QHBoxLayout(); m_Spinner = new QDoubleSpinBox(); m_Spinner->setMaximum(20); m_Spinner->setMinimum(5); m_Spinner->setValue(1); connect(m_Spinner, SIGNAL(valueChanged(double)), this, SLOT(OnSpinnerValueChanged()) ); layout->addWidget(m_Spinner); //m_Slider = new QSlider( 5, 20, 1, 1, Qt::Horizontal, this ); m_Slider = new QSlider( Qt::Horizontal, this ); m_Slider->setMinimum(5); m_Slider->setMaximum(20); m_Slider->setPageStep(1); m_Slider->setValue(1); connect( m_Slider, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); layout->addWidget( m_Slider ); - QPushButton* okButton = new QPushButton("Ok", this); + QPushButton* okButton = new QPushButton("Create Segmentation", this); connect( okButton, SIGNAL(clicked()), this, SLOT(OnAcceptThresholdPreview())); okButton->setFont( f ); layout->addWidget( okButton ); mainLayout->addLayout(layout); connect( this, SIGNAL(NewToolAssociated(mitk::Tool*)), this, SLOT(OnNewToolAssociated(mitk::Tool*)) ); } QmitkBinaryThresholdToolGUI::~QmitkBinaryThresholdToolGUI() { // !!! if (m_BinaryThresholdTool.IsNotNull()) { m_BinaryThresholdTool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged ); m_BinaryThresholdTool->ThresholdingValueChanged -= mitk::MessageDelegate1( this, &QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged ); } } void QmitkBinaryThresholdToolGUI::OnNewToolAssociated(mitk::Tool* tool) { if (m_BinaryThresholdTool.IsNotNull()) { m_BinaryThresholdTool->IntervalBordersChanged -= mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged ); m_BinaryThresholdTool->ThresholdingValueChanged -= mitk::MessageDelegate1( this, &QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged ); } m_BinaryThresholdTool = dynamic_cast( tool ); if (m_BinaryThresholdTool.IsNotNull()) { m_BinaryThresholdTool->IntervalBordersChanged += mitk::MessageDelegate3( this, &QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged ); m_BinaryThresholdTool->ThresholdingValueChanged += mitk::MessageDelegate1( this, &QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged ); } } void QmitkBinaryThresholdToolGUI::OnSpinnerValueChanged() { if (m_BinaryThresholdTool.IsNotNull()) { m_ChangingSpinner = true; double doubleVal = m_Spinner->value(); int intVal = this->DoubleToSliderInt(doubleVal); m_BinaryThresholdTool->SetThresholdValue( doubleVal ); if (m_ChangingSlider == false) m_Slider->setValue( intVal ); m_ChangingSpinner = false; } } void QmitkBinaryThresholdToolGUI::OnSliderValueChanged(int value) { if (m_BinaryThresholdTool.IsNotNull()) { m_ChangingSlider = true; double doubleVal = SliderIntToDouble(value); if (m_ChangingSpinner == false) m_Spinner->setValue(doubleVal); m_ChangingSlider = false; } } void QmitkBinaryThresholdToolGUI::OnAcceptThresholdPreview() { if (m_BinaryThresholdTool.IsNotNull()) { QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( this ); // needs a QWidget as parent, "this" is not QWidget dialog->setPrompt("What did you just segment?"); int dialogReturnValue = dialog->exec(); std::string organName = dialog->GetSegmentationName().toStdString(); mitk::Color color = dialog->GetColor(); delete dialog; if ( dialogReturnValue != QDialog::Rejected ) // user clicked cancel or pressed Esc or something similar { + this->thresholdAccepted(); m_BinaryThresholdTool->AcceptCurrentThresholdValue( organName, color ); } else { + this->thresholdCanceled(); m_BinaryThresholdTool->CancelThresholding(); } } } void QmitkBinaryThresholdToolGUI::OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat) { m_isFloat = isFloat; m_RangeMin = lower; m_RangeMax = upper; m_Spinner->setRange(lower, upper); if (!m_isFloat) { m_Slider->setRange(int(lower), int(upper)); m_Spinner->setDecimals(0); m_Spinner->setSingleStep(1); } else { m_Slider->setRange(0, 99); m_Spinner->setDecimals(2); m_Range = upper-lower; m_Spinner->setSingleStep(m_Range/100); } } void QmitkBinaryThresholdToolGUI::OnThresholdingValueChanged(double current) { m_Slider->setValue(DoubleToSliderInt(current)); m_Spinner->setValue(current); } double QmitkBinaryThresholdToolGUI::SliderIntToDouble(int val) { if (!m_isFloat) { return double(val); } else { return double(val*(m_Range)/100 + m_RangeMin); } } int QmitkBinaryThresholdToolGUI::DoubleToSliderInt(double val) { if (!m_isFloat) { return int(val); } else { int intVal = int( ((val-m_RangeMin) / m_Range)*100); return intVal; } } diff --git a/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.h b/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.h index ae86feab8b..d9d97e3ad6 100644 --- a/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.h +++ b/Modules/QmitkExt/QmitkBinaryThresholdToolGUI.h @@ -1,93 +1,99 @@ /*=================================================================== 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 QmitkBinaryThresholdToolGUI_h_Included #define QmitkBinaryThresholdToolGUI_h_Included #include "QmitkToolGUI.h" #include "QmitkExtExports.h" #include "mitkBinaryThresholdTool.h" #include class QSlider; /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for mitk::BinaryThresholdTool. This GUI shows a slider to change the tool's threshold and an OK button to accept a preview for actual thresholding. There is only a slider for INT values in QT. So, if the working image has a float/double pixeltype, we need to convert the original float intensity into a respective int value for the slider. The slider range is then between 0 and 99. If the pixeltype is INT, then we do not need any conversion. Last contributor: $Author$ */ class QmitkExt_EXPORT QmitkBinaryThresholdToolGUI : public QmitkToolGUI { Q_OBJECT public: mitkClassMacro(QmitkBinaryThresholdToolGUI, QmitkToolGUI); itkNewMacro(QmitkBinaryThresholdToolGUI); void OnThresholdingIntervalBordersChanged(double lower, double upper, bool isFloat); void OnThresholdingValueChanged(double current); signals: + /// \brief Emitted when threshold Accepted + void thresholdAccepted(); + + /// \brief Emitted when threshold Canceled + void thresholdCanceled(); + public slots: protected slots: void OnNewToolAssociated(mitk::Tool*); void OnAcceptThresholdPreview(); /// \brief Called when Spinner value has changed. Consider: Spinner contains DOUBLE values void OnSpinnerValueChanged(); /// \brief Called when Slider value has changed. Consider: Slider contains INT values void OnSliderValueChanged(int value); protected: QmitkBinaryThresholdToolGUI(); virtual ~QmitkBinaryThresholdToolGUI(); /// \brief When Slider (int value) has changed, we need to convert it to a respective double value for the spinner double SliderIntToDouble(int val); /// \brief When Spinner (double value) has changed, we need to convert it to a respective int value for the slider int DoubleToSliderInt(double val); QSlider* m_Slider; QDoubleSpinBox* m_Spinner; /// \brief is image float or int? bool m_isFloat; double m_RangeMin; double m_RangeMax; double m_Range; /// \brief helper bool values to find out, which of the GUI elements has been touched by the user. bool m_ChangingSlider, m_ChangingSpinner; mitk::BinaryThresholdTool::Pointer m_BinaryThresholdTool; }; #endif diff --git a/Modules/QmitkExt/QmitkHistogramWidget.cpp b/Modules/QmitkExt/QmitkHistogramWidget.cpp index 8243a070c1..1ff9420c9d 100644 --- a/Modules/QmitkExt/QmitkHistogramWidget.cpp +++ b/Modules/QmitkExt/QmitkHistogramWidget.cpp @@ -1,186 +1,187 @@ /*=================================================================== 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 "QmitkHistogramWidget.h" #include "mitkImageStatisticsHolder.h" #include #include #include #include #include #include #include #include #include //#include -QmitkHistogramWidget::QmitkHistogramWidget(QWidget * /*parent*/, bool showreport) +QmitkHistogramWidget::QmitkHistogramWidget(QWidget * parent, bool showreport) + : QDialog(parent) { QBoxLayout *layout = new QVBoxLayout(this); //***histogram*** QGroupBox *hgroupbox = new QGroupBox("", this); hgroupbox->setMinimumSize(900, 400); m_Plot = new QwtPlot(hgroupbox); m_Plot->setCanvasBackground(QColor(Qt::white)); m_Plot->setTitle("Histogram"); QwtText text = m_Plot->titleLabel()->text(); text.setFont(QFont("Helvetica", 12, QFont::Normal)); QwtPlotGrid *grid = new QwtPlotGrid; grid->enableXMin(true); grid->enableYMin(true); grid->setMajPen(QPen(Qt::black, 0, Qt::DotLine)); grid->setMinPen(QPen(Qt::gray, 0 , Qt::DotLine)); grid->attach(m_Plot); layout->addWidget(hgroupbox); layout->setSpacing(20); if (showreport == true) { //***report*** QGroupBox *rgroupbox = new QGroupBox("", this); rgroupbox->setMinimumSize(900, 400); QLabel *label = new QLabel("Gray Value Analysis", rgroupbox); label->setAlignment(Qt::AlignHCenter); label->setFont(QFont("Helvetica", 14, QFont::Bold)); m_Textedit = new QTextEdit(rgroupbox); m_Textedit->setFont(QFont("Helvetica", 12, QFont::Normal)); m_Textedit->setReadOnly(true); layout->addWidget(rgroupbox); } m_Picker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, QwtPicker::PointSelection, QwtPlotPicker::NoRubberBand, QwtPicker::AlwaysOn, m_Plot->canvas()); connect(m_Picker, SIGNAL(selected(const QwtDoublePoint &)), SLOT(OnSelect(const QwtDoublePoint &))); } QmitkHistogramWidget::~QmitkHistogramWidget() { } void QmitkHistogramWidget::SetHistogram(HistogramType::ConstPointer itkHistogram) { HistogramType::SizeType size = itkHistogram->GetSize(); HistogramType::IndexType index; HistogramType::MeasurementVectorType currentMeasurementVector; QwtArray xValues(size[0]); QwtArray yValues(size[0]); for (unsigned int i = 0; i < size[0]; ++i) { #if !defined(ITK_USE_REVIEW_STATISTICS) index[0] = static_cast (i); #else index[0] = static_cast (i); #endif currentMeasurementVector = itkHistogram->GetMeasurementVector(index); if (currentMeasurementVector[0] != 0.0) { xValues[i] = QwtDoubleInterval(Round(currentMeasurementVector[0]-1), Round(currentMeasurementVector[0])); yValues[i] = static_cast (itkHistogram->GetFrequency(index)); } } // rebuild the plot m_Plot->clear(); m_Histogram = new QmitkHistogram(); m_Histogram->setColor(Qt::darkCyan); m_Histogram->setData(QwtIntervalData(xValues, yValues)); m_Histogram->attach(m_Plot); this->InitializeMarker(); this->InitializeZoomer(); m_Plot->replot(); } void QmitkHistogramWidget::SetHistogram( mitk::Image* mitkImage ) { this->SetHistogram(mitkImage->GetStatistics()->GetScalarHistogram()); } void QmitkHistogramWidget::SetReport(std::string report) { m_Textedit->setText(report.c_str()); } void QmitkHistogramWidget::InitializeMarker() { m_Marker = new QwtPlotMarker(); m_Marker->setXValue(0.); m_Marker->setLineStyle(QwtPlotMarker::VLine); m_Marker->setLabelAlignment(Qt::AlignHCenter | Qt::AlignRight); m_Marker->setLinePen(QPen(QColor(200,150,0), 3, Qt::SolidLine)); m_Marker->setSymbol( QwtSymbol(QwtSymbol::Diamond, QColor(Qt::red), QColor(Qt::red), QSize(10,10))); m_Marker->attach(m_Plot); } void QmitkHistogramWidget::InitializeZoomer() { m_Zoomer = new QwtPlotZoomer(m_Plot->xBottom, m_Plot->yLeft, m_Plot->canvas()); m_Zoomer->setRubberBandPen(QPen(Qt::red, 2, Qt::DotLine)); m_Zoomer->setTrackerPen(QPen(Qt::red)); m_Zoomer->setSelectionFlags(QwtPlotZoomer::RectSelection); } void QmitkHistogramWidget::OnSelect( const QwtDoublePoint& pos ) { m_Marker->setXValue( this->Round(pos.x()) ); //unsigned int count = (unsigned int)(m_Histogram->data().value(pos.x())); QString str = QString( "%1" ) .arg( (int)(this->Round(pos.x())), 0, 10 ); QwtText text(str); text.setBackgroundBrush(QColor(200,150,0)); text.setFont(QFont("Helvetica", 14, QFont::Bold)); m_Marker->setLabel(text); m_Plot->replot(); } double QmitkHistogramWidget::GetMarkerPosition() { return m_Marker->xValue(); } double QmitkHistogramWidget::Round(double val) { double ival = (double)(int)val; if( (val - ival) > 0.5) return ival+1; else return ival; } diff --git a/Modules/QmitkExt/QmitkSlicesInterpolator.cpp b/Modules/QmitkExt/QmitkSlicesInterpolator.cpp index cd96703bf9..55b3db52db 100644 --- a/Modules/QmitkExt/QmitkSlicesInterpolator.cpp +++ b/Modules/QmitkExt/QmitkSlicesInterpolator.cpp @@ -1,1007 +1,1008 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) -Copyright (c) German Cancer Research Center, +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 +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 "QmitkSlicesInterpolator.h" #include "QmitkStdMultiWidget.h" #include "QmitkSelectableGLWidget.h" #include "mitkToolManager.h" #include "mitkDataNodeFactory.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkProgressBar.h" #include "mitkGlobalInteraction.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkInteractionConst.h" #include "mitkApplyDiffImageOperation.h" #include "mitkDiffImageApplier.h" #include "mitkSegTool2D.h" #include "mitkCoreObjectFactory.h" #include "mitkSurfaceToImageFilter.h" #include #include #include #include #include #include #include #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) const std::map QmitkSlicesInterpolator::createActionToSliceDimension() { std::map actionToSliceDimension; actionToSliceDimension[new QAction("Transversal (red window)", 0)] = 2; actionToSliceDimension[new QAction("Sagittal (green window)", 0)] = 0; actionToSliceDimension[new QAction("Coronal (blue window)", 0)] = 1; return actionToSliceDimension; } QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget* parent, const char* /*name*/) :QWidget(parent), ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ), m_Interpolator( mitk::SegmentationInterpolationController::New() ), m_MultiWidget(NULL), m_ToolManager(NULL), m_Initialized(false), m_LastSliceDimension(2), m_LastSliceIndex(0), m_2DInterpolationEnabled(false), m_3DInterpolationEnabled(false) { m_SurfaceInterpolator = mitk::SurfaceInterpolationController::GetInstance(); QHBoxLayout* layout = new QHBoxLayout(this); m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this); QGridLayout* grid = new QGridLayout(m_GroupBoxEnableExclusiveInterpolationMode); m_RBtnEnable3DInterpolation = new QRadioButton("3D",this); connect(m_RBtnEnable3DInterpolation, SIGNAL(toggled(bool)), this, SLOT(On3DInterpolationEnabled(bool))); connect(m_RBtnEnable3DInterpolation, SIGNAL(toggled(bool)), this, SIGNAL(Signal3DInterpolationEnabled(bool))); m_RBtnEnable3DInterpolation->setChecked(true); grid->addWidget(m_RBtnEnable3DInterpolation,0,0); m_BtnAccept3DInterpolation = new QPushButton("Accept", this); m_BtnAccept3DInterpolation->setEnabled(false); connect(m_BtnAccept3DInterpolation, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked())); grid->addWidget(m_BtnAccept3DInterpolation, 0,1); m_CbShowMarkers = new QCheckBox("Show Position Nodes", this); m_CbShowMarkers->setChecked(true); connect(m_CbShowMarkers, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); connect(m_CbShowMarkers, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool))); grid->addWidget(m_CbShowMarkers,0,2); m_RBtnEnable2DInterpolation = new QRadioButton("2D",this); connect(m_RBtnEnable2DInterpolation, SIGNAL(toggled(bool)), this, SLOT(On2DInterpolationEnabled(bool))); grid->addWidget(m_RBtnEnable2DInterpolation,1,0); m_BtnAcceptInterpolation = new QPushButton("Accept", this); m_BtnAcceptInterpolation->setEnabled( false ); connect( m_BtnAcceptInterpolation, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked()) ); grid->addWidget(m_BtnAcceptInterpolation,1,1); m_BtnAcceptAllInterpolations = new QPushButton("... for all slices", this); m_BtnAcceptAllInterpolations->setEnabled( false ); connect( m_BtnAcceptAllInterpolations, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked()) ); grid->addWidget(m_BtnAcceptAllInterpolations,1,2); m_RBtnDisableInterpolation = new QRadioButton("Disable", this); connect(m_RBtnDisableInterpolation, SIGNAL(toggled(bool)), this, SLOT(OnInterpolationDisabled(bool))); grid->addWidget(m_RBtnDisableInterpolation, 2,0); layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode); this->setLayout(layout); - + itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged ); InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver( itk::ModifiedEvent(), command ); itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged ); SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver( itk::ModifiedEvent(), command2 ); - + // feedback node and its visualization properties m_FeedbackNode = mitk::DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_FeedbackNode ); - + m_FeedbackNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "color", mitk::ColorProperty::New(255.0, 255.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", mitk::BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", mitk::IntProperty::New( 20 ) ); m_FeedbackNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_FeedbackNode->SetProperty( "name", mitk::StringProperty::New("Interpolation feedback") ); m_FeedbackNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_FeedbackNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetProperty( "color", mitk::ColorProperty::New(255.0,255.0,0.0) ); m_InterpolatedSurfaceNode->SetProperty( "name", mitk::StringProperty::New("Surface Interpolation feedback") ); m_InterpolatedSurfaceNode->SetProperty( "opacity", mitk::FloatProperty::New(0.5) ); m_InterpolatedSurfaceNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetProperty( "color", mitk::ColorProperty::New(0.0, 0.0, 0.0) ); m_3DContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "name", mitk::StringProperty::New("Drawn Contours") ); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); - + QWidget::setContentsMargins(0, 0, 0, 0); if ( QWidget::layout() != NULL ) { QWidget::layout()->setContentsMargins(0, 0, 0, 0); } //For running 3D Interpolation in background // create a QFuture and a QFutureWatcher connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(SurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); } void QmitkSlicesInterpolator::SetDataStorage( mitk::DataStorage& storage ) { m_DataStorage = &storage; m_SurfaceInterpolator->SetDataStorage(storage); } mitk::DataStorage* QmitkSlicesInterpolator::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void QmitkSlicesInterpolator::Initialize(mitk::ToolManager* toolManager, QmitkStdMultiWidget* multiWidget) { if (m_Initialized) { // remove old observers if (m_ToolManager) { - m_ToolManager->WorkingDataChanged + m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); - - m_ToolManager->ReferenceDataChanged + + m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); } - + if (m_MultiWidget) { disconnect( m_MultiWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnMultiWidgetDeleted(QObject*)) ); mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( TSliceObserverTag ); slicer->RemoveObserver( TTimeObserverTag ); slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( SSliceObserverTag ); slicer->RemoveObserver( STimeObserverTag ); slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( FSliceObserverTag ); slicer->RemoveObserver( FTimeObserverTag ); } - + //return; } - + m_MultiWidget = multiWidget; - + connect( m_MultiWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnMultiWidgetDeleted(QObject*)) ); - + m_ToolManager = toolManager; - + if (m_ToolManager) { // set enabled only if a segmentation is selected mitk::DataNode* node = m_ToolManager->GetWorkingData(0); QWidget::setEnabled( node != NULL ); - + // react whenever the set of selected segmentation changes m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); - + // connect to the steppers of the three multi widget widgets. after each change, call the interpolator if (m_MultiWidget) { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); m_TimeStep.resize(3); m_TimeStep[2] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTransversalTimeChanged ); TTimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } - + { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTransversalSliceChanged ); TSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } - + // connect to the steppers of the three multi widget widgets. after each change, call the interpolator slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); m_TimeStep[0] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSagittalTimeChanged ); STimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } - + { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSagittalSliceChanged ); SSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } - + // connect to the steppers of the three multi widget widgets. after each change, call the interpolator slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); m_TimeStep[1] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnFrontalTimeChanged ); FTimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } - + { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnFrontalSliceChanged ); FSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } } } - + m_Initialized = true; } QmitkSlicesInterpolator::~QmitkSlicesInterpolator() -{ +{ if (m_MultiWidget) { mitk::SliceNavigationController* slicer; if(m_MultiWidget->mitkWidget1 != NULL) { slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( TSliceObserverTag ); slicer->RemoveObserver( TTimeObserverTag ); } if(m_MultiWidget->mitkWidget2 != NULL) { slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( SSliceObserverTag ); slicer->RemoveObserver( STimeObserverTag ); } if(m_MultiWidget->mitkWidget3 != NULL) { slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( FSliceObserverTag ); slicer->RemoveObserver( FTimeObserverTag ); } } if(m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if(m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); delete m_Timer; } void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status) { OnInterpolationActivated(status); } void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status) { On3DInterpolationActivated(status); } void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status) { if (status) { OnInterpolationActivated(!status); On3DInterpolationActivated(!status); } } void QmitkSlicesInterpolator::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } } void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified() { if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } if (m_3DInterpolationEnabled) { On3DInterpolationActivated( true); } } void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified() { if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } if (m_3DInterpolationEnabled) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); } } void QmitkSlicesInterpolator::OnTransversalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[2] = event.GetPos(); - + if (m_LastSliceDimension == 2) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnSagittalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[0] = event.GetPos(); - + if (m_LastSliceDimension == 0) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnFrontalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[1] = event.GetPos(); - + if (m_LastSliceDimension == 1) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnTransversalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 2 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget1->GetRenderWindow())->RequestUpdate(); } } } void QmitkSlicesInterpolator::OnSagittalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 0 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget2->GetRenderWindow())->RequestUpdate(); } } } void QmitkSlicesInterpolator::OnFrontalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 1 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget3->GetRenderWindow())->RequestUpdate(); } } } bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e, unsigned int windowID) { if (!m_2DInterpolationEnabled) return false; - + try { const mitk::SliceNavigationController::GeometrySliceEvent& event = dynamic_cast(e); - + mitk::TimeSlicedGeometry* tsg = event.GetTimeSlicedGeometry(); if (tsg && m_TimeStep.size() > windowID) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(tsg->GetGeometry3D(m_TimeStep[windowID])); if (slicedGeometry) { mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetGeometry2D( event.GetPos() )); if (plane) Interpolate( plane, m_TimeStep[windowID] ); return true; - } + } } } catch(std::bad_cast) { return false; // so what } - + return false; } void QmitkSlicesInterpolator::Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep ) { if (m_ToolManager) { mitk::DataNode* node = m_ToolManager->GetWorkingData(0); if (node) { m_Segmentation = dynamic_cast(node->GetData()); if (m_Segmentation) { int clickedSliceDimension(-1); int clickedSliceIndex(-1); - + // calculate real slice position, i.e. slice of the image and not slice of the TimeSlicedGeometry mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex ); mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( clickedSliceDimension, clickedSliceIndex, timeStep ); m_FeedbackNode->SetData( interpolation ); // Workaround for Bug 11318 if ((interpolation.IsNotNull()) && (interpolation->GetGeometry() != NULL)) { if(clickedSliceDimension == 1) { mitk::Point3D orig = interpolation->GetGeometry()->GetOrigin(); - orig[0] = orig[0]; - orig[1] = orig[1] + 0.5; + orig[0] = orig[0]; + orig[1] = orig[1] + 0.5; orig[2] = orig[2]; interpolation->GetGeometry()->SetOrigin(orig); } } // Workaround for Bug 11318 END - + m_LastSliceDimension = clickedSliceDimension; m_LastSliceIndex = clickedSliceIndex; } } } } void QmitkSlicesInterpolator::SurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(); if(interpolatedSurface.IsNotNull()) { m_BtnAccept3DInterpolation->setEnabled(true); m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface()); m_InterpolatedSurfaceNode->SetVisibility(true); m_3DContourNode->SetVisibility(true, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); if( !m_DataStorage->Exists(m_InterpolatedSurfaceNode) && !m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode); m_DataStorage->Add(m_InterpolatedSurfaceNode); } } else if (interpolatedSurface.IsNull()) { m_BtnAccept3DInterpolation->setEnabled(false); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); } } if (m_MultiWidget) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::OnAcceptInterpolationClicked() { if (m_Segmentation && m_FeedbackNode->GetData()) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); - + mitk::OverwriteSliceImageFilter::Pointer slicewriter = mitk::OverwriteSliceImageFilter::New(); slicewriter->SetInput( m_Segmentation ); slicewriter->SetCreateUndoInformation( true ); slicewriter->SetSliceImage( dynamic_cast(m_FeedbackNode->GetData()) ); slicewriter->SetSliceDimension( m_LastSliceDimension ); slicewriter->SetSliceIndex( m_LastSliceIndex ); slicewriter->SetTimeStep( m_TimeStep[m_LastSliceDimension] ); slicewriter->Update(); m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::AcceptAllInterpolations(unsigned int windowID) { // first creates a 3D diff image, then applies this diff to the segmentation if (m_Segmentation) { int sliceDimension(-1); int dummySliceIndex(-1); if (!GetSliceForWindowsID(windowID, sliceDimension, dummySliceIndex)) { return; // cannot determine slice orientation } - + //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); - mitk::UndoStackItem::ExecuteIncrement(); - + mitk::UndoStackItem::ExecuteIncrement(); + // create a diff image for the undo operation mitk::Image::Pointer diffImage = mitk::Image::New(); diffImage->Initialize( m_Segmentation ); mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); diffImage->Initialize( pixelType, 3, m_Segmentation->GetDimensions() ); - + memset( diffImage->GetData(), 0, (pixelType.GetBpe() >> 3) * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2) ); // now the diff image is all 0 - + unsigned int timeStep( m_TimeStep[windowID] ); - + // a slicewriter to create the diff image mitk::OverwriteSliceImageFilter::Pointer diffslicewriter = mitk::OverwriteSliceImageFilter::New(); diffslicewriter->SetCreateUndoInformation( false ); diffslicewriter->SetInput( diffImage ); diffslicewriter->SetSliceDimension( sliceDimension ); diffslicewriter->SetTimeStep( timeStep ); - + unsigned int totalChangedSlices(0); unsigned int zslices = m_Segmentation->GetDimension( sliceDimension ); mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); for (unsigned int sliceIndex = 0; sliceIndex < zslices; ++sliceIndex) { mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( sliceDimension, sliceIndex, timeStep ); if (interpolation.IsNotNull()) // we don't check if interpolation is necessary/sensible - but m_Interpolator does { diffslicewriter->SetSliceImage( interpolation ); diffslicewriter->SetSliceIndex( sliceIndex ); diffslicewriter->Update(); ++totalChangedSlices; } mitk::ProgressBar::GetInstance()->Progress(); } - + if (totalChangedSlices > 0) { // store undo stack items if ( true ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image mitk::ApplyDiffImageOperation* doOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); mitk::ApplyDiffImageOperation* undoOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); undoOp->SetFactor( -1.0 ); std::stringstream comment; comment << "Accept all interpolations (" << totalChangedSlices << ")"; mitk::OperationEvent* undoStackItem = new mitk::OperationEvent( mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment.str() ); mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); - + // acutally apply the changes here mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation( doOp ); } } - + m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::FinishInterpolation(int windowID) { //this redirect is for calling from outside - + if (windowID < 0) OnAcceptAllInterpolationsClicked(); else AcceptAllInterpolations( (unsigned int)windowID ); } void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked() { QMenu orientationPopup(this); std::map::const_iterator it; for(it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++) orientationPopup.addAction(it->first); - + connect( &orientationPopup, SIGNAL(triggered(QAction*)), this, SLOT(OnAcceptAllPopupActivated(QAction*)) ); - + orientationPopup.exec( QCursor::pos() ); } void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked() { if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) { mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(dynamic_cast(m_InterpolatedSurfaceNode->GetData())); s2iFilter->SetImage(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); s2iFilter->Update(); mitk::DataNode* segmentationNode = m_ToolManager->GetWorkingData(0); segmentationNode->SetData(s2iFilter->GetOutput()); + m_RBtnDisableInterpolation->setChecked(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction* action) { try { std::map::const_iterator iter = ACTION_TO_SLICEDIMENSION.find( action ); if (iter != ACTION_TO_SLICEDIMENSION.end()) { int windowID = iter->second; AcceptAllInterpolations( windowID ); } - + } catch(...) { - /* Showing message box with possible memory error */ + /* Showing message box with possible memory error */ QMessageBox errorInfo; errorInfo.setWindowTitle("Interpolation Process"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!"); errorInfo.exec(); - + //additional error message on std::cerr std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl; } } void QmitkSlicesInterpolator::OnInterpolationActivated(bool on) { m_2DInterpolationEnabled = on; - + try { if ( m_DataStorage.IsNotNull() ) { if (on && !m_DataStorage->Exists(m_FeedbackNode)) { m_DataStorage->Add( m_FeedbackNode ); } //else //{ // m_DataStorage->Remove( m_FeedbackNode ); //} } } catch(...) { // don't care (double add/remove) } - + if (m_ToolManager) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); QWidget::setEnabled( workingNode != NULL ); - + m_BtnAcceptAllInterpolations->setEnabled( on ); m_BtnAcceptInterpolation->setEnabled( on ); m_FeedbackNode->SetVisibility( on ); - + if (!on) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } - + if (workingNode) { mitk::Image* segmentation = dynamic_cast(workingNode->GetData()); if (segmentation) { m_Interpolator->SetSegmentationVolume( segmentation ); if (referenceNode) { mitk::Image* referenceImage = dynamic_cast(referenceNode->GetData()); m_Interpolator->SetReferenceVolume( referenceImage ); // may be NULL } } } } UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::Run3DInterpolation() { m_SurfaceInterpolator->Interpolate(); } void QmitkSlicesInterpolator::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSlicesInterpolator::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,0.0)); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); float yellow[3] = {255.0,255.0,0.0}; if( currentColor[2] == yellow[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,255.0)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(yellow)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on) { m_3DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { int listID; bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); if ((workingNode->IsSelected() && workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) && !isInterpolationResult) { if (workingNode->GetIntProperty("3DInterpolationListID", listID)) { m_SurfaceInterpolator->SetCurrentListID(listID); if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } else { listID = m_SurfaceInterpolator->CreateNewContourList(); workingNode->SetIntProperty("3DInterpolationListID", listID); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_BtnAccept3DInterpolation->setEnabled(false); } - + mitk::Vector3D spacing = workingNode->GetData()->GetGeometry( m_MultiWidget->GetRenderWindow3()->GetRenderer()->GetTimeStep() )->GetSpacing(); double minSpacing (100); double maxSpacing (0); for (int i =0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } else if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceInterpolator->SetWorkingImage(dynamic_cast(workingNode->GetData())); m_SurfaceInterpolator->SetMaxSpacing(maxSpacing); m_SurfaceInterpolator->SetMinSpacing(minSpacing); m_SurfaceInterpolator->SetDistanceImageVolume(50000); } } QWidget::setEnabled( workingNode != NULL ); m_CbShowMarkers->setEnabled(m_3DInterpolationEnabled); } else if (!m_3DInterpolationEnabled) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_BtnAccept3DInterpolation->setEnabled(m_3DInterpolationEnabled); } } catch(...) { MITK_ERROR<<"Error with 3D surface interpolation!"; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::EnableInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated OnInterpolationActivated(on); } void QmitkSlicesInterpolator::Enable3DInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated On3DInterpolationActivated(on); } void QmitkSlicesInterpolator::UpdateVisibleSuggestion() { if (m_2DInterpolationEnabled) { // determine which one is the current view, try to do an initial interpolation mitk::BaseRenderer* renderer = mitk::GlobalInteraction::GetInstance()->GetFocus(); if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { const mitk::TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeSlicedGeometry) { mitk::SliceNavigationController::GeometrySliceEvent event( const_cast(timeSlicedGeometry), renderer->GetSlice() ); - + if ( renderer->GetCurrentWorldGeometry2DNode() ) { if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane1() ) { TranslateAndInterpolateChangedSlice( event, 2 ); } else if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane2() ) { TranslateAndInterpolateChangedSlice( event, 0 ); } else if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane3() ) { TranslateAndInterpolateChangedSlice( event, 1 ); } } } } } - + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject& /*e*/) { // something (e.g. undo) changed the interpolation info, we should refresh our display UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject& /*e*/) { if(m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } bool QmitkSlicesInterpolator::GetSliceForWindowsID(unsigned windowID, int& sliceDimension, int& sliceIndex) { mitk::BaseRenderer* renderer(NULL); - + // find sliceDimension for windowID: // windowID 2: transversal window = renderWindow1 // windowID 1: frontal window = renderWindow3 // windowID 0: sagittal window = renderWindow2 if ( m_MultiWidget ) { switch (windowID) { case 2: default: renderer = m_MultiWidget->mitkWidget1->GetRenderer(); break; case 1: renderer = m_MultiWidget->mitkWidget3->GetRenderer(); break; case 0: renderer = m_MultiWidget->mitkWidget2->GetRenderer(); break; } } - + if ( m_Segmentation && renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { const mitk::TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeSlicedGeometry) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(timeSlicedGeometry->GetGeometry3D(m_TimeStep[windowID])); if (slicedGeometry) { mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetGeometry2D( renderer->GetSlice() )); Interpolate( plane, m_TimeStep[windowID] ); return mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, sliceDimension, sliceIndex ); } } } - + return false; } void QmitkSlicesInterpolator::OnMultiWidgetDeleted(QObject*) { if (m_MultiWidget) { m_MultiWidget = NULL; } } diff --git a/Modules/QmitkExt/QmitkVtkHistogramWidget.cpp b/Modules/QmitkExt/QmitkVtkHistogramWidget.cpp index b154264dd7..17e936282f 100644 --- a/Modules/QmitkExt/QmitkVtkHistogramWidget.cpp +++ b/Modules/QmitkExt/QmitkVtkHistogramWidget.cpp @@ -1,277 +1,277 @@ /*=================================================================== 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 "QmitkVtkHistogramWidget.h" #include "mitkHistogramGenerator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include -QmitkVtkHistogramWidget::QmitkVtkHistogramWidget( QWidget * /*parent*/ ) -: m_HistogramMode( HISTOGRAM_MODE_ENTIREIMAGE ) +QmitkVtkHistogramWidget::QmitkVtkHistogramWidget( QWidget * parent ) +: QDialog(parent), m_HistogramMode( HISTOGRAM_MODE_ENTIREIMAGE ) { //QGroupBox *hgroupbox = new QGroupBox( "", this ); //hgroupbox->setMinimumSize( 150, 150 ); m_ChartWidget = new vtkQtChartWidget( this ); QBoxLayout *layout = new QVBoxLayout( this ); layout->addWidget( m_ChartWidget ); layout->setSpacing( 10 ); vtkQtChartArea *area = m_ChartWidget->getChartArea(); // Set up the bar chart. m_BarChart = new vtkQtBarChart(); area->insertLayer( area->getAxisLayerIndex(), m_BarChart ); // Set up the default interactor. vtkQtChartMouseSelection *selector = vtkQtChartInteractorSetup::createDefault( area ); vtkQtChartSeriesSelectionHandler *handler = new vtkQtChartSeriesSelectionHandler( selector ); handler->setModeNames( "Bar Chart - Series", "Bar Chart - Bars" ); handler->setMousePressModifiers( Qt::ControlModifier, Qt::ControlModifier ); handler->setLayer( m_BarChart ); selector->addHandler( handler ); selector->setSelectionMode("Bar Chart - Bars"); // Hide the x-axis grid. vtkQtChartAxisLayer *axisLayer = area->getAxisLayer(); vtkQtChartAxis *xAxis = axisLayer->getAxis(vtkQtChartAxis::Bottom); xAxis->getOptions()->setGridVisible(false); xAxis->getOptions()->setPrecision( 0 ); xAxis->getOptions()->setNotation( vtkQtChartAxisOptions::Standard ); vtkQtChartAxis *yAxis = axisLayer->getAxis(vtkQtChartAxis::Left); yAxis->getOptions()->setPrecision( 0 ); yAxis->getOptions()->setNotation( vtkQtChartAxisOptions::Standard ); // Set up the model for the bar chart. m_ItemModel = new QStandardItemModel( m_BarChart ); m_ItemModel->setItemPrototype( new QStandardItem() ); } QmitkVtkHistogramWidget::~QmitkVtkHistogramWidget() { } void QmitkVtkHistogramWidget::SetHistogramModeToDirectHistogram() { if ( m_HistogramMode != HISTOGRAM_MODE_DIRECT ) { m_HistogramMode = HISTOGRAM_MODE_DIRECT; } } void QmitkVtkHistogramWidget::SetHistogramModeToEntireImage() { if ( m_HistogramMode != HISTOGRAM_MODE_ENTIREIMAGE ) { m_HistogramMode = HISTOGRAM_MODE_ENTIREIMAGE; } } void QmitkVtkHistogramWidget::SetHistogramModeToMaskedImage() { if ( m_HistogramMode != HISTOGRAM_MODE_MASKEDIMAGE ) { m_HistogramMode = HISTOGRAM_MODE_MASKEDIMAGE; } } void QmitkVtkHistogramWidget::SetHistogramModeToImageRegion() { if ( m_HistogramMode != HISTOGRAM_MODE_IMAGEREGION ) { m_HistogramMode = HISTOGRAM_MODE_IMAGEREGION; } } void QmitkVtkHistogramWidget::SetHistogramModeToPlanarFigureRegion() { if ( m_HistogramMode != HISTOGRAM_MODE_PLANARFIGUREREGION ) { m_HistogramMode = HISTOGRAM_MODE_PLANARFIGUREREGION; } } void QmitkVtkHistogramWidget::UpdateItemModelFromHistogram() { this->ComputeHistogram(); if ( m_DerivedHistogram.IsNull() ) { return; } // Determine non-zero range of histogram unsigned int startIndex = 0, endIndex = 0, i = 0; HistogramConstIteratorType startIt = m_DerivedHistogram->End(); HistogramConstIteratorType endIt = m_DerivedHistogram->End(); HistogramConstIteratorType it; bool firstNonEmptyBinFound = false; for ( it = m_DerivedHistogram->Begin(); it != m_DerivedHistogram->End(); ++it, ++i ) { if ( it.GetFrequency() > 0.0 ) { endIt = it; endIndex = i; if ( !firstNonEmptyBinFound ) { firstNonEmptyBinFound = true; startIt = it; startIndex = i; } } } ++endIt; // For empty image / mask: clear histogram if ( startIt == m_DerivedHistogram->End() ) { this->ClearItemModel(); return; } // Allocate data in item model m_ItemModel->setRowCount( endIndex + 1 - startIndex ); m_ItemModel->setColumnCount( 1 ); // Fill item model with histogram data for ( it = startIt, i = 0; it != endIt; ++it, ++i ) { const double &frequency = it.GetFrequency(); const double &measurement = it.GetMeasurementVector()[0]; m_ItemModel->setVerticalHeaderItem( i, new QStandardItem() ); m_ItemModel->verticalHeaderItem( i )->setData( QVariant( measurement ), Qt::DisplayRole ); m_ItemModel->setItem( i, 0, new QStandardItem() ); m_ItemModel->item( i, 0 )->setData( QVariant( frequency ), Qt::DisplayRole ); } vtkQtChartTableSeriesModel *table = new vtkQtChartTableSeriesModel( m_ItemModel, m_BarChart ); m_BarChart->setModel( table ); m_ChartWidget->show(); } void QmitkVtkHistogramWidget::ClearItemModel() { m_ItemModel->clear(); } void QmitkVtkHistogramWidget::ComputeHistogram() { switch ( m_HistogramMode ) { case HISTOGRAM_MODE_DIRECT: { m_DerivedHistogram = m_Histogram; break; } case HISTOGRAM_MODE_ENTIREIMAGE: { mitk::HistogramGenerator::Pointer histogramGenerator = mitk::HistogramGenerator::New(); histogramGenerator->SetImage( m_Image ); histogramGenerator->ComputeHistogram(); m_DerivedHistogram = histogramGenerator->GetHistogram(); break; } case HISTOGRAM_MODE_MASKEDIMAGE: { break; } case HISTOGRAM_MODE_IMAGEREGION: { break; } case HISTOGRAM_MODE_PLANARFIGUREREGION: { break; } } } void QmitkVtkHistogramWidget::SetHistogram(const HistogramType* histogram ) { m_Histogram = histogram; } void QmitkVtkHistogramWidget::SetImage(const mitk::Image* image ) { m_Image = image; } void QmitkVtkHistogramWidget::SetImageMask(const mitk::Image* imageMask ) { m_ImageMask = imageMask; } void QmitkVtkHistogramWidget::SetImageRegion( const RegionType imageRegion ) { m_ImageRegion = imageRegion; } void QmitkVtkHistogramWidget::SetPlanarFigure(const mitk::PlanarFigure* planarFigure ) { m_PlanarFigure = planarFigure; } void QmitkVtkHistogramWidget::SetHistogramMode( unsigned int histogramMode ) { m_HistogramMode = histogramMode; } unsigned int QmitkVtkHistogramWidget::GetHistogramMode() { return m_HistogramMode; } diff --git a/Modules/QmitkExt/QmitkVtkHistogramWidget.h b/Modules/QmitkExt/QmitkVtkHistogramWidget.h index 49e684c863..2c418c5bc6 100644 --- a/Modules/QmitkExt/QmitkVtkHistogramWidget.h +++ b/Modules/QmitkExt/QmitkVtkHistogramWidget.h @@ -1,144 +1,144 @@ /*=================================================================== 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 QmitkVtkHistogramWidget_H_ #define QmitkVtkHistogramWidget_H_ #include "QmitkHistogram.h" #include "QmitkExtExports.h" #include "mitkImage.h" #include "mitkPlanarFigure.h" #include #include #include #include #include #include #include /** * \brief Widget for displaying image histograms based on the vtkQtChart * framework */ class QmitkExt_EXPORT QmitkVtkHistogramWidget : public QDialog { Q_OBJECT public: - QmitkVtkHistogramWidget( QWidget * /*parent = 0 */); + QmitkVtkHistogramWidget( QWidget * parent = 0 ); virtual ~QmitkVtkHistogramWidget(); typedef mitk::Image::HistogramType HistogramType; typedef mitk::Image::HistogramType::ConstIterator HistogramConstIteratorType; typedef itk::ImageRegion< 3 > RegionType; enum { HISTOGRAM_MODE_DIRECT = 0, HISTOGRAM_MODE_ENTIREIMAGE, HISTOGRAM_MODE_MASKEDIMAGE, HISTOGRAM_MODE_IMAGEREGION, HISTOGRAM_MODE_PLANARFIGUREREGION }; /** \brief Set histogram to be displayed directly. */ void SetHistogram(const HistogramType* histogram); /** \brief Set image from which to calculate the histogram. */ void SetImage(const mitk::Image* image); /** \brief Set binary image mask determining the voxels to include in * histogram calculation. */ void SetImageMask(const mitk::Image* imageMask); /** \brief Set 3D image region for which to calculate the histogram. */ void SetImageRegion(const RegionType imageRegion); /** \brief Set planar figure describing the region for which to calculate * the histogram. */ void SetPlanarFigure(const mitk::PlanarFigure* planarFigure); /** \brief Set/Get operation mode for Histogram */ void SetHistogramMode(unsigned int histogramMode); /** \brief Set/Get operation mode for Histogram */ unsigned int GetHistogramMode(); /** \brief Set/Get operation mode for Histogram */ void SetHistogramModeToDirectHistogram(); /** \brief Set/Get operation mode for Histogram */ void SetHistogramModeToEntireImage(); /** \brief Set/Get operation mode for Histogram */ void SetHistogramModeToMaskedImage(); /** \brief Set/Get operation mode for Histogram */ void SetHistogramModeToImageRegion(); /** \brief Set/Get operation mode for Histogram */ void SetHistogramModeToPlanarFigureRegion(); /** Fill the graphical widget with currently specified histogram. */ void UpdateItemModelFromHistogram(); /** \brief Clear the histogram (nothing is displayed). */ void ClearItemModel(); protected slots: protected: void ComputeHistogram(); vtkQtChartWidget *m_ChartWidget; vtkQtBarChart *m_BarChart; QStandardItemModel *m_ItemModel; mitk::Image::ConstPointer m_Image; mitk::Image::ConstPointer m_ImageMask; RegionType m_ImageRegion; mitk::PlanarFigure::ConstPointer m_PlanarFigure; unsigned int m_HistogramMode; // Histogram set explicitly by user HistogramType::ConstPointer m_Histogram; // Histogram derived from image (not set explicitly by user) HistogramType::ConstPointer m_DerivedHistogram; }; #endif /* QmitkVtkHistogramWidget_H_ */ diff --git a/Modules/QmitkExt/QmitkVtkLineProfileWidget.cpp b/Modules/QmitkExt/QmitkVtkLineProfileWidget.cpp index 4a5b442ed0..ba856cda0e 100644 --- a/Modules/QmitkExt/QmitkVtkLineProfileWidget.cpp +++ b/Modules/QmitkExt/QmitkVtkLineProfileWidget.cpp @@ -1,380 +1,380 @@ /*=================================================================== 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 "QmitkVtkLineProfileWidget.h" #include "mitkGeometry2D.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ((VTK_MAJOR_VERSION<=5) && (VTK_MINOR_VERSION<=4) ) #include #endif #if ((VTK_MAJOR_VERSION>=5) && (VTK_MINOR_VERSION>=6) ) #include #include #endif //#include -QmitkVtkLineProfileWidget::QmitkVtkLineProfileWidget( QWidget * /*parent*/ ) -: m_PathMode( PATH_MODE_DIRECT ) +QmitkVtkLineProfileWidget::QmitkVtkLineProfileWidget( QWidget * parent ) + : QDialog(parent), m_PathMode( PATH_MODE_DIRECT ) { m_ChartWidget = new vtkQtChartWidget( this ); QBoxLayout *layout = new QVBoxLayout( this ); layout->addWidget( m_ChartWidget ); layout->setSpacing( 10 ); vtkQtChartArea *area = m_ChartWidget->getChartArea(); // Set up the line chart. m_LineChart = new vtkQtLineChart(); area->insertLayer( area->getAxisLayerIndex(), m_LineChart ); //m_BarChart->getOptions()->setBarGroupFraction(10); // Set up the default interactor. vtkQtChartMouseSelection *selector = vtkQtChartInteractorSetup::createDefault( area ); vtkQtChartSeriesSelectionHandler *handler = new vtkQtChartSeriesSelectionHandler( selector ); handler->setModeNames( "Bar Chart - Series", "Bar Chart - Bars" ); handler->setMousePressModifiers( Qt::ControlModifier, Qt::ControlModifier ); handler->setLayer( m_LineChart ); selector->addHandler( handler ); selector->setSelectionMode("Bar Chart - Bars"); // Hide the x-axis grid. vtkQtChartAxisLayer *axisLayer = area->getAxisLayer(); vtkQtChartAxis *xAxis = axisLayer->getAxis(vtkQtChartAxis::Bottom); xAxis->getOptions()->setGridVisible(false); xAxis->getOptions()->setPrecision( 1 ); xAxis->getOptions()->setNotation( vtkQtChartAxisOptions::Standard ); vtkQtChartAxis *yAxis = axisLayer->getAxis(vtkQtChartAxis::Left); yAxis->getOptions()->setPrecision( 0 ); yAxis->getOptions()->setNotation( vtkQtChartAxisOptions::Standard ); // Set up the model for the bar chart. m_ItemModel = new QStandardItemModel( m_LineChart ); m_ItemModel->setItemPrototype( new QStandardItem() ); m_ItemModel->setHorizontalHeaderItem( 0, new QStandardItem("Intensity profile") ); #if ((VTK_MAJOR_VERSION<=5) && (VTK_MINOR_VERSION<=4) ) vtkQtChartStyleManager *styleManager = area->getStyleManager(); vtkQtChartPenBrushGenerator *pen = new vtkQtChartPenBrushGenerator(); pen->setPen(0,QPen(Qt::SolidLine)); pen->addPens(vtkQtChartColors::WildFlower); styleManager->setGenerator(pen); #endif #if ((VTK_MAJOR_VERSION>=5) && (VTK_MINOR_VERSION>=6) ) vtkQtChartBasicStyleManager *styleManager = qobject_cast(area->getStyleManager()); vtkQtChartPenGenerator *pen = new vtkQtChartPenGenerator(); pen->setPen(0,QPen(Qt::SolidLine)); pen->addPens(vtkQtChartColors::WildFlower); styleManager->setGenerator("Pen",pen); #endif // Initialize parametric path object m_ParametricPath = ParametricPathType::New(); } QmitkVtkLineProfileWidget::~QmitkVtkLineProfileWidget() { } void QmitkVtkLineProfileWidget::SetPathModeToDirectPath() { if ( m_PathMode != PATH_MODE_DIRECT ) { m_PathMode = PATH_MODE_DIRECT; } } void QmitkVtkLineProfileWidget::SetPathModeToPlanarFigure() { if ( m_PathMode != PATH_MODE_PLANARFIGURE ) { m_PathMode = PATH_MODE_PLANARFIGURE; } } void QmitkVtkLineProfileWidget::UpdateItemModelFromPath() { this->ComputePath(); if ( m_DerivedPath.IsNull() ) { throw std::invalid_argument("QmitkVtkLineProfileWidget: no path set"); } // TODO: indices according to mm //// Clear the item model m_ItemModel->clear(); MITK_INFO << "Intensity profile (t)"; MITK_INFO << "Start: " << m_DerivedPath->StartOfInput(); MITK_INFO << "End: " << m_DerivedPath->EndOfInput(); // Get geometry from image mitk::Geometry3D *imageGeometry = m_Image->GetGeometry(); // Fill item model with line profile data double distance = 0.0; mitk::Point3D currentWorldPoint; double t; unsigned int i = 0; int t_tmp = 0; QStandardItemModel *tmp_ItemModel = new QStandardItemModel(); vtkQtChartTableSeriesModel *table; vtkQtChartArea* area = m_ChartWidget->getChartArea(); for(unsigned int j = 0; j < m_VectorLineCharts.size(); j++) { area->removeLayer(m_VectorLineCharts[j]); m_VectorLineCharts[j]->getModel()->deleteLater(); m_VectorLineCharts[j]->deleteLater(); } m_VectorLineCharts.clear(); int k = 0; for ( i = 0, t = m_DerivedPath->StartOfInput(); ;++i ) { const PathType::OutputType &continuousIndex = m_DerivedPath->Evaluate( t ); mitk::Point3D worldPoint; imageGeometry->IndexToWorld( continuousIndex, worldPoint ); if ( i == 0 ) { currentWorldPoint = worldPoint; } distance += currentWorldPoint.EuclideanDistanceTo( worldPoint ); mitk::Index3D indexPoint; imageGeometry->WorldToIndex( worldPoint, indexPoint ); double intensity = m_Image->GetPixelValueByIndex( indexPoint ); MITK_INFO << t << "/" << distance << ": " << indexPoint << " (" << intensity << ")"; m_ItemModel->setVerticalHeaderItem( i, new QStandardItem() ); m_ItemModel->verticalHeaderItem( i )->setData( QVariant( distance ), Qt::DisplayRole ); m_ItemModel->setItem( i, 0, new QStandardItem() ); m_ItemModel->item( i, 0 )->setData( intensity, Qt::DisplayRole ); tmp_ItemModel->setVerticalHeaderItem( k, new QStandardItem() ); tmp_ItemModel->verticalHeaderItem( k )->setData( QVariant( distance ), Qt::DisplayRole ); tmp_ItemModel->setItem( k, 0, new QStandardItem() ); tmp_ItemModel->item( k, 0 )->setData( intensity, Qt::DisplayRole ); if ((int)t > t_tmp){ t_tmp = (int)t; vtkQtLineChart *tmp_LineChart = new vtkQtLineChart(); table = new vtkQtChartTableSeriesModel( tmp_ItemModel, tmp_LineChart ); tmp_LineChart->setModel( table ); m_VectorLineCharts.push_back(tmp_LineChart); tmp_ItemModel = new QStandardItemModel(); k = 0; tmp_ItemModel->setVerticalHeaderItem( k, new QStandardItem() ); tmp_ItemModel->verticalHeaderItem( k )->setData( QVariant( distance ), Qt::DisplayRole ); tmp_ItemModel->setItem( k, 0, new QStandardItem() ); tmp_ItemModel->item( k, 0 )->setData( intensity, Qt::DisplayRole ); } k++; // Go to next index; when iteration offset reaches zero, iteration is finished PathType::OffsetType offset = m_DerivedPath->IncrementInput( t ); if ( !(offset[0] || offset[1] || offset[2]) ) { break; } currentWorldPoint = worldPoint; } for(unsigned int j = 0; j < m_VectorLineCharts.size() ; j++) { /* int styleIndex = styleManager->getStyleIndex(m_LineChart, m_LineChart->getSeriesOptions(0)); vtkQtChartStylePen *stylePen = qobject_cast( styleManager->getGenerator("Pen")); stylePen->getStylePen(styleIndex).setStyle(Qt::SolidLine);*/ area->insertLayer(area->getAxisLayerIndex() + j +1, m_VectorLineCharts[j]); } table = new vtkQtChartTableSeriesModel( m_ItemModel, m_LineChart ); //m_LineChart->setModel( table ); } void QmitkVtkLineProfileWidget::ClearItemModel() { m_ItemModel->clear(); } void QmitkVtkLineProfileWidget::CreatePathFromPlanarFigure() { m_ParametricPath->Initialize(); if ( m_PlanarFigure.IsNull() ) { throw std::invalid_argument("QmitkVtkLineProfileWidget: PlanarFigure not set!" ); } if ( m_Image.IsNull() ) { throw std::invalid_argument("QmitkVtkLineProfileWidget: Image not set -- needed to calculate path from PlanarFigure!" ); } // Get 2D geometry frame of PlanarFigure mitk::Geometry2D *planarFigureGeometry2D = dynamic_cast< mitk::Geometry2D * >( m_PlanarFigure->GetGeometry( 0 ) ); if ( planarFigureGeometry2D == NULL ) { throw std::invalid_argument("QmitkVtkLineProfileWidget: PlanarFigure has no valid geometry!" ); } // Get 3D geometry from Image (needed for conversion of point to index) mitk::Geometry3D *imageGeometry = m_Image->GetGeometry( 0 ); if ( imageGeometry == NULL ) { throw std::invalid_argument("QmitkVtkLineProfileWidget: Image has no valid geometry!" ); } // Get first poly-line of PlanarFigure (other possible poly-lines in PlanarFigure // are not supported) typedef mitk::PlanarFigure::PolyLineType VertexContainerType; const VertexContainerType vertexContainer = m_PlanarFigure->GetPolyLine( 0 ); MITK_INFO << "WorldToIndex:"; VertexContainerType::const_iterator it; for ( it = vertexContainer.begin(); it != vertexContainer.end(); ++it ) { // Map PlanarFigure 2D point to 3D point mitk::Point3D point3D; planarFigureGeometry2D->Map( it->Point, point3D ); // Convert world to index coordinates mitk::Point3D indexPoint3D; imageGeometry->WorldToIndex( point3D, indexPoint3D ); ParametricPathType::OutputType index; index[0] = indexPoint3D[0]; index[1] = indexPoint3D[1]; index[2] = indexPoint3D[2]; MITK_INFO << point3D << " / " << index; // Add index to parametric path m_ParametricPath->AddVertex( index ); } } void QmitkVtkLineProfileWidget::ComputePath() { switch ( m_PathMode ) { case PATH_MODE_DIRECT: { m_DerivedPath = m_Path; break; } case PATH_MODE_PLANARFIGURE: { // Calculate path from PlanarFigure using geometry of specified Image this->CreatePathFromPlanarFigure(); m_DerivedPath = m_ParametricPath; break; } } } void QmitkVtkLineProfileWidget::SetImage( mitk::Image* image ) { m_Image = image; } void QmitkVtkLineProfileWidget::SetPath( const PathType* path ) { m_Path = path; } void QmitkVtkLineProfileWidget::SetPlanarFigure( const mitk::PlanarFigure* planarFigure ) { m_PlanarFigure = planarFigure; } void QmitkVtkLineProfileWidget::SetPathMode( unsigned int pathMode ) { m_PathMode = pathMode; } unsigned int QmitkVtkLineProfileWidget::GetPathMode() { return m_PathMode; } diff --git a/Modules/QmitkExt/QmitkVtkLineProfileWidget.h b/Modules/QmitkExt/QmitkVtkLineProfileWidget.h index d9cb5f7bf9..229e5afd23 100644 --- a/Modules/QmitkExt/QmitkVtkLineProfileWidget.h +++ b/Modules/QmitkExt/QmitkVtkLineProfileWidget.h @@ -1,126 +1,126 @@ /*=================================================================== 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 QmitkVtkLineProfileWidget_H_ #define QmitkVtkLineProfileWidget_H_ #include "QmitkExtExports.h" #include "mitkImage.h" #include "mitkPlanarFigure.h" #include #include #include #include #include #include #include #include #include /** * \brief Widget for displaying intensity profiles of images along a given * path */ class QmitkExt_EXPORT QmitkVtkLineProfileWidget : public QDialog { Q_OBJECT public: - QmitkVtkLineProfileWidget( QWidget * /*parent = 0*/ ); + QmitkVtkLineProfileWidget( QWidget * parent = 0 ); virtual ~QmitkVtkLineProfileWidget(); typedef itk::ParametricPath< 3 >::Superclass PathType; typedef itk::PolyLineParametricPath< 3 > ParametricPathType; enum { PATH_MODE_DIRECT = 0, PATH_MODE_PLANARFIGURE }; /** \brief Set image from which to calculate derive the intensity profile. */ void SetImage(mitk::Image* image); /** \brief Set path for calculating intensity profile directly. */ void SetPath(const PathType* path); /** \brief Set planar figure for calculating intensity profile. */ void SetPlanarFigure(const mitk::PlanarFigure* planarFigure); /** \brief Set/Get mode which path to use */ void SetPathMode(unsigned int pathMode); /** \brief Set/Get mode which path to use */ unsigned int GetPathMode(); /** \brief Set/Get mode which path to use */ void SetPathModeToDirectPath(); /** \brief Set/Get mode which path to use */ void SetPathModeToPlanarFigure(); /** Fill the graphical widget with intensity profile from currently specified image/path . */ void UpdateItemModelFromPath(); /** \brief Clear the intensity profile (nothing is displayed). */ void ClearItemModel(); protected slots: protected: void CreatePathFromPlanarFigure(); void ComputePath(); vtkQtChartWidget *m_ChartWidget; vtkQtLineChart *m_LineChart; vtkQtBarChart *m_BarChart; std::vector m_VectorLineCharts; QStandardItemModel *m_ItemModel; mitk::Image::Pointer m_Image; mitk::PlanarFigure::ConstPointer m_PlanarFigure; unsigned int m_PathMode; // Path set explicitly by user PathType::ConstPointer m_Path; // Parametric path as generated from PlanarFigure ParametricPathType::Pointer m_ParametricPath; // Path derived either form user-specified path or from PlanarFigure-generated // path PathType::ConstPointer m_DerivedPath; }; #endif /* QmitkVtkLineProfileWidget_H_ */ diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp index 3e59afea4e..ba15b2ad07 100644 --- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp @@ -1,376 +1,379 @@ /*=================================================================== 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 "mitkPaintbrushTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "ipSegmentation.h" #include "mitkLevelWindowProperty.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) int mitk::PaintbrushTool::m_Size = 1; mitk::PaintbrushTool::PaintbrushTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"), m_PaintingPixelValue(paintingPixelValue), m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28) { m_MasterContour = Contour::New(); m_MasterContour->Initialize(); m_CurrentPlane = NULL; m_WorkingNode = DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); } mitk::PaintbrushTool::~PaintbrushTool() { } void mitk::PaintbrushTool::Activated() { Superclass::Activated(); FeedbackContourTool::SetFeedbackContourVisible(true); SizeChanged.Send(m_Size); } void mitk::PaintbrushTool::Deactivated() { FeedbackContourTool::SetFeedbackContourVisible(false); if (m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); Superclass::Deactivated(); m_WorkingSlice = NULL; m_CurrentPlane = NULL; } void mitk::PaintbrushTool::SetSize(int value) { m_Size = value; } void mitk::PaintbrushTool::UpdateContour(const StateEvent* stateEvent) { //MITK_INFO<<"Update..."; // examine stateEvent and create a contour that matches the pixel mask that we are going to draw const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; // create a copy of this slice (at least match the pixel sizes/spacings), // then draw the desired mask on it and create a contour from it // Convert to ipMITKSegmentationTYPE (because getting pixels relys on that data type) itk::Image< ipMITKSegmentationTYPE, 2 >::Pointer correctPixelTypeImage; CastToItkImage(m_WorkingSlice/*dynamic_cast(m_WorkingNode->GetData())*/, correctPixelTypeImage ); assert (correctPixelTypeImage.IsNotNull() ); itk::Image< ipMITKSegmentationTYPE, 2 >::DirectionType imageDirection; imageDirection.SetIdentity(); correctPixelTypeImage->SetDirection(imageDirection); Image::Pointer temporarySlice = Image::New(); CastToMitkImage( correctPixelTypeImage, temporarySlice ); //mitkIpPicDescriptor* stupidClone = mitkIpPicClone( temporarySlice->GetSliceData()->GetPicDescriptor() ); mitkIpPicDescriptor* stupidClone = mitkIpPicNew(); CastToIpPicDescriptor( temporarySlice->GetSliceData(), stupidClone ); unsigned int pixelWidth = m_Size + 1; unsigned int pixelHeight = m_Size + 1; if ( stupidClone->n[0] <= pixelWidth || stupidClone->n[1] <= pixelHeight ) { MITK_INFO << "Brush size is bigger than your working image. Reconsider this...\n" "(Or tell your progammer until (s)he fixes this message.)" << std::endl; mitkIpPicFree( stupidClone ); return; } unsigned int lineLength( stupidClone->n[0] ); unsigned int oneContourOffset(0); float circleCenterX = (float)m_Size / 2.0; float circleCenterY = (float)m_Size / 2.0; for (unsigned int x = 0; x <= pixelWidth; ++x) { for (unsigned int y = 0; y <= pixelHeight; ++y) { unsigned int offset = lineLength * y + x; ipMITKSegmentationTYPE* current = (ipMITKSegmentationTYPE*)stupidClone->data + offset; float pixelCenterX = x + 0.5; float pixelCenterY = y + 0.5; float xoff = pixelCenterX - circleCenterX; float yoff = pixelCenterY - circleCenterY; bool inside = xoff * xoff + yoff * yoff < (m_Size * m_Size) / 4.0; // no idea, if this would work for ellipses if (inside) { *current = 1; oneContourOffset = offset; } else { *current = 0; } } } int numberOfContourPoints( 0 ); int newBufferSize( 0 ); float* contourPoints = ipMITKSegmentationGetContour8N( stupidClone, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc if (!contourPoints) { mitkIpPicFree( stupidClone ); return; } // copy point from float* to mitk::Contour Contour::Pointer contourInImageIndexCoordinates = Contour::New(); contourInImageIndexCoordinates->Initialize(); Point3D newPoint; //ipMITKSegmentationGetContour8N returns all points, which causes vtk warnings, since the first and the last points are coincident. //leaving the last point out, the contour is still drawn correctly for (int index = 0; index < numberOfContourPoints-1; ++index) { newPoint[0] = contourPoints[ 2 * index + 0 ] - circleCenterX; // master contour should be centered around (0,0) newPoint[1] = contourPoints[ 2 * index + 1] - circleCenterY; newPoint[2] = 0.0; MITK_DEBUG << "Point [" << index << "] (" << newPoint[0] << ", " << newPoint[1] << ")" << std::endl; contourInImageIndexCoordinates->AddVertex( newPoint ); } free(contourPoints); m_MasterContour = contourInImageIndexCoordinates; // The PicDescriptor is only REFERENCING(!) the data, the temporarySlice takes care of deleting the data also the descriptor is pointing on // because they got allocated by the ImageDataItem, not the descriptor. stupidClone->data = NULL; mitkIpPicFree( stupidClone ); } /** Just show the contour, get one point as the central point and add surrounding points to the contour. */ bool mitk::PaintbrushTool::OnMousePressed (Action* action, const StateEvent* stateEvent) { return this->OnMouseMoved(action, stateEvent); } /** Insert the point to the feedback contour,finish to build the contour and at the same time the painting function */ bool mitk::PaintbrushTool::OnMouseMoved (Action* itkNotUsed(action), const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; CheckIfCurrentSliceHasChanged(positionEvent); if ( m_LastContourSize != m_Size ) { UpdateContour( stateEvent ); m_LastContourSize = m_Size; } bool leftMouseButtonPressed( stateEvent->GetId() == 530 || stateEvent->GetId() == 534 || stateEvent->GetId() == 1 || stateEvent->GetId() == 5 ); Point3D worldCoordinates = positionEvent->GetWorldPosition(); Point3D indexCoordinates; m_WorkingSlice->GetGeometry()->WorldToIndex( worldCoordinates, indexCoordinates ); MITK_DEBUG << "Mouse at W " << worldCoordinates << std::endl; MITK_DEBUG << "Mouse at I " << indexCoordinates << std::endl; // round to nearest voxel center (abort if this hasn't changed) if ( m_Size % 2 == 0 ) // even { indexCoordinates[0] = ROUND( indexCoordinates[0] /*+ 0.5*/) + 0.5; indexCoordinates[1] = ROUND( indexCoordinates[1] /*+ 0.5*/ ) + 0.5; } else // odd { indexCoordinates[0] = ROUND( indexCoordinates[0] ) ; indexCoordinates[1] = ROUND( indexCoordinates[1] ) ; } static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me if ( fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps || fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed ) { lastPos = indexCoordinates; } else { MITK_DEBUG << "." << std::flush; return false; } MITK_DEBUG << "Mouse at C " << indexCoordinates; Contour::Pointer contour = Contour::New(); contour->Initialize(); for (unsigned int index = 0; index < m_MasterContour->GetNumberOfPoints(); ++index) { Point3D point = m_MasterContour->GetPoints()->ElementAt(index); point[0] += indexCoordinates[ 0 ]; point[1] += indexCoordinates[ 1 ]; contour->AddVertex( point ); } if (leftMouseButtonPressed) { FeedbackContourTool::FillContourInSlice( contour, m_WorkingSlice, m_PaintingPixelValue ); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); } // visualize contour Contour::Pointer displayContour = Contour::New(); displayContour->Initialize(); //for (unsigned int index = 0; index < contour->GetNumberOfPoints(); ++index) //{ // Point3D point = contour->GetPoints()->ElementAt(index); // if ( m_Size % 2 == 0 ) // even // { // point[0] += 0.5; // point[1] += 0.5; // } // displayContour->AddVertex( point ); //} displayContour = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), /*displayContour*/contour ); SetFeedbackContour( *displayContour ); assert( positionEvent->GetSender()->GetRenderWindow() ); RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::PaintbrushTool::OnMouseReleased(Action* /*action*/, const StateEvent* stateEvent) { //When mouse is released write segmentationresult back into image const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); -// FeedbackContourTool::SetFeedbackContourVisible(false); return true; } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ bool mitk::PaintbrushTool::OnInvertLogic(Action* action, const StateEvent* stateEvent) { if (!FeedbackContourTool::OnInvertLogic(action, stateEvent)) return false; // Inversion only for 0 and 1 as painting values if (m_PaintingPixelValue == 1) { m_PaintingPixelValue = 0; FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } else if (m_PaintingPixelValue == 0) { m_PaintingPixelValue = 1; FeedbackContourTool::SetFeedbackContourColorDefault(); } return true; } void mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const PositionEvent *event) { const PlaneGeometry* planeGeometry( dynamic_cast (event->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image::Pointer image = dynamic_cast(workingNode->GetData()); if ( !image || !planeGeometry ) return; if(m_CurrentPlane.IsNull()) { m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode->ReplaceProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetData(m_WorkingSlice); } else { bool isSameSlice (false); isSameSlice = mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix()); isSameSlice = mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()); if (!isSameSlice) { m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_CurrentPlane = NULL; m_WorkingSlice = NULL; m_WorkingNode = NULL; m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode = mitk::DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetData(m_WorkingSlice); + + //So that the paintbrush contour vanished in the previous render window + RenderingManager::GetInstance()->RequestUpdateAll(); } } if(!m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) { m_WorkingNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetProperty( "name", mitk::StringProperty::New("Paintbrush_Node") ); m_WorkingNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_WorkingNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); + m_WorkingNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_ToolManager->GetDataStorage()->Add(m_WorkingNode); } } diff --git a/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml b/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml index 6cc6005c05..3229b590a3 100644 --- a/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml +++ b/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml @@ -1,3824 +1,3838 @@ - + + + + + + + + + + + + + - + - - - + + + - + - + - + - + - + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - - - - + + + + + + + + - + - + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - - - + + + - - + + - + - + - + - + - + - + + + - + diff --git a/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml b/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml.orig similarity index 91% copy from Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml copy to Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml.orig index 6cc6005c05..1392fa4f37 100644 --- a/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml +++ b/Plugins/org.mitk.gui.qt.application/resources/StateMachine.xml.orig @@ -1,3824 +1,3942 @@ - + + + + + + + + + + + + + - + - - - + + + - + - + - + - + - + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - - - - + + + + + + + + - + - + - - - + + + - + - + +<<<<<<< HEAD + +======= - + +>>>>>>> bug-11950-RedesignMeasurementToolBox - + - + +<<<<<<< HEAD + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +======= - + +>>>>>>> bug-11950-RedesignMeasurementToolBox - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +<<<<<<< HEAD + +======= + +>>>>>>> bug-11950-RedesignMeasurementToolBox - - +<<<<<<< HEAD + + + + + +======= + + - + +>>>>>>> bug-11950-RedesignMeasurementToolBox - +<<<<<<< HEAD + +======= + +>>>>>>> bug-11950-RedesignMeasurementToolBox - - - +<<<<<<< HEAD + + + + + + +======= + + + - - + + +>>>>>>> bug-11950-RedesignMeasurementToolBox - + - + - + - + - + - + + + - + diff --git a/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp b/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp index 117c1dc2af..1e8448d07d 100644 --- a/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp +++ b/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp @@ -1,938 +1,937 @@ /*=================================================================== 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 "QmitkDataManagerView.h" #include //# Own Includes //## mitk #include "mitkDataStorageEditorInput.h" #include "mitkIDataStorageReference.h" #include "mitkNodePredicateDataType.h" #include "mitkCoreObjectFactory.h" #include "mitkPACSPlugin.h" #include "mitkDataNodeFactory.h" #include "mitkColorProperty.h" #include "mitkCommon.h" #include "mitkDelegateManager.h" #include "mitkNodePredicateData.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkEnumerationProperty.h" #include "mitkProperties.h" #include #include #include #include //## Qmitk #include #include #include #include #include #include #include "src/internal/QmitkNodeTableViewKeyFilter.h" #include "src/internal/QmitkInfoDialog.h" //## Berry #include #include #include #include #include #include //# Toolkit Includes #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 "mitkDataNodeObject.h" #include "mitkIContextMenuAction.h" #include "berryIExtensionPointService.h" const std::string QmitkDataManagerView::VIEW_ID = "org.mitk.views.datamanager"; QmitkDataManagerView::QmitkDataManagerView() { } QmitkDataManagerView::~QmitkDataManagerView() { //Remove all registered actions from each descriptor for (std::vector< std::pair< QmitkNodeDescriptor*, QAction* > >::iterator it = m_DescriptorActionList.begin();it != m_DescriptorActionList.end(); it++) { // first== the NodeDescriptor; second== the registered QAction (it->first)->RemoveAction(it->second); } } void QmitkDataManagerView::CreateQtPartControl(QWidget* parent) { m_CurrentRowCount = 0; m_Parent = parent; //# Preferences berry::IPreferencesService::Pointer prefService = berry::Platform::GetServiceRegistry() .GetServiceById(berry::IPreferencesService::ID); berry::IBerryPreferences::Pointer prefs = (prefService->GetSystemPreferences()->Node(VIEW_ID)) .Cast(); assert( prefs ); prefs->OnChanged.AddListener( berry::MessageDelegate1( this , &QmitkDataManagerView::OnPreferencesChanged ) ); //# GUI m_NodeTreeModel = new QmitkDataStorageTreeModel(this->GetDataStorage()); m_NodeTreeModel->setParent( parent ); m_NodeTreeModel->SetPlaceNewNodesOnTop( prefs->GetBool("Place new nodes on top", true) ); m_NodeTreeModel->SetShowHelperObjects( prefs->GetBool("Show helper objects", false) ); m_NodeTreeModel->SetShowNodesContainingNoData( prefs->GetBool("Show nodes containing no data", false) ); m_SurfaceDecimation = prefs->GetBool("Use surface decimation", false); //# Tree View (experimental) m_NodeTreeView = new QTreeView; m_NodeTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection ); m_NodeTreeView->setSelectionBehavior( QAbstractItemView::SelectRows ); m_NodeTreeView->setAlternatingRowColors(true); m_NodeTreeView->setDragEnabled(true); m_NodeTreeView->setDropIndicatorShown(true); m_NodeTreeView->setAcceptDrops(true); m_NodeTreeView->setContextMenuPolicy(Qt::CustomContextMenu); m_NodeTreeView->setModel(m_NodeTreeModel); - m_NodeTreeView->header()->setResizeMode(0,QHeaderView::ResizeToContents); - m_NodeTreeView->header()->setStretchLastSection(false); + m_NodeTreeView->setTextElideMode(Qt::ElideMiddle); m_NodeTreeView->installEventFilter(new QmitkNodeTableViewKeyFilter(this)); QObject::connect( m_NodeTreeView, SIGNAL(customContextMenuRequested(const QPoint&)) , this, SLOT(NodeTableViewContextMenuRequested(const QPoint&)) ); QObject::connect( m_NodeTreeModel, SIGNAL(rowsInserted (const QModelIndex&, int, int)) , this, SLOT(NodeTreeViewRowsInserted ( const QModelIndex&, int, int )) ); QObject::connect( m_NodeTreeModel, SIGNAL(rowsRemoved (const QModelIndex&, int, int)) , this, SLOT(NodeTreeViewRowsRemoved( const QModelIndex&, int, int )) ); QObject::connect( m_NodeTreeView->selectionModel() , SIGNAL( selectionChanged ( const QItemSelection &, const QItemSelection & ) ) , this , SLOT( NodeSelectionChanged ( const QItemSelection &, const QItemSelection & ) ) ); //# m_NodeMenu m_NodeMenu = new QMenu(m_NodeTreeView); // # Actions berry::IEditorRegistry* editorRegistry = berry::PlatformUI::GetWorkbench()->GetEditorRegistry(); std::list editors = editorRegistry->GetEditors("*.mitk"); if (editors.size() > 1) { m_ShowInMapper = new QSignalMapper(this); foreach(berry::IEditorDescriptor::Pointer descriptor, editors) { QAction* action = new QAction(QString::fromStdString(descriptor->GetLabel()), this); m_ShowInActions << action; m_ShowInMapper->connect(action, SIGNAL(triggered()), m_ShowInMapper, SLOT(map())); m_ShowInMapper->setMapping(action, QString::fromStdString(descriptor->GetId())); } connect(m_ShowInMapper, SIGNAL(mapped(QString)), this, SLOT(ShowIn(QString))); } QmitkNodeDescriptor* unknownDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetUnknownDataNodeDescriptor(); QmitkNodeDescriptor* imageDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("Image"); QmitkNodeDescriptor* surfaceDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("Surface"); QAction* globalReinitAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png"), "Global Reinit", this); QObject::connect( globalReinitAction, SIGNAL( triggered(bool) ) , this, SLOT( GlobalReinit(bool) ) ); unknownDataNodeDescriptor->AddAction(globalReinitAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor, globalReinitAction)); QAction* saveAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/Save_48.png"), "Save...", this); QObject::connect( saveAction, SIGNAL( triggered(bool) ) , this, SLOT( SaveSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(saveAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,saveAction)); QAction* removeAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/Remove_48.png"), "Remove", this); QObject::connect( removeAction, SIGNAL( triggered(bool) ) , this, SLOT( RemoveSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(removeAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,removeAction)); QAction* reinitAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png"), "Reinit", this); QObject::connect( reinitAction, SIGNAL( triggered(bool) ) , this, SLOT( ReinitSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(reinitAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,reinitAction)); // find contextMenuAction extension points and add them to the node descriptor berry::IExtensionPointService::Pointer extensionPointService = berry::Platform::GetExtensionPointService(); berry::IConfigurationElement::vector cmActions( extensionPointService->GetConfigurationElementsFor("org.mitk.gui.qt.datamanager.contextMenuActions") ); berry::IConfigurationElement::vector::iterator cmActionsIt; std::string cmNodeDescriptorName; std::string cmLabel; std::string cmIcon; std::string cmClass; QmitkNodeDescriptor* tmpDescriptor; QAction* contextMenuAction; QVariant cmActionDataIt; m_ConfElements.clear(); int i=1; for (cmActionsIt = cmActions.begin() ; cmActionsIt != cmActions.end() ; ++cmActionsIt) { cmIcon.erase(); if((*cmActionsIt)->GetAttribute("nodeDescriptorName", cmNodeDescriptorName) && (*cmActionsIt)->GetAttribute("label", cmLabel) && (*cmActionsIt)->GetAttribute("class", cmClass)) { (*cmActionsIt)->GetAttribute("icon", cmIcon); // create context menu entry here tmpDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(QString::fromStdString(cmNodeDescriptorName)); if(!tmpDescriptor) { MITK_WARN << "cannot add action \"" << cmLabel << "\" because descriptor " << cmNodeDescriptorName << " does not exist"; continue; } contextMenuAction = new QAction( QString::fromStdString(cmLabel), parent); tmpDescriptor->AddAction(contextMenuAction); m_DescriptorActionList.push_back(std::pair(tmpDescriptor,contextMenuAction)); m_ConfElements[contextMenuAction] = *cmActionsIt; cmActionDataIt.setValue(i); contextMenuAction->setData( cmActionDataIt ); connect( contextMenuAction, SIGNAL( triggered(bool) ) , this, SLOT( ContextMenuActionTriggered(bool) ) ); ++i; } } m_OpacitySlider = new QSlider; m_OpacitySlider->setMinimum(0); m_OpacitySlider->setMaximum(100); m_OpacitySlider->setOrientation(Qt::Horizontal); QObject::connect( m_OpacitySlider, SIGNAL( valueChanged(int) ) , this, SLOT( OpacityChanged(int) ) ); QLabel* _OpacityLabel = new QLabel("Opacity: "); QHBoxLayout* _OpacityWidgetLayout = new QHBoxLayout; _OpacityWidgetLayout->setContentsMargins(4,4,4,4); _OpacityWidgetLayout->addWidget(_OpacityLabel); _OpacityWidgetLayout->addWidget(m_OpacitySlider); QWidget* _OpacityWidget = new QWidget; _OpacityWidget->setLayout(_OpacityWidgetLayout); QWidgetAction* opacityAction = new QWidgetAction(this); opacityAction ->setDefaultWidget(_OpacityWidget); QObject::connect( opacityAction , SIGNAL( changed() ) , this, SLOT( OpacityActionChanged() ) ); unknownDataNodeDescriptor->AddAction(opacityAction , false); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,opacityAction)); m_ColorButton = new QPushButton; m_ColorButton->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum); //m_ColorButton->setText("Change color"); QObject::connect( m_ColorButton, SIGNAL( clicked() ) , this, SLOT( ColorChanged() ) ); QLabel* _ColorLabel = new QLabel("Color: "); _ColorLabel->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); QHBoxLayout* _ColorWidgetLayout = new QHBoxLayout; _ColorWidgetLayout->setContentsMargins(4,4,4,4); _ColorWidgetLayout->addWidget(_ColorLabel); _ColorWidgetLayout->addWidget(m_ColorButton); QWidget* _ColorWidget = new QWidget; _ColorWidget->setLayout(_ColorWidgetLayout); QWidgetAction* colorAction = new QWidgetAction(this); colorAction->setDefaultWidget(_ColorWidget); QObject::connect( colorAction, SIGNAL( changed() ) , this, SLOT( ColorActionChanged() ) ); unknownDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,colorAction)); m_TextureInterpolation = new QAction("Texture Interpolation", this); m_TextureInterpolation->setCheckable ( true ); QObject::connect( m_TextureInterpolation, SIGNAL( changed() ) , this, SLOT( TextureInterpolationChanged() ) ); QObject::connect( m_TextureInterpolation, SIGNAL( toggled(bool) ) , this, SLOT( TextureInterpolationToggled(bool) ) ); imageDataNodeDescriptor->AddAction(m_TextureInterpolation, false); m_DescriptorActionList.push_back(std::pair(imageDataNodeDescriptor,m_TextureInterpolation)); m_SurfaceRepresentation = new QAction("Surface Representation", this); m_SurfaceRepresentation->setMenu(new QMenu); QObject::connect( m_SurfaceRepresentation->menu(), SIGNAL( aboutToShow() ) , this, SLOT( SurfaceRepresentationMenuAboutToShow() ) ); surfaceDataNodeDescriptor->AddAction(m_SurfaceRepresentation, false); m_DescriptorActionList.push_back(std::pair(surfaceDataNodeDescriptor, m_SurfaceRepresentation)); QAction* showOnlySelectedNodes = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/ShowSelectedNode_48.png") , "Show only selected nodes", this); QObject::connect( showOnlySelectedNodes, SIGNAL( triggered(bool) ) , this, SLOT( ShowOnlySelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(showOnlySelectedNodes); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor, showOnlySelectedNodes)); QAction* toggleSelectedVisibility = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/InvertShowSelectedNode_48.png") , "Toggle visibility", this); QObject::connect( toggleSelectedVisibility, SIGNAL( triggered(bool) ) , this, SLOT( ToggleVisibilityOfSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(toggleSelectedVisibility); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,toggleSelectedVisibility)); QAction* actionShowInfoDialog = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/ShowDataInfo_48.png") , "Details...", this); QObject::connect( actionShowInfoDialog, SIGNAL( triggered(bool) ) , this, SLOT( ShowInfoDialogForSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(actionShowInfoDialog); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,actionShowInfoDialog)); QAction* otsuFilterAction = new QAction("Apply Otsu Filter", this); QObject::connect( otsuFilterAction, SIGNAL( triggered(bool) ) , this, SLOT( OtsuFilter(bool) ) ); // Otsu filter does not work properly, remove it temporarily // imageDataNodeDescriptor->AddAction(otsuFilterAction); // m_DescriptorActionList.push_back(std::pair(imageDataNodeDescriptor,otsuFilterAction)); QGridLayout* _DndFrameWidgetLayout = new QGridLayout; _DndFrameWidgetLayout->addWidget(m_NodeTreeView, 0, 0); _DndFrameWidgetLayout->setContentsMargins(0,0,0,0); m_DndFrameWidget = new QmitkDnDFrameWidget(m_Parent); m_DndFrameWidget->setLayout(_DndFrameWidgetLayout); QVBoxLayout* layout = new QVBoxLayout(parent); layout->addWidget(m_DndFrameWidget); layout->setContentsMargins(0,0,0,0); m_Parent->setLayout(layout); } void QmitkDataManagerView::SetFocus() { } void QmitkDataManagerView::ContextMenuActionTriggered( bool ) { QAction* action = qobject_cast ( sender() ); std::map::iterator it = m_ConfElements.find( action ); if( it == m_ConfElements.end() ) { MITK_WARN << "associated conf element for action " << action->text().toStdString() << " not found"; return; } berry::IConfigurationElement::Pointer confElem = it->second; mitk::IContextMenuAction* contextMenuAction = confElem->CreateExecutableExtension("class"); std::string className; std::string smoothed; confElem->GetAttribute("class", className); confElem->GetAttribute("smoothed", smoothed); if(className == "QmitkThresholdAction") { contextMenuAction->SetDataStorage(this->GetDataStorage()); } else if(className == "QmitkCreatePolygonModelAction") { contextMenuAction->SetDataStorage(this->GetDataStorage()); if(smoothed == "false") { contextMenuAction->SetSmoothed(false); } else { contextMenuAction->SetSmoothed(true); } contextMenuAction->SetDecimated(m_SurfaceDecimation); } else if(className == "QmitkStatisticsAction") { contextMenuAction->SetFunctionality(this); } contextMenuAction->Run( this->GetCurrentSelection() ); // run the action } void QmitkDataManagerView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if( m_NodeTreeModel->GetPlaceNewNodesOnTopFlag() != prefs->GetBool("Place new nodes on top", true) ) m_NodeTreeModel->SetPlaceNewNodesOnTop( !m_NodeTreeModel->GetPlaceNewNodesOnTopFlag() ); if( m_NodeTreeModel->GetShowHelperObjectsFlag()!= prefs->GetBool("Show helper objects", false) ) m_NodeTreeModel->SetShowHelperObjects( !m_NodeTreeModel->GetShowHelperObjectsFlag() ); if( m_NodeTreeModel->GetShowNodesContainingNoDataFlag()!= prefs->GetBool("Show nodes containing no data", false) ) m_NodeTreeModel->SetShowNodesContainingNoData( !m_NodeTreeModel->GetShowNodesContainingNoDataFlag() ); m_NodeTreeView->expandAll(); m_SurfaceDecimation = prefs->GetBool("Use surface decimation", false); this->GlobalReinit(); } void QmitkDataManagerView::NodeTableViewContextMenuRequested( const QPoint & pos ) { QModelIndex selected = m_NodeTreeView->indexAt ( pos ); mitk::DataNode::Pointer node = m_NodeTreeModel->GetNode(selected); QList selectedNodes = this->GetCurrentSelection(); if(!selectedNodes.isEmpty()) { m_NodeMenu->clear(); QList actions; if(selectedNodes.size() == 1 ) { actions = QmitkNodeDescriptorManager::GetInstance()->GetActions(node); for(QList::iterator it = actions.begin(); it != actions.end(); ++it) { (*it)->setData(QVariant::fromValue(node.GetPointer())); } } else actions = QmitkNodeDescriptorManager::GetInstance()->GetActions(selectedNodes); if (!m_ShowInActions.isEmpty()) { QMenu* showInMenu = m_NodeMenu->addMenu("Show In"); showInMenu->addActions(m_ShowInActions); } m_NodeMenu->addActions(actions); m_NodeMenu->popup(QCursor::pos()); } } void QmitkDataManagerView::OpacityChanged(int value) { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(node) { float opacity = static_cast(value)/100.0f; node->SetFloatProperty("opacity", opacity); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkDataManagerView::OpacityActionChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(node) { float opacity = 0.0; if(node->GetFloatProperty("opacity", opacity)) { m_OpacitySlider->setValue(static_cast(opacity*100)); } } } void QmitkDataManagerView::ColorChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(node) { QColor color = QColorDialog::getColor(); m_ColorButton->setAutoFillBackground(true); node->SetProperty("color",mitk::ColorProperty::New(color.red()/255.0,color.green()/255.0,color.blue()/255.0)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkDataManagerView::ColorActionChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(node) { mitk::Color color; mitk::ColorProperty::Pointer colorProp; node->GetProperty(colorProp,"color"); if(colorProp.IsNull()) return; color = colorProp->GetValue(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0]*255)); styleSheet.append(","); styleSheet.append(QString::number(color[1]*255)); styleSheet.append(","); styleSheet.append(QString::number(color[2]*255)); styleSheet.append(")"); m_ColorButton->setStyleSheet(styleSheet); } } void QmitkDataManagerView::TextureInterpolationChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(node) { bool textureInterpolation = false; node->GetBoolProperty("texture interpolation", textureInterpolation); m_TextureInterpolation->setChecked(textureInterpolation); } } void QmitkDataManagerView::TextureInterpolationToggled( bool checked ) { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(node) { node->SetBoolProperty("texture interpolation", checked); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkDataManagerView::SurfaceRepresentationMenuAboutToShow() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(!node) return; mitk::EnumerationProperty* representationProp = dynamic_cast (node->GetProperty("material.representation")); if(!representationProp) return; // clear menu m_SurfaceRepresentation->menu()->clear(); QAction* tmp; // create menu entries for(mitk::EnumerationProperty::EnumConstIterator it=representationProp->Begin(); it!=representationProp->End() ; it++) { tmp = m_SurfaceRepresentation->menu()->addAction(QString::fromStdString(it->second)); tmp->setCheckable(true); if(it->second == representationProp->GetValueAsString()) { tmp->setChecked(true); } QObject::connect( tmp, SIGNAL( triggered(bool) ) , this, SLOT( SurfaceRepresentationActionToggled(bool) ) ); } } void QmitkDataManagerView::SurfaceRepresentationActionToggled( bool /*checked*/ ) { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_NodeTreeView->selectionModel()->currentIndex()); if(!node) return; mitk::EnumerationProperty* representationProp = dynamic_cast (node->GetProperty("material.representation")); if(!representationProp) return; QAction* senderAction = qobject_cast ( QObject::sender() ); if(!senderAction) return; std::string activatedItem = senderAction->text().toStdString(); if ( activatedItem != representationProp->GetValueAsString() ) { if ( representationProp->IsValidEnumerationValue( activatedItem ) ) { representationProp->SetValue( activatedItem ); representationProp->InvokeEvent( itk::ModifiedEvent() ); representationProp->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } void QmitkDataManagerView::SaveSelectedNodes( bool ) { QModelIndexList indexesOfSelectedRows = m_NodeTreeView->selectionModel()->selectedRows(); mitk::DataNode* node = 0; unsigned int indexesOfSelectedRowsSize = indexesOfSelectedRows.size(); for (unsigned int i = 0; iGetNode(indexesOfSelectedRows.at(i)); // if node is not defined or if the node contains geometry data do not remove it if ( node != 0 ) { mitk::BaseData::Pointer data = node->GetData(); if (data.IsNotNull()) { QString error; try { CommonFunctionality::SaveBaseData( data.GetPointer(), node->GetName().c_str() ); } catch(std::exception& e) { error = e.what(); } catch(...) { error = "Unknown error occured"; } if( !error.isEmpty() ) QMessageBox::critical( m_Parent, "Error saving...", error ); } } } } void QmitkDataManagerView::ReinitSelectedNodes( bool ) { mitk::IRenderWindowPart* renderWindow = this->OpenRenderWindowPart(); QList selectedNodes = this->GetCurrentSelection(); foreach(mitk::DataNode::Pointer node, selectedNodes) { mitk::BaseData::Pointer basedata = node->GetData(); if ( basedata.IsNotNull() && basedata->GetTimeSlicedGeometry()->IsValid() ) { renderWindow->GetRenderingManager()->InitializeViews( basedata->GetTimeSlicedGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); renderWindow->GetRenderingManager()->RequestUpdateAll(); } } } void QmitkDataManagerView::RemoveSelectedNodes( bool ) { QModelIndexList indexesOfSelectedRows = m_NodeTreeView->selectionModel()->selectedRows(); if(indexesOfSelectedRows.size() < 1) { return; } std::vector selectedNodes; mitk::DataNode* node = 0; QString question = tr("Do you really want to remove "); for (QModelIndexList::iterator it = indexesOfSelectedRows.begin() ; it != indexesOfSelectedRows.end(); it++) { node = m_NodeTreeModel->GetNode(*it); // if node is not defined or if the node contains geometry data do not remove it if ( node != 0 /*& strcmp(node->GetData()->GetNameOfClass(), "Geometry2DData") != 0*/ ) { selectedNodes.push_back(node); question.append(QString::fromStdString(node->GetName())); question.append(", "); } } // remove the last two characters = ", " question = question.remove(question.size()-2, 2); question.append(" from data storage?"); QMessageBox::StandardButton answerButton = QMessageBox::question( m_Parent , tr("DataManager") , question , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if(answerButton == QMessageBox::Yes) { for (std::vector::iterator it = selectedNodes.begin() ; it != selectedNodes.end(); it++) { node = *it; this->GetDataStorage()->Remove(node); this->GlobalReinit(false); } } } void QmitkDataManagerView::MakeAllNodesInvisible( bool ) { QList nodes = m_NodeTreeModel->GetNodeSet(); foreach(mitk::DataNode::Pointer node, nodes) { node->SetVisibility(false); } //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ShowOnlySelectedNodes( bool ) { QList selectedNodes = this->GetCurrentSelection(); QList allNodes = m_NodeTreeModel->GetNodeSet(); foreach(mitk::DataNode::Pointer node, allNodes) { node->SetVisibility(selectedNodes.contains(node)); } //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ToggleVisibilityOfSelectedNodes( bool ) { QList selectedNodes = this->GetCurrentSelection(); bool isVisible = false; foreach(mitk::DataNode::Pointer node, selectedNodes) { isVisible = false; node->GetBoolProperty("visible", isVisible); node->SetVisibility(!isVisible); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ShowInfoDialogForSelectedNodes( bool ) { QList selectedNodes = this->GetCurrentSelection(); QmitkInfoDialog _QmitkInfoDialog(selectedNodes, this->m_Parent); _QmitkInfoDialog.exec(); } void QmitkDataManagerView::Load( bool ) { QStringList fileNames = QFileDialog::getOpenFileNames(NULL, "Load data", "", mitk::CoreObjectFactory::GetInstance()->GetFileExtensions()); for ( QStringList::Iterator it = fileNames.begin(); it != fileNames.end(); ++it ) { FileOpen((*it).toAscii(), 0); } } void QmitkDataManagerView::FileOpen( const char * fileName, mitk::DataNode* parentNode ) { mitk::DataNodeFactory::Pointer factory = mitk::DataNodeFactory::New(); try { factory->SetFileName( fileName ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); factory->Update(); for ( unsigned int i = 0 ; i < factory->GetNumberOfOutputs( ); ++i ) { mitk::DataNode::Pointer node = factory->GetOutput( i ); if ( ( node.IsNotNull() ) && ( node->GetData() != NULL ) ) { this->GetDataStorage()->Add(node, parentNode); mitk::BaseData::Pointer basedata = node->GetData(); mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeSlicedGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } catch ( itk::ExceptionObject & ex ) { itkGenericOutputMacro( << "Exception during file open: " << ex ); } QApplication::restoreOverrideCursor(); } QItemSelectionModel *QmitkDataManagerView::GetDataNodeSelectionModel() const { return m_NodeTreeView->selectionModel(); } void QmitkDataManagerView::GlobalReinit( bool ) { mitk::IRenderWindowPart* renderWindow = this->OpenRenderWindowPart(); // 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 = this->GetDataStorage()->GetSubset(pred); // calculate bounding geometry of these nodes mitk::TimeSlicedGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(rs, "visible"); // initialize the views to the bounding geometry renderWindow->GetRenderingManager()->InitializeViews(bounds); } void QmitkDataManagerView::OnSelectionChanged( berry::IWorkbenchPart::Pointer part , const QList& selection ) { if(part.GetPointer() == this) return; QItemSelection newSelection; m_NodeTreeView->selectionModel()->reset(); foreach(mitk::DataNode::Pointer node, selection) { QModelIndex treeIndex = m_NodeTreeModel->GetIndex(node); if(treeIndex.isValid()) newSelection.select(treeIndex, treeIndex); } m_NodeTreeView->selectionModel()->select(newSelection, QItemSelectionModel::SelectCurrent); } void QmitkDataManagerView::OtsuFilter( bool ) { QList selectedNodes = this->GetCurrentSelection(); mitk::Image::Pointer mitkImage = 0; foreach(mitk::DataNode::Pointer node, selectedNodes) { mitkImage = dynamic_cast( node->GetData() ); if(mitkImage.IsNull()) continue; try { // get selected mitk image const unsigned short dim = 3; typedef short InputPixelType; typedef unsigned char OutputPixelType; typedef itk::Image< InputPixelType, dim > InputImageType; typedef itk::Image< OutputPixelType, dim > OutputImageType; typedef itk::OtsuThresholdImageFilter< InputImageType, OutputImageType > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetOutsideValue( 1 ); filter->SetInsideValue( 0 ); InputImageType::Pointer itkImage; mitk::CastToItkImage(mitkImage, itkImage); filter->SetInput( itkImage ); filter->Update(); mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); std::string nameOfResultImage = node->GetName(); nameOfResultImage.append("Otsu"); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); resultNode->SetData( mitk::ImportItkImage ( filter->GetOutput() ) ); this->GetDataStorage()->Add(resultNode, node); } catch( std::exception& err ) { MITK_ERROR(this->GetClassName()) << err.what(); } } } void QmitkDataManagerView::NodeTreeViewRowsRemoved ( const QModelIndex & /*parent*/, int /*start*/, int /*end*/ ) { m_CurrentRowCount = m_NodeTreeModel->rowCount(); } void QmitkDataManagerView::NodeTreeViewRowsInserted( const QModelIndex & parent, int, int ) { m_NodeTreeView->setExpanded(parent, true); // a new row was inserted if( m_CurrentRowCount == 0 && m_NodeTreeModel->rowCount() == 1 ) { this->OpenRenderWindowPart(); m_CurrentRowCount = m_NodeTreeModel->rowCount(); /* std::vector nodes = m_NodeTreeModel->GetNodeSet(); if(nodes.size() == 1) { QModelIndex treeIndex = m_NodeTreeModel->GetIndex(nodes.front()); m_NodeTreeView->selectionModel()->setCurrentIndex( treeIndex, QItemSelectionModel::ClearAndSelect ); } */ } } void QmitkDataManagerView::NodeSelectionChanged( const QItemSelection & /*selected*/, const QItemSelection & /*deselected*/ ) { QList nodes = m_NodeTreeModel->GetNodeSet(); foreach(mitk::DataNode::Pointer node, nodes) { if ( node.IsNotNull() ) node->SetBoolProperty("selected", false); } nodes.clear(); nodes = this->GetCurrentSelection(); foreach(mitk::DataNode::Pointer node, nodes) { if ( node.IsNotNull() ) node->SetBoolProperty("selected", true); } //changing the selection does NOT require any rendering processes! //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ShowIn(const QString &editorId) { berry::IWorkbenchPage::Pointer page = this->GetSite()->GetPage(); berry::IEditorInput::Pointer input(new mitk::DataStorageEditorInput(this->GetDataStorageReference())); page->OpenEditor(input, editorId.toStdString(), false, berry::IWorkbenchPage::MATCH_ID); } mitk::IRenderWindowPart* QmitkDataManagerView::OpenRenderWindowPart() { return this->GetRenderWindowPart(QmitkAbstractView::ACTIVATE | QmitkAbstractView::OPEN); } diff --git a/Plugins/org.mitk.gui.qt.extapplication/src/internal/QmitkExtDefaultPerspective.cpp b/Plugins/org.mitk.gui.qt.extapplication/src/internal/QmitkExtDefaultPerspective.cpp index bf8dff433e..c7c7fbb228 100755 --- a/Plugins/org.mitk.gui.qt.extapplication/src/internal/QmitkExtDefaultPerspective.cpp +++ b/Plugins/org.mitk.gui.qt.extapplication/src/internal/QmitkExtDefaultPerspective.cpp @@ -1,40 +1,41 @@ /*=================================================================== 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 "QmitkExtDefaultPerspective.h" #include "berryIViewLayout.h" QmitkExtDefaultPerspective::QmitkExtDefaultPerspective() { } void QmitkExtDefaultPerspective::CreateInitialLayout(berry::IPageLayout::Pointer layout) { std::string editorArea = layout->GetEditorArea(); layout->AddView("org.mitk.views.datamanager", berry::IPageLayout::LEFT, 0.3f, editorArea); berry::IViewLayout::Pointer lo = layout->GetViewLayout("org.mitk.views.datamanager"); lo->SetCloseable(false); layout->AddView("org.mitk.views.imagenavigator", berry::IPageLayout::BOTTOM, 0.5f, "org.mitk.views.datamanager"); - berry::IFolderLayout::Pointer bottomFolder = layout->CreateFolder("bottom", berry::IPageLayout::BOTTOM, 0.7f, editorArea); - bottomFolder->AddView("org.mitk.views.propertylistview"); - bottomFolder->AddView("org.blueberry.views.logview"); + berry::IPlaceholderFolderLayout::Pointer bottomFolder = layout->CreatePlaceholderFolder("bottom", berry::IPageLayout::BOTTOM, 0.7f, editorArea); + bottomFolder->AddPlaceholder("org.mitk.views.propertylistview"); + bottomFolder->AddPlaceholder("org.blueberry.views.logview"); + bottomFolder->AddPlaceholder("org.mitk.views.modules"); } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index 5bd8ecd3f1..0cc6f6cbf8 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,794 +1,794 @@ /*=================================================================== 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 "QmitkImageStatisticsView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QmitkStdMultiWidget.h" #include "QmitkSliderNavigatorWidget.h" #include "mitkNodePredicateDataType.h" #include "mitkImageTimeSelector.h" #include "mitkProperties.h" #include "mitkProgressBar.h" // Includes for image processing #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkDataNodeObject.h" #include "mitkNodePredicateData.h" #include "mitkPlanarFigureInteractor.h" #include const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; class QmitkRequestStatisticsUpdateEvent : public QEvent { public: enum Type { StatisticsUpdateRequest = QEvent::MaxUser - 1025 }; QmitkRequestStatisticsUpdateEvent() : QEvent( (QEvent::Type) StatisticsUpdateRequest ) {}; }; typedef itk::Image ImageType; typedef itk::Image FloatImageType; typedef itk::Image, 3> VectorImageType; inline bool my_isnan(float x) { volatile float d = x; if(d!=d) return true; if(d==d) return false; return d != d; } QmitkImageStatisticsView::QmitkImageStatisticsView(QObject* /*parent*/, const char* /*name*/) : m_Controls( NULL ), m_TimeStepperAdapter( NULL ), m_SelectedImageNode( NULL ), m_SelectedImage( NULL ), m_SelectedMaskNode( NULL ), m_SelectedImageMask( NULL ), m_SelectedPlanarFigure( NULL ), m_ImageObserverTag( -1 ), m_ImageMaskObserverTag( -1 ), m_PlanarFigureObserverTag( -1 ), m_CurrentStatisticsValid( false ), m_StatisticsUpdatePending( false ), m_Visible(false) { } QmitkImageStatisticsView::~QmitkImageStatisticsView() { if ( m_SelectedImage != NULL ) m_SelectedImage->RemoveObserver( m_ImageObserverTag ); if ( m_SelectedImageMask != NULL ) m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); if ( m_SelectedPlanarFigure != NULL ) m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); } void QmitkImageStatisticsView::CreateQtPartControl(QWidget *parent) { if (m_Controls == NULL) { m_Controls = new Ui::QmitkImageStatisticsViewControls; m_Controls->setupUi(parent); this->CreateConnections(); m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_LineProfileWidget->SetPathModeToPlanarFigure(); } } void QmitkImageStatisticsView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(ClipboardHistogramButtonClicked())); connect( (QObject*)(m_Controls->m_ButtonCopyStatisticsToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(ClipboardStatisticsButtonClicked())); connect( (QObject*)(m_Controls->m_IgnoreZerosCheckbox), SIGNAL(clicked()),(QObject*) this, SLOT(IgnoreZerosCheckboxClicked())); } } void QmitkImageStatisticsView::IgnoreZerosCheckboxClicked( ) { UpdateStatistics(); } void QmitkImageStatisticsView::ClipboardHistogramButtonClicked() { if ( m_CurrentStatisticsValid && (m_CurrentStatisticsCalculator.IsNotNull()) ) { typedef mitk::ImageStatisticsCalculator::HistogramType HistogramType; const HistogramType *histogram = m_CurrentStatisticsCalculator->GetHistogram(); QString clipboard( "Measurement \t Frequency\n" ); for ( HistogramType::ConstIterator it = histogram->Begin(); it != histogram->End(); ++it ) { clipboard = clipboard.append( "%L1 \t %L2\n" ) .arg( it.GetMeasurementVector()[0], 0, 'f', 2 ) .arg( it.GetFrequency() ); } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } void QmitkImageStatisticsView::ClipboardStatisticsButtonClicked() { if ( m_CurrentStatisticsValid && (m_CurrentStatisticsCalculator.IsNotNull()) ) { const mitk::ImageStatisticsCalculator::Statistics &statistics = m_CurrentStatisticsCalculator->GetStatistics(); // Copy statistics to clipboard ("%Ln" will use the default locale for // number formatting) QString clipboard( "Mean \t StdDev \t RMS \t Max \t Min \t N \t V (mm³)\n" ); clipboard = clipboard.append( "%L1 \t %L2 \t %L3 \t %L4 \t %L5 \t %L6 \t %L7" ) .arg( statistics.Mean, 0, 'f', 10 ) .arg( statistics.Sigma, 0, 'f', 10 ) .arg( statistics.RMS, 0, 'f', 10 ) .arg( statistics.Max, 0, 'f', 10 ) .arg( statistics.Min, 0, 'f', 10 ) .arg( statistics.N ) .arg( m_Controls->m_StatisticsTable->item( 0, 6 )->text() ); QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } void QmitkImageStatisticsView::FillStatisticsTableView( const mitk::ImageStatisticsCalculator::Statistics &s, const mitk::Image *image ) { m_Controls->m_StatisticsTable->setItem( 0, 0, new QTableWidgetItem( QString("%1").arg(s.Mean, 0, 'f', 2) ) ); m_Controls->m_StatisticsTable->setItem( 0, 1, new QTableWidgetItem( QString("%1").arg(s.Sigma, 0, 'f', 2) ) ); m_Controls->m_StatisticsTable->setItem( 0, 2, new QTableWidgetItem( QString("%1").arg(s.RMS, 0, 'f', 2) ) ); m_Controls->m_StatisticsTable->setItem( 0, 3, new QTableWidgetItem( QString("%1").arg(s.Max, 0, 'f', 2) ) ); m_Controls->m_StatisticsTable->setItem( 0, 4, new QTableWidgetItem( QString("%1").arg(s.Min, 0, 'f', 2) ) ); m_Controls->m_StatisticsTable->setItem( 0, 5, new QTableWidgetItem( QString("%1").arg(s.N) ) ); const mitk::Geometry3D *geometry = image->GetGeometry(); if ( geometry != NULL ) { const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing(); double volume = spacing[0] * spacing[1] * spacing[2] * (double) s.N; m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( QString("%1").arg(volume, 0, 'f', 2) ) ); } else { m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( "NA" ) ); } } void QmitkImageStatisticsView::InvalidateStatisticsTableView() { for ( unsigned int i = 0; i < 7; ++i ) { m_Controls->m_StatisticsTable->setItem( 0, i, new QTableWidgetItem( "NA" ) ); } } void QmitkImageStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/, const QList &selectedNodes ) { // Clear any unreferenced images this->RemoveOrphanImages(); - if ( !m_Visible ) + if ( !m_Visible || selectedNodes.isEmpty()) { return; } // Check if selection makeup consists only of valid nodes: // One image, segmentation or planarFigure // One image and one of the other two bool tooManyNodes( true ); bool invalidNodes( true ); if ( selectedNodes.size() < 3 ) { tooManyNodes = false; } QList nodes(selectedNodes); if( !tooManyNodes ) { unsigned int numberImages = 0; unsigned int numberSegmentations = 0; unsigned int numberPlanarFigures = 0; for ( int index = 0; index < nodes.size(); index++ ) { m_SelectedImageMask = dynamic_cast< mitk::Image * >( nodes[ index ]->GetData() ); m_SelectedPlanarFigure = dynamic_cast< mitk::PlanarFigure * >( nodes[ index ]->GetData() ); if ( m_SelectedImageMask != NULL ) { bool isMask( false ); nodes[ index ]->GetPropertyValue("binary", isMask); if ( !isMask ) { numberImages++; } else { numberSegmentations++; if ( numberImages != 0 ) // image should be last element { std::swap( nodes[ index ], nodes[ index - 1 ] ); } } } else if ( m_SelectedPlanarFigure != NULL ) { numberPlanarFigures++; if ( numberImages != 0 ) // image should be last element { std::swap( nodes[ index ], nodes[ index - 1 ] ); } } } if ( ( numberPlanarFigures + numberSegmentations + numberImages ) == nodes.size() && //No invalid nodes ( numberPlanarFigures + numberSegmentations ) < 2 && numberImages < 2 // maximum of one image and/or one of either planar figure or segmentation ) { invalidNodes = false; } } if ( nodes.empty() || tooManyNodes || invalidNodes ) { // Nothing to do: invalidate image, clear statistics, histogram, and GUI m_SelectedImage = NULL; this->InvalidateStatisticsTableView() ; m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); return; } // Get selected element mitk::DataNode *selectedNode = nodes.front(); mitk::Image *selectedImage = dynamic_cast< mitk::Image * >( selectedNode->GetData() ); // Find the next parent/grand-parent node containing an image, if any mitk::DataStorage::SetOfObjects::ConstPointer parentObjects; mitk::DataNode *parentNode = NULL; mitk::Image *parentImage = NULL; // Possibly previous change listeners if ( (m_SelectedPlanarFigure != NULL) && (m_PlanarFigureObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_PlanarFigureObserverTag = -1; } if ( (m_SelectedImage != NULL) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } if ( (m_SelectedImageMask != NULL) && (m_ImageMaskObserverTag >= 0) ) { m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); m_ImageMaskObserverTag = -1; } // Deselect all images and masks by default m_SelectedImageNode = NULL; m_SelectedImage = NULL; m_SelectedMaskNode = NULL; m_SelectedImageMask = NULL; m_SelectedPlanarFigure = NULL; { unsigned int parentObjectIndex = 0; parentObjects = this->GetDataStorage()->GetSources( selectedNode ); while( parentObjectIndex < parentObjects->Size() ) { // Use first parent object (if multiple parents are present) parentNode = parentObjects->ElementAt( parentObjectIndex ); parentImage = dynamic_cast< mitk::Image * >( parentNode->GetData() ); if( parentImage != NULL ) { break; } parentObjectIndex++; } } if ( nodes.size() == 2 ) { parentNode = nodes.back(); parentImage = dynamic_cast< mitk::Image * >( parentNode->GetData() ); } if ( parentImage != NULL ) { m_SelectedImageNode = parentNode; m_SelectedImage = parentImage; // Check if a valid mask has been selected (Image or PlanarFigure) m_SelectedImageMask = dynamic_cast< mitk::Image * >( selectedNode->GetData() ); m_SelectedPlanarFigure = dynamic_cast< mitk::PlanarFigure * >( selectedNode->GetData() ); // Check whether ImageMask is a binary segmentation if ( (m_SelectedImageMask != NULL) ) { bool isMask( false ); selectedNode->GetPropertyValue("binary", isMask); if ( !isMask ) { m_SelectedImageNode = selectedNode; m_SelectedImage = selectedImage; m_SelectedImageMask = NULL; } else { m_SelectedMaskNode = selectedNode; } } else if ( (m_SelectedPlanarFigure != NULL) ) { m_SelectedMaskNode = selectedNode; } } else if ( selectedImage != NULL ) { m_SelectedImageNode = selectedNode; m_SelectedImage = selectedImage; } typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; ITKCommandType::Pointer changeListener; changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::RequestStatisticsUpdate ); // Add change listeners to selected objects if ( m_SelectedImage != NULL ) { m_ImageObserverTag = m_SelectedImage->AddObserver( itk::ModifiedEvent(), changeListener ); } if ( m_SelectedImageMask != NULL ) { m_ImageMaskObserverTag = m_SelectedImageMask->AddObserver( itk::ModifiedEvent(), changeListener ); } if ( m_SelectedPlanarFigure != NULL ) { m_PlanarFigureObserverTag = m_SelectedPlanarFigure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), changeListener ); } // Clear statistics / histogram GUI if nothing is selected if ( m_SelectedImage == NULL ) { // Clear statistics, histogram, and GUI this->InvalidateStatisticsTableView(); m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); } else { // Else, request statistics and GUI update this->RequestStatisticsUpdate(); } } void QmitkImageStatisticsView::UpdateStatistics() { // Remove any cached images that are no longer referenced elsewhere this->RemoveOrphanImages(); mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); if ( renderPart == NULL ) { return; } unsigned int timeStep = renderPart->GetTimeNavigationController()->GetTime()->GetPos(); if ( m_SelectedImage != NULL ) { // Check if a the selected image is a multi-channel image. If yes, statistics // cannot be calculated currently. if ( m_SelectedImage->GetPixelType().GetNumberOfComponents() > 1 ) { std::stringstream message; message << "Multi-component images not supported."; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; return; } // Retrieve ImageStatisticsCalculator from has map (or create a new one // for this image if non-existant) ImageStatisticsMapType::iterator it = m_ImageStatisticsMap.find( m_SelectedImage ); if ( it != m_ImageStatisticsMap.end() ) { m_CurrentStatisticsCalculator = it->second; MITK_INFO << "Retrieving StatisticsCalculator"; } else { m_CurrentStatisticsCalculator = mitk::ImageStatisticsCalculator::New(); m_CurrentStatisticsCalculator->SetImage( m_SelectedImage ); m_ImageStatisticsMap[m_SelectedImage] = m_CurrentStatisticsCalculator; MITK_INFO << "Creating StatisticsCalculator"; } std::string maskName; std::string maskType; unsigned int maskDimension; if ( m_SelectedImageMask != NULL ) { m_CurrentStatisticsCalculator->SetImageMask( m_SelectedImageMask ); m_CurrentStatisticsCalculator->SetMaskingModeToImage(); maskName = m_SelectedMaskNode->GetName(); maskType = m_SelectedImageMask->GetNameOfClass(); maskDimension = 3; } else if ( m_SelectedPlanarFigure != NULL ) { m_CurrentStatisticsCalculator->SetPlanarFigure( m_SelectedPlanarFigure ); m_CurrentStatisticsCalculator->SetMaskingModeToPlanarFigure(); maskName = m_SelectedMaskNode->GetName(); maskType = m_SelectedPlanarFigure->GetNameOfClass(); maskDimension = 2; } else { m_CurrentStatisticsCalculator->SetMaskingModeToNone(); maskName = "None"; maskType = ""; maskDimension = 0; } if(m_Controls->m_IgnoreZerosCheckbox->isChecked()) { m_CurrentStatisticsCalculator->SetIgnorePixelValue(0); m_CurrentStatisticsCalculator->SetDoIgnorePixelValue(true); } else { m_CurrentStatisticsCalculator->SetDoIgnorePixelValue(false); } std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); bool statisticsChanged = false; bool statisticsCalculationSuccessful = false; // Initialize progress bar mitk::ProgressBar::GetInstance()->AddStepsToDo( 100 ); // Install listener for progress events and initialize progress bar typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &QmitkImageStatisticsView::UpdateProgressBar ); unsigned long progressObserverTag = m_CurrentStatisticsCalculator ->AddObserver( itk::ProgressEvent(), progressListener ); // show wait cursor this->WaitCursorOn(); try { // Compute statistics statisticsChanged = m_CurrentStatisticsCalculator->ComputeStatistics( timeStep ); statisticsCalculationSuccessful = true; } catch ( const std::runtime_error &e ) { // In case of exception, print error message on GUI std::stringstream message; message << "" << e.what() << ""; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); } catch ( const std::exception &e ) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible "; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); } m_CurrentStatisticsCalculator->RemoveObserver( progressObserverTag ); // Make sure that progress bar closes mitk::ProgressBar::GetInstance()->Progress( 100 ); // remove wait cursor this->WaitCursorOff(); if ( statisticsCalculationSuccessful ) { if ( statisticsChanged ) { // Do not show any error messages m_Controls->m_ErrorMessageLabel->hide(); m_CurrentStatisticsValid = true; } m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetHistogram( m_CurrentStatisticsCalculator->GetHistogram( timeStep ) ); m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); MITK_INFO << "UpdateItemModelFromHistogram()"; this->FillStatisticsTableView( m_CurrentStatisticsCalculator->GetStatistics( timeStep ), m_SelectedImage ); } else { m_Controls->m_SelectedMaskLabel->setText( "None" ); // Clear statistics and histogram this->InvalidateStatisticsTableView(); m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; // If a (non-closed) PlanarFigure is selected, display a line profile widget if ( m_SelectedPlanarFigure != NULL ) { // check whether PlanarFigure is initialized const mitk::Geometry2D *planarFigureGeometry2D = m_SelectedPlanarFigure->GetGeometry2D(); if ( planarFigureGeometry2D == NULL ) { // Clear statistics, histogram, and GUI this->InvalidateStatisticsTableView(); m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); return; } // TODO: enable line profile widget m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 1 ); m_Controls->m_LineProfileWidget->SetImage( m_SelectedImage ); m_Controls->m_LineProfileWidget->SetPlanarFigure( m_SelectedPlanarFigure ); m_Controls->m_LineProfileWidget->UpdateItemModelFromPath(); } } } } void QmitkImageStatisticsView::UpdateProgressBar() { mitk::ProgressBar::GetInstance()->Progress(); } void QmitkImageStatisticsView::RequestStatisticsUpdate() { if ( !m_StatisticsUpdatePending ) { QApplication::postEvent( this, new QmitkRequestStatisticsUpdateEvent ); m_StatisticsUpdatePending = true; } } void QmitkImageStatisticsView::RemoveOrphanImages() { ImageStatisticsMapType::iterator it = m_ImageStatisticsMap.begin(); while ( it != m_ImageStatisticsMap.end() ) { mitk::Image *image = it->first; mitk::ImageStatisticsCalculator *calculator = it->second; ++it; mitk::NodePredicateData::Pointer hasImage = mitk::NodePredicateData::New( image ); if ( this->GetDataStorage()->GetNode( hasImage ) == NULL ) { if ( m_SelectedImage == image ) { m_SelectedImage = NULL; m_SelectedImageNode = NULL; } if ( m_CurrentStatisticsCalculator == calculator ) { m_CurrentStatisticsCalculator = NULL; } m_ImageStatisticsMap.erase( image ); it = m_ImageStatisticsMap.begin(); } } } bool QmitkImageStatisticsView::event( QEvent *event ) { if ( event->type() == (QEvent::Type) QmitkRequestStatisticsUpdateEvent::StatisticsUpdateRequest ) { // Update statistics m_StatisticsUpdatePending = false; this->UpdateStatistics(); return true; } return false; } void QmitkImageStatisticsView::ComputeIntensityProfile( mitk::PlanarLine* line ) { double sampling = 300; QmitkVtkHistogramWidget::HistogramType::Pointer histogram = QmitkVtkHistogramWidget::HistogramType::New(); itk::Size<1> siz; siz[0] = sampling; itk::FixedArray lower, higher; lower.Fill(0); mitk::Point3D begin = line->GetWorldControlPoint(0); mitk::Point3D end = line->GetWorldControlPoint(1); itk::Vector direction = (end - begin); higher.Fill(direction.GetNorm()); histogram->Initialize(siz, lower, higher); for(int i = 0; i < sampling; i++) { //mitk::Point3D location = begin + double(i)/sampling * direction; double d = m_SelectedImage->GetPixelValueByWorldCoordinate(begin + double(i)/sampling * direction); histogram->SetFrequency(i,d); } m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetHistogram( histogram ); m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); } void QmitkImageStatisticsView::Activated() { } void QmitkImageStatisticsView::Deactivated() { } void QmitkImageStatisticsView::Visible() { m_Visible = true; this->OnSelectionChanged(this->GetSite()->GetPage()->FindView("org.mitk.views.datamanager"), this->GetDataManagerSelection()); } void QmitkImageStatisticsView::Hidden() { m_Visible = false; } void QmitkImageStatisticsView::SetFocus() { } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui index 4de165ebba..4c4481cf97 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui @@ -1,356 +1,330 @@ QmitkImageStatisticsViewControls true 0 0 465 - 475 + 500 Form - - + + 0 0 Mask: None 2 - + + + + Error Message + + + Qt::AutoText + + + + + + + Ignore zero-valued voxels + + + + Statistics - - + + + 0 + + + 0 + + + 0 + + 0 0 100 144 16777215 144 - - - 250 - 185 - - - - 1 - Qt::ScrollBarAlwaysOff Qt::ScrollBarAsNeeded true true true Qt::DotLine true false false 80 true 80 false true true false 20 20 false false Mean StdDev RMS Max Min N V (mm³) Component 1 + + + + + 0 + + + 0 + + + + + Copy to Clipboard + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + - - - - true - - - - 0 - 0 - - - + + + - 447 - 16777215 + 150 + 160 - - false - - - QFrame::NoFrame - - - QFrame::Plain - - - -1 - - - -1 + + Histogram - - 1 + + false - - - - - - - 150 - 160 - - - - Histogram - - - false - - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - + + + 6 + + + 0 + + + 9 + + + 0 + + + 0 + + + + + 0 + + + widget_2 - - - - - - - - - Intensity Profile + + + + + + + + 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - - - Statistics to Clipboard - - - - - - - Histogram to Clipboard - - - - - - - - - Qt::Vertical - - - QSizePolicy::Preferred - - - - 10 - 1 - - - - - - - - Error Message - - - Qt::AutoText - - - - - - - Ignore zero-valued voxels - + + 0 + + + + + Copy to Clipboard + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QmitkVtkHistogramWidget QWidget
    QmitkVtkHistogramWidget.h
    1
    QmitkVtkLineProfileWidget QWidget
    QmitkVtkLineProfileWidget.h
    1
    diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp index d72a14b1c1..a982cb2094 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.cpp @@ -1,1099 +1,702 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) -Copyright (c) German Cancer Research Center, +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 +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 "mitkGlobalInteraction.h" -#include "mitkPointSet.h" -#include "mitkProperties.h" -#include "mitkStringProperty.h" -#include "mitkIDataStorageService.h" -#include "mitkDataNodeObject.h" -#include -#include -#include -#include -#include -#include -#include - -#include "mitkPlanarCircle.h" -#include "mitkPlanarPolygon.h" -#include "mitkPlanarAngle.h" -#include "mitkPlanarRectangle.h" -#include "mitkPlanarLine.h" -#include "mitkPlanarCross.h" -#include "mitkPlanarFourPointAngle.h" -#include "mitkPlanarFigureInteractor.h" -#include "mitkPlaneGeometry.h" -#include "QmitkPlanarFiguresTableModel.h" +#define MEASUREMENT_DEBUG MITK_DEBUG("QmitkMeasurementView") << __LINE__ << ": " #include "QmitkMeasurementView.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "mitkNodePredicateDataType.h" -#include "mitkPlanarFigure.h" - -#include -#include -#include +#include + #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -const std::string QmitkMeasurementView::VIEW_ID = -"org.mitk.views.measurement"; - -QmitkMeasurementView::QmitkMeasurementView() : -m_Parent(0), m_Layout(0), m_DrawActionsToolBar(0), -m_DrawActionsGroup(0), m_MeasurementInfoRenderer(0), -m_MeasurementInfoAnnotation(0), m_SelectedPlanarFigures(0), -m_SelectedImageNode(), -m_LineCounter(0), m_PathCounter(0), -m_AngleCounter(0), m_FourPointAngleCounter(0), m_EllipseCounter(0), - m_RectangleCounter(0), m_PolygonCounter(0), m_Visible(false), - m_CurrentFigureNodeInitialized(false), m_Activated(false), - m_LastRenderWindow(0) +struct QmitkPlanarFigureData { + QmitkPlanarFigureData() + : m_Figure(0), m_EndPlacementObserverTag(0), m_SelectObserverTag(0), m_StartInteractionObserverTag(0), m_EndInteractionObserverTag(0) + { + } -} + mitk::PlanarFigure* m_Figure; + unsigned int m_EndPlacementObserverTag; + unsigned int m_SelectObserverTag; + unsigned int m_StartInteractionObserverTag; + unsigned int m_EndInteractionObserverTag; +}; -QmitkMeasurementView::~QmitkMeasurementView() +struct QmitkMeasurementViewData { - this->GetDataStorage()->AddNodeEvent -= mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeAddedInDataStorage ); - - m_SelectedPlanarFigures->NodeChanged.RemoveListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeChanged ) ); - - m_SelectedPlanarFigures->NodeRemoved.RemoveListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeRemoved ) ); - - m_SelectedPlanarFigures->PropertyChanged.RemoveListener( mitk::MessageDelegate2( this, &QmitkMeasurementView::PropertyChanged ) ); - - m_SelectedImageNode->NodeChanged.RemoveListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeChanged ) ); - - m_SelectedImageNode->NodeRemoved.RemoveListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeRemoved ) ); - - m_SelectedImageNode->PropertyChanged.RemoveListener( mitk::MessageDelegate2( this, &QmitkMeasurementView::PropertyChanged ) ); - - if(this->m_LastRenderWindow != NULL) + QmitkMeasurementViewData() + : m_LineCounter(0), m_PathCounter(0), m_AngleCounter(0), + m_FourPointAngleCounter(0), m_EllipseCounter(0), + m_RectangleCounter(0), m_PolygonCounter(0), m_UnintializedPlanarFigure(false) { - this->SetMeasurementInfoToRenderWindow("",m_LastRenderWindow); - mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->RemoveRenderer( - m_MeasurementInfoRenderer); } - this->m_MeasurementInfoRenderer->Delete(); -} + // internal vars + unsigned int m_LineCounter; + unsigned int m_PathCounter; + unsigned int m_AngleCounter; + unsigned int m_FourPointAngleCounter; + unsigned int m_EllipseCounter; + unsigned int m_RectangleCounter; + unsigned int m_PolygonCounter; + QList m_CurrentSelection; + std::map m_DataNodeToPlanarFigureData; + mitk::WeakPointer m_SelectedImageNode; + bool m_UnintializedPlanarFigure; + + // WIDGETS + QWidget* m_Parent; + QLabel* m_SelectedImageLabel; + QAction* m_DrawLine; + QAction* m_DrawPath; + QAction* m_DrawAngle; + QAction* m_DrawFourPointAngle; + QAction* m_DrawEllipse; + QAction* m_DrawRectangle; + QAction* m_DrawPolygon; + QToolBar* m_DrawActionsToolBar; + QActionGroup* m_DrawActionsGroup; + QTextBrowser* m_SelectedPlanarFiguresText; + QPushButton* m_CopyToClipboard; + QGridLayout* m_Layout; +}; + +const std::string QmitkMeasurementView::VIEW_ID = "org.mitk.views.measurement"; + +QmitkMeasurementView::QmitkMeasurementView() +: d( new QmitkMeasurementViewData ) +{ +} +QmitkMeasurementView::~QmitkMeasurementView() +{ + this->RemoveAllInteractors(); + delete d; +} void QmitkMeasurementView::CreateQtPartControl(QWidget* parent) { - m_Parent = parent; - m_MeasurementInfoRenderer = vtkRenderer::New(); - m_MeasurementInfoAnnotation = vtkCornerAnnotation::New(); - vtkTextProperty *textProp = vtkTextProperty::New(); + d->m_Parent = parent; - m_MeasurementInfoAnnotation->SetMaximumFontSize(12); - textProp->SetColor(1.0, 1.0, 1.0); - m_MeasurementInfoAnnotation->SetTextProperty(textProp); + // image label + QLabel* selectedImageLabel = new QLabel("Reference Image: "); + d->m_SelectedImageLabel = new QLabel; + d->m_SelectedImageLabel->setStyleSheet("font-weight: bold;"); - m_MeasurementInfoRenderer->AddActor(m_MeasurementInfoAnnotation); - m_DrawActionsToolBar = new QToolBar; - m_DrawActionsGroup = new QActionGroup(this); - m_DrawActionsGroup->setExclusive(true); + d->m_DrawActionsToolBar = new QToolBar; + d->m_DrawActionsGroup = new QActionGroup(this); + d->m_DrawActionsGroup->setExclusive(true); //# add actions - QAction* currentAction = m_DrawActionsToolBar->addAction(QIcon( + MEASUREMENT_DEBUG << "Draw Line"; + QAction* currentAction = d->m_DrawActionsToolBar->addAction(QIcon( ":/measurement/line.png"), "Draw Line"); - m_DrawLine = currentAction; - m_DrawLine->setCheckable(true); - m_DrawActionsToolBar->addAction(currentAction); - m_DrawActionsGroup->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ActionDrawLineTriggered(bool) ) ); + currentAction->setCheckable(true); + d->m_DrawLine = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); - currentAction = m_DrawActionsToolBar->addAction(QIcon( + MEASUREMENT_DEBUG << "Draw Path"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon( ":/measurement/path.png"), "Draw Path"); - m_DrawPath = currentAction; - m_DrawPath->setCheckable(true); - m_DrawActionsToolBar->addAction(currentAction); - m_DrawActionsGroup->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ActionDrawPathTriggered(bool) ) ); + currentAction->setCheckable(true); + d->m_DrawPath = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); - currentAction = m_DrawActionsToolBar->addAction(QIcon( + MEASUREMENT_DEBUG << "Draw Angle"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon( ":/measurement/angle.png"), "Draw Angle"); - m_DrawAngle = currentAction; - m_DrawAngle->setCheckable(true); - m_DrawActionsToolBar->addAction(currentAction); - m_DrawActionsGroup->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ActionDrawAngleTriggered(bool) ) ); + currentAction->setCheckable(true); + d->m_DrawAngle = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); - currentAction = m_DrawActionsToolBar->addAction(QIcon( + MEASUREMENT_DEBUG << "Draw Four Point Angle"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon( ":/measurement/four-point-angle.png"), "Draw Four Point Angle"); - m_DrawFourPointAngle = currentAction; - m_DrawFourPointAngle->setCheckable(true); - m_DrawActionsToolBar->addAction(currentAction); - m_DrawActionsGroup->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ActionDrawFourPointAngleTriggered(bool) ) ); + currentAction->setCheckable(true); + d->m_DrawFourPointAngle = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); - currentAction = m_DrawActionsToolBar->addAction(QIcon( + MEASUREMENT_DEBUG << "Draw Circle"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon( ":/measurement/circle.png"), "Draw Circle"); - m_DrawEllipse = currentAction; - m_DrawEllipse->setCheckable(true); - m_DrawActionsToolBar->addAction(currentAction); - m_DrawActionsGroup->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ActionDrawEllipseTriggered(bool) ) ); + currentAction->setCheckable(true); + d->m_DrawEllipse = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); - currentAction = m_DrawActionsToolBar->addAction(QIcon( + MEASUREMENT_DEBUG << "Draw Rectangle"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon( ":/measurement/rectangle.png"), "Draw Rectangle"); - m_DrawRectangle = currentAction; - m_DrawRectangle->setCheckable(true); - m_DrawActionsToolBar->addAction(currentAction); - m_DrawActionsGroup->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ActionDrawRectangleTriggered(bool) ) ); + currentAction->setCheckable(true); + d->m_DrawRectangle = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); - currentAction = m_DrawActionsToolBar->addAction(QIcon( + MEASUREMENT_DEBUG << "Draw Polygon"; + currentAction = d->m_DrawActionsToolBar->addAction(QIcon( ":/measurement/polygon.png"), "Draw Polygon"); - m_DrawPolygon = currentAction; - m_DrawPolygon->setCheckable(true); - m_DrawActionsToolBar->addAction(currentAction); - m_DrawActionsGroup->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ActionDrawPolygonTriggered(bool) ) ); - - currentAction = m_DrawActionsToolBar->addAction(QIcon( - ":/Qmitk/Images_48.png"), "Reproduce potential bug"); - m_DrawActionsToolBar->addAction(currentAction); - QObject::connect( currentAction, SIGNAL( triggered(bool) ) - , this, SLOT( ReproducePotentialBug(bool) ) ); - - - QLabel* selectedImageLabel = new QLabel("Selected Image: "); - m_SelectedImage = new QLabel; - m_SelectedImage->setStyleSheet("font-weight: bold;"); - m_SelectedPlanarFiguresText = new QTextBrowser; - - m_CopyToClipboard = new QPushButton("Copy to Clipboard"); - QObject::connect( m_CopyToClipboard, SIGNAL( clicked(bool) ) - , this, SLOT( CopyToClipboard(bool) ) ); - - m_Layout = new QGridLayout; - m_Layout->addWidget(selectedImageLabel, 0, 0, 1, 1); - m_Layout->addWidget(m_SelectedImage, 0, 1, 1, 1); - m_Layout->addWidget(m_DrawActionsToolBar, 1, 0, 1, 2); - m_Layout->addWidget(m_SelectedPlanarFiguresText, 2, 0, 1, 2); - m_Layout->addWidget(m_CopyToClipboard, 3, 0, 1, 2); - m_Layout->setRowStretch(0, 1); - m_Layout->setRowStretch(1, 1); - m_Layout->setRowStretch(2, 10); - m_Layout->setRowStretch(3, 1); - m_Layout->setContentsMargins(2, 2, 2, 2); - - parent->setLayout(m_Layout); - - - m_SelectedPlanarFigures = mitk::DataStorageSelection::New(this->GetDataStorage(), false); + currentAction->setCheckable(true); + d->m_DrawPolygon = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); - m_SelectedPlanarFigures->NodeChanged.AddListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeChanged ) ); + // planar figure details text + d->m_SelectedPlanarFiguresText = new QTextBrowser; - m_SelectedPlanarFigures->NodeRemoved.AddListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeRemoved ) ); + // copy to clipboard button + d->m_CopyToClipboard = new QPushButton("Copy to Clipboard"); - m_SelectedPlanarFigures->PropertyChanged.AddListener( mitk::MessageDelegate2( this, &QmitkMeasurementView::PropertyChanged ) ); + d->m_Layout = new QGridLayout; + d->m_Layout->addWidget(selectedImageLabel, 0, 0, 1, 1); + d->m_Layout->addWidget(d->m_SelectedImageLabel, 0, 1, 1, 1); + d->m_Layout->addWidget(d->m_DrawActionsToolBar, 1, 0, 1, 2); + d->m_Layout->addWidget(d->m_SelectedPlanarFiguresText, 2, 0, 1, 2); + d->m_Layout->addWidget(d->m_CopyToClipboard, 3, 0, 1, 2); - m_SelectedImageNode = mitk::DataStorageSelection::New(this->GetDataStorage(), false); + d->m_Parent->setLayout(d->m_Layout); - m_SelectedImageNode->PropertyChanged.AddListener( mitk::MessageDelegate2( this, &QmitkMeasurementView::PropertyChanged ) ); - - m_SelectedImageNode->NodeChanged.AddListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeChanged ) ); - - m_SelectedImageNode->NodeRemoved.AddListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeRemoved ) ); - - this->GetDataStorage()->AddNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeAddedInDataStorage ) ); + // create connections + this->CreateConnections(); + // readd interactors and observers + this->AddAllInteractors(); } - -void QmitkMeasurementView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, - const QList &nodes) +void QmitkMeasurementView::CreateConnections() { - if ( nodes.isEmpty() ) return; - - m_SelectedImageNode->RemoveAllNodes(); - - mitk::BaseData* _BaseData; - mitk::PlanarFigure* _PlanarFigure; - mitk::Image* selectedImage; - m_SelectedPlanarFigures->RemoveAllNodes(); - - foreach (mitk::DataNode::Pointer _DataNode, nodes) - { - _PlanarFigure = 0; - - if (!_DataNode) - continue; - - _BaseData = _DataNode->GetData(); - - if (!_BaseData) - continue; - - // planar figure selected - if ((_PlanarFigure = dynamic_cast (_BaseData))) - { - // add to the selected planar figures - m_SelectedPlanarFigures->AddNode(_DataNode); - // take parent image as the selected image - mitk::DataStorage::SetOfObjects::ConstPointer parents = - this->GetDataStorage()->GetSources(_DataNode); - if (parents->size() > 0) - { - mitk::DataNode::Pointer parent = parents->front(); - if ((selectedImage = dynamic_cast (parent->GetData()))) - { - *m_SelectedImageNode = parent; - } - } - - } - else if ((selectedImage = dynamic_cast (_BaseData))) - { - *m_SelectedImageNode = _DataNode; - /*mitk::RenderingManager::GetInstance()->InitializeViews( - selectedImage->GetTimeSlicedGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true );*/ - } - } // end for - - this->PlanarFigureSelectionChanged(); + QObject::connect( d->m_DrawLine, SIGNAL( triggered(bool) ) + , this, SLOT( ActionDrawLineTriggered(bool) ) ); + QObject::connect( d->m_DrawPath, SIGNAL( triggered(bool) ) + , this, SLOT( ActionDrawPathTriggered(bool) ) ); + QObject::connect( d->m_DrawAngle, SIGNAL( triggered(bool) ) + , this, SLOT( ActionDrawAngleTriggered(bool) ) ); + QObject::connect( d->m_DrawFourPointAngle, SIGNAL( triggered(bool) ) + , this, SLOT( ActionDrawFourPointAngleTriggered(bool) ) ); + QObject::connect( d->m_DrawEllipse, SIGNAL( triggered(bool) ) + , this, SLOT( ActionDrawEllipseTriggered(bool) ) ); + QObject::connect( d->m_DrawRectangle, SIGNAL( triggered(bool) ) + , this, SLOT( ActionDrawRectangleTriggered(bool) ) ); + QObject::connect( d->m_DrawPolygon, SIGNAL( triggered(bool) ) + , this, SLOT( ActionDrawPolygonTriggered(bool) ) ); + QObject::connect( d->m_CopyToClipboard, SIGNAL( clicked(bool) ) + , this, SLOT( CopyToClipboard(bool) ) ); } -void QmitkMeasurementView::PlanarFigureSelectionChanged() +void QmitkMeasurementView::NodeAdded( const mitk::DataNode* node ) { - if ( !this->m_Activated ) return; - - if (m_SelectedImageNode->GetNode().IsNotNull()) + // add observer for selection in renderwindow + mitk::PlanarFigure* figure = dynamic_cast(node->GetData()); + if( figure ) { - mitk::Image* selectedImage = dynamic_cast(m_SelectedImageNode->GetNode()->GetData()); - if(selectedImage && selectedImage->GetDimension() > 3) + MEASUREMENT_DEBUG << "figure added. will add interactor if needed."; + mitk::PlanarFigureInteractor::Pointer figureInteractor + = dynamic_cast(node->GetInteractor()); + + mitk::DataNode* nonConstNode = const_cast( node ); + if(figureInteractor.IsNull()) { - m_SelectedImageNode->RemoveAllNodes(); - m_SelectedImage->setText( "4D images are not supported." ); - m_DrawActionsToolBar->setEnabled(false); + figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", nonConstNode); } else { - m_SelectedImage->setText(QString::fromStdString( - m_SelectedImageNode->GetNode()->GetName())); - m_DrawActionsToolBar->setEnabled(true); + // just to be sure that the interactor is not added twice + mitk::GlobalInteraction::GetInstance()->RemoveInteractor(figureInteractor); } - } - else - { - m_SelectedImage->setText( - "None. Please select an image."); - m_DrawActionsToolBar->setEnabled(false); - } + MEASUREMENT_DEBUG << "adding interactor to globalinteraction"; + mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); - if (m_SelectedPlanarFigures->GetSize() == 0 && this->GetRenderWindowPart() != 0) - { - foreach (QmitkRenderWindow* renderWindow, this->GetRenderWindowPart()->GetRenderWindows().values()) - { - this->SetMeasurementInfoToRenderWindow("", renderWindow); - } - } + MEASUREMENT_DEBUG << "will now add observers for planarfigure"; + QmitkPlanarFigureData data; + data.m_Figure = figure; - unsigned int j = 1; - mitk::PlanarFigure* _PlanarFigure = 0; - mitk::PlanarAngle* planarAngle = 0; - mitk::PlanarFourPointAngle* planarFourPointAngle = 0; - mitk::DataNode::Pointer node = 0; - m_SelectedPlanarFiguresText->clear(); - QString infoText; - QString plainInfoText; - std::vector nodes = m_SelectedPlanarFigures->GetNodes(); - - for (std::vector::iterator it = nodes.begin(); it - != nodes.end(); ++it, ++j) - { - plainInfoText.clear(); - node = *it; - if(j>1) - infoText.append("
    "); + // add observer for event when figure has been placed + typedef itk::SimpleMemberCommand< QmitkMeasurementView > SimpleCommandType; + SimpleCommandType::Pointer initializationCommand = SimpleCommandType::New(); + initializationCommand->SetCallbackFunction( this, &QmitkMeasurementView::PlanarFigureInitialized ); + data.m_EndPlacementObserverTag = figure->AddObserver( mitk::EndPlacementPlanarFigureEvent(), initializationCommand ); - infoText.append(QString("%1
    ").arg(QString::fromStdString( - node->GetName()))); - plainInfoText.append(QString("%1").arg(QString::fromStdString( - node->GetName()))); + // add observer for event when figure is picked (selected) + typedef itk::MemberCommand< QmitkMeasurementView > MemberCommandType; + MemberCommandType::Pointer selectCommand = MemberCommandType::New(); + selectCommand->SetCallbackFunction( this, &QmitkMeasurementView::PlanarFigureSelected ); + data.m_SelectObserverTag = figure->AddObserver( mitk::SelectPlanarFigureEvent(), selectCommand ); - _PlanarFigure = dynamic_cast (node->GetData()); + // add observer for event when interaction with figure starts + SimpleCommandType::Pointer startInteractionCommand = SimpleCommandType::New(); + startInteractionCommand->SetCallbackFunction( this, &QmitkMeasurementView::DisableCrosshairNavigation); + data.m_StartInteractionObserverTag = figure->AddObserver( mitk::StartInteractionPlanarFigureEvent(), startInteractionCommand ); - planarAngle = dynamic_cast (_PlanarFigure); - if(!planarAngle) - { - planarFourPointAngle = dynamic_cast (_PlanarFigure); - } + // add observer for event when interaction with figure starts + SimpleCommandType::Pointer endInteractionCommand = SimpleCommandType::New(); + endInteractionCommand->SetCallbackFunction( this, &QmitkMeasurementView::EnableCrosshairNavigation); + data.m_EndInteractionObserverTag = figure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), endInteractionCommand ); - if(!_PlanarFigure) - continue; + // adding to the map of tracked planarfigures + d->m_DataNodeToPlanarFigureData[nonConstNode] = data; + } + this->CheckForTopMostVisibleImage(); +} - double featureQuantity = 0.0; - for (unsigned int i = 0; i < _PlanarFigure->GetNumberOfFeatures(); ++i) +void QmitkMeasurementView::NodeChanged(const mitk::DataNode* node) +{ + // DETERMINE IF WE HAVE TO RENEW OUR DETAILS TEXT (ANY NODE CHANGED IN OUR SELECTION?) + bool renewText = false; + for( int i=0; i < d->m_CurrentSelection.size(); ++i ) + { + if( node == d->m_CurrentSelection.at(i) ) { - if ( !_PlanarFigure->IsFeatureActive( i ) ) continue; - - featureQuantity = _PlanarFigure->GetQuantity(i); - if ((planarAngle && i == planarAngle->FEATURE_ID_ANGLE) - || (planarFourPointAngle && i == planarFourPointAngle->FEATURE_ID_ANGLE)) - featureQuantity = featureQuantity * 180 / vnl_math::pi; - - infoText.append( - QString("%1: %2 %3") .arg(QString( - _PlanarFigure->GetFeatureName(i))) .arg(featureQuantity, 0, 'f', - 2) .arg(QString(_PlanarFigure->GetFeatureUnit(i)))); - - plainInfoText.append( - QString("\n%1: %2 %3") .arg(QString(_PlanarFigure->GetFeatureName(i))) .arg( - featureQuantity, 0, 'f', 2) .arg(QString( - _PlanarFigure->GetFeatureUnit(i)))); - - if(i+1 != _PlanarFigure->GetNumberOfFeatures()) - infoText.append("
    "); + renewText = true; + break; } + } - if (j != nodes.size()) - infoText.append("
    "); + if(renewText) + { + MEASUREMENT_DEBUG << "Selected nodes changed. Refreshing text."; + this->UpdateMeasurementText(); } - m_SelectedPlanarFiguresText->setHtml(infoText); + this->CheckForTopMostVisibleImage(); +} + +void QmitkMeasurementView::CheckForTopMostVisibleImage(mitk::DataNode* _NodeToNeglect) +{ + d->m_SelectedImageNode = this->DetectTopMostVisibleImage().GetPointer(); + if( d->m_SelectedImageNode.GetPointer() == _NodeToNeglect ) + d->m_SelectedImageNode = 0; - // for the last selected planar figure ... - if (_PlanarFigure) + if( d->m_SelectedImageNode.IsNotNull() && d->m_UnintializedPlanarFigure == false ) { - const mitk::PlaneGeometry - * _PlaneGeometry = - dynamic_cast (_PlanarFigure->GetGeometry2D()); - - QmitkRenderWindow* selectedRenderWindow = 0; - bool PlanarFigureInitializedWindow = false; - mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); - if (renderPart) - { - foreach(QmitkRenderWindow* renderWindow, renderPart->GetRenderWindows().values()) - { - if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, - renderWindow->GetRenderer())) - { - selectedRenderWindow = renderWindow; - break; - } - } - } - - // make node visible - if (selectedRenderWindow) - { - mitk::Point3D centerP = _PlaneGeometry->GetOrigin(); - //selectedRenderWindow->GetSliceNavigationController()->ReorientSlices( - //centerP, _PlaneGeometry->GetNormal()); - selectedRenderWindow->GetSliceNavigationController()->SelectSliceByPoint( - centerP); - - // now paint infos also on renderwindow - this->SetMeasurementInfoToRenderWindow(plainInfoText, selectedRenderWindow); - } + MEASUREMENT_DEBUG << "Reference image found"; + d->m_SelectedImageLabel->setText( QString::fromStdString( d->m_SelectedImageNode->GetName() ) ); + d->m_DrawActionsToolBar->setEnabled(true); + MEASUREMENT_DEBUG << "Updating Measurement text"; } - // no last planarfigure else - this->SetMeasurementInfoToRenderWindow("", 0); + { + MEASUREMENT_DEBUG << "No reference image available. Will disable actions for creating new planarfigures"; + if( d->m_UnintializedPlanarFigure == false ) + d->m_SelectedImageLabel->setText( "No visible image available." ); - this->RequestRenderWindowUpdate(); + d->m_DrawActionsToolBar->setEnabled(false); + } } -void QmitkMeasurementView::NodeAddedInDataStorage(const mitk::DataNode* node) +void QmitkMeasurementView::NodeRemoved(const mitk::DataNode* node) { - if(!m_Visible) - return; + MEASUREMENT_DEBUG << "node removed from data storage"; + mitk::DataNode* nonConstNode = const_cast(node); - mitk::PlanarFigure* figure = dynamic_cast(nonConstNode->GetData()); - if(figure) - { - mitk::PlanarFigureInteractor::Pointer figureInteractor - = dynamic_cast(node->GetInteractor()); + std::map::iterator it = + d->m_DataNodeToPlanarFigureData.find(nonConstNode); - if(figureInteractor.IsNull()) - figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", nonConstNode); + if( it != d->m_DataNodeToPlanarFigureData.end() ) + { + QmitkPlanarFigureData& data = it->second; - // remove old interactor if present - if( m_CurrentFigureNode.IsNotNull() && m_CurrentFigureNodeInitialized == false ) - { - mitk::Interactor::Pointer oldInteractor = m_CurrentFigureNode->GetInteractor(); - if(oldInteractor.IsNotNull()) - mitk::GlobalInteraction::GetInstance()->RemoveInteractor(oldInteractor); + MEASUREMENT_DEBUG << "removing figure interactor to globalinteraction"; + mitk::Interactor::Pointer oldInteractor = node->GetInteractor(); +// if(oldInteractor.IsNotNull()) +// mitk::GlobalInteraction::GetInstance()->RemoveInteractor(oldInteractor); - this->GetDataStorage()->Remove(m_CurrentFigureNode); - } + // remove observers + data.m_Figure->RemoveObserver( data.m_EndPlacementObserverTag ); + data.m_Figure->RemoveObserver( data.m_SelectObserverTag ); + data.m_Figure->RemoveObserver( data.m_StartInteractionObserverTag ); + data.m_Figure->RemoveObserver( data.m_EndInteractionObserverTag ); - mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); + MEASUREMENT_DEBUG << "removing from the list of tracked planar figures"; + d->m_DataNodeToPlanarFigureData.erase( it ); } -} - - -void QmitkMeasurementView::PlanarFigureInitialized() -{ - if(m_CurrentFigureNode.IsNull()) - return; - - m_CurrentFigureNodeInitialized = true; - this->PlanarFigureSelectionChanged(); - - m_DrawLine->setChecked(false); - m_DrawPath->setChecked(false); - m_DrawAngle->setChecked(false); - m_DrawFourPointAngle->setChecked(false); - m_DrawEllipse->setChecked(false); - m_DrawRectangle->setChecked(false); - m_DrawPolygon->setChecked(false); - // enable the crosshair navigation - this->EnableCrosshairNavigation(); + this->CheckForTopMostVisibleImage(nonConstNode); } -void QmitkMeasurementView::PlanarFigureSelected( itk::Object* object, const itk::EventObject& /*event*/ ) +void QmitkMeasurementView::PlanarFigureSelected( itk::Object* object, const itk::EventObject& ) { - // Mark to-be-edited PlanarFigure as selected - mitk::PlanarFigure* figure = dynamic_cast< mitk::PlanarFigure* >( object ); - if ( figure != NULL ) + MEASUREMENT_DEBUG << "planar figure " << object << " selected"; + std::map::iterator it = + d->m_DataNodeToPlanarFigureData.begin(); + + d->m_CurrentSelection.clear(); + while( it != d->m_DataNodeToPlanarFigureData.end()) { - // Get node corresponding to PlanarFigure - mitk::DataNode::Pointer figureNode = this->GetDataStorage()->GetNode( - mitk::NodePredicateData::New( figure ) ); + mitk::DataNode* node = it->first; + QmitkPlanarFigureData& data = it->second; - // Select this node (and deselect all others) - QList< mitk::DataNode::Pointer > selectedNodes = this->GetDataManagerSelection(); - for ( int i = 0; i < selectedNodes.size(); i++ ) + if( data.m_Figure == object ) { - selectedNodes[i]->SetSelected( false ); + MITK_DEBUG << "selected node found. enabling selection"; + node->SetSelected(true); + d->m_CurrentSelection.push_back( node ); } - std::vector< mitk::DataNode* > selectedNodes2 = m_SelectedPlanarFigures->GetNodes(); - for ( unsigned int i = 0; i < selectedNodes2.size(); i++ ) + else { - selectedNodes2[i]->SetSelected( false ); + node->SetSelected(false); } - figureNode->SetSelected( true ); - - m_CurrentFigureNode = figureNode; - *m_SelectedPlanarFigures = figureNode; - - // Re-initialize after selecting new PlanarFigure - this->PlanarFigureSelectionChanged(); + ++it; } + this->UpdateMeasurementText(); + this->RequestRenderWindowUpdate(); } - -mitk::DataNode::Pointer QmitkMeasurementView::DetectTopMostVisibleImage() +void QmitkMeasurementView::PlanarFigureInitialized() { - // get all images from the data storage - mitk::DataStorage::SetOfObjects::ConstPointer Images = this->GetDataStorage()->GetSubset( mitk::NodePredicateDataType::New("Image") ); - - mitk::DataNode::Pointer currentNode( m_SelectedImageNode->GetNode() ); - int maxLayer = itk::NumericTraits::min(); - - // iterate over selection - for (mitk::DataStorage::SetOfObjects::ConstIterator sofIt = Images->Begin(); sofIt != Images->End(); ++sofIt) - { - mitk::DataNode::Pointer node = sofIt->Value(); - if ( node.IsNull() ) - continue; - if (node->IsVisible(NULL) == false) - continue; - int layer = 0; - node->GetIntProperty("layer", layer); - if ( layer < maxLayer ) - continue; + MEASUREMENT_DEBUG << "planar figure initialized"; + d->m_UnintializedPlanarFigure = false; + d->m_DrawActionsToolBar->setEnabled(true); - currentNode = node; - } + d->m_DrawLine->setChecked(false); + d->m_DrawPath->setChecked(false); + d->m_DrawAngle->setChecked(false); + d->m_DrawFourPointAngle->setChecked(false); + d->m_DrawEllipse->setChecked(false); + d->m_DrawRectangle->setChecked(false); + d->m_DrawPolygon->setChecked(false); +} - return currentNode; +void QmitkMeasurementView::SetFocus() +{ + d->m_SelectedImageLabel->setFocus(); } -void QmitkMeasurementView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, - const char *propertyKey, mitk::BaseProperty *property ) +void QmitkMeasurementView::OnSelectionChanged(berry::IWorkbenchPart::Pointer part, + const QList &nodes) { - if ( m_CurrentFigureNode.IsNotNull() ) - { - m_CurrentFigureNode->GetData()->RemoveObserver( m_EndPlacementObserverTag ); - } + MEASUREMENT_DEBUG << "Determine the top most visible image"; + MEASUREMENT_DEBUG << "The PlanarFigure interactor will take the currently visible PlaneGeometry from the slice navigation controller"; - mitk::DataNode::Pointer newNode = mitk::DataNode::New(); - newNode->SetName(name.toStdString()); - newNode->SetData(figure); + this->CheckForTopMostVisibleImage(); - // Add custom property, if available - if ( (propertyKey != NULL) && (property != NULL) ) - { - newNode->AddProperty( propertyKey, property ); - } + MEASUREMENT_DEBUG << "refreshing selection and detailed text"; + d->m_CurrentSelection = nodes; + this->UpdateMeasurementText(); - // add observer for event when figure has been placed - typedef itk::SimpleMemberCommand< QmitkMeasurementView > SimpleCommandType; - SimpleCommandType::Pointer initializationCommand = SimpleCommandType::New(); - initializationCommand->SetCallbackFunction( this, &QmitkMeasurementView::PlanarFigureInitialized ); - m_EndPlacementObserverTag = figure->AddObserver( mitk::EndPlacementPlanarFigureEvent(), initializationCommand ); - - // add observer for event when figure is picked (selected) - typedef itk::MemberCommand< QmitkMeasurementView > MemberCommandType; - MemberCommandType::Pointer selectCommand = MemberCommandType::New(); - selectCommand->SetCallbackFunction( this, &QmitkMeasurementView::PlanarFigureSelected ); - m_SelectObserverTag = figure->AddObserver( mitk::SelectPlanarFigureEvent(), selectCommand ); - - // add observer for event when interaction with figure starts - SimpleCommandType::Pointer startInteractionCommand = SimpleCommandType::New(); - startInteractionCommand->SetCallbackFunction( this, &QmitkMeasurementView::DisableCrosshairNavigation); - m_StartInteractionObserverTag = figure->AddObserver( mitk::StartInteractionPlanarFigureEvent(), startInteractionCommand ); - - // add observer for event when interaction with figure starts - SimpleCommandType::Pointer endInteractionCommand = SimpleCommandType::New(); - endInteractionCommand->SetCallbackFunction( this, &QmitkMeasurementView::EnableCrosshairNavigation); - m_EndInteractionObserverTag = figure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), endInteractionCommand ); - - - // figure drawn on the topmost layer / image - this->GetDataStorage()->Add(newNode, this->DetectTopMostVisibleImage() ); - QList selectedNodes = GetDataManagerSelection(); - for(int i = 0; i < selectedNodes.size(); i++) - { - selectedNodes[i]->SetSelected(false); - } - std::vector selectedNodes2 = m_SelectedPlanarFigures->GetNodes(); - for(unsigned int i = 0; i < selectedNodes2.size(); i++) + for( int i=d->m_CurrentSelection.size()-1; i>= 0; --i) { - selectedNodes2[i]->SetSelected(false); - } - newNode->SetSelected(true); - - - m_CurrentFigureNodeInitialized = false; - m_CurrentFigureNode = newNode; + mitk::DataNode* node = d->m_CurrentSelection.at(i); - *m_SelectedPlanarFigures = newNode; - - this->RequestRenderWindowUpdate(); -} + mitk::PlanarFigure* _PlanarFigure = + _PlanarFigure = dynamic_cast (node->GetData()); -bool QmitkMeasurementView::AssertDrawingIsPossible(bool checked) -{ - if (m_SelectedImageNode->GetNode().IsNull()) - { - checked = false; - this->HandleException("Please select an image!", this->m_Parent, true); - m_DrawLine->setChecked(false); - return false; - } - - mitk::ILinkedRenderWindowPart* linkedRenderWindow = - dynamic_cast(this->GetRenderWindowPart()); - if (linkedRenderWindow) - { - linkedRenderWindow->EnableSlicingPlanes(false); + // the last selected planar figure + if( _PlanarFigure ) + { + mitk::ILinkedRenderWindowPart* linkedRenderWindow = + dynamic_cast(this->GetRenderWindowPart()); + if( linkedRenderWindow ) + { + mitk::Point3D centerP = _PlanarFigure->GetGeometry()->GetOrigin(); + linkedRenderWindow->GetRenderWindow("transversal")->GetSliceNavigationController()->SelectSliceByPoint(centerP); + } + break; + } } - // disable the crosshair navigation during the drawing - this->DisableCrosshairNavigation(); - return checked; + this->RequestRenderWindowUpdate(); } void QmitkMeasurementView::ActionDrawLineTriggered(bool checked) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) + mitk::PlanarLine::Pointer figure = mitk::PlanarLine::New(); - QString qString; - if(m_CurrentFigureNode.IsNull() || m_LineCounter == 0 || m_CurrentFigureNodeInitialized){ - qString = QString("Line%1").arg(++m_LineCounter); - } - else{ - qString = QString("Line%1").arg(m_LineCounter); - } + QString qString = QString("Line%1").arg(++d->m_LineCounter); this->AddFigureToDataStorage(figure, qString); - MITK_INFO << "PlanarLine initialized..."; + MEASUREMENT_DEBUG << "PlanarLine initialized..."; } void QmitkMeasurementView::ActionDrawPathTriggered(bool checked) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOff(); - + QString qString = QString("Path%1").arg(++d->m_PathCounter); + mitk::DataNode::Pointer node = this->AddFigureToDataStorage(figure, qString); mitk::BoolProperty::Pointer closedProperty = mitk::BoolProperty::New( false ); + node->SetProperty("ClosedPlanarPolygon", closedProperty); - QString qString; - if(m_CurrentFigureNode.IsNull() || m_PathCounter == 0 || m_CurrentFigureNodeInitialized){ - qString = QString("Path%1").arg(++m_PathCounter); - } - else{ - qString = QString("Path%1").arg(m_PathCounter); - } - this->AddFigureToDataStorage(figure, qString, - "ClosedPlanarPolygon", closedProperty); - - MITK_INFO << "PlanarPath initialized..."; + MEASUREMENT_DEBUG << "PlanarPath initialized..."; } void QmitkMeasurementView::ActionDrawAngleTriggered(bool checked) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) mitk::PlanarAngle::Pointer figure = mitk::PlanarAngle::New(); - QString qString; - if(m_CurrentFigureNode.IsNull() || m_AngleCounter == 0 || m_CurrentFigureNodeInitialized){ - qString = QString("Angle%1").arg(++m_AngleCounter); - } - else{ - qString = QString("Angle%1").arg(m_AngleCounter); - } + QString qString = QString("Angle%1").arg(++d->m_AngleCounter); this->AddFigureToDataStorage(figure, qString); - MITK_INFO << "PlanarAngle initialized..."; + MEASUREMENT_DEBUG << "PlanarAngle initialized..."; } void QmitkMeasurementView::ActionDrawFourPointAngleTriggered(bool checked) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) mitk::PlanarFourPointAngle::Pointer figure = - mitk::PlanarFourPointAngle::New(); - QString qString; - if(m_CurrentFigureNode.IsNull() || m_FourPointAngleCounter == 0 || m_CurrentFigureNodeInitialized){ - qString = QString("Four Point Angle%1").arg(++m_FourPointAngleCounter); - } - else{ - qString = QString("Four Point Angle%1").arg(m_FourPointAngleCounter); - } + mitk::PlanarFourPointAngle::New(); + QString qString = QString("Four Point Angle%1").arg(++d->m_FourPointAngleCounter); this->AddFigureToDataStorage(figure, qString); - MITK_INFO << "PlanarFourPointAngle initialized..."; + MEASUREMENT_DEBUG << "PlanarFourPointAngle initialized..."; } void QmitkMeasurementView::ActionDrawEllipseTriggered(bool checked) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); - QString qString; - if(m_CurrentFigureNode.IsNull() || m_EllipseCounter == 0 || m_CurrentFigureNodeInitialized){ - qString = QString("Circle%1").arg(++m_EllipseCounter); - } - else{ - qString = QString("Circle%1").arg(m_EllipseCounter); - } + QString qString = QString("Circle%1").arg(++d->m_EllipseCounter); this->AddFigureToDataStorage(figure, qString); - MITK_INFO << "PlanarCircle initialized..."; + MEASUREMENT_DEBUG << "PlanarCircle initialized..."; } void QmitkMeasurementView::ActionDrawRectangleTriggered(bool checked) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) mitk::PlanarRectangle::Pointer figure = mitk::PlanarRectangle::New(); - QString qString; - if(m_CurrentFigureNode.IsNull() || m_RectangleCounter == 0 || m_CurrentFigureNodeInitialized){ - qString = QString("Rectangle%1").arg(++m_RectangleCounter); - } - else{ - qString = QString("Rectangle%1").arg(m_RectangleCounter); - } + QString qString = QString("Rectangle%1").arg(++d->m_RectangleCounter); this->AddFigureToDataStorage(figure, qString); - MITK_INFO << "PlanarRectangle initialized..."; + MEASUREMENT_DEBUG << "PlanarRectangle initialized..."; } void QmitkMeasurementView::ActionDrawPolygonTriggered(bool checked) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->ClosedOn(); - QString qString; - if(m_CurrentFigureNode.IsNull() || m_PolygonCounter == 0 || m_CurrentFigureNodeInitialized){ - qString = QString("Polygon%1").arg(++m_PolygonCounter); - } - else{ - qString = QString("Polygon%1").arg(m_PolygonCounter); - } + QString qString = QString("Polygon%1").arg(++d->m_PolygonCounter); this->AddFigureToDataStorage(figure, qString); - MITK_INFO << "PlanarPolygon initialized..."; + MEASUREMENT_DEBUG << "PlanarPolygon initialized..."; } -void QmitkMeasurementView::ActionDrawArrowTriggered(bool checked) +void QmitkMeasurementView::CopyToClipboard( bool checked ) { - if(!this->AssertDrawingIsPossible(checked)) - return; + Q_UNUSED(checked) - MITK_WARN << "Draw Arrow not implemented yet."; + MEASUREMENT_DEBUG << "Copying current Text to clipboard..."; + QString clipboardText = d->m_SelectedPlanarFiguresText->toPlainText(); + QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); } -void QmitkMeasurementView::ActionDrawTextTriggered(bool checked) +mitk::DataNode::Pointer QmitkMeasurementView::AddFigureToDataStorage( + mitk::PlanarFigure* figure, const QString& name) { - if(!this->AssertDrawingIsPossible(checked)) - return; + // add as + MEASUREMENT_DEBUG << "Adding new figure to datastorage..."; + if( d->m_SelectedImageNode.IsNull() ) + { + MITK_ERROR << "No reference image available"; + return 0; + } - MITK_WARN << "Draw Text not implemented yet."; + mitk::DataNode::Pointer newNode = mitk::DataNode::New(); + newNode->SetName(name.toStdString()); + newNode->SetData(figure); + // set as selected + newNode->SetSelected( true ); + this->GetDataStorage()->Add(newNode, d->m_SelectedImageNode); + + // set all others in selection as deselected + for( size_t i=0; im_CurrentSelection.size(); ++i) + d->m_CurrentSelection.at(i)->SetSelected(false); + d->m_CurrentSelection.clear(); + d->m_CurrentSelection.push_back( newNode ); + this->UpdateMeasurementText(); + this->DisableCrosshairNavigation(); + d->m_DrawActionsToolBar->setEnabled(false); + d->m_UnintializedPlanarFigure = true; + return newNode; } -void QmitkMeasurementView::Activated() +void QmitkMeasurementView::UpdateMeasurementText() { - m_Activated = true; - MITK_INFO << "QmitkMeasurementView::Activated"; + d->m_SelectedPlanarFiguresText->clear(); - if (mitk::ILinkedRenderWindowPart* linkedRenderWindow = - dynamic_cast(this->GetRenderWindowPart())) + QString infoText; + QString plainInfoText; + unsigned int j = 1; + mitk::PlanarFigure* _PlanarFigure = 0; + mitk::PlanarAngle* planarAngle = 0; + mitk::PlanarFourPointAngle* planarFourPointAngle = 0; + mitk::DataNode::Pointer node = 0; + + for (unsigned int i=0; im_CurrentSelection.size(); ++i, ++j) { - linkedRenderWindow->EnableSlicingPlanes(false); - } + plainInfoText.clear(); + node = d->m_CurrentSelection.at(i); + _PlanarFigure = dynamic_cast (node->GetData()); - mitk::TNodePredicateDataType::Pointer isPlanarFigure - = mitk::TNodePredicateDataType::New(); + if( !_PlanarFigure ) + continue; - mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); - mitk::DataNode* node = 0; - mitk::PlanarFigure* figure = 0; - mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; - // finally add all nodes to the model - for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() - ; it++) - { - node = const_cast(it->Value().GetPointer()); - figure = dynamic_cast(node->GetData()); - if(figure) - { - figureInteractor = dynamic_cast(node->GetInteractor()); + if(j>1) + infoText.append("
    "); - if(figureInteractor.IsNull()) - figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); + infoText.append(QString("%1
    ").arg(QString::fromStdString( + node->GetName()))); + plainInfoText.append(QString("%1").arg(QString::fromStdString( + node->GetName()))); - mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); + planarAngle = dynamic_cast (_PlanarFigure); + if(!planarAngle) + { + planarFourPointAngle = dynamic_cast (_PlanarFigure); } - } - m_Visible = true; + double featureQuantity = 0.0; + for (unsigned int k = 0; k < _PlanarFigure->GetNumberOfFeatures(); ++k) + { + if ( !_PlanarFigure->IsFeatureActive( k ) ) continue; -} + featureQuantity = _PlanarFigure->GetQuantity(k); + if ((planarAngle && k == planarAngle->FEATURE_ID_ANGLE) + || (planarFourPointAngle && k == planarFourPointAngle->FEATURE_ID_ANGLE)) + featureQuantity = featureQuantity * 180 / vnl_math::pi; -void QmitkMeasurementView::Deactivated() -{ - MITK_INFO << "QmitkMeasurementView::Deactivated"; -} + infoText.append( + QString("%1: %2 %3") .arg(QString( + _PlanarFigure->GetFeatureName(k))) .arg(featureQuantity, 0, 'f', + 2) .arg(QString(_PlanarFigure->GetFeatureUnit(k)))); -void QmitkMeasurementView::ActivatedZombieView(berry::IWorkbenchPartReference::Pointer /*newZombiePart*/) -{ - if (mitk::ILinkedRenderWindowPart* linkedRenderWindow = - dynamic_cast(this->GetRenderWindowPart())) - { - linkedRenderWindow->EnableSlicingPlanes(true); + plainInfoText.append( + QString("\n%1: %2 %3") .arg(QString(_PlanarFigure->GetFeatureName(k))) .arg( + featureQuantity, 0, 'f', 2) .arg(QString( + _PlanarFigure->GetFeatureUnit(k)))); + + if(k+1 != _PlanarFigure->GetNumberOfFeatures()) + infoText.append("
    "); + } + + if (j != d->m_CurrentSelection.size()) + infoText.append("
    "); } - this->SetMeasurementInfoToRenderWindow("", m_LastRenderWindow); - this->EnableCrosshairNavigation(); + d->m_SelectedPlanarFiguresText->setHtml(infoText); +} + +void QmitkMeasurementView::AddAllInteractors() +{ + MEASUREMENT_DEBUG << "Adding interactors to all planar figures"; mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); - mitk::DataNode* node = 0; - mitk::PlanarFigure* figure = 0; - mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; - // finally add all nodes to the model + const mitk::DataNode* node = 0; + for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() ; it++) { node = const_cast(it->Value().GetPointer()); - figure = dynamic_cast(node->GetData()); - if(figure) - { - - figure->RemoveAllObservers(); - figureInteractor = dynamic_cast(node->GetInteractor()); - - if(figureInteractor) - mitk::GlobalInteraction::GetInstance()->RemoveInteractor(figureInteractor); - } + this->NodeAdded( node ); } - - m_Activated = false; } -void QmitkMeasurementView::Visible() +void QmitkMeasurementView::RemoveAllInteractors() { - m_Visible = true; - MITK_INFO << "QmitkMeasurementView::Visible"; -} + MEASUREMENT_DEBUG << "Removing interactors and observers from all planar figures"; -void QmitkMeasurementView::Hidden() -{ - m_Visible = false; - MITK_INFO << "QmitkMeasurementView::Hidden"; -} - -void QmitkMeasurementView::PropertyChanged(const mitk::DataNode* /*node*/, const mitk::BaseProperty* /*prop*/) -{ - this->PlanarFigureSelectionChanged(); -} - -void QmitkMeasurementView::NodeChanged(const mitk::DataNode* /*node*/) -{ - this->PlanarFigureSelectionChanged(); -} + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); + const mitk::DataNode* node = 0; -void QmitkMeasurementView::NodeRemoved(const mitk::DataNode* /*node*/) -{ - this->PlanarFigureSelectionChanged(); + for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() + ; it++) + { + node = const_cast(it->Value().GetPointer()); + this->NodeRemoved( node ); + } } -void QmitkMeasurementView::CopyToClipboard(bool) +mitk::DataNode::Pointer QmitkMeasurementView::DetectTopMostVisibleImage() { - std::vector headerRow; - std::vector > rows; - QString featureName; - QString featureQuantity; - std::vector newRow; - headerRow.push_back("Name"); + // get all images from the data storage + mitk::DataStorage::SetOfObjects::ConstPointer Images + = this->GetDataStorage()->GetSubset( mitk::NodePredicateDataType::New("Image") ); - std::vector nodes = m_SelectedPlanarFigures->GetNodes(); + mitk::DataNode::Pointer currentNode; + int maxLayer = itk::NumericTraits::min(); - for (std::vector::iterator it = nodes.begin(); it - != nodes.end(); ++it) + // iterate over selection + for (mitk::DataStorage::SetOfObjects::ConstIterator sofIt = Images->Begin(); sofIt != Images->End(); ++sofIt) { - mitk::PlanarFigure* planarFigure = - dynamic_cast ((*it)->GetData()); - if (!planarFigure) + mitk::DataNode::Pointer node = sofIt->Value(); + if ( node.IsNull() ) + continue; + if (node->IsVisible(NULL) == false) + continue; + int layer = 0; + node->GetIntProperty("layer", layer); + if ( layer < maxLayer ) continue; - newRow.clear(); - newRow.push_back(QString::fromStdString((*it)->GetName())); - newRow.resize(headerRow.size()); - for (unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i) - { - if ( !planarFigure->IsFeatureActive( i ) ) continue; - - featureName = planarFigure->GetFeatureName(i); - featureName.append(QString(" [%1]").arg(planarFigure->GetFeatureUnit(i))); - std::vector::iterator itColumn = std::find(headerRow.begin(), - headerRow.end(), featureName); - - featureQuantity - = QString("%1").arg(planarFigure->GetQuantity(i)).replace(QChar('.'), - ","); - if (itColumn == headerRow.end()) - { - headerRow.push_back(featureName); - newRow.push_back(featureQuantity); - } else - { - newRow[std::distance(headerRow.begin(), itColumn)] = featureQuantity; - } - - } - rows.push_back(newRow); - } - - QString clipboardText; - for (std::vector::iterator it = headerRow.begin(); it - != headerRow.end(); ++it) - clipboardText.append(QString("%1 \t").arg(*it)); - - for (std::vector >::iterator it = rows.begin(); it - != rows.end(); ++it) - { - clipboardText.append("\n"); - for (std::vector::iterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) - { - clipboardText.append(QString("%1 \t").arg(*it2)); - } - } - - QApplication::clipboard()->setText(clipboardText, QClipboard::Clipboard); - -} - -void QmitkMeasurementView::SetMeasurementInfoToRenderWindow(const QString& text, - QmitkRenderWindow* _RenderWindow) -{ - if(m_LastRenderWindow != _RenderWindow) - { - - if(m_LastRenderWindow) - { - QObject::disconnect( m_LastRenderWindow, SIGNAL( destroyed(QObject*) ) - , this, SLOT( OnRenderWindowDelete(QObject*) ) ); - } - m_LastRenderWindow = _RenderWindow; - if(m_LastRenderWindow) - { - QObject::connect( m_LastRenderWindow, SIGNAL( destroyed(QObject*) ) - , this, SLOT( OnRenderWindowDelete(QObject*) ) ); - } + currentNode = node; } - if(m_LastRenderWindow) - { - if (!text.isEmpty() && m_SelectedPlanarFigures->GetNode()->IsSelected()) - { - m_MeasurementInfoAnnotation->SetText(1, text.toLatin1().data()); - mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->InsertForegroundRenderer( - m_MeasurementInfoRenderer, true); - } - else - { - if (mitk::VtkLayerController::GetInstance( - m_LastRenderWindow->GetRenderWindow()) ->IsRendererInserted( - m_MeasurementInfoRenderer)) - mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->RemoveRenderer( - m_MeasurementInfoRenderer); - } - } + return currentNode; } void QmitkMeasurementView::EnableCrosshairNavigation() { + MEASUREMENT_DEBUG << "EnableCrosshairNavigation"; + // enable the crosshair navigation if (mitk::ILinkedRenderWindowPart* linkedRenderWindow = dynamic_cast(this->GetRenderWindowPart())) { - linkedRenderWindow->EnableLinkedNavigation(true); + MEASUREMENT_DEBUG << "enabling linked navigation"; + //linkedRenderWindow->EnableLinkedNavigation(true); + linkedRenderWindow->EnableSlicingPlanes(true); } } void QmitkMeasurementView::DisableCrosshairNavigation() { + MEASUREMENT_DEBUG << "DisableCrosshairNavigation"; + // disable the crosshair navigation during the drawing if (mitk::ILinkedRenderWindowPart* linkedRenderWindow = dynamic_cast(this->GetRenderWindowPart())) { - linkedRenderWindow->EnableLinkedNavigation(false); - } -} - -void QmitkMeasurementView::SetFocus() -{ - -} - -void QmitkMeasurementView::OnRenderWindowDelete(QObject * obj) -{ - if(obj == m_LastRenderWindow) - m_LastRenderWindow = 0; -} - -void QmitkMeasurementView::ReproducePotentialBug(bool) -{ - std::vector nodes = m_SelectedPlanarFigures->GetNodes(); - QString output; - - for (std::vector::iterator it = nodes.begin(); it - != nodes.end(); ++it) - { - mitk::DataNode* node = *it; - if (!node) continue; - - mitk::PlanarFigure* pf = dynamic_cast( node->GetData() ); - if (!pf) continue; - - output.append("huhu"); - output.append( QString::fromStdString( node->GetName() ) ); - - /** - Bug reproduction: - - 1. get geometry of planar figure from object - - 2. use this geometry to initialize rendering manager via InitializeViews - - 3. see what is produced. the DisplayGeometry of the render window will NOT contain the planar figure nicely. - - */ - - mitk::PlaneGeometry::Pointer planarFigureGeometry = dynamic_cast( pf->GetGeometry() ); - if (planarFigureGeometry.IsNull()) continue; // not expected - - - mitk::PlaneGeometry::Pointer geometryForRendering = dynamic_cast( planarFigureGeometry->Clone().GetPointer() ); - - bool applyWorkaround(true); - geometryForRendering->SetImageGeometry( applyWorkaround ); - - - std::cout << "==== with" << (applyWorkaround?"":"OUT") << " workaround ====================================" << std::endl; - std::cout << "--- PlanarFigure geometry --------------" << std::endl; - planarFigureGeometry->Print(std::cout); - std::cout << "----------------------------------------" << std::endl; - - mitk::RenderingManager::GetInstance()->InitializeViews( geometryForRendering, mitk::RenderingManager::REQUEST_UPDATE_ALL, false ); - - QmitkRenderWindow* renderWindow = this->GetRenderWindowPart()->GetRenderWindows().values().front(); - if (renderWindow == 0) - { - std::cout << "** No QmitkkRenderWindow available **" << std::endl; - return; - } - - std::cout << "--- Renderer->GetDisplayGeometry() ------------" << std::endl; - - renderWindow->GetRenderer()->GetDisplayGeometry()->Print(std::cout); - std::cout << "--- Renderer->GetCurrentWorldGeometry2D() -----" << std::endl; - renderWindow->GetRenderer()->GetCurrentWorldGeometry2D()->Print(std::cout); - std::cout << "--- Renderer->GetWorldGeometry() --------------" << std::endl; - renderWindow->GetRenderer()->GetWorldGeometry()->Print(std::cout); + MEASUREMENT_DEBUG << "disabling linked navigation"; + //linkedRenderWindow->EnableLinkedNavigation(false); + linkedRenderWindow->EnableSlicingPlanes(false); } - - m_SelectedPlanarFiguresText->setText(output); - } - diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h index 048bec3f80..5423827e34 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkMeasurementView.h @@ -1,221 +1,270 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_MEASUREMENT_H__INCLUDED #define QMITK_MEASUREMENT_H__INCLUDED +/* #include #include #include #include #include #include #include #include #include #include #include -#include - class QmitkPlanarFiguresTableModel; class QGridLayout; class QMainWindow; class QToolBar; class QLabel; class QTableView; class QTextBrowser; class QActionGroup; class QPushButton; class vtkRenderer; class vtkCornerAnnotation; +*/ +#include +#include + +/// forward declarations +struct QmitkMeasurementViewData; +namespace mitk +{ + class PlanarFigure; +} /// /// A view for doing measurements in digital images by means of /// mitk::Planarfigures which can represent drawing primitives (Lines, circles, ...). /// The view consists of only three main elements: /// 1. A toolbar for activating PlanarFigure drawing /// 2. A textbrowser which shows details for the selected PlanarFigures /// 3. A button for copying all details to the clipboard /// -class QmitkMeasurementView : public QmitkAbstractView, public mitk::IZombieViewPart +class QmitkMeasurementView : public QmitkAbstractView { Q_OBJECT + + public: + + static const std::string VIEW_ID; + QmitkMeasurementView(); + virtual ~QmitkMeasurementView(); + + void CreateQtPartControl(QWidget* parent); + void SetFocus(); + + virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, + const QList &nodes); + void NodeAdded(const mitk::DataNode* node); + void NodeChanged(const mitk::DataNode* node); + void NodeRemoved(const mitk::DataNode* node); + + void PlanarFigureSelected( itk::Object* object, const itk::EventObject& ); + protected slots: + ///# draw actions + void ActionDrawLineTriggered( bool checked = false ); + void ActionDrawPathTriggered( bool checked = false ); + void ActionDrawAngleTriggered( bool checked = false ); + void ActionDrawFourPointAngleTriggered( bool checked = false ); + void ActionDrawEllipseTriggered( bool checked = false ); + void ActionDrawRectangleTriggered( bool checked = false ); + void ActionDrawPolygonTriggered( bool checked = false ); + void CopyToClipboard( bool checked = false ); + + private: + void CreateConnections(); + mitk::DataNode::Pointer AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name); + void UpdateMeasurementText(); + void AddAllInteractors(); + void RemoveAllInteractors(); + mitk::DataNode::Pointer DetectTopMostVisibleImage(); + void EnableCrosshairNavigation(); + void DisableCrosshairNavigation(); + void PlanarFigureInitialized(); + void CheckForTopMostVisibleImage(mitk::DataNode* _NodeToNeglect=0); + + QmitkMeasurementViewData* d; +}; + +/* public: /// /// Just a shortcut /// typedef std::vector DataNodes; /// /// Initialize pointers to 0. The rest is done in CreateQtPartControl() /// QmitkMeasurementView(); /// /// Remove all event listener from DataStorage, DataStorageSelection, Selection Service /// virtual ~QmitkMeasurementView(); - static const std::string VIEW_ID; public: /// /// Initializes all variables. /// Builds up GUI. /// void CreateQtPartControl(QWidget* parent); /// /// Set widget planes visibility to false. /// Show only transversal view. /// Add an interactor to all PlanarFigures in the DataStorage (if they dont have one yet). /// Add their interactor to the global interaction. /// virtual void Activated(); virtual void Deactivated(); virtual void Visible(); virtual void Hidden(); /// /// Show widget planes and all renderwindows again. /// Remove all planar figure interactors from the global interaction. /// virtual void ActivatedZombieView(berry::IWorkbenchPartReference::Pointer zombieView); /// /// Invoked from a DataStorage selection /// virtual void NodeChanged(const mitk::DataNode* node); virtual void PropertyChanged(const mitk::DataNode* node, const mitk::BaseProperty* prop); virtual void NodeRemoved(const mitk::DataNode* node); virtual void NodeAddedInDataStorage(const mitk::DataNode* node); virtual void PlanarFigureInitialized(); virtual void PlanarFigureSelected( itk::Object* object, const itk::EventObject& event ); virtual void AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *propertyKey = NULL, mitk::BaseProperty *property = NULL ); /// /// Invoked when the DataManager selection changed. /// If an image is in the selection it will be set as the selected one for measurement, /// If a planarfigure is in the selection its parent image will be set as the selected one for measurement. /// All selected planarfigures will be added to m_SelectedPlanarFigures. /// Then PlanarFigureSelectionChanged is called /// virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes); public slots: /// /// Called when the renderwindow gets deleted /// void OnRenderWindowDelete(QObject * obj = 0); protected: /// /// Prints all features of the selected PlanarFigures into the TextBrowser. /// For the last figure in the selection list: /// - Go to the corresponding slice and show figure /// - Draw info text on the bottom right side of the corresponding renderwindow /// void PlanarFigureSelectionChanged(); /// Draws a string on the bottom left side of the render window /// void SetMeasurementInfoToRenderWindow(const QString& text, QmitkRenderWindow* _RenderWindow); bool AssertDrawingIsPossible(bool checked); void EnableCrosshairNavigation(); void DisableCrosshairNavigation(); void SetFocus(); protected slots: ///# draw actions void ActionDrawLineTriggered( bool checked = false ); void ActionDrawPathTriggered( bool checked = false ); void ActionDrawAngleTriggered( bool checked = false ); void ActionDrawFourPointAngleTriggered( bool checked = false ); void ActionDrawEllipseTriggered( bool checked = false ); void ActionDrawRectangleTriggered( bool checked = false ); void ActionDrawPolygonTriggered( bool checked = false ); void ActionDrawArrowTriggered( bool checked = false ); void ActionDrawTextTriggered( bool checked = false ); void CopyToClipboard( bool checked = false ); void ReproducePotentialBug(bool); // fields // widgets protected: - QWidget* m_Parent; - QGridLayout* m_Layout; - QLabel* m_SelectedImage; QAction* m_DrawLine; QAction* m_DrawPath; QAction* m_DrawAngle; QAction* m_DrawFourPointAngle; QAction* m_DrawEllipse; QAction* m_DrawRectangle; QAction* m_DrawPolygon; QToolBar* m_DrawActionsToolBar; QActionGroup* m_DrawActionsGroup; QTextBrowser* m_SelectedPlanarFiguresText; QPushButton* m_CopyToClipboard; vtkRenderer * m_MeasurementInfoRenderer; vtkCornerAnnotation *m_MeasurementInfoAnnotation; // Selection service /// berry::SelectionChangedAdapter must be a friend to call friend struct berry::SelectionChangedAdapter; berry::ISelectionListener::Pointer m_SelectionListener; mitk::DataStorageSelection::Pointer m_SelectedPlanarFigures; /// Selected image on which measurements will be performed /// mitk::DataStorageSelection::Pointer m_SelectedImageNode; mitk::DataNode::Pointer m_CurrentFigureNode; /// Counter variables to give a newly created Figure a unique name. /// unsigned int m_LineCounter; unsigned int m_PathCounter; unsigned int m_AngleCounter; unsigned int m_FourPointAngleCounter; unsigned int m_EllipseCounter; unsigned int m_RectangleCounter; unsigned int m_PolygonCounter; unsigned int m_EndPlacementObserverTag; unsigned int m_SelectObserverTag; unsigned int m_StartInteractionObserverTag; unsigned int m_EndInteractionObserverTag; bool m_Visible; bool m_CurrentFigureNodeInitialized; bool m_Activated; /// /// Saves the last renderwindow any info data was inserted /// QmitkRenderWindow* m_LastRenderWindow; private: + void RemoveEndPlacementObserverTag(); mitk::DataNode::Pointer DetectTopMostVisibleImage(); - -}; + */ #endif // QMITK_MEASUREMENT_H__INCLUDED diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentation.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentation.dox index c39be0a450..2130c5e5f5 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentation.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentation.dox @@ -1,268 +1,292 @@ /** \bundlemainpage{org_segment} The Segmentation Module \image html segmentation.png "Icon of the Module" Some of the features described below are not available in the open-source part of the MITK-3M3-Application. Available sections: - \ref org_mitk_gui_qt_segmentationUserManualOverview - \ref org_mitk_gui_qt_segmentationUserManualTechnical - \ref org_mitk_gui_qt_segmentationUserManualImageSelection - \ref org_mitk_gui_qt_segmentationUserManualManualKringeling - \ref org_mitk_gui_qt_segmentationUserManualManualKringeling1 - \ref org_mitk_gui_qt_segmentationUserManualManualKringeling2 - \ref org_mitk_gui_qt_segmentationUserManualManualKringeling3 - \ref org_mitk_gui_qt_segmentationUserManualManualKringeling4 - \ref org_mitk_gui_qt_segmentationUserManualManualKringeling5 - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation1 - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation2 - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation99 - \ref org_mitk_gui_qt_segmentationUserManualLesionSegmentation - \ref org_mitk_gui_qt_segmentationUserManualPostprocessing - \ref org_mitk_gui_qt_segmentationUserManualSurfaceMasking - \ref org_mitk_gui_qt_segmentationUserManualTechnicalDetail \section org_mitk_gui_qt_segmentationUserManualOverview Overview The Segmentation perspective allows you to create segmentations of anatomical and pathological structures in medical images of the human body. The perspective groups a number of tools which can be used for:
    • (semi-)automatic segmentation of organs on CT or MR image volumes
    • semi-automatic segmentation of lesions such as enlarged lymph nodes or tumors -
    • manual segmentation of any strucutures you might want to delineate +
    • manual segmentation of any structures you might want to delineate
    \image html org_mitk_gui_qt_segmentationIMGapplication.png Segmentation perspective consisting of the Data Manager view and the Segmentation view If you wonder what segmentations are good for, we shortly revisit the concept of a segmentation here. A CT or MR image is made up of volume of physical measurements (volume elements are called voxels). In CT images, for example, the gray value of each voxel corresponds to the mass absorbtion coefficient for X-rays in this voxel, which is similar in many %parts of the human body. The gray value does not contain any further information, so the computer does not know whether a given voxel is part of the body or the background, nor can it tell a brain from a liver. However, the distinction between a foreground and a background structure is required when:
    • you want to know the volume of a given organ (the computer needs to know which %parts of the image belong to this organ)
    • you want to create 3D polygon visualizations (the computer needs to know the surfaces of structures that should be drawn)
    • as a necessary pre-processing step for therapy planning, therapy support, and therapy monitoring
    Creating this distinction between foreground and background is called segmentation. -The Segmentation perspective of MITKApp uses a voxel based approach to segmentation, i.e. each voxel of an image must be completely assigned to either foreground or background. -This is, in contrast to some other applications which might use an approach based on contours, where the border of a structure might cut a voxel into two %parts. +The Segmentation perspective of the MITK ExtApp uses a voxel based approach to segmentation, i.e. each voxel of an image must be completely assigned to either foreground or background. +This is in contrast to some other applications which might use an approach based on contours, where the border of a structure might cut a voxel into two %parts. The remainder of this document will summarize the features of the Segmentation perspective and how they are used. \section org_mitk_gui_qt_segmentationUserManualTechnical Technical Issues The Segmentation perspective makes a number of assumptions. To know what this module can be used for, it will help you to know that:
    • Images must be 2D, 3D, or 3D+t
    • Images must be single-values, i.e. CT, MRI or "normal" ultrasound. Images from color doppler or photographic (RGB) images are not supported
    • Segmentations are handled as binary images of the same extent as the original image
    \section org_mitk_gui_qt_segmentationUserManualImageSelection Image Selection The Segmentation perspective makes use of the Data Manager view to give you an overview of all images and segmentations. \image html org_mitk_gui_qt_segmentationIMGselection.png Data Manager is used for selecting the current segmentation. The reference image is selected in the drop down box of the control area. To select the reference image (e.g. the original CT/MR image) use the drop down box in the control area of the Segmentation view. The segmentation image selected in the Data Manager is displayed below the drop down box. If no segmentation image exists or none is selected create a new segmentation image by using the "New segmentation" button. Some items of the graphical user interface might be disabled when no image is selected. In any case, the application will give you hints if a selection is needed. \section org_mitk_gui_qt_segmentationUserManualManualKringeling Manual Contouring With manual contouring you define which voxels are part of the segmentation and which are not. This allows you to create segmentations of any structeres that you may find in an image, even if they are not part of the human body. You might also use manual contouring to correct segmentations that result from sub-optimal automatic methods. The drawback of manual contouring is that you might need to define contours on many 2D slices. However, this is moderated by the interpolation feature, which will make suggestions for a segmentation. \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling1 Creating New Segmentations Unless you want to edit existing segmentations, you have to create a new, empty segmentation before you can edit it. To do so, click the "New manual segmentation" button. Input fields will appear where you can choose a name for the new segmentation and a color for its display. Click the checkmark button to confirm or the X button to cancel the new segmentation. Notice that the input field suggests names once you %start typing and that it also suggests colors for known organ names. If you use names that are not yet known to the application, it will automatically remember these names and consider them the next time you create a new segmentation. Once you created a new segmentation, you can notice a new item with the "binary mask" icon in the Data Manager tree view. This item is automatically selected for you, allowing you to %start editing the new segmentation right away. \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling2 Selecting Segmentations for Editing As you might want to have segmentations of multiple structures in a single patient image, the application needs to know which of them to use for editing. You select a segmenation by clicking it in the tree view of Data Manager. Note that segmentations are usually displayed as sub-items of "their" patient image. -In the rare case, where you need to edit a segmentation that is not displayed as a a sub-item, you can click both the original image AND the segmentation while holding down CTRL on the keyboard. +In the rare case, where you need to edit a segmentation that is not displayed as a a sub-item, you can click both the original image AND the segmentation while holding down CTRL or for Mac OS X the CMD on the keyboard. When a selection is made, the Segmentation View will hide all but the selected segmentation and the corresponding original image. When there are multiple segmentations, the unselected ones will remain in the Data Manager, you can make them visible at any time by selecting them. -If you want to see all segmenations at the same time, just clear the selection by clicking outside all the tree items in the Data Manager. \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling3 Selecting Editing Tools -If you are familiar with MITKApp, you know that clicking and moving the mouse in any of the 2D render windows will move around the crosshair that defines what part of the image is displayed. +If you are familiar with the MITK ExtApp, you know that clicking and moving the mouse in any of the 2D render windows will move around the crosshair that defines what part of the image is displayed. This behavior is disabled while any of the manual segmentation tools are active -- otherwise you might have a hard time concentrating on the contour you are drawing. To %start using one of the editing tools, click its button the the displayed toolbox. The selected editing tool will be active and its corresponding button will stay pressed until you click the button again. Selecting a different tool also deactivates the previous one. If you have to delineate a lot of images, you should try using shortcuts to switch tools. Just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.). \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling4 Using Editing Tools All of the editing tools work by the same principle: you use the mouse (left button) to click anywhere in a 2D window (any of the orientations transversal, sagittal, or frontal), move the mouse while holding the mouse button and release to finish the editing action. -All tools work on the original slices of the patient image, i.e. with some rotated/tilted MR image volumes you need to perform a "reinit" option in the Data Manger before you are able to use the editing tools. Multi-step undo and redo is fully supported by all editing tools. Use the application-wide undo button in the toolbar to revert erroneous %actions. \image html org_mitk_gui_qt_segmentationIMGiconAddSubtract.png Add and Subtract Tools Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed from (Subtract tool) the current segmentation. -Hold down the CTRL key to invert the operation (this will switch tools temporarily to allow for quick corrections). +Hold down the CTRL / CMD key to invert the operation (this will switch tools temporarily to allow for quick corrections). \image html org_mitk_gui_qt_segmentationIMGiconPaintWipe.png Paint and Wipe Tools Use the slider below the toolbox to change the radius of these round paintbrush tools. Move the mouse in any 2D window and press the left button to draw or erase pixels. -As the Add/Subtract tools, holding CTRL while drawing will invert the current tool's behavior. +As the Add/Subtract tools, holding CTRL / CMD while drawing will invert the current tool's behavior. \image html org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png Region Growing Tool Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool. Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. When working on an image with a high range of grey values, the selection range can be influenced more strongly by moving the cursor at higher velocity. Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. This enables you to quickly create segmentations of structures that have a good contrast to surrounding tissue, e.g. the lungs. The tool will select more or less pixels (corresponding to a changing gray value interval width) when you move the mouse up or down while holding down the left mouse button. A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to other pixels, of similar gray values, through a narrow "bridge" at the border of the structure. The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the leakage region and the tool will try to automatically remove this region (see illustration below). \image html org_mitk_gui_qt_segmentationIMGleakage.png Leakage correction feature of the Region Growing tool - +
    \image html org_mitk_gui_qt_segmentationIMGiconCorrection.png Correction Tool You do not have to draw a closed contour to use the Correction tool and do not need to switch between the Add and Substract tool to perform small corrective changes. The following figure shows the usage of this tool:
      +
    • if the user draws a line which %starts and ends outside the segmenation AND it intersects no other segmentation the endpoints of the line are connected and the resulting contour is filled
    • if the user draws a line which %starts and ends outside the segmenation a part of it is cut off (left image)
    • if the line is drawn fully inside the segmentation the marked region is added to the segmentation (right image)
    \image html org_mitk_gui_qt_segmentationIMGcorrectionActions.png %Actions of the Correction tool illustrated. - +
    \image html org_mitk_gui_qt_segmentationIMGiconFill.png Fill Tool Left-click inside a segmentation with holes to completely fill all holes. \image html org_mitk_gui_qt_segmentationIMGiconErase.png Erase Tool This tool removes a connected part of pixels that form a segmentation. You may use it to remove so called islands (see picture) or to clear a whole slice at once (hold CTRL while clicking). \subsection org_mitk_gui_qt_segmentationUserManualManualKringeling5 Interpolation -Creating segmentations for modern CT volumes is very time-consuming, because strucutres of interest can easily cover a range of 50 or more slices. -The Segmentation view offers a helpful feature for these cases: "Interpolation" creates suggestions for a segmentation whenever you have a slice that +Creating segmentations for modern CT volumes is very time-consuming, because structures of interest can easily cover a range of 50 or more slices. +The Manual Segmentation View offers two helpful features for these cases: + +
      +
    • 3D Interpolation +
    • 2D Interpolation +
    +
    +The 3D interpolation is activated by default when using the manual segmentation tools. That means if you start contouring, from the second contour onwards, the surface of the segmented area will be interpolated based on the given contour information. +The interpolation works with all available manual tools. Please note that this is currently a pure mathematical interpolation, i.e. image intensity information is not taken into account. With each further contour the interpolation result will be improved, +but the more contours you provide the longer the recalculation will take. To achieve an optimal interpolation result and in this way a most accurate segmentation you should try to describe the surface with sparse contours by segmenting in arbitrary +oriented planes. The 3D interpolation is not meant to be used for parallel slice-wise segmentation. + +\image html org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png 3D Interpolation HowTo + +You can accept the interpolation result by clicking the "Accept" - button below the tool buttons. +In this case the 3D interpolation will be deactivated automatically so that the result can be postprocessed without any interpolation running in background. During recalculation the interpolated surface is blinking yellow/white. When the interpolation +has finished the surface is shown yellow with a small opacity. Additional to the surface, black contours are shown in the 3D render window. They mark the positions of all the drawn contours which were used for the interpolation. +You can navigate between the drawn contours by clicking on the „Position“ - Nodes in the datamanager which are located below the selected segmentation. If you don't want to see these nodes just unckeck the „Show Position Nodes“ Checkbox and these nodes will be hidden. +If you want to delete a drawn contour we recommend to use the Erase-Tool since Redo/Undo is not yet working for 3D interpolation. + +
    +The 2D Interpolation creates suggestions for a segmentation whenever you have a slice that
    • has got neighboring slices with segmentations (these do not need to be direct neighbors but could also be a couple of slices away) AND
    • is completely clear of a manual segmentation -- i.e. there will be no suggestion if there is even only a single pixel of segmentation in the current slice.
    Interpolated suggestions are displayed in a different way than manual segmentations are, until you "accept" them as part of the segmentation. To accept single slices, click the "Accept" button below the toolbox. If you have segmented a whole organ in every-x-slice, you may also review the interpolations and then accept all of them at once by clicking "... all slices". \section org_mitk_gui_qt_segmentationUserManualOrganSegmentation Organ Segmentation +\note This feature is only available in our 3M3 Demo Application (http://www.mint-medical.de/productssolutions/mitk3m3/mitk3m3/#downloads) but not in the open source part of MITK + The manual contouring described above is a fallback option that will work for any kind of images and structures of interest. However, manual contouring is very time-consuming and tedious. This is why a major part of image analysis research is working towards automatic segmentation methods. The Segmentation View comprises a number of easy-to-use tools for segmentation of CT images (Liver) and MR image (left ventricle and wall, left and right lung). \subsection org_mitk_gui_qt_segmentationUserManualOrganSegmentation1 Liver on CT Images On CT image volumes, preferrably with a contrast agent in the portal venous phase, the Liver tool will fully automatically analyze and segment the image. All you have to do is to load and select the image, then click the "Liver" button. During the process, which takes a minute or two, you will get visual progress feedback by means of a contour that moves closer and closer to the real liver boundaries. \subsection org_mitk_gui_qt_segmentationUserManualOrganSegmentation2 Heart, Lung, and Hippocampus on MRI While liver segmentation is performed fully automatic, the following tools for segmentation of the heart, the lungs, and the hippocampus need a minimum amount of guidance. Click one of the buttons on the "Organ segmentation" page to add an average %model of the respective organ to the image. This %model can be dragged to the right position by using the left mouse button while holding down the CTRL key. You can also use CTRL + middle mouse button to rotate or CTRL + right mouse button to scale the %model. Before starting the automatic segmentation process by clicking the "Start segmentation" button, try placing the %model closely to the organ in the MR image (in most cases, you do not need to rotate or scale the %model). During the segmentation process, a green contour that moves closer and closer to the real liver boundaries will provide you with visual feedback of the segmentation progress. The algorithms used for segmentation of the heart and lung are method which need training by a number of example images. They will not work well with other kind of images, so here is a list of the image types that were used for training:
    • Hippocampus segmentation: T1-weighted MR images, 1.5 Tesla scanner (Magnetom Vision, Siemens Medical Solutions), 1.0 mm isotropic resolution
    • Heart: Left ventricle inner segmentation (LV Model): MRI; velocity encoded cine (VEC-cine) MRI sequence; trained on systole and diastole
    • Heart: Left ventricular wall segmentation (LV Inner Wall, LV Outer Wall): 4D MRI; short axis 12 slice spin lock sequence(SA_12_sl); trained on whole heart cycle
    • Lung segmentation: 3D and 4D MRI; works best on FLASH3D and TWIST4D sequences
    \subsection org_mitk_gui_qt_segmentationUserManualOrganSegmentation99 Other Organs As mentioned in the Heart/Lung section, most of the underlying methods are based on "training". The basic algorithm is versatile and can be applied on all kinds of segmentation problems where the structure of interest is topologically like a sphere (and not like a torus etc.). If you are interested in other organs than those offered by the current version of the Segmentation view, please contact our research team. \section org_mitk_gui_qt_segmentationUserManualLesionSegmentation Lesion Segmentation +\note This feature is only available in our 3M3 Demo Application (http://www.mint-medical.de/productssolutions/mitk3m3/mitk3m3/#downloads) but not in the open source part of MITK + Lesion segmentation is a little different from organ segmentation. Since lesions are not part of the healthy body, they sometimes have a diffused border, and are often found in varying places all over the body. The tools in this section offer efficient ways to create 3D segmentations of such lesions. The Segmentation View currently offers supoprt for enlarged lymph nodes. To segment an enlarged lymph node, find a more or less central slice of it, activate the "Lymph Node" tool and draw a rough contour on the inside of the lymph node. When releaseing the mouse button, a segmentation algorithm is started in a background task. The result will become visible after a couple of seconds, but you do not have to wait for it. If you need to segment several lymph nodes, you can continue to inspect the image right after closing the drawn contour. If the lymph node segmentation is not to your content, you can select the "Lymph Node Correction" tool and drag %parts of the lymph node surface towards the right position (works in 3D, not slice-by-slice). This kind of correction helps in many cases. If nothing else helps, you can still use the pure manual tools as a fallback. \section org_mitk_gui_qt_segmentationUserManualPostprocessing Things you can do with segmentations As mentioned in the introduction, segmentations are never an end in themselves. Consequently, the Segmentation view adds a couple of "post-processing" %actions to the Data Manager. These %actions are accessible through the context-menu of segmentations in Data Manager's list view \image html org_mitk_gui_qt_segmentationIMGDataManagerContextMenu.png Context menu items for segmentations.
    • Create polygon %model applies the marching cubes algorithms to the segmentation. This polygon %model can be used for visualization in 3D or other things such as stereolithography (3D printing).
    • Create smoothed polygon %model uses smoothing in addition to the marching cubes algorithms, which creates models that do not follow the exact outlines of the segmentation, but look smoother.
    • Statistics goes through all the voxels in the patient image that are part of the segmentation and calculates some statistical measures (minumum, maximum, median, histogram, etc.). Note that the statistics are ALWAYS calculated for the parent element of the segmentation as shown in Data Manager.
    • Autocrop can save memory. Manual segmentations have the same extent as the patient image, even if the segmentation comprises only a small sub-volume. This invisible and meaningless margin is removed by autocropping.
    \section org_mitk_gui_qt_segmentationUserManualSurfaceMasking Surface Masking You can use the surface masking tool to create binary images from a surface which is used used as a mask on an image. This task is demonstrated below: -\image html segmentationFromSurfaceBefore.png Load an image and a surface. Select the -image and the surface in the corresponding drop-down boxes (both are selected automatically -if there is just one image and one surface) +\image html segmentationFromSurfaceBefore.png Load an image and a surface. + +Select the image and the surface in the corresponding drop-down boxes (both are selected automatically if there is just one image and one surface) + +\image html segmentationFromSurfaceAfter.png Create segmentation from surface -\image html segmentationFromSurfaceAfter.png After clicking "Create segmentation -from surface" the newly created binary image is inserted in the DataManager and can -be used for further processing +After clicking "Create segmentation from surface" the newly created binary image is inserted in the DataManager and can be used for further processing \section org_mitk_gui_qt_segmentationUserManualTechnicalDetail Technical Information for Developers For technical specifications see \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system \subpage toolextensions . */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png new file mode 100644 index 0000000000..c11cee3e4c Binary files /dev/null and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGDataManagerContextMenu.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGDataManagerContextMenu.png index a8b6ec391e..a69d3b8dd2 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGDataManagerContextMenu.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGDataManagerContextMenu.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGapplication.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGapplication.png index 855ef8e33f..17c1389d92 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGapplication.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGapplication.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGcorrectionActions.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGcorrectionActions.png index f0ff206f57..dc8dc8ce03 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGcorrectionActions.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGcorrectionActions.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconAddSubtract.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconAddSubtract.png index 093d68182e..9e9aaf4c68 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconAddSubtract.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconAddSubtract.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconCorrection.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconCorrection.png index 2a5cbb4ff6..41f21628e6 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconCorrection.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconCorrection.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconErase.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconErase.png index 8d2c4f44c2..fafede4885 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconErase.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconErase.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFill.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFill.png index 488544f480..33b9498598 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFill.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFill.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconPaintWipe.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconPaintWipe.png index b429e2dc01..c034048640 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconPaintWipe.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconPaintWipe.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png index 4318a6e84d..ec2ce1bb6b 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGleakage.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGleakage.png index 2b26966cbc..a2e27f3df1 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGleakage.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGleakage.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceAfter.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceAfter.png index 272069e49a..4a1b161b91 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceAfter.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceAfter.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceBefore.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceBefore.png index 03bb8cd453..86f2354cf8 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceBefore.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/segmentationFromSurfaceBefore.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp index d2d5bde8a4..c00bfa03e2 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkCreatePolygonModelAction.cpp @@ -1,166 +1,171 @@ /*=================================================================== 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 "QmitkCreatePolygonModelAction.h" // MITK #include #include #include #include #include #include #include // Blueberry #include #include #include using namespace berry; using namespace mitk; using namespace std; QmitkCreatePolygonModelAction::QmitkCreatePolygonModelAction() { } QmitkCreatePolygonModelAction::~QmitkCreatePolygonModelAction() { } void QmitkCreatePolygonModelAction::Run(const QList &selectedNodes) { DataNode::Pointer selectedNode = selectedNodes[0]; Image::Pointer image = dynamic_cast(selectedNode->GetData()); if (image.IsNull()) return; try { if (!m_IsSmoothed) { ShowSegmentationAsSurface::Pointer surfaceFilter = ShowSegmentationAsSurface::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); surfaceFilter->AddObserver(ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); surfaceFilter->AddObserver(ProcessingError(), errorCommand); surfaceFilter->SetDataStorage(*m_DataStorage); surfaceFilter->SetPointerParameter("Input", image); surfaceFilter->SetPointerParameter("Group node", selectedNode); surfaceFilter->SetParameter("Show result", true); surfaceFilter->SetParameter("Sync visibility", false); surfaceFilter->SetParameter("Smooth", false); surfaceFilter->SetParameter("Apply median", false); surfaceFilter->SetParameter("Median kernel size", 3u); surfaceFilter->SetParameter("Gaussian SD", 1.5f); surfaceFilter->SetParameter("Decimate mesh", m_IsDecimated); surfaceFilter->SetParameter("Decimation rate", 0.8f); StatusBar::GetInstance()->DisplayText("Surface creation started in background..."); surfaceFilter->StartAlgorithm(); } else { ShowSegmentationAsSmoothedSurface::Pointer surfaceFilter = ShowSegmentationAsSmoothedSurface::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); surfaceFilter->SetDataStorage(*m_DataStorage); surfaceFilter->SetPointerParameter("Input", image); surfaceFilter->SetPointerParameter("Group node", selectedNode); berry::IWorkbenchPart::Pointer activePart = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->GetActivePart(); mitk::IRenderWindowPart* renderPart = dynamic_cast(activePart.GetPointer()); mitk::SliceNavigationController* timeNavController = 0; if (renderPart != 0) { timeNavController = renderPart->GetRenderingManager()->GetTimeNavigationController(); } int timeNr = timeNavController != 0 ? timeNavController->GetTime()->GetPos() : 0; surfaceFilter->SetParameter("TimeNr", timeNr); IPreferencesService::Pointer prefService = Platform::GetServiceRegistry().GetServiceById(IPreferencesService::ID); IPreferences::Pointer segPref = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); bool smoothingHint = segPref->GetBool("smoothing hint", true); float smoothing = (float)segPref->GetDouble("smoothing value", 1.0); float decimation = (float)segPref->GetDouble("decimation rate", 0.5); float closing = (float)segPref->GetDouble("closing ratio", 0.0); if (smoothingHint) { smoothing = 0.0; Vector3D spacing = image->GetGeometry()->GetSpacing(); for (Vector3D::Iterator iter = spacing.Begin(); iter != spacing.End(); ++iter) smoothing = max(smoothing, *iter); } surfaceFilter->SetParameter("Smoothing", smoothing); surfaceFilter->SetParameter("Decimation", decimation); surfaceFilter->SetParameter("Closing", closing); ProgressBar::GetInstance()->AddStepsToDo(8); StatusBar::GetInstance()->DisplayText("Smoothed surface creation started in background..."); - surfaceFilter->StartAlgorithm(); + try { + surfaceFilter->StartAlgorithm(); + } catch (...) + { + MITK_ERROR<<"Error creating smoothed polygon model: Not enough memory!"; + } } } catch(...) { MITK_ERROR << "Surface creation failed!"; } } void QmitkCreatePolygonModelAction::OnSurfaceCalculationDone() { StatusBar::GetInstance()->Clear(); } void QmitkCreatePolygonModelAction::SetDataStorage(DataStorage *dataStorage) { m_DataStorage = dataStorage; } void QmitkCreatePolygonModelAction::SetSmoothed(bool smoothed) { m_IsSmoothed = smoothed; } void QmitkCreatePolygonModelAction::SetDecimated(bool decimated) { m_IsDecimated = decimated; } void QmitkCreatePolygonModelAction::SetFunctionality(QtViewPart *) { } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationPostProcessing.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationPostProcessing.cpp index 9abb432071..b164fc88d1 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationPostProcessing.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationPostProcessing.cpp @@ -1,425 +1,425 @@ /*=================================================================== 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 "QmitkSegmentationPostProcessing.h" #include "QmitkNodeDescriptorManager.h" #include "QmitkToolGUI.h" #include "mitkAutoCropImageFilter.h" #include "mitkBinaryThresholdTool.h" #include "mitkRenderingManager.h" #include "mitkShowSegmentationAsSurface.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkImageCast.h" #include "mitkDataNodeObject.h" #include #include #include QmitkSegmentationPostProcessing::QmitkSegmentationPostProcessing(mitk::DataStorage* storage, QmitkFunctionality* functionality, QObject* parent) :QObject(parent) ,m_BlueBerryView(functionality) ,m_DataStorage(storage) { // register a couple of additional actions for DataManager's context menu QmitkNodeDescriptor* imageDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("NoneBinaryImage"); if (imageDataNodeDescriptor) { m_ThresholdAction = new QAction("Threshold..", parent); imageDataNodeDescriptor->AddAction(m_ThresholdAction); connect( m_ThresholdAction, SIGNAL( triggered(bool) ) , this, SLOT( ThresholdImage(bool) ) ); } else { MITK_WARN << "Could not get datamanager's node descriptor for 'Image'"; } // register a couple of additional actions for DataManager's context menu QmitkNodeDescriptor* binaryImageDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("ImageMask"); if (binaryImageDataNodeDescriptor) { m_CreateSurfaceAction = new QAction("Create polygon model", parent); binaryImageDataNodeDescriptor->AddAction(m_CreateSurfaceAction); connect( m_CreateSurfaceAction, SIGNAL( triggered(bool) ) , this, SLOT( CreateSurface(bool) ) ); m_CreateSmoothSurfaceAction = new QAction("Create smoothed polygon model", parent); binaryImageDataNodeDescriptor->AddAction(m_CreateSmoothSurfaceAction); connect( m_CreateSmoothSurfaceAction, SIGNAL( triggered(bool) ) , this, SLOT( CreateSmoothedSurface(bool) ) ); m_StatisticsAction = new QAction("Statistics", parent); binaryImageDataNodeDescriptor->AddAction(m_StatisticsAction); - berry::IBundle::Pointer imageStatisticsBundle = berry::Platform::GetBundle("org.mitk.gui.qt.imagestatistics"); - if(imageStatisticsBundle.IsNotNull()) + QSharedPointer imageStatisticsBundle = berry::Platform::GetCTKPlugin("org.mitk.gui.qt.measurementtoolbox"); + if(!imageStatisticsBundle.isNull()) connect( m_StatisticsAction, SIGNAL( triggered(bool) ) , this, SLOT( ImageStatistics(bool) ) ); else m_StatisticsAction->setEnabled( false ); m_AutocropAction = new QAction("Autocrop", parent); binaryImageDataNodeDescriptor->AddAction(m_AutocropAction); connect( m_AutocropAction, SIGNAL( triggered(bool) ) , this, SLOT( AutocropSelected(bool) ) ); } else { MITK_WARN << "Could not get datamanager's node descriptor for 'ImageMask'"; } // register for blueberry selection events m_SelectionListener = berry::ISelectionListener::Pointer(new berry::SelectionChangedAdapter(this, &QmitkSegmentationPostProcessing::SelectionChanged)); m_BlueBerryView->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelectionListener); } QmitkSegmentationPostProcessing::~QmitkSegmentationPostProcessing() { berry::ISelectionService* s = m_BlueBerryView->GetSite()->GetWorkbenchWindow()->GetSelectionService(); if(s) { s->RemovePostSelectionListener(m_SelectionListener); } // unregister a couple of additional actions for DataManager's context menu QmitkNodeDescriptor* imageDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("Image"); if (imageDataNodeDescriptor) { imageDataNodeDescriptor->RemoveAction( m_ThresholdAction ); } else { MITK_WARN << "Could not get datamanager's node descriptor for 'Image'"; } // unregister a couple of additional actions for DataManager's context menu QmitkNodeDescriptor* binaryImageDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("ImageMask"); if (binaryImageDataNodeDescriptor) { binaryImageDataNodeDescriptor->RemoveAction( m_CreateSurfaceAction ); binaryImageDataNodeDescriptor->RemoveAction( m_CreateSmoothSurfaceAction ); binaryImageDataNodeDescriptor->RemoveAction( m_StatisticsAction ); binaryImageDataNodeDescriptor->RemoveAction( m_AutocropAction ); } else { MITK_WARN << "Could not get datamanager's node descriptor for 'ImageMask'"; } } void QmitkSegmentationPostProcessing::SelectionChanged(berry::IWorkbenchPart::Pointer /*sourcepart*/, berry::ISelection::ConstPointer selection) { if ( selection.IsNull() ) { return; } // save current selection in member variable m_CurrentSelection = selection.Cast(); } QmitkSegmentationPostProcessing::NodeList QmitkSegmentationPostProcessing::GetSelectedNodes() const { NodeList result; if (m_CurrentSelection) { // iterate selection for (mitk::DataNodeSelection::iterator i = m_CurrentSelection->Begin(); i != m_CurrentSelection->End(); ++i) { // extract datatree node if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); result.push_back( node ); } } } return result; } void QmitkSegmentationPostProcessing::ThresholdImage(bool) { NodeList selection = this->GetSelectedNodes(); m_ThresholdingToolManager = mitk::ToolManager::New( m_DataStorage ); m_ThresholdingToolManager->RegisterClient(); m_ThresholdingToolManager->ActiveToolChanged += mitk::MessageDelegate( this, &QmitkSegmentationPostProcessing::OnThresholdingToolManagerToolModified ); m_ThresholdingDialog = new QDialog(qobject_cast(parent())); connect( m_ThresholdingDialog, SIGNAL(finished(int)), this, SLOT(ThresholdingDone(int)) ); QVBoxLayout* layout = new QVBoxLayout; layout->setContentsMargins(0, 0, 0, 0); mitk::Tool* tool = m_ThresholdingToolManager->GetToolById( m_ThresholdingToolManager->GetToolIdByToolType() ); if (tool) { itk::Object::Pointer possibleGUI = tool->GetGUI("Qmitk", "GUI"); QmitkToolGUI* gui = dynamic_cast( possibleGUI.GetPointer() ); if (gui) { gui->SetTool(tool); gui->setParent(m_ThresholdingDialog); layout->addWidget(gui); m_ThresholdingDialog->setLayout(layout); layout->activate(); m_ThresholdingDialog->setFixedSize(300,75); m_ThresholdingDialog->open(); } } for ( NodeList::iterator iter = selection.begin(); iter != selection.end(); ++iter ) { mitk::DataNode* node = *iter; if (node) { m_ThresholdingToolManager->SetReferenceData( node ); m_ThresholdingToolManager->ActivateTool( m_ThresholdingToolManager->GetToolIdByToolType() ); } } } void QmitkSegmentationPostProcessing::ThresholdingDone(int result) { if (result == QDialog::Rejected) m_ThresholdingToolManager->ActivateTool(-1); MITK_INFO << "Thresholding done, cleaning up"; m_ThresholdingDialog->deleteLater(); m_ThresholdingDialog = NULL; m_ThresholdingToolManager->SetReferenceData( NULL ); m_ThresholdingToolManager->SetWorkingData( NULL ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationPostProcessing::OnThresholdingToolManagerToolModified() { if ( m_ThresholdingToolManager.IsNull() ) return; //MITK_INFO << "Now got tool " << m_ThresholdingToolManager->GetActiveToolID(); if ( m_ThresholdingToolManager->GetActiveToolID() < 0) { if (m_ThresholdingDialog) m_ThresholdingDialog->accept(); } } void QmitkSegmentationPostProcessing::CreateSmoothedSurface(bool) { InternalCreateSurface(true); } void QmitkSegmentationPostProcessing::CreateSurface(bool) { InternalCreateSurface(false); } void QmitkSegmentationPostProcessing::InternalCreateSurface(bool smoothed) { NodeList selection = this->GetSelectedNodes(); for ( NodeList::iterator iter = selection.begin(); iter != selection.end(); ++iter ) { mitk::DataNode* node = *iter; if (node) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNull()) return; try { mitk::ShowSegmentationAsSurface::Pointer surfaceFilter = mitk::ShowSegmentationAsSurface::New(); // attach observer to get notified about result itk::SimpleMemberCommand::Pointer goodCommand = itk::SimpleMemberCommand::New(); goodCommand->SetCallbackFunction(this, &QmitkSegmentationPostProcessing::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), goodCommand); itk::SimpleMemberCommand::Pointer badCommand = itk::SimpleMemberCommand::New(); badCommand->SetCallbackFunction(this, &QmitkSegmentationPostProcessing::OnSurfaceCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), badCommand); mitk::DataNode::Pointer nodepointer = node; surfaceFilter->SetPointerParameter("Input", image); surfaceFilter->SetPointerParameter("Group node", nodepointer); surfaceFilter->SetParameter("Show result", true ); surfaceFilter->SetParameter("Sync visibility", false ); surfaceFilter->SetDataStorage( *m_DataStorage ); if (smoothed) { surfaceFilter->SetParameter("Smooth", true ); //surfaceFilter->SetParameter("Apply median", true ); surfaceFilter->SetParameter("Apply median", false ); // median makes the resulting surfaces look like lego models surfaceFilter->SetParameter("Median kernel size", 3u ); surfaceFilter->SetParameter("Gaussian SD", 2.5f ); surfaceFilter->SetParameter("Decimate mesh", true ); surfaceFilter->SetParameter("Decimation rate", 0.80f ); } else { surfaceFilter->SetParameter("Smooth", false ); surfaceFilter->SetParameter("Apply median", false ); surfaceFilter->SetParameter("Median kernel size", 3u ); surfaceFilter->SetParameter("Gaussian SD", 1.5f ); surfaceFilter->SetParameter("Decimate mesh", true ); surfaceFilter->SetParameter("Decimation rate", 0.8f ); } mitk::ProgressBar::GetInstance()->AddStepsToDo(10); mitk::ProgressBar::GetInstance()->Progress(2); mitk::StatusBar::GetInstance()->DisplayText("Surface creation started in background..."); surfaceFilter->StartAlgorithm(); } catch(...) { MITK_ERROR << "surface creation filter had an error"; } } else { MITK_INFO << " a NULL node selected"; } } } void QmitkSegmentationPostProcessing::OnSurfaceCalculationDone() { mitk::ProgressBar::GetInstance()->Progress(8); } void QmitkSegmentationPostProcessing::ImageStatistics(bool) { if (m_BlueBerryView) { m_BlueBerryView->GetSite()->GetWorkbenchWindow()->GetActivePage()->ShowView("org.mitk.views.imagestatistics"); } } void QmitkSegmentationPostProcessing::AutocropSelected(bool) { NodeList selection = this->GetSelectedNodes(); for ( NodeList::iterator iter = selection.begin(); iter != selection.end(); ++iter ) { mitk::DataNode* node = *iter; if (node) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNull()) return; mitk::ProgressBar::GetInstance()->AddStepsToDo(10); mitk::ProgressBar::GetInstance()->Progress(2); qApp->processEvents(); mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New(); cropFilter->SetInput( image ); cropFilter->SetBackgroundValue( 0 ); try { cropFilter->Update(); image = cropFilter->GetOutput(); if (image.IsNotNull()) { node->SetData( this->IncreaseCroppedImageSize(image) ); // bug fix 3145 } } catch(...) { MITK_ERROR << "Cropping image failed..."; } mitk::ProgressBar::GetInstance()->Progress(8); } else { MITK_INFO << " a NULL node selected"; } } } mitk::Image::Pointer QmitkSegmentationPostProcessing::IncreaseCroppedImageSize( mitk::Image::Pointer image ) { typedef itk::Image< short, 3 > ImageType; typedef itk::Image< unsigned char, 3 > PADOutputImageType; ImageType::Pointer itkTransformImage = ImageType::New(); mitk::CastToItkImage( image, itkTransformImage ); typedef itk::ConstantPadImageFilter< ImageType, PADOutputImageType > PadFilterType; PadFilterType::Pointer padFilter = PadFilterType::New(); unsigned long upperPad[3]; unsigned long lowerPad[3]; int borderLiner = 6; mitk::Point3D mitkOriginPoint; double origin[3]; origin[0]=0; origin[1]=0; origin[2]=0; itkTransformImage->SetOrigin(origin); lowerPad[0]=borderLiner/2; lowerPad[1]=borderLiner/2; lowerPad[2]=borderLiner/2; upperPad[0]=borderLiner/2; upperPad[1]=borderLiner/2; upperPad[2]=borderLiner/2; padFilter->SetInput(itkTransformImage); padFilter->SetConstant(0); padFilter->SetPadUpperBound(upperPad); padFilter->SetPadLowerBound(lowerPad); padFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer paddedImage = mitk::Image::New(); mitk::CastToMitkImage(padFilter->GetOutput(), paddedImage); paddedImage->SetGeometry(image->GetGeometry()); //calculate translation vector according to padding to get the new origin mitk::Vector3D transVector = image->GetGeometry()->GetSpacing(); transVector[0] = -(borderLiner/2); transVector[1] = -(borderLiner/2); transVector[2] = -(borderLiner/2); mitk::Vector3D newTransVectorInmm = image->GetGeometry()->GetSpacing(); image->GetGeometry()->IndexToWorld(transVector, newTransVectorInmm); paddedImage->GetGeometry()->Translate(newTransVectorInmm); //paddedImage->SetRequestedRegionToLargestPossibleRegion(); return paddedImage; } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkStatisticsAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkStatisticsAction.cpp index 4fc9977c98..6472722056 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkStatisticsAction.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkStatisticsAction.cpp @@ -1,55 +1,55 @@ /*=================================================================== 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 "QmitkStatisticsAction.h" QmitkStatisticsAction::QmitkStatisticsAction(): m_BlueBerryView(NULL) { } QmitkStatisticsAction::~QmitkStatisticsAction() { } void QmitkStatisticsAction::Run(const QList& /*selectedNodes*/) { - berry::IBundle::Pointer imageStatisticsBundle = berry::Platform::GetBundle("org.mitk.gui.qt.imagestatistics"); + QSharedPointer imageStatisticsBundle = berry::Platform::GetCTKPlugin("org.mitk.gui.qt.measurementtoolbox"); - if (m_BlueBerryView && imageStatisticsBundle.IsNotNull()) + if (m_BlueBerryView && !imageStatisticsBundle.isNull()) { m_BlueBerryView->GetSite()->GetWorkbenchWindow()->GetActivePage()->ShowView("org.mitk.views.imagestatistics"); } } void QmitkStatisticsAction::SetFunctionality(berry::QtViewPart* functionality) { this->m_BlueBerryView = functionality; } void QmitkStatisticsAction::SetDataStorage(mitk::DataStorage* /*dataStorage*/) { //not needed } void QmitkStatisticsAction::SetSmoothed(bool /*smoothed*/) { //not needed } void QmitkStatisticsAction::SetDecimated(bool /*decimated*/) { //not needed } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp index 26bd96dd20..7b0547a5e1 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.cpp @@ -1,115 +1,117 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) -Copyright (c) German Cancer Research Center, +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 +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 "QmitkThresholdAction.h" // MITK #include #include -#include +#include // Qt #include #include #include +#include using namespace berry; using namespace mitk; using namespace std; QmitkThresholdAction::QmitkThresholdAction() - : m_ThresholdingDialog(NULL) -{ +{ } QmitkThresholdAction::~QmitkThresholdAction() { } void QmitkThresholdAction::Run(const QList &selectedNodes) { m_ThresholdingToolManager = ToolManager::New(m_DataStorage); m_ThresholdingToolManager->RegisterClient(); - m_ThresholdingToolManager->ActiveToolChanged += mitk::MessageDelegate(this, &QmitkThresholdAction::OnThresholdingToolManagerToolModified); - - m_ThresholdingDialog = new QDialog(QApplication::activeWindow()); - connect(m_ThresholdingDialog, SIGNAL(finished(int)), this, SLOT(ThresholdingDone(int))); - - QVBoxLayout *layout = new QVBoxLayout; - layout->setContentsMargins(0, 0, 0, 0); Tool *binaryThresholdTool = m_ThresholdingToolManager->GetToolById(m_ThresholdingToolManager->GetToolIdByToolType()); - if (binaryThresholdTool != NULL) { - QmitkToolGUI *gui = dynamic_cast(binaryThresholdTool->GetGUI("Qmitk", "GUI").GetPointer()); - + QmitkBinaryThresholdToolGUI *gui = dynamic_cast(binaryThresholdTool->GetGUI("Qmitk", "GUI").GetPointer()); if (gui != NULL) { - gui->SetTool(binaryThresholdTool); - gui->setParent(m_ThresholdingDialog); + QDialog thresholdingDialog(QApplication::activeWindow(), Qt::Window | Qt::WindowStaysOnTopHint); - layout->addWidget(gui); + thresholdingDialog.setWindowFlags(thresholdingDialog.windowFlags() & ~Qt::WindowMinimizeButtonHint); - m_ThresholdingDialog->setLayout(layout); - m_ThresholdingDialog->setFixedSize(300, 80); + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(rejected()), &thresholdingDialog, SLOT(reject())); + connect(gui, SIGNAL(thresholdAccepted()), &thresholdingDialog, SLOT(reject())); - m_ThresholdingDialog->open(); - } + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(3, 3, 3, 3); - m_ThresholdingToolManager->SetReferenceData(selectedNodes[0]); - m_ThresholdingToolManager->ActivateTool(m_ThresholdingToolManager->GetToolIdByToolType()); - } -} + gui->SetTool(binaryThresholdTool); + gui->setParent(&thresholdingDialog); -void QmitkThresholdAction::ThresholdingDone(int result) -{ - if (result == QDialog::Rejected) - m_ThresholdingToolManager->ActivateTool(-1); + layout->addWidget(gui); + layout->addWidget(buttonBox); + + thresholdingDialog.setLayout(layout); + thresholdingDialog.setMinimumWidth(350); + + m_SelectedNode = selectedNodes[0]; + m_ThresholdingToolManager->SetReferenceData(selectedNodes[0]); + m_ThresholdingToolManager->ActivateTool(m_ThresholdingToolManager->GetToolIdByToolType()); - m_ThresholdingDialog->deleteLater(); - m_ThresholdingDialog = NULL; + m_ThresholdingToolManager->ActiveToolChanged += mitk::MessageDelegate(this, &QmitkThresholdAction::OnThresholdingToolManagerToolModified); + thresholdingDialog.exec(); + m_ThresholdingToolManager->ActiveToolChanged -= mitk::MessageDelegate(this, &QmitkThresholdAction::OnThresholdingToolManagerToolModified); - m_ThresholdingToolManager->SetReferenceData(NULL); - m_ThresholdingToolManager->SetWorkingData(NULL); + m_ThresholdingToolManager->SetReferenceData(NULL); + m_ThresholdingToolManager->ActivateTool(-1); + m_SelectedNode = 0; + } + } - RenderingManager::GetInstance()->RequestUpdateAll(); + m_ThresholdingToolManager->UnregisterClient(); } void QmitkThresholdAction::OnThresholdingToolManagerToolModified() { if (m_ThresholdingToolManager.IsNotNull()) + { if (m_ThresholdingToolManager->GetActiveToolID() < 0) - if (m_ThresholdingDialog != NULL) - m_ThresholdingDialog->accept(); + { + m_ThresholdingToolManager->SetReferenceData(m_SelectedNode); + m_ThresholdingToolManager->ActivateTool(m_ThresholdingToolManager->GetToolIdByToolType()); + } + } } void QmitkThresholdAction::SetDataStorage(DataStorage *dataStorage) { m_DataStorage = dataStorage; } void QmitkThresholdAction::SetSmoothed(bool) { } void QmitkThresholdAction::SetDecimated(bool) { } void QmitkThresholdAction::SetFunctionality(QtViewPart* /*functionality*/) { } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.h index 21c37f6150..2c24fe0c35 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkThresholdAction.h @@ -1,62 +1,57 @@ /*=================================================================== 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 QMITKTHRESHOLDACTION_H #define QMITKTHRESHOLDACTION_H #include // Parent classes #include #include // Data members #include #include -class QDialog; -class QmitkStdMultiWidget; class MITK_QT_SEGMENTATION QmitkThresholdAction : public QObject, public mitk::IContextMenuAction { Q_OBJECT Q_INTERFACES(mitk::IContextMenuAction) public: QmitkThresholdAction(); ~QmitkThresholdAction(); // IContextMenuAction void Run(const QList &selectedNodes); void SetDataStorage(mitk::DataStorage *dataStorage); void SetSmoothed(bool smoothed); void SetDecimated(bool decimated); void SetFunctionality(berry::QtViewPart *functionality); void OnThresholdingToolManagerToolModified(); -private slots: - void ThresholdingDone(int); - private: QmitkThresholdAction(const QmitkThresholdAction &); QmitkThresholdAction & operator=(const QmitkThresholdAction &); + mitk::DataNode::Pointer m_SelectedNode; mitk::DataStorage::Pointer m_DataStorage; mitk::ToolManager::Pointer m_ThresholdingToolManager; - QDialog *m_ThresholdingDialog; }; #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.cpp index a1280b58c7..01e2a48a34 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.cpp @@ -1,87 +1,93 @@ /*=================================================================== 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. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkRegionGrowingView.h" #include "QmitkStdMultiWidget.h" const std::string QmitkRegionGrowingView::VIEW_ID = "org.mitk.views.regiongrowing"; QmitkRegionGrowingView::QmitkRegionGrowingView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) { } QmitkRegionGrowingView::~QmitkRegionGrowingView() { } void QmitkRegionGrowingView::Deactivated() { + m_Controls->m_AdaptiveRGWidget->Deactivated(); +} + +void QmitkRegionGrowingView::Activated() +{ + m_Controls->m_AdaptiveRGWidget->Activated(); } void QmitkRegionGrowingView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkRegionGrowingViewControls; m_Controls->setupUi( parent ); m_Controls->m_AdaptiveRGWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_AdaptiveRGWidget->CreateConnections(); } } void QmitkRegionGrowingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; m_Controls->m_AdaptiveRGWidget->SetMultiWidget(&stdMultiWidget); } void QmitkRegionGrowingView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkRegionGrowingView::OnSelectionChanged( std::vector nodes ) { // iterate all selected objects, adjust warning visibility for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { m_Controls->lblWarning->setVisible( false ); m_Controls->m_AdaptiveRGWidget->SetInputImageNode(node); return; } } m_Controls->lblWarning->setVisible( true ); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.h index f67cbc22a2..2b99308f1a 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/regiongrowing/QmitkRegionGrowingView.h @@ -1,81 +1,82 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkRegionGrowingView_h #define QmitkRegionGrowingView_h #include #include #include "ui_QmitkRegionGrowingViewControls.h" /*! \brief QmitkRegionGrowingView Functionality for demonstration of MITK basics. This functionality allows the user to set some seed points that are used for a simple region growing algorithm from ITK. \warning This is only for demonstration, it is NOT meant to be useful! \sa QmitkFunctionality \ingroup Functionalities */ class QmitkRegionGrowingView : public QmitkFunctionality { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; QmitkRegionGrowingView(); virtual ~QmitkRegionGrowingView(); virtual void CreateQtPartControl(QWidget *parent); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); virtual void Deactivated(); + virtual void Activated(); protected slots: protected: /*! \brief ITK image processing function This function is templated like an ITK image. The MITK-Macro AccessByItk determines the actual pixel type and dimensionality of a given MITK image and calls this function for further processing (in our case region growing) */ template < typename TPixel, unsigned int VImageDimension > void ItkImageProcessing( itk::Image< TPixel, VImageDimension >* itkImage, mitk::Geometry3D* imageGeometry, mitk::DataNode* parent, int thresholdOffset, unsigned int t ); /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); Ui::QmitkRegionGrowingViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; }; #endif // _QMITKREGIONGROWINGVIEW_H_INCLUDED diff --git a/Utilities/CMakeLists.txt b/Utilities/CMakeLists.txt index 327554976f..d66fdcfa51 100644 --- a/Utilities/CMakeLists.txt +++ b/Utilities/CMakeLists.txt @@ -1,49 +1,48 @@ SUPPRESS_ALL_WARNINGS() # most stuff of these uses itk_zlib.h (via mitkIpPic.h) find_package(ITK) include(${ITK_USE_FILE}) # some legacy util files include in the old style with prefixed directory, # like #include include_directories(.) subdirs( ann ipSegmentation - #IIL4MITK pic2vtk tinyxml Poco qwt qxt mbilog glew vecmath qtsingleapplication ) # mbilog is independent of mitk, and cant use mitk macros # configuring happens through mbilog/mbilogConfig.cmake.in set(mbilog_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/mbilog" "${CMAKE_CURRENT_BINARY_DIR}/mbilog") set(mbilog_CONFIG_FILE "${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/mbilogConfig.cmake" CACHE INTERNAL "Path to module config" FORCE) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mbilog/mbilogConfig.cmake.in" "${mbilog_CONFIG_FILE}") # if(NOT MITK_CHILI_PLUGIN) subdirs(ipPic ipFunc) add_subdirectory(KWStyle) # endif(NOT MITK_CHILI_PLUGIN) set(Poco_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Poco CACHE PATH "top-level directory containing the poco include directories. E.g /usr/local/include/ or c:\\poco\\include\\poco-1.3.2" ) set(Poco_LIBRARY_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} CACHE PATH "top-level directory containing the poco libraries." ) diff --git a/Utilities/IIL4MITK/CMakeLists.txt b/Utilities/IIL4MITK/CMakeLists.txt deleted file mode 100644 index d6bd1c912c..0000000000 --- a/Utilities/IIL4MITK/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -find_package(VTK) -include(${VTK_USE_FILE}) -# mitkGL.h is in Core/Rendering :( -find_package(OpenGL) -if(NOT OPENGL_FOUND) - message("GL is required for MITK rendering") -endif(NOT OPENGL_FOUND ) -include_directories(${OPENGL_INCLUDE_DIR}) -include_directories(../ipPic) - -add_library(IIL4MITK ${MITK_WIN32_FORCE_STATIC} image.cpp item.cpp picimage.cpp texture.cpp) -target_link_libraries(IIL4MITK ${OPENGL_LIBRARIES} ${IPPIC_LIBRARIES}) -MITK_CREATE_MODULE_CONF(IIL4MITK - DEPENDS mitkIpPic -) - diff --git a/Utilities/IIL4MITK/image.cpp b/Utilities/IIL4MITK/image.cpp deleted file mode 100644 index 6962427af2..0000000000 --- a/Utilities/IIL4MITK/image.cpp +++ /dev/null @@ -1,413 +0,0 @@ -#include -#include -#include "widget.h" -#include "texture.h" -#include "image.h" - -static unsigned int -power2 (const unsigned int x) -{ - unsigned int y = 1; - - while (y < x) - { - y *= 2; - } - return y; -} - -const unsigned int iil4mitkImage::_bytes [] = {1, 2, 3, 4, 3,4}; - -iil4mitkImage::iil4mitkImage (unsigned int size) -: _width (0), _height (0), _rx (0), _ry (0), _rw (0), _rh (0), - _model (INTENSITY), _interpolation (false), _pixels (NULL), - _internal (GL_LUMINANCE), _size(size) -{ - -} - -iil4mitkImage::~iil4mitkImage () -{ - clear (); -} - -void -iil4mitkImage::setImage (const unsigned int width, const unsigned int height, - const int model, unsigned char *data) -{ - assert (width > 0); - assert (height > 0); - - _width = width; - _height = height; - _model = model; - _pixels = data; - invalidateTextures (); -} - -unsigned int -iil4mitkImage::imageWidth() const -{ - return _width; -} - -unsigned int -iil4mitkImage::imageHeight () const -{ - return _height; -} - -void -iil4mitkImage::setRegion (const unsigned int x, const unsigned y, - const unsigned w, const unsigned h) -{ - assert (x + w <= _width); - assert (y + h <= _height); - - if ((_rx != x) || (_ry != y) || (_rw != w) || (_rh != h)) { - _rx = x; _ry = y; _rw = w; _rh = h; - invalidateTextures (); - } -} - -unsigned int -iil4mitkImage::regionX () const -{ - return _rx; -} - -unsigned int -iil4mitkImage::regionY () const -{ - return _ry; -} - -unsigned int -iil4mitkImage::regionWidth () const -{ - return _rw; -} - -unsigned int -iil4mitkImage::regionHeight () const -{ - return _rh; -} - -void -iil4mitkImage::clear () -{ - _width = _height = 0; - _rx = _ry = _rw = _rh = 0; - _model = INTENSITY; - _pixels = NULL; - _internal = GL_LUMINANCE; - - std::map::iterator i; - for (i=_textures.begin(); i!=_textures.end(); ++i) { - for (unsigned int j = 0; j < (*i).second->size (); j++) { - delete (*(*i).second)[j]; - } - delete i->second; - } - _textures.clear (); -} - -float -iil4mitkImage::width () const -{ - return (float) _rw; -} - -float -iil4mitkImage::height () const -{ - return (float) _rh; -} - -int -iil4mitkImage::model () const -{ - return _model; -} - -void -iil4mitkImage::setInterpolation (const bool on) -{ - _interpolation = on; -} - -bool -iil4mitkImage::interpolation () const -{ - return _interpolation; -} - -void -iil4mitkImage::display (iil4mitkWidget* widget) -{ - GLdouble planeX [] = {-1, 0, 0, regionWidth ()}; - GLdouble planeY [] = {0, -1, 0, regionHeight ()}; - - if (!visible () - || (constraint () && (widget != constraint ()))) - { - return; - } - - if (_pixels) - { - assert (_rx + _rw <= _width); - assert (_ry + _rh <= _height); - - GLboolean texturing = glIsEnabled (GL_TEXTURE_2D); - GLboolean blending = glIsEnabled (GL_BLEND); - - glClipPlane (GL_CLIP_PLANE4, planeX); - glEnable (GL_CLIP_PLANE4); - glClipPlane (GL_CLIP_PLANE5, planeY); - glEnable (GL_CLIP_PLANE5); - - if ((_model == INTENSITY_ALPHA) || (_model == COLOR_ALPHA) || (_model == RGB) || (_model == RGBA)) - { - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - - glEnable (GL_TEXTURE_2D); - glMatrixMode (GL_MODELVIEW); - glPushMatrix (); - glColor4f (red (), green (), blue (), alpha ()); - glTranslatef (x (), y (), 0.0); - drawTextures (widget); - glPopMatrix (); - - glDisable (GL_CLIP_PLANE4); - glDisable (GL_CLIP_PLANE5); - - if (texturing == GL_FALSE) glDisable (GL_TEXTURE_2D); - if (blending == GL_FALSE) glDisable (GL_BLEND); - } -} - -void iil4mitkImage::drawTextures (iil4mitkWidget* widget) -{ - const unsigned int s = _size; // size of the tiles - unsigned int n, m; // number of the tiles - - n = (unsigned int) ceilf ((float) _rw / (float) (s - 2)); - m = (unsigned int) ceilf ((float) _rh / (float) (s - 2)); - - /* Allocate memory for the textures */ - - Textures* textures; - { - iil4mitkWidget* w; - unsigned int available, total; - - //w = (widget->IsSharing () ? widget->SharedWidget () : widget); - w = (false ? NULL : widget); - std::map::iterator tex_it = _textures.find (w); - if(tex_it!=_textures.end()) - textures = tex_it->second; - else - textures = NULL; - - if (!textures) { - textures = new Textures (); - typedef std::pair * > TexturePair; - _textures.insert (TexturePair(w, textures)); - } - available = textures->size (); - total = n * m; - //textures->resize (total); - int iii; - for (unsigned int i = available; i < total; i++) - { - textures->push_back(new _iil4mitkTexture (w)); - iii=textures->size (); - } - widget->MakeCurrent (); - } - - /* Render the textures */ - - glScalef ((float) (s-2), (float) (s-2), 1.0); - for (unsigned int i = 0; i < n; i++) - { - unsigned int pos [2]; // left-top corner of the region - unsigned int len [2]; // extent of the region - unsigned int tex [2]; // size of the texture - float res [2]; // resolution of the texture - - pos [0] = _rx+i*(s-2); - len [0] = (pos[0]+(s-2)>_rw ? _rw-pos[0] : s-2); - tex [0] = power2(s); - res [0] = 1.0f/tex[0]; - for (unsigned int j = 0; j < m; j++) - { - _iil4mitkTexture* texture; - - texture = (*textures)[i*m+j]; - pos [1] = _ry+j*(s-2); - len [1] = (pos[1]+(s-2)>_rh ? _rh-pos[1] : s-2); - tex [1] = power2(s); - res [1] = 1.0f/tex[1]; - - //if (widget->isVisible (pos [0], pos [1], len [0], len [1])) { - if (true) { - // widget->makeCurrent (); - texture->bind (); - // widget->makeCurrent (); - texture->setSize (tex[0], tex[1]); - texture->setModel (_model); - texture->setInterpolation (_interpolation); - if (!texture->isValid ()) - { - updateTexture (texture, pos[0], pos[1], len[0], len[1]); - } - - glBegin (GL_QUADS); - glTexCoord2f (res[0], res[1]); glVertex3f (0.0, 0.0, 0.0); - glTexCoord2f (1.0-res[0], res[1]); glVertex3f (1.0, 0.0, 0.0); - glTexCoord2f (1.0-res[0], 1.0-res[1]); glVertex3f (1.0, 1.0, 0.0); - glTexCoord2f (res[0], 1.0-res[1]); glVertex3f (0.0, 1.0, 0.0); - glEnd (); - } - glTranslatef (0.0, 1.0, 0.0); - } - glTranslatef (1.0, -((float) m), 0.0); - } -} - -void -iil4mitkImage::remove (iil4mitkWidget* widget) -{ - std::map::iterator i = - _textures.find (widget); - for (unsigned int j = 0; j < (*i).second->size (); j++) - { - delete (*(*i).second)[j]; - } - delete i->second; - _textures.erase(i); -} - -void -iil4mitkImage::invalidateTextures () -{ - std::map::iterator i; - for (i=_textures.begin(); i!=_textures.end(); ++i) - { - for (unsigned int j = 0; j < (*i).second->size (); j++) - { - (*(*i).second)[j]->invalidate (); - } - } -} - -void -iil4mitkImage::updateTexture (_iil4mitkTexture* texture, unsigned int x, unsigned int y, unsigned int w, unsigned int h) -{ - unsigned int p2w = texture->width (); - unsigned int p2h = texture->height (); - unsigned int size = p2w * p2h * _bytes [_model]; - unsigned char *region = (unsigned char *) malloc (size * sizeof (unsigned char)); - unsigned int left = (x == 0 ? 0 : 1); - unsigned int right = (x+w == _rw ? 0 : 1); - unsigned int top = (y == 0 ? 0 : 1); - unsigned int bottom = (y + h == _rh ? 0 : 1); - - if (x == 0) - { - copyImage (x, y-top, 1, h+top+bottom, region, p2w, p2h, 0, 1-top); - } - else - { - copyImage (x-1, y-top, 1, h+top+bottom, region, p2w, p2h, 0, 1-top); - } - if (x+w == _rw) - { - copyImage (x+w-1, y-top, 1, h+top+bottom, region, p2w, p2h, w+1, 1-top); - } - else - { - copyImage (x+w, y-top, 1, h+top+bottom, region, p2w, p2h, w+1, 1-top); - } - if (y == 0) - { - copyImage (x-left, y, w+left+right, 1, region, p2w, p2h, 1-left, 0); - } - else - { - copyImage (x-left, y-1, w+left+right, 1, region, p2w, p2h, 1-left, 0); - } - if (y+h == _rh) - { - copyImage (x-left, y+h-1, w+left+right, 1, region, p2w, p2h, 1-left, h+1); - } - else - { - copyImage (x-left, y+h, w+left+right, 1, region, p2w, p2h, 1-left, h+1); - } - copyImage (x, y, w, h, region, p2w, p2h, 1, 1); - texture->setData (region); - - /* - char filename [255]; - sprintf (filename, "tile_%u_%u.pic", x, y); - mitkIpPicDescriptor* pic = mitkIpPicNew (); - pic->type = mitkIpPicUInt; - pic->bpe = 8; - pic->dim = 2; - pic->n [0] = p2w; - pic->n [1] = p2h; - pic->data = region; - mitkIpPicPut (filename, pic); - pic->data = NULL; - mitkIpPicFree (pic); - */ - - free (region); -} - -void iil4mitkImage::copyImage(unsigned int x, unsigned int y, unsigned int w, - unsigned int h, unsigned char* data, unsigned int width, unsigned int, - unsigned int xoffset, unsigned int yoffset) -{ - const unsigned int bytes = _bytes [_model]; - const unsigned char *src = _pixels + (y * _width + x) * bytes; - unsigned char *dst = data + (yoffset * width + xoffset) * bytes; - - for (unsigned int i = 0; i < h; i++) - { - memcpy (dst, src, w * bytes); - src += _width * bytes; - dst += width * bytes; - } -} - -unsigned int -iil4mitkImage::bpe () -{ - return _bytes [_model] * 8; -} - -unsigned char* -iil4mitkImage::pixels () -{ - return _pixels; -} - -iil4mitkImage* -iil4mitkImage::find (const iil4mitkItem* item) -{ - iil4mitkImage* result = NULL; - - if (!item) return (iil4mitkImage *) NULL; - - if ( dynamic_cast(item)!=NULL ) { - result = const_cast(dynamic_cast(item)); - } - return (iil4mitkImage *) result; -} diff --git a/Utilities/IIL4MITK/image.h b/Utilities/IIL4MITK/image.h deleted file mode 100644 index 7dacf355d7..0000000000 --- a/Utilities/IIL4MITK/image.h +++ /dev/null @@ -1,195 +0,0 @@ -#ifndef _IMAGE_H_ -#define _IMAGE_H_ - -#include -#include "texture.h" -#include "item.h" -#include -#include - -/*! -\todo Manage the texture in a common way for all instances. -\todo Implement scaling of images here. -*/ -class iil4mitkImage : public iil4mitkItem { - -public: - - /*! - \brief The constructor. - */ - iil4mitkImage (unsigned int size = 128); - - /*! - \brief The destructor. - */ - virtual ~iil4mitkImage (); - - /*! - \brief The supported color models. - */ - enum {INTENSITY = 0, INTENSITY_ALPHA, COLOR, COLOR_ALPHA, RGB, RGBA}; - - /*! - \brief Sets the image. - @param width,height the image size. Make sure that the - dimensions have a power of two. Note that the image - size is limited (see \c maxDimensions). - @param model the color model - @param data the data - \note Make sure that the data keeps valid - until this instance is destroyed. - */ - virtual void setImage (const unsigned int width, const unsigned int height, const int model, unsigned char *data); - - /*! - \brief Gets the image size. - */ - unsigned int imageWidth () const; - unsigned int imageHeight () const; - - /*! - \brief Limits the image to the specified region. - @param x,y,w,h the position and size of the region - */ - virtual void setRegion (const unsigned int x, const unsigned y, const unsigned w, const unsigned h); - - /*! - \brief Gets the position and the size of the region. - */ - unsigned int regionX () const; - unsigned int regionY () const; - unsigned int regionWidth () const; - unsigned int regionHeight () const; - - /*! - \brief Clears the image. - */ - virtual void clear (); - - /*! - \brief Gets the maximal size of an image. - */ - // unsigned int maxSize () const; - - /*! - Gets the color model of the image. - */ - int model () const; - - /*! - \brief Switches interpolation for scaled images on or off. - */ - virtual void setInterpolation (const bool on = true); - - /*! - \brief Gets the interpolation method. - */ - bool interpolation () const; - - /*! - \brief Invalidates the content of the textures. - */ - virtual void invalidateTextures (); - -public: - - virtual float width () const; - virtual float height () const; - virtual void display (iil4mitkWidget* widget); - virtual void remove (iil4mitkWidget* widget); - -public: - - /*! - \brief Gets the first image of the item tree. - */ - static iil4mitkImage* find (const iil4mitkItem* item); - -protected: - - /*! - \brief Draws the image by means of the textures. - */ - void drawTextures (iil4mitkWidget* widget); - - /*! - \brief Updates the content of the given texture. - */ - void updateTexture (_iil4mitkTexture* texture, unsigned int x, unsigned int y, unsigned int w, unsigned int h); - - /*! - \brief Copies a specified region of the image. - @param x,y,w,h the geometry of the region - @param data the image data where the region is copied to - @param width,height the geometry of the image data - @param xoffset,yoffset the image point where the region is copied to - */ - virtual void copyImage (unsigned int x, unsigned int y, unsigned int w, unsigned int h, unsigned char* data, unsigned int width, unsigned int height, unsigned int xoffset, unsigned int yoffset); - - /*! - \brief Sets the color model of the image. - - \note method needed? - */ - // void setModel (int model); - - /*! - \brief Gets the number of bits per element of the image. - */ - unsigned int bpe (); - - /*! - \brief Gets the pixels of the image. - */ - unsigned char* pixels (); - - -private: - - /*! - \brief The dimensions of the image. - */ - unsigned int _width, _height; - - /*! - \brief The region of the image which will be displayed. - */ - unsigned int _rx, _ry, _rw, _rh; - - /*! - \brief The color model of the image. - */ - int _model; - - /*! - \brief The interpolation method. - */ - bool _interpolation; - - /*! - \brief The bytes per element of the image. - */ - static const unsigned int _bytes [6]; - - /*! - \brief The pixels of the image. - */ - unsigned char *_pixels; - - /*! - \brief The internal color model of the texture. - */ - GLenum _internal; - - /*! - \brief The list of allocated textures. - */ - typedef std::vector<_iil4mitkTexture*> Textures; - std::map _textures; - - unsigned int _size; - -}; - -#endif diff --git a/Utilities/IIL4MITK/item.cpp b/Utilities/IIL4MITK/item.cpp deleted file mode 100644 index 8c27c755d4..0000000000 --- a/Utilities/IIL4MITK/item.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "widget.h" -#include "item.h" - -iil4mitkItem::iil4mitkItem () - : _x (0.0), _y (0.0), _red (1.0), _green (1.0), _blue (1.0), _alpha (1.0), _lineWidth (0.0), _constraint (0), _visible (true), _pickable (false), _scalable (true) -{ -} - -void -iil4mitkItem::remove (iil4mitkWidget* ) -{ -} - -void -iil4mitkItem::move (const float x, const float y) -{ - _x = x; - _y = y; -} - -float -iil4mitkItem::x () const -{ - return _x; -} - -float -iil4mitkItem::y () const -{ - return _y; -} - -float -iil4mitkItem::width () const -{ - return 0.0; -} - -float -iil4mitkItem::height () const -{ - return 0.0; -} - - -void -iil4mitkItem::setColor (const float red, const float green, const float blue, const float alpha) -{ - _red = red; - _green = green; - _blue = blue; - _alpha = alpha; -} - -float -iil4mitkItem::red () const -{ - return _red; -} - -float -iil4mitkItem::green () const -{ - return _green; -} - -float -iil4mitkItem::blue () const -{ - return _blue; -} - -float -iil4mitkItem::alpha () const -{ - return _alpha; -} - -void -iil4mitkItem::setLineWidth (const float width) -{ - _lineWidth = width; -} - -float -iil4mitkItem::lineWidth () const -{ - return _lineWidth; -} - -void -iil4mitkItem::setPickable (const bool on) -{ - _pickable = on; -} - -bool -iil4mitkItem::pickable () const -{ - return _pickable; -} - -iil4mitkItem* -iil4mitkItem::picked (iil4mitkWidget*, const float x, const float y) -{ - if (!pickable () || !visible ()) return NULL; - - bool p = !((x < this->x ()) || (y < this->y ()) || (x > this->x () + this->width ()) || (y > this->y () + this->height ())); - iil4mitkItem* obj = (p ? this : NULL); - return obj; -} - -void -iil4mitkItem::setScalable (const bool scalable) -{ - _scalable = scalable; -} - -bool -iil4mitkItem::scalable () const -{ - return _scalable; -} - -void -iil4mitkItem::setConstraint (iil4mitkWidget* widget) { - _constraint = widget; -} - -iil4mitkWidget* -iil4mitkItem::constraint () const -{ - return _constraint; -} - -void -iil4mitkItem::setVisible (const bool visible) -{ - _visible = visible; -} - -bool -iil4mitkItem::visible () const -{ - return _visible; -} - -void -iil4mitkItem::hide () -{ - setVisible (false); -} - -void -iil4mitkItem::show () -{ - setVisible (true); -} diff --git a/Utilities/IIL4MITK/item.h b/Utilities/IIL4MITK/item.h deleted file mode 100644 index 456657c52b..0000000000 --- a/Utilities/IIL4MITK/item.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef _ITEM_H_ -#define _ITEM_H_ - -#ifdef WIN32 - #include -#endif - -#ifndef __APPLE__ - #include "GL/gl.h" -#else - #include "OpenGL/gl.h" -#endif - - -/*! -\brief The items are objects that can be displayed by -the widget. When the widget demands an update of the display, -each item draws itself into the viewport. If the user wants to -interact with some item, each item provides the information -whether it has been picked by the user. - -\todo Add a scale method. -\todo Add a parameter iil4mitkWidget to the picking method. Done. -*/ - -class iil4mitkItem { - -public: - - /*! - \brief The constructor. - */ - iil4mitkItem(); - - virtual ~iil4mitkItem() {} - - /*! - \brief Displays the item. - \note Make sure that the proper OpenGL context has been - made current. - \todo Should the method return whether something has been - rendered? - */ - virtual void display (iil4mitkWidget* widget) = 0; - - /*! - \brief Frees the resources of the item for the given widget. - \note Make sure that the proper OpenGL context has been - made current. - */ - virtual void remove (iil4mitkWidget* widget); - - /*! - \brief Sets the position of the item. - */ - void move (const float x, const float y); - - /*! - \brief Gets the position of the item. - */ - float x () const; - float y () const; - - /*! - \brief Gets the size of the item. - \note The method may be helpful for picking. - */ - virtual float width () const; - virtual float height () const; - - /*! - \brief Sets the color of the item. - @param red,green,blue the channels of the rgb color model - @param alpha the opacity. If alpha is equal to one, the - item is rendered opaque, otherwise it is rendered - semi-transparent. If alpha is zero, the item won't be visible. - */ - virtual void setColor (const float red, const float green, const float blue, const float alpha); - - /*! - \brief Gets the color channels. - */ - float red () const; - float green () const; - float blue () const; - float alpha () const; - - /*! - \brief Sets the widht of the isocontour. - @param width the line width - */ - void setLineWidth (const float width); - - /*! - \brief Gets the width of the isocontour. - */ - float lineWidth () const; - - /*! - \brief Sets the flag whether the user can pick the item. - */ - void setPickable (const bool on); - - /*! - \brief Checks if the user can pick the item. - @returns If the item is allowed to be picked, true is returned. - */ - bool pickable () const; - - /*! - \brief Checks if the item has been picked by the user. - @param widget the widget which caused an event - @param x,y the point the user has picked - @returns the picked item, or null if no item has been picked - */ - virtual iil4mitkItem* picked (iil4mitkWidget* widget, const float x, const float y); - - /*! - \brief Sets if items should be scaled when the viewport is zoomed. - If so, items will grow as far as the viewport is zoomed. Otherwise, - items will keep its size constant. - */ - void setScalable (const bool scalable); - - /*! - \brief Checks if items will be scaled when the viewport changes. - */ - bool scalable () const; - - /*! - \brief Sets the given widget as a constraint for rendering, i.e., - the contents are rendered only if the widget that demands the - rendering is the same as the given one. - @param widget the widget which the rendering is constraint to. If - null is given, the constraint will be removed. - */ - void setConstraint (iil4mitkWidget* widget); - - /*! - \brief Gets the widget which the rendering is constraint to. - */ - iil4mitkWidget* constraint () const; - - /*! - \brief Makes the item visible or hidden, respectively. - @param visible If true the item is displayed, otherwise it is not. - */ - void setVisible (const bool visible); - - /*! - \brief Checks if the item will be visible. - @returns If the item will be displayed, true is returned. - */ - bool visible () const; - - /*! - \brief Shows the item. - */ - void show (); - - /*! - \brief Hides the item. - */ - void hide (); - -private: - - /*! - \brief The position of the item. - */ - float _x, _y; - - /*! - \brief The color channels. - */ - float _red, _green, _blue, _alpha; - - /*! - \brief The line width. - */ - float _lineWidth; - - /*! - \brief The widget which the rendering is constraint to. - */ - iil4mitkWidget* _constraint; - - /*! - \brief Flags if the item will be displayed during - the next rendering. - */ - bool _visible; - - /*! - \brief Flags if the item is allowed to be picked. - */ - bool _pickable; - - /*! - \brief Flags if the item is scalable. If so, - items will grow as far as the viewport is zoomed. - Otherwise, items will keep its size constant. - */ - bool _scalable; - -}; - -#endif diff --git a/Utilities/IIL4MITK/picimage.cpp b/Utilities/IIL4MITK/picimage.cpp deleted file mode 100644 index 69a5aa75b4..0000000000 --- a/Utilities/IIL4MITK/picimage.cpp +++ /dev/null @@ -1,633 +0,0 @@ -#include -#include "texture.h" -#include "picimage.h" -#include - -iil4mitkPicImage::iil4mitkPicImage (unsigned int size) - : iil4mitkImage(size), _pic (NULL), _min (0.0), _max (0.0), _colors (NULL), _binary (false), _mask (false), _outline(false), _outlineWidth(1.0) -{ -} - -iil4mitkPicImage::~iil4mitkPicImage () -{ -} - -void -iil4mitkPicImage::setPicImage (mitkIpPicDescriptor* pic, int model) -{ - assert (pic); - assert (pic->dim >= 2); - - _pic = pic; - iil4mitkImage::setImage (pic->n[0], pic->n[1], model, (unsigned char *) pic->data); -} - -mitkIpPicDescriptor* -iil4mitkPicImage::image () const -{ - return _pic; -} - -void -iil4mitkPicImage::setExtrema (const float minimum, const float maximum) -{ - // assert (minimum < maximum); - - _min = minimum; - _max = maximum; - invalidateTextures (); -} - -void -iil4mitkPicImage::setWindow (const float level, const float window) -{ - // assert (window > 0); - _min = level - window / 2.0; - _max = _min + window; - invalidateTextures (); -} - -float -iil4mitkPicImage::minimum () const -{ - return _min; -} - -float -iil4mitkPicImage::maximum () const -{ - return _max; -} - -float -iil4mitkPicImage::level () const -{ - return (_min + _max) / 2.0; -} - -float -iil4mitkPicImage::window () const -{ - return (_max - _min); -} - -void - iil4mitkPicImage::setOpacityExtrema (const float minimum, const float maximum) -{ - // assert (minimum < maximum); - - _minOpac = minimum; - _maxOpac = maximum; - invalidateTextures (); -} - -void -iil4mitkPicImage::setOpacityWindow (const float level, const float window) -{ - // assert (window > 0); - _minOpac = level - window / 2.0; - _maxOpac = _minOpac + window; - invalidateTextures (); -} - -float -iil4mitkPicImage::minimumOpacity () const -{ - return _minOpac; -} - -float -iil4mitkPicImage::maximumOpacity () const -{ - return _maxOpac; -} - -float -iil4mitkPicImage::levelOpacity () const -{ - return (_minOpac + _maxOpac) / 2.0; -} - -float -iil4mitkPicImage::windowOpacity () const -{ - return (_maxOpac - _minOpac); -} - -void -iil4mitkPicImage::setColors (const unsigned char* colors) -{ - _colors = colors; -} - -const unsigned char* -iil4mitkPicImage::colors () const -{ - return _colors; -} - -void -iil4mitkPicImage::setBinary (const bool on) -{ - if (_binary != on) { - if (on) { - _mask = false; - } - _binary = on; - invalidateTextures (); - } -} - -bool -iil4mitkPicImage::binary () const -{ - return _binary; -} - -void -iil4mitkPicImage::setOutline (const bool on) -{ - if (_binary) { - _outline = on; - } -} - -void -iil4mitkPicImage::setOutlineWidth(float width) -{ - if (_binary) { - _outlineWidth = width; - } -} - - -bool -iil4mitkPicImage::outline () const -{ - return _outline; -} - -void -iil4mitkPicImage::setMask (const bool on) -{ - if (_mask != on) { - if (on) { - _binary = false; - } - _mask = on; - invalidateTextures (); - } -} - -bool -iil4mitkPicImage::mask () const -{ - return _mask; -} - -void -iil4mitkPicImage::clear () -{ - _pic = NULL; - _min = _max = 0.0; - _minOpac = _maxOpac = 0.0; - _colors = NULL; - _binary = false; - _mask = false; - _outline = false; - iil4mitkImage::clear (); -} - -iil4mitkPicImage* -iil4mitkPicImage::find (const iil4mitkItem* item) -{ - iil4mitkPicImage* result = NULL; - - if (!item) return NULL; - - if ( dynamic_cast(item)!=NULL ) { - result = const_cast(dynamic_cast(item)); - } - return result; -} - -#define MASK_INTENSITIES(TYPE, PIC) \ -{ \ -TYPE* source = (TYPE *) src; \ -unsigned char* dest = dst; \ -register float a; \ -if (model () == INTENSITY) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -*dest = (a > 255.0 ? 0 : (a < 0.0 ? 0 : 255)); \ -source++; \ -dest++; \ -} else \ -if (model () == INTENSITY_ALPHA) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -dest [1] = dest [0] = (a > 255.0 ? 0 : (a < 0.0 ? 0 : 255)); \ -source++; \ -dest += 2; \ -} \ -} - -#define BINARY_INTENSITIES(TYPE, PIC) \ -{ \ -TYPE* source = (TYPE *) src; \ -unsigned char* dest = dst; \ -register float a; \ -if (model () == INTENSITY) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -*dest = (a > 255.0 ? 255 : (a < 0.0 ? 0 : 255)); \ -source++; \ -dest++; \ -} else \ -if (model () == INTENSITY_ALPHA) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -dest [0] = (a > 255.0 ? 255 : (a < 0.0 ? 0 : 255)); \ -dest [1] = (a > 0.0 ? 255 : 0); \ -source++; \ -dest += 2; \ -} \ -} - -#define LIMIT_INTENSITIES(TYPE, PIC) \ -{ \ -TYPE* source = (TYPE *) src; \ -unsigned char* dest = dst; \ -register unsigned int i; \ -register float a,b,c,d; \ -if (model () == INTENSITY) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -*dest = (a > 255.0 ? 255 : (a < 0.0 ? 0 : (unsigned char) a)); \ -source++; \ -dest++; \ -} else \ -if (model () == INTENSITY_ALPHA) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -dest [0] = (a > 255.0 ? 255 : (a < 0.0 ? 0 : (unsigned char) a)); \ -dest [1] = (a > 0.0 ? 255 : 0); \ -source++; \ -dest += 2; \ -} else \ -if ((model () == COLOR) && _colors) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -i = (a > 255.0 ? 255 : (a < 0.0 ? 0 : (unsigned char) a)); \ -dest [0] = _colors [i*3]; \ -dest [1] = _colors [i*3+1]; \ -dest [2] = _colors [i*3+2]; \ -source++; \ -dest += 3; \ -} else \ -if ((model () == COLOR_ALPHA) && _colors) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -i = (a > 255.0 ? 255 : (a < 0.0 ? 0 : (unsigned char) a)); \ -dest [0] = _colors [i*4 ]; \ -dest [1] = _colors [i*4+1]; \ -dest [2] = _colors [i*4+2]; \ -dest [3] = _colors [i*4+3]; \ -source++; \ -dest += 4; \ -} else \ -if ((model () == COLOR) && !_colors) \ -while (dest < eol) { \ -a = source [0] * scale - bias; \ -b = source [slice] * scale - bias; \ -c = source [2*slice] * scale - bias; \ -dest [0] = (a > 255.0 ? 255 : (a < 0.0 ? 0 : (unsigned char) a)); \ -dest [1] = (b > 255.0 ? 255 : (b < 0.0 ? 0 : (unsigned char) b)); \ -dest [2] = (c > 255.0 ? 255 : (c < 0.0 ? 0 : (unsigned char) c)); \ -source++; \ -dest += 3; \ -} \ -if ((model () == COLOR_ALPHA) && !_colors) \ -{ \ -a = source [0] * scale - bias; \ -b = source [slice] * scale - bias; \ -c = source [2*slice] * scale - bias; \ -d = source [3*slice] * scale - bias; \ -dest [0] = (a > 255.0 ? 255 : (a < 0.0 ? 0 : (unsigned char) a)); \ -dest [1] = (b > 255.0 ? 255 : (b < 0.0 ? 0 : (unsigned char) b)); \ -dest [2] = (c > 255.0 ? 255 : (c < 0.0 ? 0 : (unsigned char) c)); \ -dest [3] = (d > 255.0 ? 255 : (d < 0.0 ? 0 : (unsigned char) d)); \ -source++; \ -dest += 4; \ -} \ -} - -// prototype -#ifdef USE_MMX -#define MAXSHORT 32767 -static void extrema (unsigned char* dst, short* src, const unsigned int num, const short minimum, const short maximum) -{ - const short low = -1024; - const short up = MAXSHORT - (maximum - low); - const short down = (minimum - low) + up; - const short scale = (255.0f/(float) (maximum-minimum)) * 256; - const short values [] = { - low, // lowest value - low, - low, - low, - up, // saturate above - up, - up, - up, - down, // saturate below - down, - down, - down, - scale, // scale - scale, - scale, - scale - }; - const unsigned int n = num / 16; - - __asm__ __volatile__ ( - ".align 16 \n\t" - "l1: \n\t" - "prefetcht0 64(%1) \n\t" - - "movq (%1),%%mm0 \n\t" - "movq 8(%1),%%mm1 \n\t" - "psubsw (%2),%%mm0 \n\t" - "psubsw (%2),%%mm1 \n\t" - "paddsw 8(%2),%%mm0 \n\t" - "paddsw 8(%2),%%mm1 \n\t" - "psubusw 16(%2),%%mm0 \n\t" - "psubusw 16(%2),%%mm1 \n\t" - "pmullw 24(%2),%%mm0 \n\t" - "pmullw 24(%2),%%mm1 \n\t" - "psrlw $8,%%mm0 \n\t" - "psrlw $8,%%mm1 \n\t" - "packuswb %%mm1,%%mm0 \n\t" - "movntq %%mm0,(%0) \n\t" - - "movq 16(%1),%%mm2 \n\t" - "movq 24(%1),%%mm3 \n\t" - "psubsw (%2),%%mm2 \n\t" - "psubsw (%2),%%mm3 \n\t" - "paddsw 8(%2),%%mm2 \n\t" - "paddsw 8(%2),%%mm3 \n\t" - "psubusw 16(%2),%%mm2 \n\t" - "psubusw 16(%2),%%mm3 \n\t" - "pmullw 24(%2),%%mm2 \n\t" - "pmullw 24(%2),%%mm3 \n\t" - "psrlw $8,%%mm2 \n\t" - "psrlw $8,%%mm3 \n\t" - "packuswb %%mm3,%%mm2 \n\t" - "movntq %%mm2,8(%0) \n\t" - - "emms \n\t" - "add $16,%0 \n\t" - "add $32,%1 \n\t" - "dec %3 \n\t" - "jnz l1 \n\t" - "emms \n\t" - : - :"r" (dst), "r" (src), "r" (values), "r" (n) - ); -} -#endif - -#ifndef PIC_IMAGE_PI - #define PIC_IMAGE_PI 3.141592653589 -#endif - -// Convert color pixels from (R,G,B) to (H,S,I). -// Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002. -template -void RGBtoHSI(T* RGB, T* HSI) { - T R = RGB[0], - G = RGB[1], - B = RGB[2], - nR = (R<0?0:(R>255?255:R))/255, - nG = (G<0?0:(G>255?255:G))/255, - nB = (B<0?0:(B>255?255:B))/255, - m = nR0) H = (nB<=nG)?theta:360-theta; - if (sum>0) S = 1 - 3/sum*m; - I = sum/3; - HSI[0] = (T)H; - HSI[1] = (T)S; - HSI[2] = (T)I; -} - -// Convert color pixels from (H,S,I) to (R,G,B). -template -void HSItoRGB(T* HSI, T* RGB) { - T H = (T)HSI[0], - S = (T)HSI[1], - I = (T)HSI[2], - a = I*(1-S), - R = 0, G = 0, B = 0; - if (H<120) { - B = a; - R = (T)(I*(1+S*std::cos(H*PIC_IMAGE_PI/180)/std::cos((60-H)*PIC_IMAGE_PI/180))); - G = 3*I-(R+B); - } else if (H<240) { - H-=120; - R = a; - G = (T)(I*(1+S*std::cos(H*PIC_IMAGE_PI/180)/std::cos((60-H)*PIC_IMAGE_PI/180))); - B = 3*I-(R+G); - } else { - H-=240; - G = a; - B = (T)(I*(1+S*std::cos(H*PIC_IMAGE_PI/180)/std::cos((60-H)*PIC_IMAGE_PI/180))); - R = 3*I-(G+B); - } - R*=255; G*=255; B*=255; - RGB[0] = (T)(R<0?0:(R>255?255:R)); - RGB[1] = (T)(G<0?0:(G>255?255:G)); - RGB[2] = (T)(B<0?0:(B>255?255:B)); -} - -void iil4mitkPicImage::copyImage (unsigned int x, unsigned int y, unsigned int w, unsigned int h, unsigned char* data, unsigned int width, unsigned int, unsigned int xoffset, unsigned int yoffset) -{ - assert (_pic); - // assert (_min <= _max); - - unsigned int slice = _pic->n[0] * _pic->n[1] * (_pic->bpe / 8); - float scale = (_max -_min > 0 ? 255.0 / (_max - _min) : 0.0); - float bias = _min * scale; - float scaleOpac = (_maxOpac -_minOpac > 0 ? 255.0 / (_maxOpac - _minOpac) : 0.0); - float biasOpac = _minOpac * scaleOpac; - unsigned char *src = (unsigned char *) _pic->data + (y * _pic->n[0] + x) * (_pic->bpe/8); - unsigned char *dst = data + (yoffset * width + xoffset) * (bpe () / 8); - unsigned char *eol = dst + w * (bpe () / 8); - - //printf ("updateTexture: start = (%u/%u), end = (%u/%u), length = (%u/%u)\n", x, y, x+w-1, y+h-1, w, h); - for (unsigned int i = 0; i < h; i++) { - - // copy current line - - if (model () == RGB) - { - unsigned char* source = (unsigned char *) src; - unsigned char* dest = dst; - while (dest < eol) - { - if(_min!=0 || _max!=255) - { - // level/window mechanism for intensity in HSI space - double rgb[3], hsi[3]; - rgb[0] = source[0]; - rgb[1] = source[1]; - rgb[2] = source[2]; - RGBtoHSI(rgb,hsi); - hsi[2] = hsi[2] * 255.0 * scale - bias; - hsi[2] = (hsi[2] > 255.0 ? 255 : (hsi[2] < 0.0 ? 0 : hsi[2])); - hsi[2] /= 255.0; - HSItoRGB(hsi,rgb); - dest[0] = (unsigned char)rgb[0]; - dest[1] = (unsigned char)rgb[1]; - dest[2] = (unsigned char)rgb[2]; - source+=3; - dest+=3; - } - else - { - *dest = *source; - ++source; - ++dest; - } - } - } - else if (model () == RGBA) - { - unsigned char* source = (unsigned char *) src; - unsigned char* dest = dst; - while (dest < eol) - { - if(_min!=0 || _max!=255 || _minOpac!=0 || _maxOpac!=255) - { - double rgb[3], alpha, hsi[3]; - - // level/window mechanism for intensity in HSI space - rgb[0] = source[0]; - rgb[1] = source[1]; - rgb[2] = source[2]; - alpha = source[3]; - RGBtoHSI(rgb,hsi); - hsi[2] = hsi[2] * 255.0 * scale - bias; - hsi[2] = (hsi[2] > 255.0 ? 255 : (hsi[2] < 0.0 ? 0 : hsi[2])); - hsi[2] /= 255.0; - HSItoRGB(hsi,rgb); - - // level/window mechanism for opacity - alpha = alpha * scaleOpac - biasOpac; - alpha = (alpha > 255.0 ? 255 : (alpha < 0.0 ? 0 : alpha)); - - dest[0] = (unsigned char)rgb[0]; - dest[1] = (unsigned char)rgb[1]; - dest[2] = (unsigned char)rgb[2]; - dest[3] = (unsigned char)alpha; - - source+=4; - dest+=4; - } - else - { - *dest = *source; - ++source; - ++dest; - } - } - } else - if (mask ()) { - mitkIpPicFORALL(MASK_INTENSITIES, _pic); - } else - if (binary ()) { - mitkIpPicFORALL(BINARY_INTENSITIES, _pic); - } else { -#ifdef USE_MMX - if (mitkIpPicDR(_pic->type, _pic->bpe) == mitkIpPicDR(mitkIpPicInt, 16)) { - unsigned char* d = dst; - unsigned char* s = src; - if (w / 16) { - extrema (dst, (short *) src, w, (short) _min, (short) _max); - } - if (w % 16) { - mitkIpPicFORALL(LIMIT_INTENSITIES, _pic); - dst = d; - src = s; - } - } else { - mitkIpPicFORALL(LIMIT_INTENSITIES, _pic); - } -#else - mitkIpPicFORALL(LIMIT_INTENSITIES, _pic); -#endif - } - - // go to next line - - src += _pic->n[0] * (_pic->bpe/8); - dst += width * (bpe () / 8); - eol = dst + w * (bpe () / 8); - } -} - - -void -iil4mitkPicImage::display (iil4mitkWidget* widget) -{ - if (!_outline) { - iil4mitkImage::display( widget ); - } - else { - glMatrixMode (GL_MODELVIEW); - glPushMatrix (); - glColor4f ( red(), green(), blue(), alpha() ); - //glColor4f( 1.0, 0.0, 0.0, 1.0 ); - glTranslatef( x(), y(), 0.0 ); - glLineWidth(_outlineWidth); - glBegin( GL_LINES ); - - int line = _pic->n[0]; - float fLine = (float)line; - float x=0.0, y=0.0; - mitkIpInt1_t *current; - mitkIpInt1_t *end = ((mitkIpInt1_t*)_pic->data) + (_pic->n[0]*_pic->n[1]); - int ii = 0, nn = _pic->n[0]*_pic->n[1]; - for (current = (mitkIpInt1_t*)_pic->data; current= line && *(current-line) == 0) { - glVertex3f( x, y, 0.0 ); - glVertex3f( x+1.0, y, 0.0 ); - } - if (ii <= nn-line && *(current+line) == 0) { - glVertex3f( x, y+1.0, 0.0 ); - glVertex3f( x+1.0, y+1.0, 0.0 ); - } - if (ii > 1 && *(current-1) == 0) { - glVertex3f( x, y, 0.0 ); - glVertex3f( x, y+1.0, 0.0 ); - } - if (ii < nn-1 && *(current+1) == 0) { - glVertex3f( x+1.0, y, 0.0 ); - glVertex3f( x+1.0, y+1.0, 0.0 ); - } - } - x += 1.0; - if (x >= fLine) { - x = 0.0; - y += 1.0; - } - } - glEnd (); - glLineWidth(1.0f); - glPopMatrix (); - } -} diff --git a/Utilities/IIL4MITK/picimage.h b/Utilities/IIL4MITK/picimage.h deleted file mode 100644 index 6408df5deb..0000000000 --- a/Utilities/IIL4MITK/picimage.h +++ /dev/null @@ -1,226 +0,0 @@ -#ifndef _PIC_IMAGE_H_ -#define _PIC_IMAGE_H_ - -#include -#include "image.h" - -/*! -\brief The class adds support for PIC images by providing a proper interface -to map the intensity range to the physical one of the display. -*/ -class iil4mitkPicImage : public iil4mitkImage { - -public: - /*! - \brief The constructor. - */ - iil4mitkPicImage (unsigned int size = 128); - - /*! - \brief The destructor. - */ - virtual ~iil4mitkPicImage (); - - /*! - \brief Sets the PIC image. - @param pic the PIC image - @param model the color model - - \note Is the model needed here? - */ - void setPicImage (mitkIpPicDescriptor* pic, int model = INTENSITY); - - /*! - \brief Gets the PIC image. - */ - mitkIpPicDescriptor* image () const; - - /*! - \brief Sets the range of the intensities which will be displayed. - @param minimum the minimal intensity - @param maximum the maximal intensity - */ - void setExtrema (const float minimum, const float maximum); - - /*! - \brief Sets the range of the intensities which will be displayed. - @param level the level of the window - @param window the width of the window - */ - void setWindow (const float level, const float window); - - /*! - \brief Gets the minimal intensity which will be displayed. - */ - float minimum () const; - - /*! - \brief Gets the maximal intensity which will be displayed. - */ - float maximum () const; - - /*! - \brief Gets the level of the window which limits the - displayed intensities. - */ - float level () const; - - /*! - \brief Gets the width of the window which limits the - displayed intensities. - */ - float window () const; - - /*! - \brief Sets the range of opacity values which will be displayed. - @param minimum the minimal opacity - @param maximum the maximal opacity - */ - void setOpacityExtrema (const float minimum, const float maximum); - - /*! - \brief Sets the range of the opacities which will be displayed. - @param level the level of the window - @param window the width of the window - */ - void setOpacityWindow (const float level, const float window); - - /*! - \brief Gets the minimal opacity which will be displayed. - */ - float minimumOpacity () const; - - /*! - \brief Gets the maximal opacity which will be displayed. - */ - float maximumOpacity () const; - - /*! - \brief Gets the level of the window which limits the - displayed opacities. - */ - float levelOpacity () const; - - /*! - \brief Gets the width of the window which limits the - displayed opacities. - */ - float windowOpacity () const; - - /*! - \brief Sets the color map which assigns each intensity - a color according to the color model. - @param colors the array of colors using either the - RGB format or RGBA format which depends on the color - model. - */ - void setColors (const unsigned char* colors); - - /*! - \brief Gets the color map. - */ - const unsigned char* colors () const; - - /*! - \brief Sets the binary flag which forces the data - to be converted into a binary image. - */ - void setBinary (const bool on); - - /*! - \brief Checks if the binary flag is set. - */ - bool binary () const; - - /*! - \brief Sets the outline flag which forces the data - to be displayed as outline. Only possible with binary data! - */ - void setOutline (const bool on); - /*! - \brief Sets the line width used to draw the binary - outline. Requires the outline flag to be set via setOutline(true) first. - */ - void setOutlineWidth (float width); - - /*! - \brief Checks if the outline flag is set. - */ - bool outline () const; - - /*! - \brief Sets the mask flag which ensures that - only pixels are displayed which are in the - intensity window. - */ - void setMask (const bool on); - - /*! - \brief Checks if the mask flag is set. - */ - bool mask () const; - -public: - - virtual void clear (); - virtual void display (iil4mitkWidget* widget); - -public: - - /*! - \brief Gets the first image of the item tree. - */ - static iil4mitkPicImage* find (const iil4mitkItem* item); - -protected: - - virtual void copyImage (unsigned int x, unsigned int y, unsigned int w, unsigned int h, unsigned char* data, unsigned int width, unsigned int height, unsigned int xoffset, unsigned int yoffset); - -private: - - /*! - \brief The PIC image. - */ - mitkIpPicDescriptor* _pic; - - /*! - \brief The extremal values. - */ - float _min, _max; - - /*! - \brief The extremal opacity values. - */ - float _minOpac, _maxOpac; - - /*! - \brief The color map. - */ - const unsigned char* _colors; - - /*! - \brief The binary flag. If the flag is set, the data - will be displayed as a binary image. - */ - bool _binary; - - /*! - \brief The mask flag. If the flag is set, the data - which is in the defined intensity window will be shown - as a binary image. In contrast to the binary flag, - the upper values are set to zero. - */ - bool _mask; - - /*! - \brief The outline flag. If the flag is set, the binary data - will be displayed with lines showing the outline. - */ - bool _outline; - /*! - \brief If the outline flag is set, this attribute controls the line width - */ - float _outlineWidth; -}; - -#endif diff --git a/Utilities/IIL4MITK/texture.cpp b/Utilities/IIL4MITK/texture.cpp deleted file mode 100644 index ed4923408b..0000000000 --- a/Utilities/IIL4MITK/texture.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include "texture.h" - -_iil4mitkTexture::_iil4mitkTexture (iil4mitkWidget* aParent) - : _width (0), _height (0), _model (0), _internal (0), _valid (false), _interpolation (false), _red (1.0), _green (1.0), _blue (1.0), _alpha (1.0),parent(aParent) -{ - assert (parent); - - parent->MakeCurrent (); - glGenTextures (1, &_name); - glBindTexture (GL_TEXTURE_2D, _name); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); -} - -/* -Pay attention that the context which the widget share with another widget -will not be deleted. -*/ -_iil4mitkTexture::~_iil4mitkTexture () { - if(parent!=NULL) - parent->MakeCurrent (); - glDeleteTextures (1, &_name); - - // Are the textures deleted automatically? - -} - -void -_iil4mitkTexture::bind () -{ - glBindTexture (GL_TEXTURE_2D, _name); -} - - -void -_iil4mitkTexture::setSize (const unsigned int width, const unsigned int height) -{ - assert (width > 0); - assert (height > 0); - - if ((width != _width) || (height != _height)) { - _valid = false; - } - _width = width; - _height = height; -} - -unsigned int -_iil4mitkTexture::width () const -{ - return _width; -} - -unsigned int -_iil4mitkTexture::height () const -{ - return _height; -} - -void -_iil4mitkTexture::setModel (int model) -{ - switch (model) { - case INTENSITY: - _model = GL_LUMINANCE; - break; - case INTENSITY_ALPHA: - _model = GL_LUMINANCE_ALPHA; - break; - case COLOR: - _model = GL_RGB; - break; - case COLOR_ALPHA: - _model = GL_RGBA; - break; - case RGB: - _model = GL_RGB; - break; - case RGBA: - _model = GL_RGBA; - break; - } - if (_internal != _model) { - _internal = _model; - _valid = false; - } -} - -void -_iil4mitkTexture::setData (const unsigned char* data) -{ - glTexImage2D (GL_TEXTURE_2D, 0, _internal, _width, _height, 0, _model, GL_UNSIGNED_BYTE, data); - _valid = true; -} - -void -_iil4mitkTexture::setInterpolation (const bool on) { - if (on) { - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } else { - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } - _interpolation = on; -} - -void -_iil4mitkTexture::invalidate () -{ - - _valid = false; -} - -bool -_iil4mitkTexture::isValid () -{ - return _valid; -} diff --git a/Utilities/IIL4MITK/texture.h b/Utilities/IIL4MITK/texture.h deleted file mode 100644 index b1c1b17fe2..0000000000 --- a/Utilities/IIL4MITK/texture.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef _TEXTURE_H_ -#define _TEXTURE_H_ - -#include "widget.h" - -/* -The class holds a texture which is associated -to a particular widget. It is not responsible for -the rendering of images. - -\todo Remove the makeCurrent calls -*/ -class _iil4mitkTexture { - -public: - - /*! - \brief The constructor. - \note Make sure that the desired OpenGL context is selected. - */ - _iil4mitkTexture (iil4mitkWidget* parent); - - /*! - \brief The destructor. - - Frees the texture in the OpenGL context, if it is still - \note Make sure that the desired OpenGL context is selected. - existing. - */ - virtual ~_iil4mitkTexture (); - - /*! - \brief Binds the texture to the OpenGL context of the widget. - \note Make sure that the OpenGL context is made current. - */ - void bind (); - - /*! - \brief Sets the size of the texture. - */ - void setSize (const unsigned int width, const unsigned int height); - - /*! - \brief Gets the width of the texture. - */ - unsigned int width () const; - - /*! - \brief Gets the height of the texture. - */ - unsigned int height () const; - - /*! - \brief The color models of the image. - */ - enum {INTENSITY = 0, INTENSITY_ALPHA, COLOR, COLOR_ALPHA, RGB, RGBA}; - - /*! - \brief Sets the color model of the data. If the color is different from white, - the RGBA color model is used. - @param model the color model which is requested - */ - void setModel (int model); - - /*! - \brief Sets the image data for the texture. If some of the above - parameters have been changed, the method brings the texture - to a valid state. - \note Make sure that the OpenGL context is made current and - the texture has been bound. - \see valid() - */ - void setData (const unsigned char* data); - - /*! - \brief Turns the bilinear interpolation of the texture on/off. - \note Make sure that the OpenGL context is made current and - the texture has been bound. - */ - void setInterpolation (const bool on = true); - - /*! - \brief Makes the texture invalid. - */ - void invalidate (); - - /*! - \brief Checks if the texture is still valid. For example, the texture - gets invalid if its parameters are changed without setting new data. - */ - bool isValid (); - -private: - - /*! - \brief The texture name. - */ - GLuint _name; - - /*! - \brief The size of the texture. - */ - unsigned int _width, _height; - - /*! - \brief The color model of the data. - */ - GLenum _model; - - /*! - \brief The data. - */ - const unsigned char* _data; - - /*! - \brief The internal format of the texture. - */ - GLenum _internal; - - /*! - \brief Flags if the texture is valid. - */ - bool _valid; - - /*! - \brief Flags if the texture will be interpolated. - */ - bool _interpolation; - - /*! - \brief The color which is used for intensity images. - */ - float _red, _green, _blue, _alpha; - - iil4mitkWidget* parent; -}; - -#endif diff --git a/Utilities/IIL4MITK/widget.h b/Utilities/IIL4MITK/widget.h deleted file mode 100644 index 54eb3b62e3..0000000000 --- a/Utilities/IIL4MITK/widget.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef IIL_WIDGET_WRAPPER -#define IIL_WIDGET_WRAPPER - -#include -#include -// #include - -#ifdef WIN32 - #include -#endif - -#ifndef __APPLE__ - #include "GL/gl.h" -#else - #include "OpenGL/gl.h" -#endif - - -class iil4mitkItem; - -typedef vtkRenderWindow iil4mitkWidget; - -#endif