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/MITKDashboardScript.TEMPLATE.cmake b/CMake/MITKDashboardScript.TEMPLATE.cmake index bc4947040c..ddc5cfa971 100644 --- a/CMake/MITKDashboardScript.TEMPLATE.cmake +++ b/CMake/MITKDashboardScript.TEMPLATE.cmake @@ -1,119 +1,120 @@ # # OS: Ubuntu 9.04 2.6.28-18-generic # Hardware: x86_64 GNU/Linux # GPU: NA # # Note: The specific version and processor type of this machine should be reported in the # header above. Indeed, this file will be send to the dashboard as a NOTE file. cmake_minimum_required(VERSION 2.8.4) # # Dashboard properties # set(MY_COMPILER "gcc-4.4.5") # For Windows, e.g. #set(MY_COMPILER "VC9.0") set(CTEST_CMAKE_COMMAND "/usr/bin/cmake") set(CTEST_CMAKE_GENERATOR "Unix Makefiles") set(CTEST_DASHBOARD_ROOT "/opt/dartclients") # For Windows, e.g. #set(CTEST_CMAKE_COMMAND "cmake") #set(CTEST_CMAKE_GENERATOR "Visual Studio 9 2008 Win64") #set(CTEST_DASHBOARD_ROOT "C:/dartclients") # The directory containing the Qt binaries set(QT_BINARY_DIR "/usr/bin/") # For Windows, e.g. #set(QT_BINARY_DIR "V:/windows/x64/QT-4.7.0_VC9.0_Bin/bin") # # Dashboard options # set(WITH_KWSTYLE FALSE) set(WITH_MEMCHECK FALSE) set(WITH_COVERAGE FALSE) set(WITH_DOCUMENTATION FALSE) #set(DOCUMENTATION_ARCHIVES_OUTPUT_DIRECTORY ) # for example: $ENV{HOME}/Projects/Doxygen set(CTEST_BUILD_CONFIGURATION "Release") set(CTEST_TEST_TIMEOUT 500) if(UNIX OR MINGW) set(CTEST_BUILD_FLAGS "-j4") # Use multiple CPU cores to build else() set(CTEST_BUILD_FLAGS "") endif() # experimental: # - run_ctest() macro will be called *ONE* time # - binary directory will *NOT* be cleaned # continuous: # - run_ctest() macro will be called EVERY 5 minutes ... # - binary directory will *NOT* be cleaned # - configure/build will be executed *ONLY* if the repository has been updated # nightly: # - run_ctest() macro will be called *ONE* time # - binary directory *WILL BE* cleaned set(SCRIPT_MODE "experimental") # "experimental", "continuous", "nightly" # # Project specific properties # set(CTEST_SOURCE_DIRECTORY "${CTEST_DASHBOARD_ROOT}/MITK") set(CTEST_BINARY_DIRECTORY "${CTEST_DASHBOARD_ROOT}/MITK-Superbuild-${CTEST_BUILD_CONFIGURATION}-${SCRIPT_MODE}") set(ADDITIONAL_CMAKECACHE_OPTION " # add entries like this # MITK_USE_OpenCV:BOOL=OFF ") # List of test that should be explicitly disabled on this machine set(TEST_TO_EXCLUDE_REGEX "") # set any extra environment variables here set(ENV{DISPLAY} ":0") find_program(CTEST_COVERAGE_COMMAND NAMES gcov) find_program(CTEST_MEMORYCHECK_COMMAND NAMES valgrind) find_program(CTEST_GIT_COMMAND NAMES git) # # Git repository - Overwrite the default value provided by the driver script # # The git repository containing MITK code #set(GIT_REPOSITORY "/home/username/MITK") # The branch of the MITK git repository to check out #set(GIT_BRANCH "bug-xxx-label") ########################################## # WARNING: DO NOT EDIT BEYOND THIS POINT # ########################################## # # Convenient macro allowing to download a file # macro(downloadFile url dest) file(DOWNLOAD "${url}" "${dest}" STATUS status) list(GET status 0 error_code) list(GET status 1 error_msg) if(error_code) message(FATAL_ERROR "error: Failed to download ${url} - ${error_msg}") endif() endmacro() # # Download and include setup script # if(NOT DEFINED GIT_BRANCH OR GIT_BRANCH STREQUAL "") set(hb "HEAD") else() - set(hb "refs/heads/${GIT_BRANCH}") + string(REGEX REPLACE "^origin/(.*)$" "\\1" _branch_name ${GIT_BRANCH}) + set(hb "refs/heads/${_branch_name}") endif() set(url "http://mitk.org/git/?p=MITK.git;a=blob_plain;f=CMake/MITKDashboardSetup.cmake;hb=${hb}") set(dest ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}.setup) downloadFile("${url}" "${dest}") include(${dest}) diff --git a/CMake/MITKDashboardSetup.cmake b/CMake/MITKDashboardSetup.cmake index aa6fe9b405..bf8522e32f 100644 --- a/CMake/MITKDashboardSetup.cmake +++ b/CMake/MITKDashboardSetup.cmake @@ -1,136 +1,131 @@ # This file is intended to be included at the end of a custom MITKDashboardScript.TEMPLATE.cmake file list(APPEND CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") # # Automatically determined properties # set(MY_OPERATING_SYSTEM "${CMAKE_HOST_SYSTEM}") # Windows 7, Linux-2.6.32, Darwin... site_name(CTEST_SITE) if(QT_BINARY_DIR) set(QT_QMAKE_EXECUTABLE "${QT_BINARY_DIR}/qmake") else() set(QT_QMAKE_EXECUTABLE "qmake") endif() execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} --version OUTPUT_VARIABLE MY_QT_VERSION RESULT_VARIABLE qmake_error) if(qmake_error) message(FATAL_ERROR "Error when executing ${QT_QMAKE_EXECUTABLE} --version\n${qmake_error}") endif() string(REGEX REPLACE ".*Qt version ([0-9.]+) .*" "\\1" MY_QT_VERSION ${MY_QT_VERSION}) # # Project specific properties # if(NOT CTEST_BUILD_NAME) set(CTEST_BUILD_NAME "${MY_OPERATING_SYSTEM}-${MY_COMPILER}-Qt-${MY_QT_VERSION}-${CTEST_BUILD_CONFIGURATION}") endif() set(PROJECT_BUILD_DIR "MITK-build") set(CTEST_PATH "$ENV{PATH}") if(WIN32) set(VTK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/VTK-build/bin/${CTEST_BUILD_CONFIGURATION}") set(ITK_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/ITK-build/bin/${CTEST_BUILD_CONFIGURATION}") set(BOOST_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/Boost-src/stage/lib") set(GDCM_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/GDCM-build/bin/${CTEST_BUILD_CONFIGURATION}") set(OPENCV_BINARY_DIR "${CTEST_BINARY_DIRECTORY}/OpenCV-build/bin/${CTEST_BUILD_CONFIGURATION}") set(BLUEBERRY_OSGI_DIR "${CTEST_BINARY_DIRECTORY}/MITK-build/bin/BlueBerry/org.blueberry.osgi/bin/${CTEST_BUILD_CONFIGURATION}") set(CTEST_PATH "${CTEST_PATH};${QT_BINARY_DIR};${VTK_BINARY_DIR};${ITK_BINARY_DIR};${BOOST_BINARY_DIR};${GDCM_BINARY_DIR};${OPENCV_BINARY_DIR};${BLUEBERRY_OSGI_DIR}") endif() set(ENV{PATH} "${CTEST_PATH}") set(SUPERBUILD_TARGETS "") # If the dashscript doesn't define a GIT_REPOSITORY variable, let's define it here. if(NOT DEFINED GIT_REPOSITORY OR GIT_REPOSITORY STREQUAL "") set(GIT_REPOSITORY "http://git.mitk.org/MITK.git") endif() # # Display build info # message("Site name: ${CTEST_SITE}") message("Build name: ${CTEST_BUILD_NAME}") message("Script Mode: ${SCRIPT_MODE}") message("Coverage: ${WITH_COVERAGE}, MemCheck: ${WITH_MEMCHECK}") # # Set initial cache options # if(CMAKE_GENERATOR MATCHES "[Mm]ake") set(CTEST_USE_LAUNCHERS 1) else() set(CTEST_USE_LAUNCHERS 0) endif() # Remove this if block after all dartclients work if(DEFINED ADDITIONNAL_CMAKECACHE_OPTION) message(WARNING "Rename ADDITIONNAL to ADDITIONAL in your dartlclient script: ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") set(ADDITIONAL_CMAKECACHE_OPTION ${ADDITIONNAL_CMAKECACHE_OPTION}) endif() if(NOT DEFINED MITK_USE_Boost) set(MITK_USE_Boost 1) endif() if(NOT DEFINED MITK_USE_OpenCV) set(MITK_USE_OpenCV 1) endif() if(NOT DEFINED MITK_BUILD_ALL_APPS) set(MITK_BUILD_ALL_APPS TRUE) endif() if(NOT DEFINED BLUEBERRY_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS TRUE) endif() if(NOT DEFINED MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS TRUE) endif() if(NOT DEFINED MITK_BUILD_EXAMPLES) set(MITK_BUILD_EXAMPLES TRUE) endif() set(INITIAL_CMAKECACHE_OPTIONS " BLUEBERRY_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS} MITK_BUILD_ALL_PLUGINS:BOOL=${MITK_BUILD_ALL_PLUGINS} MITK_BUILD_ALL_APPS:BOOL=${MITK_BUILD_ALL_APPS} MITK_BUILD_EXAMPLES:BOOL=${MITK_BUILD_EXAMPLES} SUPERBUILD_EXCLUDE_MITKBUILD_TARGET:BOOL=TRUE MITK_USE_Boost:BOOL=${MITK_USE_Boost} MITK_USE_OpenCV:BOOL=${MITK_USE_OpenCV} ${ADDITIONAL_CMAKECACHE_OPTION} ") # Write a cache file for populating the MITK initial cache (not the superbuild cache). # This can be used to provide variables which are not passed through the # superbuild process to the MITK configure step) if(MITK_INITIAL_CACHE) set(mitk_cache_file "${CTEST_SCRIPT_DIRECTORY}/mitk_initial_cache.txt") file(WRITE "${mitk_cache_file}" "${MITK_INITIAL_CACHE}") set(INITIAL_CMAKECACHE_OPTIONS "${INITIAL_CMAKECACHE_OPTIONS} MITK_INITIAL_CACHE_FILE:INTERNAL=${mitk_cache_file} ") endif() # # Download and include dashboard driver script # -if(NOT DEFINED GIT_BRANCH OR GIT_BRANCH STREQUAL "") - set(hb "HEAD") -else() - set(hb "refs/heads/${GIT_BRANCH}") -endif() set(url "http://mitk.org/git/?p=MITK.git;a=blob_plain;f=CMake/MITKDashboardDriverScript.cmake;hb=${hb}") set(dest ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}.driver) downloadFile("${url}" "${dest}") include(${dest}) diff --git a/CMake/NSIS.template.in b/CMake/NSIS.template.in new file mode 100644 index 0000000000..c6637389d2 --- /dev/null +++ b/CMake/NSIS.template.in @@ -0,0 +1,952 @@ +; CPack install script designed for a nmake build + +;-------------------------------- +; You must define these values + + !define VERSION "@CPACK_PACKAGE_VERSION@" + !define PATCH "@CPACK_PACKAGE_VERSION_PATCH@" + !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" + +;-------------------------------- +;Variables + + Var MUI_TEMP + Var STARTMENU_FOLDER + Var SV_ALLUSERS + Var START_MENU + Var DO_NOT_ADD_TO_PATH + Var ADD_TO_PATH_ALL_USERS + Var ADD_TO_PATH_CURRENT_USER + Var INSTALL_DESKTOP + Var IS_DEFAULT_INSTALLDIR +;-------------------------------- +;Include Modern UI + + !include "MUI.nsh" + + ;Default installation folder + InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + ;Allow for very long title on the welcome page + !define MUI_WELCOMEPAGE_TITLE_3LINES + +;-------------------------------- +;General + + ;Name and file + Name "@CPACK_NSIS_PACKAGE_NAME@" + OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" + + ;Set compression + SetCompressor @CPACK_NSIS_COMPRESSOR@ + +@CPACK_NSIS_DEFINES@ + + !include Sections.nsh + +;--- Component support macros: --- +; The code for the add/remove functionality is from: +; http://nsis.sourceforge.net/Add/Remove_Functionality +; It has been modified slightly and extended to provide +; inter-component dependencies. +Var AR_SecFlags +Var AR_RegFlags +@CPACK_NSIS_SECTION_SELECTED_VARS@ + +; Loads the "selected" flag for the section named SecName into the +; variable VarName. +!macro LoadSectionSelectedIntoVar SecName VarName + SectionGetFlags ${${SecName}} $${VarName} + IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits +!macroend + +; Loads the value of a variable... can we get around this? +!macro LoadVar VarName + IntOp $R0 0 + $${VarName} +!macroend + +; Sets the value of a variable +!macro StoreVar VarName IntValue + IntOp $${VarName} 0 + ${IntValue} +!macroend + +!macro InitSection SecName + ; This macro reads component installed flag from the registry and + ;changes checked state of the section on the components page. + ;Input: section index constant name specified in Section command. + + ClearErrors + ;Reading component status from registry + ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" "Installed" + IfErrors "default_${SecName}" + ;Status will stay default if registry value not found + ;(component was never installed) + IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags + IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off + IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit + + ; Note whether this component was installed before + !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags + IntOp $R0 $AR_RegFlags & $AR_RegFlags + + ;Writing modified flags + SectionSetFlags ${${SecName}} $AR_SecFlags + + "default_${SecName}:" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected +!macroend + +!macro FinishSection SecName + ; This macro reads section flag set by user and removes the section + ;if it is not selected. + ;Then it writes component installed flag to registry + ;Input: section index constant name specified in Section command. + + SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags + ;Checking lowest bit: + IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} + IntCmp $AR_SecFlags 1 "leave_${SecName}" + ;Section is not selected: + ;Calling Section uninstall macro and writing zero installed flag + !insertmacro "Remove_${${SecName}}" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \ + "Installed" 0 + Goto "exit_${SecName}" + + "leave_${SecName}:" + ;Section is selected: + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@\Components\${SecName}" \ + "Installed" 1 + + "exit_${SecName}:" +!macroend + +!macro RemoveSection SecName + ; This macro is used to call section's Remove_... macro + ;from the uninstaller. + ;Input: section index constant name specified in Section command. + + !insertmacro "Remove_${${SecName}}" +!macroend + +; Determine whether the selection of SecName changed +!macro MaybeSelectionChanged SecName + !insertmacro LoadVar ${SecName}_selected + SectionGetFlags ${${SecName}} $R1 + IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits + + ; See if the status has changed: + IntCmp $R0 $R1 "${SecName}_unchanged" + !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected + + IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected" + !insertmacro "Deselect_required_by_${SecName}" + goto "${SecName}_unchanged" + + "${SecName}_was_selected:" + !insertmacro "Select_${SecName}_depends" + + "${SecName}_unchanged:" +!macroend +;--- End of Add/Remove macros --- + +;-------------------------------- +;Interface Settings + + !define MUI_HEADERIMAGE + !define MUI_ABORTWARNING + +;-------------------------------- +; path functions + +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 + +;---------------------------------------- +; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" +;---------------------------------------- +!verbose 3 +!include "WinMessages.NSH" +!verbose 4 +;==================================================== +; get_NT_environment +; Returns: the selected environment +; Output : head of the stack +;==================================================== +!macro select_NT_profile UN +Function ${UN}select_NT_profile + StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single + DetailPrint "Selected environment for all users" + Push "all" + Return + environment_single: + DetailPrint "Selected environment for current user only." + Push "current" + Return +FunctionEnd +!macroend +!insertmacro select_NT_profile "" +!insertmacro select_NT_profile "un." +;---------------------------------------------------- +!define NT_current_env 'HKCU "Environment"' +!define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + +!ifndef WriteEnvStr_RegKey + !ifdef ALL_USERS + !define WriteEnvStr_RegKey \ + 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' + !else + !define WriteEnvStr_RegKey 'HKCU "Environment"' + !endif +!endif + +; AddToPath - Adds the given dir to the search path. +; Input - head of the stack +; Note - Win9x systems requires reboot + +Function AddToPath + Exch $0 + Push $1 + Push $2 + Push $3 + + # don't add if the path doesn't exist + IfFileExists "$0\*.*" "" AddToPath_done + + ReadEnvStr $1 PATH + ; if the path is too long for a NSIS variable NSIS will return a 0 + ; length string. If we find that, then warn and skip any path + ; modification as it will trash the existing path. + StrLen $2 $1 + IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done + CheckPathLength_ShowPathWarning: + Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" + Goto AddToPath_done + CheckPathLength_Done: + Push "$1;" + Push "$0;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$0\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + GetFullPathName /SHORT $3 $0 + Push "$1;" + Push "$3;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + Push "$1;" + Push "$3\;" + Call StrStr + Pop $2 + StrCmp $2 "" "" AddToPath_done + + Call IsNT + Pop $1 + StrCmp $1 1 AddToPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" a + FileSeek $1 -1 END + FileReadByte $1 $2 + IntCmp $2 26 0 +2 +2 # DOS EOF + FileSeek $1 -1 END # write over EOF + FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" + FileClose $1 + SetRebootFlag true + Goto AddToPath_done + + AddToPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto DoTrim + ReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + DoTrim: + StrCmp $1 "" AddToPath_NTdoIt + Push $1 + Call Trim + Pop $1 + StrCpy $0 "$1;$0" + AddToPath_NTdoIt: + StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $0 + Goto DoSend + WriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $0 + DoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + AddToPath_done: + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; RemoveFromPath - Remove a given dir from the path +; Input: head of the stack + +Function un.RemoveFromPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + + IntFmt $6 "%c" 26 # DOS EOF + + Call un.IsNT + Pop $1 + StrCmp $1 1 unRemoveFromPath_NT + ; Not on NT + StrCpy $1 $WINDIR 2 + FileOpen $1 "$1\autoexec.bat" r + GetTempFileName $4 + FileOpen $2 $4 w + GetFullPathName /SHORT $0 $0 + StrCpy $0 "SET PATH=%PATH%;$0" + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoop: + FileRead $1 $3 + StrCpy $5 $3 1 -1 # read last char + StrCmp $5 $6 0 +2 # if DOS EOF + StrCpy $3 $3 -1 # remove DOS EOF so we can compare + StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine + StrCmp $3 "" unRemoveFromPath_dosLoopEnd + FileWrite $2 $3 + Goto unRemoveFromPath_dosLoop + unRemoveFromPath_dosLoopRemoveLine: + SetRebootFlag true + Goto unRemoveFromPath_dosLoop + + unRemoveFromPath_dosLoopEnd: + FileClose $2 + FileClose $1 + StrCpy $1 $WINDIR 2 + Delete "$1\autoexec.bat" + CopyFiles /SILENT $4 "$1\autoexec.bat" + Delete $4 + Goto unRemoveFromPath_done + + unRemoveFromPath_NT: + StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey + ReadRegStr $1 ${NT_current_env} "PATH" + Goto unDoTrim + unReadAllKey: + ReadRegStr $1 ${NT_all_env} "PATH" + unDoTrim: + StrCpy $5 $1 1 -1 # copy last char + StrCmp $5 ";" +2 # if last char != ; + StrCpy $1 "$1;" # append ; + Push $1 + Push "$0;" + Call un.StrStr ; Find `$0;` in $1 + Pop $2 ; pos of our dir + StrCmp $2 "" unRemoveFromPath_done + ; else, it is in path + # $0 - path to add + # $1 - path var + StrLen $3 "$0;" + StrLen $4 $2 + StrCpy $5 $1 -$4 # $5 is now the part before the path to remove + StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove + StrCpy $3 $5$6 + + StrCpy $5 $3 1 -1 # copy last char + StrCmp $5 ";" 0 +2 # if last char == ; + StrCpy $3 $3 -1 # remove last char + + StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey + WriteRegExpandStr ${NT_current_env} "PATH" $3 + Goto unDoSend + unWriteAllKey: + WriteRegExpandStr ${NT_all_env} "PATH" $3 + unDoSend: + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + unRemoveFromPath_done: + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Uninstall sutff +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +########################################### +# Utility Functions # +########################################### + +;==================================================== +; IsNT - Returns 1 if the current system is NT, 0 +; otherwise. +; Output: head of the stack +;==================================================== +; IsNT +; no input +; output, top of the stack = 1 if NT or 0 if not +; +; Usage: +; Call IsNT +; Pop $R0 +; ($R0 at this point is 1 or 0) + +!macro IsNT un +Function ${un}IsNT + Push $0 + ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + StrCmp $0 "" 0 IsNT_yes + ; we are not NT. + Pop $0 + Push 0 + Return + + IsNT_yes: + ; NT!!! + Pop $0 + Push 1 +FunctionEnd +!macroend +!insertmacro IsNT "" +!insertmacro IsNT "un." + +; StrStr +; input, top of stack = string to search for +; top of stack-1 = string to search in +; output, top of stack (replaces with the portion of the string remaining) +; modifies no other variables. +; +; Usage: +; Push "this is a long ass string" +; Push "ass" +; Call StrStr +; Pop $R0 +; ($R0 at this point is "ass string") + +!macro StrStr un +Function ${un}StrStr +Exch $R1 ; st=haystack,old$R1, $R1=needle + Exch ; st=old$R1,haystack + Exch $R2 ; st=old$R1,old$R2, $R2=haystack + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=needle + ; $R2=haystack + ; $R3=len(needle) + ; $R4=cnt + ; $R5=tmp + loop: + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 done + StrCmp $R5 "" done + IntOp $R4 $R4 + 1 + Goto loop +done: + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 +FunctionEnd +!macroend +!insertmacro StrStr "" +!insertmacro StrStr "un." + +Function Trim ; Added by Pelaca + Exch $R1 + Push $R2 +Loop: + StrCpy $R2 "$R1" 1 -1 + StrCmp "$R2" " " RTrim + StrCmp "$R2" "$\n" RTrim + StrCmp "$R2" "$\r" RTrim + StrCmp "$R2" ";" RTrim + GoTo Done +RTrim: + StrCpy $R1 "$R1" -1 + Goto Loop +Done: + Pop $R2 + Exch $R1 +FunctionEnd + +Function ConditionalAddToRegisty + Pop $0 + Pop $1 + StrCmp "$0" "" ConditionalAddToRegisty_EmptyString + WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" \ + "$1" "$0" + ;MessageBox MB_OK "Set Registry: '$1' to '$0'" + DetailPrint "Set install registry entry: '$1' to '$0'" + ConditionalAddToRegisty_EmptyString: +FunctionEnd + +;-------------------------------- + +!ifdef CPACK_USES_DOWNLOAD +Function DownloadFile + IfFileExists $INSTDIR\* +2 + CreateDirectory $INSTDIR + Pop $0 + + ; Skip if already downloaded + IfFileExists $INSTDIR\$0 0 +2 + Return + + StrCpy $1 "@CPACK_DOWNLOAD_SITE@" + + try_again: + NSISdl::download "$1/$0" "$INSTDIR\$0" + + Pop $1 + StrCmp $1 "success" success + StrCmp $1 "Cancelled" cancel + MessageBox MB_OK "Download failed: $1" + cancel: + Return + success: +FunctionEnd +!endif + +;-------------------------------- +; Installation types +@CPACK_NSIS_INSTALLATION_TYPES@ + +;-------------------------------- +; Component sections +@CPACK_NSIS_COMPONENT_SECTIONS@ + +;-------------------------------- +; Define some macro setting for the gui +@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ +@CPACK_NSIS_INSTALLER_ICON_CODE@ +@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@ +@CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE@ + +;-------------------------------- +;Pages + !insertmacro MUI_PAGE_WELCOME + + !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" + Page custom InstallOptionsPage + !insertmacro MUI_PAGE_DIRECTORY + + ;Start Menu Folder Page Configuration + !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" + !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" + !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER + + @CPACK_NSIS_PAGE_COMPONENTS@ + + !insertmacro MUI_PAGE_INSTFILES + !insertmacro MUI_PAGE_FINISH + + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" ;first language is the default language + !insertmacro MUI_LANGUAGE "Albanian" + !insertmacro MUI_LANGUAGE "Arabic" + !insertmacro MUI_LANGUAGE "Basque" + !insertmacro MUI_LANGUAGE "Belarusian" + !insertmacro MUI_LANGUAGE "Bosnian" + !insertmacro MUI_LANGUAGE "Breton" + !insertmacro MUI_LANGUAGE "Bulgarian" + !insertmacro MUI_LANGUAGE "Croatian" + !insertmacro MUI_LANGUAGE "Czech" + !insertmacro MUI_LANGUAGE "Danish" + !insertmacro MUI_LANGUAGE "Dutch" + !insertmacro MUI_LANGUAGE "Estonian" + !insertmacro MUI_LANGUAGE "Farsi" + !insertmacro MUI_LANGUAGE "Finnish" + !insertmacro MUI_LANGUAGE "French" + !insertmacro MUI_LANGUAGE "German" + !insertmacro MUI_LANGUAGE "Greek" + !insertmacro MUI_LANGUAGE "Hebrew" + !insertmacro MUI_LANGUAGE "Hungarian" + !insertmacro MUI_LANGUAGE "Icelandic" + !insertmacro MUI_LANGUAGE "Indonesian" + !insertmacro MUI_LANGUAGE "Irish" + !insertmacro MUI_LANGUAGE "Italian" + !insertmacro MUI_LANGUAGE "Japanese" + !insertmacro MUI_LANGUAGE "Korean" + !insertmacro MUI_LANGUAGE "Kurdish" + !insertmacro MUI_LANGUAGE "Latvian" + !insertmacro MUI_LANGUAGE "Lithuanian" + !insertmacro MUI_LANGUAGE "Luxembourgish" + !insertmacro MUI_LANGUAGE "Macedonian" + !insertmacro MUI_LANGUAGE "Malay" + !insertmacro MUI_LANGUAGE "Mongolian" + !insertmacro MUI_LANGUAGE "Norwegian" + !insertmacro MUI_LANGUAGE "Polish" + !insertmacro MUI_LANGUAGE "Portuguese" + !insertmacro MUI_LANGUAGE "PortugueseBR" + !insertmacro MUI_LANGUAGE "Romanian" + !insertmacro MUI_LANGUAGE "Russian" + !insertmacro MUI_LANGUAGE "Serbian" + !insertmacro MUI_LANGUAGE "SerbianLatin" + !insertmacro MUI_LANGUAGE "SimpChinese" + !insertmacro MUI_LANGUAGE "Slovak" + !insertmacro MUI_LANGUAGE "Slovenian" + !insertmacro MUI_LANGUAGE "Spanish" + !insertmacro MUI_LANGUAGE "Swedish" + !insertmacro MUI_LANGUAGE "Thai" + !insertmacro MUI_LANGUAGE "TradChinese" + !insertmacro MUI_LANGUAGE "Turkish" + !insertmacro MUI_LANGUAGE "Ukrainian" + !insertmacro MUI_LANGUAGE "Welsh" + + +;-------------------------------- +;Reserve Files + + ;These files should be inserted before other files in the data block + ;Keep these lines before any File command + ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) + + ReserveFile "NSIS.InstallOptions.ini" + !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS + +;-------------------------------- +;Installer Sections + +Section "-Core installation" + ;Use the entire tree produced by the INSTALL target. Keep the + ;list of directories here in sync with the RMDir commands below. + SetOutPath "$INSTDIR" + @CPACK_NSIS_FULL_INSTALL@ + + ;Store installation folder + WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR + + ;Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" + Push "DisplayName" + Push "@CPACK_NSIS_DISPLAY_NAME@" + Call ConditionalAddToRegisty + Push "DisplayVersion" + Push "@CPACK_PACKAGE_VERSION@" + Call ConditionalAddToRegisty + Push "Publisher" + Push "@CPACK_PACKAGE_VENDOR@" + Call ConditionalAddToRegisty + Push "UninstallString" + Push "$INSTDIR\Uninstall.exe" + Call ConditionalAddToRegisty + Push "NoRepair" + Push "1" + Call ConditionalAddToRegisty + + !ifdef CPACK_NSIS_ADD_REMOVE + ;Create add/remove functionality + Push "ModifyPath" + Push "$INSTDIR\AddRemove.exe" + Call ConditionalAddToRegisty + !else + Push "NoModify" + Push "1" + Call ConditionalAddToRegisty + !endif + + ; Optional registration + Push "DisplayIcon" + Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@" + Call ConditionalAddToRegisty + Push "HelpLink" + Push "@CPACK_NSIS_HELP_LINK@" + Call ConditionalAddToRegisty + Push "URLInfoAbout" + Push "@CPACK_NSIS_URL_INFO_ABOUT@" + Call ConditionalAddToRegisty + Push "Contact" + Push "@CPACK_NSIS_CONTACT@" + Call ConditionalAddToRegisty + !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + + ;Create shortcuts + CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" +@CPACK_NSIS_CREATE_ICONS@ +@CPACK_NSIS_CREATE_ICONS_EXTRA@ + CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + + ;Read a value from an InstallOptions INI file + !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" + !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" + + ; Write special uninstall registry entries + Push "StartMenu" + Push "$STARTMENU_FOLDER" + Call ConditionalAddToRegisty + Push "DoNotAddToPath" + Push "$DO_NOT_ADD_TO_PATH" + Call ConditionalAddToRegisty + Push "AddToPathAllUsers" + Push "$ADD_TO_PATH_ALL_USERS" + Call ConditionalAddToRegisty + Push "AddToPathCurrentUser" + Push "$ADD_TO_PATH_CURRENT_USER" + Call ConditionalAddToRegisty + Push "InstallToDesktop" + Push "$INSTALL_DESKTOP" + Call ConditionalAddToRegisty + + !insertmacro MUI_STARTMENU_WRITE_END + +@CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ + +SectionEnd + +Section "-Add to path" + Push $INSTDIR\bin + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath + StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 + Call AddToPath + doNotAddToPath: +SectionEnd + +;-------------------------------- +; Create custom pages +Function InstallOptionsPage + !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@" + !insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" + +FunctionEnd + +;-------------------------------- +; determine admin versus local install +Function un.onInit + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + Goto done + + noLM: + ;Get installation folder from registry if available + + done: + +FunctionEnd + +;--- Add/Remove callback functions: --- +!macro SectionList MacroName + ;This macro used to perform operation on multiple sections. + ;List all of your components in following manner here. +@CPACK_NSIS_COMPONENT_SECTION_LIST@ +!macroend + +Section -FinishComponents + ;Removes unselected components and writes component status to registry + !insertmacro SectionList "FinishSection" + +!ifdef CPACK_NSIS_ADD_REMOVE + ; Get the name of the installer executable + System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' + StrCpy $R3 $R0 + + ; Strip off the last 13 characters, to see if we have AddRemove.exe + StrLen $R1 $R0 + IntOp $R1 $R0 - 13 + StrCpy $R2 $R0 13 $R1 + StrCmp $R2 "AddRemove.exe" addremove_installed + + ; We're not running AddRemove.exe, so install it + CopyFiles $R3 $INSTDIR\AddRemove.exe + + addremove_installed: +!endif +SectionEnd +;--- End of Add/Remove callback functions --- + +;-------------------------------- +; Component dependencies +Function .onSelChange + !insertmacro SectionList MaybeSelectionChanged +FunctionEnd + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + ReadRegStr $START_MENU SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "StartMenu" + ;MessageBox MB_OK "Start menu is in: $START_MENU" + ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "DoNotAddToPath" + ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathAllUsers" + ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "AddToPathCurrentUser" + ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" + ReadRegStr $INSTALL_DESKTOP SHCTX \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" "InstallToDesktop" + ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " + +@CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ + + ;Remove files we installed. + ;Keep the list of directories here in sync with the File commands above. +@CPACK_NSIS_DELETE_FILES@ +@CPACK_NSIS_DELETE_DIRECTORIES@ + +!ifdef CPACK_NSIS_ADD_REMOVE + ;Remove the add/remove program + Delete "$INSTDIR\AddRemove.exe" +!endif + + ;Remove the uninstaller itself. + Delete "$INSTDIR\Uninstall.exe" + DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_NAME@" + + ;Remove the installation directory if it is empty. + RMDir "$INSTDIR" + + ; Remove the registry entries. + DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + ; Removes all optional components + !insertmacro SectionList "RemoveSection" + + !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP + + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" +@CPACK_NSIS_DELETE_ICONS@ +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + startMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors startMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop + startMenuDeleteLoopDone: + + ; If the user changed the shortcut, then untinstall may not work. This should + ; try to fix it. + StrCpy $MUI_TEMP "$START_MENU" + Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" +@CPACK_NSIS_DELETE_ICONS_EXTRA@ + + ;Delete empty start menu parent diretories + StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" + + secondStartMenuDeleteLoop: + ClearErrors + RMDir $MUI_TEMP + GetFullPathName $MUI_TEMP "$MUI_TEMP\.." + + IfErrors secondStartMenuDeleteLoopDone + + StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop + secondStartMenuDeleteLoopDone: + + DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" + + Push $INSTDIR\bin + StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 + Call un.RemoveFromPath + doNotRemoveFromPath: +SectionEnd + +;-------------------------------- +; determine admin versus local install +; Is install for "AllUsers" or "JustMe"? +; Default to "JustMe" - set to "AllUsers" if admin or on Win9x +; This function is used for the very first "custom page" of the installer. +; This custom page does not show up visibly, but it executes prior to the +; first visible page and sets up $INSTDIR properly... +; Choose different default installation folder based on SV_ALLUSERS... +; "Program Files" for AllUsers, "My Documents" for JustMe... + +Function .onInit + ; Reads components status for registry + !insertmacro SectionList "InitSection" + + ; check to see if /D has been used to change + ; the install directory by comparing it to the + ; install directory that is expected to be the + ; default + StrCpy $IS_DEFAULT_INSTALLDIR 0 + StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 + StrCpy $IS_DEFAULT_INSTALLDIR 1 + + StrCpy $SV_ALLUSERS "JustMe" + ; if default install dir then change the default + ; if it is installed for JustMe + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + ClearErrors + UserInfo::GetName + IfErrors noLM + Pop $0 + UserInfo::GetAccountType + Pop $1 + StrCmp $1 "Admin" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Admin group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + StrCmp $1 "Power" 0 +3 + SetShellVarContext all + ;MessageBox MB_OK 'User "$0" is in the Power Users group' + StrCpy $SV_ALLUSERS "AllUsers" + Goto done + + noLM: + StrCpy $SV_ALLUSERS "AllUsers" + ;Get installation folder from registry if available + + done: + StrCmp $SV_ALLUSERS "AllUsers" 0 +3 + StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 + StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" + + StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage + !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" + + noOptionsPage: +FunctionEnd diff --git a/CMake/mitkSetupCPack.cmake b/CMake/mitkSetupCPack.cmake index 5c609f4820..d39eec1ecd 100644 --- a/CMake/mitkSetupCPack.cmake +++ b/CMake/mitkSetupCPack.cmake @@ -1,57 +1,67 @@ # # First, set the generator variable # if(NOT CPACK_GENERATOR) if(WIN32) find_program(NSIS_MAKENSIS NAMES makensis PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS] DOC "Where is makensis.exe located" ) if(NOT NSIS_MAKENSIS) set(CPACK_GENERATOR ZIP) else() set(CPACK_GENERATOR "NSIS;ZIP") endif(NOT NSIS_MAKENSIS) else() if(APPLE) set(CPACK_GENERATOR DragNDrop) else() set(CPACK_GENERATOR TGZ) endif() endif() endif(NOT CPACK_GENERATOR) +# On windows set default install directory appropriately for 32 and 64 bit +# installers if not already set +if(WIN32 AND NOT CPACK_NSIS_INSTALL_ROOT) + if(CMAKE_CL_64) + set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64") + else() + set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") + endif() +endif() + # include required mfc libraries include(InstallRequiredSystemLibraries) set(CPACK_PACKAGE_NAME "MITK") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MITK is a medical image processing tool") set(CPACK_PACKAGE_VENDOR "German Cancer Research Center (DKFZ)") set(CPACK_PACKAGE_DESCRIPTION_FILE "${MITK_SOURCE_DIR}/LICENSE.txt") set(CPACK_RESOURCE_FILE_LICENSE "${MITK_SOURCE_DIR}/LICENSE.txt") set(CPACK_PACKAGE_VERSION_MAJOR "${MITK_VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${MITK_VERSION_MINOR}") # tell cpack to strip all debug symbols from all files set(CPACK_STRIP_FILES ON) # append revision number if available -if(MITK_REVISION_ID) +if(MITK_REVISION_ID AND MITK_VERSION_PATCH STREQUAL "99") if(MITK_WC_TYPE STREQUAL "git") set(git_hash ${MITK_REVISION_ID}) string(LENGTH "${git_hash}" hash_length) if(hash_length GREATER 6) string(SUBSTRING ${git_hash} 0 6 git_hash) endif() set(CPACK_PACKAGE_VERSION_PATCH "${MITK_VERSION_PATCH}_r${git_hash}") else() set(CPACK_PACKAGE_VERSION_PATCH "${MITK_VERSION_PATCH}_r${MITK_REVISION_ID}") endif() else() set(CPACK_PACKAGE_VERSION_PATCH "${MITK_VERSION_PATCH}") endif() diff --git a/CMake/mitkSetupVariables.cmake b/CMake/mitkSetupVariables.cmake index 25a9a3e5aa..e8106ecc22 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 "02") +set(MITK_VERSION_MINOR "06") set(MITK_VERSION_PATCH "99") 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/CTK.cmake b/CMakeExternals/CTK.cmake index 2c4faee1de..859431b560 100644 --- a/CMakeExternals/CTK.cmake +++ b/CMakeExternals/CTK.cmake @@ -1,79 +1,79 @@ #----------------------------------------------------------------------------- # CTK #----------------------------------------------------------------------------- if(MITK_USE_CTK) # Sanity checks if(DEFINED CTK_DIR AND NOT EXISTS ${CTK_DIR}) message(FATAL_ERROR "CTK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj CTK) set(proj_DEPENDENCIES ) set(CTK_DEPENDS ${proj}) if(NOT DEFINED CTK_DIR) - set(revision_tag 70c0a8d3) + set(revision_tag 96bb84d8) #IF(${proj}_REVISION_TAG) # SET(revision_tag ${${proj}_REVISION_TAG}) #ENDIF() set(ctk_optional_cache_args ) if(MITK_USE_Python) list(APPEND ctk_optional_cache_args -DCTK_LIB_Scripting/Python/Widgets:BOOL=ON ) endif() if(MITK_USE_DCMTK) list(APPEND ctk_optional_cache_args -DDCMTK_DIR:PATH=${DCMTK_DIR} ) list(APPEND proj_DEPENDENCIES DCMTK) else() list(APPEND ctk_optional_cache_args -DDCMTK_URL:STRING=${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/CTK_DCMTK_085525e6.tar.gz ) endif() FOREACH(type RUNTIME ARCHIVE LIBRARY) IF(DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY) LIST(APPEND mitk_optional_cache_args -DCTK_PLUGIN_${type}_OUTPUT_DIRECTORY:PATH=${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}) ENDIF() ENDFOREACH() ExternalProject_Add(${proj} SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}-src BINARY_DIR ${proj}-build PREFIX ${proj}-cmake URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/CTK_${revision_tag}.tar.gz - URL_MD5 a3130b2c3e7a1d320740938f61b65840 + URL_MD5 e214b632dc876592923d5d8ca77296d5 UPDATE_COMMAND "" INSTALL_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} ${ctk_optional_cache_args} -DDESIRED_QT_VERSION:STRING=4 -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} -DGit_EXECUTABLE:FILEPATH=${GIT_EXECUTABLE} -DGIT_EXECUTABLE:FILEPATH=${GIT_EXECUTABLE} -DCTK_LIB_PluginFramework:BOOL=ON -DCTK_LIB_DICOM/Widgets:BOOL=ON -DCTK_PLUGIN_org.commontk.eventadmin:BOOL=ON -DCTK_PLUGIN_org.commontk.configadmin:BOOL=ON -DCTK_USE_GIT_PROTOCOL:BOOL=OFF -DDCMTK_URL:STRING=${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/CTK_DCMTK_085525e6.tar.gz DEPENDS ${proj_DEPENDENCIES} ) set(CTK_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}-build) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/MITKData.cmake b/CMakeExternals/MITKData.cmake index 8c60c69ea2..3363e4c247 100644 --- a/CMakeExternals/MITKData.cmake +++ b/CMakeExternals/MITKData.cmake @@ -1,39 +1,38 @@ #----------------------------------------------------------------------------- # MITK Data #----------------------------------------------------------------------------- # Sanity checks if(DEFINED MITK_DATA_DIR AND NOT EXISTS ${MITK_DATA_DIR}) message(FATAL_ERROR "MITK_DATA_DIR variable is defined but corresponds to non-existing directory") endif() set(proj MITK-Data) set(proj_DEPENDENCIES) set(MITK-Data_DEPENDS ${proj}) if(BUILD_TESTING) - set(revision_tag 578a7578) + set(revision_tag ae9267c8) #if(${proj}_REVISION_TAG) # set(revision_tag ${${proj}_REVISION_TAG}) #endif() ExternalProject_Add(${proj} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MITK-Data_${revision_tag}.tar.gz - URL_MD5 c7e6573a559d70363231c22ba7eabcb8 UPDATE_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${proj_DEPENDENCIES} ) - + set(MITK_DATA_DIR ${ep_source_dir}/${proj}) - + else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") - + endif(BUILD_TESTING) - + 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/CMakeLists.txt b/CMakeLists.txt index f0e600cf65..2935bae42d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,906 +1,909 @@ if(APPLE) # With XCode 4.3, the SDK location changed. Older CMake # versions are not able to find it. cmake_minimum_required(VERSION 2.8.8) else() cmake_minimum_required(VERSION 2.8.4) endif() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK) endif() #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 43) # _src_dir_length_max - strlen(ITK-src) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # See http://cmake.org/cmake/help/cmake-2-8-docs.html#section_Policies for details #----------------------------------------------------------------------------- set(project_policies CMP0001 # NEW: CMAKE_BACKWARDS_COMPATIBILITY should no longer be used. CMP0002 # NEW: Logical target names must be globally unique. CMP0003 # NEW: Libraries linked via full path no longer produce linker search paths. CMP0004 # NEW: Libraries linked may NOT have leading or trailing whitespace. CMP0005 # NEW: Preprocessor definition values are now escaped automatically. CMP0006 # NEW: Installing MACOSX_BUNDLE targets requires a BUNDLE DESTINATION. CMP0007 # NEW: List command no longer ignores empty elements. CMP0008 # NEW: Libraries linked by full-path must have a valid library file name. CMP0009 # NEW: FILE GLOB_RECURSE calls should not follow symlinks by default. CMP0010 # NEW: Bad variable reference syntax is an error. CMP0011 # NEW: Included scripts do automatic cmake_policy PUSH and POP. CMP0012 # NEW: if() recognizes numbers and boolean constants. CMP0013 # NEW: Duplicate binary directories are not allowed. CMP0014 # NEW: Input directories must have CMakeLists.txt ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # Update CMake module path #------------------------------------------------------------------------------ set(CMAKE_MODULE_PATH ${MITK_SOURCE_DIR}/CMake ${CMAKE_MODULE_PATH} ) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(mitkMacroEmptyExternalProject) include(mitkFunctionGenerateProjectXml) include(mitkFunctionSuppressWarnings) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_USE_SUPERBUILD) set(output_dir ${MITK_BINARY_DIR}/bin) if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_BINARY_DIR}/MITK-build/bin) endif() else() if(NOT DEFINED MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(output_dir ${MITK_BINARY_DIR}/bin) else() set(output_dir ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) endif() endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${output_dir} CACHE INTERNAL "Single output directory for building all libraries.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) set(MITK_BUILD_TUTORIAL OFF CACHE INTERNAL "Deprecated! Use MITK_BUILD_EXAMPLES instead!") option(MITK_BUILD_EXAMPLES "Build the MITK Examples" ${MITK_BUILD_TUTORIAL}) option(MITK_USE_Boost "Use the Boost C++ library" OFF) option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_CTK "Use CTK in MITK" ${MITK_USE_BLUEBERRY}) option(MITK_USE_QT "Use Nokia's Qt library" ${MITK_USE_CTK}) option(MITK_USE_DCMTK "EXEPERIMENTAL, superbuild only: Use DCMTK in MITK" ${MITK_USE_CTK}) option(MITK_USE_OpenCV "Use Intel's OpenCV library" OFF) option(MITK_USE_Python "Use Python wrapping in MITK" OFF) set(MITK_USE_CableSwig ${MITK_USE_Python}) mark_as_advanced(MITK_BUILD_ALL_APPS MITK_USE_CTK MITK_USE_DCMTK ) if(MITK_USE_Boost) option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() if(MITK_USE_CTK) if(NOT MITK_USE_QT) message("Forcing MITK_USE_QT to ON because of MITK_USE_CTK") set(MITK_USE_QT ON CACHE BOOL "Use Nokia's Qt library in MITK" FORCE) endif() if(NOT MITK_USE_DCMTK) message("Setting MITK_USE_DCMTK to ON because DCMTK needs to be build for CTK") set(MITK_USE_DCMTK ON CACHE BOOL "Use DCMTK in MITK" FORCE) endif() endif() if(MITK_USE_QT) # find the package at the very beginning, so that QT4_FOUND is available find_package(Qt4 4.6.2 REQUIRED) endif() # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() #----------------------------------------------------------------------------- # Additional CXX/C Flags #----------------------------------------------------------------------------- set(ADDITIONAL_C_FLAGS "" CACHE STRING "Additional C Flags") mark_as_advanced(ADDITIONAL_C_FLAGS) set(ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX Flags") mark_as_advanced(ADDITIONAL_CXX_FLAGS) #----------------------------------------------------------------------------- # Project.xml #----------------------------------------------------------------------------- # A list of topologically ordered targets set(CTEST_PROJECT_SUBPROJECTS) if(MITK_USE_BLUEBERRY) list(APPEND CTEST_PROJECT_SUBPROJECTS BlueBerry) endif() list(APPEND CTEST_PROJECT_SUBPROJECTS MITK-Core MITK-CoreUI MITK-IGT MITK-ToF MITK-DTI MITK-Registration MITK-Modules # all modules not contained in a specific subproject MITK-Plugins # all plugins not contained in a specific subproject MITK-Examples Unlabeled # special "subproject" catching all unlabeled targets and tests ) # Configure CTestConfigSubProject.cmake that could be used by CTest scripts configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in ${MITK_BINARY_DIR}/CTestConfigSubProject.cmake) if(CTEST_PROJECT_ADDITIONAL_TARGETS) # those targets will be executed at the end of the ctest driver script # and they also get their own subproject label set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}") else() set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}") endif() # Generate Project.xml file expected by the CTest driver script mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD}) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(CheckCXXSourceCompiles) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionGetGccVersion) include(MacroParseArguments) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkFunctionOrganizeSources) include(mitkFunctionGetVersion) include(mitkFunctionCreateWindowsBatchScript) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionCompileSnippets) include(mitkMacroCreateModuleConf) include(mitkMacroCreateModule) include(mitkMacroCheckModule) include(mitkMacroCreateModuleTests) include(mitkFunctionAddCustomModuleTest) include(mitkMacroUseModule) include(mitkMacroMultiplexPicType) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) #----------------------------------------------------------------------------- # Prerequesites #----------------------------------------------------------------------------- find_package(ITK REQUIRED) find_package(VTK REQUIRED) if(ITK_USE_SYSTEM_GDCM) find_package(GDCM PATHS ${ITK_GDCM_DIR} REQUIRED) endif() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # ASK THE USER TO SHOW THE CONSOLE WINDOW FOR CoreApp and ExtApp option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(DEFINED MITK_CTEST_SCRIPT_MODE AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") ) set(MITK_FAST_TESTING 1) endif() endif() #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on Mac OSX all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name}) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} ${app_name}) endif() endforeach() endif() #----------------------------------------------------------------------------- # Set symbol visibility Flags #----------------------------------------------------------------------------- # MinGW does not export all symbols automatically, so no need to set flags if(CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW) set(VISIBILITY_CXX_FLAGS ) #"-fvisibility=hidden -fvisibility-inlines-hidden") endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS} ${ADDITIONAL_C_FLAGS}") set(MITK_CXX_FLAGS "${VISIBILITY_CXX_FLAGS} ${COVERAGE_CXX_FLAGS} ${ADDITIONAL_CXX_FLAGS}") include(mitkSetupC++0xVariables) set(cflags ) if(WIN32) set(cflags "${cflags} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN") endif() if(CMAKE_COMPILER_IS_GNUCXX) - set(cflags "${cflags} -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -D_FORTIFY_SOURCE=2") + set(cflags "${cflags} -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings") mitkFunctionCheckCompilerFlags("-fdiagnostics-show-option" cflags) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" cflags) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" cflags) if(MITK_USE_C++0x) mitkFunctionCheckCompilerFlags("-std=c++0x" MITK_CXX_FLAGS) endif() mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) # With older version of gcc supporting the flag -fstack-protector-all, an extra dependency to libssp.so # is introduced. If gcc is smaller than 4.4.0 and the build type is Release let's not include the flag. # Doing so should allow to build package made for distribution using older linux distro. if(${GCC_VERSION} VERSION_GREATER "4.4.0" OR (CMAKE_BUILD_TYPE STREQUAL "Debug" AND ${GCC_VERSION} VERSION_LESS "4.4.0")) mitkFunctionCheckCompilerFlags("-fstack-protector-all" cflags) endif() if(MINGW) # suppress warnings about auto imported symbols set(MITK_CXX_FLAGS "-Wl,--enable-auto-import ${MITK_CXX_FLAGS}") # we need to define a Windows version set(MITK_CXX_FLAGS "-D_WIN32_WINNT=0x0500 ${MITK_CXX_FLAGS}") endif() #set(MITK_CXX_FLAGS "-Woverloaded-virtual -Wold-style-cast -Wstrict-null-sentinel -Wsign-promo ${MITK_CXX_FLAGS}") set(MITK_CXX_FLAGS "-Woverloaded-virtual -Wstrict-null-sentinel ${MITK_CXX_FLAGS}") + set(MITK_CXX_FLAGS_RELEASE "-D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") + endif() set(MITK_C_FLAGS "${cflags} ${MITK_C_FLAGS}") set(MITK_CXX_FLAGS "${cflags} ${MITK_CXX_FLAGS}") #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) enable_testing() include(CTest) mark_as_advanced(TCL_TCLSH DART_ROOT) option(MITK_ENABLE_GUI_TESTING OFF "Enable the MITK GUI tests") # Setup file for setting custom ctest vars configure_file( CMake/CTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch( std::exception & excp ) { fprintf(stderr,\"%s\\n\",excp.what()); return EXIT_FAILURE; } catch( ... ) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; } ") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the external project template if(MITK_USE_BLUEBERRY) include(mitkTestProjectTemplate) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Compile Utilities and set-up MITK variables #----------------------------------------------------------------------------- include(mitkSetupVariables) #----------------------------------------------------------------------------- # Cleanup #----------------------------------------------------------------------------- file(GLOB _MODULES_CONF_FILES ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) if(_MODULES_CONF_FILES) file(REMOVE ${_MODULES_CONF_FILES}) endif() add_subdirectory(Utilities) if(MITK_USE_BLUEBERRY) # We need to hack a little bit because MITK applications may need # to enable certain BlueBerry plug-ins. However, these plug-ins # are validated separately from the MITK plug-ins and know nothing # about potential MITK plug-in dependencies of the applications. Hence # we cannot pass the MITK application list to the BlueBerry # ctkMacroSetupPlugins call but need to extract the BlueBerry dependencies # from the applications and set them explicitly. include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled and if target_libraries.cmake exists if(${option_name} AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/target_libraries.cmake") foreach(_target_dep ${target_libraries}) if(_target_dep MATCHES org_blueberry_) string(REPLACE _ . _app_bb_dep ${_target_dep}) # explicitly set the build option for the BlueBerry plug-in set(BLUEBERRY_BUILD_${_app_bb_dep} ON CACHE BOOL "Build the ${_app_bb_dep} plug-in") endif() endforeach() endif() endforeach() set(mbilog_DIR "${mbilog_BINARY_DIR}") if(MITK_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS ON) endif() add_subdirectory(BlueBerry) set(BlueBerry_DIR ${CMAKE_CURRENT_BINARY_DIR}/BlueBerry CACHE PATH "The directory containing a CMake configuration file for BlueBerry" FORCE) include(mitkMacroCreateCTKPlugin) endif() #----------------------------------------------------------------------------- # Set C/CXX Flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") if(MITK_USE_QT) add_definitions(-DQWT_DLL) endif() #----------------------------------------------------------------------------- # Add custom targets representing CDash subprojects #----------------------------------------------------------------------------- foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled") add_custom_target(${subproject}) endif() endforeach() #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- link_directories(${MITK_LINK_DIRECTORIES}) add_subdirectory(Core) add_subdirectory(Modules) if(MITK_USE_BLUEBERRY) find_package(BlueBerry REQUIRED) set(MITK_DEFAULT_SUBPROJECTS MITK-Plugins) # Plug-in testing (needs some work to be enabled again) if(BUILD_TESTING) include(berryTestingHelpers) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp") get_target_property(_is_macosx_bundle CoreApp MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp.app/Contents/MacOS/CoreApp") endif() set(BLUEBERRY_TEST_APP_ID "org.mitk.qt.coreapplication") endif() include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") set(mitk_plugins_fullpath ) foreach(mitk_plugin ${MITK_EXT_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/AppList.cmake") set(mitk_apps_fullpath ) foreach(mitk_app ${MITK_APPS}) list(APPEND mitk_apps_fullpath "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${mitk_app}") endforeach() ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() # Construct a list of paths containing runtime directories # for MITK applications on Windows set(MITK_RUNTIME_PATH "${VTK_LIBRARY_DIRS}/%VS_BUILD_TYPE%;${ITK_LIBRARY_DIRS}/%VS_BUILD_TYPE%;${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/%VS_BUILD_TYPE%" ) if(QT4_FOUND) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${QT_LIBRARY_DIR}/../bin") endif() if(MITK_USE_BLUEBERRY) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CTK_RUNTIME_LIBRARY_DIRS}/%VS_BUILD_TYPE%") if(DEFINED CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY) if(IS_ABSOLUTE "${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}") set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}/%VS_BUILD_TYPE%") else() set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CTK_PLUGIN_RUNTIME_OUTPUT_DIRECTORY}/%VS_BUILD_TYPE%") endif() else() set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/plugins/%VS_BUILD_TYPE%") endif() endif() if(GDCM_DIR) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${GDCM_DIR}/bin/%VS_BUILD_TYPE%") endif() if(OpenCV_DIR) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${OpenCV_DIR}/bin/%VS_BUILD_TYPE%") endif() # DCMTK is statically build #if(DCMTK_DIR) # set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${DCMTK_DIR}/bin/%VS_BUILD_TYPE%") #endif() if(MITK_USE_Boost AND MITK_USE_Boost_LIBRARIES AND NOT MITK_USE_SYSTEM_Boost) set(MITK_RUNTIME_PATH "${MITK_RUNTIME_PATH};${Boost_LIBRARY_DIRS}") endif() #----------------------------------------------------------------------------- # Python Wrapping #----------------------------------------------------------------------------- set(MITK_WRAPPING_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Wrapping) set(MITK_WRAPPING_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Wrapping) option(MITK_USE_Python "Build cswig Python wrapper support (requires CableSwig)." OFF) if(MITK_USE_Python) add_subdirectory(Wrapping) endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- add_subdirectory(Documentation) #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) # MITK_APPS is set in Applications/AppList.cmake (included somewhere above # if MITK_USE_BLUEBERRY is set to ON). if(MITK_APPS) set(activated_apps_no 0) list(LENGTH MITK_APPS app_count) # Check how many apps have been enabled # If more than one app has been activated, the we use the # default CPack configuration. Otherwise that apps configuration # will be used, if present. foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name}) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) # check if the application is enabled if(${option_name}) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/Applications/${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/Applications/${target_dir}/CPackConfig.cmake") configure_file(${PROJECT_SOURCE_DIR}/Applications/${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${target_dir}") endif() endforeach() endif() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) if(MITK_USE_BLUEBERRY) # This is for installation support of external projects depending on # MITK plugins. The export file should not be used for linking to MITK # libraries without using LINK_DIRECTORIES, since the exports are incomplete # yet(depending libraries are not exported). if(MITK_PLUGIN_LIBRARIES) export(TARGETS ${MITK_PLUGIN_LIBRARIES} APPEND FILE ${MITK_EXPORTS_FILE}) endif() endif() configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) set(VISIBILITY_AVAILABLE 0) set(visibility_test_flag "") mitkFunctionCheckCompilerFlags("-fvisibility=hidden" visibility_test_flag) if(visibility_test_flag) # The compiler understands -fvisiblity=hidden (probably gcc >= 4 or Clang) set(VISIBILITY_AVAILABLE 1) endif() configure_file(mitkExportMacros.h.in ${MITK_BINARY_DIR}/mitkExportMacros.h) configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(VECMATH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/vecmath) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) file(GLOB _MODULES_CONF_FILES RELATIVE ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME} ${PROJECT_BINARY_DIR}/${MODULES_CONF_DIRNAME}/*.cmake) set(MITK_MODULE_NAMES) foreach(_module ${_MODULES_CONF_FILES}) string(REPLACE Config.cmake "" _module_name ${_module}) list(APPEND MITK_MODULE_NAMES ${_module_name}) endforeach() configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) # If we are under Windows, create two batch files which correctly # set up the environment for the application and for Visual Studio if(WIN32) include(mitkFunctionCreateWindowsBatchScript) set(VS_SOLUTION_FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.sln") foreach(VS_BUILD_TYPE debug release) mitkFunctionCreateWindowsBatchScript("${MITK_SOURCE_DIR}/CMake/StartVS.bat.in" ${PROJECT_BINARY_DIR}/StartVS_${VS_BUILD_TYPE}.bat ${VS_BUILD_TYPE}) endforeach() endif(WIN32) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) 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/mitkDataNodeFactory.cpp b/Core/Code/DataManagement/mitkDataNodeFactory.cpp index 86df353629..8a6c733691 100644 --- a/Core/Code/DataManagement/mitkDataNodeFactory.cpp +++ b/Core/Code/DataManagement/mitkDataNodeFactory.cpp @@ -1,482 +1,482 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include // C-Standard library includes #include #include // STL-related includes #include #include #include #include #include // VTK-related includes #include #include #include #include #include #include #include #include #include #include #include // ITK-related includes #include #include #include #include #include #include #include #include #include #include #include #ifdef NOMINMAX # define DEF_NOMINMAX # undef NOMINMAX #endif #include #ifdef DEF_NOMINMAX # ifndef NOMINMAX # define NOMINMAX # endif # undef DEF_NOMINMAX #endif #include #include // MITK-related includes #include "mitkSurface.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkProperties.h" //#include "mitkMaterialProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkScalarModeProperty.h" #include "mitkImage.h" #include "mitkLookupTableProperty.h" #include "mitkLookupTable.h" #include "mitkImageChannelSelector.h" #include "mitkImageSliceSelector.h" #include "mitkCoreObjectFactory.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkProgressBar.h" #include bool mitk::DataNodeFactory::m_TextureInterpolationActive = false; // default value for texture interpolation if nothing is defined in global options (see QmitkMainTemplate.ui.h) mitk::DataNodeFactory::DataNodeFactory() { m_Serie = false; m_OldProgress = 0; this->Modified(); //ensure that a CoreObjectFactory has been instantiated mitk::CoreObjectFactory::GetInstance(); } mitk::DataNodeFactory::~DataNodeFactory() {} void mitk::DataNodeFactory::SetImageSerie(bool serie) { m_Serie = serie; } void mitk::DataNodeFactory::GenerateData() { // IF filename is something.pic, and something.pic does not exist, try to read something.pic.gz // if there are both, something.pic and something.pic.gz, only the requested file is read // not only for images, but for all formats std::ifstream exists(m_FileName.c_str()); if (!exists) { std::string testfilename = m_FileName + ".gz"; std::ifstream exists(testfilename.c_str()); if (exists.good()) { m_FileName += ".gz"; } else { testfilename = m_FileName + ".GZ"; std::ifstream exists(testfilename.c_str()); if (exists.good()) { m_FileName += ".GZ"; } else { std::string message("File does not exist, or cannot be read. Filename = "); message += m_FileName; MITK_ERROR << message; itkExceptionMacro( << message ); } } } // part for DICOM // const char *numbers = "0123456789."; // std::string::size_type first_non_number; // first_non_number = itksys::SystemTools::GetFilenameName(m_FileName).find_first_not_of ( numbers ); if (DicomSeriesReader::IsDicom(this->m_FileName) /*|| first_non_number == std::string::npos*/) { this->ReadFileSeriesTypeDCM(); } else { bool usedNewDTNF = false; // the mitkBaseDataIO class returns a pointer of a vector of BaseData objects std::vector baseDataVector = mitk::BaseDataIO::LoadBaseDataFromFile( m_FileName, m_FilePrefix, m_FilePattern, m_Serie ); if( !baseDataVector.empty() ) this->ResizeOutputs((unsigned int)baseDataVector.size()); for(int i=0; i<(int)baseDataVector.size(); i++) { mitk::BaseData::Pointer baseData = baseDataVector.at(i); if( baseData.IsNotNull() ) { usedNewDTNF = true; mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(baseData); this->SetDefaultCommonProperties( node ); this->SetOutput(i, node); } } if(!usedNewDTNF && ( m_FileName != "" ) && !(m_Serie == false)) ReadFileSeriesTypeITKImageSeriesReader(); } } void mitk::DataNodeFactory::ResizeOutputs( const unsigned int& num ) { unsigned int prevNum = this->GetNumberOfOutputs(); this->SetNumberOfOutputs( num ); for ( unsigned int i = prevNum; i < num; ++i ) { this->SetNthOutput( i, this->MakeOutput( i ).GetPointer() ); } } bool mitk::DataNodeFactory::FileNameEndsWith( const std::string& name ) { if (m_FileName.size() < name.size()) return false; return m_FileName.substr(m_FileName.size() - name.size()) == name; } bool mitk::DataNodeFactory::FilePatternEndsWith( const std::string& name ) { return m_FilePattern.find( name ) != std::string::npos; } std::string mitk::DataNodeFactory::GetBaseFileName() { return itksys::SystemTools::GetFilenameName( m_FileName ); } std::string mitk::DataNodeFactory::GetBaseFilePrefix() { return itksys::SystemTools::GetFilenameName( m_FilePrefix ); } std::string mitk::DataNodeFactory::GetDirectory() { if ( !m_FileName.empty() ) return itksys::SystemTools::GetFilenamePath( m_FileName ); if ( !m_FilePrefix.empty() ) return itksys::SystemTools::GetFilenamePath( m_FilePrefix ); return std::string(); } void mitk::DataNodeFactory::ReadFileSeriesTypeDCM() { const char* previousCLocale = setlocale(LC_NUMERIC, NULL); setlocale(LC_NUMERIC, "C"); std::locale previousCppLocale( std::cin.getloc() ); std::locale l( "C" ); std::cin.imbue(l); if ( DicomSeriesReader::IsPhilips3DDicom(this->GetFileName()) ) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; this->ResizeOutputs(1); DataNode::Pointer node = this->GetOutput(0); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(this->GetFileName()); if (DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { node->SetName(this->GetBaseFileName()); } setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); return; } - DicomSeriesReader::UidFileNamesMap names_map = DicomSeriesReader::GetSeries(this->GetDirectory(), this->m_SeriesRestrictions); + DicomSeriesReader::UidFileNamesMap names_map = DicomSeriesReader::GetSeries(this->GetDirectory(), true, this->m_SeriesRestrictions); // true = group gantry tilt images const unsigned int size = names_map.size(); this->ResizeOutputs(size); ProgressBar::GetInstance()->AddStepsToDo(size); ProgressBar::GetInstance()->Progress(); unsigned int outputIndex = 0u; const DicomSeriesReader::UidFileNamesMap::const_iterator n_end = names_map.end(); for (DicomSeriesReader::UidFileNamesMap::const_iterator n_it = names_map.begin(); n_it != n_end; ++n_it) { const std::string &uid = n_it->first; DataNode::Pointer node = this->GetOutput(outputIndex); MITK_INFO << "Reading series " << outputIndex << ": " << uid << std::endl; - if (DicomSeriesReader::LoadDicomSeries(n_it->second, *node)) + if (DicomSeriesReader::LoadDicomSeries(n_it->second, *node, true, true, true)) { std::string nodeName(uid); std::string studyDescription; if ( node->GetStringProperty( "dicom.study.StudyDescription", studyDescription ) ) { nodeName = studyDescription; std::string seriesDescription; if ( node->GetStringProperty( "dicom.series.SeriesDescription", seriesDescription ) ) { nodeName += "/" + seriesDescription; } } node->SetName(nodeName); ++outputIndex; } else { MITK_ERROR << "Skipping series " << outputIndex << " due to exception" << std::endl; } ProgressBar::GetInstance()->Progress(); } setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); } void mitk::DataNodeFactory::ReadFileSeriesTypeITKImageSeriesReader() { typedef itk::Image ImageType; typedef itk::ImageSeriesReader< ImageType > ReaderType; typedef itk::NumericSeriesFileNames NameGenerator; if ( ! this->GenerateFileList() ) { itkWarningMacro( "Sorry, file list could not be generated!" ); return ; } if ( m_MatchedFileNames.size() == 0 ) { itkWarningMacro( "Sorry, no files matched the given filename ("<< m_FileName <<")!" ); return ; } // // Finally, initialize the ITK-reader and load the files! // ReaderType::Pointer reader = ReaderType::New(); reader->SetFileNames( m_MatchedFileNames ); try { reader->Update(); ResizeOutputs( reader->GetNumberOfOutputs() ); for ( unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i ) { //Initialize mitk image from itk mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( reader->GetOutput( i ) ); image->SetVolume( reader->GetOutput( i )->GetBufferPointer() ); //add the mitk image to the node mitk::DataNode::Pointer node = this->GetOutput( i ); node->SetData( image ); mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( m_FileName ); node->SetProperty( "name", nameProp ); } } catch ( const std::exception & e ) { itkWarningMacro( << e.what() ); return ; } } mitk::ColorProperty::Pointer mitk::DataNodeFactory::DefaultColorForOrgan( const std::string& organ ) { static bool initialized = false; static std::map< std::string, std::string > s_ColorMap; if (!initialized) { // all lowercase here, please! s_ColorMap.insert( std::make_pair( "ankle", "0xe38686") ); s_ColorMap.insert( std::make_pair( "appendix", "0xe38686") ); s_ColorMap.insert( std::make_pair( "blood vessels", "0xff3131") ); s_ColorMap.insert( std::make_pair( "bronchial tree", "0x3168ff") ); s_ColorMap.insert( std::make_pair( "bone", "0xd5d5d5") ); s_ColorMap.insert( std::make_pair( "brain", "0xff9cca") ); s_ColorMap.insert( std::make_pair( "coccyx", "0xe38686") ); s_ColorMap.insert( std::make_pair( "colon", "0xe38686") ); s_ColorMap.insert( std::make_pair( "cyst", "0xe38686") ); s_ColorMap.insert( std::make_pair( "elbow", "0xe38686") ); s_ColorMap.insert( std::make_pair( "eye", "0xe38686") ); s_ColorMap.insert( std::make_pair( "fallopian tube", "0xe38686") ); s_ColorMap.insert( std::make_pair( "fat", "0xff2bee") ); s_ColorMap.insert( std::make_pair( "hand", "0xe38686") ); s_ColorMap.insert( std::make_pair( "gall bladder", "0x567f18") ); s_ColorMap.insert( std::make_pair( "heart", "0xeb1d32") ); s_ColorMap.insert( std::make_pair( "hip", "0xe38686") ); s_ColorMap.insert( std::make_pair( "kidney", "0xd33f00") ); s_ColorMap.insert( std::make_pair( "knee", "0xe38686") ); s_ColorMap.insert( std::make_pair( "larynx", "0xe38686") ); s_ColorMap.insert( std::make_pair( "liver", "0xffcc3d") ); s_ColorMap.insert( std::make_pair( "lung", "0x6bdcff") ); s_ColorMap.insert( std::make_pair( "lymph node", "0xff0000") ); s_ColorMap.insert( std::make_pair( "muscle", "0xff456a") ); s_ColorMap.insert( std::make_pair( "nerve", "0xffea4f") ); s_ColorMap.insert( std::make_pair( "nose", "0xe38686") ); s_ColorMap.insert( std::make_pair( "oesophagus", "0xe38686") ); s_ColorMap.insert( std::make_pair( "ovaries", "0xe38686") ); s_ColorMap.insert( std::make_pair( "pancreas", "0xf9ab3d") ); s_ColorMap.insert( std::make_pair( "pelvis", "0xe38686") ); s_ColorMap.insert( std::make_pair( "penis", "0xe38686") ); s_ColorMap.insert( std::make_pair( "pharynx", "0xe38686") ); s_ColorMap.insert( std::make_pair( "prostate", "0xe38686") ); s_ColorMap.insert( std::make_pair( "rectum", "0xe38686") ); s_ColorMap.insert( std::make_pair( "sacrum", "0xe38686") ); s_ColorMap.insert( std::make_pair( "seminal vesicle", "0xe38686") ); s_ColorMap.insert( std::make_pair( "shoulder", "0xe38686") ); s_ColorMap.insert( std::make_pair( "spinal cord", "0xf5f93d") ); s_ColorMap.insert( std::make_pair( "spleen", "0xf96c3d") ); s_ColorMap.insert( std::make_pair( "stomach", "0xf96c3d") ); s_ColorMap.insert( std::make_pair( "teeth", "0xfffcd8") ); s_ColorMap.insert( std::make_pair( "testicles", "0xe38686") ); s_ColorMap.insert( std::make_pair( "thyroid", "0xfff694") ); s_ColorMap.insert( std::make_pair( "tongue", "0xe38686") ); s_ColorMap.insert( std::make_pair( "tumor", "0x937011") ); s_ColorMap.insert( std::make_pair( "urethra", "0xf8ff32") ); s_ColorMap.insert( std::make_pair( "urinary bladder", "0xf8ff32") ); s_ColorMap.insert( std::make_pair( "uterus", "0xe38686") ); s_ColorMap.insert( std::make_pair( "vagina", "0xe38686") ); s_ColorMap.insert( std::make_pair( "vertebra", "0xe38686") ); s_ColorMap.insert( std::make_pair( "wrist", "0xe38686") ); initialized = true; } std::string lowercaseOrgan(organ); for(unsigned int i = 0; i < organ.length(); i++) { lowercaseOrgan[i] = tolower(lowercaseOrgan[i]); } std::map< std::string, std::string >::iterator iter = s_ColorMap.find( lowercaseOrgan ); if ( iter != s_ColorMap.end() ) { std::string hexColor = iter->second; std::string hexRed = std::string("0x") + hexColor.substr( 2, 2 ); std::string hexGreen = std::string("0x") + hexColor.substr( 4, 2 ); std::string hexBlue = std::string("0x") + hexColor.substr( 6, 2 ); long int red = strtol( hexRed.c_str(), NULL, 16 ); long int green = strtol( hexGreen.c_str(), NULL, 16 ); long int blue = strtol( hexBlue.c_str(), NULL, 16 ); return ColorProperty::New( (float)red/ 255.0, (float)green/ 255.0, (float)blue/ 255.0 ); } else { // a default color (green) return ColorProperty::New( 0.0, 1.0, 0.0 ); } } void mitk::DataNodeFactory::SetDefaultCommonProperties(mitk::DataNode::Pointer &node) { // path mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenamePath( m_FileName ) ); node->SetProperty( StringProperty::PATH, pathProp ); // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if(nameProp.IsNull() || (strcmp(nameProp->GetValue(),"No Name!")==0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer() ); if(baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(),"No Name!")==0)) { // name neither defined in node, nor in BaseData -> name = filename if (FileNameEndsWith( ".gz" )) m_FileName = m_FileName.substr( 0, m_FileName.length()-3 ); nameProp = mitk::StringProperty::New( itksys::SystemTools::GetFilenameWithoutLastExtension( m_FileName ) ); node->SetProperty( "name", nameProp ); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New( baseDataNameProp->GetValue() ); node->SetProperty( "name", nameProp ); } } // visibility if(!node->GetProperty("visible")) node->SetVisibility(true); } 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/DataManagement/mitkPointSet.cpp b/Core/Code/DataManagement/mitkPointSet.cpp index b98affc4c8..13f5b3b5e6 100755 --- a/Core/Code/DataManagement/mitkPointSet.cpp +++ b/Core/Code/DataManagement/mitkPointSet.cpp @@ -1,786 +1,786 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" mitk::PointSet::PointSet() { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other): BaseData(other) { this->Expand(other.GetTimeSteps()); for (unsigned int t=0; t < other.GetTimeSteps(); t++) { for (int i=0; i< other.GetSize(t); i++) { this->InsertPoint(i, other.GetPoint(i,t), t); } } this->SetGeometry(other.GetGeometry()); } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize( 1 ); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData( pointData ); m_CalculateBoundingBox = false; Superclass::InitializeTimeSlicedGeometry(1); m_Initialized = true; } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand( unsigned int timeSteps ) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if ( timeSteps > oldSize ) { Superclass::Expand( timeSteps ); m_PointSetSeries.resize( timeSteps ); for ( unsigned int i = oldSize; i < timeSteps; ++i ) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData( pointData ); } //if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent( PointSetExtendTimeRangeEvent() ); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize( unsigned int t ) const { if ( t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet( int t ) const { if ( t < (int)m_PointSetSeries.size() ) { return m_PointSetSeries[t]; } else { return NULL; } } int mitk::PointSet::SearchPoint( Point3D point, float distance, int t ) const { if ( t >= (int)m_PointSetSeries.size() ) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill( 0 ); PointType indexPoint; this->GetGeometry( t )->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for ( it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i ) { bool ok = m_PointSetSeries[t]->GetPoints() ->GetElementIfIndexExists( it->Index(), &out ); if ( !ok ) { return -1; } else if ( indexPoint == out ) //if totally equal { return it->Index(); } //distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if ( dist < bestDist ) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint( PointIdentifier id, int t ) const { PointType out; out.Fill(0); if ( (unsigned int) t >= m_PointSetSeries.size() ) { return out; } if ( m_PointSetSeries[t]->GetPoints()->IndexExists(id) ) { m_PointSetSeries[t]->GetPoint( id, &out ); this->GetGeometry(t)->IndexToWorld( out, out ); return out; } else { return out; } } bool mitk::PointSet ::GetPointIfExists( PointIdentifier id, PointType* point, int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return false; } if ( m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point) ) { this->GetGeometry( t )->IndexToWorld( *point, *point ); return true; } else { return false; } } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; mitk::Geometry3D* tempGeometry = this->GetGeometry( t ); if (tempGeometry == NULL) { MITK_INFO<< __FILE__ << ", l." << __LINE__ << ": GetGeometry of "<< t <<" returned NULL!" << std::endl; return; } tempGeometry->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->GetPoints()->InsertElement( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; mitk::Geometry3D* tempGeometry = this->GetGeometry( t ); if (tempGeometry == NULL) { MITK_INFO<< __FILE__ << ", l." << __LINE__ << ": GetGeometry of "<< t <<" returned NULL!" << std::endl; return; } tempGeometry->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->GetPoints()->InsertElement( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } bool mitk::PointSet::SwapPointPosition( PointIdentifier id, bool moveUpwards, int t ) { if(IndexExists(id, t) ) { PointType point = GetPoint(id,t); if(moveUpwards) {//up if(IndexExists(id-1,t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id-1,point,t); this->Modified(); return true; } } else {//down if(IndexExists(id+1,t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id+1,point,t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists( int position, int t ) const { if ( (unsigned int) t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetPoints()->IndexExists( position ); } else { return false; } } bool mitk::PointSet::GetSelectInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo( int position, bool selected, int t ) { if ( this->IndexExists( position, t ) ) { // timeStep to ms ScalarType timeInMS = this->GetTimeSlicedGeometry()->TimeStepToMS( t ); // point Point3D point = this->GetPoint( position, t ); PointOperation* op; if (selected) { op = new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position ); } else { op = new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position ); } this->ExecuteOperation( op ); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return 0; } int numberOfSelected = 0; PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return -1; } PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if ( it->Value().selected == true ) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation( Operation* operation ) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if ( pointOp ) { timeStep = this->GetTimeSlicedGeometry() ->MSToTimeStep( pointOp->GetTimeInMS() ); } if ( timeStep < 0 ) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT://inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates mitk::Geometry3D* geometry = this->GetGeometry( timeStep ); if (geometry == NULL) { MITK_INFO<<"GetGeometry returned NULL!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { - pointOp->GetIndex(), + static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType() }; m_PointSetSeries[timeStep]->GetPointData() ->InsertElement(position, pointData); this->Modified(); //boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent( PointSetAddEvent() ); this->OnPointSetChange(); } break; case OpMOVE://moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates this->GetGeometry( timeStep )->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if ( !m_PointSetSeries[timeStep]->GetPointData( pointOp->GetIndex(), &pointData ) ) { m_PointSetSeries[timeStep]->SetPointData( pointOp->GetIndex(), pointData ); } this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetMoveEvent() ); } break; case OpREMOVE://removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetRemoveEvent() ); } break; case OpSELECTPOINT://select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT://unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); PointsContainer::STLContainerType::iterator it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } //to tell the mappers, that the data is modified and has to be updated //only call modified if anything is done, so call in cases //this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object*)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if ( this->GetSource( ) ) { this->GetSource( )->UpdateOutputInformation( ); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // mitk::TimeSlicedGeometry* timeGeometry = GetTimeSlicedGeometry(); if ( timeGeometry->GetTimeSteps() != m_PointSetSeries.size() ) { itkExceptionMacro(<<"timeGeometry->GetTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for ( unsigned int i = 0 ; i < m_PointSetSeries.size() ; ++i ) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if ( m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull) ) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for ( unsigned int j = 0; j < 3; ++j ) { if ( itkBounds[j*2+1] - itkBounds[j*2] < 1.0 ) { BoundingBox::CoordRepType center = (itkBounds[j*2] + itkBounds[j*2+1]) / 2.0; itkBounds[j*2] = center - 0.5; itkBounds[j*2+1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeSlicedGeometry()->UpdateInformation(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion( itk::DataObject * ) { } void mitk::PointSet::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (PointSetSeries::const_iterator it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer* points = ps->GetPoints(); MeshType::PointDataContainer* datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } diff --git a/Core/Code/IO/mitkDicomSeriesReader.cpp b/Core/Code/IO/mitkDicomSeriesReader.cpp index 18a1b9b43e..6863c7a488 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.cpp +++ b/Core/Code/IO/mitkDicomSeriesReader.cpp @@ -1,1131 +1,1364 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { typedef itk::GDCMSeriesFileNames DcmFileNamesGeneratorType; + +DicomSeriesReader::SliceGroupingAnalysisResult::SliceGroupingAnalysisResult() +:m_GantryTilt(false) +{ +} + +DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetBlockFilenames() +{ + return m_GroupedFiles; +} + +DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetUnsortedFilenames() +{ + return m_UnsortedFiles; +} + +bool DicomSeriesReader::SliceGroupingAnalysisResult::ContainsGantryTilt() +{ + return m_GantryTilt; +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToSortedBlock(const std::string& filename) +{ + m_GroupedFiles.push_back( filename ); +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToUnsortedBlock(const std::string& filename) +{ + m_UnsortedFiles.push_back( filename ); +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::FlagGantryTilt() +{ + m_GantryTilt = true; +} + +void DicomSeriesReader::SliceGroupingAnalysisResult::UndoPrematureGrouping() +{ + assert( !m_GroupedFiles.empty() ); + m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() ); + m_GroupedFiles.pop_back(); +} + + + + + + + const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; initialized = true; } return dictionary; } DataNode::Pointer -DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, UpdateCallBackMethod callback) +DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback) { DataNode::Pointer node = DataNode::New(); - if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, callback)) + if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback)) { if( filenames.empty() ) { return NULL; } return node; } else { return NULL; } } bool -DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, UpdateCallBackMethod callback) +DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback) { if( filenames.empty() ) { MITK_WARN << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(NULL); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); switch (io->GetComponentType()) { case DcmIoType::UCHAR: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::CHAR: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::USHORT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::SHORT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::UINT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::INT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::ULONG: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::LONG: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::FLOAT: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::DOUBLE: - DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, callback); + DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; default: MITK_ERROR << "Found unsupported DICOM pixel type: (enum value) " << io->GetComponentType(); } if (node.GetData()) { return true; } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // transversal gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } + +DicomSeriesReader::GantryTiltInformation::GantryTiltInformation() +: m_ShiftUp(0.0) +, m_ShiftRight(0.0) +, m_ShiftNormal(0.0) +, m_NumberOfSlicesApart(1) +{ +} + +DicomSeriesReader::GantryTiltInformation::GantryTiltInformation( + const Point3D& origin1, const Point3D& origin2, + const Vector3D& right, const Vector3D& up, + unsigned int numberOfSlicesApart) +: m_ShiftUp(0.0) +, m_ShiftRight(0.0) +, m_ShiftNormal(0.0) +, m_NumberOfSlicesApart(numberOfSlicesApart) +{ + assert(numberOfSlicesApart); + // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack: + // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1) + // check if this line passes through origin 2 + + /* + Determine if line (imagePosition2 + l * normal) contains imagePosition1. + Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal) + + E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html + + squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2 + / + |pointAlongNormal - origin2| ^ 2 + + ( x meaning the cross product ) + */ + + Vector3D normal = itk::CrossProduct(right, up); + Point3D pointAlongNormal = origin2 + normal; + + double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm(); + double denominator = (pointAlongNormal - origin2).GetSquaredNorm(); + + double distance = sqrt(numerator / denominator); + + if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt + { + MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry"; + MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; + MITK_DEBUG << " ==> storing this shift for later analysis:"; + MITK_DEBUG << " v right: " << right; + MITK_DEBUG << " v up: " << up; + MITK_DEBUG << " v normal: " << normal; + + Point3D projectionRight = projectPointOnLine( origin1, origin2, right ); + Point3D projectionUp = projectPointOnLine( origin1, origin2, up ); + Point3D projectionNormal = projectPointOnLine( origin1, origin2, normal ); + + m_ShiftRight = (projectionRight - origin2).GetNorm(); + m_ShiftUp = (projectionUp - origin2).GetNorm(); + m_ShiftNormal = (projectionNormal - origin2).GetNorm(); + + MITK_DEBUG << " shift normal: " << m_ShiftNormal; + MITK_DEBUG << " shift up: " << m_ShiftUp; + MITK_DEBUG << " shift right: " << m_ShiftRight; + + MITK_DEBUG << " tilt angle (rad): " << tanh( m_ShiftUp / m_ShiftNormal ); + MITK_DEBUG << " tilt angle (deg): " << tanh( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535; + } +} + +Point3D +DicomSeriesReader::GantryTiltInformation::projectPointOnLine( Point3D p, Point3D lineOrigin, Vector3D lineDirection ) +{ + /** + See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/ + + vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) ) + */ + + Vector3D lineOriginToP = p - lineOrigin; + ScalarType innerProduct = lineOriginToP * lineDirection; + + ScalarType factor = innerProduct / lineDirection.GetSquaredNorm(); + Point3D projection = lineOrigin + factor * lineDirection; + + return projection; +} + +ScalarType +DicomSeriesReader::GantryTiltInformation::GetTiltCorrectedAdditionalSize() const +{ + // this seems to be a bit too much sometimes, but better too much than cutting off parts of the image + return int(m_ShiftUp + 1.0); // to next bigger int: plus 1, then cut off after point +} + +ScalarType +DicomSeriesReader::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const +{ + // so many mm shifted per slice! + return m_ShiftUp / static_cast(m_NumberOfSlicesApart); +} + +ScalarType +DicomSeriesReader::GantryTiltInformation::GetRealZSpacing() const +{ + return m_ShiftNormal / static_cast(m_NumberOfSlicesApart); +} + + +bool +DicomSeriesReader::GantryTiltInformation::IsSheared() const +{ + return ( m_ShiftRight > 0.001 + || m_ShiftUp > 0.001); +} + + +bool +DicomSeriesReader::GantryTiltInformation::IsRegularGantryTilt() const +{ + return ( m_ShiftRight < 0.001 + && m_ShiftUp > 0.001); +} + + +std::string +DicomSeriesReader::ConstCharStarToString(const char* s) +{ + return s ? std::string(s) : std::string(); +} + +Point3D +DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful) +{ + Point3D p; + successful = true; + + std::istringstream originReader(s); + std::string coordinate; + unsigned int dim(0); + while( std::getline( originReader, coordinate, '\\' ) ) + { + if (dim > 4) break; // otherwise we access invalid array index + + p[dim++] = atof(coordinate.c_str()); + } + + if (dim != 3) + { + successful = false; + MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; + } + + return p; +} + +void +DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) +{ + successful = true; + + std::istringstream orientationReader(s); + std::string coordinate; + unsigned int dim(0); + while( std::getline( orientationReader, coordinate, '\\' ) ) + { + if (dim > 6) break; // otherwise we access invalid array index + + if (dim<3) + { + right[dim++] = atof(coordinate.c_str()); + } + else + { + up[dim++ - 3] = atof(coordinate.c_str()); + } + } + + if (dim != 6) + { + successful = false; + MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; + } +} -DicomSeriesReader::TwoStringContainers + +DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, + bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again - TwoStringContainers result; + SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; - MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader "; + MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (StringContainer::const_iterator fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that - const char* value = tagValueMappings[fileIter->c_str()][tagImagePositionPatient]; - if (value) - { - thisOriginString = value; - } + thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] ); - std::istringstream originReader(thisOriginString); - std::string coordinate; - unsigned int dim(0); - while( std::getline( originReader, coordinate, '\\' ) ) thisOrigin[dim++] = atof(coordinate.c_str()); - - if (dim != 3) - { - MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << "instead of 3 values."; - } + bool ignoredConversionError(-42); // hard to get here, no graceful way to react + thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " << thisOriginString << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position - result.second.push_back( *fileIter ); + result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; - // Now make sure this direction is along the normal vector of the first slice + // Here we calculate if this slice and the previous one are well aligned, + // i.e. we test if the previous origin is on a line through the current + // origin, directed into the normal direction of the current slice. + // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, - // which cannot be loaded into a single mitk::Image at the moment + // which cannot be simply loaded into a single mitk::Image at the moment. + // For this case, we flag this finding in the result and DicomSeriesReader + // can correct for that later. - // Again ugly code to read tag Image Orientation into two vEctors Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point - std::string thisOrientationString; - const char* value = tagValueMappings[fileIter->c_str()][tagImageOrientation]; - if (value) - { - thisOrientationString = value; - } - std::istringstream orientationReader(thisOrientationString); - std::string coordinate; - unsigned int dim(0); - while( std::getline( orientationReader, coordinate, '\\' ) ) - if (dim<3) right[dim++] = atof(coordinate.c_str()); - else up[dim++ - 3] = atof(coordinate.c_str()); - - if (dim != 6) - { - MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << "instead of 6 values."; - } - - - /* - Determine if line (thisOrigin + l * normal) contains lastDifferentOrigin. - - Done by calculating the distance of lastDifferentOrigin from line (thisOrigin + l *normal) - - E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html - - squared distance = | (pointAlongNormal - thisOrign) x (thisOrigin - lastDifferentOrigin) | ^ 2 - / - |pointAlongNormal - thisOrigin| ^ 2 - - ( x meaning the cross product ) - - MITK_DEBUG << "Tilt check: right vector (" << right[0] << "," << right[1] << "," << right[2] << "), " - "up vector (" << up[0] << "," << up[1] << "," << up[2] << ")"; - */ + DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError ); + + GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); - Vector3D normal = itk::CrossProduct(right, up); - Point3D pointAlongNormal = thisOrigin + normal; - - double numerator = itk::CrossProduct( pointAlongNormal - thisOrigin , thisOrigin - lastDifferentOrigin ).GetSquaredNorm(); - double denominator = (pointAlongNormal - thisOrigin).GetSquaredNorm(); - - double distance = sqrt(numerator / denominator); - - if (distance > 0.001) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt + if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { - MITK_DEBUG << " Series might contain a tilted geometry"; - MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; - MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; - - /* Pessimistic approach: split block right here - - result.first.assign( files.begin(), fileIter ); - result.second.insert( result.second.end(), fileIter, files.end() ); + /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ + + // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. + // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. + // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) - return result; // stop processing with first split - */ + // tell apart gantry tilt from overall skewedness + // sort out irregularly sheared slices, that IS NOT tilting - /* optimistic approach: save file for later, check all further files */ - - result.second.push_back(*fileIter); - fileFitsIntoPattern = false; + if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) + { + result.FlagGantryTilt(); + result.AddFileToSortedBlock(*fileIter); // this file is good for current block + fileFitsIntoPattern = true; + } + else + { + result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis + fileFitsIntoPattern = false; + } } else { - result.first.push_back(*fileIter); // this file is good for current block + result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 - /* - Pessimistic approach: split right here: - - result.first.assign( files.begin(), fileIter ); - result.second.insert( result.second.end(), fileIter, files.end() ); - - return result; // stop processing with first split - */ - /* Optimistic approach: check if any of the remaining slices fits in */ - result.second.push_back( *fileIter ); // sort away for further analysis + result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { - result.first.push_back(*fileIter); // this file is good for current block + result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { - result.first.push_back(*fileIter); // this file is good for current block + result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } - // recored current origin for reference in later iterations + // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } + + if ( result.ContainsGantryTilt() ) + { + // check here how many files were grouped. + // IF it was only two files AND we assume tiltedness (e.g. save "distance") + // THEN we would want to also split the two previous files (simple) because + // we don't have any reason to assume they belong together + + if ( result.GetBlockFilenames().size() == 2 ) + { + result.UndoPrematureGrouping(); + } + } return result; } DicomSeriesReader::UidFileNamesMap -DicomSeriesReader::GetSeries(const StringContainer& files, const StringContainer &restrictions) +DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { - return GetSeries(files, true, restrictions); + return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::UidFileNamesMap -DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer& /*restrictions*/) +DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ UidFileNamesMap groupsOfSimilarImages; // preliminary result, refined into the final result mapOf3DPlusTBlocks // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return groupsOfSimilarImages; } // assign files IDs that will separate them for loading into image blocks for (gdcm::Scanner::ConstIterator fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { //MITK_DEBUG << "Scan file " << fileIter->first << std::endl; if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries + if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue; // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); groupsOfSimilarImages [ moreUniqueSeriesId ].push_back( fileIter->first ); } - // PART III: sort slices spatially + // PART II: sort slices spatially for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { try { groupsOfSimilarImages[ groupIter->first ] = SortSeriesSlices( groupIter->second ); // sort each slice group spatially } catch(...) { MITK_ERROR << "Catched something."; } } - // PART II: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), + // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks UidFileNamesMap mapOf3DPlusTBlocks; // final result of this function for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { UidFileNamesMap mapOf3DBlocks; // intermediate result for only this group(!) + std::map mapOf3DBlockAnalysisResults; StringContainer filesStillToAnalyze = groupIter->second; std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { - TwoStringContainers analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, scanner.GetMappings() ); + SliceGroupingAnalysisResult analysisResult = + AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, + groupImagesWithGantryTilt, + scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; - mapOf3DBlocks[ newGroupUID.str() ] = analysisResult.first; + mapOf3DBlocks[ newGroupUID.str() ] = analysisResult.GetBlockFilenames(); MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << mapOf3DBlocks[ newGroupUID.str() ].size() << " files"; ++subgroup; - filesStillToAnalyze = analysisResult.second; // remember what needs further analysis + filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of mapOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output // TODO avoid collisions (or prove impossibility) mapOf3DPlusTBlocks.insert( mapOf3DBlocks.begin(), mapOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( UidFileNamesMap::const_iterator block3DIter = mapOf3DBlocks.begin(); block3DIter != mapOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue( mapOf3DBlocks[thisBlockKey].front().c_str(), tagImagePositionPatient ), *previous_origin_value = scanner.GetValue( mapOf3DBlocks[previousBlockKey].front().c_str(), tagImagePositionPatient ), *destination_value = scanner.GetValue( mapOf3DBlocks[thisBlockKey].back().c_str(), tagImagePositionPatient ), *previous_destination_value = scanner.GetValue( mapOf3DBlocks[previousBlockKey].back().c_str(), tagImagePositionPatient ); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { - std::string thisOriginString = origin_value; - std::string previousOriginString = previous_origin_value; + std::string thisOriginString = ConstCharStarToString( origin_value ); + std::string previousOriginString = ConstCharStarToString( previous_origin_value ); // also compare last origin, because this might differ if z-spacing is different - std::string thisDestinationString = destination_value; - std::string previousDestinationString = previous_destination_value; + std::string thisDestinationString = ConstCharStarToString( destination_value ); + std::string previousDestinationString = ConstCharStarToString( previous_destination_value ); identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); } } catch(...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block mapOf3DPlusTBlocks[previousBlockKey].insert( mapOf3DPlusTBlocks[previousBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " ==> group closed with " << mapOf3DPlusTBlocks[previousBlockKey].size() / numberOfFilesInPreviousBlock << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( UidFileNamesMap::const_iterator groupIter = mapOf3DPlusTBlocks.begin(); groupIter != mapOf3DPlusTBlocks.end(); ++groupIter ) { MITK_DEBUG << " Image volume " << groupIter->first << " with " << groupIter->second.size() << " files"; } MITK_DEBUG << "Done. "; MITK_DEBUG << "================================================================================"; return mapOf3DPlusTBlocks; } DicomSeriesReader::UidFileNamesMap -DicomSeriesReader::GetSeries(const std::string &dir, const StringContainer &restrictions) +DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive - return GetSeries(directoryLister.GetFilenames(), restrictions); + return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception& e) { MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols - - std::string constructedID; - + + const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; + if (!tagSeriesInstanceUid) + { + mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here."; + } + std::string constructedID = tagSeriesInstanceUid; + + constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); + constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); + constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); + constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); + + // be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) + // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); try { - constructedID = tagValueMap[ tagSeriesInstanceUID ]; + bool conversionError(false); + Vector3D right; right.Fill(0.0); + Vector3D up; right.Fill(0.0); + DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError ); + //string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]); + std::ostringstream ss; + ss.setf(std::ios::fixed, std::ios::floatfield); + ss.precision(5); + ss << right[0] << "\\" + << right[1] << "\\" + << right[2] << "\\" + << up[0] << "\\" + << up[1] << "\\" + << up[2]; + std::string simplifiedOrientationString(ss.str()); + + constructedID += IDifyTagValue( simplifiedOrientationString ); } catch (std::exception& e) { - MITK_ERROR << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image."; - MITK_ERROR << "Error from exception: " << e.what(); + MITK_WARN << "Could not access tag " << tagImageOrientation << ": " << e.what(); } - constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); - constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); - constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); - constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); - constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer -DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, const StringContainer &restrictions) +DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { - UidFileNamesMap allSeries = GetSeries(dir, restrictions); + UidFileNamesMap allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for ( UidFileNamesMap::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { resultingFileList.insert( resultingFileList.end(), idIter->second.begin(), idIter->second.end() ); // append } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { sorter.Sort(unsortedFilenames); return sorter.GetFilenames(); } catch(std::logic_error&) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { - // make sure we habe Image Position and Orientation + // make sure we have Image Position and Orientation if ( ! ( ds1.FindDataElement(gdcm::Tag(0x0020,0x0032)) && ds1.FindDataElement(gdcm::Tag(0x0020,0x0037)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0032)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0037)) ) ) { MITK_WARN << "Dicom images are missing attributes for a meaningful sorting."; throw std::logic_error("Dicom images are missing attributes for a meaningful sorting."); } gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); - if (image_orientation1 != image_orientation2) + /* + we tolerate very small differences in image orientation, since we got to know about + acquisitions where these values change across a single series (7th decimal digit) + (http://bugs.mitk.org/show_bug.cgi?id=12263) + + still, we want to check if our assumption of 'almost equal' orientations is valid + */ + for (unsigned int dim = 0; dim < 6; ++dim) { - MITK_ERROR << "Dicom images have different orientations."; - throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); + if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 ) + { + MITK_ERROR << "Dicom images have different orientations."; + throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); + } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } if ( fabs(dist1 - dist2) < mitk::eps) { gdcm::Attribute<0x0008,0x0032> acq_time1; // Acquisition time (may be missing, so we check existence first) gdcm::Attribute<0x0008,0x0032> acq_time2; gdcm::Attribute<0x0020,0x0012> acq_number1; // Acquisition number (may also be missing, so we check existence first) gdcm::Attribute<0x0020,0x0012> acq_number2; if (ds1.FindDataElement(gdcm::Tag(0x0008,0x0032)) && ds2.FindDataElement(gdcm::Tag(0x0008,0x0032))) { acq_time1.Set(ds1); acq_time2.Set(ds2); return acq_time1 < acq_time2; } else if (ds1.FindDataElement(gdcm::Tag(0x0020,0x0012)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0012))) { acq_number1.Set(ds1); acq_number2.Set(ds2); return acq_number1 < acq_number2; } else { return true; } } else { // default: compare position return dist1 < dist2; } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, image); } void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, Image* image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); //DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for ( std::list::iterator i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ ) { const StringContainer& files = (*i); unsigned int slice(0); for ( StringContainer::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); //If more than one time step add postfix ".t" + timestep if(timeStep != 0) { propertyKeySliceLocation.append(".t" + timeStep); propertyKeyInstanceNumber.append(".t" + timeStep); propertyKeySOPInstanceNumber.append(".t" + timeStep); } image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) ); image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) ); image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) ); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary(); const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); itk::MetaDataDictionary::ConstIterator dictIter = dict.Begin(); while ( dictIter != dict.End() ) { - MITK_DEBUG << "Key " << dictIter->first; + //MITK_DEBUG << "Key " << dictIter->first; std::string value; if ( itk::ExposeMetaData( dict, dictIter->first, value ) ) { - MITK_DEBUG << "Value " << value; + //MITK_DEBUG << "Value " << value; TagToPropertyMapType::const_iterator valuePosition = propertyLookup.find( dictIter->first ); if ( valuePosition != propertyLookup.end() ) { std::string propertyKey = valuePosition->second; - MITK_DEBUG << "--> " << propertyKey; + //MITK_DEBUG << "--> " << propertyKey; image->SetProperty( propertyKey.c_str(), StringProperty::New(value) ); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ; } ++dictIter; } } } // end namespace mitk #include diff --git a/Core/Code/IO/mitkDicomSeriesReader.h b/Core/Code/IO/mitkDicomSeriesReader.h index 2e14dc9a14..d7484fb62d 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.h +++ b/Core/Code/IO/mitkDicomSeriesReader.h @@ -1,471 +1,687 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkDicomSeriesReader_h #define mitkDicomSeriesReader_h #include "mitkDataNode.h" #include "mitkConfig.h" #include #include #include #ifdef NOMINMAX # define DEF_NOMINMAX # undef NOMINMAX #endif #include #ifdef DEF_NOMINMAX # ifndef NOMINMAX # define NOMINMAX # endif # undef DEF_NOMINMAX #endif #include #include #include #include #include #include namespace mitk { /** \brief Loading DICOM images as MITK images. - \ref DicomSeriesReader_purpose - \ref DicomSeriesReader_limitations - \ref DicomSeriesReader_usage - \ref DicomSeriesReader_sorting - \ref DicomSeriesReader_sorting1 - \ref DicomSeriesReader_sorting2 - \ref DicomSeriesReader_sorting3 - \ref DicomSeriesReader_sorting4 - \ref DicomSeriesReader_tests \section DicomSeriesReader_purpose Purpose DicomSeriesReader serves as a central class for loading DICOM images as mitk::Image. As the term "DICOM image" covers a huge variety of possible modalities and implementations, and since MITK assumes that 3D images are made up of continuous blocks of slices without any gaps or changes in orientation, the loading mechanism must implement a number of decisions and compromises. The main intention of this implementation is not efficiency but correcness of generated slice positions! \section DicomSeriesReader_limitations Assumptions and limitations The class is working only with GDCM 2.0.14 (or possibly newer). This version is the default of an MITK super-build. Support for other versions or ITK's DicomIO was dropped because of the associated complexity of DicomSeriesReader. \b Assumptions - expected to work for SOP Classes CT Image Storage and MR Image Storage (NOT for the "Enhanced" variants containing multi-frame images) - special treatment for a certain type of Philips 3D ultrasound (recogized by tag 3001,0010 set to "Philips3D") - loader will always attempt to read multiple single slices as a single 3D image volume (i.e. mitk::Image) - slices will be grouped by basic properties such as orientation, rows, columns, spacing and grouped into as large blocks as possible \b Options - images that cover the same piece of space (i.e. position, orientation, and dimensions are equal) can be interpreted as time-steps of the same image, i.e. a series will be loaded as 3D+t \b Limitations - the 3D+t assumption only works if all time-steps have an equal number of slices and if all have the Acquisition Time attribute set to meaningful values - Images from tilted CT gantries CAN ONLY be loaded as a series of single-slice images, since mitk::Image or the accompanying mapper are not (yet?) capable of representing such geometries - Secondary Capture images are expected to have the (0018,2010) tag describing the pixel spacing. If only the (0028,0030) tag is set, the spacing will be misinterpreted as (1,1) \section DicomSeriesReader_usage Usage The starting point for an application is a set of DICOM files that should be loaded. For convenience, DicomSeriesReader can also parse a whole directory for DICOM files, but an application should better know exactly what to load. Loading is then done in two steps: 1. Group the files into spatial blocks by calling GetSeries(). This method will sort all passed files into meaningful blocks that could fit into an mitk::Image. Sorting for 3D+t loading is optional but default. The \b return value of this function is a list of identifiers similar to DICOM UIDs, each associated to a sorted list of file names. 2. Load a sorted set of files by calling LoadDicomSeries(). This method expects go receive the sorting output of GetSeries(). The method will then invoke ITK methods to actually load the files into memory and put them into mitk::Images. Again, loading as 3D+t is optional. Example: \code // only a directory is known at this point: /home/who/dicom DicomSeriesReader::UidFileNamesMap allImageBlocks = DicomSeriesReader::GetSeries("/home/who/dicom/"); // file now divided into groups of identical image size, orientation, spacing, etc. // each of these lists should be loadable as an mitk::Image. DicomSeriesReader::StringContainer seriesToLoad = allImageBlocks[...]; // decide what to load // final step: load into DataNode (can result in 3D+t image) DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( oneBlockSorted ); Image::Pointer image = dynamic_cast( node->GetData() ); \endcode \section DicomSeriesReader_sorting Logic for sorting 2D slices from DICOM images into 3D+t blocks for mitk::Image The general sorting mechanism (implemented in GetSeries) groups and sorts a set of DICOM files, each assumed to contain a single CT/MR slice. In the following we refer to those file groups as "blocks", since this is what they are meant to become when loaded into an mitk::Image. \subsection DicomSeriesReader_sorting1 Step 1: Avoiding pure non-sense A first pass separates slices that cannot possibly be loaded together because of restrictions of mitk::Image. After this steps, each block contains only slices that match in all of the following DICOM tags: - (0020,0037) Image Orientation - (0028,0030) Pixel Spacing - (0018,0050) Slice Thickness - (0028,0010) Number Of Rows - (0028,0011) Number Of Columns - (0020,000e) Series Instance UID : could be argued about, might be dropped in the future (optionally) \subsection DicomSeriesReader_sorting2 Step 2: Sort slices spatially Before slices are further analyzed, they are sorted spatially. As implemented by GdcmSortFunction(), slices are sorted by 1. distance from origin (calculated using (0020,0032) Image Position Patient and (0020,0037) Image Orientation) 2. when distance is equal, (0008,0032) Acquisition Time is used as a backup criterion (necessary for meaningful 3D+t sorting) \subsection DicomSeriesReader_sorting3 Step 3: Ensure equal z spacing Since inter-slice distance is not recorded in DICOM tags, we must ensure that blocks are made up of slices that have equal distances between neighboring slices. This is especially necessary because itk::ImageSeriesReader is later used for the actual loading, and this class expects (and does nocht verify) equal inter-slice distance. To achieve such grouping, the inter-slice distance is calculated from the first two different slice positions of a block. Following slices are added to a block as long as they can be added by adding the calculated inter-slice distance to the last slice of the block. Slices that do not fit into the expected distance pattern, are set aside for further analysis. This grouping is done until each file has been assigned to a group. Slices that share a position in space are also sorted into separate blocks during this step. So the result of this step is a set of blocks that contain only slices with equal z spacing and uniqe slices at each position. \subsection DicomSeriesReader_sorting4 Step 4 (optional): group 3D blocks as 3D+t when possible This last step depends on an option of GetSeries(). When requested, image blocks from the previous step are merged again whenever two blocks occupy the same portion of space (i.e. same origin, number of slices and z-spacing). + \section DicomSeriesReader_gantrytilt Handling of gantry tilt + + When CT gantry tilt is used, the gantry plane (= X-Ray source and detector ring) and the vertical plane do not align + anymore. This scanner feature is used for example to reduce metal artifacs (e.g. Lee C , Evaluation of Using CT + Gantry Tilt Scan on Head and Neck Cancer Patients with Dental Structure: Scans Show Less Metal Artifacts. Presented + at: Radiological Society of North America 2011 Scientific Assembly and Annual Meeting; November 27- December 2, + 2011 Chicago IL.). + + The acquired planes of such CT series do not match the expectations of a orthogonal geometry in mitk::Image: if you + stack the slices, they show a small shift along the Y axis: +\verbatim + + without tilt with tilt + + |||||| ////// + |||||| ////// +-- |||||| --------- ////// -------- table orientation + |||||| ////// + |||||| ////// + +Stacked slices: + + without tilt with tilt + + -------------- -------------- + -------------- -------------- + -------------- -------------- + -------------- -------------- + -------------- -------------- + +\endverbatim + + + As such gemetries do not in conjunction with mitk::Image, DicomSeriesReader performs a correction for such series + if the groupImagesWithGantryTilt or correctGantryTilt flag in GetSeries and LoadDicomSeries is set (default = on). + + The correction algorithms undoes two errors introduced by ITK's ImageSeriesReader: + - the plane shift that is ignored by ITK's reader is recreated by applying a shearing transformation using itk::ResampleFilter. + - the spacing is corrected (it is calculated by ITK's reader from the distance between two origins, which is NOT the slice distance in this special case) + + Both errors are introduced in + itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20) + + For the correction, we examine two slices of a series, both described as a pair (origin/orientation): + - we calculate if the second origin is on a line along the normal of the first slice + - if this is not the case, the geometry will not fit a normal mitk::Image/mitk::Geometry3D + - we then project the second origin into the first slice's coordinate system to quantify the shift + - both is done in class GantryTiltInformation with quite some comments. + + \section DicomSeriesReader_whynotinitk Why is this not in ITK? + + Some of this code would probably be better located in ITK. It is just a matter of resources that this is not the + case yet. Any attempts into this direction are welcome and can be supported. At least the gantry tilt correction + should be a simple addition to itk::ImageSeriesReader. + \section DicomSeriesReader_tests Tests regarding DICOM loading A number of tests have been implemented to check our assumptions regarding DICOM loading. Please see \ref DICOMTesting + \todo refactor all the protected helper objects/methods into a separate header so we compile faster */ class MITK_CORE_EXPORT DicomSeriesReader { public: /** \brief Lists of filenames. */ typedef std::vector StringContainer; /** \brief For grouped lists of filenames, assigned an ID each. */ typedef std::map UidFileNamesMap; /** \brief Interface for the progress callback. */ typedef void (*UpdateCallBackMethod)(float); /** \brief Provide combination of preprocessor defines that was active during compilation. Since this class is a combination of several possible implementations, separated only by ifdef's, calling instances might want to know which flags were active at compile time. */ static std::string GetConfigurationString(); /** \brief Checks if a specific file contains DICOM data. */ static bool IsDicom(const std::string &filename); /** \brief see other GetSeries(). Find all series (and sub-series -- see details) in a particular directory. */ static UidFileNamesMap GetSeries(const std::string &dir, + bool groupImagesWithGantryTilt, const StringContainer &restrictions = StringContainer()); /** \brief see other GetSeries(). \warning Untested, could or could not work. This differs only by having an additional restriction to a single known DICOM series. Internally, it uses the other GetSeries() method. */ static StringContainer GetSeries(const std::string &dir, const std::string &series_uid, + bool groupImagesWithGantryTilt, const StringContainer &restrictions = StringContainer()); /** \brief PREFERRED version of this method - scan and sort DICOM files. Parse a list of files for images of DICOM series. For each series, an enumeration of the files contained in it is created. \return The resulting maps UID-like keys (based on Series Instance UID and slice properties) to sorted lists of file names. SeriesInstanceUID will be enhanced to be unique for each set of file names that is later loadable as a single mitk::Image. This implies that Image orientation, slice thickness, pixel spacing, rows, and columns must be the same for each file (i.e. the image slice contained in the file). If this separation logic requires that a SeriesInstanceUID must be made more specialized, it will follow the same logic as itk::GDCMSeriesFileNames to enhance the UID with more digits and dots. Optionally, more tags can be used to separate files into different logical series by setting the restrictions parameter. \warning Adding restrictions is not yet implemented! */ static UidFileNamesMap - GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer &restrictions = StringContainer()); + GetSeries(const StringContainer& files, + bool sortTo3DPlust, + bool groupImagesWithGantryTilt, + const StringContainer &restrictions = StringContainer()); /** \brief See other GetSeries(). Use GetSeries(const StringContainer& files, bool sortTo3DPlust, const StringContainer &restrictions) instead. */ static UidFileNamesMap - GetSeries(const StringContainer& files, const StringContainer &restrictions = StringContainer()); + GetSeries(const StringContainer& files, + bool groupImagesWithGantryTilt, + const StringContainer &restrictions = StringContainer()); /** Loads a DICOM series composed by the file names enumerated in the file names container. If a callback method is supplied, it will be called after every progress update with a progress value in [0,1]. \param filenames The filenames to load. \param sort Whether files should be sorted spatially (true) or not (false - maybe useful if presorted) \param load4D Whether to load the files as 3D+t (if possible) */ static DataNode::Pointer LoadDicomSeries(const StringContainer &filenames, bool sort = true, bool load4D = true, + bool correctGantryTilt = true, UpdateCallBackMethod callback = 0); /** \brief See LoadDicomSeries! Just a slightly different interface. */ static bool LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort = true, bool load4D = true, + bool correctGantryTilt = true, UpdateCallBackMethod callback = 0); protected: + /** + \brief Return type of DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption. + + Class contains the grouping result of method DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption, + which takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction. + + The result contains of two blocks: a first one is the grouping result, all of those images can be loaded + into one image block because they have an equal origin-to-origin distance without any gaps in-between. + */ + class SliceGroupingAnalysisResult + { + public: + + SliceGroupingAnalysisResult(); + + /** + \brief Grouping result, all same origin-to-origin distance w/o gaps. + */ + StringContainer GetBlockFilenames(); + + /** + \brief Remaining files, which could not be grouped. + */ + StringContainer GetUnsortedFilenames(); + + /** + \brief Wheter or not the grouped result contain a gantry tilt. + */ + bool ContainsGantryTilt(); + + /** + \brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only. + */ + void AddFileToSortedBlock(const std::string& filename); + + /** + \brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only. + */ + void AddFileToUnsortedBlock(const std::string& filename); + + /** + \brief Meant for internal use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption only. + \todo Could make sense to enhance this with an instance of GantryTiltInformation to store the whole result! + */ + void FlagGantryTilt(); + + /** + \brief Only meaningful for use by AnalyzeFileForITKImageSeriesReaderSpacingAssumption. + */ + void UndoPrematureGrouping(); + + protected: + + StringContainer m_GroupedFiles; + StringContainer m_UnsortedFiles; + + bool m_GantryTilt; + }; + + /** + \brief Gantry tilt analysis result. + + Takes geometry information for two slices of a DICOM series and + calculates whether these fit into an orthogonal block or not. + If NOT, they can either be the result of an acquisition with + gantry tilt OR completly broken by some shearing transformation. + + All calculations are done in the constructor, results can then + be read via the remaining methods. + */ + class GantryTiltInformation + { + public: + + /** + \brief Just so we can create empty instances for assigning results later. + */ + GantryTiltInformation(); + + /** + \brief THE constructor, which does all the calculations. + + See code comments for explanation. + */ + GantryTiltInformation( const Point3D& origin1, + const Point3D& origin2, + const Vector3D& right, + const Vector3D& up, + unsigned int numberOfSlicesApart); + + /** + \brief Whether the slices were sheared. + */ + bool IsSheared() const; + + /** + \brief Whether the shearing is a gantry tilt or more complicated. + */ + bool IsRegularGantryTilt() const; + + /** + \brief The offset distance in Y direction for each slice (describes the tilt result). + */ + ScalarType GetMatrixCoefficientForCorrectionInWorldCoordinates() const; + + + /** + \brief The z / inter-slice spacing. Needed to correct ImageSeriesReader's result. + */ + ScalarType GetRealZSpacing() const; + + /** + \brief The shift between first and last slice in mm. + + Needed to resize an orthogonal image volume. + */ + ScalarType GetTiltCorrectedAdditionalSize() const; + + protected: + + /** + \brief Projection of point p onto line through lineOrigin in direction of lineDirection. + */ + Point3D projectPointOnLine( Point3D p, Point3D lineOrigin, Vector3D lineDirection ); + + ScalarType m_ShiftUp; + ScalarType m_ShiftRight; + ScalarType m_ShiftNormal; + unsigned int m_NumberOfSlicesApart; + }; + /** \brief for internal sorting. */ typedef std::pair TwoStringContainers; /** \brief Maps DICOM tags to MITK properties. */ typedef std::map TagToPropertyMapType; /** \brief Ensure an equal z-spacing for a group of files. + + Takes as input a number of images, which are all equally oriented and spatially sorted along their normal direction. Internally used by GetSeries. Returns two lists: the first one contins slices of equal inter-slice spacing. The second list contains remaining files, which need to be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption again. Relevant code that is matched here is in itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20) */ static - TwoStringContainers - AnalyzeFileForITKImageSeriesReaderSpacingAssumption(const StringContainer& files, const gdcm::Scanner::MappingType& tagValueMappings_); + SliceGroupingAnalysisResult + AnalyzeFileForITKImageSeriesReaderSpacingAssumption(const StringContainer& files, bool groupsOfSimilarImages, const gdcm::Scanner::MappingType& tagValueMappings_); + + static + std::string + ConstCharStarToString(const char* s); + + static + Point3D + DICOMStringToPoint3D(const std::string& s, bool& successful); + + static + void + DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful); + + template + static + typename ImageType::Pointer + InPlaceFixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo ); + /** \brief Sort a set of file names in an order that is meaningful for loading them into an mitk::Image. \warning This method assumes that input files are similar in basic properties such as slice thicknes, image orientation, pixel spacing, rows, columns. It should always be ok to put the result of a call to GetSeries(..) into this method. Sorting order is determined by 1. image position along its normal (distance from world origin) 2. acquisition time If P denotes a position and T denotes a time step, this method will order slices from three timesteps like this: \verbatim P1T1 P1T2 P1T3 P2T1 P2T2 P2T3 P3T1 P3T2 P3T3 \endverbatim */ static StringContainer SortSeriesSlices(const StringContainer &unsortedFilenames); public: /** \brief Checks if a specific file is a Philips3D ultrasound DICOM file. */ static bool IsPhilips3DDicom(const std::string &filename); protected: /** \brief Read a Philips3D ultrasound DICOM file and put into an mitk::Image. */ static bool ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image); /** \brief Construct a UID that takes into account sorting criteria from GetSeries(). */ static std::string CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ); /** \brief Helper for CreateMoreUniqueSeriesIdentifier */ static std::string CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ); /** \brief Helper for CreateMoreUniqueSeriesIdentifier */ static std::string IDifyTagValue(const std::string& value); typedef itk::GDCMImageIO DcmIoType; /** \brief Progress callback for DicomSeriesReader. */ class CallbackCommand : public itk::Command { public: CallbackCommand(UpdateCallBackMethod callback) : m_Callback(callback) { } void Execute(const itk::Object *caller, const itk::EventObject&) { (*this->m_Callback)(static_cast(caller)->GetProgress()); } void Execute(itk::Object *caller, const itk::EventObject&) { (*this->m_Callback)(static_cast(caller)->GetProgress()); } protected: UpdateCallBackMethod m_Callback; }; /** \brief Scan for slice image information */ static void ScanForSliceInformation( const StringContainer &filenames, gdcm::Scanner& scanner ); /** \brief Performs actual loading of a series and creates an image having the specified pixel type. */ template static void - LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, UpdateCallBackMethod callback); + LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback); /** \brief Feed files into itk::ImageSeriesReader and retrieve a 3D MITK image. \param command can be used for progress reporting */ template static Image::Pointer - LoadDICOMByITK( const StringContainer&, CallbackCommand* command = NULL); + LoadDICOMByITK( const StringContainer&, bool correctTilt, const GantryTiltInformation& tiltInfo, CallbackCommand* command = NULL); /** \brief Sort files into time step blocks of a 3D+t image. Called by LoadDicom. Expects to be fed a single list of filenames that have been sorted by GetSeries previously (one map entry). This method will check how many timestep can be filled with given files. Assumption is that the number of time steps is determined by how often the first position in space repeats. I.e. if the first three files in the input parameter all describe the same location in space, we'll construct three lists of files. and sort the remaining files into them. \todo We can probably remove this method if we somehow transfer 3D+t information from GetSeries to LoadDicomSeries. */ static std::list SortIntoBlocksFor3DplusT( const StringContainer& presortedFilenames, const gdcm::Scanner::MappingType& tagValueMappings_, bool sort, bool& canLoadAs4D); /** \brief Defines spatial sorting for sorting by GDCM 2. Sorts by image position along image normal (distance from world origin). In cases of conflict, acquisition time is used as a secondary sort criterium. */ static bool GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2); /** \brief Copy information about files and DICOM tags from ITK's MetaDataDictionary and from the list of input files to the PropertyList of mitk::Image. \todo Tag copy must follow; image level will cause some additional files parsing, probably. */ static void CopyMetaDataToImageProperties( StringContainer filenames, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, Image* image); static void CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, Image* image); /** \brief Map between DICOM tags and MITK properties. Uses as a positive list for copying specified DICOM tags (from ITK's ImageIO) to MITK properties. ITK provides MetaDataDictionary entries of form "gggg|eeee" (g = group, e = element), e.g. "0028,0109" (Largest Pixel in Series), which we want to sort as dicom.series.largest_pixel_in_series". */ static const TagToPropertyMapType& GetDICOMTagsToMITKPropertyMap(); }; } #endif /* MITKDICOMSERIESREADER_H_ */ diff --git a/Core/Code/IO/mitkDicomSeriesReader.txx b/Core/Code/IO/mitkDicomSeriesReader.txx index a67ba660fc..80c95e54c9 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.txx +++ b/Core/Code/IO/mitkDicomSeriesReader.txx @@ -1,317 +1,489 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDICOMSERIESREADER_TXX_ #define MITKDICOMSERIESREADER_TXX_ #include #include #include +#include +#include +#include + +#include + namespace mitk { template -void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, UpdateCallBackMethod callback) +void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback) { const char* previousCLocale = setlocale(LC_NUMERIC, NULL); setlocale(LC_NUMERIC, "C"); std::locale previousCppLocale( std::cin.getloc() ); std::locale l( "C" ); std::cin.imbue(l); + + const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) + const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation try { mitk::Image::Pointer image = mitk::Image::New(); CallbackCommand *command = callback ? new CallbackCommand(callback) : 0; bool initialize_node = false; /* special case for Philips 3D+t ultrasound images */ if ( DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str()) ) { ReadPhilips3DDicom(filenames.front().c_str(), image); initialize_node = true; } else { /* default case: assume "normal" image blocks, possibly 3D+t */ bool canLoadAs4D(true); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); + + // need non-const access for map + gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); - std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, scanner.GetMappings(), sort, canLoadAs4D ); + std::list imageBlocks = SortIntoBlocksFor3DplusT( filenames, tagValueMappings, sort, canLoadAs4D ); unsigned int volume_count = imageBlocks.size(); + GantryTiltInformation tiltInfo; + + // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice possible + if ( !imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) + { + // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents + // check first and last position slice from tags, make some calculations to detect tilt + + std::string firstFilename(imageBlocks.front().front()); + // calculate from first and last slice to minimize rounding errors + std::string secondFilename(imageBlocks.front().back()); + + std::string imagePosition1( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImagePositionPatient ] ) ); + std::string imageOrientation( ConstCharStarToString( tagValueMappings[ firstFilename.c_str() ][ tagImageOrientation ] ) ); + std::string imagePosition2( ConstCharStarToString( tagValueMappings[secondFilename.c_str() ][ tagImagePositionPatient ] ) ); + + bool ignoredConversionError(-42); // hard to get here, no graceful way to react + Point3D origin1( DICOMStringToPoint3D( imagePosition1, ignoredConversionError ) ); + Point3D origin2( DICOMStringToPoint3D( imagePosition2, ignoredConversionError ) ); + + Vector3D right; right.Fill(0.0); + Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point + DICOMStringToOrientationVectors( imageOrientation, right, up, ignoredConversionError ); + + tiltInfo = GantryTiltInformation ( origin1, origin2, right, up, filenames.size()-1 ); + correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); + + MITK_DEBUG << "** Loading now: shear? " << tiltInfo.IsSheared(); + MITK_DEBUG << "** Loading now: normal tilt? " << tiltInfo.IsRegularGantryTilt(); + MITK_DEBUG << "** Loading now: perform tilt correction? " << correctTilt; + } + else + { + correctTilt = false; // we CANNOT do that + } + if (volume_count == 1 || !canLoadAs4D || !load4D) { - image = LoadDICOMByITK( imageBlocks.front() , command ); // load first 3D block + image = LoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, command ); // load first 3D block initialize_node = true; } else if (volume_count > 1) { // It is 3D+t! Read it and store into mitk image typedef itk::Image ImageType; typedef itk::ImageSeriesReader ReaderType; DcmIoType::Pointer io = DcmIoType::New(); typename ReaderType::Pointer reader = ReaderType::New(); reader->SetImageIO(io); reader->ReverseOrderOff(); if (command) { reader->AddObserver(itk::ProgressEvent(), command); } unsigned int act_volume = 1u; reader->SetFileNames(imageBlocks.front()); reader->Update(); - image->InitializeByItk(reader->GetOutput(), 1, volume_count); - image->SetImportVolume(reader->GetOutput()->GetBufferPointer(), 0u); + + typename ImageType::Pointer readVolume = reader->GetOutput(); + // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position + if (correctTilt) + { + readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); + } + + image->InitializeByItk( readVolume.GetPointer(), 1, volume_count); + image->SetImportVolume( readVolume->GetBufferPointer(), 0u); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); DicomSeriesReader::CopyMetaDataToImageProperties( imageBlocks, scanner.GetMappings(), io, image); MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", " << image->GetDimension(1) << ", " << image->GetDimension(2) << ", " << image->GetDimension(3) << "]"; #if (GDCM_MAJOR_VERSION == 2) && (GDCM_MINOR_VERSION < 1) && (GDCM_BUILD_VERSION < 15) // workaround for a GDCM 2 bug until version 2.0.15: // GDCM read spacing vector wrongly. Instead of "row spacing, column spacing", it misinterprets the DICOM tag as "column spacing, row spacing". // this is undone here, until we use a GDCM that has this issue fixed. // From the commit comments, GDCM 2.0.15 fixed the spacing interpretation with bug 2901181 // http://sourceforge.net/tracker/index.php?func=detail&aid=2901181&group_id=137895&atid=739587 Vector3D correctedImageSpacing = image->GetGeometry()->GetSpacing(); std::swap( correctedImageSpacing[0], correctedImageSpacing[1] ); image->GetGeometry()->SetSpacing( correctedImageSpacing ); #endif MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", " << image->GetGeometry()->GetSpacing()[1] << ", " << image->GetGeometry()->GetSpacing()[2] << "]"; for (std::list::iterator df_it = ++imageBlocks.begin(); df_it != imageBlocks.end(); ++df_it) { reader->SetFileNames(*df_it); reader->Update(); - image->SetImportVolume(reader->GetOutput()->GetBufferPointer(), act_volume++); + readVolume = reader->GetOutput(); + + if (correctTilt) + { + readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); + } + + image->SetImportVolume(readVolume->GetBufferPointer(), act_volume++); } initialize_node = true; } + } - if (initialize_node) - { - // forward some image properties to node + if (initialize_node) + { + // forward some image properties to node node.GetPropertyList()->ConcatenatePropertyList( image->GetPropertyList(), true ); node.SetData( image ); setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); } } catch (std::exception& e) { // reset locale then throw up setlocale(LC_NUMERIC, previousCLocale); std::cin.imbue(previousCppLocale); throw e; } } template -Image::Pointer DicomSeriesReader::LoadDICOMByITK( const StringContainer& filenames, CallbackCommand* command ) +Image::Pointer DicomSeriesReader::LoadDICOMByITK( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo, CallbackCommand* command ) { /******** Normal Case, 3D (also for GDCM < 2 usable) ***************/ mitk::Image::Pointer image = mitk::Image::New(); typedef itk::Image ImageType; typedef itk::ImageSeriesReader ReaderType; DcmIoType::Pointer io = DcmIoType::New(); typename ReaderType::Pointer reader = ReaderType::New(); reader->SetImageIO(io); reader->ReverseOrderOff(); if (command) { reader->AddObserver(itk::ProgressEvent(), command); } reader->SetFileNames(filenames); reader->Update(); - image->InitializeByItk(reader->GetOutput()); - image->SetImportVolume(reader->GetOutput()->GetBufferPointer()); + typename ImageType::Pointer readVolume = reader->GetOutput(); + + // if we detected that the images are from a tilted gantry acquisition, we need to push some pixels into the right position + if (correctTilt) + { + readVolume = InPlaceFixUpTiltedGeometry( reader->GetOutput(), tiltInfo ); + } + + image->InitializeByItk(readVolume.GetPointer()); + image->SetImportVolume(readVolume->GetBufferPointer()); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); DicomSeriesReader::CopyMetaDataToImageProperties( filenames, scanner.GetMappings(), io, image); MITK_DEBUG << "Volume dimension: [" << image->GetDimension(0) << ", " << image->GetDimension(1) << ", " << image->GetDimension(2) << "]"; #if (GDCM_MAJOR_VERSION == 2) && (GDCM_MINOR_VERSION < 1) && (GDCM_BUILD_VERSION < 15) // workaround for a GDCM 2 bug until version 2.0.15: // GDCM read spacing vector wrongly. Instead of "row spacing, column spacing", it misinterprets the DICOM tag as "column spacing, row spacing". // this is undone here, until we use a GDCM that has this issue fixed. // From the commit comments, GDCM 2.0.15 fixed the spacing interpretation with bug 2901181 // http://sourceforge.net/tracker/index.php?func=detail&aid=2901181&group_id=137895&atid=739587 Vector3D correctedImageSpacing = image->GetGeometry()->GetSpacing(); std::swap( correctedImageSpacing[0], correctedImageSpacing[1] ); image->GetGeometry()->SetSpacing( correctedImageSpacing ); #endif MITK_DEBUG << "Volume spacing: [" << image->GetGeometry()->GetSpacing()[0] << ", " << image->GetGeometry()->GetSpacing()[1] << ", " << image->GetGeometry()->GetSpacing()[2] << "]"; return image; } void DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner& scanner) { const gdcm::Tag ippTag(0x0020,0x0032); //Image position (Patient) scanner.AddTag(ippTag); + + const gdcm::Tag tagImageOrientation(0x0020,0x0037); //Image orientation + scanner.AddTag(tagImageOrientation); // TODO what if tags don't exist? const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag( tagSliceLocation ); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag( tagInstanceNumber ); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag( tagSOPInstanceNumber ); scanner.Scan(filenames); // make available image position for each file } std::list DicomSeriesReader::SortIntoBlocksFor3DplusT( const StringContainer& presortedFilenames, const gdcm::Scanner::MappingType& tagValueMappings, bool /*sort*/, bool& canLoadAs4D ) { std::list imageBlocks; // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); std::string firstPosition; unsigned int numberOfBlocks(0); // number of 3D image blocks const gdcm::Tag ippTag(0x0020,0x0032); //Image position (Patient) // loop files to determine number of image blocks for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); fileIter != sorted_filenames.end(); ++fileIter) { gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find( fileIter->c_str() )->second; if(tagToValueMap.find(ippTag) == tagToValueMap.end()) { continue; } std::string position = tagToValueMap.find(ippTag)->second; MITK_DEBUG << " " << *fileIter << " at " << position; if (firstPosition.empty()) { firstPosition = position; } if ( position == firstPosition ) { ++numberOfBlocks; } else { break; // enough information to know the number of image blocks } } MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files // loop files to sort them into image blocks unsigned int numberOfExpectedSlices(0); for (unsigned int block = 0; block < numberOfBlocks; ++block) { StringContainer filesOfCurrentBlock; for ( StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; fileIter != sorted_filenames.end(); //fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts ) { filesOfCurrentBlock.push_back( *fileIter ); for (unsigned int b = 0; b < numberOfBlocks; ++b) { if (fileIter != sorted_filenames.end()) ++fileIter; } } imageBlocks.push_back(filesOfCurrentBlock); if (block == 0) { numberOfExpectedSlices = filesOfCurrentBlock.size(); } else { if (filesOfCurrentBlock.size() != numberOfExpectedSlices) { MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks << " image blocks of " << numberOfExpectedSlices << " images each. Block " << block << " got " << filesOfCurrentBlock.size() << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much as possible) canLoadAs4D = false; } } } return imageBlocks; } +template +typename ImageType::Pointer +DicomSeriesReader::InPlaceFixUpTiltedGeometry( ImageType* input, const GantryTiltInformation& tiltInfo ) +{ + typedef itk::ResampleImageFilter ResampleFilterType; + typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); + resampler->SetInput( input ); + + /* + Transform for a point is + - transform from actual position to index coordinates + - apply a shear that undoes the gantry tilt + - transform back into world coordinates + + Anybody who does this in a simpler way: don't forget to write up how and why your solution works + */ + typedef itk::AffineTransform< double, ImageType::ImageDimension > TransformType; + typename TransformType::Pointer transformShear = TransformType::New(); + + /** + What I think should be done here: + + - apply a shear and spacing correction to the image block that corrects the ITK reader's error + - ITK ignores the shear and loads slices into an orthogonal volume + - ITK calculates the spacing from the origin distance, which is more than the actual spacing with gantry tilt images + - to undo the effect + - we have calculated some information in tiltInfo: + - the shift in Y direction that is added with each additional slice is the most important information + - the Y-shift is calculated in mm world coordinates + - we apply a shearing transformation to the ITK-read image volume + - to do this locally, + - we transform the image volume back to origin and "normal" orientation by applying the inverse of its transform + - this should bring us into the image's "index coordinate" system + - we apply a shear with the Y-shift factor put into a unit transform at row 1, col 2 + - we probably would need the shift factor in index coordinates but we use mm, which appears strange, see below + - we transform the image volume back to its actual position + - presumably back from index to world coordinates + - we lastly apply modify the image spacing in z direction by replacing this number with the correctly calulcated inter-slice distance + + Here comes the unsolved PROBLEM: + - WHY is it correct to apply the shift factor in millimeters, when we assume we are in a index coordinate system? + - or why is it not millimeters but index coordinates? + - or what else is the wrong assumption here? + - This is a serious question to anybody very firm in transforms, ITK, etc. + - this is also a chocolate reward question. Provide a good explanation with corrected code as a git commit and get it. + */ + + // ScalarType factor = tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates() / input->GetSpacing()[1]; + ScalarType factor = tiltInfo.GetMatrixCoefficientForCorrectionInWorldCoordinates(); + // row 1, column 2 corrects shear in parallel to Y axis, proportional to distance in Z direction + transformShear->Shear( 1, 2, factor ); + + typename TransformType::Pointer imageIndexToWorld = TransformType::New(); + imageIndexToWorld->SetOffset( input->GetOrigin().GetVectorFromOrigin() ); + imageIndexToWorld->SetMatrix( input->GetDirection() ); + + typename TransformType::Pointer imageWorldToIndex = TransformType::New(); + imageIndexToWorld->GetInverse( imageWorldToIndex ); + + typename TransformType::Pointer gantryTiltCorrection = TransformType::New(); + gantryTiltCorrection->Compose( imageWorldToIndex ); + gantryTiltCorrection->Compose( transformShear ); + gantryTiltCorrection->Compose( imageIndexToWorld ); + + resampler->SetTransform( gantryTiltCorrection ); + + typedef itk::LinearInterpolateImageFunction< ImageType, double > InterpolatorType; + typename InterpolatorType::Pointer interpolator = InterpolatorType::New(); + resampler->SetInterpolator( interpolator ); + /* + This would be the right place to invent a meaningful value for positions outside of the image. + For CT, HU -1000 might be meaningful, but a general solution seems not possible. Even for CT, + -1000 would only look natural for many not all images. + */ + resampler->SetDefaultPixelValue( std::numeric_limits::min() ); + + // adjust size in Y direction! (maybe just transform the outer last pixel to see how much space we would need + + resampler->SetOutputParametersFromImage( input ); // we basically need the same image again, just sheared + + typename ImageType::SizeType largerSize = resampler->GetSize(); // now the resampler already holds the input image's size. + largerSize[1] += tiltInfo.GetTiltCorrectedAdditionalSize(); + resampler->SetSize( largerSize ); + + resampler->Update(); + typename ImageType::Pointer result = resampler->GetOutput(); + + // ImageSeriesReader calculates z spacing as the distance between the first two origins. + // This is not correct in case of gantry tilt, so we set our calculated spacing. + typename ImageType::SpacingType correctedSpacing = result->GetSpacing(); + correctedSpacing[2] = tiltInfo.GetRealZSpacing(); + result->SetSpacing( correctedSpacing ); + + return result; +} + + } #endif diff --git a/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/Interactions/mitkPositionTracker.cpp b/Core/Code/Interactions/mitkPositionTracker.cpp index 525653b7ce..6eb5fbd743 100755 --- a/Core/Code/Interactions/mitkPositionTracker.cpp +++ b/Core/Code/Interactions/mitkPositionTracker.cpp @@ -1,84 +1,84 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include // for PointDataType #include mitk::PositionTracker::PositionTracker(const char * type, mitk::OperationActor* /*operationActor*/) : mitk::StateMachine(type) {} bool mitk::PositionTracker::ExecuteAction(Action* /*action*/, mitk::StateEvent const* stateEvent) { bool ok = false; const DisplayPositionEvent* displayPositionEvent = dynamic_cast(stateEvent->GetEvent()); mitk::DataNode::Pointer dtnode; mitk::DataStorage* ds = NULL; if (displayPositionEvent == NULL) return false; if (stateEvent->GetEvent()->GetSender()!=NULL) { ds = stateEvent->GetEvent()->GetSender()->GetDataStorage(); } else { itkWarningMacro(<<"StateEvent::GetSender()==NULL - setting timeInMS to 0"); return false; } if (ds == NULL) return false; // looking for desired point set dtnode = ds->GetNode(mitk::NodePredicateProperty::New("inputdevice", mitk::BoolProperty::New(true))); if (dtnode.IsNull()) return false; dtnode->SetIntProperty("BaseRendererMapperID", stateEvent->GetEvent()->GetSender()->GetMapperID()); mitk::PointSet* ps = dynamic_cast(dtnode->GetData()); if (ps == NULL) return false; int position = 0; if( ps->GetPointSet()->GetPoints()->IndexExists( position )) //first element { ps->GetPointSet()->GetPoints()->SetElement( position, displayPositionEvent->GetWorldPosition()); } else { - mitk::PointSet::PointDataType pointData = {position , false /*selected*/, mitk::PTUNDEFINED}; + mitk::PointSet::PointDataType pointData = {static_cast(position) , false /*selected*/, mitk::PTUNDEFINED}; ps->GetPointSet()->GetPointData()->InsertElement(position, pointData); ps->GetPointSet()->GetPoints()->InsertElement(position, displayPositionEvent->GetWorldPosition()); } ps->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return ok; } diff --git a/Core/Code/Testing/CMakeLists.txt b/Core/Code/Testing/CMakeLists.txt index 2eb2a32c77..cc799367ad 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_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/DICOMTesting/DICOMTesting.dox b/Core/Code/Testing/DICOMTesting/DICOMTesting.dox.h similarity index 94% rename from Core/Code/Testing/DICOMTesting/DICOMTesting.dox rename to Core/Code/Testing/DICOMTesting/DICOMTesting.dox.h index 15aea78acd..96e8bb67b3 100644 --- a/Core/Code/Testing/DICOMTesting/DICOMTesting.dox +++ b/Core/Code/Testing/DICOMTesting/DICOMTesting.dox.h @@ -1,144 +1,148 @@ /** \page DICOMTesting MITK DICOM testing \section DICOMTesting_introduction Introduction Reading DICOM data into mitk::Images is a complicated business since DICOM and MITK have very different ideas of images. DicomSeriesReader brings DICOM and MITK as close together as possible by offering methods to load DICOM CT and MR images into mitk::Images, optionally grouping slices to 3D+t images. Since there are so many possible sources for mistakes with any change to this loading process, testing the many assumptions implemented in DicomSeriesReader is worthwhile. This document describes what and how theses kind of tests are implemented. \section DICOMTesting_problem Problem description The task of loading DICOM files into mitk::Images is a challenge because of major differences in the way that DICOM and MITK represent images: - DICOM images - are mostly stored as one slice per file - do not describe how they can be arraged into image stacks with orthogonal axis - sometimes they cannot be arranged in image stacks as described above (e.g. tilted gantry) - mitk::Image (at least its mature areas) - represents image stacks with orthogonal axis (nothing like a tilted gantry) - have a concept of a fourth dimension (time steps) Because image processing applications based on MITK still need a way to load DICOM images into mitk::Image objects, MITK needs a way to build image stacks and this needs to be well tested. For more background information, see David Clunie's most valuable posts on comp.protocols.dicom, e.g.: - http://groups.google.com/group/comp.protocols.dicom/browse_thread/thread/6db91972e161f0d4/6e0304ac264a6eb5 - http://groups.google.com/group/comp.protocols.dicom/browse_thread/thread/e9bd1497bea3e66b/187a7dc8810613d2 - http://groups.google.com/group/comp.protocols.dicom/browse_thread/thread/5d80bb0b7fafcb81/cf96119e3b024ed8 - http://groups.google.com/group/comp.protocols.dicom/browse_thread/thread/4568635e083a3fba/e2a8ceec23032601 \section DICOMTesting_testidea Test principle The general idea for DICOM loaing tests is to run a set of known DICOM files through DicomSeriesReader's methods GetSeries() and LoadDicomSeries() to generate mitk::Images. These images are then compared to expected image properties, such as the number of individual mitk::Images, positions, orientations, spacings, etc. Stored expectations look like this (should be self-explanatory): \verbatim -- Image 1 Pixeltype: s BitsPerPixel: 16 Dimension: 4 Dimensions: 64 64 6 1 Geometry: Matrix: 5.25 0 0 0 5.2468 0.139598 0 -0.183222 3.99757 Offset: -159.672 -309.974 -69.0122 Center: 0 0 0 Translation: -159.672 -309.974 -69.0122 Scale: 1 1 1 Origin: -159.672 -309.974 -69.0122 Spacing: 5.25 5.25 4 TimeBounds: 0 1 -- Image 2 Pixeltype: s BitsPerPixel: 16 Dimension: 4 Dimensions: 64 64 41 1 Geometry: Matrix: 5.25 0 0 0 5.25 0 0 0 4 Offset: -160.672 -311.672 -285 Center: 0 0 0 Translation: -160.672 -311.672 -285 Scale: 1 1 1 Origin: -160.672 -311.672 -285 Spacing: 5.25 5.25 4 TimeBounds: 0 1 \endverbatim \section DICOMTesting_implementation Implementation \section DICOMTesting_implementation_utils Test helpers (applications and classes) Application DumpDICOMMitkImage Takes a list of DICOM images, loads them using TestDICOMLoading, then dumps information about the resulting mitk::Images to standard output. This application is helpful when defining reference data for tests. Application VerifyDICOMMitkImageDump Takes a list of DICOM images and loads them using TestDICOMLoading. Takes a dump file as generated by DumpDICOMMitkImage, parses it and compares it to actually generated mitk::Images. This application is used to implement the majority of test cases. They all load images, then verify the expected result structure. Class TestDICOMLoading \section PageDICOMLoadingTests_testcaseimplementation Test case implementation Individual test cases are stored in the MITK-Data repository and constructed by Core/Code/Testing/DICOMTesting/Testing/CMakeLists.txt The CMake file parses given directories for subdirectories containing specific test cases. Each directory contains two files: - File "input": lists DICOM files that should be loaded for this test case - File "expected.dump": contains the image properties in the above mentioned dump format Each test case is translated into a CTest test which evaluates the return value of a call to VerifyDICOMMitkImageDump. \section PageDICOMLoadingTests_testcases Implemented test cases -From test set TinyCTAbdomen (see description.txt in this directory for details on test images): +From test set \b TinyCTAbdomen (see description.txt in this directory for details on test images): - singleslice : just a single slice (should work and contain meaningful spacing) - two_slices : two slices, spacing should be calculated correctly - all : load a "normal" series as a single 3D block - 3D_and_T : load a small set of slices with multiple time-steps - - diff_orientation : load a set of files containing two differently oriented image blocks + - diff_orientation : load a set of files containing two differently oriented image blocks; at least two images (110,111) have minor errors in small decimal digits - diff_orientation_gaps : load a set of files containing two differently oriented image blocks, each missing slices, so blocks must be split - diff_spacing : load a set of files containint two set of slices with different spacings - gap : load slices that cannot form a single 3D block, because single slices are missing - gaps : slices missing in differnt locations, so multiple splits needed - unsorted_gaps : as before, just file names are unsorted - single_negative_spacing : from reported bug related to single MR images with misleading spacing information - tilted_gantry : slice origins do not align along first slice normal (happens with tilted gantries) - interleaved : two volumes of slices with origins along the same line. The volumes' slices interleave in their border region. This test is meant to correctly sort apart the two blocks instead of generating many two-slices groups in the interleaved region. +From test set \b TiltHead (see description.txt in this directory for details on test images): + + - head_ct_tilt : load a series with gantry tilt and check its orientation + \section DICOMTesting_othertests Specific other tests This list is meant to provide an up-to-date list of all implemented DICOM loading tests. If you ever find this outdated, please update it or make the persons who invalidated the list update it. mitkDICOMTestingSanityTest_* These tests implement basic testing of the implemented helper classes. The tests use DicomSeriesReader to load a number of DICOM image. They verify: - DicomSeriesReader recognizes all input files as DICOM images - DicomSeriesReader generates a number of mitk::Images from the DICOM images - the number of mitk::Images matches a number given on the command line or CTest's add_test() - helper methods in class TestDICOMLoading make minimal sense (comparison of an image dump to itself must be valid) */ diff --git a/Core/Code/Testing/DICOMTesting/Testing/CMakeLists.txt b/Core/Code/Testing/DICOMTesting/Testing/CMakeLists.txt index 50def10d0c..5a6abc3527 100644 --- a/Core/Code/Testing/DICOMTesting/Testing/CMakeLists.txt +++ b/Core/Code/Testing/DICOMTesting/Testing/CMakeLists.txt @@ -1,67 +1,77 @@ MITK_CREATE_MODULE_TESTS(LABELS MITK-Core) include(mitkFunctionAddTestLabel) # verify minimum expectations: # files are recognized as DICOM # loading files results in a given number of images mitkAddCustomModuleTest(mitkDICOMTestingSanityTest_NoFiles mitkDICOMTestingSanityTest 0) mitkAddCustomModuleTest(mitkDICOMTestingSanityTest_CTImage mitkDICOMTestingSanityTest 1 ${MITK_DATA_DIR}/spacing-ok-ct.dcm) mitkAddCustomModuleTest(mitkDICOMTestingSanityTest_MRImage mitkDICOMTestingSanityTest 1 ${MITK_DATA_DIR}/spacing-ok-mr.dcm) mitkAddCustomModuleTest(mitkDICOMTestingSanityTest_SCImage mitkDICOMTestingSanityTest 1 ${MITK_DATA_DIR}/spacing-ok-sc.dcm) mitkAddCustomModuleTest(mitkDICOMTestingSanityTest_DefectImage mitkDICOMTestingSanityTest 0 ${MITK_DATA_DIR}/spacing-ok-sc-no2032.dcm) #see bug 8108 if(NOT APPLE) set(VERIFY_DUMP_CMD ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/VerifyDICOMMitkImageDump) set(CT_ABDOMEN_DIR ${MITK_DATA_DIR}/TinyCTAbdomen) +set(CT_TILT_DIR ${MITK_DATA_DIR}/TiltHead) # these variables could be passed as parameters to a generic test creation function -set(CURRENT_DATASET_DIR ${CT_ABDOMEN_DIR}) set(TESTS_DIR Tests) set(INPUT_LISTNAME input) set(EXPECTED_DUMP expected.dump) -# find all test input lists -file(GLOB_RECURSE allInputs ${CURRENT_DATASET_DIR}/${TESTS_DIR}/*/${INPUT_LISTNAME}) - -function(expectFileExists filename) - if(NOT EXISTS ${filename}) - message(SEND_ERROR "Test case expected file ${filename} which does not exist! Please fix your CMake code or file layout.") - endif(NOT EXISTS ${filename}) -endfunction(expectFileExists) - -foreach(input ${allInputs}) - # extract only the name of the directory of this very test case - string(REGEX REPLACE ".*${TESTS_DIR}/([^/]+)/.*" "\\1" input ${input}) - set(inputfilelist "${CURRENT_DATASET_DIR}/${TESTS_DIR}/${input}/${INPUT_LISTNAME}") - set(expecteddump "${CURRENT_DATASET_DIR}/${TESTS_DIR}/${input}/${EXPECTED_DUMP}") - set(testname "DICOM_Load_${input}") - - message(STATUS "DICOM loading test case '${input}'") - expectFileExists(${inputfilelist}) - expectFileExists(${expecteddump}) - - # TODO provide error messages if input not valid - - set(dicomFiles) # clear list - # read list of file names from file "input" - file(STRINGS ${inputfilelist} rawDicomFiles) - foreach(raw ${rawDicomFiles}) - # prepend each file with an abosolute path - set(fileWithFullPath ${CURRENT_DATASET_DIR}/${raw}) - list(APPEND dicomFiles ${fileWithFullPath}) - endforeach(raw ${rawDicomFiles}) - - #message(STATUS " Load ${dicomFiles}") - add_test(${testname} - ${VERIFY_DUMP_CMD} ${expecteddump} ${dicomFiles}) - mitkFunctionAddTestLabel(${testname}) - -endforeach(input allInputs) -endif() +function(AddDicomTestsFromDataRepository CURRENT_DATASET_DIR TESTS_DIR INPUT_LISTNAME EXPECTED_DUMP) + + # find all test input lists + file(GLOB_RECURSE allInputs ${CURRENT_DATASET_DIR}/${TESTS_DIR}/*/${INPUT_LISTNAME}) + + function(expectFileExists filename) + if(NOT EXISTS ${filename}) + message(SEND_ERROR "Test case expected file ${filename} which does not exist! Please fix your CMake code or file layout.") + endif(NOT EXISTS ${filename}) + endfunction(expectFileExists) + + foreach(input ${allInputs}) + # extract only the name of the directory of this very test case + string(REGEX REPLACE ".*${TESTS_DIR}/([^/]+)/.*" "\\1" input ${input}) + set(inputfilelist "${CURRENT_DATASET_DIR}/${TESTS_DIR}/${input}/${INPUT_LISTNAME}") + set(expecteddump "${CURRENT_DATASET_DIR}/${TESTS_DIR}/${input}/${EXPECTED_DUMP}") + set(testname "DICOM_Load_${input}") + + message(STATUS "DICOM loading test case '${input}'") + expectFileExists(${inputfilelist}) + expectFileExists(${expecteddump}) + + # TODO provide error messages if input not valid + + set(dicomFiles) # clear list + # read list of file names from file "input" + file(STRINGS ${inputfilelist} rawDicomFiles) + foreach(raw ${rawDicomFiles}) + # prepend each file with an abosolute path + set(fileWithFullPath ${CURRENT_DATASET_DIR}/${raw}) + list(APPEND dicomFiles ${fileWithFullPath}) + endforeach(raw ${rawDicomFiles}) + + #message(STATUS " Load ${dicomFiles}") + add_test(${testname} + ${VERIFY_DUMP_CMD} ${expecteddump} ${dicomFiles}) + mitkFunctionAddTestLabel(${testname}) + + endforeach(input allInputs) + +endfunction(AddDicomTestsFromDataRepository CURRENT_DATASET_DIR TESTS_DIR INPUT_LISTNAME EXPECTED_DUMP) + + +AddDicomTestsFromDataRepository(${CT_ABDOMEN_DIR} ${TESTS_DIR} ${INPUT_LISTNAME} ${EXPECTED_DUMP}) +AddDicomTestsFromDataRepository(${CT_TILT_DIR} ${TESTS_DIR} ${INPUT_LISTNAME} ${EXPECTED_DUMP}) + + +endif() # apple diff --git a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp index 691d34c4bd..b74a1c613b 100644 --- a/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp +++ b/Core/Code/Testing/DICOMTesting/mitkTestDICOMLoading.cpp @@ -1,432 +1,432 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "mitkTestDICOMLoading.h" #include mitk::TestDICOMLoading::TestDICOMLoading() :m_PreviousCLocale(NULL) { } void mitk::TestDICOMLoading::SetDefaultLocale() { // remember old locale only once if (m_PreviousCLocale == NULL) { m_PreviousCLocale = setlocale(LC_NUMERIC, NULL); // set to "C" setlocale(LC_NUMERIC, "C"); m_PreviousCppLocale = std::cin.getloc(); std::locale l( "C" ); std::cin.imbue(l); std::cout.imbue(l); } } void mitk::TestDICOMLoading::ResetUserLocale() { if (m_PreviousCLocale) { setlocale(LC_NUMERIC, m_PreviousCLocale); std::cin.imbue(m_PreviousCppLocale); std::cout.imbue(m_PreviousCppLocale); m_PreviousCLocale = NULL; } } mitk::TestDICOMLoading::ImageList mitk::TestDICOMLoading::LoadFiles( const StringContainer& files ) { for (StringContainer::const_iterator iter = files.begin(); iter != files.end(); ++iter) { MITK_DEBUG << "File " << *iter; } ImageList result; - DicomSeriesReader::UidFileNamesMap seriesInFiles = DicomSeriesReader::GetSeries( files ); + DicomSeriesReader::UidFileNamesMap seriesInFiles = DicomSeriesReader::GetSeries( files, true ); // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) for (DicomSeriesReader::UidFileNamesMap::const_iterator seriesIter = seriesInFiles.begin(); seriesIter != seriesInFiles.end(); ++seriesIter) { StringContainer files = seriesIter->second; DataNode::Pointer node = DicomSeriesReader::LoadDicomSeries( files ); if (node.IsNotNull()) { Image::Pointer image = dynamic_cast( node->GetData() ); result.push_back( image ); } else { } } return result; } std::string mitk::TestDICOMLoading::TypeIDToString(const std::type_info& ti) { if (ti == typeid(unsigned char)) return "UCHAR"; else if (ti == typeid(char)) return "CHAR"; else if (ti == typeid(unsigned short)) return "USHORT"; else if (ti == typeid(short)) return "SHORT"; else if (ti == typeid(unsigned int)) return "UINT"; else if (ti == typeid(int)) return "INT"; else if (ti == typeid(long unsigned int)) return "ULONG"; else if (ti == typeid(long int)) return "LONG"; else if (ti == typeid(float)) return "FLOAT"; else if (ti == typeid(double)) return "DOUBLE"; else return "UNKNOWN"; } // add a line to stringstream result (see DumpImageInformation #define DumpLine(field, data) DumpILine(0, field, data) // add an indented(!) line to stringstream result (see DumpImageInformation #define DumpILine(indent, field, data) \ { \ std::string DumpLine_INDENT; DumpLine_INDENT.resize(indent, ' ' ); \ result << DumpLine_INDENT << field << ": " << data << "\n"; \ } std::string mitk::TestDICOMLoading::DumpImageInformation( const Image* image ) { std::stringstream result; if (image == NULL) return result.str(); SetDefaultLocale(); // basic image data DumpLine( "Pixeltype", TypeIDToString( (image->GetPixelType().GetTypeId()) )); DumpLine( "BitsPerPixel", image->GetPixelType().GetBpe() ); DumpLine( "Dimension", image->GetDimension() ); result << "Dimensions: "; for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) result << image->GetDimension(dim) << " "; result << "\n"; // geometry data result << "Geometry: \n"; Geometry3D* geometry = image->GetGeometry(); if (geometry) { AffineTransform3D* transform = geometry->GetIndexToWorldTransform(); if (transform) { result << " " << "Matrix: "; const AffineTransform3D::MatrixType& matrix = transform->GetMatrix(); for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) result << matrix[i][j] << " "; result << "\n"; result << " " << "Offset: "; const AffineTransform3D::OutputVectorType& offset = transform->GetOffset(); for (unsigned int i = 0; i < 3; ++i) result << offset[i] << " "; result << "\n"; result << " " << "Center: "; const AffineTransform3D::InputPointType& center = transform->GetCenter(); for (unsigned int i = 0; i < 3; ++i) result << center[i] << " "; result << "\n"; result << " " << "Translation: "; const AffineTransform3D::OutputVectorType& translation = transform->GetTranslation(); for (unsigned int i = 0; i < 3; ++i) result << translation[i] << " "; result << "\n"; result << " " << "Scale: "; const double* scale = transform->GetScale(); for (unsigned int i = 0; i < 3; ++i) result << scale[i] << " "; result << "\n"; result << " " << "Origin: "; const Point3D& origin = geometry->GetOrigin(); for (unsigned int i = 0; i < 3; ++i) result << origin[i] << " "; result << "\n"; result << " " << "Spacing: "; const Vector3D& spacing = geometry->GetSpacing(); for (unsigned int i = 0; i < 3; ++i) result << spacing[i] << " "; result << "\n"; result << " " << "TimeBounds: "; const TimeBounds timeBounds = geometry->GetTimeBounds(); for (unsigned int i = 0; i < 2; ++i) result << timeBounds[i] << " "; result << "\n"; } } ResetUserLocale(); return result.str(); } std::string mitk::TestDICOMLoading::trim(const std::string& pString, const std::string& pWhitespace) { const size_t beginStr = pString.find_first_not_of(pWhitespace); if (beginStr == std::string::npos) { // no content return ""; } const size_t endStr = pString.find_last_not_of(pWhitespace); const size_t range = endStr - beginStr + 1; return pString.substr(beginStr, range); } std::string mitk::TestDICOMLoading::reduce(const std::string& pString, const std::string& pFill, const std::string& pWhitespace) { // trim first std::string result(trim(pString, pWhitespace)); // replace sub ranges size_t beginSpace = result.find_first_of(pWhitespace); while (beginSpace != std::string::npos) { const size_t endSpace = result.find_first_not_of(pWhitespace, beginSpace); const size_t range = endSpace - beginSpace; result.replace(beginSpace, range, pFill); const size_t newStart = beginSpace + pFill.length(); beginSpace = result.find_first_of(pWhitespace, newStart); } return result; } bool mitk::TestDICOMLoading::CompareSpacedValueFields( const std::string& reference, const std::string& test, double /*eps*/ ) { bool result(true); // tokenize string, compare each token, if possible by float comparison std::stringstream referenceStream(reduce(reference)); std::stringstream testStream(reduce(test)); std::string refToken; std::string testToken; while ( std::getline( referenceStream, refToken, ' ' ) && std::getline ( testStream, testToken, ' ' ) ) { float refNumber; float testNumber; if ( this->StringToNumber(refToken, refNumber) ) { MITK_DEBUG << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << refToken << "'" << " value " << refNumber; if ( this->StringToNumber(testToken, testNumber) ) { result &= ( fabs(refNumber - testNumber) < mitk::eps ); } else { MITK_ERROR << refNumber << " cannot be compared to '" << testToken << "'"; } } else { MITK_DEBUG << "Token '" << refToken << "'" << " handled as string"; result &= refToken == testToken; } } if ( std::getline( referenceStream, refToken, ' ' ) ) { MITK_ERROR << "Reference string still had values when test string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } else if ( std::getline( testStream, testToken, ' ' ) ) { MITK_ERROR << "Test string still had values when reference string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } return result; } bool mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referenceDump, const std::string& testDump ) { KeyValueMap reference = ParseDump(referenceDump); KeyValueMap test = ParseDump(testDump); bool testResult(true); // verify all expected values for (KeyValueMap::const_iterator refIter = reference.begin(); refIter != reference.end(); ++refIter) { const std::string& refKey = refIter->first; const std::string& refValue = refIter->second; if ( test.find(refKey) != test.end() ) { const std::string& testValue = test[refKey]; bool thisTestResult = CompareSpacedValueFields( refValue, testValue ); testResult &= thisTestResult; MITK_DEBUG << refKey << ": '" << refValue << "' == '" << testValue << "' ? " << (thisTestResult?"YES":"NO"); } else { MITK_ERROR << "Reference dump contains a key'" << refKey << "' (value '" << refValue << "')." ; MITK_ERROR << "This key is expected to be generated for tests (but was not). Most probably you need to update your test data."; return false; } } // now check test dump does not contain any additional keys for (KeyValueMap::const_iterator testIter = test.begin(); testIter != test.end(); ++testIter) { const std::string& key = testIter->first; const std::string& value = testIter->second; if ( reference.find(key) == reference.end() ) { MITK_ERROR << "Test dump contains an unexpected key'" << key << "' (value '" << value << "')." ; MITK_ERROR << "This key is not expected. Most probably you need to update your test data."; return false; } } return testResult; } mitk::TestDICOMLoading::KeyValueMap mitk::TestDICOMLoading::ParseDump( const std::string& dump ) { KeyValueMap parsedResult; std::string shredder(dump); std::stack surroundingKeys; std::stack expectedIndents; expectedIndents.push(0); while (true) { std::string::size_type newLinePos = shredder.find( '\n' ); if (newLinePos == std::string::npos || newLinePos == 0) break; std::string line = shredder.substr( 0, newLinePos ); shredder = shredder.erase( 0, newLinePos+1 ); std::string::size_type keyPosition = line.find_first_not_of( ' ' ); std::string::size_type colonPosition = line.find( ':' ); std::string key = line.substr(keyPosition, colonPosition - keyPosition); std::string::size_type firstSpacePosition = key.find_first_of(" "); if (firstSpacePosition != std::string::npos) { key.erase(firstSpacePosition); } if ( keyPosition > expectedIndents.top() ) { // more indent than before expectedIndents.push(keyPosition); } else if (keyPosition == expectedIndents.top() ) { if (!surroundingKeys.empty()) { surroundingKeys.pop(); // last of same length } } else { // less indent than before do expectedIndents.pop(); while (expectedIndents.top() != keyPosition); // unwind until current indent is found } if (!surroundingKeys.empty()) { key = surroundingKeys.top() + "." + key; // construct current key name } surroundingKeys.push(key); // this is the new embracing key std::string value = line.substr(colonPosition+1); MITK_DEBUG << " Key: '" << key << "' value '" << value << "'" ; parsedResult[key] = value; // store parsing result } return parsedResult; } diff --git a/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp b/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp index 9462c9c97c..86dd838e3c 100644 --- a/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp +++ b/Core/Code/Testing/mitkDicomSeriesReaderTest.cpp @@ -1,162 +1,162 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkDicomSeriesReader.h" #include "mitkProperties.h" static std::map > GetTagInformationFromFile(mitk::DicomSeriesReader::StringContainer files) { gdcm::Scanner scanner; std::map > tagInformations; const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag( tagSliceLocation ); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag( tagInstanceNumber ); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag( tagSOPInstanceNumber ); //unsigned int slice(0); scanner.Scan(files); // return const_cast(scanner.GetMappings()); gdcm::Scanner::MappingType& tagValueMappings = const_cast(scanner.GetMappings()); for(std::vector::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter) { std::map tags; tags.insert(std::pair (tagSliceLocation, tagValueMappings[fIter->c_str()][tagSliceLocation])); tags.insert(std::pair (tagInstanceNumber, tagValueMappings[fIter->c_str()][tagInstanceNumber])); tags.insert(std::pair (tagSOPInstanceNumber, tagValueMappings[fIter->c_str()][tagSOPInstanceNumber])); tagInformations.insert(std::pair > (fIter->c_str(), tags)); } return tagInformations; } int mitkDicomSeriesReaderTest(int argc, char* argv[]) { // always start with this! MITK_TEST_BEGIN("DicomSeriesReader") if(argc < 1) { MITK_ERROR << "No directory given!"; return -1; } char* dir; dir = argv[1]; //check if DICOMTags have been set as property for mitk::Image - mitk::DicomSeriesReader::UidFileNamesMap seriesInFiles = mitk::DicomSeriesReader::GetSeries( dir ); + mitk::DicomSeriesReader::UidFileNamesMap seriesInFiles = mitk::DicomSeriesReader::GetSeries( dir, true ); std::list images; std::map fileMap; // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a standard topic??) for (mitk::DicomSeriesReader::UidFileNamesMap::const_iterator seriesIter = seriesInFiles.begin(); seriesIter != seriesInFiles.end(); ++seriesIter) { mitk::DicomSeriesReader::StringContainer files = seriesIter->second; mitk::DataNode::Pointer node = mitk::DicomSeriesReader::LoadDicomSeries( files ); MITK_TEST_CONDITION_REQUIRED(node.IsNotNull(),"Testing node") if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); images.push_back( image ); fileMap.insert( std::pair(image,files)); } } //Test if DICOM tags have been added correctly to the mitk::image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number for ( std::list::const_iterator imageIter = images.begin(); imageIter != images.end(); ++imageIter ) { const mitk::Image::Pointer image = *imageIter; //Get tag information for all dicom files of this image std::map > tagInformations = GetTagInformationFromFile((*fileMap.find(image)).second); mitk::StringLookupTableProperty* sliceLocation = dynamic_cast(image->GetProperty("dicom.image.0020.1041").GetPointer()); mitk::StringLookupTableProperty* instanceNumber = dynamic_cast(image->GetProperty("dicom.image.0020.0013").GetPointer()); mitk::StringLookupTableProperty* SOPInstnaceNumber = dynamic_cast(image->GetProperty("dicom.image.0008.0018").GetPointer()); mitk::StringLookupTableProperty* files = dynamic_cast(image->GetProperty("files").GetPointer()); MITK_TEST_CONDITION(sliceLocation != NULL, "Test if tag for slice location has been set to mitk image"); if(sliceLocation != NULL) { for(int i = 0; i < (int)sliceLocation->GetValue().GetLookupTable().size(); i++) { if(i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << sliceLocation->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSliceLocation] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(sliceLocation->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSliceLocation], "Test if value for slice location is correct"); } } } MITK_TEST_CONDITION(instanceNumber != NULL, "Test if tag for image instance number has been set to mitk image"); if(instanceNumber != NULL) { for(int i = 0; i < (int)instanceNumber->GetValue().GetLookupTable().size(); i++) { if(i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << instanceNumber->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagInstanceNumber] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(instanceNumber->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagInstanceNumber], "Test if value for instance number is correct"); } } } MITK_TEST_CONDITION(SOPInstnaceNumber != NULL, "Test if tag for SOP instance number has been set to mitk image"); if(SOPInstnaceNumber != NULL) { for(int i = 0; i < (int)SOPInstnaceNumber->GetValue().GetLookupTable().size(); i++) { if(i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << instanceNumber->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSOPInstanceNumber] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(SOPInstnaceNumber->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSOPInstanceNumber], "Test if value for SOP instance number is correct"); } } } } MITK_TEST_END() } 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/Code/Testing/mitkImageWriterTest.cpp b/Core/Code/Testing/mitkImageWriterTest.cpp index 859160a7b3..6faa40cf0f 100644 --- a/Core/Code/Testing/mitkImageWriterTest.cpp +++ b/Core/Code/Testing/mitkImageWriterTest.cpp @@ -1,200 +1,202 @@ /*=================================================================== 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 "mitkImageWriter.h" #include "mitkDataNodeFactory.h" #include "mitkTestingMacros.h" #include #include #ifdef WIN32 #include "process.h" +#else +#include #endif std::string AppendExtension(const std::string &filename, const char *extension) { std::string new_filename = filename; new_filename += extension; return new_filename; } /** * test for "ImageWriter". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ int mitkImageWriterTest(int argc , char* argv[]) { // always start with this! MITK_TEST_BEGIN("ImageWriter") // let's create an object of our class mitk::ImageWriter::Pointer myImageWriter = mitk::ImageWriter::New(); // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(myImageWriter.IsNotNull(),"Testing instantiation") // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! // load image MITK_TEST_CONDITION_REQUIRED(argc != 0, "File to load has been specified"); mitk::Image::Pointer image = NULL; mitk::DataNodeFactory::Pointer factory = mitk::DataNodeFactory::New(); try { MITK_TEST_OUTPUT(<< "Loading file: " << argv[1]); factory->SetFileName( argv[1] ); factory->Update(); MITK_TEST_CONDITION_REQUIRED(factory->GetNumberOfOutputs() > 0, "file loaded"); mitk::DataNode::Pointer node = factory->GetOutput( 0 ); image = dynamic_cast(node->GetData()); if(image.IsNull()) { std::cout<<"file "<< argv[1]<< "is not an image - test will not be applied."<SetInput(image); MITK_TEST_CONDITION_REQUIRED(myImageWriter->GetInput()==image,"test Set/GetInput()"); myImageWriter->SetFileName(filename); MITK_TEST_CONDITION_REQUIRED(!strcmp(myImageWriter->GetFileName(),filename.c_str()),"test Set/GetFileName()"); myImageWriter->SetFilePrefix("pref"); MITK_TEST_CONDITION_REQUIRED(!strcmp(myImageWriter->GetFilePrefix(),"pref"),"test Set/GetFilePrefix()"); myImageWriter->SetFilePattern("pattern"); MITK_TEST_CONDITION_REQUIRED(!strcmp(myImageWriter->GetFilePattern(),"pattern"),"test Set/GetFilePattern()"); // write ITK .mhd image (2D and 3D only) if( image->GetDimension() <= 3 ) { try { myImageWriter->SetExtension(".mhd"); myImageWriter->Update(); std::fstream fin, fin2; fin.open(AppendExtension(filename, ".mhd").c_str(),std::ios::in); std::string rawExtension = ".raw"; fin2.open(AppendExtension(filename, ".raw").c_str(),std::ios::in); if( !fin2.is_open() ) { rawExtension = ".zraw"; fin2.open(AppendExtension(filename, ".zraw").c_str(),std::ios::in); } MITK_TEST_CONDITION_REQUIRED(fin.is_open(),"Write .mhd file"); MITK_TEST_CONDITION_REQUIRED(fin2.is_open(),"Write .raw file"); fin.close(); fin2.close(); remove(AppendExtension(filename, ".mhd").c_str()); remove(AppendExtension(filename, rawExtension.c_str()).c_str()); } catch (...) { MITK_TEST_FAILED_MSG(<< "Exception during .mhd file writing"); } } //testing more component image writing as nrrd files try { myImageWriter->SetExtension(".nrrd"); myImageWriter->Update(); std::fstream fin; fin.open(AppendExtension(filename, ".nrrd").c_str(),std::ios::in); MITK_TEST_CONDITION_REQUIRED(fin.is_open(),"Write .nrrd file"); fin.close(); remove(AppendExtension(filename, ".nrrd").c_str()); } catch(...) { MITK_TEST_FAILED_MSG(<< "Exception during .nrrd file writing"); } // testing image writing as png files // test only for 2D images since the PNG is using a series writer in case a 3D image // should be saved -> the output name comparison would fail and also the use case // is very uncommon // write ITK .mhd image (2D and 3D only) if( image->GetDimension() == 2 ) { try { myImageWriter->SetExtension(".png"); myImageWriter->Update(); std::fstream fin; fin.open(AppendExtension(filename, ".png").c_str(),std::ios::in); MITK_TEST_CONDITION_REQUIRED(fin.is_open(),"Write .png file"); fin.close(); remove(AppendExtension(filename, ".png").c_str()); } catch(itk::ExceptionObject &e) { MITK_TEST_FAILED_MSG(<< "Exception during .png file writing: " << e.what() ); } } // test for exception handling try { MITK_TEST_FOR_EXCEPTION_BEGIN(itk::ExceptionObject) myImageWriter->SetInput(image); myImageWriter->SetFileName("/usr/bin"); myImageWriter->Update(); MITK_TEST_FOR_EXCEPTION_END(itk::ExceptionObject) } catch(...) { //this means that a wrong exception (i.e. no itk:Exception) has been thrown MITK_TEST_FAILED_MSG(<< "Wrong exception (i.e. no itk:Exception) caught during write"); } // always end with this! MITK_TEST_END(); } diff --git a/Core/Code/Testing/mitkRenderingTestHelper.cpp b/Core/Code/Testing/mitkRenderingTestHelper.cpp index 216c7cdd1b..e780afb803 100644 --- a/Core/Code/Testing/mitkRenderingTestHelper.cpp +++ b/Core/Code/Testing/mitkRenderingTestHelper.cpp @@ -1,145 +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 "mitkRenderingTestHelper.h" #include #include #include #include +#include #include #include #include #include mitkRenderingTestHelper::mitkRenderingTestHelper(int width, int height, int argc, char* argv[]) +:m_width(width), m_height(height) { // Global interaction must(!) be initialized mitk::GlobalInteraction::GetInstance()->Initialize("global"); m_DataStorage = mitk::StandaloneDataStorage::New(); m_RenderWindow = mitk::RenderWindow::New(); m_RenderWindow->GetRenderer()->SetDataStorage(m_DataStorage); m_RenderWindow->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard2D); this->GetVtkRenderWindow()->SetSize( width, height ); this->SetInputFileNames(argc, argv); } mitkRenderingTestHelper::~mitkRenderingTestHelper() { } void mitkRenderingTestHelper::Render() { //if the datastorage is initialized and at least 1 image is loaded render it if(m_DataStorage.IsNotNull() || m_DataStorage->GetAll()->Size() >= 1 ) { mitk::TimeSlicedGeometry::Pointer geo = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); + m_RenderWindow->GetRenderer()->Resize(m_width, m_height); mitk::RenderingManager::GetInstance()->InitializeViews( geo ); mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow->GetVtkRenderWindow()); } else { MITK_ERROR << "No images loaded in data storage!"; } //use this to actually show the iamge in a renderwindow // this->GetVtkRenderWindow()->Render(); // this->GetVtkRenderWindow()->GetInteractor()->Start(); } void mitkRenderingTestHelper::SetInputFileNames(int argc, char* argv[]) { // parse parameters for (int i = 1; i < argc; ++i) { //add everything to a list but -T and -V std::string tmp = argv[i]; if((tmp.compare("-T")) && (tmp.compare("-V"))) { this->AddToStorage(tmp); } else { break; } } } void mitkRenderingTestHelper::SetViewDirection(mitk::SliceNavigationController::ViewDirection viewDirection) { mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow())->GetSliceNavigationController()->SetDefaultViewDirection(viewDirection); } vtkRenderer* mitkRenderingTestHelper::GetVtkRenderer() { return m_RenderWindow->GetRenderer()->GetVtkRenderer(); } void mitkRenderingTestHelper::SetProperty(const char *propertyKey, mitk::BaseProperty* property ) { this->m_DataStorage->GetNode(mitk::NodePredicateDataType::New("Image"))->SetProperty(propertyKey, property); } vtkRenderWindow* mitkRenderingTestHelper::GetVtkRenderWindow() { return m_RenderWindow->GetVtkRenderWindow(); } //method to save a screenshot of the renderwindow (e.g. create a reference screenshot) void mitkRenderingTestHelper::SaveAsPNG(std::string fileName) { vtkSmartPointer renderer = this->GetVtkRenderer(); bool doubleBuffering( renderer->GetRenderWindow()->GetDoubleBuffer() ); renderer->GetRenderWindow()->DoubleBufferOff(); vtkSmartPointer magnifier = vtkSmartPointer::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(1.0); vtkSmartPointer fileWriter = vtkSmartPointer::New(); fileWriter->SetInput(magnifier->GetOutput()); fileWriter->SetFileName(fileName.c_str()); fileWriter->Write(); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void mitkRenderingTestHelper::AddToStorage(const std::string &filename) { mitk::DataNodeFactory::Pointer reader = mitk::DataNodeFactory::New(); try { reader->SetFileName( filename ); reader->Update(); if(reader->GetNumberOfOutputs()<1) { MITK_ERROR << "Could not find test data '" << filename << "'"; } mitk::DataNode::Pointer node = reader->GetOutput( 0 ); this->m_DataStorage->Add(node); } catch ( itk::ExceptionObject & e ) { MITK_ERROR << "Failed loading test data '" << filename << "': " << e.what(); } } diff --git a/Core/Code/Testing/mitkRenderingTestHelper.h b/Core/Code/Testing/mitkRenderingTestHelper.h index fdac395dd2..12d9a70a14 100644 --- a/Core/Code/Testing/mitkRenderingTestHelper.h +++ b/Core/Code/Testing/mitkRenderingTestHelper.h @@ -1,84 +1,87 @@ /*=================================================================== 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 mitkRenderingTestHelper_h #define mitkRenderingTestHelper_h #include #include #include #include class vtkRenderWindow; class vtkRenderer; class mitkRenderingTestHelper { public: /** @brief Generate a rendering test helper object including a render window of the size width * height (in pixel). @param argc Number of parameters. (here: Images) "Usage: [filename1 filenam2 -V referenceScreenshot (optional -T /directory/to/save/differenceImage)] @param argv Given parameters. **/ mitkRenderingTestHelper(int width, int height, int argc, char *argv[]); ~mitkRenderingTestHelper(); /** @brief Getter for the vtkRenderer. **/ vtkRenderer* GetVtkRenderer(); /** @brief Getter for the vtkRenderWindow which should be used to call vtkRegressionTestImage. **/ vtkRenderWindow* GetVtkRenderWindow(); /** @brief Method can be used to save a screenshot (e.g. reference screenshot as a .png file. @param fileName The filename of the new screenshot (including path). **/ void SaveAsPNG(std::string fileName); /** @brief This method set the property of the member datastorage @param property Set a property for each image in the datastorage m_DataStorage. **/ void SetProperty(const char *propertyKey, mitk::BaseProperty *property); /** @brief Set the view direction of the renderwindow (e.g. sagittal, coronal, transversal) **/ void SetViewDirection(mitk::SliceNavigationController::ViewDirection viewDirection); /** @brief Render everything into an mitkRenderWindow. Call SetViewDirection() and SetProperty() before this method. **/ void Render(); protected: /** @brief This method tries to load the given file into a member datastorage, in order to render it. @param fileName The filename of the file to be loaded (including path). **/ void AddToStorage(const std::string& filename); /** @brief This method tries to parse the given argv for files (e.g. images) and load them into a member datastorage, in order to render it. @param argc Number of parameters. @param argv Given parameters. **/ void SetInputFileNames(int argc, char *argv[]); mitk::RenderWindow::Pointer m_RenderWindow; //<< Contains the mitkRenderWindow into which the test renders the data mitk::DataStorage::Pointer m_DataStorage; //<< Contains the mitkDataStorage which contains the data to be rendered + int m_width; + int m_height; + }; #endif 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 1a9738f9fd..7ae420c5a7 100644 --- a/Core/Documentation/Doxygen/Concepts/Properties.dox +++ b/Core/Documentation/Doxygen/Concepts/Properties.dox @@ -1,227 +1,234 @@ /** -\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 distant lines - Shows all angles and lines of the contour (in 2D views) even if they are not on the view's current slice.
    • show distant points - Shows the points cross, even if the point is not in 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/Documentation/Doxygen/UserManual/ModuleList.dox b/Documentation/Doxygen/UserManual/ModuleList.dox index 0400ead77f..7e7c4e61f1 100644 --- a/Documentation/Doxygen/UserManual/ModuleList.dox +++ b/Documentation/Doxygen/UserManual/ModuleList.dox @@ -1,33 +1,34 @@ /** \page ModuleListPage MITK Modules \section ModuleListPageOverview Overview The modules and bundles provide much of the extended functionality of MITK. Each encapsulates a solution to a problem and associated features. This way one can easily assemble the necessary capabilites for a workflow without adding a lot of bloat, by combining modules as needed. The distinction between developer and end user use is for convenience only and mainly distinguishes which group a module is primarily aimed at. \section ModuleListPageEndUserModuleList List of Modules for End User Use \li \subpage org_basicimageprocessing \li \subpage org_datamanager \li \subpage org_diffusion \li \subpage IGTGeneralModulePage \li \subpage org_imagecropper \li \subpage org_imagenavigator \li \subpage org_measurementtoolbox \li \subpage org_moviemaker + \li \subpage org_meshdecimation \li \subpage org_pointsetinteraction \li \subpage RegistrationModuleOverviewPage \li \subpage org_segment \li \subpage org_volvis \section ModuleListPageDevModuleList List of Modules for Developer Use and Examples \li \subpage org_surfacematerialeditor \li \subpage org_toftutorial \li \subpage org_mitkexamples \li \subpage org_mitkexamplesopencv -*/ \ No newline at end of file +*/ diff --git a/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.h b/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.h index a9a59749c7..43cc93fc37 100644 --- a/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.h +++ b/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.h @@ -1,116 +1,125 @@ /*=================================================================== 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. ===================================================================*/ /*========================================================================= Program: Tensor ToolKit - TTK Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkReduceDirectionGradientsFilter.h $ Language: C++ Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ Version: $Revision: 68 $ Copyright (c) INRIA 2010. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _itk_ReduceDirectionGradientsFilter_h_ #define _itk_ReduceDirectionGradientsFilter_h_ #include #include #include namespace itk { template class ReduceDirectionGradientsFilter : public ImageToImageFilter, itk::VectorImage > { public: typedef ReduceDirectionGradientsFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< itk::VectorImage, itk::VectorImage > Superclass; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(ReduceDirectionGradientsFilter, ImageToImageFilter); typedef TInputScalarType InputScalarType; typedef itk::VectorImage InputImageType; typedef typename InputImageType::PixelType InputPixelType; typedef TOutputScalarType OutputScalarType; typedef itk::VectorImage OutputImageType; typedef typename OutputImageType::PixelType OutputPixelType; typedef OutputScalarType BaselineScalarType; typedef BaselineScalarType BaselinePixelType; typedef typename itk::Image BaselineImageType; typedef vnl_vector_fixed< double, 3 > GradientDirectionType; typedef itk::VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; + typedef std::vector IndicesVector; + typedef std::map BValueMap; + itkGetMacro(OriginalGradientDirections, GradientDirectionContainerType::Pointer) itkSetMacro(OriginalGradientDirections, GradientDirectionContainerType::Pointer) itkGetMacro(GradientDirections, GradientDirectionContainerType::Pointer) itkSetMacro(GradientDirections, GradientDirectionContainerType::Pointer) itkGetMacro(NumGradientDirections, int) itkSetMacro(NumGradientDirections, int) itkGetMacro(Iterations, unsigned long) itkSetMacro(Iterations, unsigned long) - std::vector< int > GetUsedGradientIndices(); + IndicesVector GetUsedGradientIndices(){return m_UsedGradientIndices;} + void SetOriginalBValueMap(BValueMap inp){m_OriginalBValueMap = inp;} + void SetShellSelectionBValueMap(BValueMap inp){m_InputBValueMap = inp;} + protected: ReduceDirectionGradientsFilter(); ~ReduceDirectionGradientsFilter() {}; void GenerateData(); double Costs(); GradientDirectionContainerType::Pointer m_GradientDirections; GradientDirectionContainerType::Pointer m_OriginalGradientDirections; - std::vector< int > m_UsedGradientIndices; - std::vector< int > m_UnUsedGradientIndices; - std::vector< int > m_BaselineImageIndices; + IndicesVector m_UsedGradientIndices; + IndicesVector m_UnUsedGradientIndices; + IndicesVector m_BaselineImageIndices; + + BValueMap m_OriginalBValueMap; + BValueMap m_InputBValueMap; int m_NumGradientDirections; unsigned long m_Iterations; }; } // end of namespace #ifndef ITK_MANUAL_INSTANTIATION #include "itkReduceDirectionGradientsFilter.txx" #endif #endif diff --git a/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.txx b/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.txx index 630f5c6e97..8564234509 100644 --- a/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.txx +++ b/Modules/DiffusionImaging/Algorithms/itkReduceDirectionGradientsFilter.txx @@ -1,207 +1,234 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*========================================================================= Program: Tensor ToolKit - TTK Module: $URL: svn://scm.gforge.inria.fr/svn/ttk/trunk/Algorithms/itkReduceDirectionGradientsFilter.txx $ Language: C++ Date: $Date: 2010-06-07 13:39:13 +0200 (Mo, 07 Jun 2010) $ Version: $Revision: 68 $ Copyright (c) INRIA 2010. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _itk_ReduceDirectionGradientsFilter_txx_ #define _itk_ReduceDirectionGradientsFilter_txx_ #endif #define _USE_MATH_DEFINES #include "itkReduceDirectionGradientsFilter.h" #include #include #include #include namespace itk { - template - ReduceDirectionGradientsFilter - ::ReduceDirectionGradientsFilter(): - m_Iterations(100000) +template +ReduceDirectionGradientsFilter +::ReduceDirectionGradientsFilter(): + m_Iterations(100000) +{ + this->SetNumberOfRequiredInputs( 1 ); +} + +template +double +ReduceDirectionGradientsFilter +::Costs() +{ + double costs = 0; + int c=0; + for (IndicesVector::iterator it = m_UsedGradientIndices.begin(); it!=m_UsedGradientIndices.end(); ++it) { - this->SetNumberOfRequiredInputs( 1 ); + for (IndicesVector::iterator it2 = m_UsedGradientIndices.begin()+c; it2!=m_UsedGradientIndices.end(); ++it2) + if (*it!=*it2) + { + vnl_vector_fixed v1 = m_OriginalGradientDirections->at(*it); + vnl_vector_fixed v2 = m_OriginalGradientDirections->at(*it2); + v1.normalize(); v2.normalize(); + double angle = acos(dot_product(v1,v2)); + if (angle>0) + costs += 1/angle; + } + c++; } - template - double - ReduceDirectionGradientsFilter - ::Costs() - { - double costs = 0; - int c=0; - for (std::vector::iterator it = m_UsedGradientIndices.begin(); it!=m_UsedGradientIndices.end(); ++it) - { - for (std::vector::iterator it2 = m_UsedGradientIndices.begin()+c; it2!=m_UsedGradientIndices.end(); ++it2) - if (*it!=*it2) - { - vnl_vector_fixed v1 = m_OriginalGradientDirections->at(*it); - vnl_vector_fixed v2 = m_OriginalGradientDirections->at(*it2); - v1.normalize(); v2.normalize(); - double angle = acos(dot_product(v1,v2)); - if (angle>0) - costs += 1/angle; - } - c++; - } + return costs; +} - return costs; +template +void +ReduceDirectionGradientsFilter +::GenerateData() +{ + srand(time(NULL)); + + if(m_InputBValueMap.empty()) + { + // if no InputMap is set, do for each shell the same + m_InputBValueMap = m_OriginalBValueMap; } - template - void - ReduceDirectionGradientsFilter - ::GenerateData() + if(m_InputBValueMap.find(0) != m_InputBValueMap.end()) { - srand(time(NULL)); + //delete b0 from input BValueMap + m_InputBValueMap.erase(0); + } + + BValueMap manipulatedMap = m_OriginalBValueMap; + + for(BValueMap::iterator it = m_InputBValueMap.begin(); it != m_InputBValueMap.end(); it++ ){ // initialize index vectors m_UsedGradientIndices.clear(); m_UnUsedGradientIndices.clear(); - if ( m_OriginalGradientDirections->Size()<= m_NumGradientDirections ) - m_NumGradientDirections = m_OriginalGradientDirections->Size(); + if ( it->second.size() <= m_NumGradientDirections ){ + itkWarningMacro( << "current directions: " << it->second.size() << " wanted directions: " << m_NumGradientDirections); + m_NumGradientDirections = it->second.size(); + continue; + } + int c=0; - int numB0 = 0; - for (int i=0; iSize(); i++) + + for (int i=0; isecond.size(); i++) { - vnl_vector_fixed< double, 3 > v = m_OriginalGradientDirections->at(i); - v.normalize(); - if (fabs(v[0])>0.001 || fabs(v[1])>0.001 || fabs(v[2])>0.001) - { - if (csecond.at(i)); + }else{ + m_UnUsedGradientIndices.push_back(it->second.at(i)); } - else - m_BaselineImageIndices.push_back(i); + c++; } - numB0 = m_BaselineImageIndices.size(); double costs = Costs(); MITK_INFO << "starting costs: " << costs; - m_Iterations = m_NumGradientDirections*m_OriginalGradientDirections->Size(); + m_Iterations = m_NumGradientDirections * (it->second.size()); for (unsigned long i=0; iSetSpacing( this->GetInput()->GetSpacing() ); // Set the image spacing - outImage->SetOrigin( this->GetInput()->GetOrigin() ); // Set the image origin - outImage->SetDirection( this->GetInput()->GetDirection() ); // Set the image direction - outImage->SetLargestPossibleRegion( this->GetInput()->GetLargestPossibleRegion()); - outImage->SetBufferedRegion( this->GetInput()->GetLargestPossibleRegion() ); - outImage->SetRequestedRegion( this->GetInput()->GetLargestPossibleRegion() ); - outImage->SetVectorLength(m_NumGradientDirections+numB0); - outImage->Allocate(); - - itk::ImageRegionIterator< OutputImageType > newIt(outImage, outImage->GetLargestPossibleRegion()); - newIt.GoToBegin(); - - typename InputImageType::Pointer inImage = const_cast(this->GetInput(0)); - itk::ImageRegionIterator< InputImageType > oldIt(inImage, inImage->GetLargestPossibleRegion()); - oldIt.GoToBegin(); - - // initial new value of voxel - OutputPixelType newVec; - newVec.SetSize(m_NumGradientDirections+numB0); - newVec.AllocateElements(m_NumGradientDirections+numB0); - - int ind1 = -1; - while(!newIt.IsAtEnd()) - { + manipulatedMap[it->first] = m_UsedGradientIndices; - // progress - typename OutputImageType::IndexType ind = newIt.GetIndex(); - ind1 = ind.m_Index[2]; + } - // init new vector with zeros - newVec.Fill(0.0); + int vecLength = 0 ; + for(BValueMap::iterator it = manipulatedMap.begin(); it != manipulatedMap.end(); it++) + { + vecLength += it->second.size(); + } - // the old voxel value with duplicates - InputPixelType oldVec = oldIt.Get(); - for(int i=0; iSetSpacing( this->GetInput()->GetSpacing() ); // Set the image spacing + outImage->SetOrigin( this->GetInput()->GetOrigin() ); // Set the image origin + outImage->SetDirection( this->GetInput()->GetDirection() ); // Set the image direction + outImage->SetLargestPossibleRegion( this->GetInput()->GetLargestPossibleRegion()); + outImage->SetBufferedRegion( this->GetInput()->GetLargestPossibleRegion() ); + outImage->SetRequestedRegion( this->GetInput()->GetLargestPossibleRegion() ); + outImage->SetVectorLength( vecLength ); // Set the vector length + outImage->Allocate(); - for(int i=0; i newIt(outImage, outImage->GetLargestPossibleRegion()); + newIt.GoToBegin(); - newIt.Set(newVec); + typename InputImageType::Pointer inImage = const_cast(this->GetInput(0)); + itk::ImageRegionIterator< InputImageType > oldIt(inImage, inImage->GetLargestPossibleRegion()); + oldIt.GoToBegin(); - ++newIt; - ++oldIt; - } + // initial new value of voxel + OutputPixelType newVec; + newVec.SetSize( vecLength ); + newVec.AllocateElements( vecLength ); - m_GradientDirections = GradientDirectionContainerType::New(); - for(int i=0; iInsertElement(i, m_OriginalGradientDirections->at(m_BaselineImageIndices.at(i))); + int ind1 = -1; + while(!newIt.IsAtEnd()) + { + + // progress + typename OutputImageType::IndexType ind = newIt.GetIndex(); + ind1 = ind.m_Index[2]; + + // init new vector with zeros + newVec.Fill(0.0); - for(int i=0; iInsertElement(i+numB0, m_OriginalGradientDirections->at(m_UsedGradientIndices.at(i))); + // the old voxel value with duplicates + InputPixelType oldVec = oldIt.Get(); - this->SetNumberOfRequiredOutputs (1); - this->SetNthOutput (0, outImage); + int index = 0; + for(BValueMap::iterator it = manipulatedMap.begin(); it != manipulatedMap.end(); it++) + { + for(int j = 0; j < it->second.size(); j++) + { + newVec[index] = oldVec[it->second.at(j)]; + index++; + } + } + + newIt.Set(newVec); + + ++newIt; + ++oldIt; } - template - std::vector< int > - ReduceDirectionGradientsFilter - ::GetUsedGradientIndices() + m_GradientDirections = GradientDirectionContainerType::New(); + int index = 0; + for(BValueMap::iterator it = manipulatedMap.begin(); it != manipulatedMap.end(); it++) { - return m_UsedGradientIndices; + for(int j = 0; j < it->second.size(); j++) + { + m_GradientDirections->InsertElement(index, m_OriginalGradientDirections->at(it->second.at(j))); + index++; + } } + + this->SetNumberOfRequiredOutputs (1); + this->SetNthOutput (0, outImage); +} + + + } // end of namespace diff --git a/Modules/DiffusionImaging/Algorithms/itkTractDensityImageFilter.cpp b/Modules/DiffusionImaging/Algorithms/itkTractDensityImageFilter.cpp index 1f42facfbb..1902bb1613 100644 --- a/Modules/DiffusionImaging/Algorithms/itkTractDensityImageFilter.cpp +++ b/Modules/DiffusionImaging/Algorithms/itkTractDensityImageFilter.cpp @@ -1,218 +1,223 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) -Copyright (c) German Cancer Research Center, +Coindex[1]right (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 "itkTractDensityImageFilter.h" // VTK #include #include #include // misc #include namespace itk{ - template< class OutputImageType > - TractDensityImageFilter< OutputImageType >::TractDensityImageFilter() +template< class OutputImageType > +TractDensityImageFilter< OutputImageType >::TractDensityImageFilter() : m_BinaryOutput(false) , m_InvertImage(false) , m_UpsamplingFactor(1) , m_InputImage(NULL) , m_UseImageGeometry(false) - { + , m_OutputAbsoluteValues(false) +{ - } +} - template< class OutputImageType > - TractDensityImageFilter< OutputImageType >::~TractDensityImageFilter() - { - } +template< class OutputImageType > +TractDensityImageFilter< OutputImageType >::~TractDensityImageFilter() +{ +} - template< class OutputImageType > - itk::Point TractDensityImageFilter< OutputImageType >::GetItkPoint(double point[3]) - { +template< class OutputImageType > +itk::Point TractDensityImageFilter< OutputImageType >::GetItkPoint(double point[3]) +{ itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; - } +} - template< class OutputImageType > - void TractDensityImageFilter< OutputImageType >::GenerateData() - { +template< class OutputImageType > +void TractDensityImageFilter< OutputImageType >::GenerateData() +{ // generate upsampled image mitk::Geometry3D::Pointer geometry = m_FiberBundle->GetGeometry(); typename OutputImageType::Pointer outImage = this->GetOutput(); // calculate new image parameters mitk::Vector3D newSpacing; mitk::Point3D newOrigin; itk::Matrix newDirection; ImageRegion<3> upsampledRegion; if (m_UseImageGeometry && !m_InputImage.IsNull()) { - newSpacing = m_InputImage->GetSpacing()/m_UpsamplingFactor; - upsampledRegion = m_InputImage->GetLargestPossibleRegion(); - newOrigin = m_InputImage->GetOrigin(); - typename OutputImageType::RegionType::SizeType size = upsampledRegion.GetSize(); - size[0] *= m_UpsamplingFactor; - size[1] *= m_UpsamplingFactor; - size[2] *= m_UpsamplingFactor; - upsampledRegion.SetSize(size); - newDirection = m_InputImage->GetDirection(); + MITK_INFO << "TractDensityImageFilter: using image geometry"; + newSpacing = m_InputImage->GetSpacing()/m_UpsamplingFactor; + upsampledRegion = m_InputImage->GetLargestPossibleRegion(); + newOrigin = m_InputImage->GetOrigin(); + typename OutputImageType::RegionType::SizeType size = upsampledRegion.GetSize(); + size[0] *= m_UpsamplingFactor; + size[1] *= m_UpsamplingFactor; + size[2] *= m_UpsamplingFactor; + upsampledRegion.SetSize(size); + newDirection = m_InputImage->GetDirection(); } else { - newSpacing = geometry->GetSpacing()/m_UpsamplingFactor; - newOrigin = geometry->GetOrigin(); - mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); - newOrigin[0] += bounds.GetElement(0); - newOrigin[1] += bounds.GetElement(2); - newOrigin[2] += bounds.GetElement(4); - - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - newDirection[j][i] = geometry->GetMatrixColumn(i)[j]; - upsampledRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); - upsampledRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); - upsampledRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); + MITK_INFO << "TractDensityImageFilter: using fiber bundle geometry"; + newSpacing = geometry->GetSpacing()/m_UpsamplingFactor; + newOrigin = geometry->GetOrigin(); + mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); + newOrigin[0] += bounds.GetElement(0); + newOrigin[1] += bounds.GetElement(2); + newOrigin[2] += bounds.GetElement(4); + + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + newDirection[j][i] = geometry->GetMatrixColumn(i)[j]; + upsampledRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); + upsampledRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); + upsampledRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); } typename OutputImageType::RegionType::SizeType upsampledSize = upsampledRegion.GetSize(); // apply new image parameters outImage->SetSpacing( newSpacing ); outImage->SetOrigin( newOrigin ); outImage->SetDirection( newDirection ); outImage->SetRegions( upsampledRegion ); outImage->Allocate(); + outImage->FillBuffer(0.0); int w = upsampledSize[0]; int h = upsampledSize[1]; int d = upsampledSize[2]; // set/initialize output OutPixelType* outImageBufferPointer = (OutPixelType*)outImage->GetBufferPointer(); - for (int i=0; iGetDeepCopy(); - m_FiberBundle->ResampleFibers(minSpacing/10); + m_FiberBundle->ResampleFibers(minSpacing); + MITK_INFO << "TractDensityImageFilter: starting image generation"; vtkSmartPointer fiberPolyData = m_FiberBundle->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); - int numFibers = m_FiberBundle->GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); - - // fill output image - for( int j=0; j vertex = GetItkPoint(fiberPolyData->GetPoint(points[j])); - itk::Index<3> index; - itk::ContinuousIndex contIndex; - outImage->TransformPhysicalPointToIndex(vertex, index); - outImage->TransformPhysicalPointToContinuousIndex(vertex, contIndex); - - float frac_x = contIndex[0] - index[0]; - float frac_y = contIndex[1] - index[1]; - float frac_z = contIndex[2] - index[2]; - - int px = index[0]; - if (frac_x<0) - { - px -= 1; - frac_x += 1; - } - - int py = index[1]; - if (frac_y<0) - { - py -= 1; - frac_y += 1; - } + vtkIdType numPoints(0); + vtkIdType* points(NULL); + vLines->GetNextCell ( numPoints, points ); - int pz = index[2]; - if (frac_z<0) + // fill output image + for( int j=0; j vertex = GetItkPoint(fiberPolyData->GetPoint(points[j])); + itk::Index<3> index; + itk::ContinuousIndex contIndex; + outImage->TransformPhysicalPointToIndex(vertex, index); + outImage->TransformPhysicalPointToContinuousIndex(vertex, contIndex); + + float frac_x = contIndex[0] - index[0]; + float frac_y = contIndex[1] - index[1]; + float frac_z = contIndex[2] - index[2]; + + if (frac_x<0) + { + index[0] -= 1; + frac_x += 1; + } + if (frac_y<0) + { + index[1] -= 1; + frac_y += 1; + } + if (frac_z<0) + { + index[2] -= 1; + frac_z += 1; + } + + frac_x = 1-frac_x; + frac_y = 1-frac_y; + frac_z = 1-frac_z; + + // int coordinates inside image? + if (index[0] < 0 || index[0] >= w-1) + continue; + if (index[1] < 0 || index[1] >= h-1) + continue; + if (index[2] < 0 || index[2] >= d-1) + continue; + + if (m_BinaryOutput) + { + outImageBufferPointer[( index[0] + w*(index[1] + h*index[2] ))] = 1; + outImageBufferPointer[( index[0] + w*(index[1]+1+ h*index[2] ))] = 1; + outImageBufferPointer[( index[0] + w*(index[1] + h*index[2]+h))] = 1; + outImageBufferPointer[( index[0] + w*(index[1]+1+ h*index[2]+h))] = 1; + outImageBufferPointer[( index[0]+1 + w*(index[1] + h*index[2] ))] = 1; + outImageBufferPointer[( index[0]+1 + w*(index[1] + h*index[2]+h))] = 1; + outImageBufferPointer[( index[0]+1 + w*(index[1]+1+ h*index[2] ))] = 1; + outImageBufferPointer[( index[0]+1 + w*(index[1]+1+ h*index[2]+h))] = 1; + } + else + { + outImageBufferPointer[( index[0] + w*(index[1] + h*index[2] ))] += ( frac_x)*( frac_y)*( frac_z); + outImageBufferPointer[( index[0] + w*(index[1]+1+ h*index[2] ))] += ( frac_x)*(1-frac_y)*( frac_z); + outImageBufferPointer[( index[0] + w*(index[1] + h*index[2]+h))] += ( frac_x)*( frac_y)*(1-frac_z); + outImageBufferPointer[( index[0] + w*(index[1]+1+ h*index[2]+h))] += ( frac_x)*(1-frac_y)*(1-frac_z); + outImageBufferPointer[( index[0]+1 + w*(index[1] + h*index[2] ))] += (1-frac_x)*( frac_y)*( frac_z); + outImageBufferPointer[( index[0]+1 + w*(index[1] + h*index[2]+h))] += (1-frac_x)*( frac_y)*(1-frac_z); + outImageBufferPointer[( index[0]+1 + w*(index[1]+1+ h*index[2] ))] += (1-frac_x)*(1-frac_y)*( frac_z); + outImageBufferPointer[( index[0]+1 + w*(index[1]+1+ h*index[2]+h))] += (1-frac_x)*(1-frac_y)*(1-frac_z); + } } - frac_x = 1-frac_x; - frac_y = 1-frac_y; - frac_z = 1-frac_z; - - // int coordinates inside image? - if (px < 0 || px >= w-1) - continue; - if (py < 0 || py >= h-1) - continue; - if (pz < 0 || pz >= d-1) - continue; - - if (m_BinaryOutput) - { - outImageBufferPointer[( px + w*(py + h*pz ))] = 1; - outImageBufferPointer[( px + w*(py+1+ h*pz ))] = 1; - outImageBufferPointer[( px + w*(py + h*pz+h))] = 1; - outImageBufferPointer[( px + w*(py+1+ h*pz+h))] = 1; - outImageBufferPointer[( px+1 + w*(py + h*pz ))] = 1; - outImageBufferPointer[( px+1 + w*(py + h*pz+h))] = 1; - outImageBufferPointer[( px+1 + w*(py+1+ h*pz ))] = 1; - outImageBufferPointer[( px+1 + w*(py+1+ h*pz+h))] = 1; - } - else - { - outImageBufferPointer[( px + w*(py + h*pz ))] += ( frac_x)*( frac_y)*( frac_z); - outImageBufferPointer[( px + w*(py+1+ h*pz ))] += ( frac_x)*(1-frac_y)*( frac_z); - outImageBufferPointer[( px + w*(py + h*pz+h))] += ( frac_x)*( frac_y)*(1-frac_z); - outImageBufferPointer[( px + w*(py+1+ h*pz+h))] += ( frac_x)*(1-frac_y)*(1-frac_z); - outImageBufferPointer[( px+1 + w*(py + h*pz ))] += (1-frac_x)*( frac_y)*( frac_z); - outImageBufferPointer[( px+1 + w*(py + h*pz+h))] += (1-frac_x)*( frac_y)*(1-frac_z); - outImageBufferPointer[( px+1 + w*(py+1+ h*pz ))] += (1-frac_x)*(1-frac_y)*( frac_z); - outImageBufferPointer[( px+1 + w*(py+1+ h*pz+h))] += (1-frac_x)*(1-frac_y)*(1-frac_z); - } - } } - if (!m_BinaryOutput) + + if (!m_OutputAbsoluteValues && !m_BinaryOutput) { - OutPixelType max = 0; - for (int i=0; i0) + MITK_INFO << "TractDensityImageFilter: max-normalizing output image"; + OutPixelType max = 0; for (int i=0; i0) + for (int i=0; i #include #include #include #include namespace itk{ template< class OutputImageType > class TractDensityImageFilter : public ImageSource< OutputImageType > { public: typedef TractDensityImageFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename OutputImageType::PixelType OutPixelType; - itkNewMacro(Self); - itkTypeMacro( TractDensityImageFilter, ImageSource ); + itkNewMacro(Self) + itkTypeMacro( TractDensityImageFilter, ImageSource ) /** Upsampling factor **/ - itkSetMacro( UpsamplingFactor, unsigned int); - itkGetMacro( UpsamplingFactor, unsigned int); + itkSetMacro( UpsamplingFactor, float) + itkGetMacro( UpsamplingFactor, float) /** Invert Image **/ - itkSetMacro( InvertImage, bool); - itkGetMacro( InvertImage, bool); + itkSetMacro( InvertImage, bool) + itkGetMacro( InvertImage, bool) /** Binary Output **/ - itkSetMacro( BinaryOutput, bool); - itkGetMacro( BinaryOutput, bool); + itkSetMacro( BinaryOutput, bool) + itkGetMacro( BinaryOutput, bool) + + /** Output absolute values of #fibers per voxel **/ + itkSetMacro( OutputAbsoluteValues, bool) + itkGetMacro( OutputAbsoluteValues, bool) /** Use input image geometry to initialize output image **/ - itkSetMacro( UseImageGeometry, bool); - itkGetMacro( UseImageGeometry, bool); + itkSetMacro( UseImageGeometry, bool) + itkGetMacro( UseImageGeometry, bool) - itkSetMacro( FiberBundle, mitk::FiberBundleX::Pointer); - itkSetMacro( InputImage, typename OutputImageType::Pointer); + itkSetMacro( FiberBundle, mitk::FiberBundleX::Pointer) + itkSetMacro( InputImage, typename OutputImageType::Pointer) void GenerateData(); protected: itk::Point GetItkPoint(double point[3]); TractDensityImageFilter(); virtual ~TractDensityImageFilter(); typename OutputImageType::Pointer m_InputImage; mitk::FiberBundleX::Pointer m_FiberBundle; - unsigned int m_UpsamplingFactor; + float m_UpsamplingFactor; bool m_InvertImage; bool m_BinaryOutput; bool m_UseImageGeometry; + bool m_OutputAbsoluteValues; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkTractDensityImageFilter.cpp" #endif #endif // __itkTractDensityImageFilter_h__ diff --git a/Modules/DiffusionImaging/Algorithms/itkTractsToFiberEndingsImageFilter.h b/Modules/DiffusionImaging/Algorithms/itkTractsToFiberEndingsImageFilter.h index b77f777506..385a2c7c91 100644 --- a/Modules/DiffusionImaging/Algorithms/itkTractsToFiberEndingsImageFilter.h +++ b/Modules/DiffusionImaging/Algorithms/itkTractsToFiberEndingsImageFilter.h @@ -1,79 +1,79 @@ /*=================================================================== 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. ===================================================================*/ #ifndef __itkTractsToFiberEndingsImageFilter_h__ #define __itkTractsToFiberEndingsImageFilter_h__ #include #include #include #include #include namespace itk{ template< class OutputImageType > class TractsToFiberEndingsImageFilter : public ImageSource< OutputImageType > { public: typedef TractsToFiberEndingsImageFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename OutputImageType::PixelType OutPixelType; - itkNewMacro(Self); - itkTypeMacro( TractsToFiberEndingsImageFilter, ImageSource ); + itkNewMacro(Self) + itkTypeMacro( TractsToFiberEndingsImageFilter, ImageSource ) /** Upsampling factor **/ - itkSetMacro( UpsamplingFactor, unsigned int); - itkGetMacro( UpsamplingFactor, unsigned int); + itkSetMacro( UpsamplingFactor, float) + itkGetMacro( UpsamplingFactor, float) /** Invert Image **/ - itkSetMacro( InvertImage, bool); - itkGetMacro( InvertImage, bool); + itkSetMacro( InvertImage, bool) + itkGetMacro( InvertImage, bool) - itkSetMacro( FiberBundle, mitk::FiberBundleX::Pointer); - itkSetMacro( InputImage, typename OutputImageType::Pointer); + itkSetMacro( FiberBundle, mitk::FiberBundleX::Pointer) + itkSetMacro( InputImage, typename OutputImageType::Pointer) /** Use input image geometry to initialize output image **/ - itkSetMacro( UseImageGeometry, bool); - itkGetMacro( UseImageGeometry, bool); + itkSetMacro( UseImageGeometry, bool) + itkGetMacro( UseImageGeometry, bool) void GenerateData(); protected: itk::Point GetItkPoint(double point[3]); TractsToFiberEndingsImageFilter(); virtual ~TractsToFiberEndingsImageFilter(); mitk::FiberBundleX::Pointer m_FiberBundle; - unsigned int m_UpsamplingFactor; + float m_UpsamplingFactor; bool m_InvertImage; bool m_UseImageGeometry; typename OutputImageType::Pointer m_InputImage; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkTractsToFiberEndingsImageFilter.cpp" #endif #endif // __itkTractsToFiberEndingsImageFilter_h__ diff --git a/Modules/DiffusionImaging/Algorithms/itkTractsToRgbaImageFilter.cpp b/Modules/DiffusionImaging/Algorithms/itkTractsToRgbaImageFilter.cpp index 128f5bb525..3440c4326e 100644 --- a/Modules/DiffusionImaging/Algorithms/itkTractsToRgbaImageFilter.cpp +++ b/Modules/DiffusionImaging/Algorithms/itkTractsToRgbaImageFilter.cpp @@ -1,285 +1,284 @@ /*=================================================================== 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 "itkTractsToRgbaImageFilter.h" // VTK #include #include #include // misc #include namespace itk{ template< class OutputImageType > TractsToRgbaImageFilter< OutputImageType >::TractsToRgbaImageFilter() - : m_BinaryOutput(false) - , m_InvertImage(false) + : m_InvertImage(false) , m_UpsamplingFactor(1) , m_InputImage(NULL) , m_UseImageGeometry(false) { } template< class OutputImageType > TractsToRgbaImageFilter< OutputImageType >::~TractsToRgbaImageFilter() { } template< class OutputImageType > itk::Point TractsToRgbaImageFilter< OutputImageType >::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } template< class OutputImageType > void TractsToRgbaImageFilter< OutputImageType >::GenerateData() { if(&typeid(OutPixelType) != &typeid(itk::RGBAPixel)) return; // generate upsampled image mitk::Geometry3D::Pointer geometry = m_FiberBundle->GetGeometry(); typename OutputImageType::Pointer outImage = this->GetOutput(); // calculate new image parameters mitk::Vector3D newSpacing; mitk::Point3D newOrigin; itk::Matrix newDirection; ImageRegion<3> upsampledRegion; if (m_UseImageGeometry && !m_InputImage.IsNull()) { newSpacing = m_InputImage->GetSpacing()/m_UpsamplingFactor; upsampledRegion = m_InputImage->GetLargestPossibleRegion(); newOrigin = m_InputImage->GetOrigin(); typename OutputImageType::RegionType::SizeType size = upsampledRegion.GetSize(); size[0] *= m_UpsamplingFactor; size[1] *= m_UpsamplingFactor; size[2] *= m_UpsamplingFactor; upsampledRegion.SetSize(size); newDirection = m_InputImage->GetDirection(); } else { newSpacing = geometry->GetSpacing()/m_UpsamplingFactor; newOrigin = geometry->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); newOrigin[0] += bounds.GetElement(0); newOrigin[1] += bounds.GetElement(2); newOrigin[2] += bounds.GetElement(4); for (int i=0; i<3; i++) for (int j=0; j<3; j++) newDirection[j][i] = geometry->GetMatrixColumn(i)[j]; upsampledRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); upsampledRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); upsampledRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); } typename OutputImageType::RegionType::SizeType upsampledSize = upsampledRegion.GetSize(); // apply new image parameters outImage->SetSpacing( newSpacing ); outImage->SetOrigin( newOrigin ); outImage->SetDirection( newDirection ); outImage->SetRegions( upsampledRegion ); outImage->Allocate(); int w = upsampledSize[0]; int h = upsampledSize[1]; int d = upsampledSize[2]; // set/initialize output unsigned char* outImageBufferPointer = (unsigned char*)outImage->GetBufferPointer(); float* buffer = new float[w*h*d*4]; for (int i=0; iGetDeepCopy(); - m_FiberBundle->ResampleFibers(minSpacing/10); + m_FiberBundle->ResampleFibers(minSpacing); vtkSmartPointer fiberPolyData = m_FiberBundle->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int numFibers = m_FiberBundle->GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); // calc directions (which are used as weights) std::list< itk::Point > rgbweights; std::list intensities; for( int j=0; j vertex = GetItkPoint(fiberPolyData->GetPoint(points[j])); itk::Point vertexPost = GetItkPoint(fiberPolyData->GetPoint(points[j+1])); itk::Point dir; dir[0] = fabs((vertexPost[0] - vertex[0]) * outImage->GetSpacing()[0]); dir[1] = fabs((vertexPost[1] - vertex[1]) * outImage->GetSpacing()[1]); dir[2] = fabs((vertexPost[2] - vertex[2]) * outImage->GetSpacing()[2]); rgbweights.push_back(dir); float intensity = sqrt(dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]); intensities.push_back(intensity); // last point gets same as previous one if(j==numPoints-2) { rgbweights.push_back(dir); intensities.push_back(intensity); } } // fill output image for( int j=0; j vertex = GetItkPoint(fiberPolyData->GetPoint(points[j])); itk::Index<3> index; itk::ContinuousIndex contIndex; outImage->TransformPhysicalPointToIndex(vertex, index); outImage->TransformPhysicalPointToContinuousIndex(vertex, contIndex); float frac_x = contIndex[0] - index[0]; float frac_y = contIndex[1] - index[1]; float frac_z = contIndex[2] - index[2]; int px = index[0]; if (frac_x<0) { px -= 1; frac_x += 1; } int py = index[1]; if (frac_y<0) { py -= 1; frac_y += 1; } int pz = index[2]; if (frac_z<0) { pz -= 1; frac_z += 1; } // int coordinates inside image? if (px < 0 || px >= w-1) continue; if (py < 0 || py >= h-1) continue; if (pz < 0 || pz >= d-1) continue; float scale = 100 * pow((float)m_UpsamplingFactor,3); itk::Point rgbweight = rgbweights.front(); rgbweights.pop_front(); float intweight = intensities.front(); intensities.pop_front(); // add to r-channel in output image buffer[0+4*( px + w*(py + h*pz ))] += (1-frac_x)*(1-frac_y)*(1-frac_z) * rgbweight[0] * scale; buffer[0+4*( px + w*(py+1+ h*pz ))] += (1-frac_x)*( frac_y)*(1-frac_z) * rgbweight[0] * scale; buffer[0+4*( px + w*(py + h*pz+h))] += (1-frac_x)*(1-frac_y)*( frac_z) * rgbweight[0] * scale; buffer[0+4*( px + w*(py+1+ h*pz+h))] += (1-frac_x)*( frac_y)*( frac_z) * rgbweight[0] * scale; buffer[0+4*( px+1 + w*(py + h*pz ))] += ( frac_x)*(1-frac_y)*(1-frac_z) * rgbweight[0] * scale; buffer[0+4*( px+1 + w*(py + h*pz+h))] += ( frac_x)*(1-frac_y)*( frac_z) * rgbweight[0] * scale; buffer[0+4*( px+1 + w*(py+1+ h*pz ))] += ( frac_x)*( frac_y)*(1-frac_z) * rgbweight[0] * scale; buffer[0+4*( px+1 + w*(py+1+ h*pz+h))] += ( frac_x)*( frac_y)*( frac_z) * rgbweight[0] * scale; // add to g-channel in output image buffer[1+4*( px + w*(py + h*pz ))] += (1-frac_x)*(1-frac_y)*(1-frac_z) * rgbweight[1] * scale; buffer[1+4*( px + w*(py+1+ h*pz ))] += (1-frac_x)*( frac_y)*(1-frac_z) * rgbweight[1] * scale; buffer[1+4*( px + w*(py + h*pz+h))] += (1-frac_x)*(1-frac_y)*( frac_z) * rgbweight[1] * scale; buffer[1+4*( px + w*(py+1+ h*pz+h))] += (1-frac_x)*( frac_y)*( frac_z) * rgbweight[1] * scale; buffer[1+4*( px+1 + w*(py + h*pz ))] += ( frac_x)*(1-frac_y)*(1-frac_z) * rgbweight[1] * scale; buffer[1+4*( px+1 + w*(py + h*pz+h))] += ( frac_x)*(1-frac_y)*( frac_z) * rgbweight[1] * scale; buffer[1+4*( px+1 + w*(py+1+ h*pz ))] += ( frac_x)*( frac_y)*(1-frac_z) * rgbweight[1] * scale; buffer[1+4*( px+1 + w*(py+1+ h*pz+h))] += ( frac_x)*( frac_y)*( frac_z) * rgbweight[1] * scale; // add to b-channel in output image buffer[2+4*( px + w*(py + h*pz ))] += (1-frac_x)*(1-frac_y)*(1-frac_z) * rgbweight[2] * scale; buffer[2+4*( px + w*(py+1+ h*pz ))] += (1-frac_x)*( frac_y)*(1-frac_z) * rgbweight[2] * scale; buffer[2+4*( px + w*(py + h*pz+h))] += (1-frac_x)*(1-frac_y)*( frac_z) * rgbweight[2] * scale; buffer[2+4*( px + w*(py+1+ h*pz+h))] += (1-frac_x)*( frac_y)*( frac_z) * rgbweight[2] * scale; buffer[2+4*( px+1 + w*(py + h*pz ))] += ( frac_x)*(1-frac_y)*(1-frac_z) * rgbweight[2] * scale; buffer[2+4*( px+1 + w*(py + h*pz+h))] += ( frac_x)*(1-frac_y)*( frac_z) * rgbweight[2] * scale; buffer[2+4*( px+1 + w*(py+1+ h*pz ))] += ( frac_x)*( frac_y)*(1-frac_z) * rgbweight[2] * scale; buffer[2+4*( px+1 + w*(py+1+ h*pz+h))] += ( frac_x)*( frac_y)*( frac_z) * rgbweight[2] * scale; // add to a-channel in output image buffer[3+4*( px + w*(py + h*pz ))] += (1-frac_x)*(1-frac_y)*(1-frac_z) * intweight * scale; buffer[3+4*( px + w*(py+1+ h*pz ))] += (1-frac_x)*( frac_y)*(1-frac_z) * intweight * scale; buffer[3+4*( px + w*(py + h*pz+h))] += (1-frac_x)*(1-frac_y)*( frac_z) * intweight * scale; buffer[3+4*( px + w*(py+1+ h*pz+h))] += (1-frac_x)*( frac_y)*( frac_z) * intweight * scale; buffer[3+4*( px+1 + w*(py + h*pz ))] += ( frac_x)*(1-frac_y)*(1-frac_z) * intweight * scale; buffer[3+4*( px+1 + w*(py + h*pz+h))] += ( frac_x)*(1-frac_y)*( frac_z) * intweight * scale; buffer[3+4*( px+1 + w*(py+1+ h*pz ))] += ( frac_x)*( frac_y)*(1-frac_z) * intweight * scale; buffer[3+4*( px+1 + w*(py+1+ h*pz+h))] += ( frac_x)*( frac_y)*( frac_z) * intweight * scale; } } float maxRgb = 0.000000001; float maxInt = 0.000000001; int numPix; numPix = w*h*d*4; // calc maxima for(int i=0; i maxRgb) maxRgb = buffer[i]; } else { if(buffer[i] > maxInt) maxInt = buffer[i]; } } // write output, normalized uchar 0..255 for(int i=0; i #include #include #include #include namespace itk{ template< class OutputImageType > class TractsToRgbaImageFilter : public ImageSource< OutputImageType > { public: typedef TractsToRgbaImageFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename OutputImageType::PixelType OutPixelType; typedef itk::Image InputImageType; - itkNewMacro(Self); - itkTypeMacro( TractsToRgbaImageFilter, ImageSource ); + itkNewMacro(Self) + itkTypeMacro( TractsToRgbaImageFilter, ImageSource ) /** Upsampling factor **/ - itkSetMacro( UpsamplingFactor, unsigned int); - itkGetMacro( UpsamplingFactor, unsigned int); + itkSetMacro( UpsamplingFactor, float) + itkGetMacro( UpsamplingFactor, float) /** Invert Image **/ - itkSetMacro( InvertImage, bool); - itkGetMacro( InvertImage, bool); + itkSetMacro( InvertImage, bool) + itkGetMacro( InvertImage, bool) - /** Binary Output **/ - itkSetMacro( BinaryOutput, bool); - itkGetMacro( BinaryOutput, bool); - - itkSetMacro( FiberBundle, mitk::FiberBundleX::Pointer); - itkSetMacro( InputImage, typename InputImageType::Pointer); + itkSetMacro( FiberBundle, mitk::FiberBundleX::Pointer) + itkSetMacro( InputImage, typename InputImageType::Pointer) /** Use input image geometry to initialize output image **/ - itkSetMacro( UseImageGeometry, bool); - itkGetMacro( UseImageGeometry, bool); + itkSetMacro( UseImageGeometry, bool) + itkGetMacro( UseImageGeometry, bool) void GenerateData(); protected: itk::Point GetItkPoint(double point[3]); TractsToRgbaImageFilter(); virtual ~TractsToRgbaImageFilter(); mitk::FiberBundleX::Pointer m_FiberBundle; - unsigned int m_UpsamplingFactor; + float m_UpsamplingFactor; bool m_InvertImage; - bool m_BinaryOutput; bool m_UseImageGeometry; typename InputImageType::Pointer m_InputImage; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkTractsToRgbaImageFilter.cpp" #endif #endif // __itkTractsToRgbaImageFilter_h__ diff --git a/Modules/DiffusionImaging/CMakeLists.txt b/Modules/DiffusionImaging/CMakeLists.txt index 0baae5d9ef..7a6789cdff 100644 --- a/Modules/DiffusionImaging/CMakeLists.txt +++ b/Modules/DiffusionImaging/CMakeLists.txt @@ -1,28 +1,28 @@ find_package(ITK) if(ITK_GDCM_DIR) include(${ITK_GDCM_DIR}/GDCMConfig.cmake) if(GDCM_MAJOR_VERSION EQUAL 2) add_definitions(-DGDCM2) set(ITK_USES_GDCM2 1) endif(GDCM_MAJOR_VERSION EQUAL 2) endif(ITK_GDCM_DIR) MITK_CREATE_MODULE( MitkDiffusionImaging SUBPROJECTS MITK-DTI - INCLUDE_DIRS IODataStructures Reconstruction Tractography Rendering Algorithms DicomImport Interactions IODataStructures/DiffusionWeightedImages IODataStructures/QBallImages IODataStructures/TensorImages IODataStructures/FiberBundle IODataStructures/FiberBundleX IODataStructures/PlanarFigureComposite IODataStructures/TbssImages Algorithms/Connectomics IODataStructures/ConnectomicsNetwork ${CMAKE_CURRENT_BINARY_DIR} + INCLUDE_DIRS IODataStructures Reconstruction Tractography Tractography/GibbsTracking Rendering Algorithms DicomImport Interactions IODataStructures/DiffusionWeightedImages IODataStructures/QBallImages IODataStructures/TensorImages IODataStructures/FiberBundle IODataStructures/FiberBundleX IODataStructures/PlanarFigureComposite IODataStructures/TbssImages Algorithms/Connectomics IODataStructures/ConnectomicsNetwork ${CMAKE_CURRENT_BINARY_DIR} DEPENDS MitkExt SceneSerializationBase QmitkExt MitkGraphAlgorithms PACKAGE_DEPENDS Boost ) MITK_USE_MODULE(MitkDiffusionImaging) if(MitkDiffusionImaging_IS_ENABLED) file(DOWNLOAD http://mitk.org/download/data/FibertrackingLUT.tar.gz ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FibertrackingLUT.tar.gz TIMEOUT 10) execute_process(COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} tar xzf FibertrackingLUT.tar.gz) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Rendering/mitkShaderFiberClipping.xml ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/mitkShaderFiberClipping.xml) MITK_INSTALL(FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/mitkShaderFiberClipping.xml ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FiberTrackingLUTBaryCoords.bin ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FiberTrackingLUTIndices.bin) endif() add_subdirectory(Testing) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/mitkDiffusionImagingConfigure.h.in ${CMAKE_CURRENT_BINARY_DIR}/mitkDiffusionImagingConfigure.h) diff --git a/Modules/DiffusionImaging/CMakeLists.txt~ b/Modules/DiffusionImaging/CMakeLists.txt~ deleted file mode 100644 index 67913004df..0000000000 --- a/Modules/DiffusionImaging/CMakeLists.txt~ +++ /dev/null @@ -1,28 +0,0 @@ -FIND_PACKAGE(ITK) -IF(ITK_GDCM_DIR) - INCLUDE(${ITK_GDCM_DIR}/GDCMConfig.cmake) - IF(GDCM_MAJOR_VERSION EQUAL 2) - ADD_DEFINITIONS(-DGDCM2) - SET(ITK_USES_GDCM2 1) - ENDIF(GDCM_MAJOR_VERSION EQUAL 2) -ENDIF(ITK_GDCM_DIR) - -MITK_CREATE_MODULE( MitkDiffusionImaging - SUBPROJECTS MITK-DTI - INCLUDE_DIRS IODataStructures Reconstruction Tractography Rendering Algorithms DicomImport Interactions IODataStructures/DiffusionWeightedImages IODataStructures/QBallImages IODataStructures/TensorImages IODataStructures/FiberBundle IODataStructures/FiberBundleX IODataStructures/PlanarFigureComposite IODataStructures/TbssImages ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS MitkExt SceneSerializationBase QmitkExt GraphAlgorithms - PACKAGE_DEPENDS Boost -) - -MITK_USE_MODULE(MitkDiffusionImaging) -if(MitkDiffusionImaging_IS_ENABLED) - file(DOWNLOAD http://mitk.org/download/data/FibertrackingLUT.tar.gz ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FibertrackingLUT.tar.gz TIMEOUT 10) - execute_process(COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} tar xzf FibertrackingLUT.tar.gz) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Rendering/mitkShaderFiberClipping.xml ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/mitkShaderFiberClipping.xml) - MITK_INSTALL(FILES ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/mitkShaderFiberClipping.xml ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FiberTrackingLUTBaryCoords.bin ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FiberTrackingLUTIndices.bin) -endif() - -ADD_SUBDIRECTORY(Testing) - -CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/mitkDiffusionImagingConfigure.h.in ${CMAKE_CURRENT_BINARY_DIR}/mitkDiffusionImagingConfigure.h) - diff --git a/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.cpp b/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.cpp index 8401093610..547b996cce 100644 --- a/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.cpp +++ b/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.cpp @@ -1,252 +1,260 @@ /*=================================================================== 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 "mitkConnectomicsNetworkReader.h" #include "mitkConnectomicsNetworkDefinitions.h" #include #include "itksys/SystemTools.hxx" #include void mitk::ConnectomicsNetworkReader::GenerateData() { MITK_INFO << "Reading connectomics network"; if ( ( ! m_OutputCache ) ) { Superclass::SetNumberOfRequiredOutputs(0); this->GenerateOutputInformation(); } if (!m_OutputCache) { itkWarningMacro("Tree cache is empty!"); } Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, m_OutputCache.GetPointer()); } void mitk::ConnectomicsNetworkReader::GenerateOutputInformation() { m_OutputCache = OutputType::New(); std::string ext = itksys::SystemTools::GetFilenameLastExtension(m_FileName); ext = itksys::SystemTools::LowerCase(ext); if ( m_FileName == "") { MITK_ERROR << "No file name specified."; } else if (ext == ".cnf") { try { TiXmlDocument doc( m_FileName ); - doc.LoadFile(); + bool loadOkay = doc.LoadFile(); + if(!loadOkay) + { + mitkThrow() << "Could not open file " << m_FileName << " for reading."; + } TiXmlHandle hDoc(&doc); TiXmlElement* pElem; TiXmlHandle hRoot(0); pElem = hDoc.FirstChildElement().Element(); // save this for later hRoot = TiXmlHandle(pElem); pElem = hRoot.FirstChildElement(mitk::ConnectomicsNetworkDefinitions::XML_GEOMETRY).Element(); // read geometry mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); // read origin mitk::Point3D origin; double temp = 0; pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_ORIGIN_X, &temp); origin[0] = temp; pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_ORIGIN_Y, &temp); origin[1] = temp; pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_ORIGIN_Z, &temp); origin[2] = temp; geometry->SetOrigin(origin); // read spacing float spacing[3]; pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_SPACING_X, &temp); spacing[0] = temp; pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_SPACING_Y, &temp); spacing[1] = temp; pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_SPACING_Z, &temp); spacing[2] = temp; geometry->SetSpacing(spacing); // read transform vtkMatrix4x4* m = vtkMatrix4x4::New(); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_XX, &temp); m->SetElement(0,0,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_XY, &temp); m->SetElement(1,0,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_XZ, &temp); m->SetElement(2,0,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_YX, &temp); m->SetElement(0,1,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_YY, &temp); m->SetElement(1,1,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_YZ, &temp); m->SetElement(2,1,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_ZX, &temp); m->SetElement(0,2,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_ZY, &temp); m->SetElement(1,2,temp); pElem->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_MATRIX_ZZ, &temp); m->SetElement(2,2,temp); m->SetElement(0,3,origin[0]); m->SetElement(1,3,origin[1]); m->SetElement(2,3,origin[2]); m->SetElement(3,3,1); geometry->SetIndexToWorldTransformByVtkMatrix(m); geometry->SetImageGeometry(true); m_OutputCache->SetGeometry(geometry); // read network std::map< int, mitk::ConnectomicsNetwork::VertexDescriptorType > idToVertexMap; // read vertices pElem = hRoot.FirstChildElement(mitk::ConnectomicsNetworkDefinitions::XML_VERTICES).Element(); { // walk through the vertices TiXmlElement* vertexElement = pElem->FirstChildElement(); for( vertexElement; vertexElement; vertexElement=vertexElement->NextSiblingElement()) { std::vector< float > pos; std::string label; int vertexID(0); vertexElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_VERTEX_X, &temp); pos.push_back(temp); vertexElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_VERTEX_Y, &temp); pos.push_back(temp); vertexElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_VERTEX_Z, &temp); pos.push_back(temp); vertexElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_VERTEX_ID, &vertexID); vertexElement->QueryStringAttribute(mitk::ConnectomicsNetworkDefinitions::XML_VERTEX_LABEL, &label); mitk::ConnectomicsNetwork::VertexDescriptorType newVertex = m_OutputCache->AddVertex( vertexID ); m_OutputCache->SetLabel( newVertex, label ); m_OutputCache->SetCoordinates( newVertex, pos ); if ( idToVertexMap.count( vertexID ) > 0 ) { MITK_ERROR << "Aborting network creation, duplicate vertex ID in file."; return; } idToVertexMap.insert( std::pair< int, mitk::ConnectomicsNetwork::VertexDescriptorType >( vertexID, newVertex) ); } } // read edges pElem = hRoot.FirstChildElement(mitk::ConnectomicsNetworkDefinitions::XML_EDGES).Element(); { // walk through the edges TiXmlElement* edgeElement = pElem->FirstChildElement(); for( edgeElement; edgeElement; edgeElement=edgeElement->NextSiblingElement()) { int edgeID(0), edgeSourceID(0), edgeTargetID(0), edgeWeight(0); edgeElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_EDGE_ID, &edgeID); edgeElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_EDGE_SOURCE_ID, &edgeSourceID); edgeElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_EDGE_TARGET_ID, &edgeTargetID); edgeElement->Attribute(mitk::ConnectomicsNetworkDefinitions::XML_EDGE_WEIGHT_ID, &edgeWeight); mitk::ConnectomicsNetwork::VertexDescriptorType source = idToVertexMap.find( edgeSourceID )->second; mitk::ConnectomicsNetwork::VertexDescriptorType target = idToVertexMap.find( edgeTargetID )->second; m_OutputCache->AddEdge( source, target, edgeSourceID, edgeTargetID, edgeWeight); } } m_OutputCache->UpdateBounds(); MITK_INFO << "Network read"; } + catch (mitk::Exception e) + { + MITK_ERROR << e.GetDescription(); + } catch(...) { - MITK_INFO << "Could not read file "; + MITK_ERROR << "Unknown error occured while trying to read file."; } } } void mitk::ConnectomicsNetworkReader::Update() { this->GenerateData(); } const char* mitk::ConnectomicsNetworkReader::GetFileName() const { return m_FileName.c_str(); } void mitk::ConnectomicsNetworkReader::SetFileName(const char* aFileName) { m_FileName = aFileName; } const char* mitk::ConnectomicsNetworkReader::GetFilePrefix() const { return m_FilePrefix.c_str(); } void mitk::ConnectomicsNetworkReader::SetFilePrefix(const char* aFilePrefix) { m_FilePrefix = aFilePrefix; } const char* mitk::ConnectomicsNetworkReader::GetFilePattern() const { return m_FilePattern.c_str(); } void mitk::ConnectomicsNetworkReader::SetFilePattern(const char* aFilePattern) { m_FilePattern = aFilePattern; } bool mitk::ConnectomicsNetworkReader::CanReadFile( const std::string filename, const std::string /*filePrefix*/, const std::string /*filePattern*/) { // First check the extension if( filename == "" ) { return false; } std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".cnf") { return true; } return false; } diff --git a/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.cpp b/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.cpp index f254971e40..9cd0ecc3fa 100644 --- a/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.cpp +++ b/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.cpp @@ -1,1288 +1,1374 @@ /*=================================================================== 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 "mitkFiberBundleX.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char* mitk::FiberBundleX::COLORCODING_ORIENTATION_BASED = "Color_Orient"; //const char* mitk::FiberBundleX::COLORCODING_FA_AS_OPACITY = "Color_Orient_FA_Opacity"; const char* mitk::FiberBundleX::COLORCODING_FA_BASED = "FA_Values"; const char* mitk::FiberBundleX::COLORCODING_CUSTOM = "custom"; const char* mitk::FiberBundleX::FIBER_ID_ARRAY = "Fiber_IDs"; mitk::FiberBundleX::FiberBundleX( vtkPolyData* fiberPolyData ) : m_CurrentColorCoding(NULL) , m_NumFibers(0) { m_FiberPolyData = vtkSmartPointer::New(); if (fiberPolyData != NULL) { vtkSmartPointer cleaner = vtkSmartPointer::New(); cleaner->SetInput(fiberPolyData); cleaner->Update(); fiberPolyData = cleaner->GetOutput(); m_FiberPolyData->DeepCopy(fiberPolyData); this->DoColorCodingOrientationBased(); } if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)) MITK_DEBUG << "ok"; vtkUnsignedCharArray* tmpColors = (vtkUnsignedCharArray*) m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED); if (tmpColors!=NULL) { int tmpColorss = tmpColors->GetNumberOfTuples(); int tmpColorc = tmpColors->GetNumberOfComponents(); } m_NumFibers = m_FiberPolyData->GetNumberOfLines(); this->UpdateFiberGeometry(); this->SetColorCoding(COLORCODING_ORIENTATION_BASED); this->GenerateFiberIds(); } mitk::FiberBundleX::~FiberBundleX() { } mitk::FiberBundleX::Pointer mitk::FiberBundleX::GetDeepCopy() { mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(m_FiberPolyData); if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)) MITK_DEBUG << "ok"; vtkUnsignedCharArray* tmpColors = (vtkUnsignedCharArray*) m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED); int tmpColorss = tmpColors->GetNumberOfTuples(); int tmpColorc = tmpColors->GetNumberOfComponents(); newFib->SetColorCoding(m_CurrentColorCoding); return newFib; } vtkSmartPointer mitk::FiberBundleX::GeneratePolyDataByIds(std::vector fiberIds) { MITK_DEBUG << "\n=====FINAL RESULT: fib_id ======\n"; MITK_DEBUG << "Number of new Fibers: " << fiberIds.size(); // iterate through the vectorcontainer hosting all desired fiber Ids vtkSmartPointer newFiberPolyData = vtkSmartPointer::New(); vtkSmartPointer newLineSet = vtkSmartPointer::New(); vtkSmartPointer newPointSet = vtkSmartPointer::New(); // if FA array available, initialize fa double array // if color orient array is available init color array vtkSmartPointer faValueArray; vtkSmartPointer colorsT; //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; int componentSize = sizeof(rgba); if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_FA_BASED)){ MITK_DEBUG << "FA VALUES AVAILABLE, init array for new fiberbundle"; faValueArray = vtkSmartPointer::New(); } if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)){ MITK_DEBUG << "colorValues available, init array for new fiberbundle"; colorsT = vtkUnsignedCharArray::New(); colorsT->SetNumberOfComponents(componentSize); colorsT->SetName(COLORCODING_ORIENTATION_BASED); } std::vector::iterator finIt = fiberIds.begin(); while ( finIt != fiberIds.end() ) { if (*finIt < 0 || *finIt>GetNumFibers()){ MITK_INFO << "FiberID can not be negative or >NumFibers!!! check id Extraction!" << *finIt; break; } vtkSmartPointer fiber = m_FiberIdDataSet->GetCell(*finIt);//->DeepCopy(fiber); vtkSmartPointer fibPoints = fiber->GetPoints(); vtkSmartPointer newFiber = vtkSmartPointer::New(); newFiber->GetPointIds()->SetNumberOfIds( fibPoints->GetNumberOfPoints() ); for(int i=0; iGetNumberOfPoints(); i++) { // MITK_DEBUG << "id: " << fiber->GetPointId(i); // MITK_DEBUG << fibPoints->GetPoint(i)[0] << " | " << fibPoints->GetPoint(i)[1] << " | " << fibPoints->GetPoint(i)[2]; newFiber->GetPointIds()->SetId(i, newPointSet->GetNumberOfPoints()); newPointSet->InsertNextPoint(fibPoints->GetPoint(i)[0], fibPoints->GetPoint(i)[1], fibPoints->GetPoint(i)[2]); if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_FA_BASED)){ // MITK_DEBUG << m_FiberIdDataSet->GetPointData()->GetArray(FA_VALUE_ARRAY)->GetTuple(fiber->GetPointId(i)); } if (m_FiberIdDataSet->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED)){ // MITK_DEBUG << "ColorValue: " << m_FiberIdDataSet->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)->GetTuple(fiber->GetPointId(i))[0]; } } newLineSet->InsertNextCell(newFiber); ++finIt; } newFiberPolyData->SetPoints(newPointSet); newFiberPolyData->SetLines(newLineSet); MITK_DEBUG << "new fiberbundle polydata points: " << newFiberPolyData->GetNumberOfPoints(); MITK_DEBUG << "new fiberbundle polydata lines: " << newFiberPolyData->GetNumberOfLines(); MITK_DEBUG << "=====================\n"; // mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(newFiberPolyData); return newFiberPolyData; } // merge two fiber bundles mitk::FiberBundleX::Pointer mitk::FiberBundleX::AddBundle(mitk::FiberBundleX* fib) { if (fib==NULL) { MITK_WARN << "trying to call AddBundle with NULL argument"; return NULL; } vtkSmartPointer vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); // add current fiber bundle int numFibers = GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(m_FiberPolyData->GetPoint(points[j])); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } vLines = fib->m_FiberPolyData->GetLines(); vLines->InitTraversal(); // add new fiber bundle numFibers = fib->GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(fib->m_FiberPolyData->GetPoint(points[j])); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } // initialize polydata vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(vNewPolyData); return newFib; } // subtract two fiber bundles mitk::FiberBundleX::Pointer mitk::FiberBundleX::SubtractBundle(mitk::FiberBundleX* fib) { vtkSmartPointer vNewPolyData = vtkSmartPointer::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); // iterate over current fibers int numFibers = GetNumFibers(); for( int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer vLines2 = fib->m_FiberPolyData->GetLines(); vLines2->InitTraversal(); int numFibers2 = fib->GetNumFibers(); bool contained = false; for( int i2=0; i2GetNextCell ( numPoints2, points2 ); // check endpoints itk::Point point_start = GetItkPoint(m_FiberPolyData->GetPoint(points[0])); itk::Point point_end = GetItkPoint(m_FiberPolyData->GetPoint(points[numPoints-1])); itk::Point point2_start = GetItkPoint(fib->m_FiberPolyData->GetPoint(points2[0])); itk::Point point2_end = GetItkPoint(fib->m_FiberPolyData->GetPoint(points2[numPoints2-1])); if (point_start.SquaredEuclideanDistanceTo(point2_start)<=mitk::eps && point_end.SquaredEuclideanDistanceTo(point2_end)<=mitk::eps || point_start.SquaredEuclideanDistanceTo(point2_end)<=mitk::eps && point_end.SquaredEuclideanDistanceTo(point2_start)<=mitk::eps) { // further checking ??? if (numPoints2==numPoints) contained = true; } } // add to result because fiber is not subtracted if (!contained) { vtkSmartPointer container = vtkSmartPointer::New(); for( int j=0; jInsertNextPoint(m_FiberPolyData->GetPoint(points[j])); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } } if(vNewLines->GetNumberOfCells()==0) return NULL; // initialize polydata vNewPolyData->SetPoints(vNewPoints); vNewPolyData->SetLines(vNewLines); // initialize fiber bundle mitk::FiberBundleX::Pointer newFib = mitk::FiberBundleX::New(vNewPolyData); return newFib; } itk::Point mitk::FiberBundleX::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } /* * set polydata (additional flag to recompute fiber geometry, default = true) */ void mitk::FiberBundleX::SetFiberPolyData(vtkSmartPointer fiberPD, bool updateGeometry) { if (fiberPD == NULL) this->m_FiberPolyData = vtkSmartPointer::New(); else { m_FiberPolyData->DeepCopy(fiberPD); DoColorCodingOrientationBased(); } m_NumFibers = m_FiberPolyData->GetNumberOfLines(); if (updateGeometry) UpdateFiberGeometry(); SetColorCoding(COLORCODING_ORIENTATION_BASED); GenerateFiberIds(); } /* * return vtkPolyData */ vtkSmartPointer mitk::FiberBundleX::GetFiberPolyData() { return m_FiberPolyData; } void mitk::FiberBundleX::DoColorCodingOrientationBased() { //===== FOR WRITING A TEST ======================== // colorT size == tupelComponents * tupelElements // compare color results // to cover this code 100% also polydata needed, where colorarray already exists // + one fiber with exactly 1 point // + one fiber with 0 points //================================================= /* make sure that processing colorcoding is only called when necessary */ if ( m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED) && m_FiberPolyData->GetNumberOfPoints() == m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)->GetNumberOfTuples() ) { // fiberstructure is already colorcoded MITK_DEBUG << " NO NEED TO REGENERATE COLORCODING! " ; this->ResetFiberOpacity(); this->SetColorCoding(COLORCODING_ORIENTATION_BASED); return; } /* Finally, execute color calculation */ vtkPoints* extrPoints = NULL; extrPoints = m_FiberPolyData->GetPoints(); int numOfPoints = 0; if (extrPoints!=NULL) numOfPoints = extrPoints->GetNumberOfPoints(); //colors and alpha value for each single point, RGBA = 4 components unsigned char rgba[4] = {0,0,0,0}; // int componentSize = sizeof(rgba); int componentSize = 4; vtkSmartPointer colorsT = vtkUnsignedCharArray::New(); colorsT->Allocate(numOfPoints * componentSize); colorsT->SetNumberOfComponents(componentSize); colorsT->SetName(COLORCODING_ORIENTATION_BASED); /* checkpoint: does polydata contain any fibers */ int numOfFibers = m_FiberPolyData->GetNumberOfLines(); if (numOfFibers < 1) { MITK_DEBUG << "\n ========= Number of Fibers is 0 and below ========= \n"; return; } /* extract single fibers of fiberBundle */ vtkCellArray* fiberList = m_FiberPolyData->GetLines(); fiberList->InitTraversal(); for (int fi=0; fiGetNextCell(pointsPerFiber, idList); // MITK_DEBUG << "Fib#: " << fi << " of " << numOfFibers << " pnts in fiber: " << pointsPerFiber ; /* single fiber checkpoints: is number of points valid */ if (pointsPerFiber > 1) { /* operate on points of single fiber */ for (int i=0; i 0) { /* The color value of the current point is influenced by the previous point and next point. */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > nextPntvtk(extrPoints->GetPoint(idList[i+1])[0], extrPoints->GetPoint(idList[i+1])[1], extrPoints->GetPoint(idList[i+1])[2]); vnl_vector_fixed< double, 3 > prevPntvtk(extrPoints->GetPoint(idList[i-1])[0], extrPoints->GetPoint(idList[i-1])[1], extrPoints->GetPoint(idList[i-1])[2]); vnl_vector_fixed< double, 3 > diff1; diff1 = currentPntvtk - nextPntvtk; vnl_vector_fixed< double, 3 > diff2; diff2 = currentPntvtk - prevPntvtk; vnl_vector_fixed< double, 3 > diff; diff = (diff1 - diff2) / 2.0; diff.normalize(); rgba[0] = (unsigned char) (255.0 * std::fabs(diff[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff[2])); rgba[3] = (unsigned char) (255.0); } else if (i==0) { /* First point has no previous point, therefore only diff1 is taken */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > nextPntvtk(extrPoints->GetPoint(idList[i+1])[0], extrPoints->GetPoint(idList[i+1])[1], extrPoints->GetPoint(idList[i+1])[2]); vnl_vector_fixed< double, 3 > diff1; diff1 = currentPntvtk - nextPntvtk; diff1.normalize(); rgba[0] = (unsigned char) (255.0 * std::fabs(diff1[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff1[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff1[2])); rgba[3] = (unsigned char) (255.0); } else if (i==pointsPerFiber-1) { /* Last point has no next point, therefore only diff2 is taken */ vnl_vector_fixed< double, 3 > currentPntvtk(extrPoints->GetPoint(idList[i])[0], extrPoints->GetPoint(idList[i])[1],extrPoints->GetPoint(idList[i])[2]); vnl_vector_fixed< double, 3 > prevPntvtk(extrPoints->GetPoint(idList[i-1])[0], extrPoints->GetPoint(idList[i-1])[1], extrPoints->GetPoint(idList[i-1])[2]); vnl_vector_fixed< double, 3 > diff2; diff2 = currentPntvtk - prevPntvtk; diff2.normalize(); rgba[0] = (unsigned char) (255.0 * std::fabs(diff2[0])); rgba[1] = (unsigned char) (255.0 * std::fabs(diff2[1])); rgba[2] = (unsigned char) (255.0 * std::fabs(diff2[2])); rgba[3] = (unsigned char) (255.0); } colorsT->InsertTupleValue(idList[i], rgba); } //end for loop } else if (pointsPerFiber == 1) { /* a single point does not define a fiber (use vertex mechanisms instead */ continue; // colorsT->InsertTupleValue(0, rgba); } else { MITK_DEBUG << "Fiber with 0 points detected... please check your tractography algorithm!" ; continue; } }//end for loop m_FiberPolyData->GetPointData()->AddArray(colorsT); /*========================= - this is more relevant for renderer than for fiberbundleX datastructure - think about sourcing this to a explicit method which coordinates colorcoding */ this->SetColorCoding(COLORCODING_ORIENTATION_BASED); // =========================== //mini test, shall be ported to MITK TESTINGS! if (colorsT->GetSize() != numOfPoints*componentSize) MITK_DEBUG << "ALLOCATION ERROR IN INITIATING COLOR ARRAY"; } void mitk::FiberBundleX::DoColorCodingFaBased() { if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_FA_BASED) != 1 ) return; this->SetColorCoding(COLORCODING_FA_BASED); MITK_DEBUG << "FBX: done CC FA based"; this->GenerateFiberIds(); } void mitk::FiberBundleX::DoUseFaFiberOpacity() { if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_FA_BASED) != 1 ) return; if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_ORIENTATION_BASED) != 1 ) return; vtkDoubleArray* FAValArray = (vtkDoubleArray*) m_FiberPolyData->GetPointData()->GetArray(COLORCODING_FA_BASED); vtkUnsignedCharArray* ColorArray = dynamic_cast (m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)); for(long i=0; iGetNumberOfTuples(); i++) { double faValue = FAValArray->GetValue(i); faValue = faValue * 255.0; ColorArray->SetComponent(i,3, (unsigned char) faValue ); } this->SetColorCoding(COLORCODING_ORIENTATION_BASED); MITK_DEBUG << "FBX: done CC OPACITY"; this->GenerateFiberIds(); } void mitk::FiberBundleX::ResetFiberOpacity() { vtkUnsignedCharArray* ColorArray = dynamic_cast (m_FiberPolyData->GetPointData()->GetArray(COLORCODING_ORIENTATION_BASED)); if (ColorArray==NULL) return; for(long i=0; iGetNumberOfTuples(); i++) ColorArray->SetComponent(i,3, 255.0 ); } void mitk::FiberBundleX::SetFAMap(mitk::Image::Pointer FAimage) { MITK_DEBUG << "SetFAMap"; vtkSmartPointer faValues = vtkDoubleArray::New(); faValues->SetName(COLORCODING_FA_BASED); faValues->Allocate(m_FiberPolyData->GetNumberOfPoints()); // MITK_DEBUG << faValues->GetNumberOfTuples(); // MITK_DEBUG << faValues->GetSize(); faValues->SetNumberOfValues(m_FiberPolyData->GetNumberOfPoints()); // MITK_DEBUG << faValues->GetNumberOfTuples(); // MITK_DEBUG << faValues->GetSize(); vtkPoints* pointSet = m_FiberPolyData->GetPoints(); for(long i=0; iGetNumberOfPoints(); ++i) { Point3D px; px[0] = pointSet->GetPoint(i)[0]; px[1] = pointSet->GetPoint(i)[1]; px[2] = pointSet->GetPoint(i)[2]; double faPixelValue = 1-FAimage->GetPixelValueByWorldCoordinate(px); // faValues->InsertNextTuple1(faPixelValue); faValues->InsertValue(i, faPixelValue); // MITK_DEBUG << faPixelValue; // MITK_DEBUG << faValues->GetValue(i); } m_FiberPolyData->GetPointData()->AddArray(faValues); this->GenerateFiberIds(); if(m_FiberPolyData->GetPointData()->HasArray(COLORCODING_FA_BASED)) MITK_DEBUG << "FA VALUE ARRAY SET"; // vtkDoubleArray* valueArray = (vtkDoubleArray*) m_FiberPolyData->GetPointData()->GetArray(FA_VALUE_ARRAY); // for(long i=0; iGetNumberOfPoints(); i++) // { // MITK_DEBUG << "value at pos "<< i << ": " << valueArray->GetValue(i); // } } void mitk::FiberBundleX::GenerateFiberIds() { if (m_FiberPolyData == NULL) return; vtkSmartPointer idFiberFilter = vtkSmartPointer::New(); idFiberFilter->SetInput(m_FiberPolyData); idFiberFilter->CellIdsOn(); // idFiberFilter->PointIdsOn(); // point id's are not needed idFiberFilter->SetIdsArrayName(FIBER_ID_ARRAY); idFiberFilter->FieldDataOn(); idFiberFilter->Update(); m_FiberIdDataSet = idFiberFilter->GetOutput(); MITK_DEBUG << "Generating Fiber Ids...[done] | " << m_FiberIdDataSet->GetNumberOfCells(); } mitk::FiberBundleX::Pointer mitk::FiberBundleX::ExtractFiberSubset(mitk::PlanarFigure* pf) { if (pf==NULL) return NULL; std::vector tmp = ExtractFiberIdSubset(pf); if (tmp.size()<=0) return mitk::FiberBundleX::New(); vtkSmartPointer pTmp = GeneratePolyDataByIds(tmp); return mitk::FiberBundleX::New(pTmp); } std::vector mitk::FiberBundleX::ExtractFiberIdSubset(mitk::PlanarFigure* pf) { MITK_DEBUG << "Extracting fibers!"; // vector which is returned, contains all extracted FiberIds std::vector FibersInROI; if (pf==NULL) return FibersInROI; /* Handle type of planarfigure */ // if incoming pf is a pfc mitk::PlanarFigureComposite::Pointer pfcomp= dynamic_cast(pf); if (!pfcomp.IsNull()) { // process requested boolean operation of PFC switch (pfcomp->getOperationType()) { case 0: { MITK_DEBUG << "AND PROCESSING"; //AND //temporarly store results of the child in this vector, we need that to accumulate the std::vector childResults = this->ExtractFiberIdSubset(pfcomp->getChildAt(0)); MITK_DEBUG << "first roi got fibers in ROI: " << childResults.size(); MITK_DEBUG << "sorting..."; std::sort(childResults.begin(), childResults.end()); MITK_DEBUG << "sorting done"; std::vector AND_Assamblage(childResults.size()); //std::vector AND_Assamblage; fill(AND_Assamblage.begin(), AND_Assamblage.end(), -1); //AND_Assamblage.reserve(childResults.size()); //max size AND can reach anyway std::vector::iterator it; for (int i=1; igetNumberOfChildren(); ++i) { std::vector tmpChild = this->ExtractFiberIdSubset(pfcomp->getChildAt(i)); MITK_DEBUG << "ROI " << i << " has fibers in ROI: " << tmpChild.size(); sort(tmpChild.begin(), tmpChild.end()); it = std::set_intersection(childResults.begin(), childResults.end(), tmpChild.begin(), tmpChild.end(), AND_Assamblage.begin() ); } MITK_DEBUG << "resize Vector"; long i=0; while (i < AND_Assamblage.size() && AND_Assamblage[i] != -1){ //-1 represents a placeholder in the array ++i; } AND_Assamblage.resize(i); MITK_DEBUG << "returning AND vector, size: " << AND_Assamblage.size(); return AND_Assamblage; // break; } case 1: { //OR std::vector OR_Assamblage = this->ExtractFiberIdSubset(pfcomp->getChildAt(0)); std::vector::iterator it; MITK_DEBUG << OR_Assamblage.size(); for (int i=1; igetNumberOfChildren(); ++i) { it = OR_Assamblage.end(); std::vector tmpChild = this->ExtractFiberIdSubset(pfcomp->getChildAt(i)); OR_Assamblage.insert(it, tmpChild.begin(), tmpChild.end()); MITK_DEBUG << "ROI " << i << " has fibers in ROI: " << tmpChild.size() << " OR Assamblage: " << OR_Assamblage.size(); } sort(OR_Assamblage.begin(), OR_Assamblage.end()); it = unique(OR_Assamblage.begin(), OR_Assamblage.end()); OR_Assamblage.resize( it - OR_Assamblage.begin() ); MITK_DEBUG << "returning OR vector, size: " << OR_Assamblage.size(); return OR_Assamblage; } case 2: { //NOT //get IDs of all fibers std::vector childResults; childResults.reserve(this->GetNumFibers()); vtkSmartPointer idSet = m_FiberIdDataSet->GetCellData()->GetArray(FIBER_ID_ARRAY); MITK_DEBUG << "m_NumOfFib: " << this->GetNumFibers() << " cellIdNum: " << idSet->GetNumberOfTuples(); for(long i=0; iGetNumFibers(); i++) { MITK_DEBUG << "i: " << i << " idset: " << idSet->GetTuple(i)[0]; childResults.push_back(idSet->GetTuple(i)[0]); } std::sort(childResults.begin(), childResults.end()); std::vector NOT_Assamblage(childResults.size()); //fill it with -1, otherwise 0 will be stored and 0 can also be an ID of fiber! fill(NOT_Assamblage.begin(), NOT_Assamblage.end(), -1); std::vector::iterator it; for (long i=0; igetNumberOfChildren(); ++i) { std::vector tmpChild = ExtractFiberIdSubset(pfcomp->getChildAt(i)); sort(tmpChild.begin(), tmpChild.end()); it = std::set_difference(childResults.begin(), childResults.end(), tmpChild.begin(), tmpChild.end(), NOT_Assamblage.begin() ); } MITK_DEBUG << "resize Vector"; long i=0; while (NOT_Assamblage[i] != -1){ //-1 represents a placeholder in the array ++i; } NOT_Assamblage.resize(i); return NOT_Assamblage; } default: MITK_DEBUG << "we have an UNDEFINED composition... ERROR" ; break; } } else { mitk::Geometry2D::ConstPointer pfgeometry = pf->GetGeometry2D(); const mitk::PlaneGeometry* planeGeometry = dynamic_cast (pfgeometry.GetPointer()); Vector3D planeNormal = planeGeometry->GetNormal(); planeNormal.Normalize(); Point3D planeOrigin = planeGeometry->GetOrigin(); MITK_DEBUG << "planeOrigin: " << planeOrigin[0] << " | " << planeOrigin[1] << " | " << planeOrigin[2] << endl; MITK_DEBUG << "planeNormal: " << planeNormal[0] << " | " << planeNormal[1] << " | " << planeNormal[2] << endl; std::vector PointsOnPlane; // contains all pointIds which are crossing the cutting plane std::vector PointsInROI; // based on PointsOnPlane, all ROI relevant point IDs are stored here /* Define cutting plane by ROI (PlanarFigure) */ vtkSmartPointer plane = vtkSmartPointer::New(); plane->SetOrigin(planeOrigin[0],planeOrigin[1],planeOrigin[2]); plane->SetNormal(planeNormal[0],planeNormal[1],planeNormal[2]); //same plane but opposite normal direction. so point cloud will be reduced -> better performance // vtkSmartPointer planeR = vtkSmartPointer::New(); //define new origin along the normal but close to the original one // OriginNew = OriginOld + 1*Normal // Vector3D extendedNormal; // int multiplyFactor = 1; // extendedNormal[0] = planeNormal[0] * multiplyFactor; // extendedNormal[1] = planeNormal[1] * multiplyFactor; // extendedNormal[2] = planeNormal[2] * multiplyFactor; // Point3D RplaneOrigin = planeOrigin - extendedNormal; // planeR->SetOrigin(RplaneOrigin[0],RplaneOrigin[1],RplaneOrigin[2]); // planeR->SetNormal(-planeNormal[0],-planeNormal[1],-planeNormal[2]); // MITK_DEBUG << "RPlaneOrigin: " << RplaneOrigin[0] << " | " << RplaneOrigin[1] // << " | " << RplaneOrigin[2]; /* get all points/fibers cutting the plane */ MITK_DEBUG << "start clipping"; vtkSmartPointer clipper = vtkSmartPointer::New(); clipper->SetInput(m_FiberIdDataSet); clipper->SetClipFunction(plane); clipper->GenerateClipScalarsOn(); clipper->GenerateClippedOutputOn(); vtkSmartPointer clipperout = clipper->GetClippedOutput(); MITK_DEBUG << "end clipping"; /* for some reason clipperoutput is not initialized for futher processing * so far only writing out clipped polydata provides requested */ // MITK_DEBUG << "writing clipper output"; // vtkSmartPointer writerC = vtkSmartPointer::New(); // writerC->SetInput(clipperout1); // writerC->SetFileName("/vtkOutput/Clipping.vtk"); // writerC->SetFileTypeToASCII(); // writerC->Write(); // MITK_DEBUG << "writing done"; MITK_DEBUG << "init and update clipperoutput"; clipperout->GetPointData()->Initialize(); clipperout->Update(); MITK_DEBUG << "init and update clipperoutput completed"; // MITK_DEBUG << "start clippingRecursive"; // vtkSmartPointer Rclipper = vtkSmartPointer::New(); // Rclipper->SetInput(clipperout1); // Rclipper->SetClipFunction(planeR); // Rclipper->GenerateClipScalarsOn(); // Rclipper->GenerateClippedOutputOn(); // vtkSmartPointer clipperout = Rclipper->GetClippedOutput(); // MITK_DEBUG << "end clipping recursive"; // MITK_DEBUG << "writing clipper output 2"; // vtkSmartPointer writerC1 = vtkSmartPointer::New(); // writerC1->SetInput(clipperout); // writerC1->SetFileName("/vtkOutput/RClipping.vtk"); // writerC1->SetFileTypeToASCII(); // writerC1->Write(); // MITK_DEBUG << "init and update clipperoutput"; // clipperout->GetPointData()->Initialize(); // clipperout->Update(); // MITK_DEBUG << "init and update clipperoutput completed"; MITK_DEBUG << "STEP 1: find all points which have distance 0 to the given plane"; /*======STEP 1====== * extract all points, which are crossing the plane */ // Scalar values describe the distance between each remaining point to the given plane. Values sorted by point index vtkSmartPointer distanceList = clipperout->GetPointData()->GetScalars(); vtkIdType sizeOfList = distanceList->GetNumberOfTuples(); PointsOnPlane.reserve(sizeOfList); /* use reserve for high-performant push_back, no hidden copy procedures are processed then! * size of list can be optimized by reducing allocation, but be aware of iterator and vector size*/ for (int i=0; iGetTuple(i); // check if point is on plane. // 0.01 due to some approximation errors when calculating distance if (distance[0] >= -0.01 && distance[0] <= 0.01) PointsOnPlane.push_back(i); } // DEBUG print out all interesting points, stop where array starts with value -1. after -1 no more interesting idx are set! // std::vector::iterator rit = PointsOnPlane.begin(); // while (rit != PointsOnPlane.end() ) { // std::cout << "interesting point: " << *rit << " coord: " << clipperout->GetPoint(*rit)[0] << " | " << clipperout->GetPoint(*rit)[1] << " | " << clipperout->GetPoint(*rit)[2] << endl; // rit++; // } MITK_DEBUG << "Num Of points on plane: " << PointsOnPlane.size(); MITK_DEBUG << "Step 2: extract Interesting points with respect to given extraction planarFigure"; PointsInROI.reserve(PointsOnPlane.size()); /*=======STEP 2===== * extract ROI relevant pointIds */ mitk::PlanarCircle::Pointer circleName = mitk::PlanarCircle::New(); mitk::PlanarPolygon::Pointer polyName = mitk::PlanarPolygon::New(); if ( pf->GetNameOfClass() == circleName->GetNameOfClass() ) { //calculate circle radius mitk::Point3D V1w = pf->GetWorldControlPoint(0); //centerPoint mitk::Point3D V2w = pf->GetWorldControlPoint(1); //radiusPoint double distPF = V1w.EuclideanDistanceTo(V2w); for (int i=0; iGetPoint(PointsOnPlane[i])[0] - V1w[0]) * (clipperout->GetPoint(PointsOnPlane[i])[0] - V1w[0]) + (clipperout->GetPoint(PointsOnPlane[i])[1] - V1w[1]) * (clipperout->GetPoint(PointsOnPlane[i])[1] - V1w[1]) + (clipperout->GetPoint(PointsOnPlane[i])[2] - V1w[2]) * (clipperout->GetPoint(PointsOnPlane[i])[2] - V1w[2])) ; if( XdistPnt <= distPF) PointsInROI.push_back(PointsOnPlane[i]); } } else if ( pf->GetNameOfClass() == polyName->GetNameOfClass() ) { //create vtkPolygon using controlpoints from planarFigure polygon vtkSmartPointer polygonVtk = vtkPolygon::New(); //get the control points from pf and insert them to vtkPolygon unsigned int nrCtrlPnts = pf->GetNumberOfControlPoints(); for (int i=0; iGetPoints()->InsertNextPoint((double)pf->GetWorldControlPoint(i)[0], (double)pf->GetWorldControlPoint(i)[1], (double)pf->GetWorldControlPoint(i)[2] ); } //prepare everything for using pointInPolygon function double n[3]; polygonVtk->ComputeNormal(polygonVtk->GetPoints()->GetNumberOfPoints(), static_cast(polygonVtk->GetPoints()->GetData()->GetVoidPointer(0)), n); double bounds[6]; polygonVtk->GetPoints()->GetBounds(bounds); for (int i=0; iGetPoint(PointsOnPlane[i])[0], clipperout->GetPoint(PointsOnPlane[i])[1], clipperout->GetPoint(PointsOnPlane[i])[2]}; int isInPolygon = polygonVtk->PointInPolygon(checkIn, polygonVtk->GetPoints()->GetNumberOfPoints() , static_cast(polygonVtk->GetPoints()->GetData()->GetVoidPointer(0)), bounds, n); if( isInPolygon ) PointsInROI.push_back(PointsOnPlane[i]); } } MITK_DEBUG << "Step3: Identify fibers"; // we need to access the fiberId Array, so make sure that this array is available if (!clipperout->GetCellData()->HasArray(FIBER_ID_ARRAY)) { MITK_DEBUG << "ERROR: FiberID array does not exist, no correlation between points and fiberIds possible! Make sure calling GenerateFiberIds()"; return FibersInROI; // FibersInRoi is empty then } if (PointsInROI.size()<=0) return FibersInROI; // prepare a structure where each point id is represented as an indexId. // vector looks like: | pntId | fiberIdx | std::vector< long > pointindexFiberMap; // walk through the whole subline section and create an vector sorted by point index vtkCellArray *clipperlines = clipperout->GetLines(); clipperlines->InitTraversal(); long numOfLineCells = clipperlines->GetNumberOfCells(); long numofClippedPoints = clipperout->GetNumberOfPoints(); pointindexFiberMap.resize(numofClippedPoints); //prepare resulting vector FibersInROI.reserve(PointsInROI.size()); MITK_DEBUG << "\n===== Pointindex based structure initialized ======\n"; // go through resulting "sub"lines which are stored as cells, "i" corresponds to current line id. for (int i=0, ic=0 ; iGetCell(ic, npts, pts); // go through point ids in hosting subline, "j" corresponds to current pointindex in current line i. eg. idx[0]=45; idx[1]=46 for (long j=0; jGetCellData()->GetArray(FIBER_ID_ARRAY)->GetTuple(i)[0] << " to pointId: " << pts[j]; pointindexFiberMap[ pts[j] ] = clipperout->GetCellData()->GetArray(FIBER_ID_ARRAY)->GetTuple(i)[0]; // MITK_DEBUG << "in array: " << pointindexFiberMap[ pts[j] ]; } } MITK_DEBUG << "\n===== Pointindex based structure finalized ======\n"; // get all Points in ROI with according fiberID for (long k = 0; k < PointsInROI.size(); k++) { //MITK_DEBUG << "point " << PointsInROI[k] << " belongs to fiber " << pointindexFiberMap[ PointsInROI[k] ]; if (pointindexFiberMap[ PointsInROI[k] ]<=GetNumFibers() && pointindexFiberMap[ PointsInROI[k] ]>=0) FibersInROI.push_back(pointindexFiberMap[ PointsInROI[k] ]); else MITK_INFO << "ERROR in ExtractFiberIdSubset; impossible fiber id detected"; } } // detecting fiberId duplicates MITK_DEBUG << "check for duplicates"; sort(FibersInROI.begin(), FibersInROI.end()); bool hasDuplicats = false; for(long i=0; i::iterator it; it = unique (FibersInROI.begin(), FibersInROI.end()); FibersInROI.resize( it - FibersInROI.begin() ); } return FibersInROI; } void mitk::FiberBundleX::UpdateFiberGeometry() { if (m_NumFibers<=0) // no fibers present; apply default geometry { mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetImageGeometry(true); float b[] = {0, 1, 0, 1, 0, 1}; geometry->SetFloatBounds(b); SetGeometry(geometry); return; } float min = itk::NumericTraits::NonpositiveMin(); float max = itk::NumericTraits::max(); float b[] = {max, min, max, min, max, min}; vtkCellArray* cells = m_FiberPolyData->GetLines(); cells->InitTraversal(); for (int i=0; iGetNumberOfCells(); i++) { vtkCell* cell = m_FiberPolyData->GetCell(i); int p = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(j, p); if (p[0]b[1]) b[1]=p[0]; if (p[1]b[3]) b[3]=p[1]; if (p[2]b[5]) b[5]=p[2]; } } // provide some border margin for(int i=0; i<=4; i+=2) b[i] -=10; for(int i=1; i<=5; i+=2) b[i] +=10; mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->SetFloatBounds(b); this->SetGeometry(geometry); } QStringList mitk::FiberBundleX::GetAvailableColorCodings() { QStringList availableColorCodings; int numColors = m_FiberPolyData->GetPointData()->GetNumberOfArrays(); for(int i=0; iGetPointData()->GetArrayName(i)); } //this controlstructure shall be implemented by the calling method if (availableColorCodings.isEmpty()) MITK_DEBUG << "no colorcodings available in fiberbundleX"; // for(int i=0; im_CurrentColorCoding = (char*) COLORCODING_ORIENTATION_BASED; } else if( strcmp (COLORCODING_FA_BASED,requestedColorCoding) == 0 ) { this->m_CurrentColorCoding = (char*) COLORCODING_FA_BASED; } else if( strcmp (COLORCODING_CUSTOM,requestedColorCoding) == 0 ) { this->m_CurrentColorCoding = (char*) COLORCODING_CUSTOM; } else { MITK_DEBUG << "FIBERBUNDLE X: UNKNOWN COLORCODING in FIBERBUNDLEX Datastructure"; this->m_CurrentColorCoding = (char*) COLORCODING_CUSTOM; //will cause blank colorcoding of fibers } } -void mitk::FiberBundleX::DoFiberSmoothing(int pointsPerCm) +void mitk::FiberBundleX::MirrorFibers(unsigned int axis) { + if (axis>2) + return; + + vtkSmartPointer vtkNewPoints = vtkPoints::New(); + vtkSmartPointer vtkNewCells = vtkCellArray::New(); + + vtkSmartPointer vLines = m_FiberPolyData->GetLines(); + vLines->InitTraversal(); + for (int i=0; iGetNextCell ( numPoints, pointIds ); + + vtkSmartPointer container = vtkSmartPointer::New(); + for (int j=0; jGetPoint(pointIds[j]); + p[axis] = -p[axis]; + vtkIdType id = vtkNewPoints->InsertNextPoint(p); + container->GetPointIds()->InsertNextId(id); + } + vtkNewCells->InsertNextCell(container); + } + + m_FiberPolyData = vtkSmartPointer::New(); + m_FiberPolyData->SetPoints(vtkNewPoints); + m_FiberPolyData->SetLines(vtkNewCells); + UpdateColorCoding(); + UpdateFiberGeometry(); +} + +bool mitk::FiberBundleX::RemoveShortFibers(float lengthInMM) +{ + if (lengthInMM<=0) + return true; + + vtkSmartPointer vtkNewPoints = vtkPoints::New(); + vtkSmartPointer vtkNewCells = vtkCellArray::New(); + + vtkSmartPointer vLines = m_FiberPolyData->GetLines(); + vLines->InitTraversal(); + for (int i=0; iGetNextCell ( numPoints, pointIds ); + // calculate fiber length + float length = 0; + itk::Point lastP; + for (int j=0; jGetPoint(pointIds[j]); + if (j>0) + length += sqrt(pow(p[0]-lastP[0], 2)+pow(p[1]-lastP[1], 2)+pow(p[2]-lastP[2], 2)); + lastP[0] = p[0]; + lastP[1] = p[1]; + lastP[2] = p[2]; + } + + if (length>=lengthInMM) + { + vtkSmartPointer container = vtkSmartPointer::New(); + for (int j=0; jGetPoint(pointIds[j]); + vtkIdType id = vtkNewPoints->InsertNextPoint(p); + container->GetPointIds()->InsertNextId(id); + } + vtkNewCells->InsertNextCell(container); + } + } + + if (vtkNewCells->GetNumberOfCells()<=0) + return false; + + m_FiberPolyData = vtkSmartPointer::New(); + m_FiberPolyData->SetPoints(vtkNewPoints); + m_FiberPolyData->SetLines(vtkNewCells); + UpdateColorCoding(); + UpdateFiberGeometry(); + return true; +} + +void mitk::FiberBundleX::DoFiberSmoothing(int pointsPerCm) +{ vtkSmartPointer vtkSmoothPoints = vtkPoints::New(); //in smoothpoints the interpolated points representing a fiber are stored. //in vtkcells all polylines are stored, actually all id's of them are stored vtkSmartPointer vtkSmoothCells = vtkCellArray::New(); //cellcontainer for smoothed lines vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); vtkIdType pointHelperCnt = 0; for (int i=0; iGetNextCell ( numPoints, pointIds ); vtkSmartPointer points = vtkSmartPointer::New(); float length = 0; itk::Point lastP; for (int j=0; jGetPoint(pointIds[j]); points->InsertNextPoint(p); if (j>0) length += sqrt(pow(p[0]-lastP[0], 2)+pow(p[1]-lastP[1], 2)+pow(p[2]-lastP[2], 2)); lastP[0] = p[0]; lastP[1] = p[1]; lastP[2] = p[2]; } length /=10; int sampling = pointsPerCm*length; /////PROCESS POLYLINE SMOOTHING///// vtkSmartPointer xSpline = vtkKochanekSpline::New(); vtkSmartPointer ySpline = vtkKochanekSpline::New(); vtkSmartPointer zSpline = vtkKochanekSpline::New(); vtkSmartPointer spline = vtkParametricSpline::New(); spline->SetXSpline(xSpline); spline->SetYSpline(ySpline); spline->SetZSpline(zSpline); spline->SetPoints(points); vtkSmartPointer functionSource = vtkParametricFunctionSource::New(); functionSource->SetParametricFunction(spline); functionSource->SetUResolution(sampling); functionSource->SetVResolution(sampling); functionSource->SetWResolution(sampling); functionSource->Update(); vtkPolyData* outputFunction = functionSource->GetOutput(); vtkPoints* tmpSmoothPnts = outputFunction->GetPoints(); //smoothPoints of current fiber vtkSmartPointer smoothLine = vtkPolyLine::New(); smoothLine->GetPointIds()->SetNumberOfIds(tmpSmoothPnts->GetNumberOfPoints()); for (int j=0; jGetNumberOfPoints(); j++) { smoothLine->GetPointIds()->SetId(j, j+pointHelperCnt); vtkSmoothPoints->InsertNextPoint(tmpSmoothPnts->GetPoint(j)); } vtkSmoothCells->InsertNextCell(smoothLine); pointHelperCnt += tmpSmoothPnts->GetNumberOfPoints(); } m_FiberPolyData = vtkSmartPointer::New(); m_FiberPolyData->SetPoints(vtkSmoothPoints); m_FiberPolyData->SetLines(vtkSmoothCells); UpdateColorCoding(); UpdateFiberGeometry(); } // Resample fiber to get equidistant points void mitk::FiberBundleX::ResampleFibers(float pointDistance) { vtkSmartPointer newPoly = vtkSmartPointer::New(); vtkSmartPointer newCellArray = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); vtkSmartPointer vLines = m_FiberPolyData->GetLines(); vLines->InitTraversal(); int numberOfLines = m_NumFibers; for (int i=0; iGetNextCell ( numPoints, points ); vtkSmartPointer container = vtkSmartPointer::New(); double* point = m_FiberPolyData->GetPoint(points[0]); vtkIdType pointId = newPoints->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); float dtau = 0; int cur_p = 1; itk::Vector dR; float normdR = 0; for (;;) { while (dtau <= pointDistance && cur_p < numPoints) { itk::Vector v1; point = m_FiberPolyData->GetPoint(points[cur_p-1]); v1[0] = point[0]; v1[1] = point[1]; v1[2] = point[2]; itk::Vector v2; point = m_FiberPolyData->GetPoint(points[cur_p]); v2[0] = point[0]; v2[1] = point[1]; v2[2] = point[2]; dR = v2 - v1; normdR = std::sqrt(dR.GetSquaredNorm()); dtau += normdR; cur_p++; } if (dtau >= pointDistance) { itk::Vector v1; point = m_FiberPolyData->GetPoint(points[cur_p-1]); v1[0] = point[0]; v1[1] = point[1]; v1[2] = point[2]; itk::Vector v2 = v1 - dR*( (dtau-pointDistance)/normdR ); pointId = newPoints->InsertNextPoint(v2.GetDataPointer()); container->GetPointIds()->InsertNextId(pointId); } else { point = m_FiberPolyData->GetPoint(points[numPoints-1]); pointId = newPoints->InsertNextPoint(point); container->GetPointIds()->InsertNextId(pointId); break; } dtau = dtau-pointDistance; } newCellArray->InsertNextCell(container); } newPoly->SetPoints(newPoints); newPoly->SetLines(newCellArray); m_FiberPolyData = newPoly; UpdateFiberGeometry(); UpdateColorCoding(); } // reapply selected colorcoding in case polydata structure has changed void mitk::FiberBundleX::UpdateColorCoding() { char* cc = GetCurrentColorCoding(); if( strcmp (COLORCODING_ORIENTATION_BASED,cc) == 0 ) DoColorCodingOrientationBased(); else if( strcmp (COLORCODING_FA_BASED,cc) == 0 ) DoColorCodingFaBased(); } // reapply selected colorcoding in case polydata structure has changed bool mitk::FiberBundleX::Equals(mitk::FiberBundleX* fib) { if (fib==NULL) return false; mitk::FiberBundleX::Pointer tempFib = this->SubtractBundle(fib); mitk::FiberBundleX::Pointer tempFib2 = fib->SubtractBundle(this); if (tempFib.IsNull() && tempFib2.IsNull()) return true; return false; } /* ESSENTIAL IMPLEMENTATION OF SUPERCLASS METHODS */ void mitk::FiberBundleX::UpdateOutputInformation() { } void mitk::FiberBundleX::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::FiberBundleX::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::FiberBundleX::VerifyRequestedRegion() { return true; } void mitk::FiberBundleX::SetRequestedRegion( itk::DataObject *data ) { } diff --git a/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.h b/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.h index a6a6f5e76f..05ea707efb 100644 --- a/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.h +++ b/Modules/DiffusionImaging/IODataStructures/FiberBundleX/mitkFiberBundleX.h @@ -1,122 +1,125 @@ /*=================================================================== 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 _MITK_FiberBundleX_H #define _MITK_FiberBundleX_H //includes for MITK datastructure #include #include "MitkDiffusionImagingExports.h" #include //includes storing fiberdata #include //may be replaced by class precompile argument #include // may be replaced by class #include // my be replaced by class #include #include #include namespace mitk { /** * \brief Base Class for Fiber Bundles; */ class MitkDiffusionImaging_EXPORT FiberBundleX : public BaseData { public: // fiber colorcodings static const char* COLORCODING_ORIENTATION_BASED; static const char* COLORCODING_FA_BASED; static const char* COLORCODING_CUSTOM; static const char* FIBER_ID_ARRAY; virtual void UpdateOutputInformation(); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual bool VerifyRequestedRegion(); virtual void SetRequestedRegion( itk::DataObject *data ); mitkClassMacro( FiberBundleX, BaseData ) itkNewMacro( Self ) mitkNewMacro1Param(Self, vtkSmartPointer) // custom constructor // colorcoding related methods void SetColorCoding(const char*); void SetFAMap(mitk::Image::Pointer); void DoColorCodingOrientationBased(); void DoColorCodingFaBased(); void DoUseFaFiberOpacity(); void ResetFiberOpacity(); // fiber smoothing/resampling void ResampleFibers(float pointDistance = 1); void DoFiberSmoothing(int pointsPerCm); + bool RemoveShortFibers(float lengthInMM); + void MirrorFibers(unsigned int axis); // add/subtract fibers FiberBundleX::Pointer AddBundle(FiberBundleX* fib); FiberBundleX::Pointer SubtractBundle(FiberBundleX* fib); // fiber subset extraction - FiberBundleX::Pointer ExtractFiberSubset(PlanarFigure *pf); - std::vector ExtractFiberIdSubset(PlanarFigure* pf); - vtkSmartPointer GeneratePolyDataByIds( std::vector ); + FiberBundleX::Pointer ExtractFiberSubset(PlanarFigure *pf); + std::vector ExtractFiberIdSubset(PlanarFigure* pf); + vtkSmartPointer GeneratePolyDataByIds( std::vector ); // TODO: make protected + void GenerateFiberIds(); // TODO: make protected // get/set data void SetFiberPolyData(vtkSmartPointer, bool updateGeometry = true); vtkSmartPointer GetFiberPolyData(); QStringList GetAvailableColorCodings(); char* GetCurrentColorCoding(); itkGetMacro(NumFibers, int) // copy fiber bundle mitk::FiberBundleX::Pointer GetDeepCopy(); + // compare fiber bundles bool Equals(FiberBundleX* fib); - void GenerateFiberIds(); protected: FiberBundleX( vtkPolyData* fiberPolyData = NULL ); virtual ~FiberBundleX(); itk::Point GetItkPoint(double point[3]); // calculate geometry from fiber extent void UpdateFiberGeometry(); // calculate colorcoding values according to m_CurrentColorCoding void UpdateColorCoding(); private: // actual fiber container vtkSmartPointer m_FiberPolyData; // contains fiber ids vtkSmartPointer m_FiberIdDataSet; char* m_CurrentColorCoding; int m_NumFibers; }; } // namespace mitk #endif /* _MITK_FiberBundleX_H */ diff --git a/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageReader.h b/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageReader.h index a09ac8c638..e3a1333873 100644 --- a/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageReader.h +++ b/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageReader.h @@ -1,69 +1,70 @@ /*=================================================================== 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. ===================================================================*/ #ifndef __mitkNrrdQBallImageReader_h #define __mitkNrrdQBallImageReader_h #include "mitkCommon.h" #include "itkVectorContainer.h" #include "mitkFileReader.h" #include "vnl/vnl_vector_fixed.h" #include "mitkQBallImage.h" #include "mitkQBallImageSource.h" #include "itkVectorImage.h" +#include namespace mitk { - /** \brief + /** \brief */ - class NrrdQBallImageReader : public mitk::QBallImageSource, public FileReader + class MitkDiffusionImaging_EXPORT NrrdQBallImageReader : public mitk::QBallImageSource, public FileReader { public: typedef mitk::QBallImage OutputType; typedef mitk::QBallImageSource QBImgSourceType; - + mitkClassMacro( NrrdQBallImageReader, QBImgSourceType ); itkNewMacro(Self); const char* GetFileName() const; void SetFileName(const char* aFileName); const char* GetFilePrefix() const; void SetFilePrefix(const char* aFilePrefix); const char* GetFilePattern() const; void SetFilePattern(const char* aFilePattern); static bool CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern); protected: /** Does the real work. */ virtual void GenerateData(); virtual void GenerateOutputInformation(); std::string m_FileName; std::string m_FilePrefix; std::string m_FilePattern; private: void operator=(const Self&); //purposely not implemented }; } //namespace MITK #endif // __mitkNrrdQBallImageReader_h diff --git a/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageWriter.h b/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageWriter.h index f8d129e561..0f7e637c9c 100644 --- a/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageWriter.h +++ b/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkNrrdQBallImageWriter.h @@ -1,129 +1,130 @@ /*=================================================================== 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. ===================================================================*/ #ifndef _MITK_NRRDQBI_WRITER__H_ #define _MITK_NRRDQBI_WRITER__H_ #include #include #include +#include namespace mitk { /** * Writes diffusion volumes to a file * @ingroup Process */ -class NrrdQBallImageWriter : public mitk::FileWriterWithInformation +class MitkDiffusionImaging_EXPORT NrrdQBallImageWriter : public mitk::FileWriterWithInformation { public: mitkClassMacro( NrrdQBallImageWriter, mitk::FileWriterWithInformation ); mitkWriterMacro; itkNewMacro( Self ); - + typedef mitk::QBallImage InputType; - + /** * Sets the filename of the file to write. * @param FileName the name of the file to write. */ itkSetStringMacro( FileName ); /** * @returns the name of the file to be written to disk. */ itkGetStringMacro( FileName ); /** * @warning multiple write not (yet) supported */ itkSetStringMacro( FilePrefix ); /** * @warning multiple write not (yet) supported */ itkGetStringMacro( FilePrefix ); /** * @warning multiple write not (yet) supported */ itkSetStringMacro( FilePattern ); /** * @warning multiple write not (yet) supported */ itkGetStringMacro( FilePattern ); /** * Sets the input object for the filter. * @param input the diffusion volumes to write to file. */ void SetInput( InputType* input ); /** * @returns the 0'th input object of the filter. */ InputType* GetInput(); /** * Returns false if an error happened during writing */ itkGetMacro( Success, bool ); - + /** * @return possible file extensions for the data type associated with the writer */ virtual std::vector GetPossibleFileExtensions(); // FileWriterWithInformation methods virtual const char * GetDefaultFilename() { return "QBalls.qbi"; } virtual const char * GetFileDialogPattern() { return "Q-Ball Images (*.qbi *.hqbi"; } virtual const char * GetDefaultExtension() { return ".qbi"; } - virtual bool CanWriteBaseDataType(BaseData::Pointer data) { return (dynamic_cast(data.GetPointer()) != NULL); }; - virtual void DoWrite(BaseData::Pointer data) { + virtual bool CanWriteBaseDataType(BaseData::Pointer data) { return (dynamic_cast(data.GetPointer()) != NULL); }; + virtual void DoWrite(BaseData::Pointer data) { if (CanWriteBaseDataType(data)) { - this->SetInput(dynamic_cast(data.GetPointer())); - this->Update(); + this->SetInput(dynamic_cast(data.GetPointer())); + this->Update(); } }; protected: - + NrrdQBallImageWriter(); virtual ~NrrdQBallImageWriter(); virtual void GenerateData(); - + std::string m_FileName; std::string m_FilePrefix; std::string m_FilePattern; - + bool m_Success; - -}; - + +}; + } // end of namespace mitk #endif //_MITK_NRRDQBI_WRITER__H_ diff --git a/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkQBallImageSource.h b/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkQBallImageSource.h index c75c833c7d..eb4cc66e4a 100644 --- a/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkQBallImageSource.h +++ b/Modules/DiffusionImaging/IODataStructures/QBallImages/mitkQBallImageSource.h @@ -1,48 +1,49 @@ /*=================================================================== 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. ===================================================================*/ #ifndef QBallImageSource_H_HEADER_INCLUDED_C1E7D6EC #define QBallImageSource_H_HEADER_INCLUDED_C1E7D6EC #include "mitkImageSource.h" #include "mitkQBallImage.h" +#include namespace mitk { -class QBallImageSource : public ImageSource +class MitkDiffusionImaging_EXPORT QBallImageSource : public ImageSource { public: typedef mitk::QBallImage OutputImageType; typedef OutputImageType::Pointer OutputImagePointer; typedef SlicedData::RegionType OutputImageRegionType; typedef itk::DataObject::Pointer DataObjectPointer; - + mitkClassMacro(QBallImageSource,ImageSource); - itkNewMacro(Self); + itkNewMacro(Self); virtual DataObjectPointer MakeOutput(unsigned int idx); protected: QBallImageSource(); virtual ~QBallImageSource() {} - + }; } // namespace mitk #endif /* QBallImageSource_H_HEADER_INCLUDED_C1E7D6EC */ diff --git a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp index 2b926f69b2..c2926481b5 100644 --- a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp +++ b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp @@ -1,413 +1,413 @@ /*=================================================================== 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 __mitkNrrdTbssImageReader_cpp #define __mitkNrrdTbssImageReader_cpp #include "mitkNrrdTbssImageReader.h" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkNiftiImageIO.h" #include #include #include #include "itksys/SystemTools.hxx" namespace mitk { void NrrdTbssImageReader ::GenerateData() { // Since everything is completely read in GenerateOutputInformation() it is stored // in a cache variable. A timestamp is associated. // If the timestamp of the cache variable is newer than the MTime, we only need to // assign the cache variable to the DataObject. // Otherwise, the tree must be read again from the file and OuputInformation must // be updated! if ( ( ! m_OutputCache ) || ( this->GetMTime( ) > m_CacheTime.GetMTime( ) ) ) { this->GenerateOutputInformation(); itkWarningMacro("Cache regenerated!"); } if (!m_OutputCache) { itkWarningMacro("Tree cache is empty!") } int vecsize = m_OutputCache->GetImage()->GetVectorLength(); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetImage(m_OutputCache->GetImage()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetGroupInfo(m_OutputCache->GetGroupInfo()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetMetaInfo(m_OutputCache->GetMetaInfo()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetIsMeta(m_OutputCache->GetIsMeta()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetContainsDistanceMap(m_OutputCache->GetContainsDistanceMap()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetContainsMeanSkeleton(m_OutputCache->GetContainsMeanSkeleton()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetContainsSkeletonMask(m_OutputCache->GetContainsSkeletonMask()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->SetContainsGradient(m_OutputCache->GetContainsGradient()); - static_cast(this->GetOutput()) + static_cast(this->GetOutput(0)) ->InitializeFromVectorImage(); } void NrrdTbssImageReader ::GenerateOutputInformation() { OutputType::Pointer outputForCache = OutputType::New(); if ( m_FileName == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename to be read is empty!"); } else { try { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { MITK_INFO << " ** Changing locale from " << setlocale(LC_ALL, NULL) << " to '" << locale << "'"; setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } MITK_INFO << "NrrdTbssImageReader READING IMAGE INFORMATION"; ImageType::Pointer img; std::string ext = itksys::SystemTools::GetFilenameLastExtension(m_FileName); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".tbss") { typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(this->m_FileName); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); reader->SetImageIO(io); reader->Update(); img = reader->GetOutput(); MITK_INFO << "NrrdTbssImageReader READING HEADER INFORMATION"; itk::MetaDataDictionary imgMetaDictionary = img->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; //int numberOfGradientImages = 0; std::string measurementInfo; bool isMeta = false; bool containsSkeleton; bool containsSkeletonMask; bool containsGradient; bool containsDistanceMap; std::vector > metaInfo; std::vector< std::pair > groups; for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); MITK_INFO << *itKey << " ---> " << metaString; if (itKey->find("Group_index") != std::string::npos) { std::vector tokens; this->Tokenize(metaString, tokens, " "); std::pair< std::string, int > p; p.first=""; for (int i=0; ifind("Measurement info") != std::string::npos) { measurementInfo = metaString; } else if(itKey->find("meta") != std::string::npos) { if(metaString == "true") { isMeta = true; } } else if(itKey->find("mean fa skeleton mask") != std::string::npos) { std::pair p; p.first = mitk::TbssImage::MEAN_FA_SKELETON_MASK; p.second = atoi(metaString.c_str()); metaInfo.push_back(p); containsSkeletonMask = true; } else if(itKey->find("mean fa skeleton") != std::string::npos) { std::pair p; p.first = mitk::TbssImage::MEAN_FA_SKELETON; p.second = atoi(metaString.c_str()); metaInfo.push_back(p); containsSkeleton = true; } else if(itKey->find("gradient_x") != std::string::npos) { std::pair p; p.first = mitk::TbssImage::GRADIENT_X; p.second = atoi(metaString.c_str()); metaInfo.push_back(p); containsGradient = true; } else if(itKey->find("gradient_y") != std::string::npos) { std::pair p; p.first = mitk::TbssImage::GRADIENT_Y; p.second = atoi(metaString.c_str()); metaInfo.push_back(p); containsGradient = true; } else if(itKey->find("gradient_z") != std::string::npos) { std::pair p; p.first = mitk::TbssImage::GRADIENT_Z; p.second = atoi(metaString.c_str()); metaInfo.push_back(p); containsGradient = true; } else if(itKey->find("tubular structure") != std::string::npos) { std::pair p; p.first = mitk::TbssImage::TUBULAR_STRUCTURE; p.second = atoi(metaString.c_str()); metaInfo.push_back(p); } else if(itKey->find("distance map") != std::string::npos) { std::pair p; p.first = mitk::TbssImage::DISTANCE_MAP; p.second = atoi(metaString.c_str()); metaInfo.push_back(p); containsDistanceMap = true; } } outputForCache->SetIsMeta(isMeta); outputForCache->SetContainsGradient(containsGradient); outputForCache->SetContainsSkeletonMask(containsSkeletonMask); outputForCache->SetContainsMeanSkeleton(containsSkeleton); outputForCache->SetContainsDistanceMap(containsDistanceMap); outputForCache->SetGroupInfo(groups); outputForCache->SetMeasurementInfo(measurementInfo); outputForCache->SetMetaInfo(metaInfo); } // This call updates the output information of the associated VesselTreeData outputForCache->SetImage(img); // outputForCache->SetB_Value(m_B_Value); //outputForCache->SetDirections(m_DiffusionVectors); // outputForCache->SetOriginalDirections(m_OriginalDiffusionVectors); // outputForCache->SetMeasurementFrame(m_MeasurementFrame); // Since we have already read the tree, we can store it in a cache variable // so that it can be assigned to the DataObject in GenerateData(); m_OutputCache = outputForCache; m_CacheTime.Modified(); try { MITK_INFO << " ** Changing locale back from " << setlocale(LC_ALL, NULL) << " to '" << currLocale << "'"; setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } } catch(std::exception& e) { MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } } const char* NrrdTbssImageReader ::GetFileName() const { return m_FileName.c_str(); } void NrrdTbssImageReader ::SetFileName(const char* aFileName) { m_FileName = aFileName; } const char* NrrdTbssImageReader ::GetFilePrefix() const { return m_FilePrefix.c_str(); } void NrrdTbssImageReader ::SetFilePrefix(const char* aFilePrefix) { m_FilePrefix = aFilePrefix; } const char* NrrdTbssImageReader ::GetFilePattern() const { return m_FilePattern.c_str(); } void NrrdTbssImageReader ::SetFilePattern(const char* aFilePattern) { m_FilePattern = aFilePattern; } bool NrrdTbssImageReader ::CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern) { // First check the extension if( filename == "" ) return false; // check if image is serie if( filePattern != "" && filePrefix != "" ) return false; std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".tbss") { itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(filename); try { reader->Update(); } catch(itk::ExceptionObject e) { MITK_INFO << e.GetDescription(); return false; } /* typename ImageType::Pointer img = reader->GetOutput(); itk::MetaDataDictionary imgMetaDictionary = img->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("tbss") != std::string::npos) { if (metaString.find("ROI") != std::string::npos) { return true; } } } } */ // return false; return true; } return false; } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp index 54812008dc..77e3c61c48 100644 --- a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp +++ b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp @@ -1,363 +1,363 @@ /*=================================================================== 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 __mitkNrrdTbssRoiReader_cpp #define __mitkNrrdTbssRoiReader_cpp #include "mitkNrrdTbssRoiImageReader.h" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkNiftiImageIO.h" #include #include #include #include "itksys/SystemTools.hxx" namespace mitk { void NrrdTbssRoiImageReader ::GenerateData() { try { // Change locale if needed const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, NULL ); if ( locale.compare(currLocale)!=0 ) { try { MITK_INFO << " ** Changing locale from " << setlocale(LC_ALL, NULL) << " to '" << locale << "'"; setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } // READ IMAGE INFORMATION const unsigned int MINDIM = 3; const unsigned int MAXDIM = 4; MITK_INFO << "loading " << m_FileName << " via mitk::NrrdTbssImageReader... " << std::endl; // Check to see if we can read the file given the name or prefix if ( m_FileName == "" ) { itkWarningMacro( << "Filename is empty!" ) return; } itk::NrrdImageIO::Pointer imageIO = itk::NrrdImageIO::New(); imageIO->SetFileName( m_FileName.c_str() ); imageIO->ReadImageInformation(); unsigned int ndim = imageIO->GetNumberOfDimensions(); if ( ndim < MINDIM || ndim > MAXDIM ) { itkWarningMacro( << "Sorry, only dimensions 3 is supported. The given file has " << ndim << " dimensions!" ) return; } itk::ImageIORegion ioRegion( ndim ); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[ MAXDIM ]; dimensions[ 0 ] = 0; dimensions[ 1 ] = 0; dimensions[ 2 ] = 0; dimensions[ 3 ] = 0; float spacing[ MAXDIM ]; spacing[ 0 ] = 1.0f; spacing[ 1 ] = 1.0f; spacing[ 2 ] = 1.0f; spacing[ 3 ] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for ( i = 0; i < ndim ; ++i ) { ioStart[ i ] = 0; ioSize[ i ] = imageIO->GetDimensions( i ); if(iGetDimensions( i ); spacing[ i ] = imageIO->GetSpacing( i ); if(spacing[ i ] <= 0) spacing[ i ] = 1.0f; } if(i<3) { origin[ i ] = imageIO->GetOrigin( i ); } } ioRegion.SetSize( ioSize ); ioRegion.SetIndex( ioStart ); MITK_INFO << "ioRegion: " << ioRegion << std::endl; imageIO->SetIORegion( ioRegion ); void* buffer = new unsigned char[imageIO->GetImageSizeInBytes()]; imageIO->Read( buffer ); //mitk::Image::Pointer static_cast(this->GetOutput())image = mitk::Image::New(); if((ndim==4) && (dimensions[3]<=1)) ndim = 3; if((ndim==3) && (dimensions[2]<=1)) ndim = 2; mitk::PixelType pixelType = mitk::PixelType(imageIO->GetComponentTypeInfo(), imageIO->GetComponentTypeInfo(), imageIO->GetComponentSize(), imageIO->GetNumberOfComponents(), imageIO->GetComponentTypeAsString( imageIO->GetComponentType() ).c_str(), imageIO->GetPixelTypeAsString( imageIO->GetPixelType() ).c_str() ); //pixelType.Initialize( imageIO->GetComponentTypeInfo(), imageIO->GetNumberOfComponents(), imageIO->GetPixelType() ); - static_cast(this->GetOutput())->Initialize( pixelType, ndim, dimensions ); - static_cast(this->GetOutput())->SetImportChannel( buffer, 0, Image::ManageMemory ); + static_cast(this->GetOutput(0))->Initialize( pixelType, ndim, dimensions ); + static_cast(this->GetOutput(0))->SetImportChannel( buffer, 0, Image::ManageMemory ); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3? 3 : ndim); for ( i=0; i < itkDimMax3; ++i) for( j=0; j < itkDimMax3; ++j ) matrix[i][j] = imageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = static_cast (static_cast - (this->GetOutput())->GetSlicedGeometry(0)->GetGeometry2D(0)); + (this->GetOutput(0))->GetSlicedGeometry(0)->GetGeometry2D(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D - SlicedGeometry3D* slicedGeometry = static_cast(this->GetOutput())->GetSlicedGeometry(0); - slicedGeometry->InitializeEvenlySpaced(planeGeometry, static_cast(this->GetOutput())->GetDimension(2)); + SlicedGeometry3D* slicedGeometry = static_cast(this->GetOutput(0))->GetSlicedGeometry(0); + slicedGeometry->InitializeEvenlySpaced(planeGeometry, static_cast(this->GetOutput(0))->GetDimension(2)); slicedGeometry->SetSpacing(spacing); // re-initialize TimeSlicedGeometry - static_cast(this->GetOutput())->GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, static_cast(this->GetOutput())->GetDimension(3)); + static_cast(this->GetOutput(0))->GetTimeSlicedGeometry()->InitializeEvenlyTimed(slicedGeometry, static_cast(this->GetOutput(0))->GetDimension(3)); buffer = NULL; - MITK_INFO << "number of image components: "<< static_cast(this->GetOutput())->GetPixelType().GetNumberOfComponents() << std::endl; + MITK_INFO << "number of image components: "<< static_cast(this->GetOutput(0))->GetPixelType().GetNumberOfComponents() << std::endl; // READ TBSS HEADER INFORMATION ImageType::Pointer img; std::string ext = itksys::SystemTools::GetFilenameLastExtension(m_FileName); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".roi") { typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(this->m_FileName); reader->SetImageIO(imageIO); reader->Update(); img = reader->GetOutput(); - static_cast(this->GetOutput())->SetImage(img); + static_cast(this->GetOutput(0))->SetImage(img); itk::MetaDataDictionary imgMetaDictionary = img->GetMetaDataDictionary(); ReadRoiInfo(imgMetaDictionary); } // RESET LOCALE try { MITK_INFO << " ** Changing locale back from " << setlocale(LC_ALL, NULL) << " to '" << currLocale << "'"; setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } MITK_INFO << "...finished!" << std::endl; } catch(std::exception& e) { MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } void NrrdTbssRoiImageReader ::ReadRoiInfo(itk::MetaDataDictionary dict) { std::vector imgMetaKeys = dict.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; std::vector< itk::Index<3> > roi; for (; itKey != imgMetaKeys.end(); itKey ++) { double x,y,z; itk::Index<3> ix; itk::ExposeMetaData (dict, *itKey, metaString); if (itKey->find("ROI_index") != std::string::npos) { MITK_INFO << *itKey << " ---> " << metaString; sscanf(metaString.c_str(), "%lf %lf %lf\n", &x, &y, &z); ix[0] = x; ix[1] = y; ix[2] = z; roi.push_back(ix); } else if(itKey->find("preprocessed FA") != std::string::npos) { MITK_INFO << *itKey << " ---> " << metaString; - static_cast(this->GetOutput())->SetPreprocessedFA(true); - static_cast(this->GetOutput())->SetPreprocessedFAFile(metaString); + static_cast(this->GetOutput(0))->SetPreprocessedFA(true); + static_cast(this->GetOutput(0))->SetPreprocessedFAFile(metaString); } // Name of structure if (itKey->find("structure") != std::string::npos) { MITK_INFO << *itKey << " ---> " << metaString; - static_cast(this->GetOutput())->SetStructure(metaString); + static_cast(this->GetOutput(0))->SetStructure(metaString); } } - static_cast(this->GetOutput())->SetRoi(roi); + static_cast(this->GetOutput(0))->SetRoi(roi); } const char* NrrdTbssRoiImageReader ::GetFileName() const { return m_FileName.c_str(); } void NrrdTbssRoiImageReader ::SetFileName(const char* aFileName) { m_FileName = aFileName; } const char* NrrdTbssRoiImageReader ::GetFilePrefix() const { return m_FilePrefix.c_str(); } void NrrdTbssRoiImageReader ::SetFilePrefix(const char* aFilePrefix) { m_FilePrefix = aFilePrefix; } const char* NrrdTbssRoiImageReader ::GetFilePattern() const { return m_FilePattern.c_str(); } void NrrdTbssRoiImageReader ::SetFilePattern(const char* aFilePattern) { m_FilePattern = aFilePattern; } bool NrrdTbssRoiImageReader ::CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern) { // First check the extension if( filename == "" ) return false; // check if image is serie if( filePattern != "" && filePrefix != "" ) return false; std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".roi") { itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetImageIO(io); reader->SetFileName(filename); try { reader->Update(); } catch(itk::ExceptionObject e) { MITK_INFO << e.GetDescription(); return false; } return true; } return false; } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.cpp b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.cpp index 820009b6cc..d80fb67c68 100644 --- a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.cpp +++ b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.cpp @@ -1,51 +1,51 @@ /*=================================================================== 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 __MITK_NRRD_TBSS_VOULMES_IO_FACTORY_CPP__ #define __MITK_NRRD_TBSS_VOULMES_IO_FACTORY_CPP__ #include "mitkTbssImageSource.h" #include "mitkTbssImage.h" mitk::TbssImageSource::TbssImageSource() { // Create the output. We use static_cast<> here because we know the default // output must be of type DiffusionImage mitk::TbssImage::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, output.GetPointer()); } itk::DataObject::Pointer mitk::TbssImageSource::MakeOutput( unsigned int /*idx*/ ) { return static_cast(mitk::TbssImage::New().GetPointer()); } -/* -template -mitk::TbssImageSource::OutputType* mitk::TbssImageSource::GetOutput(unsigned int idx) + + +mitk::TbssImageSource::OutputType* mitk::TbssImageSource::GetOutput(unsigned int idx) { return static_cast (this->ProcessObject::GetOutput(idx)); } -*/ + #endif //__MITK_NRRD_TBSS_VOULMES_IO_FACTORY_CPP__ diff --git a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.h b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.h index a7cbdb2160..29d175fa4a 100644 --- a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.h +++ b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssImageSource.h @@ -1,75 +1,75 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_TBSS_IMAGE_DATA_SOURCE_H_HEADER_ #define _MITK_TBSS_IMAGE_DATA_SOURCE_H_HEADER_ // Should be changed in a new type for TBSS #include "mitkImageSource.h" #include "MitkDiffusionImagingExports.h" namespace mitk { class TbssImage; //##Documentation //## @brief Superclass of all classes generating diffusion volumes (instances //## of class DiffusionImage) as output. //## //## @ingroup Process class MitkDiffusionImaging_EXPORT TbssImageSource : public ImageSource { public: mitkClassMacro(TbssImageSource, BaseProcess) itkNewMacro(Self) typedef TbssImage OutputType; typedef itk::DataObject::Pointer DataObjectPointer; virtual DataObjectPointer MakeOutput(unsigned int idx); //OutputType * GetOutput(unsigned int idx); //void SetOutput(OutputType* output); //OutputType* GetOutput() //{return Superclass::GetOutput();} - //OutputType* GetOutput(unsigned int idx); + OutputType* GetOutput(unsigned int idx); //virtual void GraftOutput(OutputType* graft); //virtual void GraftNthOutput(unsigned int idx, OutputType *graft); protected: TbssImageSource(); virtual ~TbssImageSource(){} }; } // namespace mitk //#include "mitkTbssImageSource.cpp" #endif /* _MITK_DIFFUSION_IMAGE_DATA_SOURCE_H_HEADER_ */ diff --git a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp index 4d4ba13ccb..841eda40f5 100644 --- a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp +++ b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp @@ -1,51 +1,51 @@ /*=================================================================== 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 __MITK_NRRD_TBSSROI_VOLUMES_IO_FACTORY_CPP__ #define __MITK_NRRD_TBSSROI_VOLUMES_IO_FACTORY_CPP__ #include "mitkTbssRoiImageSource.h" #include "mitkTbssRoiImage.h" mitk::TbssRoiImageSource::TbssRoiImageSource() { // Create the output. We use static_cast<> here because we know the default // output must be of type DiffusionImage mitk::TbssRoiImage::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, output.GetPointer()); } itk::DataObject::Pointer mitk::TbssRoiImageSource::MakeOutput( unsigned int /*idx*/ ) { return static_cast(mitk::TbssRoiImage::New().GetPointer()); } -/* -template -mitk::TbssImageSource::OutputType* mitk::TbssImageSource::GetOutput(unsigned int idx) + + +mitk::TbssRoiImageSource::OutputType* mitk::TbssRoiImageSource::GetOutput(unsigned int idx) { return static_cast (this->ProcessObject::GetOutput(idx)); } -*/ + #endif //__MITK_NRRD_TBSS_VOULMES_IO_FACTORY_CPP__ diff --git a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.h b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.h index bd4affc767..3d318684ea 100644 --- a/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.h +++ b/Modules/DiffusionImaging/IODataStructures/TbssImages/mitkTbssRoiImageSource.h @@ -1,75 +1,75 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_TBSS_ROI_IMAGE_DATA_SOURCE_H_HEADER_ #define _MITK_TBSS_ROI_IMAGE_DATA_SOURCE_H_HEADER_ // Should be changed in a new type for TBSS #include "mitkImageSource.h" #include "MitkDiffusionImagingExports.h" namespace mitk { class TbssRoiImage; //##Documentation //## @brief Superclass of all classes generating diffusion volumes (instances //## of class DiffusionImage) as output. //## //## @ingroup Process class MitkDiffusionImaging_EXPORT TbssRoiImageSource : public ImageSource { public: mitkClassMacro(TbssRoiImageSource, BaseProcess) itkNewMacro(Self) typedef TbssRoiImage OutputType; typedef itk::DataObject::Pointer DataObjectPointer; virtual DataObjectPointer MakeOutput(unsigned int idx); - //OutputType * GetOutput(unsigned int idx); + OutputType * GetOutput(unsigned int idx); //void SetOutput(OutputType* output); - //OutputType* GetOutput() + OutputType* GetOutput(); //{return Superclass::GetOutput();} //OutputType* GetOutput(unsigned int idx); //virtual void GraftOutput(OutputType* graft); //virtual void GraftNthOutput(unsigned int idx, OutputType *graft); protected: TbssRoiImageSource(); virtual ~TbssRoiImageSource(){} }; } // namespace mitk //#include "mitkTbssRoiImageSource.cpp" #endif /* _MITK_DIFFUSION_IMAGE_DATA_SOURCE_H_HEADER_ */ diff --git a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp index e04b5e81c7..7bd21ff93e 100644 --- a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp +++ b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp @@ -1,1133 +1,1128 @@ /*=================================================================== 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 __itkDiffusionMultiShellQballReconstructionImageFilter_cpp #define __itkDiffusionMultiShellQballReconstructionImageFilter_cpp #include #include #include #include #include #include #include #include #include #include - -#define _USE_MATH_DEFINES -#include - #include "mitkDiffusionFunctionCollection.h" #include "itkPointShell.h" #include namespace itk { template< class T, class TG, class TO, int L, int NODF> DiffusionMultiShellQballReconstructionImageFilter ::DiffusionMultiShellQballReconstructionImageFilter() : m_GradientDirectionContainer(NULL), m_NumberOfGradientDirections(0), m_NumberOfBaselineImages(1), m_Threshold(NumericTraits< ReferencePixelType >::NonpositiveMin()), m_BValue(1.0), m_Lambda(0.0), m_IsHemisphericalArrangementOfGradientDirections(false), m_IsArithmeticProgession(false), m_ReconstructionType(Mode_Standard1Shell) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template void DiffusionMultiShellQballReconstructionImageFilter ::Normalize( OdfPixelType & out) { for(int i=0; i double DiffusionMultiShellQballReconstructionImageFilter ::CalculateThreashold(const double value, const double delta) { return (value<0)*(0.5*delta) + (value>=0 && value=delta && value<1-delta)*value+(value>=1-delta && value<1)*(1-0.5*delta-0.5*((1-value)*(1-value))/delta) + (value>=1)*(1-0.5*delta); } template void DiffusionMultiShellQballReconstructionImageFilter ::Threshold(vnl_vector & vec, double delta) { if (delta==0){ //Clip attenuation values. If att<0 => att=0, if att>1 => att=1 for (int i=0; i=0 && vec[i]<=1)*vec[i]+(vec[i]>1); } else{ //Use function from Aganj et al, MRM, 2010 for (int i=0; i< vec.size(); i++) vec[i]=CalculateThreashold(vec[i], delta); } } template void DiffusionMultiShellQballReconstructionImageFilter ::Threshold(vnl_matrix & mat, double delta) { if (delta==0){ //Clip attenuation values. If att<0 => att=0, if att>1 => att=1 for (int i=0; i=0 && mat(i,j)<=1)*mat(i,j)+(mat(i,j)>1); } else{ //Use function from Aganj et al, MRM, 2010 for (int i=0; i void DiffusionMultiShellQballReconstructionImageFilter ::Projection1( vnl_matrix & E, double delta ) { const double sF = sqrt(5.0); vnl_vector vOnes(E.rows()); vOnes.fill(1.0); vnl_matrix T0(E.rows(), E.cols()); vnl_matrix C(E.rows(), 7); vnl_matrix A(E.rows(), 7); vnl_matrix B(E.rows(), 7); vnl_vector s0(E.rows()); vnl_vector a0(E.rows()); vnl_vector b0(E.rows()); vnl_vector ta(E.rows()); vnl_vector tb(E.rows()); vnl_vector e(E.rows()); vnl_vector m(E.rows()); vnl_vector a(E.rows()); vnl_vector b(E.rows()); // logarithmierung aller werte in E for(int i = 0 ; i < E.rows(); i++) { for(int j = 0 ; j < E.cols(); j++) { T0(i,j) = -log(E(i,j)); } } //T0 = -T0.apply(std::log); // Summeiere Zeilenweise über alle Shells sum = E1+E2+E3 for(int i = 0 ; i < E.rows(); i++) { s0[i] = T0(i,0) + T0(i,1) + T0(i,2); } for(int i = 0; i < E.rows(); i ++) { // Alle Signal-Werte auf der Ersten shell E(N,0) normiert auf s0 a0 = E(i,0) / s0[i]; // Alle Signal-Werte auf der Zweiten shell E(N,1) normiert auf s0 b0 = E(i,1) / s0[i]; } ta = a0 * 3.0; tb = b0 * 3.0; e = tb - (ta * 2.0); m = (tb * 2.0 ) + ta; for(int i = 0; i < E.rows(); i++) { C(i,0) = tb[i] < 1+3*delta && 0.5+1.5*(sF+1)*delta < ta[i] && ta[i] < 1-3* (sF+2) *delta; C(i,1) = e[i] <= -1 +3*(2*sF+5)* delta && ta[i] >= 1-3*(sF+2)*delta; C(i,2) = m[i] > 3 -3*sF*delta && -1+3*(2*sF+5)*delta < e[i] && e[i]<-3*sF*delta; C(i,3) = m[i] >= 3-3*sF*delta && e[i] >= -3 *sF * delta; C(i,4) = 2.5 + 1.5*(5+sF)*delta < m[i] && m[i] < 3-3*sF*delta && e[i] > -3*sF*delta; C(i,5) = ta[i] <= 0.5+1.5 *(sF+1)*delta && m[i] <= 2.5 + 1.5 *(5+sF) * delta; C(i,6) = !( C(i,0) || C(i,1) || C(i,2) || C(i,3) || C(i,4) || C(i,5) ); // ~ANY(C(i,[0-5] ),2) A(i,0)=(bool)C(i,0) * a0(i); A(i,1)=(bool)C(i,1) * (1.0/3.0-(sF+2)*delta); A(i,2)=(bool)C(i,2) * (0.2+0.8*a0(i)-0.4*b0(i)-delta/sF); A(i,3)=(bool)C(i,3) * (0.2+delta/sF); A(i,4)=(bool)C(i,4) * (0.2*a0(i)+0.4*b0(i)+2*delta/sF); A(i,5)=(bool)C(i,5) * (1.0/6.0+0.5*(sF+1)*delta); A(i,6)=(bool)C(i,6) * a0(i); B(i,0)=(bool)C(i,0) * (1.0/3.0+delta); B(i,1)=(bool)C(i,1) * (1.0/3.0+delta); B(i,2)=(bool)C(i,2) * (0.4-0.4*a0(i)+0.2*b0(i)-2*delta/sF); B(i,3)=(bool)C(i,3) * (0.4-3*delta/sF); B(i,4)=(bool)C(i,4) * (0.4*a0(i)+0.8*b0(i)-delta/sF); B(i,5)=(bool)C(i,5) * (1.0/3.0+delta); B(i,6)=(bool)C(i,6) * b0(i); } for(int i = 0 ; i < E.rows(); i++) { double sumA = 0; double sumB = 0; for(int j = 0 ; j < 7; j++) { sumA += A(i,j); sumB += B(i,j); } a[i] = sumA; b[i] = sumB; } for(int i = 0; i < E.rows(); i++) { E(i,0) = exp(-(a[i]*s0[i])); E(i,1) = exp(-(b[i]*s0[i])); E(i,2) = exp(-((1-a[i]-b[i])*s0[i])); } } template void DiffusionMultiShellQballReconstructionImageFilter ::Projection2( vnl_vector & A, vnl_vector & a, vnl_vector & b, double delta0) { - const double s6 = sqrt(6); + const double s6 = sqrt(6.0); const double s15 = s6/2.0; vnl_vector delta(a.size()); delta.fill(delta0); vnl_matrix AM(a.size(), 15); vnl_matrix aM(a.size(), 15); vnl_matrix bM(a.size(), 15); vnl_matrix B(a.size(), 15); AM.set_column(0, A); AM.set_column(1, A); AM.set_column(2, A); AM.set_column(3, delta); AM.set_column(4, (A+a-b - (delta*s6))/3.0); AM.set_column(5, delta); AM.set_column(6, delta); AM.set_column(7, delta); AM.set_column(8, A); AM.set_column(9, 0.2*(a*2+A-2*(s6+1)*delta)); AM.set_column(10,0.2*(b*(-2)+A+2-2*(s6+1)*delta)); AM.set_column(11, delta); AM.set_column(12, delta); AM.set_column(13, delta); AM.set_column(14, 0.5-(1+s15)*delta); aM.set_column(0, a); aM.set_column(1, a); aM.set_column(2, -delta + 1); aM.set_column(3, a); aM.set_column(4, (A*2+a*5+b+s6*delta)/6.0); aM.set_column(5, a); aM.set_column(6, -delta + 1); aM.set_column(7, 0.5*(a+b)+(1+s15)*delta); aM.set_column(8, -delta + 1); aM.set_column(9, 0.2*(a*4+A*2+(s6+1)*delta)); aM.set_column(10, -delta + 1); aM.set_column(11, (s6+3)*delta); aM.set_column(12, -delta + 1); aM.set_column(13, -delta + 1); aM.set_column(14, -delta + 1); bM.set_column(0, b); bM.set_column(1, delta); bM.set_column(2, b); bM.set_column(3, b); bM.set_column(4, (A*(-2)+a+b*5-s6*delta)/6.0); bM.set_column(5, delta); bM.set_column(6, b); bM.set_column(7, 0.5*(a+b)-(1+s15)*delta); bM.set_column(8, delta); bM.set_column(9, delta); bM.set_column(10, 0.2*(b*4-A*2+1-(s6+1)*delta)); bM.set_column(11, delta); bM.set_column(12, delta); bM.set_column(13, -delta*(s6+3) + 1); bM.set_column(14, delta); delta0 *= 0.99; for(int i = 0 ; i < a.size(); i ++) { for(int j = 0 ; j < 15; j ++) { B(i,j) = delta0 < AM(i,j) && 2 * (AM(i,j) + delta0 * s15) < aM(i,j) - bM(i,j) && bM(i,j) > delta0 && aM(i,j) < 1- delta0; } } vnl_matrix R2(a.size(), 15); vnl_matrix A_(a.size(), 15); vnl_matrix a_(a.size(), 15); vnl_matrix b_(a.size(), 15); vnl_matrix OnesVecMat(1, 15); OnesVecMat.fill(1.0); vnl_matrix AVecMat(a.size(), 1); AVecMat.set_column(0,A); vnl_matrix aVecMat(a.size(), 1); aVecMat.set_column(0,a); vnl_matrix bVecMat(a.size(), 1); bVecMat.set_column(0,b); A_ = AM - (AVecMat * OnesVecMat); a_ = aM - (aVecMat * OnesVecMat); b_ = bM - (bVecMat * OnesVecMat); for(int i = 0 ; i < a.size(); i++) for(int j = 0 ; j < 15; j++) { A_(i,j) *= A_(i,j); a_(i,j) *= a_(i,j); b_(i,j) *= b_(i,j); } R2 = A_ + a_ + b_; for(int i = 0 ; i < a.size(); i ++) { for(int j = 0 ; j < 15; j ++) { if(B(i,j) == 0) R2(i,j) = 1e20; } } std::vector indicies(a.size()); // suche den spalten-index der zu der kleinsten Zahl einer Zeile korrespondiert for(int i = 0 ; i < a.size(); i++) { unsigned int index = 0; double minvalue = 999; for(int j = 0 ; j < 15 ; j++) { if(R2(i,j) < minvalue){ minvalue = R2(i,j); index = j; } } indicies[i] = index; } for(int i = 0 ; i < a.size(); i++) { A[i] = AM(i,indicies[i]); a[i] = aM(i,indicies[i]); b[i] = bM(i,indicies[i]); } } template void DiffusionMultiShellQballReconstructionImageFilter ::S_S0Normalization( vnl_vector & vec, typename NumericTraits::AccumulateType b0 ) { double b0f = (double)b0; for(int i = 0; i < vec.size(); i++) { if (b0f==0) b0f = 0.01; if(vec[i] >= b0f) vec[i] = b0f - 0.001; vec[i] /= b0f; } } template void DiffusionMultiShellQballReconstructionImageFilter ::S_S0Normalization( vnl_matrix & mat, typename NumericTraits::AccumulateType b0 ) { double b0f = (double)b0; for(int i = 0; i < mat.rows(); i++) { for( int j = 0; j < mat.cols(); j++ ){ if (b0f==0) b0f = 0.01; if(mat(i,j) >= b0f) mat(i,j) = b0f - 0.001; mat(i,j) /= b0f; } } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::DoubleLogarithm(vnl_vector & vec) { for(int i = 0; i < vec.size(); i++) { vec[i] = log(-log(vec[i])); } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::SetGradientImage( GradientDirectionContainerType *gradientDirection , const GradientImagesType *gradientImage , float bvalue) { this->m_BValue = bvalue; this->m_GradientDirectionContainer = gradientDirection; this->m_NumberOfBaselineImages = 0; this->m_ReconstructionType = Mode_Standard1Shell; - GradientDirectionContainerType::ConstIterator gdcit; - for( gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) - { - double bValueKey = int(((m_BValue* gdcit.Value().two_norm() * gdcit.Value().two_norm())+7.5)/10)*10; - m_GradientIndexMap[bValueKey].push_back(gdcit.Index()); + if(m_BValueMap.size() == 0){ + itkWarningMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : no GradientIndexMapAvalible"); + + GradientDirectionContainerType::ConstIterator gdcit; + for( gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) + { + double bValueKey = int(((m_BValue * gdcit.Value().two_norm() * gdcit.Value().two_norm())+7.5)/10)*10; + m_BValueMap[bValueKey].push_back(gdcit.Index()); + } + } - //if(listOfUserSelctedBValues.size() == 0){ - // itkExceptionMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : No list Of User Selcted B Values available"); - //} - if(m_GradientIndexMap.size() == 0){ - itkExceptionMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : no GradientIndexMapAvalible"); + if(m_BValueMap.find(0) == m_BValueMap.end()) + { + itkExceptionMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : GradientIndxMap with no b-Zero indecies found: check input image"); } - //if(listOfUserSelctedBValues.size() != m_GradientIndexMap.size()){ - // itkExceptionMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : The number of user selected B Values != number of Image BValues"); - //} - if(m_GradientIndexMap.size() == 4){ + if(m_BValueMap.size() == 4 ){ - GradientIndexMapIteraotr it = m_GradientIndexMap.begin(); + BValueMapIteraotr it = m_BValueMap.begin(); it++; const int b1 = (*it).first; it++; const int b2 = (*it).first; it++; const int b3 = (*it).first; if(b2 - b1 == b1 && b3 - b2 == b1 ) { m_ReconstructionType = Mode_Analytical3Shells; } } - if(m_GradientIndexMap.size() > 2 && m_ReconstructionType != Mode_Analytical3Shells) + if(m_BValueMap.size() > 2 && m_ReconstructionType != Mode_Analytical3Shells) { m_ReconstructionType = Mode_NumericalNShells; } - this->m_NumberOfBaselineImages = m_GradientIndexMap[0].size(); + this->m_NumberOfBaselineImages = m_BValueMap[0].size(); this->m_NumberOfGradientDirections = gradientDirection->Size() - this->m_NumberOfBaselineImages; // ensure that the gradient image we received has as many components as // the number of gradient directions if( gradientImage->GetVectorLength() != this->m_NumberOfBaselineImages + m_NumberOfGradientDirections ) { itkExceptionMacro( << m_NumberOfGradientDirections << " gradients + " << this->m_NumberOfBaselineImages << "baselines = " << m_NumberOfGradientDirections + this->m_NumberOfBaselineImages << " directions specified but image has " << gradientImage->GetVectorLength() << " components."); } this->ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::BeforeThreadedGenerateData() { itk::TimeProbe clock; clock.Start(); if( m_NumberOfGradientDirections < (((L+1)*(L+2))/2) /* && m_NumberOfGradientDirections < 6 */ ) { itkExceptionMacro( << "At least " << ((L+1)*(L+2))/2 << " gradient directions are required" ); } // Input must be an itk::VectorImage. std::string gradientImageClassName(this->ProcessObject::GetInput(0)->GetNameOfClass()); if ( strcmp(gradientImageClassName.c_str(),"VectorImage") != 0 ) itkExceptionMacro( << "There is only one Gradient image. I expect that to be a VectorImage. But its of type: " << gradientImageClassName ); m_BZeroImage = BZeroImageType::New(); typename GradientImagesType::Pointer img = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); m_BZeroImage->Allocate(); this->ComputeReconstructionMatrix(); clock.Stop(); MITK_INFO << "Before GenerateData : " << clock.GetTotal(); } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::StandardOneShellReconstruction(const OutputImageRegionType& outputRegionForThread) { // Get output image pointer typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); // ImageRegionIterator for the output image ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); // ImageRegionIterator for the BZero (output) image ImageRegionConstIterator< BZeroImageType > bzeroImageIterator(m_BZeroImage, outputRegionForThread); bzeroImageIterator.GoToBegin(); - IndiciesVector SignalIndicies = m_GradientIndexMap[1]; + IndiciesVector SignalIndicies = m_BValueMap[1]; // if the gradient directiosn aragement is hemispherical, duplicate all gradient directions // alone, interested in the value, the direction can be neglected if(m_IsHemisphericalArrangementOfGradientDirections){ int NumbersOfGradientIndicies = SignalIndicies.size(); for (int i = 0 ; i < NumbersOfGradientIndicies; i++) SignalIndicies.push_back(SignalIndicies[i]); } // Get input gradient image pointer typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); // Const ImageRegionIterator for input gradient image typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; GradientIteratorType git(gradientImagePointer, outputRegionForThread ); git.GoToBegin(); typedef typename GradientImagesType::PixelType GradientVectorType; // iterate overall voxels of the gradient image region while( ! git.IsAtEnd() ) { GradientVectorType b = git.Get(); // ODF Vector OdfPixelType odf(0.0); // Create the Signal Vector vnl_vector SignalVector(m_NumberOfGradientDirections); if( (bzeroImageIterator.Get() != 0) && (bzeroImageIterator.Get() >= m_Threshold) ) { for( unsigned int i = 0; i< SignalIndicies.size(); i++ ) { SignalVector[i] = static_cast(b[SignalIndicies[i]]); } // apply threashold an generate ln(-ln(E)) signal // Replace SignalVector with PreNormalized SignalVector S_S0Normalization(SignalVector, bzeroImageIterator.Get()); DoubleLogarithm(SignalVector); // ODF coeffs-vector vnl_vector coeffs(m_NumberCoefficients); // approximate ODF coeffs coeffs = ( (*m_CoeffReconstructionMatrix) * SignalVector ); coeffs[0] = 1.0/(2.0*sqrt(QBALL_ANAL_RECON_PI)); - odf = mitk::vnl_function::element_cast(( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs )).data_block(); + odf = element_cast(( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs )).data_block(); odf *= (QBALL_ANAL_RECON_PI*4/NODF); } // set ODF to ODF-Image oit.Set( odf ); ++oit; ++bzeroImageIterator; ++git; } MITK_INFO << "One Thread finished reconstruction"; } #include //#include //#include template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::NumericalNShellReconstruction(const OutputImageRegionType& outputRegionForThread) { // vnl_levenberg_marquardt LMOptimizer = new vnl_levenberg_marquardt(); } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::GenerateAveragedBZeroImage(const OutputImageRegionType& outputRegionForThread) { typedef typename GradientImagesType::PixelType GradientVectorType; ImageRegionIterator< BZeroImageType > bzeroIterator(m_BZeroImage, outputRegionForThread); bzeroIterator.GoToBegin(); - IndiciesVector BZeroIndicies = m_GradientIndexMap[0]; + IndiciesVector BZeroIndicies = m_BValueMap[0]; typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); // Const ImageRegionIterator for input gradient image typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; GradientIteratorType git(gradientImagePointer, outputRegionForThread ); git.GoToBegin(); while( ! git.IsAtEnd() ) { GradientVectorType b = git.Get(); // compute the average bzero signal typename NumericTraits::AccumulateType b0 = NumericTraits::Zero; for(unsigned int i = 0; i < BZeroIndicies.size(); ++i) { b0 += b[BZeroIndicies[i]]; } b0 /= BZeroIndicies.size(); bzeroIterator.Set(b0); ++bzeroIterator; ++git; } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::AnalyticalThreeShellReconstruction(const OutputImageRegionType& outputRegionForThread) { typedef typename GradientImagesType::PixelType GradientVectorType; // Input Gradient Image and Output ODF Image typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); // Define Image iterators ImageRegionIterator< OutputImageType > odfOutputImageIterator(outputImage, outputRegionForThread); ImageRegionConstIterator< BZeroImageType > bzeroImageIterator(m_BZeroImage, outputRegionForThread); ImageRegionConstIterator< GradientImagesType > gradientInputImageIterator(gradientImagePointer, outputRegionForThread ); // All iterators seht to Begin of the specific OutputRegion odfOutputImageIterator.GoToBegin(); bzeroImageIterator.GoToBegin(); gradientInputImageIterator.GoToBegin(); // Get Shell Indicies for all non-BZero Gradients // it MUST be a arithmetic progression eg.: 1000, 2000, 3000 - GradientIndexMapIteraotr it = m_GradientIndexMap.begin(); + BValueMapIteraotr it = m_BValueMap.begin(); it++; // it = b-value = 1000 IndiciesVector Shell1Indiecies = (*it).second; it++; // it = b-value = 2000 IndiciesVector Shell2Indiecies = (*it).second; it++; // it = b-value = 3000 IndiciesVector Shell3Indiecies = (*it).second; // if input data is a hemispherical arragement, duplicate eache gradient for each shell if(m_IsHemisphericalArrangementOfGradientDirections){ int NumbersOfGradientIndicies = Shell1Indiecies.size(); for (int i = 0 ; i < NumbersOfGradientIndicies; i++){ Shell1Indiecies.push_back(Shell1Indiecies[i]); Shell2Indiecies.push_back(Shell2Indiecies[i]); Shell3Indiecies.push_back(Shell3Indiecies[i]); } } // Nx3 Signal Vector with E(0) = Shell 1, E(1) = Shell 2, E(2) = Shell 3 vnl_matrix< double > * E = new vnl_matrix(Shell1Indiecies.size(), 3); vnl_vector * AlphaValues = new vnl_vector(Shell1Indiecies.size()); vnl_vector * BetaValues = new vnl_vector(Shell1Indiecies.size()); vnl_vector * LAValues = new vnl_vector(Shell1Indiecies.size()); vnl_vector * PValues = new vnl_vector(Shell1Indiecies.size()); OdfPixelType odf(0.0); // iterate overall voxels of the gradient image region while( ! gradientInputImageIterator.IsAtEnd() ) { if( (bzeroImageIterator.Get() != 0) && (bzeroImageIterator.Get() >= m_Threshold) ) { // Get the Signal-Value for each Shell at each direction (specified in the ShellIndicies Vector .. this direction corresponse to this shell...) GradientVectorType b = gradientInputImageIterator.Get(); for(int i = 0 ; i < Shell1Indiecies.size(); i++) { E->put(i,0, static_cast(b[Shell1Indiecies[i]])); E->put(i,1, static_cast(b[Shell2Indiecies[i]])); E->put(i,2, static_cast(b[Shell3Indiecies[i]])); } //Approximated-Signal by SH fit - using the specific shell directions and values // approximated Signal : S = SHBasis * Coeffs // with Coeffs : C = (B_T * B + lambda * L) ^ -1 * B_T * OS // OS := Original-Signal E->set_column(1, (*m_SHBasisMatrix) * ((*m_SignalReonstructionMatrix) * (E->get_column(1)))); E->set_column(2, (*m_SHBasisMatrix) * ((*m_SignalReonstructionMatrix) * (E->get_column(2)))); // Normalize the Signal: Si/S0 S_S0Normalization(*E,bzeroImageIterator.Get()); //Implements Eq. [19] and Fig. 4. Threshold(*E); //inqualities [31]. Taking the lograithm of th first tree inqualities //convert the quadratic inqualities to linear ones. Projection1(*E); double E1, E2, E3, P2,A,B2,B,P,alpha,beta,lambda, ER1, ER2; for( unsigned int i = 0; i< Shell1Indiecies.size(); i++ ) { E1 = E->get(i,0); E2 = E->get(i,1); E3 = E->get(i,2); P2 = E2-E1*E1; A = (E3 -E1*E2) / ( 2* P2); B2 = A * A -(E1 * E3 - E2 * E2) /P2; B = 0; if(B2 > 0) B = sqrt(B2); P = 0; if(P2 > 0) P = sqrt(P2); alpha = A + B; beta = A - B; lambda = 0.5 + 0.5 * std::sqrt(1 - std::pow((P * 2 ) / (alpha - beta), 2));; ER1 = std::fabs(lambda * (alpha - beta) + (beta - E1 )) + std::fabs(lambda * (std::pow(alpha, 2) - std::pow(beta, 2)) + (std::pow(beta, 2) - E2 )) + std::fabs(lambda * (std::pow(alpha, 3) - std::pow(beta, 3)) + (std::pow(beta, 3) - E3 )); ER2 = std::fabs((lambda-1) * (alpha - beta) + (beta - E1 )) + std::fabs((lambda-1) * (std::pow(alpha, 2) - std::pow(beta, 2)) + (std::pow(beta, 2) - E2 )) + std::fabs((lambda-1) * (std::pow(alpha, 3) - std::pow(beta, 3)) + (std::pow(beta, 3) - E3 )); PValues->put(i, P); AlphaValues->put(i, alpha); BetaValues->put(i, beta); LAValues->put(i,(lambda * (ER1 < ER2)) + ((1-lambda) * (ER1 >= ER2))); } Projection2(*PValues, *AlphaValues, *BetaValues); //Threshold(*AlphaValues); //Threshold(*BetaValues); DoubleLogarithm(*AlphaValues); DoubleLogarithm(*BetaValues); vnl_vector SignalVector(element_product((*LAValues) , (*AlphaValues)-(*BetaValues)) + (*BetaValues)); vnl_vector coeffs((*m_CoeffReconstructionMatrix) *SignalVector ); // the first coeff is a fix value coeffs[0] = 1.0/(2.0*sqrt(QBALL_ANAL_RECON_PI)); // Cast the Signal-Type from double to float for the ODF-Image - odf = mitk::vnl_function::element_cast( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs ).data_block(); + odf = element_cast( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs ).data_block(); odf *= (QBALL_ANAL_RECON_PI*4/NODF); //Normalize(odf); } // set ODF to ODF-Image odfOutputImageIterator.Set( odf ); ++odfOutputImageIterator; // iterate ++bzeroImageIterator; ++gradientInputImageIterator; } MITK_INFO << "THREAD FINISHED"; delete E; delete AlphaValues; delete BetaValues; delete PValues; delete LAValues; } template< class T, class TG, class TO, int L, int NODF> vnl_vector DiffusionMultiShellQballReconstructionImageFilter ::AnalyticalThreeShellParameterEstimation(const IndiciesVector * shell1Indicies,const IndiciesVector * shell2Indicies ,const IndiciesVector * shell3Indicies, vnl_vector) { } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, int NumberOfThreads) { itk::TimeProbe clock; GenerateAveragedBZeroImage(outputRegionForThread); clock.Start(); switch(m_ReconstructionType) { case Mode_Standard1Shell: StandardOneShellReconstruction(outputRegionForThread); break; case Mode_Analytical3Shells: AnalyticalThreeShellReconstruction(outputRegionForThread); break; case Mode_NumericalNShells: break; } clock.Stop(); MITK_INFO << "Reconstruction in : " << clock.GetTotal() << " TU"; } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter:: ComputeSphericalHarmonicsBasis(vnl_matrix * QBallReference, vnl_matrix *SHBasisOutput, vnl_matrix* LaplaciaBaltramiOutput, vnl_vector* SHOrderAssociation, vnl_matrix* SHEigenvalues ) { for(unsigned int i=0; i< (*SHBasisOutput).rows(); i++) { for(int k = 0; k <= L; k += 2) { for(int m =- k; m <= k; m++) { int j = ( k * k + k + 2 ) / 2 + m - 1; // Compute SHBasisFunctions double phi = (*QBallReference)(0,i); double th = (*QBallReference)(1,i); (*SHBasisOutput)(i,j) = mitk::sh::Yj(m,k,th,phi); // Laplacian Baltrami Order Association if(LaplaciaBaltramiOutput) (*LaplaciaBaltramiOutput)(j,j) = k*k*(k + 1)*(k+1); // SHEigenvalues with order Accosiation kj if(SHEigenvalues) (*SHEigenvalues)(j,j) = -k* (k+1); // Order Association if(SHOrderAssociation) (*SHOrderAssociation)[j] = k; } } } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::ComputeFunkRadonTransformationMatrix(vnl_vector* SHOrderAssociationReference, vnl_matrix* FRTMatrixOutput ) { for(int i=0; i bool DiffusionMultiShellQballReconstructionImageFilter ::CheckHemisphericalArrangementOfGradientDirections() { // handle acquisition schemes where only half of the spherical // shell is sampled by the gradient directions. In this case, // each gradient direction is duplicated in negative direction. vnl_vector centerMass(3); centerMass.fill(0.0); int count = 0; GradientDirectionContainerType::ConstIterator gdcit1; for( gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) { if(gdcit1.Value().one_norm() > 0.0) { centerMass += gdcit1.Value(); count ++; } } centerMass /= count; if(centerMass.two_norm() > 0.1) { return false; } return true; } template< class T, class TG, class TO, int L, int NOdfDirections> void DiffusionMultiShellQballReconstructionImageFilter ::ComputeReconstructionMatrix() { typedef std::auto_ptr< vnl_matrix< double> > MatrixDoublePtr; typedef std::auto_ptr< vnl_vector< int > > VectorIntPtr; typedef std::auto_ptr< vnl_matrix_inverse< double > > InverseMatrixDoublePtr; - std::map >::const_iterator it = (m_GradientIndexMap.begin()); + std::map >::const_iterator it = (m_BValueMap.begin()); it++; - const std::vector gradientIndiciesVector= (*it).second; + const std::vector gradientIndiciesVector = (*it).second; int numberOfGradientDirections = gradientIndiciesVector.size(); - if( numberOfGradientDirections < (((L+1)*(L+2))/2) || numberOfGradientDirections < 6 ) { itkExceptionMacro( << "At least (L+1)(L+2)/2 gradient directions for each shell are required; current : " << numberOfGradientDirections ); } CheckDuplicateDiffusionGradients(); // check if gradient directions are arrangement as a hemisphere(true) or sphere(false) m_IsHemisphericalArrangementOfGradientDirections = CheckHemisphericalArrangementOfGradientDirections(); if(m_IsHemisphericalArrangementOfGradientDirections) numberOfGradientDirections *= 2; MatrixDoublePtr Q(new vnl_matrix(3, numberOfGradientDirections)); Q->fill(0.0); // Cartesian to spherical coordinates { int j = 0; for(int i = 0; i < gradientIndiciesVector.size(); i++) { double x = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(0); double y = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(1); double z = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); (*Q)(0,j) = cart[0]; (*Q)(1,j) = cart[1]; (*Q)(2,j++) = cart[2]; } if(m_IsHemisphericalArrangementOfGradientDirections) { for(int i = 0; i < gradientIndiciesVector.size(); i++) { double x = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(0); double y = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(1); double z = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); (*Q)(0,j) = cart[0]; (*Q)(1,j) = cart[1]; (*Q)(2,j++) = cart[2]; } } } const int LOrder = L; m_NumberCoefficients = (int)(LOrder*LOrder + LOrder + 2.0)/2.0 + LOrder; MITK_INFO << m_NumberCoefficients; m_SHBasisMatrix = new vnl_matrix(numberOfGradientDirections,m_NumberCoefficients); m_SHBasisMatrix->fill(0.0); VectorIntPtr SHOrderAssociation(new vnl_vector(m_NumberCoefficients)); SHOrderAssociation->fill(0.0); MatrixDoublePtr LaplacianBaltrami(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); LaplacianBaltrami->fill(0.0); MatrixDoublePtr FRTMatrix(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); FRTMatrix->fill(0.0); MatrixDoublePtr SHEigenvalues(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); SHEigenvalues->fill(0.0); // SHBasis-Matrix + LaplacianBaltrami-Matrix + SHOrderAssociationVector ComputeSphericalHarmonicsBasis(Q.get() ,m_SHBasisMatrix, LaplacianBaltrami.get(), SHOrderAssociation.get(), SHEigenvalues.get()); // Compute FunkRadon Transformation Matrix Associated to SHBasis Order lj ComputeFunkRadonTransformationMatrix(SHOrderAssociation.get() ,FRTMatrix.get()); MatrixDoublePtr temp(new vnl_matrix(((m_SHBasisMatrix->transpose()) * (*m_SHBasisMatrix)) + (m_Lambda * (*LaplacianBaltrami)))); InverseMatrixDoublePtr pseudo_inv(new vnl_matrix_inverse((*temp))); MatrixDoublePtr inverse(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); inverse->fill(0.0); (*inverse) = pseudo_inv->inverse(); // ODF Factor ( missing 1/4PI ?? ) double factor = (1.0/(16.0*QBALL_ANAL_RECON_PI*QBALL_ANAL_RECON_PI)); m_SignalReonstructionMatrix = new vnl_matrix((*inverse) * (m_SHBasisMatrix->transpose())); m_CoeffReconstructionMatrix = new vnl_matrix(( factor * ((*FRTMatrix) * ((*SHEigenvalues) * (*m_SignalReonstructionMatrix))) )); // this code goes to the image adapter coeffs->odfs later vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); m_ODFSphericalHarmonicBasisMatrix = new vnl_matrix(NOdfDirections,m_NumberCoefficients); m_ODFSphericalHarmonicBasisMatrix->fill(0.0); for(int i=0; i template< class VNLType > void DiffusionMultiShellQballReconstructionImageFilter ::printMatrix( VNLType * mat ) { std::stringstream stream; for(int i = 0 ; i < mat->rows(); i++) { stream.str(""); for(int j = 0; j < mat->cols(); j++) { stream << (*mat)(i,j) << " "; } } MITK_INFO << stream.str(); } template< class T, class TG, class TO, int L, int NODF> bool DiffusionMultiShellQballReconstructionImageFilter ::CheckDuplicateDiffusionGradients() { bool value = false; - GradientIndexMapIteraotr mapIterator = m_GradientIndexMap.begin(); - while(mapIterator != m_GradientIndexMap.end()) + BValueMapIteraotr mapIterator = m_BValueMap.begin(); + while(mapIterator != m_BValueMap.end()) { std::vector::const_iterator it1 = mapIterator->second.begin(); std::vector::const_iterator it2 = mapIterator->second.begin(); for(; it1 != mapIterator->second.end(); ++it1) { for(; it2 != mapIterator->second.end(); ++it2) { if(m_GradientDirectionContainer->ElementAt(*it1) == m_GradientDirectionContainer->ElementAt(*it2) && it1 != it2) { itkWarningMacro( << "Some of the Diffusion Gradients equal each other. Corresponding image data should be averaged before calling this filter." ); value = true; } } } ++mapIterator; } return value; } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::PrintSelf(std::ostream& os, Indent indent) const { std::locale C("C"); std::locale originalLocale = os.getloc(); os.imbue(C); Superclass::PrintSelf(os,indent); //os << indent << "OdfReconstructionMatrix: " << m_ReconstructionMatrix << std::endl; if ( m_GradientDirectionContainer ) { os << indent << "GradientDirectionContainer: " << m_GradientDirectionContainer << std::endl; } else { os << indent << "GradientDirectionContainer: (Gradient directions not set)" << std::endl; } os << indent << "NumberOfGradientDirections: " << m_NumberOfGradientDirections << std::endl; os << indent << "NumberOfBaselineImages: " << m_NumberOfBaselineImages << std::endl; os << indent << "Threshold for reference B0 image: " << m_Threshold << std::endl; os << indent << "BValue: " << m_BValue << std::endl; os.imbue( originalLocale ); } } #endif // __itkDiffusionMultiShellQballReconstructionImageFilter_cpp diff --git a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h index 88ad2d65a9..fb7a499f38 100644 --- a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h +++ b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h @@ -1,215 +1,229 @@ /*=================================================================== 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 __itkDiffusionMultiShellQballReconstructionImageFilter_h_ #define __itkDiffusionMultiShellQballReconstructionImageFilter_h_ #include "itkImageToImageFilter.h" #include "vnl/vnl_vector_fixed.h" #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_svd.h" #include "itkVectorContainer.h" #include "itkVectorImage.h" #include namespace itk{ /** \class DiffusionMultiShellQballReconstructionImageFilter Aganj_2010 */ template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NOrderL, int NrOdfDirections> class DiffusionMultiShellQballReconstructionImageFilter : public ImageToImageFilter< Image< TReferenceImagePixelType, 3 >, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > { public: typedef DiffusionMultiShellQballReconstructionImageFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< Image< TReferenceImagePixelType, 3>, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > Superclass; typedef TReferenceImagePixelType ReferencePixelType; typedef TGradientImagePixelType GradientPixelType; typedef Vector< TOdfPixelType, NrOdfDirections > OdfPixelType; typedef typename Superclass::InputImageType ReferenceImageType; typedef Image< OdfPixelType, 3 > OdfImageType; typedef OdfImageType OutputImageType; typedef TOdfPixelType BZeroPixelType; typedef Image< BZeroPixelType, 3 > BZeroImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** Typedef defining one (of the many) gradient images. */ typedef Image< GradientPixelType, 3 > GradientImageType; /** An alternative typedef defining one (of the many) gradient images. * It will be assumed that the vectorImage has the same dimension as the * Reference image and a vector length parameter of \c n (number of * gradient directions)*/ typedef VectorImage< GradientPixelType, 3 > GradientImagesType; /** Holds the ODF reconstruction matrix */ typedef vnl_matrix< TOdfPixelType >* OdfReconstructionMatrixType; typedef vnl_matrix< double > * CoefficientMatrixType; /** Holds each magnetic field gradient used to acquire one DWImage */ typedef vnl_vector_fixed< double, 3 > GradientDirectionType; /** Container to hold gradient directions of the 'n' DW measurements */ typedef VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; - typedef std::map > GradientIndexMap; - typedef std::map >::iterator GradientIndexMapIteraotr; + typedef std::map > BValueMap; + typedef std::map >::iterator BValueMapIteraotr; typedef std::vector IndiciesVector; // --------------------------------------------------------------------------------------------// /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(DiffusionMultiShellQballReconstructionImageFilter, ImageToImageFilter); /** set method to add gradient directions and its corresponding * image. The image here is a VectorImage. The user is expected to pass the * gradient directions in a container. The ith element of the container * corresponds to the gradient direction of the ith component image the * VectorImage. For the baseline image, a vector of all zeros * should be set.*/ void SetGradientImage( GradientDirectionContainerType *, const GradientImagesType *image , float bvalue);//, std::vector listOfUserSelctedBValues ); /** Get reference image */ virtual ReferenceImageType * GetReferenceImage() { return ( static_cast< ReferenceImageType *>(this->ProcessObject::GetInput(0)) ); } /** Return the gradient direction. idx is 0 based */ virtual GradientDirectionType GetGradientDirection( unsigned int idx) const { if( idx >= m_GradientDirectionContainer->Size() ) { itkExceptionMacro( << "Gradient direction " << idx << "does not exist" ); } return m_GradientDirectionContainer->ElementAt( idx+1 ); } void Normalize(OdfPixelType & odf ); void S_S0Normalization( vnl_vector & vec, typename NumericTraits::AccumulateType b0 = 0 ); void S_S0Normalization( vnl_matrix & mat, typename NumericTraits::AccumulateType b0 = 0 ); void DoubleLogarithm(vnl_vector & vec); void Threshold(vnl_vector & vec, double delta = 0.01); void Threshold(vnl_matrix & mat, double delta = 0.01); double CalculateThreashold(const double value, const double delta); void Projection1( vnl_matrix & mat, double delta = 0.01); void Projection2( vnl_vector & A, vnl_vector & alpha, vnl_vector & beta, double delta = 0.01); /** Threshold on the reference image data. The output ODF will be a null * pdf for pixels in the reference image that have a value less than this * threshold. */ itkSetMacro( Threshold, ReferencePixelType ); itkGetMacro( Threshold, ReferencePixelType ); itkGetMacro( BZeroImage, typename BZeroImageType::Pointer); //itkGetMacro( ODFSumImage, typename BlaImage::Pointer); itkSetMacro( Lambda, double ); itkGetMacro( Lambda, double ); itkGetConstReferenceMacro( BValue, TOdfPixelType); + void SetBValueMap(BValueMap map){this->m_BValueMap = map;} + protected: DiffusionMultiShellQballReconstructionImageFilter(); ~DiffusionMultiShellQballReconstructionImageFilter() {}; void PrintSelf(std::ostream& os, Indent indent) const; void ComputeReconstructionMatrix(); bool CheckDuplicateDiffusionGradients(); void ComputeSphericalHarmonicsBasis(vnl_matrix* QBallReference, vnl_matrix* SHBasisOutput, vnl_matrix* LaplaciaBaltramiOutput, vnl_vector* SHOrderAssociation , vnl_matrix * SHEigenvalues); void ComputeFunkRadonTransformationMatrix(vnl_vector* SHOrderAssociationReference, vnl_matrix* FRTMatrixOutput ); bool CheckHemisphericalArrangementOfGradientDirections(); void BeforeThreadedGenerateData(); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, int NumberOfThreads ); vnl_vector AnalyticalThreeShellParameterEstimation(const IndiciesVector * shell1, const IndiciesVector * shell2, const IndiciesVector * shell3, vnl_vector b); void StandardOneShellReconstruction(const OutputImageRegionType& outputRegionForThread); void AnalyticalThreeShellReconstruction(const OutputImageRegionType& outputRegionForThread); void NumericalNShellReconstruction(const OutputImageRegionType& outputRegionForThread); void GenerateAveragedBZeroImage(const OutputImageRegionType& outputRegionForThread); private: enum ReconstructionType { Mode_Analytical3Shells, Mode_NumericalNShells, Mode_Standard1Shell }; //CoefficientMatrixType m_ReconstructionMatrix; CoefficientMatrixType m_CoeffReconstructionMatrix; CoefficientMatrixType m_ODFSphericalHarmonicBasisMatrix; CoefficientMatrixType m_SignalReonstructionMatrix; CoefficientMatrixType m_SHBasisMatrix; /** container to hold gradient directions */ GradientDirectionContainerType::Pointer m_GradientDirectionContainer; /** Number of gradient measurements */ unsigned int m_NumberOfGradientDirections; /** Number of baseline images */ unsigned int m_NumberOfBaselineImages; /** Threshold on the reference image data */ ReferencePixelType m_Threshold; /** LeBihan's b-value for normalizing tensors */ float m_BValue; typename BZeroImageType::Pointer m_BZeroImage; - GradientIndexMap m_GradientIndexMap; + BValueMap m_BValueMap; double m_Lambda; bool m_IsHemisphericalArrangementOfGradientDirections; bool m_IsArithmeticProgession; int m_NumberCoefficients; ReconstructionType m_ReconstructionType; template< class VNLType > void printMatrix( VNLType * mat ); + //------------------------- VNL-function ------------------------------------ + + + template + static vnl_vector< WntValue> element_cast (vnl_vector< CurrentValue> const& v1) + { + vnl_vector result(v1.size()); + + for(int i = 0 ; i < v1.size(); i++) + result[i] = static_cast< WntValue>(v1[i]); + return result; + } }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkDiffusionMultiShellQballReconstructionImageFilter.cpp" #endif #endif //__itkDiffusionMultiShellQballReconstructionImageFilter_h_ diff --git a/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper3D.cpp b/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper3D.cpp index b2b3397b97..631218e626 100644 --- a/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper3D.cpp +++ b/Modules/DiffusionImaging/Rendering/mitkFiberBundleXMapper3D.cpp @@ -1,201 +1,195 @@ /*=================================================================== 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 "mitkFiberBundleXMapper3D.h" #include //#include //#include #include #include #include //not essential for mapper #include mitk::FiberBundleXMapper3D::FiberBundleXMapper3D() { m_lut = vtkLookupTable::New(); m_lut->Build(); } mitk::FiberBundleXMapper3D::~FiberBundleXMapper3D() { } const mitk::FiberBundleX* mitk::FiberBundleXMapper3D::GetInput() { - MITK_INFO << "FiberBundleXxXXMapper3D() GetInput()"; return static_cast ( GetData() ); } /* This method is called once the mapper gets new input, for UI rotation or changes in colorcoding this method is NOT called */ void mitk::FiberBundleXMapper3D::GenerateData(mitk::BaseRenderer *renderer) { //MITK_INFO << "GENERATE DATA FOR FBX :)"; //=====timer measurement==== QTime myTimer; myTimer.start(); //========================== mitk::FiberBundleX* FBX = dynamic_cast (this->GetData()); if (FBX == NULL) return; vtkSmartPointer FiberData = FBX->GetFiberPolyData(); if (FiberData == NULL) return; FBXLocalStorage3D *localStorage = m_LSH.GetLocalStorage(renderer); localStorage->m_FiberMapper->SetInput(FiberData); if ( FiberData->GetPointData()->GetNumberOfArrays() > 0 ) localStorage->m_FiberMapper->SelectColorArray( FBX->GetCurrentColorCoding() ); localStorage->m_FiberMapper->ScalarVisibilityOn(); localStorage->m_FiberMapper->SetScalarModeToUsePointFieldData(); localStorage->m_FiberActor->SetMapper(localStorage->m_FiberMapper); // localStorage->m_FiberActor->GetProperty()->SetOpacity(0.999); localStorage->m_FiberMapper->SetLookupTable(m_lut); // set Opacity float tmpopa; this->GetDataNode()->GetOpacity(tmpopa, NULL); localStorage->m_FiberActor->GetProperty()->SetOpacity((double) tmpopa); // set color if (FBX->GetCurrentColorCoding() != NULL){ // localStorage->m_FiberMapper->SelectColorArray(""); localStorage->m_FiberMapper->SelectColorArray(FBX->GetCurrentColorCoding()); MITK_DEBUG << "MapperFBX: " << FBX->GetCurrentColorCoding(); if(FBX->GetCurrentColorCoding() == FBX->COLORCODING_CUSTOM) { float temprgb[3]; this->GetDataNode()->GetColor( temprgb, NULL ); double trgb[3] = { (double) temprgb[0], (double) temprgb[1], (double) temprgb[2] }; localStorage->m_FiberActor->GetProperty()->SetColor(trgb); } } localStorage->m_FiberAssembly->AddPart(localStorage->m_FiberActor); localStorage->m_LastUpdateTime.Modified(); //since this method is called after generating all necessary data for fiber visualization, all modifications are represented so far. - - //====timer measurement======== - MITK_INFO << "Execution Time GenerateData() (nmiliseconds): " << myTimer.elapsed(); - //============================= - } void mitk::FiberBundleXMapper3D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { if ( !this->IsVisible( renderer ) ) return; // Calculate time step of the input data for the specified renderer (integer value) // this method is implemented in mitkMapper this->CalculateTimeStep( renderer ); //check if updates occured in the node or on the display FBXLocalStorage3D *localStorage = m_LSH.GetLocalStorage(renderer); const DataNode *node = this->GetDataNode(); if ( (localStorage->m_LastUpdateTime < node->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) ) { - MITK_INFO << "UPDATE NEEDED FOR _ " << renderer->GetName(); + MITK_DEBUG << "UPDATE NEEDED FOR _ " << renderer->GetName(); this->GenerateData(renderer); } } void mitk::FiberBundleXMapper3D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { // MITK_INFO << "FiberBundleXxXXMapper3D()SetDefaultProperties"; //MITK_INFO << "FiberBundleMapperX3D SetDefault Properties(...)"; // node->AddProperty( "DisplayChannel", mitk::IntProperty::New( true ), renderer, overwrite ); node->AddProperty( "LineWidth", mitk::IntProperty::New( true ), renderer, overwrite ); node->AddProperty( "opacity", mitk::FloatProperty::New( 1.0 ), renderer, overwrite); // node->AddProperty( "VertexOpacity_1", mitk::BoolProperty::New( false ), renderer, overwrite); // node->AddProperty( "Set_FA_VertexAlpha", mitk::BoolProperty::New( false ), renderer, overwrite); // node->AddProperty( "pointSize", mitk::FloatProperty::New(0.5), renderer, overwrite); // node->AddProperty( "setShading", mitk::IntProperty::New(1), renderer, overwrite); // node->AddProperty( "Xmove", mitk::IntProperty::New( 0 ), renderer, overwrite); // node->AddProperty( "Ymove", mitk::IntProperty::New( 0 ), renderer, overwrite); // node->AddProperty( "Zmove", mitk::IntProperty::New( 0 ), renderer, overwrite); // node->AddProperty( "RepPoints", mitk::BoolProperty::New( false ), renderer, overwrite); // node->AddProperty( "TubeSides", mitk::IntProperty::New( 8 ), renderer, overwrite); // node->AddProperty( "TubeRadius", mitk::FloatProperty::New( 0.15 ), renderer, overwrite); // node->AddProperty( "TubeOpacity", mitk::FloatProperty::New( 1.0 ), renderer, overwrite); node->AddProperty( "pickable", mitk::BoolProperty::New( true ), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } vtkProp* mitk::FiberBundleXMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { //MITK_INFO << "FiberBundleXxXXMapper3D()GetVTKProp"; //this->GenerateData(); return m_LSH.GetLocalStorage(renderer)->m_FiberAssembly; } void mitk::FiberBundleXMapper3D::ApplyProperties(mitk::BaseRenderer* renderer) { } void mitk::FiberBundleXMapper3D::UpdateVtkObjects() { } void mitk::FiberBundleXMapper3D::SetVtkMapperImmediateModeRendering(vtkMapper *) { } mitk::FiberBundleXMapper3D::FBXLocalStorage3D::FBXLocalStorage3D() { m_FiberActor = vtkSmartPointer::New(); m_FiberMapper = vtkSmartPointer::New(); m_FiberAssembly = vtkSmartPointer::New(); } diff --git a/Modules/DiffusionImaging/Testing/CMakeLists.txt b/Modules/DiffusionImaging/Testing/CMakeLists.txt index 7880dc4d2c..b08720a69b 100644 --- a/Modules/DiffusionImaging/Testing/CMakeLists.txt +++ b/Modules/DiffusionImaging/Testing/CMakeLists.txt @@ -1,5 +1,10 @@ MITK_CREATE_MODULE_TESTS() + mitkAddCustomModuleTest(mitkFiberBundleXReaderWriterTest mitkFiberBundleXReaderWriterTest ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX.fib) -## deactivated, see bug 12017 -# mitkAddCustomModuleTest(mitkFiberBundleXTest mitkFiberBundleXTest ${MITK_DATA_DIR}/DiffusionImaging/fiberBundleX.fib) + + +mitkAddCustomModuleTest(mitkTbssNrrdImageReaderTest mitkTbssNrrdImageReaderTest ${MITK_DATA_DIR}/DiffusionImaging/tbss.tbss) +#mitkAddCustomModuleTest(mitkTbssRoiNrrdImageReaderTest mitkTbssRoiNrrdImageReaderTest ${MITK_DATA_DIR}/DiffusionImaging/bodyfornix.roi) +mitkAddCustomModuleTest(mitkTbssNrrdImageWriterTest mitkTbssNrrdImageWriterTest ${MITK_DATA_DIR}/DiffusionImaging/tbss.tbss ${MITK_DATA_DIR}/DiffusionImaging/tbss2.tbss) + diff --git a/Modules/DiffusionImaging/Testing/files.cmake b/Modules/DiffusionImaging/Testing/files.cmake index d4797a0809..5e2911f640 100644 --- a/Modules/DiffusionImaging/Testing/files.cmake +++ b/Modules/DiffusionImaging/Testing/files.cmake @@ -1,8 +1,15 @@ -SET(MODULE_TESTS - mitkFactoryRegistrationTest.cpp -) - SET(MODULE_CUSTOM_TESTS mitkFiberBundleXReaderWriterTest.cpp + # mitkFiberBundleXTest.cpp ## deactivated, see bug 12017 + # mitkGibbsTrackingTest.cpp # deactivated until new tarball is available + + mitkTbssNrrdImageReaderTest.cpp + #mitkTbssRoiNrrdImageReaderTest.cpp + mitkTbssNrrdImageWriterTest.cpp ) + +set(MODULE_TESTS + mitkFactoryRegistrationTest.cpp +) + diff --git a/Modules/DiffusionImaging/Testing/mitkFiberBundleXReaderWriterTest.cpp b/Modules/DiffusionImaging/Testing/mitkFiberBundleXReaderWriterTest.cpp index b4ea260f0d..c2f10980c7 100644 --- a/Modules/DiffusionImaging/Testing/mitkFiberBundleXReaderWriterTest.cpp +++ b/Modules/DiffusionImaging/Testing/mitkFiberBundleXReaderWriterTest.cpp @@ -1,80 +1,81 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include #include #include /**Documentation * Test for fiber bundle reader and writer */ int mitkFiberBundleXReaderWriterTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkFiberBundleXReaderWriterTest"); + std::cout << argv[1]<1,"check for fielename") - mitk::FiberBundleXReader::Pointer reader = mitk::FiberBundleXReader::New(); mitk::FiberBundleXWriter::Pointer writer = mitk::FiberBundleXWriter::New(); mitk::FiberBundleX::Pointer fib1; mitk::FiberBundleX::Pointer fib2; // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. - MITK_TEST_CONDITION_REQUIRED(reader.IsNotNull(),"reader instantiation") MITK_TEST_CONDITION_REQUIRED(writer.IsNotNull(),"writer instantiation") try{ RegisterDiffusionImagingObjectFactory(); // test if fib1 can be read const std::string s1="", s2=""; + + + std::vector fibInfile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[1], s1, s2, false ); mitk::BaseData::Pointer baseData = fibInfile.at(0); fib1 = dynamic_cast(baseData.GetPointer()); MITK_TEST_CONDITION_REQUIRED(fib1.IsNotNull(),"check if reader returned null") // test if fib1 can be written MITK_TEST_CONDITION_REQUIRED( writer->CanWriteBaseDataType(fib1.GetPointer()),"writer can write data") writer->SetFileName( std::string(MITK_TEST_OUTPUT_DIR)+"/writerTest.fib" ); writer->DoWrite( fib1.GetPointer() ); // test if fib1 can be read again as fib2 fibInfile = mitk::BaseDataIO::LoadBaseDataFromFile( std::string(MITK_TEST_OUTPUT_DIR)+"/writerTest.fib", s1, s2, false ); baseData = fibInfile.at(0); fib2 = dynamic_cast(baseData.GetPointer()); MITK_TEST_CONDITION_REQUIRED(fib2.IsNotNull(),"reader can read file written before") // test if fib1 equals fib2 MITK_TEST_CONDITION_REQUIRED(fib1->Equals(fib2),"fiber bundles are not changed during reading/writing") } catch(...) { - //this means that a wrong exception (i.e. no itk:Exception) has been thrown - std::cout << "Wrong exception (i.e. no itk:Exception) caught during write [FAILED]" << std::endl; return EXIT_FAILURE; } // always end with this! MITK_TEST_END(); } diff --git a/Modules/DiffusionImaging/Testing/mitkFiberBundleXTest.cpp b/Modules/DiffusionImaging/Testing/mitkFiberBundleXTest.cpp index 52ca547666..8a3fdfda4b 100644 --- a/Modules/DiffusionImaging/Testing/mitkFiberBundleXTest.cpp +++ b/Modules/DiffusionImaging/Testing/mitkFiberBundleXTest.cpp @@ -1,97 +1,100 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include #include #include /**Documentation * Test for fiber bundle reader and writer */ int mitkFiberBundleXTest(int argc, char* argv[]) { MITK_TEST_BEGIN("mitkFiberBundleXTest"); MITK_TEST_CONDITION_REQUIRED(argc>1,"check for fielename") mitk::FiberBundleXReader::Pointer reader = mitk::FiberBundleXReader::New(); mitk::FiberBundleX::Pointer fib1, fib2; // first test: did this work? // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since // it makes no sense to continue without an object. MITK_TEST_CONDITION_REQUIRED(reader.IsNotNull(),"reader instantiation") try{ RegisterDiffusionImagingObjectFactory(); // test if fib1 can be read const std::string s1="", s2=""; std::vector fibInfile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[1], s1, s2, false ); mitk::BaseData::Pointer baseData = fibInfile.at(0); fib1 = dynamic_cast(baseData.GetPointer()); MITK_TEST_CONDITION_REQUIRED(fib1.IsNotNull(),"check if reader 1 returned null") fibInfile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[1], s1, s2, false ); baseData = fibInfile.at(0); fib2 = dynamic_cast(baseData.GetPointer()); MITK_TEST_CONDITION_REQUIRED(fib2.IsNotNull(),"check if reader 2 returned null") MITK_TEST_CONDITION_REQUIRED(fib1->Equals(fib2),"check if equals method is working"); int randNum = rand()%20; - MITK_INFO << "DoFiberSmoothing " << randNum; fib2->DoFiberSmoothing(randNum); + MITK_INFO << "DoFiberSmoothing(" << randNum << ")" << randNum; fib2->DoFiberSmoothing(randNum); MITK_TEST_CONDITION_REQUIRED(!fib1->Equals(fib2),"check if fiber resampling method does something"); mitk::FiberBundleX::Pointer fib3 = fib1->AddBundle(fib2); MITK_TEST_CONDITION_REQUIRED(!fib1->Equals(fib3),"check if A+B!=A"); fib3 = fib3->SubtractBundle(fib2); MITK_TEST_CONDITION_REQUIRED(fib1->Equals(fib3),"check if A+B-B==A"); fib1->AddBundle(NULL); MITK_INFO << "GenerateFiberIds"; fib1->GenerateFiberIds(); MITK_INFO << "GetFiberPolyData"; fib1->GetFiberPolyData(); MITK_INFO << "GetAvailableColorCodings"; fib1->GetAvailableColorCodings(); MITK_INFO << "GetCurrentColorCoding"; fib1->GetCurrentColorCoding(); MITK_INFO << "SetFiberPolyData"; fib1->SetFiberPolyData(NULL); MITK_INFO << "ExtractFiberSubset"; fib1->ExtractFiberSubset(NULL); MITK_INFO << "ExtractFiberIdSubset"; fib1->ExtractFiberIdSubset(NULL); std::vector< long > tmp; MITK_INFO << "GeneratePolyDataByIds"; fib1->GeneratePolyDataByIds(tmp); MITK_INFO << "SetColorCoding"; fib1->SetColorCoding(NULL); MITK_INFO << "SetFAMap"; fib1->SetFAMap(NULL); MITK_INFO << "DoColorCodingOrientationBased"; fib1->DoColorCodingOrientationBased(); MITK_INFO << "DoColorCodingFaBased"; fib1->DoColorCodingFaBased(); MITK_INFO << "DoUseFaFiberOpacity"; fib1->DoUseFaFiberOpacity(); MITK_INFO << "ResetFiberOpacity"; fib1->ResetFiberOpacity(); + + float randFloat = rand()%300; + MITK_INFO << "RemoveShortFibers(" << randFloat << ")"; fib1->RemoveShortFibers(randFloat); } catch(...) { //this means that a wrong exception (i.e. no itk:Exception) has been thrown std::cout << "Wrong exception (i.e. no itk:Exception) caught during write [FAILED]" << std::endl; return EXIT_FAILURE; } // always end with this! MITK_TEST_END(); } diff --git a/Modules/DiffusionImaging/Testing/mitkGibbsTrackingTest.cpp b/Modules/DiffusionImaging/Testing/mitkGibbsTrackingTest.cpp new file mode 100644 index 0000000000..cf057f06a1 --- /dev/null +++ b/Modules/DiffusionImaging/Testing/mitkGibbsTrackingTest.cpp @@ -0,0 +1,92 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace mitk; + +/**Documentation + * Test for gibbs tracking filter + */ +int mitkGibbsTrackingTest(int argc, char* argv[]) +{ + MITK_TEST_BEGIN("mitkGibbsTrackingTest"); + + MITK_TEST_CONDITION_REQUIRED(argc>5,"check for input data") + + QBallImage::Pointer mitkQballImage; + Image::Pointer mitkMaskImage; + mitk::FiberBundleX::Pointer fib1; + + try{ + RegisterDiffusionImagingObjectFactory(); + + // test if fib1 can be read + const std::string s1="", s2=""; + std::vector infile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[1], s1, s2, false ); + mitkQballImage = dynamic_cast(infile.at(0).GetPointer()); + MITK_TEST_CONDITION_REQUIRED(mitkQballImage.IsNotNull(),"check qball image") + + infile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[2], s1, s2, false ); + mitkMaskImage = dynamic_cast(infile.at(0).GetPointer()); + MITK_TEST_CONDITION_REQUIRED(mitkMaskImage.IsNotNull(),"check qball image") + + infile = mitk::BaseDataIO::LoadBaseDataFromFile( argv[5], s1, s2, false ); + fib1 = dynamic_cast(infile.at(0).GetPointer()); + MITK_TEST_CONDITION_REQUIRED(fib1.IsNotNull(),"check fiber bundle") + + typedef itk::Vector OdfVectorType; + typedef itk::Image OdfVectorImgType; + typedef itk::Image MaskImgType; + typedef itk::GibbsTrackingFilter GibbsTrackingFilterType; + + OdfVectorImgType::Pointer itk_qbi = OdfVectorImgType::New(); + mitk::CastToItkImage(mitkQballImage, itk_qbi); + + MaskImgType::Pointer itk_mask = MaskImgType::New(); + mitk::CastToItkImage(mitkMaskImage, itk_mask); + + GibbsTrackingFilterType::Pointer gibbsTracker = GibbsTrackingFilterType::New(); + gibbsTracker->SetQBallImage(itk_qbi.GetPointer()); + gibbsTracker->SetMaskImage(itk_mask); + gibbsTracker->SetDuplicateImage(false); + gibbsTracker->SetRandomSeed(1); + gibbsTracker->SetParameterFile(argv[3]); + gibbsTracker->SetLutPath(argv[4]); + gibbsTracker->Update(); + + mitk::FiberBundleX::Pointer fib2 = mitk::FiberBundleX::New(gibbsTracker->GetFiberBundle()); + MITK_TEST_CONDITION_REQUIRED(fib1->Equals(fib2), "check if gibbs tracking has changed"); + + gibbsTracker->SetRandomSeed(0); + gibbsTracker->Update(); + fib2 = mitk::FiberBundleX::New(gibbsTracker->GetFiberBundle()); + MITK_TEST_CONDITION_REQUIRED(!fib1->Equals(fib2), "check if gibbs tracking has changed after wrong seed"); + } + catch(...) + { + return EXIT_FAILURE; + } + + // always end with this! + MITK_TEST_END(); +} diff --git a/Modules/DiffusionImaging/Testing/mitkTbssNrrdImageReaderTest.cpp b/Modules/DiffusionImaging/Testing/mitkTbssNrrdImageReaderTest.cpp new file mode 100644 index 0000000000..5ee0cb4acc --- /dev/null +++ b/Modules/DiffusionImaging/Testing/mitkTbssNrrdImageReaderTest.cpp @@ -0,0 +1,67 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include "mitkDiffusionImagingObjectFactory.h" +#include "mitkCoreObjectFactory.h" + + +/**Documentation + * test for the class "mitkNrrdTbssImageReader". + */ +int mitkTbssNrrdImageReaderTest(int argc , char* argv[]) +{ + MITK_TEST_BEGIN("TbssNrrdImageReaderTest"); + + + + + mitk::NrrdTbssImageReader::Pointer tbssNrrdReader = mitk::NrrdTbssImageReader::New(); + + MITK_TEST_CONDITION_REQUIRED(tbssNrrdReader.GetPointer(), "Testing initialization of test object!"); + + RegisterDiffusionImagingObjectFactory(); + + + tbssNrrdReader->SetFileName(argv[1]); + MITK_TEST_CONDITION_REQUIRED( tbssNrrdReader->CanReadFile(argv[1], "", ""), "Testing CanReadFile() method with valid input file name!"); + tbssNrrdReader->Update(); + + mitk::TbssImage* tbssImg = tbssNrrdReader->GetOutput(0); + + MITK_TEST_CONDITION_REQUIRED(tbssImg != NULL, "Testing that tbssImg is not null"); + + mitk::TbssImage::SizeType size = tbssImg->GetLargestPossibleRegion().GetSize(); + + MITK_TEST_CONDITION_REQUIRED(size[0]==2 && size[1]==2 && size[2]==2, "Test size of tbss image"); + + // Test groups + std::vector< std::pair > groups = tbssImg->GetGroupInfo(); + + std::pair group1 = groups.at(0); + std::pair group2 = groups.at(1); + + + + MITK_TEST_CONDITION_REQUIRED(group1.first.compare("group1") && group1.second==1, "Test group 1 info"); + + MITK_TEST_CONDITION_REQUIRED(group2.first.compare("group2") && group2.second==1, "Test group 2 info"); + + + + MITK_TEST_END(); +} diff --git a/Modules/DiffusionImaging/Testing/mitkTbssNrrdImageWriterTest.cpp b/Modules/DiffusionImaging/Testing/mitkTbssNrrdImageWriterTest.cpp new file mode 100644 index 0000000000..08dfd2a2e8 --- /dev/null +++ b/Modules/DiffusionImaging/Testing/mitkTbssNrrdImageWriterTest.cpp @@ -0,0 +1,79 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include "mitkDiffusionImagingObjectFactory.h" +#include "mitkCoreObjectFactory.h" + + +/**Documentation + * test for the class "mitkNrrdTbssImageWriter". + */ +int mitkTbssNrrdImageWriterTest(int argc , char* argv[]) +{ + MITK_TEST_BEGIN("TbssNrrdImageWriterTest"); + + mitk::NrrdTbssImageReader::Pointer tbssNrrdReader = mitk::NrrdTbssImageReader::New(); + + MITK_TEST_CONDITION_REQUIRED(tbssNrrdReader.GetPointer(), "Testing initialization of test object!"); + + RegisterDiffusionImagingObjectFactory(); + + tbssNrrdReader->SetFileName(argv[1]); + MITK_TEST_CONDITION_REQUIRED( tbssNrrdReader->CanReadFile(argv[1], "", ""), "Testing CanReadFile() method with valid input file name!"); + tbssNrrdReader->Update(); + + mitk::TbssImage* tbssImg = tbssNrrdReader->GetOutput(0); + + MITK_TEST_CONDITION_REQUIRED(tbssImg != NULL, "Testing that tbssImg is not null"); + + mitk::NrrdTbssImageWriter::Pointer tbssNrrdWriter = mitk::NrrdTbssImageWriter::New(); + + + try{ + tbssNrrdWriter->SetFileName(argv[2]); + tbssNrrdWriter->SetInput(tbssImg); + tbssNrrdWriter->Update(); + } + catch(...) { + std::cout << "Writing failed" << std::endl; + return EXIT_FAILURE; + } + + mitk::NrrdTbssImageReader::Pointer tbssNrrdReader2 = mitk::NrrdTbssImageReader::New(); + tbssNrrdReader2->SetFileName(argv[1]); + MITK_TEST_CONDITION_REQUIRED( tbssNrrdReader2->CanReadFile(argv[1], "", ""), "Testing CanReadFile() method with previously saved file!"); + + tbssNrrdReader2->Update(); + mitk::TbssImage* tbssImg2 = tbssNrrdReader2->GetOutput(0); + + + std::vector< std::pair > groups = tbssImg->GetGroupInfo(); + std::vector< std::pair > groups2 = tbssImg2->GetGroupInfo(); + + MITK_TEST_CONDITION(groups == groups2, "Check that group information is identical"); + + + mitk::TbssImage::SizeType size = tbssImg->GetLargestPossibleRegion().GetSize(); + mitk::TbssImage::SizeType size2 = tbssImg2->GetLargestPossibleRegion().GetSize(); + + MITK_TEST_CONDITION(size == size2, "Check that sizes are equal"); + + + MITK_TEST_END(); +} diff --git a/Modules/DiffusionImaging/Testing/mitkTbssRoiNrrdImageReaderTest.cpp b/Modules/DiffusionImaging/Testing/mitkTbssRoiNrrdImageReaderTest.cpp new file mode 100644 index 0000000000..b809d77fe3 --- /dev/null +++ b/Modules/DiffusionImaging/Testing/mitkTbssRoiNrrdImageReaderTest.cpp @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include + +#include +#include "mitkDiffusionImagingObjectFactory.h" +#include "mitkCoreObjectFactory.h" + + +/**Documentation + * test for the class "mitkNrrdTbssImageReader". + */ +int mitkTbssRoiNrrdImageReaderTest(int argc , char* argv[]) +{ + MITK_TEST_BEGIN("NrrdTbssRoiImageReaderTest"); + + + mitk::NrrdTbssRoiImageReader::Pointer roiReader = mitk::NrrdTbssRoiImageReader::New(); + + MITK_TEST_CONDITION_REQUIRED(roiReader.GetPointer(), "Testing initialization of test object!"); + + RegisterDiffusionImagingObjectFactory(); + + + roiReader->SetFileName(argv[1]); + MITK_TEST_CONDITION_REQUIRED( roiReader->CanReadFile(argv[1], "", ""), "Testing CanReadFile() method with valid input file name!"); + roiReader->Update(); + + mitk::TbssRoiImage* roiImg = roiReader->GetOutput(0); + + MITK_TEST_CONDITION_REQUIRED(roiImg != NULL, "Testing that roiImg is not null"); + + mitk::TbssRoiImage::SizeType size = roiImg->GetLargestPossibleRegion().GetSize(); + + MITK_TEST_CONDITION_REQUIRED(size[0]==182 && size[1]==218 && size[2]==182, "Test size of ROI image"); + + + // Test indices of the roi + std::vector< itk::Index<3> > indices = roiImg->GetRoi(); + + MITK_TEST_CONDITION(indices.size() == 24, "Test the number of indices belonging to the Roi"); + + itk::Index<3> ix = indices.at(0); + + MITK_TEST_CONDITION(ix[0]==90 && ix[1]==132 && ix[2]==74, "Test whether first index is correct"); + + ix = indices.at(23); + + MITK_TEST_CONDITION(ix[0]==90 && ix[1]==117 && ix[2]==88, "Test whether last index is correct"); + + + + + MITK_TEST_END(); +} diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/BuildFibres.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/BuildFibres.cpp deleted file mode 100644 index 04e8638711..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/BuildFibres.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/*=================================================================== - -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 _BUILDFIBRES -#define _BUILDFIBRES - -//#include "matrix.h" -#define _USE_MATH_DEFINES -#include - -#include -#include -#include - -using namespace std; - -#define PI M_PI - -#include "ParticleGrid.cpp" - -#include -#include -#include -#include -#include -#include - -#include -#include - -class FiberBuilder -{ -public: - Particle *particles; - int pcnt; - int attrcnt; - typedef vector< Particle* > ParticleContainerType; - typedef vector< ParticleContainerType* > FiberContainerType; - - vtkSmartPointer m_VtkCellArray; - vtkSmartPointer m_VtkPoints; - - typedef itk::Vector OdfVectorType; - typedef itk::Image ItkQBallImgType; - ItkQBallImgType::Pointer m_ItkQBallImage; - float m_FiberLength; - itk::Point m_LastPoint; - - FiberBuilder(float *points, int numPoints, double spacing[], ItkQBallImgType::Pointer image) - { - m_FiberLength = 0; - m_ItkQBallImage = image; - particles = (Particle*) malloc(sizeof(Particle)*numPoints); - pcnt = numPoints; - attrcnt = 10; - for (int k = 0; k < numPoints; k++) - { - Particle *p = &(particles[k]); - p->R = pVector(points[attrcnt*k]/spacing[0]-0.5, points[attrcnt*k+1]/spacing[1]-0.5,points[attrcnt*k+2]/spacing[2]-0.5); - p->N = pVector(points[attrcnt*k+3],points[attrcnt*k+4],points[attrcnt*k+5]); - p->cap = points[attrcnt*k+6]; - p->len = points[attrcnt*k+7]; - p->mID = (int) points[attrcnt*k+8]; - p->pID = (int) points[attrcnt*k+9]; - p->ID = k; - p->label = 0; - } - m_VtkCellArray = vtkSmartPointer::New(); - m_VtkPoints = vtkSmartPointer::New(); - } - - ~FiberBuilder() - { - free(particles); - } - - vtkSmartPointer iterate(int minFiberLength) - { - int cur_label = 1; - int numFibers = 0; - m_FiberLength = 0; - for (int k = 0; k < pcnt;k++) - { - Particle *dp = &(particles[k]); - if (dp->label == 0) - { - vtkSmartPointer container = vtkSmartPointer::New(); - dp->label = cur_label; - dp->numerator = 0; - labelPredecessors(dp, container); - labelSuccessors(dp, container); - cur_label++; - if(m_FiberLength >= minFiberLength) - { - m_VtkCellArray->InsertNextCell(container); - numFibers++; - } - m_FiberLength = 0; - } - } - vtkSmartPointer fiberPolyData = vtkSmartPointer::New(); - fiberPolyData->SetPoints(m_VtkPoints); - fiberPolyData->SetLines(m_VtkCellArray); -// vtkSmartPointer cleaner = vtkSmartPointer::New(); -// cleaner->SetInput(fiberPolyData); -// cleaner->Update(); -// fiberPolyData = cleaner->GetOutput(); - return fiberPolyData; - } - - void AddPoint(Particle *dp, vtkSmartPointer container) - { - if (dp->inserted) - return; - - dp->inserted = true; - - itk::ContinuousIndex index; - index[0] = dp->R[0]; - index[1] = dp->R[1]; - index[2] = dp->R[2]; - itk::Point point; - m_ItkQBallImage->TransformContinuousIndexToPhysicalPoint( index, point ); - vtkIdType id = m_VtkPoints->InsertNextPoint(point.GetDataPointer()); - container->GetPointIds()->InsertNextId(id); - - if(container->GetNumberOfPoints()>1) - m_FiberLength += m_LastPoint.EuclideanDistanceTo(point); - - m_LastPoint = point; - } - - void labelPredecessors(Particle *dp, vtkSmartPointer container) - { - if (dp->mID != -1 && dp->mID!=dp->ID) - { - if (dp->ID!=particles[dp->mID].pID) - { - if (dp->ID==particles[dp->mID].mID) - { - int tmp = particles[dp->mID].pID; - particles[dp->mID].pID = particles[dp->mID].mID; - particles[dp->mID].mID = tmp; - } - } - if (particles[dp->mID].label == 0) - { - particles[dp->mID].label = dp->label; - particles[dp->mID].numerator = dp->numerator-1; - labelPredecessors(&(particles[dp->mID]), container); - } - } - - AddPoint(dp, container); - } - - void labelSuccessors(Particle *dp, vtkSmartPointer container) - { - AddPoint(dp, container); - - if (dp->pID != -1 && dp->pID!=dp->ID) - { - if (dp->ID!=particles[dp->pID].mID) - { - if (dp->ID==particles[dp->pID].pID) - { - int tmp = particles[dp->pID].pID; - particles[dp->pID].pID = particles[dp->pID].mID; - particles[dp->pID].mID = tmp; - } - } - if (particles[dp->pID].label == 0) - { - particles[dp->pID].label = dp->label; - particles[dp->pID].numerator = dp->numerator+1; - labelSuccessors(&(particles[dp->pID]), container); - } - } - } -}; - -#endif diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/EnergyComputerBase.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/EnergyComputerBase.cpp deleted file mode 100644 index b21e77ce88..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/EnergyComputerBase.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/*=================================================================== - -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 _ENCOMPINTERFACE -#define _ENCOMPINTERFACE - - -#include "SphereInterpolator.cpp" -#include "ParticleGrid.cpp" -#include -#include -#include - -inline float myATAN2(float y,float x) -{ - float phi = acos(x); - // float phi = ((x>=1.0) ? ((0.0000*x+-0.0000)) : ((x>=1.0) ? ((-10.0167*x+10.0167)) : ((x>=0.9) ? ((-3.1336*x+3.2713)) : ((x>=0.8) ? ((-1.9247*x+2.1833)) : ((x>=0.5) ? ((-1.3457*x+1.7200)) : ((x>=0.0) ? ((-1.0472*x+1.5708)) : ((x>=-0.5) ? ((-1.0472*x+1.5708)) : ((x>=-0.8) ? ((-1.3457*x+1.4216)) : ((x>=-0.9) ? ((-1.9247*x+0.9583)) : ((x>=-1.0) ? ((-3.1336*x+-0.1297)) : ((x>=-1.0) ? ((-10.0167*x+-6.8751)) : 1 ))))))))))); - if (y < 0) phi = 2*PI - phi; - if (phi<0) phi = phi + PI; - return phi; - -} - - -class EnergyComputerBase -{ - -public: - - float *m_QBallImageData; - const int *m_QBallImageSize; - SphereInterpolator *m_SphereInterpolator; - ParticleGrid *m_ParticleGrid; - - int w,h,d; - float voxsize_w; - float voxsize_h; - float voxsize_d; - - int w_sp,h_sp,d_sp; - float voxsize_sp_w; - float voxsize_sp_h; - float voxsize_sp_d; - - - int nip; // number of data vertices on sphere - - - float *m_MaskImageData; - float *cumulspatprob; - int *spatidx; - int scnt; - - - - float eigen_energy; - vnl_matrix_fixed m_RotationMatrix; - - EnergyComputerBase(float *qBallImageData, const int *qBallImageSize, double *voxsize, SphereInterpolator *sp, ParticleGrid *pcon, float *maskImageData, int spmult, vnl_matrix_fixed rotMatrix) - { - m_RotationMatrix = rotMatrix; - m_QBallImageData = qBallImageData; - m_QBallImageSize = qBallImageSize; - m_SphereInterpolator = sp; - - m_MaskImageData = maskImageData; - - nip = m_QBallImageSize[0]; - - - w = m_QBallImageSize[1]; - h = m_QBallImageSize[2]; - d = m_QBallImageSize[3]; - - voxsize_w = voxsize[0]; - voxsize_h = voxsize[1]; - voxsize_d = voxsize[2]; - - - w_sp = m_QBallImageSize[1]*spmult; - h_sp = m_QBallImageSize[2]*spmult; - d_sp = m_QBallImageSize[3]*spmult; - - voxsize_sp_w = voxsize[0]/spmult; - voxsize_sp_h = voxsize[1]/spmult; - voxsize_sp_d = voxsize[2]/spmult; - - - fprintf(stderr,"Data size (voxels) : %i x %i x %i\n",w,h,d); - fprintf(stderr,"voxel size: %f x %f x %f\n",voxsize_w,voxsize_h,voxsize_d); - fprintf(stderr,"mask_oversamp_mult: %i\n",spmult); - - if (nip != sp->nverts) - { - fprintf(stderr,"EnergyComputer: error during init: data does not match with interpolation scheme\n"); - } - - m_ParticleGrid = pcon; - - - int totsz = w_sp*h_sp*d_sp; - cumulspatprob = (float*) malloc(sizeof(float) * totsz); - spatidx = (int*) malloc(sizeof(int) * totsz); - if (cumulspatprob == 0 || spatidx == 0) - { - fprintf(stderr,"EnergyCOmputerBase: out of memory!\n"); - return ; - } - - - scnt = 0; - cumulspatprob[0] = 0; - for (int x = 1; x < w_sp;x++) - for (int y = 1; y < h_sp;y++) - for (int z = 1; z < d_sp;z++) - { - int idx = x+(y+z*h_sp)*w_sp; - if (m_MaskImageData[idx] > 0.5) - { - cumulspatprob[scnt+1] = cumulspatprob[scnt] + m_MaskImageData[idx]; - spatidx[scnt] = idx; - scnt++; - } - } - - for (int k = 0; k < scnt; k++) - { - cumulspatprob[k] /= cumulspatprob[scnt]; - } - - fprintf(stderr,"#active voxels: %i (in mask units) \n",scnt); - - - - } - - ~EnergyComputerBase() - { - free(cumulspatprob); - free(spatidx); - } - - virtual void setParameters() - { - - } - - - - void drawSpatPosition(pVector *R) - { - float r = mtrand.frand(); - int j; - int rl = 1; - int rh = scnt; - while(rh != rl) - { - j = rl + (rh-rl)/2; - if (r < cumulspatprob[j]) - { - rh = j; - continue; - } - if (r > cumulspatprob[j]) - { - rl = j+1; - continue; - } - break; - } - R->SetXYZ(voxsize_sp_w*((float)(spatidx[rh-1] % w_sp) + mtrand.frand()), - voxsize_sp_h*((float)((spatidx[rh-1]/w_sp) % h_sp) + mtrand.frand()), - voxsize_sp_d*((float)(spatidx[rh-1]/(w_sp*h_sp)) + mtrand.frand())); - } - - float SpatProb(pVector R) - { - int rx = int(R.GetX()/voxsize_sp_w); - int ry = int(R.GetY()/voxsize_sp_h); - int rz = int(R.GetZ()/voxsize_sp_d); - if (rx >= 0 && rx < w_sp && ry >= 0 && ry < h_sp && rz >= 0 && rz < d_sp){ - return m_MaskImageData[rx + w_sp* (ry + h_sp*rz)]; - } - else - return 0; - } - - /* - inline float evaluateODF(pVector &R, pVector &N, float &len) - { - const int CU = 10; - pVector Rs; - float Dn = 0; - int xint,yint,zint,spatindex; - - sinterp->getInterpolation(N); - for (int i=-CU; i < CU;i++) - { - Rs = R + (N * len) * ((float)i/CU); - xint = int(Rs.x); - yint = int(Rs.y); - zint = int(Rs.z); - if (xint > 0 && xint < w-1 && yint > 0 && yint < h-1 && zint > 0 && zint < d-1) - { - spatindex = (xint + w*(yint+h*zint)) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2]); - } - } - - Dn /= (float)(2*CU+1); - return Dn; - } -*/ - - - inline float evaluateODF(pVector &R, pVector &N, float &len) - { - const int CU = 10; - pVector Rs; - float Dn = 0; - int xint,yint,zint,spatindex; - - vnl_vector_fixed n; - n[0] = N[0]; - n[1] = N[1]; - n[2] = N[2]; - n = m_RotationMatrix*n; - m_SphereInterpolator->getInterpolation(n); - - for (int i=-CU; i <= CU;i++) - { - Rs = R + (N * len) * ((float)i/CU); - - float Rx = Rs[0]/voxsize_w-0.5; - float Ry = Rs[1]/voxsize_h-0.5; - float Rz = Rs[2]/voxsize_d-0.5; - - - xint = int(floor(Rx)); - yint = int(floor(Ry)); - zint = int(floor(Rz)); - - - if (xint >= 0 && xint < w-1 && yint >= 0 && yint < h-1 && zint >= 0 && zint < d-1) - { - float xfrac = Rx-xint; - float yfrac = Ry-yint; - float zfrac = Rz-zint; - - float weight; - - weight = (1-xfrac)*(1-yfrac)*(1-zfrac); - spatindex = (xint + w*(yint+h*zint)) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - weight = (xfrac)*(1-yfrac)*(1-zfrac); - spatindex = (xint+1 + w*(yint+h*zint)) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - weight = (1-xfrac)*(yfrac)*(1-zfrac); - spatindex = (xint + w*(yint+1+h*zint)) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - weight = (1-xfrac)*(1-yfrac)*(zfrac); - spatindex = (xint + w*(yint+h*(zint+1))) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - weight = (xfrac)*(yfrac)*(1-zfrac); - spatindex = (xint+1 + w*(yint+1+h*zint)) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - weight = (1-xfrac)*(yfrac)*(zfrac); - spatindex = (xint + w*(yint+1+h*(zint+1))) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - weight = (xfrac)*(1-yfrac)*(zfrac); - spatindex = (xint+1 + w*(yint+h*(zint+1))) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - weight = (xfrac)*(yfrac)*(zfrac); - spatindex = (xint+1 + w*(yint+1+h*(zint+1))) *nip; - Dn += (m_QBallImageData[spatindex + m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + m_QBallImageData[spatindex + m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; - - - } - - - } - - Dn *= 1.0/(2*CU+1); - return Dn; - } - - - /* - inline float evaluateODF(pVector &R, pVector &N, float &len) - { - - R.storeXYZ(); - - float Rx = pVector::store[0]/voxsize_w; - float Ry = pVector::store[1]/voxsize_h; - float Rz = pVector::store[2]/voxsize_d; - - - int xint = int(Rx); - int yint = int(Ry); - int zint = int(Rz); - - if (xint >= 0 && xint < w-1 && yint >= 0 && yint < h-1 && zint >= 0 && zint < d-1) - { - float xfrac = Rx-xint; - float yfrac = Ry-yint; - float zfrac = Rz-zint; - sinterp->getInterpolation(N); - - float weight; - int spatindex; - float Dn = 0; - - weight = (1-xfrac)*(1-yfrac)*(1-zfrac); - spatindex = (xint + w*(yint+h*zint)) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - weight = (xfrac)*(1-yfrac)*(1-zfrac); - spatindex = (xint+1 + w*(yint+h*zint)) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - weight = (1-xfrac)*(yfrac)*(1-zfrac); - spatindex = (xint + w*(yint+1+h*zint)) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - weight = (1-xfrac)*(1-yfrac)*(zfrac); - spatindex = (xint + w*(yint+h*(zint+1))) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - weight = (xfrac)*(yfrac)*(1-zfrac); - spatindex = (xint+1 + w*(yint+1+h*zint)) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - weight = (1-xfrac)*(yfrac)*(zfrac); - spatindex = (xint + w*(yint+1+h*(zint+1))) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - weight = (xfrac)*(1-yfrac)*(zfrac); - spatindex = (xint+1 + w*(yint+h*(zint+1))) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - weight = (xfrac)*(yfrac)*(zfrac); - spatindex = (xint+1 + w*(yint+1+h*(zint+1))) *nip; - Dn += (dataimg[spatindex + sinterp->idx[0]]*sinterp->interpw[0] + dataimg[spatindex + sinterp->idx[1]]*sinterp->interpw[1] + dataimg[spatindex + sinterp->idx[2]]* sinterp->interpw[2])*weight; - - return Dn; - - } - return 0; - } - -*/ - - virtual inline float computeExternalEnergy(pVector &R, pVector &N, float &cap, float &len, Particle *dp) { return 0;} - virtual inline float computeInternalEnergy(Particle *p1) {return 0;} - virtual inline float computeInternalEnergyConnection(Particle *p1,int ep1) {return 0;} - virtual inline float computeInternalEnergyConnection(Particle *p1,int ep1, Particle *p2, int ep2) {return 0;} - - - -}; - -#endif - - diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/EnergyComputer_connec.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/EnergyComputer_connec.cpp deleted file mode 100644 index ec23b23b27..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/EnergyComputer_connec.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/*=================================================================== - -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. - -===================================================================*/ - - - -/// bessel for wid = 1 - -//#define mbesseli0(x) ((x>=1.0) ? ((-0.2578*x+0.7236)*exp(x)) : ((x>=0.9) ? ((-0.2740*x+0.7398)*exp(x)) : ((x>=0.8) ? ((-0.3099*x+0.7720)*exp(x)) : ((x>=0.7) ? ((-0.3634*x+0.8149)*exp(x)) : ((x>=0.5) ? ((-0.4425*x+0.8663)*exp(x)) : ((x>=0.3) ? ((-0.5627*x+0.9264)*exp(x)) : ((x>=0.2) ? ((-0.6936*x+0.9657)*exp(x)) : ((x>=0.1) ? ((-0.8016*x+0.9873)*exp(x)) : ((x>=0.0) ? ((-0.9290*x+1.0000)*exp(x)) : 1 ))))))))) -//#define mbesseli0(x) ((x>=1.0) ? ((0.5652*x+0.7009)) : ((x>=0.8) ? ((0.4978*x+0.7683)) : ((x>=0.6) ? ((0.3723*x+0.8686)) : ((x>=0.4) ? ((0.2582*x+0.9371)) : ((x>=0.2) ? ((0.1519*x+0.9796)) : ((x>=0.0) ? ((0.0501*x+1.0000)) : 1 )))))) - - -inline float mbesseli0(float x) -{ - float y = x*x; - float erg = BESSEL_APPROXCOEFF[0]; - erg += y*BESSEL_APPROXCOEFF[1]; - erg += y*y*BESSEL_APPROXCOEFF[2]; - erg += y*y*y*BESSEL_APPROXCOEFF[3]; - return erg; -} - -// -// -// inline REAL mbesseli0(REAL x) -// { -// REAL y = x*x; -// REAL erg = BESSEL_APPROXCOEFF[0]; -// erg += y*BESSEL_APPROXCOEFF[1]; -// erg += y*x*BESSEL_APPROXCOEFF[2]; -// erg += x*x*BESSEL_APPROXCOEFF[3]; -// return erg; -// } - -inline float mexp(float x) -{ - - return((x>=7.0) ? 0 : ((x>=5.0) ? (-0.0029*x+0.0213) : ((x>=3.0) ? (-0.0215*x+0.1144) : ((x>=2.0) ? (-0.0855*x+0.3064) : ((x>=1.0) ? (-0.2325*x+0.6004) : ((x>=0.5) ? (-0.4773*x+0.8452) : ((x>=0.0) ? (-0.7869*x+1.0000) : 1 ))))))); - // return exp(-x); - -} - - -#include "ParticleGrid.cpp" - -#include "EnergyComputerBase.cpp" -#include - - -class EnergyComputer : public EnergyComputerBase -{ - -public: - - - float eigencon_energy; - - float chempot2; - float meanval_sq; - - float gamma_s; - float gamma_reg_s; - - float particle_weight; - float ex_strength; - float in_strength; - - float particle_length_sq; - float curv_hard; - - - EnergyComputer(float *data, const int *dsz, double *cellsize, SphereInterpolator *sp, ParticleGrid *pcon, float *spimg, int spmult, vnl_matrix_fixed rotMatrix) : EnergyComputerBase(data,dsz,cellsize,sp,pcon,spimg,spmult,rotMatrix) - { - - } - - void setParameters(float pwei,float pwid,float chempot_connection, float length,float curv_hardthres, float inex_balance, float chempot2, float meanv) - { - this->chempot2 = chempot2; - meanval_sq = meanv; - - eigencon_energy = chempot_connection; - eigen_energy = 0; - particle_weight = pwei; - - float bal = 1/(1+exp(-inex_balance)); - ex_strength = 2*bal; // cleanup (todo) - in_strength = 2*(1-bal)/length/length; // cleanup (todo) - // in_strength = 0.64/length/length; // cleanup (todo) - - particle_length_sq = length*length; - curv_hard = curv_hardthres; - - float sigma_s = pwid; - gamma_s = 1/(sigma_s*sigma_s); - gamma_reg_s =1/(length*length/4); - } - - - - //////////////////////////////////////////////////////////////////////////// - ////// External Energy - //////////////////////////////////////////////////////////////////////////// - inline float computeExternalEnergy(pVector &R, pVector &N, float &cap, float &len, Particle *dp) - { - float m = SpatProb(R); - if (m == 0) - { - return -INFINITY; - } - - float Dn = evaluateODF(R,N,len); - - float Sn = 0; - float Pn = 0; - - m_ParticleGrid->computeNeighbors(R); - for (;;) - { - Particle *p = m_ParticleGrid->getNextNeighbor(); - if (p == 0) break; - if (dp != p) - { - float dot = fabs(N*p->N); - float bw = mbesseli0(dot); - float dpos = (p->R-R).norm_square(); - float w = mexp(dpos*gamma_s); - Sn += w*(bw+chempot2)*p->cap ; - //Sn += w*(bw-meanval_sq)*p->cap ; - w = mexp(dpos*gamma_reg_s); - Pn += w*bw; - } - } - - float energy = 0; - energy += (2*(Dn/particle_weight-Sn) - (mbesseli0(1.0)+chempot2)*cap)*cap; - //energy += (2*(Dn/particle_weight-Sn) - (mbesseli0(1.0)-meanval_sq)*cap)*cap; - - return energy*ex_strength; - } - - - //////////////////////////////////////////////////////////////////////////// - ////// Internal Energy - //////////////////////////////////////////////////////////////////////////// - - inline float computeInternalEnergy(Particle *dp) - { - float energy = eigen_energy; - - if (dp->pID != -1) - energy += computeInternalEnergyConnection(dp,+1); - - if (dp->mID != -1) - energy += computeInternalEnergyConnection(dp,-1); - - //ie_file << energy << "\n"; - - return energy; - } - - inline float computeInternalEnergyConnection(Particle *p1,int ep1) - { - Particle *p2 = 0; - int ep2; - if (ep1 == 1) - p2 = m_ParticleGrid->ID_2_address[p1->pID]; - else - p2 = m_ParticleGrid->ID_2_address[p1->mID]; - if (p2->mID == p1->ID) - ep2 = -1; - else if (p2->pID == p1->ID) - ep2 = 1; - else - fprintf(stderr,"EnergyComputer_connec: Connections are inconsistent!\n"); - - if (p2 == 0) - fprintf(stderr,"bug2"); - - return computeInternalEnergyConnection(p1,ep1,p2,ep2); - } - - inline float computeInternalEnergyConnection(Particle *p1,int ep1, Particle *p2, int ep2) - { -#ifdef TIMING - tic(&internalenergy_time); -#endif - - if ((p1->N*p2->N)*ep1*ep2 > -curv_hard) - return -INFINITY; - - pVector R1 = p1->R + (p1->N * (p1->len * ep1)); - pVector R2 = p2->R + (p2->N * (p2->len * ep2)); - - if ((R1-R2).norm_square() > particle_length_sq) - return -INFINITY; - - pVector R = (p2->R + p1->R)*0.5; - - if (SpatProb(R) == 0) - return -INFINITY; - - float norm1 = (R1-R).norm_square(); - float norm2 = (R2-R).norm_square(); - - - float energy = (eigencon_energy-norm1-norm2)*in_strength; - -#ifdef TIMING - toc(&internalenergy_time); -#endif - - return energy; - } - - - - - - - - - -}; - diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/MersenneTwister.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/MersenneTwister.h deleted file mode 100644 index 01d0784e24..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/MersenneTwister.h +++ /dev/null @@ -1,611 +0,0 @@ -/*=================================================================== - -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. - -===================================================================*/ -// MersenneTwister.h -// Mersenne Twister random number generator -- a C++ class MTRand -// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus -// Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com - -// The Mersenne Twister is an algorithm for generating random numbers. It -// was designed with consideration of the flaws in various other generators. -// The period, 2^19937-1, and the order of equidistribution, 623 dimensions, -// are far greater. The generator is also fast; it avoids multiplication and -// division, and it benefits from caches and pipelines. For more information -// see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html - -// Reference -// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally -// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on -// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. - -// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, -// Copyright (C) 2000 - 2003, Richard J. Wagner -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The names of its contributors may not be used to endorse or promote -// products derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// The original code included the following notice: -// -// When you use this, send an email to: matumoto@math.keio.ac.jp -// with an appropriate reference to your work. -// -// It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu -// when you write. - -#ifndef MERSENNETWISTER_H -#define MERSENNETWISTER_H - -// Not thread safe (unless auto-initialization is avoided and each thread has -// its own MTRand object) - -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -class MTRand { -// Data -public: - typedef unsigned long uint32; // unsigned integer type, at least 32 bits - - enum { N = 624 }; // length of state vector - enum { SAVE = N + 1 }; // length of array for save() - -// protected: - enum { M = 397 }; // period parameter - - uint32 state[N]; // internal state - uint32 *pNext; // next value to get from state - int left; // number of values left before reload needed - - -//Methods -public: - MTRand( const uint32& oneSeed ); // initialize with a simple uint32 - MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array - MTRand(); // auto-initialize with /dev/urandom or time() and clock() - ~MTRand(); - - // Do NOT use for CRYPTOGRAPHY without securely hashing several returned - // values together, otherwise the generator state can be learned after - // reading 624 consecutive values. - - // Access to 32-bit random numbers - double rand(); // real number in [0,1] - float frand(); // real number in [0,1] - double rand( const double& n ); // real number in [0,n] - double randExc(); // real number in [0,1) - double randExc( const double& n ); // real number in [0,n) - double randDblExc(); // real number in (0,1) - double randDblExc( const double& n ); // real number in (0,n) - uint32 randInt(); // integer in [0,2^32-1] - uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32 - double operator()() { return rand(); } // same as rand() - - // Access to 53-bit random numbers (capacity of IEEE double precision) - double rand53(); // real number in [0,1) - - // Access to nonuniform random number distributions - double randNorm( const double& mean = 0.0, const double& variance = 0.0 ); - float frandn(); - - // Re-seeding functions with same behavior as initializers - void seed( const uint32 oneSeed ); - void seed( uint32 *const bigSeed, const uint32 seedLength = N ); - void seed(); - - - ////// - int Poisson(); - void initPoisson(float mean, int accuracy); - float *cumulPoisson; - int accuracyPoisson; - - int drawGamma(); - void initGamma(float theta, int shape); - float *cumulGamma; - int accuracyGamma; - - - - // Saving and loading generator state - void save( uint32* saveArray ) const; // to array of size SAVE - void load( uint32 *const loadArray ); // from such array - friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); - friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); - -protected: - void initialize( const uint32 oneSeed ); - void reload(); - uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; } - uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; } - uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; } - uint32 mixBits( const uint32& u, const uint32& v ) const - { return hiBit(u) | loBits(v); } - uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const - { return m ^ (mixBits(s0,s1)>>1) ^ (-loBit(s1) & 0x9908b0dfUL); } - static uint32 hash( time_t t, clock_t c ); -}; - - -inline MTRand::MTRand( const uint32& oneSeed ) - { seed(oneSeed); - cumulPoisson = 0; - cumulGamma = 0; - } - -inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) - { seed(bigSeed,seedLength); - cumulPoisson = 0; - cumulGamma = 0; - - } - -inline MTRand::MTRand() - { seed(); - cumulPoisson = 0; - cumulGamma = 0; - } - -inline MTRand::~MTRand() -{ - if (cumulPoisson != 0) - free(cumulPoisson); - if (cumulGamma != 0) - free(cumulGamma); -} -inline double MTRand::rand() - { return double(randInt()) * (1.0/4294967295.0); } - -inline float MTRand::frand() - { - return float(randInt()) * (1.0/4294967295.0); - } - - -inline double MTRand::rand( const double& n ) - { return rand() * n; } - -inline double MTRand::randExc() - { return double(randInt()) * (1.0/4294967296.0); } - -inline double MTRand::randExc( const double& n ) - { return randExc() * n; } - -inline double MTRand::randDblExc() - { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } - -inline double MTRand::randDblExc( const double& n ) - { return randDblExc() * n; } - -inline double MTRand::rand53() -{ - uint32 a = randInt() >> 5, b = randInt() >> 6; - return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada -} - -inline double MTRand::randNorm( const double& mean, const double& variance ) -{ - // Return a real number from a normal (Gaussian) distribution with given - // mean and variance by Box-Muller method - double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance; - double phi = 2.0 * M_PI * randExc(); - return mean + r * cos(phi); -} - -inline float MTRand::frandn() -{ - // Return a real number from a normal (Gaussian) distribution with given - // mean and variance by Box-Muller method - float r = sqrt( -2.0 * log( 1.0-randDblExc()) ); - double phi = 2.0 * M_PI * randExc(); - return r * cos(phi); -} - -inline int MTRand::Poisson() -{ - int j; - - float r = frand(); - - int rl = 0; - int rh = accuracyPoisson-1; - while(rh != rl) - { - j = rl + (rh-rl)/2; - if (r < cumulPoisson[j]) - { - rh = j; - continue; - } - if (r > cumulPoisson[j]) - { - rl = j+1; - continue; - } - break; - } - j = rh; - - return j; -} - - -inline int MTRand::drawGamma() -{ - int j; - - float r = frand(); - - int rl = 0; - int rh = accuracyGamma-1; - while(rh != rl) - { - j = rl + (rh-rl)/2; - if (r < cumulGamma[j]) - { - rh = j; - continue; - } - if (r > cumulGamma[j]) - { - rl = j+1; - continue; - } - break; - } - j = rh; - - return j; -} - - -/* -inline void MTRand::initPoisson(float mean,int accuracy) -{ - float p[accuracy]; - float Z = exp(-mean); - p[0] = 1; - for (int i = 1; i < accuracy;i++) - p[i] = p[i-1]*mean/i; - for (int i = 0; i < accuracy;i++) - p[i] *= Z; - - if (cumulPoisson != 0) - free(cumulPoisson); - - cumulPoisson = (float*) malloc(sizeof(float)*accuracy); - cumulPoisson[0] = p[0]; - for (int i = 1; i < accuracy;i++) - cumulPoisson[i] = cumulPoisson[i-1]+p[i]; - for (int i = 0; i < accuracy;i++) - cumulPoisson[i] = cumulPoisson[i]/cumulPoisson[accuracy-1]; - - accuracyPoisson = accuracy; - - -} - - - - - -inline void MTRand::initGamma(float theta,int k) -{ - int accuracy = int(k*theta*5); - - float p[accuracy]; - int fac = 1; - for (int i = 1; i < k-1;i++) - fac *= i; - - for (int i = 0; i < accuracy;i++) - { - p[i] = pow(i/theta,k-1)/fac * exp(i/theta); - } - if (cumulGamma != 0) - free(cumulGamma); - - cumulGamma = (float*) malloc(sizeof(float)*accuracy); - cumulGamma[0] = p[0]; - for (int i = 1; i < accuracy;i++) - cumulGamma[i] = cumulGamma[i-1]+p[i]; - for (int i = 0; i < accuracy;i++) - cumulGamma[i] = cumulGamma[i]/cumulGamma[accuracy-1]; - - accuracyGamma = accuracy; - - -} - - -*/ - - - - -inline MTRand::uint32 MTRand::randInt() -{ - // Pull a 32-bit integer from the generator state - // Every other access function simply transforms the numbers extracted here - - if( left == 0 ) reload(); - --left; - - register uint32 s1; - s1 = *pNext++; - s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9d2c5680UL; - s1 ^= (s1 << 15) & 0xefc60000UL; - return ( s1 ^ (s1 >> 18) ); -} - -inline MTRand::uint32 MTRand::randInt( const uint32& n ) -{ - // Find which bits are used in n - // Optimized by Magnus Jonsson (magnus@smartelectronix.com) - uint32 used = n; - used |= used >> 1; - used |= used >> 2; - used |= used >> 4; - used |= used >> 8; - used |= used >> 16; - - // Draw numbers until one is found in [0,n] - uint32 i; - do - i = randInt() & used; // toss unused bits to shorten search - while( i > n ); - return i; -} - - -inline void MTRand::seed( const uint32 oneSeed ) -{ - // Seed the generator with a simple uint32 - initialize(oneSeed); - reload(); -} - - -inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) -{ - // Seed the generator with an array of uint32's - // There are 2^19937-1 possible initial states. This function allows - // all of those to be accessed by providing at least 19937 bits (with a - // default seed length of N = 624 uint32's). Any bits above the lower 32 - // in each element are discarded. - // Just call seed() if you want to get array from /dev/urandom - initialize(19650218UL); - register int i = 1; - register uint32 j = 0; - register int k = ( N > seedLength ? N : seedLength ); - for( ; k; --k ) - { - state[i] = - state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); - state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; - state[i] &= 0xffffffffUL; - ++i; ++j; - if( i >= N ) { state[0] = state[N-1]; i = 1; } - if( j >= seedLength ) j = 0; - } - for( k = N - 1; k; --k ) - { - state[i] = - state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); - state[i] -= i; - state[i] &= 0xffffffffUL; - ++i; - if( i >= N ) { state[0] = state[N-1]; i = 1; } - } - state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array - reload(); -} - - -inline void MTRand::seed() -{ - // Seed the generator with an array from /dev/urandom if available - // Otherwise use a hash of time() and clock() values - - // First try getting an array from /dev/urandom - FILE* urandom = fopen( "/dev/urandom", "rb" ); - if( urandom ) - { - uint32 bigSeed[N]; - register uint32 *s = bigSeed; - register int i = N; - register bool success = true; - while( success && i-- ) - success = fread( s++, sizeof(uint32), 1, urandom ); - fclose(urandom); - if( success ) { seed( bigSeed, N ); return; } - } - - // Was not successful, so use time() and clock() instead - seed( hash( time(NULL), clock() ) ); -} - - -inline void MTRand::initialize( const uint32 seed ) -{ - // Initialize generator state with seed - // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. - // In previous versions, most significant bits (MSBs) of the seed affect - // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. - register uint32 *s = state; - register uint32 *r = state; - register int i = 1; - *s++ = seed & 0xffffffffUL; - for( ; i < N; ++i ) - { - *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; - r++; - } -} - - -inline void MTRand::reload() -{ - // Generate N new values in state - // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) - register uint32 *p = state; - register int i; - for( i = N - M; i--; ++p ) - *p = twist( p[M], p[0], p[1] ); - for( i = M; --i; ++p ) - *p = twist( p[M-N], p[0], p[1] ); - *p = twist( p[M-N], p[0], state[0] ); - - left = N, pNext = state; -} - - -inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) -{ - // Get a uint32 from t and c - // Better than uint32(x) in case x is floating point in [0,1] - // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) - - static uint32 differ = 0; // guarantee time-based seeds will change - - uint32 h1 = 0; - unsigned char *p = (unsigned char *) &t; - for( size_t i = 0; i < sizeof(t); ++i ) - { - h1 *= UCHAR_MAX + 2U; - h1 += p[i]; - } - uint32 h2 = 0; - p = (unsigned char *) &c; - for( size_t j = 0; j < sizeof(c); ++j ) - { - h2 *= UCHAR_MAX + 2U; - h2 += p[j]; - } - return ( h1 + differ++ ) ^ h2; -} - - -inline void MTRand::save( uint32* saveArray ) const -{ - register uint32 *sa = saveArray; - register const uint32 *s = state; - register int i = N; - for( ; i--; *sa++ = *s++ ) {} - *sa = left; -} - - -inline void MTRand::load( uint32 *const loadArray ) -{ - register uint32 *s = state; - register uint32 *la = loadArray; - register int i = N; - for( ; i--; *s++ = *la++ ) {} - left = *la; - pNext = &state[N-left]; -} - - -inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) -{ - register const MTRand::uint32 *s = mtrand.state; - register int i = mtrand.N; - for( ; i--; os << *s++ << "\t" ) {} - return os << mtrand.left; -} - - -inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) -{ - register MTRand::uint32 *s = mtrand.state; - register int i = mtrand.N; - for( ; i--; is >> *s++ ) {} - is >> mtrand.left; - mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; - return is; -} - -#endif // MERSENNETWISTER_H - -// Change log: -// -// v0.1 - First release on 15 May 2000 -// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus -// - Translated from C to C++ -// - Made completely ANSI compliant -// - Designed convenient interface for initialization, seeding, and -// obtaining numbers in default or user-defined ranges -// - Added automatic seeding from /dev/urandom or time() and clock() -// - Provided functions for saving and loading generator state -// -// v0.2 - Fixed bug which reloaded generator one step too late -// -// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew -// -// v0.4 - Removed trailing newline in saved generator format to be consistent -// with output format of built-in types -// -// v0.5 - Improved portability by replacing static const int's with enum's and -// clarifying return values in seed(); suggested by Eric Heimburg -// - Removed MAXINT constant; use 0xffffffffUL instead -// -// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits -// - Changed integer [0,n] generator to give better uniformity -// -// v0.7 - Fixed operator precedence ambiguity in reload() -// - Added access for real numbers in (0,1) and (0,n) -// -// v0.8 - Included time.h header to properly support time_t and clock_t -// -// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto -// - Allowed for seeding with arrays of any length -// - Added access for real numbers in [0,1) with 53-bit resolution -// - Added access for real numbers from normal (Gaussian) distributions -// - Increased overall speed by optimizing twist() -// - Doubled speed of integer [0,n] generation -// - Fixed out-of-range number generation on 64-bit machines -// - Improved portability by substituting literal constants for long enum's -// - Changed license from GNU LGPL to BSD diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/ParticleGrid.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/ParticleGrid.cpp deleted file mode 100644 index ab8c49410a..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/ParticleGrid.cpp +++ /dev/null @@ -1,625 +0,0 @@ -/*=================================================================== - -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 _PARTICLEGRID -#define _PARTICLEGRID - - -#include "auxilary_classes.cpp" - - -template - class ParticleGrid -{ - - //////////////// Container -public: - T *particles; // particles in linear array - int pcnt; // actual number of particles - int concnt; // number of connections - int celloverflows; - - T **ID_2_address; - -private: - - int capacity; // maximal number of particles - int increase_step; - - /////////////////// Grid - - T **grid; // the grid - - // grid size - int nx; - int ny; - int nz; - - // scaling factor for grid - float mulx; - float muly; - float mulz; - - int csize; // particle capacity of single cell in grid - int *occnt; // occupation count of grid cells - int gridsize; // total number of cells - float m_Memory; - - struct NeighborTracker // to run over the neighbors - { - int cellidx[8]; - int cellidx_c[8]; - - int cellcnt; - int pcnt; - - } nbtrack; - - - -public: - - - ParticleGrid() - { - - //// involving the container - capacity = 0; - particles = 0; - ID_2_address = 0; - pcnt = 0; - concnt = 0; - celloverflows = 0; - - ////// involvin the grid - nx = 0; ny = 0; nz = 0; csize = 0; - gridsize = 0; - grid = (T**) 0; - occnt = (int*) 0; - - increase_step = 100000; - m_Memory = 0; - } - - float GetMemoryUsage() - { - return m_Memory; - } - - int allocate(int _capacity, - int _nx, int _ny, int _nz, float cellsize, int cellcapacity) - { - //// involving the container - capacity = _capacity; - particles = (T*) malloc(sizeof(T)*capacity); - ID_2_address = (T**) malloc(sizeof(T*)*capacity); - - if (particles == 0 || ID_2_address == 0) - { - fprintf(stderr,"error: Out of Memory\n"); - capacity = 0; - return -1; - } - else - { - fprintf(stderr,"Allocated Memory for %i particles \n",capacity); - } - - pcnt = 0; - int i; - for (i = 0;i < capacity;i++) - { - ID_2_address[i] = &(particles[i]); // initialize pointer in LUT - particles[i].ID = i; // initialize unique IDs - } - - ////// involvin the grid - nx = _nx; ny = _ny; nz = _nz; csize = cellcapacity; - gridsize = nx*ny*nz; - - m_Memory = (float)(sizeof(T*)*gridsize*csize)/1000000; - - grid = (T**) malloc(sizeof(T*)*gridsize*csize); - occnt = (int*) malloc(sizeof(int)*gridsize); - - if (grid == 0 || occnt == 0) - { - fprintf(stderr,"error: Out of Memory\n"); - capacity = 0; - return -1; - } - - for (i = 0;i < gridsize;i++) - occnt[i] = 0; - - mulx = 1/cellsize; - muly = 1/cellsize; - mulz = 1/cellsize; - - return 1; - } - - - - int reallocate() - { - int new_capacity = capacity + increase_step; - T* new_particles = (T*) realloc(particles,sizeof(T)*new_capacity); - T** new_ID_2_address = (T**) realloc(ID_2_address,sizeof(T*)*new_capacity); - if (new_particles == 0 || new_ID_2_address == 0) - { - fprintf(stderr,"ParticleGird:reallocate: out of memory!\n"); - return -1; - } - fprintf(stderr," now %i particles are allocated \n",new_capacity); - m_Memory = (float)(sizeof(T*)*new_capacity)/1000000; - - int i; - for (i = 0; i < capacity; i++) - { - new_ID_2_address[i] = new_ID_2_address[i] - particles + new_particles; // shift address - } - for (i = capacity; i < new_capacity; i++) - { - new_particles[i].ID = i; // initialize unique IDs - new_ID_2_address[i] = &(new_particles[i]) ; // initliaze pointer in LUT - } - for (i = 0; i < nx*ny*nz*csize; i++) - { - grid[i] = grid[i] - particles + new_particles; - } - particles = new_particles; - ID_2_address = new_ID_2_address; - capacity = new_capacity; - - return 1; - } - - - - ~ParticleGrid() - { - if (particles != 0) - free(particles); - if (grid != 0) - free(grid); - if (occnt != 0) - free(occnt); - if (ID_2_address != 0) - free(ID_2_address); - } - - - - int ID_2_index(int ID) - { - if (ID == -1) - return -1; - else - return (ID_2_address[ID] - particles); - - } - - - T* newParticle(pVector R) - { - /////// get free place in container; - if (pcnt >= capacity) - { - fprintf(stderr,"capacity overflow , reallocating ...\n"); - if (reallocate() == -1) - { - fprintf(stderr,"out of Memory!!\n"); - return 0; - } - } - - int xint = int(R[0]*mulx); - if (xint < 0) { //fprintf(stderr,"error: out of grid\n"); - return 0;} - if (xint >= nx) { // fprintf(stderr,"error: out of grid\n"); - return 0;} - int yint = int(R[1]*muly); - if (yint < 0) { //fprintf(stderr,"error: out of grid\n"); - return 0;} - if (yint >= ny) {// fprintf(stderr,"error: out of grid\n"); - return 0;} - int zint = int(R[2]*mulz); - if (zint < 0) {// fprintf(stderr,"error: out of grid\n"); - return 0;} - if (zint >= nz) { //fprintf(stderr,"error: out of grid\n"); - return 0;} - - int idx = xint + nx*(yint + ny*zint); - if (occnt[idx] < csize) - { - T *p = &(particles[pcnt]); - p->R = R; - p->mID = -1; - p->pID = -1; - pcnt++; - p->gridindex = csize*idx + occnt[idx]; - grid[p->gridindex] = p; - occnt[idx]++; - return p; - } - else - { - celloverflows++; - //fprintf(stderr,"error: cell overflow \n"); - return 0; - } - } - - - inline void updateGrid(int k) - { - T* p = &(particles[k]); - - /////// find new grid cell - int xint = int(p->R[0]*mulx); - if (xint < 0) { remove(k); return; } - if (xint >= nx) { remove(k); return; } - int yint = int(p->R[1]*muly); - if (yint < 0) { remove(k); return; } - if (yint >= ny) { remove(k); return; } - int zint = int(p->R[2]*mulz); - if (zint < 0) { remove(k); return; } - if (zint >= nz) { remove(k); return; } - - - int idx = xint + nx*(yint+ zint*ny); - int cellidx = p->gridindex/csize; - if (idx != cellidx) // cell has changed - { - - if (occnt[idx] < csize) - { - // remove from old position in grid; - int grdindex = p->gridindex; - grid[grdindex] = grid[cellidx*csize + occnt[cellidx]-1]; - grid[grdindex]->gridindex = grdindex; - occnt[cellidx]--; - - // insert at new position in grid - p->gridindex = idx*csize + occnt[idx]; - grid[p->gridindex] = p; - occnt[idx]++; - } - else - remove(k); - - } - } - - - inline bool tryUpdateGrid(int k) - { - T* p = &(particles[k]); - - /////// find new grid cell - int xint = int(p->R[0]*mulx); - if (xint < 0) { return false; } - if (xint >= nx) { return false; } - int yint = int(p->R[1]*muly); - if (yint < 0) { return false; } - if (yint >= ny) { return false; } - int zint = int(p->R[2]*mulz); - if (zint < 0) { return false; } - if (zint >= nz) { return false; } - - - int idx = xint + nx*(yint+ zint*ny); - int cellidx = p->gridindex/csize; - if (idx != cellidx) // cell has changed - { - - if (occnt[idx] < csize) - { - // remove from old position in grid; - int grdindex = p->gridindex; - grid[grdindex] = grid[cellidx*csize + occnt[cellidx]-1]; - grid[grdindex]->gridindex = grdindex; - occnt[cellidx]--; - - // insert at new position in grid - p->gridindex = idx*csize + occnt[idx]; - grid[p->gridindex] = p; - occnt[idx]++; - return true; - } - else - return false; - - } - return true; - } - - - - inline void remove(int k) - { - T* p = &(particles[k]); - int grdindex = p->gridindex; - int cellidx = grdindex/csize; - int idx = grdindex%csize; - - // remove pending connections - if (p->mID != -1) - destroyConnection(p,-1); - if (p->pID != -1) - destroyConnection(p,+1); - - // remove from grid - if (idx < occnt[cellidx]-1) - { - grid[grdindex] = grid[cellidx*csize + occnt[cellidx]-1]; - grid[grdindex]->gridindex = grdindex; - } - occnt[cellidx]--; - - - - // remove from container - if (kID; - int move_ID = particles[pcnt-1].ID; - - *p = particles[pcnt-1]; // move very last particle to empty slot - particles[pcnt-1].ID = todel_ID; // keep IDs unique - grid[p->gridindex] = p; // keep gridindex consistent - - // permute address table - ID_2_address[todel_ID] = &(particles[pcnt-1]); - ID_2_address[move_ID] = p; - - } - pcnt--; - - - - } - - inline void computeNeighbors(pVector &R) - { - float xfrac = R.GetX()*mulx; - float yfrac = R.GetY()*muly; - float zfrac = R.GetZ()*mulz; - int xint = int(xfrac); - int yint = int(yfrac); - int zint = int(zfrac); - - int dx = -1; - if (xfrac-xint > 0.5) dx = 1; - if (xint <= 0) { xint = 0; dx = 1; } - if (xint >= nx-1) { xint = nx-1; dx = -1; } - - int dy = -1; - if (yfrac-yint > 0.5) dy = 1; - if (yint <= 0) {yint = 0; dy = 1; } - if (yint >= ny-1) {yint = ny-1; dy = -1;} - - int dz = -1; - if (zfrac-zint > 0.5) dz = 1; - if (zint <= 0) {zint = 0; dz = 1; } - if (zint >= nz-1) {zint = nz-1; dz = -1;} - - - nbtrack.cellidx[0] = xint + nx*(yint+zint*ny); - nbtrack.cellidx[1] = nbtrack.cellidx[0] + dx; - nbtrack.cellidx[2] = nbtrack.cellidx[1] + dy*nx; - nbtrack.cellidx[3] = nbtrack.cellidx[2] - dx; - nbtrack.cellidx[4] = nbtrack.cellidx[0] + dz*nx*ny; - nbtrack.cellidx[5] = nbtrack.cellidx[4] + dx; - nbtrack.cellidx[6] = nbtrack.cellidx[5] + dy*nx; - nbtrack.cellidx[7] = nbtrack.cellidx[6] - dx; - - - nbtrack.cellidx_c[0] = csize*nbtrack.cellidx[0]; - nbtrack.cellidx_c[1] = csize*nbtrack.cellidx[1]; - nbtrack.cellidx_c[2] = csize*nbtrack.cellidx[2]; - nbtrack.cellidx_c[3] = csize*nbtrack.cellidx[3]; - nbtrack.cellidx_c[4] = csize*nbtrack.cellidx[4]; - nbtrack.cellidx_c[5] = csize*nbtrack.cellidx[5]; - nbtrack.cellidx_c[6] = csize*nbtrack.cellidx[6]; - nbtrack.cellidx_c[7] = csize*nbtrack.cellidx[7]; - - - - nbtrack.cellcnt = 0; - nbtrack.pcnt = 0; - - } - - - - inline T *getNextNeighbor() - { - - if (nbtrack.pcnt < occnt[nbtrack.cellidx[nbtrack.cellcnt]]) - { - - return grid[nbtrack.cellidx_c[nbtrack.cellcnt] + (nbtrack.pcnt++)]; - - } - else - { - - for(;;) - { - nbtrack.cellcnt++; - if (nbtrack.cellcnt >= 8) - return 0; - if (occnt[nbtrack.cellidx[nbtrack.cellcnt]] > 0) - break; - } - - nbtrack.pcnt = 1; - return grid[nbtrack.cellidx_c[nbtrack.cellcnt]]; - } - } - - - inline void createConnection(T *P1,int ep1, T *P2, int ep2) - { - if (ep1 == -1) - P1->mID = P2->ID; - else - P1->pID = P2->ID; - - if (ep2 == -1) - P2->mID = P1->ID; - else - P2->pID = P1->ID; - - concnt++; - } - - inline void destroyConnection(T *P1,int ep1, T *P2, int ep2) - { - if (ep1 == -1) - P1->mID = -1; - else - P1->pID = -1; - - if (ep2 == -1) - P2->mID = -1; - else - P2->pID = -1; - concnt--; - } - - inline void destroyConnection(T *P1,int ep1) - { - - T *P2 = 0; - if (ep1 == 1) - { - P2 = ID_2_address[P1->pID]; - P1->pID = -1; - } - else - { - P2 = ID_2_address[P1->mID]; - P1->mID = -1; - } - if (P2->mID == P1->ID) - { - P2->mID = -1; - } - else - { - P2->pID = -1; - } - concnt--; - - } -}; - - - - -/* - - -struct Connection -{ - int lID; - int rID; -}; - - -class ConnectionContainer -{ - - //////////////// Container -public: - Connection *cons; // cons in linear array - int ccnt; // actual number of cons - -private: - - int capacity; // maximal number of particles - - - -public: - - - ConnectionContainer() - { - - //// involving the container - capacity = 0; - cons = 0; - ccnt = 0; - - } - - - - void allocate(int _capacity) - { - - //// involving the container - capacity = _capacity; - cons = (Connection*) malloc(sizeof(Connection)*capacity); - ccnt = 0; - - } - - ~ConnectionContainer() - { - if (cons != 0) - free(cons); - } - - - Connection* newConnection(int lid,int rid) - { - /////// get free place in container; - if (ccnt < capacity) - { - Connection *c = &(cons[ccnt]); - c->lID = lid; - c->rID = rid; - ccnt++; - return c; - } - return 0; - } - - inline void remove(int k) - { - Connection* c = &(cons[k]); - - // remove from container - if (k -#include "MersenneTwister.h" - -class RJMCMCBase -{ -public: - - ParticleGrid m_ParticleGrid; - float *m_QBallImageData; - const int *datasz; - EnergyComputerBase *enc; - int m_Iterations; - float width; - float height; - float depth; - double *voxsize; - int m_NumAttributes; - int m_AcceptedProposals; - - RJMCMCBase(float *points,int numPoints, float *dimg, const int *dsz, double *voxsize, double cellsize) - : m_QBallImageData(dimg) - , datasz(dsz) - , enc(0) - , width(dsz[1]*voxsize[0]) - , height(dsz[2]*voxsize[1]) - , depth(dsz[3]*voxsize[2]) - , voxsize(voxsize) - , m_NumAttributes(0) - , m_AcceptedProposals(0) - { - fprintf(stderr,"Data dimensions (mm) : %f x %f x %f\n",width,height,depth); - fprintf(stderr,"Data dimensions (voxel) : %i x %i x %i\n",datasz[1],datasz[2],datasz[3]); - fprintf(stderr,"voxel size (mm) : %lf x %lf x %lf\n",voxsize[0],voxsize[1],voxsize[2]); - - float cellcnt_x = (int)((float)width/cellsize) +1; - float cellcnt_y = (int)((float)height/cellsize) +1; - float cellcnt_z = (int)((float)depth/cellsize) +1; - //int cell_capacity = 2048; - //int cell_capacity = 64; - int cell_capacity = 512; - - fprintf(stderr,"grid dimensions : %f x %f x %f\n",cellcnt_x,cellcnt_y,cellcnt_z); - fprintf(stderr,"grid cell size (mm) : %f^3\n",cellsize); - fprintf(stderr,"cell capacity : %i\n",cell_capacity); - fprintf(stderr,"#cells*cellcap : %.1f K\n",cell_capacity*cellcnt_x*cellcnt_y*cellcnt_z/1000); - - int minsize = 1000000; - int err = m_ParticleGrid.allocate(((numPoints>minsize)? (numPoints+100000) : minsize), cellcnt_x, cellcnt_y, cellcnt_z, cellsize, cell_capacity); - - if (err == -1) - { - fprintf(stderr,"RJMCMCBase: out of Memory!\n"); - return; - } - - m_NumAttributes = 10; - for (int k = 0; k < numPoints; k++) - { - Particle *p = m_ParticleGrid.newParticle(pVector(points[m_NumAttributes*k], points[m_NumAttributes*k+1],points[m_NumAttributes*k+2])); - if (p!=0) - { - p->N = pVector(points[m_NumAttributes*k+3],points[m_NumAttributes*k+4],points[m_NumAttributes*k+5]); - p->cap = points[m_NumAttributes*k+6]; - p->len = points[m_NumAttributes*k+7]; - p->mID = (int) points[m_NumAttributes*k+8]; - p->pID = (int) points[m_NumAttributes*k+9]; - if (p->mID != -1) - m_ParticleGrid.concnt++; - if (p->pID != -1) - m_ParticleGrid.concnt++; - p->label = 0; - } - else - { - fprintf(stderr,"error: cannot allocate particle, con. indices will be wrong! \n"); - } - } - m_ParticleGrid.concnt /= 2; - - m_Iterations = 0; - m_AcceptedProposals = 0; - } - - ~RJMCMCBase() - { - - } - - void WriteOutParticles(float *npoints) - { - for (int k = 0; k < m_ParticleGrid.pcnt; k++) - { - Particle *p = &(m_ParticleGrid.particles[k]); - npoints[m_NumAttributes*k] = p->R.GetX(); - npoints[m_NumAttributes*k+1] = p->R.GetY(); - npoints[m_NumAttributes*k+2] = p->R.GetZ(); - npoints[m_NumAttributes*k+3] = p->N.GetX(); - npoints[m_NumAttributes*k+4] = p->N.GetY(); - npoints[m_NumAttributes*k+5] = p->N.GetZ(); - npoints[m_NumAttributes*k+6] = p->cap; - npoints[m_NumAttributes*k+7] = p->len; - npoints[m_NumAttributes*k+8] = m_ParticleGrid.ID_2_index(p->mID); - npoints[m_NumAttributes*k+9] = m_ParticleGrid.ID_2_index(p->pID); - } - } - - void SetEnergyComputer(EnergyComputerBase *e) - { - enc = e; - } - - void Iterate(float* acceptance, unsigned long* numCon, unsigned long* numPart, bool *abort) - { - m_AcceptedProposals = 0; - for (int it = 0; it < m_Iterations;it++) - { - if (*abort) - break; - - IterateOneStep(); - - *numCon = m_ParticleGrid.concnt; - *numPart = m_ParticleGrid.pcnt; - } - *acceptance = (float)m_AcceptedProposals/m_Iterations; - } - - virtual void IterateOneStep() - { - - } -}; - - - diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/RJMCMC_randshift.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/RJMCMC_randshift.cpp deleted file mode 100644 index 0f15ef7675..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/RJMCMC_randshift.cpp +++ /dev/null @@ -1,635 +0,0 @@ -/*=================================================================== - -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 "ParticleGrid.cpp" -#include "RJMCMCBase.cpp" -#include - -class RJMCMC : public RJMCMCBase -{ -public: - - float T_in ; - float T_ex ; - float dens; - - - float p_birth; - float p_death; - float p_shift; - float p_shiftopt; - float p_cap; - float p_con; - - - - float sigma_g; - float gamma_g; - float Z_g; - - float dthres; - float nthres; - float T_prop; - float stopprobability; - float del_prob; - - - - float len_def; - float len_sig; - - float cap_def; - float cap_sig; - - float externalEnergy; - float internalEnergy; - - float m_ChempotParticle; - - - Track TrackProposal, TrackBackup; - - - SimpSamp simpsamp; - - - RJMCMC(float *points,int numPoints, float *dimg, const int *dsz, double *voxsz, double cellsz) : RJMCMCBase(points,numPoints,dimg,dsz,voxsz,cellsz) - { - externalEnergy = 0; - internalEnergy = 0; - } - - void SetParameters(float Temp, int numit, float plen, float curv_hardthres, float chempot_particle) - { - m_Iterations = numit; - - p_birth = 0.25; - p_death = 0.05; - p_shift = 0.15; - p_shiftopt = 0.1; - p_con = 0.45; - p_cap = 0.0; - - m_ChempotParticle = chempot_particle; - - float sum = p_birth+p_death+p_shift+p_shiftopt+p_con; - p_birth /= sum; p_death /= sum; p_shift /= sum; p_shiftopt /= sum; - - T_in = Temp; - T_ex = 0.01; - dens = exp(-chempot_particle/T_in); - - len_def = plen; - len_sig = 0.0; - cap_def = 1.0; - cap_sig = 0.0; - - // shift proposal - sigma_g = len_def/8.0; - gamma_g = 1/(sigma_g*sigma_g*2); - Z_g = pow(2*PI*sigma_g,3.0/2.0)*(PI*sigma_g/len_def); - - // conn proposal - dthres = len_def; - nthres = curv_hardthres; - T_prop = 0.5; - dthres *= dthres; - stopprobability = exp(-1/T_prop); - del_prob = 0.1; - } - - void SetTemperature(float temp) - { - T_in = temp; - dens = exp(-m_ChempotParticle/T_in); - } - - void IterateOneStep() - { - float randnum = mtrand.frand(); - //randnum = 0; - - /////////////////////////////////////////////////////////////// - //////// Birth Proposal - /////////////////////////////////////////////////////////////// - if (randnum < p_birth) - { - -#ifdef TIMING - tic(&birthproposal_time); - birthstats.propose(); -#endif - - pVector R; - enc->drawSpatPosition(&R); - - //fprintf(stderr,"drawn: %f, %f, %f\n",R[0],R[1],R[2]); - //R.setXYZ(20.5*3, 35.5*3, 1.5*3); - - pVector N; N.rand_sphere(); - //N.setXYZ(1,0,0); - float cap = cap_def - cap_sig*mtrand.frand(); - float len = len_def;// + len_sig*(mtrand.frand()-0.5); - Particle prop; - prop.R = R; - prop.N = N; - prop.cap = cap; - prop.len = len; - - - float prob = dens * p_death /((p_birth)*(m_ParticleGrid.pcnt+1)); - - float ex_energy = enc->computeExternalEnergy(R,N,cap,len,0); - float in_energy = enc->computeInternalEnergy(&prop); - - prob *= exp((in_energy/T_in+ex_energy/T_ex)) ; - - if (prob > 1 || mtrand.frand() < prob) - { - Particle *p = m_ParticleGrid.newParticle(R); - if (p!=0) - { - p->R = R; - p->N = N; - p->cap = cap; - p->len = len; -#ifdef TIMING - birthstats.accepted(); -#endif - m_AcceptedProposals++; - } - } - -#ifdef TIMING - toc(&birthproposal_time); -#endif - } - /////////////////////////////////////////////////////////////// - //////// Death Proposal - /////////////////////////////////////////////////////////////// - else if (randnum < p_birth+p_death) - { - if (m_ParticleGrid.pcnt > 0) - { -#ifdef TIMING - tic(&deathproposal_time); - deathstats.propose(); -#endif - - int pnum = rand()%m_ParticleGrid.pcnt; - Particle *dp = &(m_ParticleGrid.particles[pnum]); - if (dp->pID == -1 && dp->mID == -1) - { - - float ex_energy = enc->computeExternalEnergy(dp->R,dp->N,dp->cap,dp->len,dp); - float in_energy = enc->computeInternalEnergy(dp); - - float prob = m_ParticleGrid.pcnt * (p_birth) /(dens*p_death); //*SpatProb(dp->R); - prob *= exp(-(in_energy/T_in+ex_energy/T_ex)) ; - if (prob > 1 || mtrand.frand() < prob) - { - m_ParticleGrid.remove(pnum); -#ifdef TIMING - deathstats.accepted(); -#endif - m_AcceptedProposals++; - } - } -#ifdef TIMING - toc(&deathproposal_time); -#endif - } - - } - /////////////////////////////////////////////////////////////// - //////// Cap change Proposal - /////////////////////////////////////////////////////////////// - else if (randnum < p_birth+p_death+p_cap) - { - if (m_ParticleGrid.pcnt > 0) - { - - int pnum = rand()%m_ParticleGrid.pcnt; - Particle *p = &(m_ParticleGrid.particles[pnum]); - Particle prop_p = *p; - - prop_p.cap = cap_def - cap_sig*mtrand.frand(); - - float ex_energy = enc->computeExternalEnergy(prop_p.R,prop_p.N,prop_p.cap,p->len,p) - - enc->computeExternalEnergy(p->R,p->N,p->cap,p->len,p); - //float in_energy = enc->computeExternalEnergy(prop_p.R,prop_p.N,p->cap,p->len,p) - // - enc->computeExternalEnergy(p->R,p->N,p->cap,p->len,p); - float prob = exp(ex_energy/T_ex); - // * SpatProb(p->R) / SpatProb(prop_p.R); - if (mtrand.frand() < prob) - { - p->cap = prop_p.cap; - m_AcceptedProposals++; - } - - } - - } - - /////////////////////////////////////////////////////////////// - //////// Shift Proposal - /////////////////////////////////////////////////////////////// - else if (randnum < p_birth+p_death+p_shift+p_cap) - { - float energy = 0; - if (m_ParticleGrid.pcnt > 0) - { -#ifdef TIMING - tic(&shiftproposal_time); - shiftstats.propose(); -#endif - - int pnum = rand()%m_ParticleGrid.pcnt; - Particle *p = &(m_ParticleGrid.particles[pnum]); - Particle prop_p = *p; - - prop_p.R.distortn(sigma_g); - prop_p.N.distortn(sigma_g/(2*p->len)); - prop_p.N.normalize(); - - - float ex_energy = enc->computeExternalEnergy(prop_p.R,prop_p.N,p->cap,p->len,p) - - enc->computeExternalEnergy(p->R,p->N,p->cap,p->len,p); - float in_energy = enc->computeInternalEnergy(&prop_p) - enc->computeInternalEnergy(p); - - float prob = exp(ex_energy/T_ex+in_energy/T_in); - // * SpatProb(p->R) / SpatProb(prop_p.R); - if (mtrand.frand() < prob) - { - pVector Rtmp = p->R; - pVector Ntmp = p->N; - p->R = prop_p.R; - p->N = prop_p.N; - if (!m_ParticleGrid.tryUpdateGrid(pnum)) - { - p->R = Rtmp; - p->N = Ntmp; - } -#ifdef TIMING - shiftstats.accepted(); -#endif - m_AcceptedProposals++; - } - -#ifdef TIMING - toc(&shiftproposal_time); -#endif - - } - - } - else if (randnum < p_birth+p_death+p_shift+p_shiftopt+p_cap) - { - float energy = 0; - if (m_ParticleGrid.pcnt > 0) - { - - int pnum = rand()%m_ParticleGrid.pcnt; - Particle *p = &(m_ParticleGrid.particles[pnum]); - - bool no_proposal = false; - Particle prop_p = *p; - if (p->pID != -1 && p->mID != -1) - { - Particle *plus = m_ParticleGrid.ID_2_address[p->pID]; - int ep_plus = (plus->pID == p->ID)? 1 : -1; - Particle *minus = m_ParticleGrid.ID_2_address[p->mID]; - int ep_minus = (minus->pID == p->ID)? 1 : -1; - prop_p.R = (plus->R + plus->N * (plus->len * ep_plus) + minus->R + minus->N * (minus->len * ep_minus))*0.5; - prop_p.N = plus->R - minus->R; - prop_p.N.normalize(); - } - else if (p->pID != -1) - { - Particle *plus = m_ParticleGrid.ID_2_address[p->pID]; - int ep_plus = (plus->pID == p->ID)? 1 : -1; - prop_p.R = plus->R + plus->N * (plus->len * ep_plus * 2); - prop_p.N = plus->N; - } - else if (p->mID != -1) - { - Particle *minus = m_ParticleGrid.ID_2_address[p->mID]; - int ep_minus = (minus->pID == p->ID)? 1 : -1; - prop_p.R = minus->R + minus->N * (minus->len * ep_minus * 2); - prop_p.N = minus->N; - } - else - no_proposal = true; - - if (!no_proposal) - { - float cos = prop_p.N*p->N; - float p_rev = exp(-((prop_p.R-p->R).norm_square() + (1-cos*cos))*gamma_g)/Z_g; - - float ex_energy = enc->computeExternalEnergy(prop_p.R,prop_p.N,p->cap,p->len,p) - - enc->computeExternalEnergy(p->R,p->N,p->cap,p->len,p); - float in_energy = enc->computeInternalEnergy(&prop_p) - enc->computeInternalEnergy(p); - - float prob = exp(ex_energy/T_ex+in_energy/T_in)*p_shift*p_rev/(p_shiftopt+p_shift*p_rev); - //* SpatProb(p->R) / SpatProb(prop_p.R); - - if (mtrand.frand() < prob) - { - pVector Rtmp = p->R; - pVector Ntmp = p->N; - p->R = prop_p.R; - p->N = prop_p.N; - if (!m_ParticleGrid.tryUpdateGrid(pnum)) - { - p->R = Rtmp; - p->N = Ntmp; - } - m_AcceptedProposals++; - } - } - } - - } - else - { - - - if (m_ParticleGrid.pcnt > 0) - { - -#ifdef TIMING - tic(&connproposal_time); - connstats.propose(); -#endif - - int pnum = rand()%m_ParticleGrid.pcnt; - Particle *p = &(m_ParticleGrid.particles[pnum]); - - EndPoint P; - P.p = p; - P.ep = (mtrand.frand() > 0.5)? 1 : -1; - - RemoveAndSaveTrack(P); - if (TrackBackup.proposal_probability != 0) - { - MakeTrackProposal(P); - - float prob = (TrackProposal.energy-TrackBackup.energy)/T_in ; - - // prob = exp(prob)*(TrackBackup.proposal_probability) - // /(TrackProposal.proposal_probability); - prob = exp(prob)*(TrackBackup.proposal_probability * pow(del_prob,TrackProposal.length)) - /(TrackProposal.proposal_probability * pow(del_prob,TrackBackup.length)); - if (mtrand.frand() < prob) - { - ImplementTrack(TrackProposal); -#ifdef TIMING - connstats.accepted(); -#endif - m_AcceptedProposals++; - } - else - { - ImplementTrack(TrackBackup); - } - } - else - ImplementTrack(TrackBackup); - -#ifdef TIMING - toc(&connproposal_time); -#endif - } - } - } - - - void ImplementTrack(Track &T) - { - for (int k = 1; k < T.length;k++) - { - m_ParticleGrid.createConnection(T.track[k-1].p,T.track[k-1].ep,T.track[k].p,-T.track[k].ep); - } - } - - - - void RemoveAndSaveTrack(EndPoint P) - { - EndPoint Current = P; - - int cnt = 0; - float energy = 0; - float AccumProb = 1.0; - TrackBackup.track[cnt] = Current; - - EndPoint Next; - - - - for (;;) - { - Next.p = 0; - if (Current.ep == 1) - { - if (Current.p->pID != -1) - { - Next.p = m_ParticleGrid.ID_2_address[Current.p->pID]; - Current.p->pID = -1; - m_ParticleGrid.concnt--; - } - } - else if (Current.ep == -1) - { - if (Current.p->mID != -1) - { - Next.p = m_ParticleGrid.ID_2_address[Current.p->mID]; - Current.p->mID = -1; - m_ParticleGrid.concnt--; - } - } - else - { fprintf(stderr,"RJMCMC_randshift: Connection inconsistent 3\n"); break; } - - if (Next.p == 0) // no successor - { - Next.ep = 0; // mark as empty successor - break; - } - else - { - if (Next.p->pID == Current.p->ID) - { - Next.p->pID = -1; - Next.ep = 1; - } - else if (Next.p->mID == Current.p->ID) - { - Next.p->mID = -1; - Next.ep = -1; - } - else - { fprintf(stderr,"RJMCMC_randshift: Connection inconsistent 4\n"); break; } - } - - - ComputeEndPointProposalDistribution(Current); - - AccumProb *= (simpsamp.probFor(Next)); - - if (Next.p == 0) // no successor -> break - break; - - energy += enc->computeInternalEnergyConnection(Current.p,Current.ep,Next.p,Next.ep); - - Current = Next; - Current.ep *= -1; - cnt++; - TrackBackup.track[cnt] = Current; - - - if (mtrand.rand() > del_prob) - { - break; - } - - } - TrackBackup.energy = energy; - TrackBackup.proposal_probability = AccumProb; - TrackBackup.length = cnt+1; - - } - - - - void MakeTrackProposal(EndPoint P) - { - EndPoint Current = P; - int cnt = 0; - float energy = 0; - float AccumProb = 1.0; - TrackProposal.track[cnt++] = Current; - Current.p->label = 1; - - for (;;) - { - - // next candidate is already connected - if ((Current.ep == 1 && Current.p->pID != -1) || (Current.ep == -1 && Current.p->mID != -1)) - break; - - // track too long - if (cnt > 250) - break; - - ComputeEndPointProposalDistribution(Current); - - // // no candidates anymore - // if (simpsamp.isempty()) - // break; - - int k = simpsamp.draw(); - - // stop tracking proposed - if (k==0) - break; - - EndPoint Next = simpsamp.objs[k]; - float probability = simpsamp.probFor(k); - - // accumulate energy and proposal distribution - energy += enc->computeInternalEnergyConnection(Current.p,Current.ep,Next.p,Next.ep); - AccumProb *= probability; - - // track to next endpoint - Current = Next; - Current.ep *= -1; - - Current.p->label = 1; // put label to avoid loops - TrackProposal.track[cnt++] = Current; - - - - } - - TrackProposal.energy = energy; - TrackProposal.proposal_probability = AccumProb; - TrackProposal.length = cnt; - - // clear labels - for (int j = 0; j < TrackProposal.length;j++) - { - TrackProposal.track[j].p->label = 0; - } - - } - - - - - void ComputeEndPointProposalDistribution(EndPoint P) - { - Particle *p = P.p; - int ep = P.ep; - - float dist,dot; - pVector R = p->R + (p->N * ep*p->len); - m_ParticleGrid.computeNeighbors(R); - simpsamp.clear(); - - simpsamp.add(stopprobability,EndPoint(0,0)); - - for (;;) - { - Particle *p2 = m_ParticleGrid.getNextNeighbor(); - if (p2 == 0) break; - if (p!=p2 && p2->label == 0) - { - if (p2->mID == -1) - { - dist = (p2->R - p2->N * p2->len - R).norm_square(); - if (dist < dthres) - { - dot = p2->N*p->N * ep; - if (dot > nthres) - { - float en = enc->computeInternalEnergyConnection(p,ep,p2,-1); - simpsamp.add(exp(en/T_prop),EndPoint(p2,-1)); - } - } - } - if (p2->pID == -1) - { - dist = (p2->R + p2->N * p2->len - R).norm_square(); - if (dist < dthres) - { - dot = p2->N*p->N * (-ep); - if (dot > nthres) - { - float en = enc->computeInternalEnergyConnection(p,ep,p2,+1); - simpsamp.add(exp(en/T_prop),EndPoint(p2,+1)); - } - } - } - } - } - } - - -}; - - - diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/SphereInterpolator.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/SphereInterpolator.cpp deleted file mode 100644 index f13bfbd18b..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/SphereInterpolator.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/*=================================================================== - -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 "auxilary_classes.cpp" - - -class SphereInterpolator -{ -public: - float *barycoords; - int *indices; - int size; // size of LUT - int sN; // (sizeofLUT-1)/2 - int nverts; // number of data vertices - - - float beta; - float inva; - float b; - - - int *idx; - float *interpw; - - SphereInterpolator(float *barycoords, int *indices, int numverts, int sizeLUT, float beta) - { - this->barycoords = barycoords; - this->indices = indices; - this->size = sizeLUT; - this->sN = (sizeLUT-1)/2; - this->nverts = numverts; - this->beta = beta; - - inva = (sqrt(1+beta)-sqrt(beta)); - b = 1/(1-sqrt(1/beta + 1)); - - } - - - inline void getInterpolation(vnl_vector_fixed N) - { - float nx = N[0]; - float ny = N[1]; - float nz = N[2]; - - if (nz > 0.5) - { - int x = float2int(nx); - int y = float2int(ny); - int i = 3*6*(x+y*size); // (:,1,x,y) - idx = indices+i; - interpw = barycoords +i; - return; - } - if (nz < -0.5) - { - int x = float2int(nx); - int y = float2int(ny); - int i = 3*(1+6*(x+y*size)); // (:,2,x,y) - idx = indices+i; - interpw = barycoords +i; - return; - } - if (nx > 0.5) - { - int z = float2int(nz); - int y = float2int(ny); - int i = 3*(2+6*(z+y*size)); // (:,2,x,y) - idx = indices+i; - interpw = barycoords +i; - return; - } - if (nx < -0.5) - { - int z = float2int(nz); - int y = float2int(ny); - int i = 3*(3+6*(z+y*size)); // (:,2,x,y) - idx = indices+i; - interpw = barycoords +i; - return; - } - if (ny > 0) - { - int x = float2int(nx); - int z = float2int(nz); - int i = 3*(4+6*(x+z*size)); // (:,1,x,y) - idx = indices+i; - interpw = barycoords +i; - return; - } - else - { - int x = float2int(nx); - int z = float2int(nz); - int i = 3*(5+6*(x+z*size)); // (:,1,x,y) - idx = indices+i; - interpw = barycoords +i; - return; - } - - } - - - inline float invrescale(float f) - { - float x = (fabs(f)-b)*inva; - if (f>0) - return (x*x-beta); - else - return beta - x*x; - } - - inline int float2int(float x) - { - return int((invrescale(x)+1)*sN-0.5); - - } - - -}; - - - diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/auxilary_classes.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/auxilary_classes.cpp deleted file mode 100644 index 297649be37..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/auxilary_classes.cpp +++ /dev/null @@ -1,658 +0,0 @@ -/*=================================================================== - -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 _AUXCLASS -#define _AUXCLASS - -#ifndef INFINITY -#define INFINITY 1000000000 -#endif - -//#define INLINE __attribute__ ((always_inline)) -#define INLINE inline - -//#define __SSE2 - - - -#ifdef __SSE2 -#include - -class pVector -{ - -private: - __m128 r; - -public: - - static float store[4]; - - pVector() - { - - } - - pVector(__m128 q) - { - r = q; - } - - - pVector(float x,float y,float z) - { - r = _mm_set_ps(0,z,y,x); - } - - - INLINE void storeXYZ() - { - _mm_store_ps(store,r); - } - - INLINE void setXYZ(float sx,float sy,float sz) - { - r = _mm_set_ps(0,sz,sy,sx); - } - - - - - INLINE void rand(float w,float h,float d) - { - float x = mtrand.frand()*w; - float y = mtrand.frand()*h; - float z = mtrand.frand()*d; - r = _mm_set_ps(0,z,y,x); - - } - - INLINE void rand_sphere() - { - r = _mm_set_ps(0,mtrand.frandn(),mtrand.frandn(),mtrand.frandn()); - normalize(); - } - - INLINE void normalize() - { - __m128 q = _mm_mul_ps(r,r); - _mm_store_ps(store,q); - float norm = sqrt(store[0]+store[1]+store[2]) + 0.00000001; - q = _mm_set_ps1(1/norm); - r = _mm_mul_ps(r,q); - - } - - INLINE float norm_square() - { - __m128 q = _mm_mul_ps(r,r); - _mm_store_ps(store,q); - return store[0]+store[1]+store[2]; - } - - INLINE void distortn(float sigma) - { - __m128 s = _mm_set_ps(0,mtrand.frandn(),mtrand.frandn(),mtrand.frandn()); - __m128 q = _mm_set_ps1(sigma); - r = _mm_add_ps(r,_mm_mul_ps(q,s)); - } - - INLINE pVector operator*(float s) - { - __m128 q = _mm_set_ps1(s); - return pVector(_mm_mul_ps(q,r)); - } - - INLINE void operator*=(float &s) - { - __m128 q = _mm_set_ps1(s); - r = _mm_mul_ps(q,r); - } - - INLINE pVector operator+(pVector R) - { - return pVector(_mm_add_ps(R.r,r)); - } - - INLINE void operator+=(pVector R) - { - r = _mm_add_ps(r,R.r); - } - - INLINE pVector operator-(pVector R) - { - return pVector(_mm_sub_ps(r,R.r)); - } - - INLINE void operator-=(pVector R) - { - r = _mm_sub_ps(r,R.r); - } - - INLINE pVector operator/(float &s) - { - __m128 q = _mm_set_ps1(s); - return pVector(_mm_div_ps(q,r)); - } - - INLINE void operator/=(float &s) - { - __m128 q = _mm_set_ps1(s); - r = _mm_div_ps(r,q); - } - - INLINE float operator*(pVector R) - { - __m128 q = _mm_mul_ps(r,R.r); - _mm_store_ps(store,q); - return store[0]+store[1]+store[2]; - } - - -}; - -float pVector::store[4]; - -#else - -class pVector -{ -private: - float x; - float y; - float z; - -public: - - pVector() - { - - } - - pVector(float x,float y,float z) - { - this->x = x; - this->y = y; - this->z = z; - } - - INLINE void SetXYZ(float sx,float sy, float sz) - { - x = sx; - y = sy; - z = sz; - } - - INLINE void GetXYZ(float *xyz) - { - xyz[0] = x; - xyz[1] = y; - xyz[2] = z; - } - - INLINE float GetX() - { - return x; - } - - INLINE float GetY() - { - return y; - } - - INLINE float GetZ() - { - return z; - } - - INLINE void rand(float w, float h, float d) - { - this->x = mtrand.frand()*w; - this->y = mtrand.frand()*h; - this->z = mtrand.frand()*d; - } - - INLINE void rand_sphere() - { - this->x = mtrand.frandn(); - this->y = mtrand.frandn(); - this->z = mtrand.frandn(); - normalize(); - } - - INLINE void normalize() - { - float norm = sqrt(x*x+y*y+z*z)+ 0.00000001; - *this /= norm; - } - - INLINE float norm_square() - { - return x*x + y*y + z*z; - } - - INLINE void distortn(float sigma) - { - x += sigma*mtrand.frandn(); - y += sigma*mtrand.frandn(); - z += sigma*mtrand.frandn(); - } - - INLINE float operator[](int index) - { - switch(index) - { - case 0: - return x; - case 1: - return y; - case 2: - return z; - default: - return 0.0f; - } - } - - INLINE pVector operator*(float s) - { - return pVector(s*x,s*y,s*z); - } - - INLINE void operator*=(float &s) - { - x *= s; - y *= s; - z *= s; - } - - INLINE pVector operator+(pVector R) - { - return pVector(x+R.x,y+R.y,z+R.z); - } - - INLINE void operator+=(pVector R) - { - x += R.x; - y += R.y; - z += R.z; - } - - INLINE pVector operator-(pVector R) - { - return pVector(x-R.x,y-R.y,z-R.z); - } - - INLINE void operator-=(pVector R) - { - x -= R.x; - y -= R.y; - z -= R.z; - } - - INLINE pVector operator/(float &s) - { - return pVector(x/s,y/s,z/s); - } - - INLINE void operator/=(float &s) - { - x /= s; - y /= s; - z /= s; - } - - - INLINE float operator*(pVector R) - { - return x*R.x+y*R.y+z*R.z; - } -}; - -#endif - - -class Particle -{ -public: - - Particle() - { - label = 0; - pID = -1; - mID = -1; - inserted = false; - } - - ~Particle() - { - } - - pVector R; - pVector N; - float cap; - float len; - - int gridindex; // index in the grid where it is living - int ID; - int pID; - int mID; - - int label; - int numerator; - bool inserted; -}; - - -class EnergyGradient -{ -public: - pVector gR; - pVector gN; - - INLINE float norm2() - { - return gR.norm_square() + gN.norm_square(); - } -} ; - - -template -class SimpSamp -{ - - float *P; - int cnt; - -public: - T *objs; - - - SimpSamp() - { - P = (float*) malloc(sizeof(float)*1000); - objs = (T*) malloc(sizeof(T)*1000); - } - ~SimpSamp() - { - free(P); - free(objs); - } - - INLINE void clear() - { - cnt = 1; - P[0] = 0; - } - - INLINE void add(float p, T obj) - { - P[cnt] = P[cnt-1] + p; - objs[cnt-1] = obj; - cnt++; - } - - // INLINE int draw() - // { - // float r = mtrand.frand()*P[cnt-1]; - // for (int i = 1; i < cnt; i++) - // { - // if (r <= P[i]) - // return i-1; - // } - // return cnt-2; - // } - - INLINE int draw() - { - float r = mtrand.frand()*P[cnt-1]; - int j; - int rl = 1; - int rh = cnt-1; - while(rh != rl) - { - j = rl + (rh-rl)/2; - if (r < P[j]) - { - rh = j; - continue; - } - if (r > P[j]) - { - rl = j+1; - continue; - } - break; - } - return rh-1; - } - - - - - - INLINE T drawObj() - { - return objs[draw()]; - } - - INLINE bool isempty() - { - if (cnt == 1) - return true; - else - return false; - } - - - float probFor(int idx) - { - return (P[idx+1]-P[idx])/P[cnt-1]; - } - - float probFor(T &t) - { - for (int i = 1; i< cnt;i++) - { - if (t == objs[i-1]) - return probFor(i-1); - } - return 0; - } - - - -}; - - -class EndPoint -{ -public: - EndPoint() - {} - - EndPoint(Particle *p,int ep) - { - this->p = p; - this->ep = ep; - } - Particle *p; - int ep; - - inline bool operator==(EndPoint P) - { - return (P.p == p) && (P.ep == ep); - } -}; - -class Track -{ -public: - EndPoint track[1000]; - float energy; - float proposal_probability; - int length; - - void clear() - { - length = 0; - energy = 0; - proposal_probability = 1; - } - - - bool isequal(Track &t) - { - for (int i = 0; i < length;i++) - { - if (track[i].p != t.track[i].p || track[i].ep != t.track[i].ep) - return false; - } - return true; - } - -}; - -float getMax(float *arr, int cnt) -{ - float max = arr[0]; - for (int i = 1; i < cnt; i++) - { - if (arr[i] > max) - max = arr[i]; - } - return max; -} - - - -float getMin(float *arr, int cnt) -{ - float min = arr[0]; - for (int i = 1; i < cnt; i++) - { - if (arr[i] < min) - min = arr[i]; - } - return min; -} - - -int getArgMin(float *arr, int cnt) -{ - float min = arr[0]; - int idx = 0; - for (int i = 1; i < cnt; i++) - { - if (arr[i] < min) - { - min = arr[i]; - idx = i; - } - } - return idx; -} - - - - -inline float distLseg(pVector &R1,pVector &N1,pVector &R2,pVector &N2,float &len) -{ - - pVector D = R1-R2; - float beta = N1*N2; - float divisor = 1.001-beta*beta; - float gamma1 = N1*D; - float gamma2 = N2*D; - float t,u; - float EPdist[4]; - - pVector Q; - float dist = 102400000.0; - - while(true) - { - - t = -(gamma1+beta*gamma2) / divisor; - u = (gamma1*beta+gamma2) / divisor; - if (fabs(t) < len && fabs(u) < len) - { - Q = D + N1*t - N2*u; - dist = Q*Q; - break; - } - - beta = len*beta; - - t = beta - gamma1; - if (fabs(t) < len) - { - Q = D + N1*t - N2*len; - float d = Q*Q; - if (d < dist) dist = d; - } - - t = -beta - gamma1; - if (fabs(t) < len) - { - Q = D + N1*t + N2*len; - float d = Q*Q; - if (d < dist) dist = d; - } - - u = beta + gamma2; - if (fabs(u) < len) - { - Q = D + N1*len - N2*u; - float d = Q*Q; - if (d < dist) dist = d; - } - - u = -beta + gamma2; - if (fabs(u) < len) - { - Q = D - N1*len - N2*u; - float d = Q*Q; - if (d < dist) dist = d; - } - - if (dist != 102400000.0) - break; - - - EPdist[0] = beta + gamma1 - gamma2; - EPdist[1] = -beta + gamma1 + gamma2; - EPdist[2] = -beta - gamma1 - gamma2; - EPdist[3] = beta - gamma1 + gamma2; - int c = getArgMin(EPdist,4); - if (c==0) {t = +len; u = +len; } - if (c==1) {t = +len; u = -len; } - if (c==2) {t = -len; u = +len; } - if (c==3) {t = -len; u = -len; } - Q = D + N1*t - N2*u; - dist = Q*Q; - break; - - } - - - return dist; - -} - - -#endif - - diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkEnergyComputer.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkEnergyComputer.cpp new file mode 100644 index 0000000000..b0e835ebf5 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkEnergyComputer.cpp @@ -0,0 +1,460 @@ +/*=================================================================== + +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 "mitkEnergyComputer.h" +#include +#include + +using namespace mitk; + +EnergyComputer::EnergyComputer(ItkQBallImgType* qballImage, ItkFloatImageType* mask, ParticleGrid* particleGrid, SphereInterpolator* interpolator, ItkRandGenType* randGen) + : m_UseTrilinearInterpolation(true) +{ + m_ParticleGrid = particleGrid; + m_RandGen = randGen; + m_Image = qballImage; + m_SphereInterpolator = interpolator; + m_Mask = mask; + + m_ParticleLength = m_ParticleGrid->m_ParticleLength; + m_SquaredParticleLength = m_ParticleLength*m_ParticleLength; + + m_Size[0] = m_Image->GetLargestPossibleRegion().GetSize()[0]; + m_Size[1] = m_Image->GetLargestPossibleRegion().GetSize()[1]; + m_Size[2] = m_Image->GetLargestPossibleRegion().GetSize()[2]; + + if (m_Size[0]<3 || m_Size[1]<3 || m_Size[2]<3) + m_UseTrilinearInterpolation = false; + + m_Spacing[0] = m_Image->GetSpacing()[0]; + m_Spacing[1] = m_Image->GetSpacing()[1]; + m_Spacing[2] = m_Image->GetSpacing()[2]; + + // calculate rotation matrix + vnl_matrix temp = m_Image->GetDirection().GetVnlMatrix(); + vnl_matrix directionMatrix; directionMatrix.set_size(3,3); + vnl_copy(temp, directionMatrix); + vnl_vector_fixed d0 = directionMatrix.get_column(0); d0.normalize(); + vnl_vector_fixed d1 = directionMatrix.get_column(1); d1.normalize(); + vnl_vector_fixed d2 = directionMatrix.get_column(2); d2.normalize(); + directionMatrix.set_column(0, d0); + directionMatrix.set_column(1, d1); + directionMatrix.set_column(2, d2); + vnl_matrix_fixed I = directionMatrix*directionMatrix.transpose(); + if(!I.is_identity(mitk::eps)) + fprintf(stderr,"itkGibbsTrackingFilter: image direction is not a rotation matrix. Tracking not possible!\n"); + m_RotationMatrix = directionMatrix; + + if (QBALL_ODFSIZE != m_SphereInterpolator->nverts) + fprintf(stderr,"EnergyComputer: error during init: data does not match with interpolation scheme\n"); + + int totsz = m_Size[0]*m_Size[1]*m_Size[2]; + m_CumulatedSpatialProbability.resize(totsz, 0.0); // +1? + m_ActiveIndices.resize(totsz, 0); + + // calculate active voxels and cumulate probabilities + m_NumActiveVoxels = 0; + m_CumulatedSpatialProbability[0] = 0; + for (int x = 0; x < m_Size[0];x++) + for (int y = 0; y < m_Size[1];y++) + for (int z = 0; z < m_Size[2];z++) + { + int idx = x+(y+z*m_Size[1])*m_Size[0]; + ItkFloatImageType::IndexType index; + index[0] = x; index[1] = y; index[2] = z; + if (m_Mask->GetPixel(index) > 0.5) + { + m_CumulatedSpatialProbability[m_NumActiveVoxels+1] = m_CumulatedSpatialProbability[m_NumActiveVoxels] + m_Mask->GetPixel(index); + m_ActiveIndices[m_NumActiveVoxels] = idx; + m_NumActiveVoxels++; + } + } + for (int k = 0; k < m_NumActiveVoxels; k++) + m_CumulatedSpatialProbability[k] /= m_CumulatedSpatialProbability[m_NumActiveVoxels]; + + std::cout << "EnergyComputer: " << m_NumActiveVoxels << " active voxels found" << std::endl; +} + +void EnergyComputer::SetParameters(float particleWeight, float particleWidth, float connectionPotential, float curvThres, float inexBalance, float particlePotential) +{ + m_ParticleChemicalPotential = particlePotential; + m_ConnectionPotential = connectionPotential; + m_ParticleWeight = particleWeight; + + float bal = 1/(1+exp(-inexBalance)); + m_ExtStrength = 2*bal; + m_IntStrength = 2*(1-bal)/m_SquaredParticleLength; + + m_CurvatureThreshold = curvThres; + + float sigma_s = particleWidth; + gamma_s = 1/(sigma_s*sigma_s); + gamma_reg_s =1/(m_SquaredParticleLength/4); +} + +// draw random position from active voxels +void EnergyComputer::DrawRandomPosition(vnl_vector_fixed& R) +{ + float r = m_RandGen->GetVariate();//m_RandGen->frand(); + int j; + int rl = 1; + int rh = m_NumActiveVoxels; + while(rh != rl) + { + j = rl + (rh-rl)/2; + if (r < m_CumulatedSpatialProbability[j]) + { + rh = j; + continue; + } + if (r > m_CumulatedSpatialProbability[j]) + { + rl = j+1; + continue; + } + break; + } + R[0] = m_Spacing[0]*((float)(m_ActiveIndices[rh-1] % m_Size[0]) + m_RandGen->GetVariate()); + R[1] = m_Spacing[1]*((float)((m_ActiveIndices[rh-1]/m_Size[0]) % m_Size[1]) + m_RandGen->GetVariate()); + R[2] = m_Spacing[2]*((float)(m_ActiveIndices[rh-1]/(m_Size[0]*m_Size[1])) + m_RandGen->GetVariate()); +} + +// return spatial probability of position +float EnergyComputer::SpatProb(vnl_vector_fixed pos) +{ + ItkFloatImageType::IndexType index; + index[0] = floor(pos[0]/m_Spacing[0]); + index[1] = floor(pos[1]/m_Spacing[1]); + index[2] = floor(pos[2]/m_Spacing[2]); + + if (m_Mask->GetLargestPossibleRegion().IsInside(index)) // is inside image? + return m_Mask->GetPixel(index); + else + return 0; +} + +float EnergyComputer::EvaluateOdf(vnl_vector_fixed& pos, vnl_vector_fixed dir) +{ + const int sampleSteps = 10; // evaluate ODF at 2*sampleSteps+1 positions along dir + vnl_vector_fixed samplePos; // current position to evaluate + float result = 0; // average of sampled ODF values + int xint, yint, zint; // voxel containing samplePos + + // rotate particle direction according to image rotation + dir = m_RotationMatrix*dir; + + // get interpolation for rotated direction + m_SphereInterpolator->getInterpolation(dir); + + // sample ODF values along particle direction + for (int i=-sampleSteps; i <= sampleSteps;i++) + { + samplePos = pos + (dir * m_ParticleLength) * ((float)i/sampleSteps); + + if (!m_UseTrilinearInterpolation) // image has not enough slices to use trilinear interpolation + { + ItkQBallImgType::IndexType index; + index[0] = floor(pos[0]/m_Spacing[0]); + index[1] = floor(pos[1]/m_Spacing[1]); + index[2] = floor(pos[2]/m_Spacing[2]); + if (m_Image->GetLargestPossibleRegion().IsInside(index)) + { + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2]); + } + } + else // use trilinear interpolation + { + float Rx = samplePos[0]/m_Spacing[0]-0.5; + float Ry = samplePos[1]/m_Spacing[1]-0.5; + float Rz = samplePos[2]/m_Spacing[2]-0.5; + + xint = floor(Rx); + yint = floor(Ry); + zint = floor(Rz); + + if (xint >= 0 && xint < m_Size[0]-1 && yint >= 0 && yint < m_Size[1]-1 && zint >= 0 && zint < m_Size[2]-1) + { + float xfrac = Rx-xint; + float yfrac = Ry-yint; + float zfrac = Rz-zint; + + ItkQBallImgType::IndexType index; + float weight; + + weight = (1-xfrac)*(1-yfrac)*(1-zfrac); + index[0] = xint; index[1] = yint; index[2] = zint; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + + weight = (xfrac)*(1-yfrac)*(1-zfrac); + index[0] = xint+1; index[1] = yint; index[2] = zint; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + + weight = (1-xfrac)*(yfrac)*(1-zfrac); + index[0] = xint; index[1] = yint+1; index[2] = zint; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + + weight = (1-xfrac)*(1-yfrac)*(zfrac); + index[0] = xint; index[1] = yint; index[2] = zint+1; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + + weight = (xfrac)*(yfrac)*(1-zfrac); + index[0] = xint+1; index[1] = yint+1; index[2] = zint; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + + weight = (1-xfrac)*(yfrac)*(zfrac); + index[0] = xint; index[1] = yint+1; index[2] = zint+1; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + + weight = (xfrac)*(1-yfrac)*(zfrac); + index[0] = xint+1; index[1] = yint; index[2] = zint+1; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + + weight = (xfrac)*(yfrac)*(zfrac); + index[0] = xint+1; index[1] = yint+1; index[2] = zint+1; + result += (m_Image->GetPixel(index)[m_SphereInterpolator->idx[0]-1]*m_SphereInterpolator->interpw[0] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[1]-1]*m_SphereInterpolator->interpw[1] + + m_Image->GetPixel(index)[m_SphereInterpolator->idx[2]-1]* m_SphereInterpolator->interpw[2])*weight; + } + } + } + result /= (2*sampleSteps+1); // average result over taken samples + return result; +} + +float EnergyComputer::ComputeExternalEnergy(vnl_vector_fixed &R, vnl_vector_fixed &N, Particle *dp) +{ + if (SpatProb(R) == 0) // check if position is inside mask + return itk::NumericTraits::NonpositiveMin(); + + float odfVal = EvaluateOdf(R, N); // evaluate ODF in given direction + + float modelVal = 0; + m_ParticleGrid->ComputeNeighbors(R); // retrieve neighbouring particles from particle grid + Particle* neighbour = m_ParticleGrid->GetNextNeighbor(); + while (neighbour!=NULL) // iterate over nieghbouring particles + { + if (dp != neighbour) // don't evaluate against itself + { + // see Reisert et al. "Global Reconstruction of Neuronal Fibers", MICCAI 2009 + float dot = fabs(dot_product(N,neighbour->dir)); + float bw = mbesseli0(dot); + float dpos = (neighbour->pos-R).squared_magnitude(); + float w = mexp(dpos*gamma_s); + modelVal += w*(bw+m_ParticleChemicalPotential); + w = mexp(dpos*gamma_reg_s); + } + neighbour = m_ParticleGrid->GetNextNeighbor(); + } + + float energy = 2*(odfVal/m_ParticleWeight-modelVal) - (mbesseli0(1.0)+m_ParticleChemicalPotential); + return energy*m_ExtStrength; +} + +float EnergyComputer::ComputeInternalEnergy(Particle *dp) +{ + float energy = 0; + + if (dp->pID != -1) // has predecessor + energy += ComputeInternalEnergyConnection(dp,+1); + + if (dp->mID != -1) // has successor + energy += ComputeInternalEnergyConnection(dp,-1); + + return energy; +} + +float EnergyComputer::ComputeInternalEnergyConnection(Particle *p1,int ep1) +{ + Particle *p2 = 0; + int ep2; + + if (ep1 == 1) + p2 = m_ParticleGrid->GetParticle(p1->pID); // get predecessor + else + p2 = m_ParticleGrid->GetParticle(p1->mID); // get successor + + // check in which direction the connected particle is pointing + if (p2->mID == p1->ID) + ep2 = -1; + else if (p2->pID == p1->ID) + ep2 = 1; + else + std::cout << "EnergyComputer: Connections are inconsistent!" << std::endl; + + return ComputeInternalEnergyConnection(p1,ep1,p2,ep2); +} + +float EnergyComputer::ComputeInternalEnergyConnection(Particle *p1,int ep1, Particle *p2, int ep2) +{ + // see Reisert et al. "Global Reconstruction of Neuronal Fibers", MICCAI 2009 + if ((dot_product(p1->dir,p2->dir))*ep1*ep2 > -m_CurvatureThreshold) // angle between particles is too sharp + return itk::NumericTraits::NonpositiveMin(); + + // calculate the endpoints of the two particles + vnl_vector_fixed endPoint1 = p1->pos + (p1->dir * (m_ParticleLength * ep1)); + vnl_vector_fixed endPoint2 = p2->pos + (p2->dir * (m_ParticleLength * ep2)); + + // check if endpoints are too far apart to connect + if ((endPoint1-endPoint2).squared_magnitude() > m_SquaredParticleLength) + return itk::NumericTraits::NonpositiveMin(); + + // calculate center point of the two particles + vnl_vector_fixed R = (p2->pos + p1->pos); R *= 0.5; + + // they are not allowed to connect if the mask image does not allow it + if (SpatProb(R) == 0) + return itk::NumericTraits::NonpositiveMin(); + + // get distances of endpoints to center point + float norm1 = (endPoint1-R).squared_magnitude(); + float norm2 = (endPoint2-R).squared_magnitude(); + + // calculate actual internal energy + float energy = (m_ConnectionPotential-norm1-norm2)*m_IntStrength; + return energy; +} + +float EnergyComputer::mbesseli0(float x) +{ + // BESSEL_APPROXCOEFF[0] = -0.1714; + // BESSEL_APPROXCOEFF[1] = 0.5332; + // BESSEL_APPROXCOEFF[2] = -1.4889; + // BESSEL_APPROXCOEFF[3] = 2.0389; + float y = x*x; + float erg = -0.1714; + erg += y*0.5332; + erg += y*y*-1.4889; + erg += y*y*y*2.0389; + return erg; +} + +float EnergyComputer::mexp(float x) +{ + return((x>=7.0) ? 0 : ((x>=5.0) ? (-0.0029*x+0.0213) : ((x>=3.0) ? (-0.0215*x+0.1144) : ((x>=2.0) ? (-0.0855*x+0.3064) : ((x>=1.0) ? (-0.2325*x+0.6004) : ((x>=0.5) ? (-0.4773*x+0.8452) : ((x>=0.0) ? (-0.7869*x+1.0000) : 1 ))))))); + // return exp(-x); +} + +//ComputeFiberCorrelation() +//{ +// float bD = 15; + +// vnl_matrix_fixed bDir = +// *itk::PointShell >::DistributePointShell(); + +// const int N = QBALL_ODFSIZE; + +// vnl_matrix_fixed temp = bDir.transpose(); +// vnl_matrix_fixed C = temp*bDir; +// vnl_matrix_fixed Q = C; +// vnl_vector_fixed mean; +// for(int i=0; i repMean; +// for (int i=0; i P = Q*Q; + +// std::vector pointer; +// pointer.reserve(N*N); +// double * start = C.data_block(); +// double * end = start + N*N; +// for (double * iter = start; iter != end; ++iter) +// { +// pointer.push_back(iter); +// } +// std::sort(pointer.begin(), pointer.end(), LessDereference()); + +// vnl_vector_fixed alpha; +// vnl_vector_fixed beta; +// for (int i=0; im_Meanval_sq = (sum*sum)/N; + +// vnl_vector_fixed alpha_0; +// vnl_vector_fixed alpha_2; +// vnl_vector_fixed alpha_4; +// vnl_vector_fixed alpha_6; +// for(int i=0; i T; +// T.set_column(0,alpha_0); +// T.set_column(1,alpha_2); +// T.set_column(2,alpha_4); +// T.set_column(3,alpha_6); + +// vnl_vector_fixed coeff = vnl_matrix_inverse(T).pinverse()*beta; + +// MITK_INFO << "itkGibbsTrackingFilter: Bessel oefficients: " << coeff; + +// BESSEL_APPROXCOEFF = new float[4]; + +// BESSEL_APPROXCOEFF[0] = coeff(0); +// BESSEL_APPROXCOEFF[1] = coeff(1); +// BESSEL_APPROXCOEFF[2] = coeff(2); +// BESSEL_APPROXCOEFF[3] = coeff(3); +// BESSEL_APPROXCOEFF[0] = -0.1714; +// BESSEL_APPROXCOEFF[1] = 0.5332; +// BESSEL_APPROXCOEFF[2] = -1.4889; +// BESSEL_APPROXCOEFF[3] = 2.0389; +//} diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkEnergyComputer.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkEnergyComputer.h new file mode 100644 index 0000000000..d847131fb0 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkEnergyComputer.h @@ -0,0 +1,84 @@ +/*=================================================================== + +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 _ENCOMP +#define _ENCOMP + +#include +#include +#include +#include +#include + +using namespace mitk; + +class MitkDiffusionImaging_EXPORT EnergyComputer +{ + +public: + + typedef itk::Vector OdfVectorType; + typedef itk::Image ItkQBallImgType; + typedef itk::Image ItkFloatImageType; + typedef itk::Statistics::MersenneTwisterRandomVariateGenerator ItkRandGenType; + + EnergyComputer(ItkQBallImgType* qballImage, ItkFloatImageType* mask, ParticleGrid* particleGrid, SphereInterpolator* interpolator, ItkRandGenType* randGen); + void SetParameters(float particleWeight, float particleWidth, float connectionPotential, float curvThres, float inexBalance, float particlePotential); + + // get random position inside mask + void DrawRandomPosition(vnl_vector_fixed& R); + + // external energy calculation + float ComputeExternalEnergy(vnl_vector_fixed& R, vnl_vector_fixed& N, Particle* dp); + + // internal energy calculation + float ComputeInternalEnergyConnection(Particle *p1,int ep1); + float ComputeInternalEnergyConnection(Particle *p1,int ep1, Particle *p2, int ep2); + float ComputeInternalEnergy(Particle *dp); + +protected: + + vnl_matrix_fixed m_RotationMatrix; + SphereInterpolator* m_SphereInterpolator; + ParticleGrid* m_ParticleGrid; + ItkRandGenType* m_RandGen; + ItkQBallImgType* m_Image; + ItkFloatImageType* m_Mask; + vnl_vector_fixed m_Size; + vnl_vector_fixed m_Spacing; + std::vector< float > m_CumulatedSpatialProbability; + std::vector< int > m_ActiveIndices; // indices inside mask + + bool m_UseTrilinearInterpolation; // is deactivated if less than 3 image slices are available + int m_NumActiveVoxels; // voxels inside mask + float m_ConnectionPotential; // larger value results in larger energy value -> higher proposal acceptance probability + float m_ParticleChemicalPotential; // larger value results in larger energy value -> higher proposal acceptance probability + float gamma_s; + float gamma_reg_s; + float m_ParticleWeight; // defines how much one particle contributes to the artificial signal + float m_ExtStrength; // weighting factor for external energy + float m_IntStrength; // weighting factor for internal energy + float m_ParticleLength; // particle length + float m_SquaredParticleLength; // squared particle length + float m_CurvatureThreshold; // maximum angle accepted between two connected particles + + float SpatProb(vnl_vector_fixed pos); + float EvaluateOdf(vnl_vector_fixed &pos, vnl_vector_fixed dir); + float mbesseli0(float x); + float mexp(float x); +}; + +#endif diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkFiberBuilder.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkFiberBuilder.cpp new file mode 100644 index 0000000000..f5add08263 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkFiberBuilder.cpp @@ -0,0 +1,138 @@ +/*=================================================================== + +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 "mitkFiberBuilder.h" + +using namespace mitk; + +FiberBuilder::FiberBuilder(ParticleGrid* grid, ItkFloatImageType* image) +{ + m_Grid = grid; + m_Image = image; + m_FiberLength = 0; +} + +FiberBuilder::~FiberBuilder() +{ + +} + +vtkSmartPointer FiberBuilder::iterate(int minFiberLength) +{ + m_VtkCellArray = vtkSmartPointer::New(); + m_VtkPoints = vtkSmartPointer::New(); + + int cur_label = 1; + int numFibers = 0; + m_FiberLength = 0; + for (int k = 0; k < m_Grid->m_NumParticles;k++) + { + Particle *dp = m_Grid->GetParticle(k); + if (dp->label == 0) + { + vtkSmartPointer container = vtkSmartPointer::New(); + dp->label = cur_label; + LabelPredecessors(dp, -1, container); + LabelSuccessors(dp, 1, container); + cur_label++; + if(m_FiberLength >= minFiberLength) + { + m_VtkCellArray->InsertNextCell(container); + numFibers++; + } + m_FiberLength = 0; + } + } + for (int k = 0; k < m_Grid->m_NumParticles;k++) + { + Particle *dp = m_Grid->GetParticle(k); + dp->label = 0; + } + + vtkSmartPointer fiberPolyData = vtkSmartPointer::New(); + fiberPolyData->SetPoints(m_VtkPoints); + fiberPolyData->SetLines(m_VtkCellArray); + return fiberPolyData; +} + +void FiberBuilder::LabelPredecessors(Particle* p, int ep, vtkPolyLine* container) +{ + Particle* p2 = NULL; + if (ep==1) + p2 = m_Grid->GetParticle(p->pID); + else + p2 = m_Grid->GetParticle(p->mID); + + if (p2!=NULL && p2->label==0) + { + p2->label = 1; // assign particle to current fiber + + if (p2->pID==p->ID) + LabelPredecessors(p2, -1, container); + else if (p2->mID==p->ID) + LabelPredecessors(p2, 1, container); + else + std::cout << "FiberBuilder: connection inconsistent (LabelPredecessors)" << std::endl; + } + + AddPoint(p, container); +} + + +void FiberBuilder::LabelSuccessors(Particle* p, int ep, vtkPolyLine* container) +{ + AddPoint(p, container); + + Particle* p2 = NULL; + if (ep==1) + p2 = m_Grid->GetParticle(p->pID); + else + p2 = m_Grid->GetParticle(p->mID); + + if (p2!=NULL && p2->label==0) + { + p2->label = 1; // assign particle to current fiber + + if (p2->pID==p->ID) + LabelSuccessors(p2, -1, container); + else if (p2->mID==p->ID) + LabelSuccessors(p2, 1, container); + else + std::cout << "FiberBuilder: connection inconsistent (LabelPredecessors)" << std::endl; + } +} + +void FiberBuilder::AddPoint(Particle *dp, vtkSmartPointer container) +{ + if (dp->label!=1) + return; + + dp->label = 2; + + itk::ContinuousIndex index; + index[0] = dp->pos[0]/m_Image->GetSpacing()[0]-0.5; + index[1] = dp->pos[1]/m_Image->GetSpacing()[1]-0.5; + index[2] = dp->pos[2]/m_Image->GetSpacing()[2]-0.5; + itk::Point point; + m_Image->TransformContinuousIndexToPhysicalPoint( index, point ); + vtkIdType id = m_VtkPoints->InsertNextPoint(point.GetDataPointer()); + container->GetPointIds()->InsertNextId(id); + + if(container->GetNumberOfPoints()>1) + m_FiberLength += m_LastPoint.EuclideanDistanceTo(point); + + m_LastPoint = point; +} diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkFiberBuilder.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkFiberBuilder.h new file mode 100644 index 0000000000..e30f456a4c --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkFiberBuilder.h @@ -0,0 +1,67 @@ +/*=================================================================== + +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 _BUILDFIBRES +#define _BUILDFIBRES + +// MITK +#include +#include + +// VTK +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace mitk +{ + +class MitkDiffusionImaging_EXPORT FiberBuilder +{ +public: + + typedef itk::Image ItkFloatImageType; + + FiberBuilder(ParticleGrid* grid, ItkFloatImageType* image); + ~FiberBuilder(); + + vtkSmartPointer iterate(int minFiberLength); + +protected: + + void AddPoint(Particle *dp, vtkSmartPointer container); + + void LabelPredecessors(Particle* p, int ep, vtkPolyLine* container); + void LabelSuccessors(Particle* p, int ep, vtkPolyLine* container); + + itk::Point m_LastPoint; + float m_FiberLength; + ItkFloatImageType::Pointer m_Image; + ParticleGrid* m_Grid; + vtkSmartPointer m_VtkCellArray; + vtkSmartPointer m_VtkPoints; + +}; + +} + +#endif diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkMetropolisHastingsSampler.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkMetropolisHastingsSampler.cpp new file mode 100644 index 0000000000..0007ba3368 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkMetropolisHastingsSampler.cpp @@ -0,0 +1,478 @@ +/*=================================================================== + +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 "mitkMetropolisHastingsSampler.h" + +using namespace mitk; + +MetropolisHastingsSampler::MetropolisHastingsSampler(ParticleGrid* grid, EnergyComputer* enComp, ItkRandGenType* randGen, float curvThres) + : m_AcceptedProposals(0) + , m_ExTemp(0.01) + , m_BirthProb(0.25) + , m_DeathProb(0.05) + , m_ShiftProb(0.15) + , m_OptShiftProb(0.1) + , m_ConnectionProb(0.45) + , m_TractProb(0.5) + , m_DelProb(0.1) + , m_ChempotParticle(0.0) +{ + m_RandGen = randGen; + m_ParticleGrid = grid; + m_EnergyComputer = enComp; + + m_ParticleLength = m_ParticleGrid->m_ParticleLength; + m_DistanceThreshold = m_ParticleLength*m_ParticleLength; + m_Sigma = m_ParticleLength/8.0; + m_Gamma = 1/(m_Sigma*m_Sigma*2); + m_Z = pow(2*M_PI*m_Sigma,3.0/2.0)*(M_PI*m_Sigma/m_ParticleLength); + + m_CurvatureThreshold = curvThres; + m_StopProb = exp(-1/m_TractProb); +} + +void MetropolisHastingsSampler::SetProbabilities(float birth, float death, float shift, float optShift, float connect) +{ + m_BirthProb = birth; + m_DeathProb = death; + m_ShiftProb = shift; + m_OptShiftProb = optShift; + m_ConnectionProb = connect; + float sum = m_BirthProb+m_DeathProb+m_ShiftProb+m_OptShiftProb+m_ConnectionProb; + if (sum!=1 && sum>mitk::eps) + { + m_BirthProb /= sum; + m_DeathProb /= sum; + m_ShiftProb /= sum; + m_OptShiftProb /= sum; + m_ConnectionProb /= sum; + } + std::cout << "Update proposal probabilities:" << std::endl; + std::cout << "Birth: " << m_BirthProb << std::endl; + std::cout << "Death: " << m_DeathProb << std::endl; + std::cout << "Shift: " << m_ShiftProb << std::endl; + std::cout << "Optimal shift: " << m_OptShiftProb << std::endl; + std::cout << "Connection: " << m_ConnectionProb << std::endl; +} + +// update temperature of simulated annealing process +void MetropolisHastingsSampler::SetTemperature(float val) +{ + m_InTemp = val; + m_Density = exp(-m_ChempotParticle/m_InTemp); +} + +// add small random number drawn from gaussian to each vector element +void MetropolisHastingsSampler::DistortVector(float sigma, vnl_vector_fixed& vec) +{ + vec[0] += m_RandGen->GetNormalVariate(0.0, sigma); + vec[1] += m_RandGen->GetNormalVariate(0.0, sigma); + vec[2] += m_RandGen->GetNormalVariate(0.0, sigma); +} + +// generate normalized random vector +vnl_vector_fixed MetropolisHastingsSampler::GetRandomDirection() +{ + vnl_vector_fixed vec; + vec[0] = m_RandGen->GetNormalVariate(); + vec[1] = m_RandGen->GetNormalVariate(); + vec[2] = m_RandGen->GetNormalVariate(); + vec.normalize(); + return vec; +} + +// generate actual proposal (birth, death, shift and connection of particle) +void MetropolisHastingsSampler::MakeProposal() +{ + float randnum = m_RandGen->GetVariate(); + + // Birth Proposal + if (randnum < m_BirthProb) + { + vnl_vector_fixed R; + m_EnergyComputer->DrawRandomPosition(R); + vnl_vector_fixed N = GetRandomDirection(); + Particle prop; + prop.pos = R; + prop.dir = N; + + float prob = m_Density * m_DeathProb /((m_BirthProb)*(m_ParticleGrid->m_NumParticles+1)); + + float ex_energy = m_EnergyComputer->ComputeExternalEnergy(R,N,0); + float in_energy = m_EnergyComputer->ComputeInternalEnergy(&prop); + prob *= exp((in_energy/m_InTemp+ex_energy/m_ExTemp)) ; + + if (prob > 1 || m_RandGen->GetVariate() < prob) + { + Particle *p = m_ParticleGrid->NewParticle(R); + if (p!=0) + { + p->pos = R; + p->dir = N; + m_AcceptedProposals++; + } + } + } + // Death Proposal + else if (randnum < m_BirthProb+m_DeathProb) + { + if (m_ParticleGrid->m_NumParticles > 0) + { + int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; + Particle *dp = m_ParticleGrid->GetParticle(pnum); + if (dp->pID == -1 && dp->mID == -1) + { + float ex_energy = m_EnergyComputer->ComputeExternalEnergy(dp->pos,dp->dir,dp); + float in_energy = m_EnergyComputer->ComputeInternalEnergy(dp); + + float prob = m_ParticleGrid->m_NumParticles * (m_BirthProb) /(m_Density*m_DeathProb); //*SpatProb(dp->R); + prob *= exp(-(in_energy/m_InTemp+ex_energy/m_ExTemp)) ; + if (prob > 1 || m_RandGen->GetVariate() < prob) + { + m_ParticleGrid->RemoveParticle(pnum); + m_AcceptedProposals++; + } + } + } + + } + // Shift Proposal + else if (randnum < m_BirthProb+m_DeathProb+m_ShiftProb) + { + if (m_ParticleGrid->m_NumParticles > 0) + { + int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; + Particle *p = m_ParticleGrid->GetParticle(pnum); + Particle prop_p = *p; + + DistortVector(m_Sigma, prop_p.pos); + DistortVector(m_Sigma/(2*m_ParticleLength), prop_p.dir); + prop_p.dir.normalize(); + + + float ex_energy = m_EnergyComputer->ComputeExternalEnergy(prop_p.pos,prop_p.dir,p) + - m_EnergyComputer->ComputeExternalEnergy(p->pos,p->dir,p); + float in_energy = m_EnergyComputer->ComputeInternalEnergy(&prop_p) - m_EnergyComputer->ComputeInternalEnergy(p); + + float prob = exp(ex_energy/m_ExTemp+in_energy/m_InTemp); + if (m_RandGen->GetVariate() < prob) + { + vnl_vector_fixed Rtmp = p->pos; + vnl_vector_fixed Ntmp = p->dir; + p->pos = prop_p.pos; + p->dir = prop_p.dir; + if (!m_ParticleGrid->TryUpdateGrid(pnum)) + { + p->pos = Rtmp; + p->dir = Ntmp; + } + m_AcceptedProposals++; + } + } + } + // Optimal Shift Proposal + else if (randnum < m_BirthProb+m_DeathProb+m_ShiftProb+m_OptShiftProb) + { + if (m_ParticleGrid->m_NumParticles > 0) + { + + int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; + Particle *p = m_ParticleGrid->GetParticle(pnum); + + bool no_proposal = false; + Particle prop_p = *p; + if (p->pID != -1 && p->mID != -1) + { + Particle *plus = m_ParticleGrid->GetParticle(p->pID); + int ep_plus = (plus->pID == p->ID)? 1 : -1; + Particle *minus = m_ParticleGrid->GetParticle(p->mID); + int ep_minus = (minus->pID == p->ID)? 1 : -1; + prop_p.pos = (plus->pos + plus->dir * (m_ParticleLength * ep_plus) + minus->pos + minus->dir * (m_ParticleLength * ep_minus)); + prop_p.pos *= 0.5; + prop_p.dir = plus->pos - minus->pos; + prop_p.dir.normalize(); + } + else if (p->pID != -1) + { + Particle *plus = m_ParticleGrid->GetParticle(p->pID); + int ep_plus = (plus->pID == p->ID)? 1 : -1; + prop_p.pos = plus->pos + plus->dir * (m_ParticleLength * ep_plus * 2); + prop_p.dir = plus->dir; + } + else if (p->mID != -1) + { + Particle *minus = m_ParticleGrid->GetParticle(p->mID); + int ep_minus = (minus->pID == p->ID)? 1 : -1; + prop_p.pos = minus->pos + minus->dir * (m_ParticleLength * ep_minus * 2); + prop_p.dir = minus->dir; + } + else + no_proposal = true; + + if (!no_proposal) + { + float cos = dot_product(prop_p.dir, p->dir); + float p_rev = exp(-((prop_p.pos-p->pos).squared_magnitude() + (1-cos*cos))*m_Gamma)/m_Z; + + float ex_energy = m_EnergyComputer->ComputeExternalEnergy(prop_p.pos,prop_p.dir,p) + - m_EnergyComputer->ComputeExternalEnergy(p->pos,p->dir,p); + float in_energy = m_EnergyComputer->ComputeInternalEnergy(&prop_p) - m_EnergyComputer->ComputeInternalEnergy(p); + + float prob = exp(ex_energy/m_ExTemp+in_energy/m_InTemp)*m_ShiftProb*p_rev/(m_OptShiftProb+m_ShiftProb*p_rev); + + if (m_RandGen->GetVariate() < prob) + { + vnl_vector_fixed Rtmp = p->pos; + vnl_vector_fixed Ntmp = p->dir; + p->pos = prop_p.pos; + p->dir = prop_p.dir; + if (!m_ParticleGrid->TryUpdateGrid(pnum)) + { + p->pos = Rtmp; + p->dir = Ntmp; + } + m_AcceptedProposals++; + } + } + } + } + else + { + if (m_ParticleGrid->m_NumParticles > 0) + { + int pnum = m_RandGen->GetIntegerVariate()%m_ParticleGrid->m_NumParticles; + Particle *p = m_ParticleGrid->GetParticle(pnum); + + EndPoint P; + P.p = p; + P.ep = (m_RandGen->GetVariate() > 0.5)? 1 : -1; + + RemoveAndSaveTrack(P); + if (m_BackupTrack.m_Probability != 0) + { + MakeTrackProposal(P); + + float prob = (m_ProposalTrack.m_Energy-m_BackupTrack.m_Energy)/m_InTemp ; + + prob = exp(prob)*(m_BackupTrack.m_Probability * pow(m_DelProb,m_ProposalTrack.m_Length)) + /(m_ProposalTrack.m_Probability * pow(m_DelProb,m_BackupTrack.m_Length)); + if (m_RandGen->GetVariate() < prob) + { + ImplementTrack(m_ProposalTrack); + m_AcceptedProposals++; + } + else + { + ImplementTrack(m_BackupTrack); + } + } + else + ImplementTrack(m_BackupTrack); + } + } +} + +// establish connections between particles stored in input Track +void MetropolisHastingsSampler::ImplementTrack(Track &T) +{ + for (int k = 1; k < T.m_Length;k++) + m_ParticleGrid->CreateConnection(T.track[k-1].p,T.track[k-1].ep,T.track[k].p,-T.track[k].ep); +} + +// remove pending track from random particle, save it in m_BackupTrack and calculate its probability +void MetropolisHastingsSampler::RemoveAndSaveTrack(EndPoint P) +{ + EndPoint Current = P; + int cnt = 0; + float energy = 0; + float AccumProb = 1.0; + m_BackupTrack.track[cnt] = Current; + EndPoint Next; + + for (;;) + { + Next.p = 0; + if (Current.ep == 1) + { + if (Current.p->pID != -1) + { + Next.p = m_ParticleGrid->GetParticle(Current.p->pID); + Current.p->pID = -1; + m_ParticleGrid->m_NumConnections--; + } + } + else if (Current.ep == -1) + { + if (Current.p->mID != -1) + { + Next.p = m_ParticleGrid->GetParticle(Current.p->mID); + Current.p->mID = -1; + m_ParticleGrid->m_NumConnections--; + } + } + else + { fprintf(stderr,"MetropolisHastingsSampler_randshift: Connection inconsistent 3\n"); break; } + + if (Next.p == 0) // no successor + { + Next.ep = 0; // mark as empty successor + break; + } + else + { + if (Next.p->pID == Current.p->ID) + { + Next.p->pID = -1; + Next.ep = 1; + } + else if (Next.p->mID == Current.p->ID) + { + Next.p->mID = -1; + Next.ep = -1; + } + else + { fprintf(stderr,"MetropolisHastingsSampler_randshift: Connection inconsistent 4\n"); break; } + } + + ComputeEndPointProposalDistribution(Current); + AccumProb *= (m_SimpSamp.probFor(Next)); + + if (Next.p == 0) // no successor -> break + break; + + energy += m_EnergyComputer->ComputeInternalEnergyConnection(Current.p,Current.ep,Next.p,Next.ep); + + Current = Next; + Current.ep *= -1; + cnt++; + m_BackupTrack.track[cnt] = Current; + + if (m_RandGen->GetVariate() > m_DelProb) + break; + } + m_BackupTrack.m_Energy = energy; + m_BackupTrack.m_Probability = AccumProb; + m_BackupTrack.m_Length = cnt+1; +} + +// generate new track using kind of a local tracking starting from P in the given direction, store it in m_ProposalTrack and calculate its probability +void MetropolisHastingsSampler::MakeTrackProposal(EndPoint P) +{ + EndPoint Current = P; + int cnt = 0; + float energy = 0; + float AccumProb = 1.0; + m_ProposalTrack.track[cnt++] = Current; + Current.p->label = 1; + + for (;;) + { + // next candidate is already connected + if ((Current.ep == 1 && Current.p->pID != -1) || (Current.ep == -1 && Current.p->mID != -1)) + break; + + // track too long +// if (cnt > 250) +// break; + + ComputeEndPointProposalDistribution(Current); + + int k = m_SimpSamp.draw(m_RandGen->GetVariate()); + + // stop tracking proposed + if (k==0) + break; + + EndPoint Next = m_SimpSamp.objs[k]; + float probability = m_SimpSamp.probFor(k); + + // accumulate energy and proposal distribution + energy += m_EnergyComputer->ComputeInternalEnergyConnection(Current.p,Current.ep,Next.p,Next.ep); + AccumProb *= probability; + + // track to next endpoint + Current = Next; + Current.ep *= -1; + + Current.p->label = 1; // put label to avoid loops + m_ProposalTrack.track[cnt++] = Current; + } + + m_ProposalTrack.m_Energy = energy; + m_ProposalTrack.m_Probability = AccumProb; + m_ProposalTrack.m_Length = cnt; + + // clear labels + for (int j = 0; j < m_ProposalTrack.m_Length;j++) + m_ProposalTrack.track[j].p->label = 0; +} + +// get neigbouring particles of P and calculate the according connection probabilities +void MetropolisHastingsSampler::ComputeEndPointProposalDistribution(EndPoint P) +{ + Particle *p = P.p; + int ep = P.ep; + + float dist,dot; + vnl_vector_fixed R = p->pos + (p->dir * (ep*m_ParticleLength) ); + m_ParticleGrid->ComputeNeighbors(R); + m_SimpSamp.clear(); + + m_SimpSamp.add(m_StopProb,EndPoint(0,0)); + + for (;;) + { + Particle *p2 = m_ParticleGrid->GetNextNeighbor(); + if (p2 == 0) break; + if (p!=p2 && p2->label == 0) + { + if (p2->mID == -1) + { + dist = (p2->pos - p2->dir * m_ParticleLength - R).squared_magnitude(); + if (dist < m_DistanceThreshold) + { + dot = dot_product(p2->dir,p->dir) * ep; + if (dot > m_CurvatureThreshold) + { + float en = m_EnergyComputer->ComputeInternalEnergyConnection(p,ep,p2,-1); + m_SimpSamp.add(exp(en/m_TractProb),EndPoint(p2,-1)); + } + } + } + if (p2->pID == -1) + { + dist = (p2->pos + p2->dir * m_ParticleLength - R).squared_magnitude(); + if (dist < m_DistanceThreshold) + { + dot = dot_product(p2->dir,p->dir) * (-ep); + if (dot > m_CurvatureThreshold) + { + float en = m_EnergyComputer->ComputeInternalEnergyConnection(p,ep,p2,+1); + m_SimpSamp.add(exp(en/m_TractProb),EndPoint(p2,+1)); + } + } + } + } + } +} + +// return number of accepted proposals +int MetropolisHastingsSampler::GetNumAcceptedProposals() +{ + return m_AcceptedProposals; +} + + diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkMetropolisHastingsSampler.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkMetropolisHastingsSampler.h new file mode 100644 index 0000000000..f9396f3f04 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkMetropolisHastingsSampler.h @@ -0,0 +1,98 @@ +/*=================================================================== + +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 _SAMPLER +#define _SAMPLER + +// MITK +#include +#include +#include +#include + +// ITK +#include +#include + +// MISC +#include + +namespace mitk +{ + +class MitkDiffusionImaging_EXPORT MetropolisHastingsSampler +{ +public: + + typedef itk::Image< float, 3 > ItkFloatImageType; + typedef itk::Statistics::MersenneTwisterRandomVariateGenerator ItkRandGenType; + + MetropolisHastingsSampler(ParticleGrid* grid, EnergyComputer* enComp, ItkRandGenType* randGen, float curvThres); + void SetTemperature(float val); + + void MakeProposal(); + int GetNumAcceptedProposals(); + void SetProbabilities(float birth, float death, float shift, float optShift, float connect); + +protected: + + // connection proposal related methods + void ImplementTrack(Track& T); + void RemoveAndSaveTrack(EndPoint P); + void MakeTrackProposal(EndPoint P); + void ComputeEndPointProposalDistribution(EndPoint P); + + // generate random vectors + void DistortVector(float sigma, vnl_vector_fixed& vec); + vnl_vector_fixed GetRandomDirection(); + + ItkRandGenType* m_RandGen; // random generator + Track m_ProposalTrack; // stores proposal track + Track m_BackupTrack; // stores track removed for new proposal traCK + SimpSamp m_SimpSamp; // neighbouring particles and their probabilities for the local tracking + + float m_InTemp; // simulated annealing temperature + float m_ExTemp; // simulated annealing temperature + float m_Density; + + float m_BirthProb; // probability for particle birth + float m_DeathProb; // probability for particle death + float m_ShiftProb; // probability for particle shift + float m_OptShiftProb; // probability for optimal particle shift + float m_ConnectionProb; // probability for particle connection proposal + + float m_Sigma; + float m_Gamma; + float m_Z; + + float m_DistanceThreshold; // threshold for maximum distance between connected particles + float m_CurvatureThreshold; // threshold for maximum angle between connected particles + float m_TractProb; + float m_StopProb; + float m_DelProb; + float m_ParticleLength; + float m_ChempotParticle; + + ParticleGrid* m_ParticleGrid; + EnergyComputer* m_EnergyComputer; + int m_AcceptedProposals; +}; + +} + +#endif + + diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticle.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticle.h new file mode 100644 index 0000000000..14e04b2cc5 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticle.h @@ -0,0 +1,73 @@ +/*=================================================================== + +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 _PARTICLE +#define _PARTICLE + +#include +#include + +namespace mitk +{ + +class MitkDiffusionImaging_EXPORT Particle +{ +public: + + Particle() + { + label = 0; + pID = -1; + mID = -1; + } + + ~Particle() + { + } + + vnl_vector_fixed pos; // particle position (world coordinates. corner based voxels. not accounted for image rotation. + vnl_vector_fixed dir; // normalized direction vector + + int gridindex; // index in the grid where it is living + int ID; // particle ID + int pID; // successor ID + int mID; // predecessor ID + unsigned char label; // label used in the fiber building process +}; + +class MitkDiffusionImaging_EXPORT EndPoint +{ +public: + EndPoint() + {} + + EndPoint(Particle *p,int ep) + { + this->p = p; + this->ep = ep; + } + Particle *p; + int ep; + + inline bool operator==(EndPoint P) + { + return (P.p == p) && (P.ep == ep); + } +}; + +} + +#endif diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticleGrid.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticleGrid.cpp new file mode 100644 index 0000000000..c9d08b8b3c --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticleGrid.cpp @@ -0,0 +1,389 @@ +/*=================================================================== + +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 "mitkParticleGrid.h" +#include +#include + +using namespace mitk; + +ParticleGrid::ParticleGrid(ItkFloatImageType* image, float particleLength) +{ + // initialize counters + m_NumParticles = 0; + m_NumConnections = 0; + m_NumCellOverflows = 0; + m_ParticleLength = particleLength; + + // define isotropic grid from voxel spacing and particle length + float cellSize = 2*m_ParticleLength; + m_GridSize[0] = image->GetLargestPossibleRegion().GetSize()[0]*image->GetSpacing()[0]/cellSize +1; + m_GridSize[1] = image->GetLargestPossibleRegion().GetSize()[1]*image->GetSpacing()[1]/cellSize +1; + m_GridSize[2] = image->GetLargestPossibleRegion().GetSize()[2]*image->GetSpacing()[2]/cellSize +1; + m_GridScale[0] = 1/cellSize; + m_GridScale[1] = 1/cellSize; + m_GridScale[2] = 1/cellSize; + + m_CellCapacity = 1024; // maximum number of particles per grid cell + m_ContainerCapacity = 100000; // initial particle container capacity + int numCells = m_GridSize[0]*m_GridSize[1]*m_GridSize[2]; // number of grid cells + + m_Particles.resize(m_ContainerCapacity); // allocate and initialize particles + m_Grid.resize(numCells*m_CellCapacity, NULL); // allocate and initialize particle grid + m_OccupationCount.resize(numCells, 0); // allocate and initialize occupation counter array + m_NeighbourTracker.cellidx.resize(8, 0); // allocate and initialize neighbour tracker + m_NeighbourTracker.cellidx_c.resize(8, 0); + + for (int i = 0;i < m_ContainerCapacity;i++) // initialize particle IDs + m_Particles[i].ID = i; + + std::cout << "ParticleGrid: allocated " << (sizeof(Particle)*m_ContainerCapacity + sizeof(Particle*)*m_GridSize[0]*m_GridSize[1]*m_GridSize[2])/1048576 << "mb for " << m_ContainerCapacity/1000 << "k particles." << std::endl; +} + +ParticleGrid::~ParticleGrid() +{ +} + +bool ParticleGrid::ReallocateGrid() +{ + std::cout << "ParticleGrid: reallocating ..." << std::endl; + int new_capacity = m_ContainerCapacity + 100000; // increase container capacity by 100k particles + try + { + m_Particles.resize(new_capacity); // reallocate particles + + for (int i = 0; i R) +{ + if (m_NumParticles >= m_ContainerCapacity) + { + if (!ReallocateGrid()) + return NULL; + } + + int xint = int(R[0]*m_GridScale[0]); + if (xint < 0) + return NULL; + if (xint >= m_GridSize[0]) + return NULL; + int yint = int(R[1]*m_GridScale[1]); + if (yint < 0) + return NULL; + if (yint >= m_GridSize[1]) + return NULL; + int zint = int(R[2]*m_GridScale[2]); + if (zint < 0) + return NULL; + if (zint >= m_GridSize[2]) + return NULL; + + int idx = xint + m_GridSize[0]*(yint + m_GridSize[1]*zint); + if (m_OccupationCount[idx] < m_CellCapacity) + { + Particle *p = &(m_Particles[m_NumParticles]); + p->pos = R; + p->mID = -1; + p->pID = -1; + m_NumParticles++; + p->gridindex = m_CellCapacity*idx + m_OccupationCount[idx]; + m_Grid[p->gridindex] = p; + m_OccupationCount[idx]++; + return p; + } + else + { + m_NumCellOverflows++; + return NULL; + } +} + +bool ParticleGrid::TryUpdateGrid(int k) +{ + Particle* p = &(m_Particles[k]); + + int xint = int(p->pos[0]*m_GridScale[0]); + if (xint < 0) + return false; + if (xint >= m_GridSize[0]) + return false; + int yint = int(p->pos[1]*m_GridScale[1]); + if (yint < 0) + return false; + if (yint >= m_GridSize[1]) + return false; + int zint = int(p->pos[2]*m_GridScale[2]); + if (zint < 0) + return false; + if (zint >= m_GridSize[2]) + return false; + + int idx = xint + m_GridSize[0]*(yint+ zint*m_GridSize[1]); + int cellidx = p->gridindex/m_CellCapacity; + if (idx != cellidx) // cell has changed + { + + if (m_OccupationCount[idx] < m_CellCapacity) + { + // remove from old position in grid; + int grdindex = p->gridindex; + m_Grid[grdindex] = m_Grid[cellidx*m_CellCapacity + m_OccupationCount[cellidx]-1]; + m_Grid[grdindex]->gridindex = grdindex; + m_OccupationCount[cellidx]--; + + // insert at new position in grid + p->gridindex = idx*m_CellCapacity + m_OccupationCount[idx]; + m_Grid[p->gridindex] = p; + m_OccupationCount[idx]++; + return true; + } + else + { + m_NumCellOverflows++; + return false; + } + } + return true; +} + +void ParticleGrid::RemoveParticle(int k) +{ + Particle* p = &(m_Particles[k]); + int gridIndex = p->gridindex; + int cellIdx = gridIndex/m_CellCapacity; + int idx = gridIndex%m_CellCapacity; + + // remove pending connections + if (p->mID != -1) + DestroyConnection(p,-1); + if (p->pID != -1) + DestroyConnection(p,+1); + + // remove from grid + if (idx < m_OccupationCount[cellIdx]-1) + { + m_Grid[gridIndex] = m_Grid[cellIdx*m_CellCapacity+m_OccupationCount[cellIdx]-1]; + m_Grid[cellIdx*m_CellCapacity+m_OccupationCount[cellIdx]-1] = NULL; + m_Grid[gridIndex]->gridindex = gridIndex; + } + m_OccupationCount[cellIdx]--; + + // remove from container + if (k < m_NumParticles-1) + { + Particle* last = &m_Particles[m_NumParticles-1]; // last particle + + // update connections of last particle because its index is changing + if (last->mID!=-1) + { + if ( m_Particles[last->mID].mID == m_NumParticles-1 ) + m_Particles[last->mID].mID = k; + else if ( m_Particles[last->mID].pID == m_NumParticles-1 ) + m_Particles[last->mID].pID = k; + } + if (last->pID!=-1) + { + if ( m_Particles[last->pID].mID == m_NumParticles-1 ) + m_Particles[last->pID].mID = k; + else if ( m_Particles[last->pID].pID == m_NumParticles-1 ) + m_Particles[last->pID].pID = k; + } + + m_Particles[k] = m_Particles[m_NumParticles-1]; // move very last particle to empty slot + m_Particles[m_NumParticles-1].ID = m_NumParticles-1; // update ID of removed particle to match the index + m_Particles[k].ID = k; // update ID of moved particle + m_Grid[m_Particles[k].gridindex] = &m_Particles[k]; // update address of moved particle + } + m_NumParticles--; +} + +void ParticleGrid::ComputeNeighbors(vnl_vector_fixed &R) +{ + float xfrac = R[0]*m_GridScale[0]; + float yfrac = R[1]*m_GridScale[1]; + float zfrac = R[2]*m_GridScale[2]; + int xint = int(xfrac); + int yint = int(yfrac); + int zint = int(zfrac); + + int dx = -1; + if (xfrac-xint > 0.5) dx = 1; + if (xint <= 0) { xint = 0; dx = 1; } + if (xint >= m_GridSize[0]-1) { xint = m_GridSize[0]-1; dx = -1; } + + int dy = -1; + if (yfrac-yint > 0.5) dy = 1; + if (yint <= 0) {yint = 0; dy = 1; } + if (yint >= m_GridSize[1]-1) {yint = m_GridSize[1]-1; dy = -1;} + + int dz = -1; + if (zfrac-zint > 0.5) dz = 1; + if (zint <= 0) {zint = 0; dz = 1; } + if (zint >= m_GridSize[2]-1) {zint = m_GridSize[2]-1; dz = -1;} + + + m_NeighbourTracker.cellidx[0] = xint + m_GridSize[0]*(yint+zint*m_GridSize[1]); + m_NeighbourTracker.cellidx[1] = m_NeighbourTracker.cellidx[0] + dx; + m_NeighbourTracker.cellidx[2] = m_NeighbourTracker.cellidx[1] + dy*m_GridSize[0]; + m_NeighbourTracker.cellidx[3] = m_NeighbourTracker.cellidx[2] - dx; + m_NeighbourTracker.cellidx[4] = m_NeighbourTracker.cellidx[0] + dz*m_GridSize[0]*m_GridSize[1]; + m_NeighbourTracker.cellidx[5] = m_NeighbourTracker.cellidx[4] + dx; + m_NeighbourTracker.cellidx[6] = m_NeighbourTracker.cellidx[5] + dy*m_GridSize[0]; + m_NeighbourTracker.cellidx[7] = m_NeighbourTracker.cellidx[6] - dx; + + + m_NeighbourTracker.cellidx_c[0] = m_CellCapacity*m_NeighbourTracker.cellidx[0]; + m_NeighbourTracker.cellidx_c[1] = m_CellCapacity*m_NeighbourTracker.cellidx[1]; + m_NeighbourTracker.cellidx_c[2] = m_CellCapacity*m_NeighbourTracker.cellidx[2]; + m_NeighbourTracker.cellidx_c[3] = m_CellCapacity*m_NeighbourTracker.cellidx[3]; + m_NeighbourTracker.cellidx_c[4] = m_CellCapacity*m_NeighbourTracker.cellidx[4]; + m_NeighbourTracker.cellidx_c[5] = m_CellCapacity*m_NeighbourTracker.cellidx[5]; + m_NeighbourTracker.cellidx_c[6] = m_CellCapacity*m_NeighbourTracker.cellidx[6]; + m_NeighbourTracker.cellidx_c[7] = m_CellCapacity*m_NeighbourTracker.cellidx[7]; + + m_NeighbourTracker.cellcnt = 0; + m_NeighbourTracker.pcnt = 0; +} + +Particle* ParticleGrid::GetNextNeighbor() +{ + if (m_NeighbourTracker.pcnt < m_OccupationCount[m_NeighbourTracker.cellidx[m_NeighbourTracker.cellcnt]]) + { + return m_Grid[m_NeighbourTracker.cellidx_c[m_NeighbourTracker.cellcnt] + (m_NeighbourTracker.pcnt++)]; + } + else + { + for(;;) + { + m_NeighbourTracker.cellcnt++; + if (m_NeighbourTracker.cellcnt >= 8) + return 0; + if (m_OccupationCount[m_NeighbourTracker.cellidx[m_NeighbourTracker.cellcnt]] > 0) + break; + } + m_NeighbourTracker.pcnt = 1; + return m_Grid[m_NeighbourTracker.cellidx_c[m_NeighbourTracker.cellcnt]]; + } +} + +void ParticleGrid::CreateConnection(Particle *P1,int ep1, Particle *P2, int ep2) +{ + if (ep1 == -1) + P1->mID = P2->ID; + else + P1->pID = P2->ID; + + if (ep2 == -1) + P2->mID = P1->ID; + else + P2->pID = P1->ID; + + m_NumConnections++; +} + +void ParticleGrid::DestroyConnection(Particle *P1,int ep1, Particle *P2, int ep2) +{ + if (ep1 == -1) + P1->mID = -1; + else + P1->pID = -1; + + if (ep2 == -1) + P2->mID = -1; + else + P2->pID = -1; + m_NumConnections--; +} + +void ParticleGrid::DestroyConnection(Particle *P1,int ep1) +{ + + Particle *P2 = 0; + if (ep1 == 1) + { + P2 = &m_Particles[P1->pID]; + P1->pID = -1; + } + else + { + P2 = &m_Particles[P1->mID]; + P1->mID = -1; + } + + if (P2->mID == P1->ID) + P2->mID = -1; + else + P2->pID = -1; + + m_NumConnections--; +} + +bool ParticleGrid::CheckConsistency() +{ + for (int i=0; iID != i) + { + std::cout << "Particle ID error!" << std::endl; + return false; + } + + if (p->mID!=-1) + { + Particle* p2 = &m_Particles[p->mID]; + if (p2->mID!=p->ID && p2->pID!=p->ID) + { + std::cout << "Connection inconsistent!" << std::endl; + return false; + } + } + if (p->pID!=-1) + { + Particle* p2 = &m_Particles[p->pID]; + if (p2->mID!=p->ID && p2->pID!=p->ID) + { + std::cout << "Connection inconsistent!" << std::endl; + return false; + } + } + } + return true; +} diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticleGrid.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticleGrid.h new file mode 100644 index 0000000000..8d8d73e5a8 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkParticleGrid.h @@ -0,0 +1,120 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef _PARTICLEGRID +#define _PARTICLEGRID + +// MITK +#include "MitkDiffusionImagingExports.h" +#include + +// ITK +#include + +namespace mitk +{ + +class MitkDiffusionImaging_EXPORT ParticleGrid +{ + +public: + + typedef itk::Image< float, 3 > ItkFloatImageType; + + int m_NumParticles; // number of particles + int m_NumConnections; // number of connections + int m_NumCellOverflows; // number of cell overflows + float m_ParticleLength; + + ParticleGrid(ItkFloatImageType* image, float particleLength); + ~ParticleGrid(); + + Particle* GetParticle(int ID); + + Particle* NewParticle(vnl_vector_fixed R); + bool TryUpdateGrid(int k); + void RemoveParticle(int k); + + void ComputeNeighbors(vnl_vector_fixed &R); + Particle* GetNextNeighbor(); + + void CreateConnection(Particle *P1,int ep1, Particle *P2, int ep2); + void DestroyConnection(Particle *P1,int ep1, Particle *P2, int ep2); + void DestroyConnection(Particle *P1,int ep1); + + bool CheckConsistency(); + +protected: + + bool ReallocateGrid(); + + std::vector< Particle* > m_Grid; // the grid + std::vector< Particle > m_Particles; // particle container + std::vector< int > m_OccupationCount; // number of particles per grid cell + + int m_ContainerCapacity; // maximal number of particles + + vnl_vector_fixed< int, 3 > m_GridSize; // grid dimensions + vnl_vector_fixed< float, 3 > m_GridScale; // scaling factor for grid + + int m_CellCapacity; // particle capacity of single cell in grid + + struct NeighborTracker // to run over the neighbors + { + std::vector< int > cellidx; + std::vector< int > cellidx_c; + int cellcnt; + int pcnt; + } m_NeighbourTracker; + +}; + +class MitkDiffusionImaging_EXPORT Track +{ +public: + std::vector< EndPoint > track; + float m_Energy; + float m_Probability; + int m_Length; + + Track() + { + track.resize(1000); + } + + ~Track(){} + + void clear() + { + m_Length = 0; + m_Energy = 0; + m_Probability = 1; + } + + bool isequal(Track& t) + { + for (int i = 0; i < m_Length;i++) + { + if (track[i].p != t.track[i].p || track[i].ep != t.track[i].ep) + return false; + } + return true; + } +}; + +} + +#endif diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkSimpSamp.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkSimpSamp.h new file mode 100644 index 0000000000..acf68a767d --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkSimpSamp.h @@ -0,0 +1,121 @@ +/*=================================================================== + +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 _SIMPSAMP +#define _SIMPSAMP + +#include +#include +#include + +using namespace std; + +namespace mitk +{ + +class MitkDiffusionImaging_EXPORT SimpSamp +{ + + float *P; + int cnt; + +public: + EndPoint* objs; + + + SimpSamp() + { + P = (float*) malloc(sizeof(float)*1000); + objs = (EndPoint*) malloc(sizeof(EndPoint)*1000); + } + ~SimpSamp() + { + free(P); + free(objs); + } + + inline void clear() + { + cnt = 1; + P[0] = 0; + } + + inline void add(float p, EndPoint obj) + { + P[cnt] = P[cnt-1] + p; + objs[cnt-1] = obj; + cnt++; + } + + inline int draw(float prob) + { + float r = prob*P[cnt-1]; + int j; + int rl = 1; + int rh = cnt-1; + while(rh != rl) + { + j = rl + (rh-rl)/2; + if (r < P[j]) + { + rh = j; + continue; + } + if (r > P[j]) + { + rl = j+1; + continue; + } + break; + } + return rh-1; + } + + inline EndPoint drawObj(float prob) + { + return objs[draw(prob)]; + } + + inline bool isempty() + { + if (cnt == 1) + return true; + else + return false; + } + + + float probFor(int idx) + { + return (P[idx+1]-P[idx])/P[cnt-1]; + } + + float probFor(EndPoint& t) + { + for (int i = 1; i< cnt;i++) + { + if (t == objs[i-1]) + return probFor(i-1); + } + return 0; + } +}; + +} + +#endif + + diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkSphereInterpolator.h b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkSphereInterpolator.h new file mode 100644 index 0000000000..7852beb200 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/GibbsTracking/mitkSphereInterpolator.h @@ -0,0 +1,260 @@ +/*=================================================================== + +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 _SPHEREINTERPOLATOR +#define _SPHEREINTERPOLATOR + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class MitkDiffusionImaging_EXPORT SphereInterpolator +{ + +public: + + int size; // size of LUT + int sN; // (sizeofLUT-1)/2 + int nverts; // number of data vertices + float beta; + float inva; + float b; + + float* barycoords; + int* indices; + int* idx; + float* interpw; + + SphereInterpolator(string lutPath) + { + if (lutPath.length()==0) + { + if (!LoadLookuptables()) + { + MITK_INFO << "SphereInterpolator: unable to load lookuptables"; + return; + } + } + else + { + if (!LoadLookuptables(lutPath)) + { + MITK_INFO << "SphereInterpolator: unable to load lookuptables"; + return; + } + } + + size = 301; + sN = (size-1)/2; + nverts = QBALL_ODFSIZE; + beta = 0.5; + + inva = (sqrt(1+beta)-sqrt(beta)); + b = 1/(1-sqrt(1/beta + 1)); + } + + ~SphereInterpolator() + { + delete[] barycoords; + delete[] indices; + } + + bool LoadLookuptables(string lutPath) + { + std::cout << "SphereInterpolator: loading lookuptables from custom path" << std::endl; + + QString path(lutPath.c_str()); path += "FiberTrackingLUTBaryCoords.bin"; + std::ifstream BaryCoordsStream; + BaryCoordsStream.open(path.toStdString().c_str(), ios::in | ios::binary); + if (BaryCoordsStream.is_open()) + { + float tmp; + barycoords = new float [1630818]; + BaryCoordsStream.seekg (0, ios::beg); + for (int i=0; i<1630818; i++) + { + BaryCoordsStream.read((char *)&tmp, sizeof(tmp)); + barycoords[i] = tmp; + } + BaryCoordsStream.close(); + } + else + return false; + + ifstream IndicesStream; + path = lutPath.c_str(); path += "FiberTrackingLUTIndices.bin"; + IndicesStream.open(path.toStdString().c_str(), ios::in | ios::binary); + if (IndicesStream.is_open()) + { + int tmp; + indices = new int [1630818]; + IndicesStream.seekg (0, ios::beg); + for (int i=0; i<1630818; i++) + { + IndicesStream.read((char *)&tmp, 4); + indices[i] = tmp; + } + IndicesStream.close(); + } + else + return false; + + return true; + } + + bool LoadLookuptables() + { + std::cout << "SphereInterpolator: loading lookuptables" << std::endl; + QString applicationDir = QCoreApplication::applicationDirPath(); + applicationDir.append("/"); + mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); + applicationDir.append("../"); + mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); + applicationDir.append("../../"); + mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); + applicationDir = QCoreApplication::applicationDirPath(); + applicationDir.append("/bin/"); + mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); + + string lutPath = mitk::StandardFileLocations::GetInstance()->FindFile("FiberTrackingLUTBaryCoords.bin"); + std::ifstream BaryCoordsStream; + BaryCoordsStream.open(lutPath.c_str(), ios::in | ios::binary); + if (BaryCoordsStream.is_open()) + { + float tmp; + barycoords = new float [1630818]; + BaryCoordsStream.seekg (0, ios::beg); + for (int i=0; i<1630818; i++) + { + BaryCoordsStream.read((char *)&tmp, sizeof(tmp)); + barycoords[i] = tmp; + } + BaryCoordsStream.close(); + } + else + return false; + + ifstream IndicesStream; + lutPath = mitk::StandardFileLocations::GetInstance()->FindFile("FiberTrackingLUTIndices.bin"); + IndicesStream.open(lutPath.c_str(), ios::in | ios::binary); + if (IndicesStream.is_open()) + { + int tmp; + indices = new int [1630818]; + IndicesStream.seekg (0, ios::beg); + for (int i=0; i<1630818; i++) + { + IndicesStream.read((char *)&tmp, 4); + indices[i] = tmp; + } + IndicesStream.close(); + } + else + return false; + + return true; + } + + inline void getInterpolation(vnl_vector_fixed N) + { + float nx = N[0]; + float ny = N[1]; + float nz = N[2]; + + if (nz > 0.5) + { + int x = float2int(nx); + int y = float2int(ny); + int i = 3*6*(x+y*size); // (:,1,x,y) + idx = indices+i; + interpw = barycoords +i; + return; + } + if (nz < -0.5) + { + int x = float2int(nx); + int y = float2int(ny); + int i = 3*(1+6*(x+y*size)); // (:,2,x,y) + idx = indices+i; + interpw = barycoords +i; + return; + } + if (nx > 0.5) + { + int z = float2int(nz); + int y = float2int(ny); + int i = 3*(2+6*(z+y*size)); // (:,2,x,y) + idx = indices+i; + interpw = barycoords +i; + return; + } + if (nx < -0.5) + { + int z = float2int(nz); + int y = float2int(ny); + int i = 3*(3+6*(z+y*size)); // (:,2,x,y) + idx = indices+i; + interpw = barycoords +i; + return; + } + if (ny > 0) + { + int x = float2int(nx); + int z = float2int(nz); + int i = 3*(4+6*(x+z*size)); // (:,1,x,y) + idx = indices+i; + interpw = barycoords +i; + return; + } + else + { + int x = float2int(nx); + int z = float2int(nz); + int i = 3*(5+6*(x+z*size)); // (:,1,x,y) + idx = indices+i; + interpw = barycoords +i; + return; + } + + } + + + inline float invrescale(float f) + { + float x = (fabs(f)-b)*inva; + if (f>0) + return (x*x-beta); + else + return beta - x*x; + } + + inline int float2int(float x) + { + return int((invrescale(x)+1)*sN-0.5); + + } + + +}; + +#endif diff --git a/Modules/DiffusionImaging/Tractography/GibbsTracking/pcRJMCMC.cpp b/Modules/DiffusionImaging/Tractography/GibbsTracking/pcRJMCMC.cpp deleted file mode 100644 index 2a8d03222d..0000000000 --- a/Modules/DiffusionImaging/Tractography/GibbsTracking/pcRJMCMC.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/*=================================================================== - -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 __pcRJMCMC_cpp__ -#define __pcRJMCMC_cpp__ - -//#include "mex.h" -//#include "matrix.h" -#define _USE_MATH_DEFINES -#include - -#include -#include -#include - -using namespace std; - -#define float float -#define PI M_PI -//#define INFINITY 99999999999.0 - -//#define TIMING - -#ifdef TIMING - -static struct timeval timeS; - -class PropStats -{ - int N; - int accept; -public: - void clear() { N = 0; accept = 0;} - void propose() {N++;} - void accepted() {accept++;} - - void report(const char *s) - { - //mexPrintf("%s #proposals: %8.2fk acceptratio: %.2f \% \n",s,1.0*N/1000.0,100.0*accept/N); - } -}; - - -class Timing -{ -public: - Timing() { time = 0; ncalls = 0;} - void clear() {time = 0; ncalls=0;} - - - long time; - int ncalls; - - void report(const char *s) - { - //mexPrintf("%s total: %10.2fms calls: %10.1fk t/call: %10.3fms \n",s,time/1000.0,1.0*ncalls/1000.0,1.0*time/ncalls); - } - - void report_time(const char *s) - { - //mexPrintf("%s: %.2fms \n",s,time/1000.0); - } - -}; - -inline void tic(Timing *t) -{ - gettimeofday( &timeS, NULL); - t->time -= (timeS.tv_sec*1000000 + timeS.tv_usec); - t->ncalls++; -} -inline void toc(Timing *t) -{ - gettimeofday( &timeS, NULL); - t->time += (timeS.tv_sec*1000000 + timeS.tv_usec); -} - -Timing externalenergy_time; -Timing internalenergy_time; -Timing odfeval_time; -Timing total_time; - -Timing shiftproposal_time; -Timing birthproposal_time; -Timing deathproposal_time; -Timing capproposal_time; -Timing lenproposal_time; -Timing connproposal_time; - -PropStats deathstats; -PropStats birthstats; -PropStats connstats; -PropStats shiftstats; -PropStats capstats; -PropStats lenstats; - - -#endif - - - -#include "MersenneTwister.h" -MTRand mtrand; -float *BESSEL_APPROXCOEFF; - - -#include "EnergyComputer_connec.cpp" -//#include "EnergyComputer_center.cpp" -//#include "RJMCMC_singlegradprop.cpp" -#include "RJMCMC_randshift.cpp" - - -//void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) -//{ - -// if(nrhs != 7 && nrhs != 8) { - -// printf(" wrong usage!!!.\n\n"); -// return; -// } else if(nlhs>3) { -// printf("Too many output arguments\n"); -// return; -// } - -// int pcnt = 0; -// const mxArray *Points; -// Points = prhs[pcnt++]; -// int numPoints = mxGetN(Points); -// float *points = (float*) mxGetData(Points); - -// const mxArray *DataImg; -// DataImg = prhs[pcnt++]; -// float *dimg = (float*) mxGetData(DataImg); -// const int *dsize = mxGetDimensions(DataImg); - -// const mxArray *Mask; -// Mask = prhs[pcnt++]; -// float *mask = (float*) mxGetData(Mask); -// const int *dmsize = mxGetDimensions(Mask); -// int mask_oversamp_mult = dmsize[0]/dsize[1]; - - - -// const mxArray *VoxSize; -// VoxSize = prhs[pcnt++]; -// double *voxsize = (double*) mxGetPr(VoxSize); - - - -// const mxArray *Params; -// Params = prhs[pcnt++]; -// double *params = (double*) mxGetPr(Params); - -// float Temp = (float) params[0]; -// int numit = (int) params[1]; -// float conprob = (float) params[2]; -// float particle_weight = (float) params[3]; -// float particle_width = (float) params[4]; -// float particle_len = (float) params[5]; -// float chempot_connection = (float) params[6]; -// float chempot_particle = (float) params[7]; -// float inex_balance = (float) params[8]; -// float chempot2 = (float) params[9]; -// float meanval_sq = (float) params[10]; - -// const mxArray *BesselExpansion; -// BesselExpansion = prhs[pcnt++]; -// BESSEL_APPROXCOEFF = (float*) mxGetData(BesselExpansion); - - - -// // read spherical-interpolator data - -// const mxArray *sinterpstruct = prhs[pcnt++]; -// mxArray *Indices = mxGetField(sinterpstruct,0,"indices"); -// mxArray *BaryCoords = mxGetField(sinterpstruct,0,"barycoords"); -// mxArray *Beta = mxGetField(sinterpstruct,0,"beta"); -// mxArray *NumInterpPoints = mxGetField(sinterpstruct,0,"numpoints"); - -// float *indimg = (float*) mxGetData(Indices); -// const int *isize = mxGetDimensions(Indices); -// int totsz = isize[0]*isize[1]*isize[2]*isize[3]; -// int *indeximg = (int*) malloc(sizeof(int)*totsz); -// for (int k =0;k < totsz;k++) -// indeximg[k] = int(indimg[k])-1; -// float *barycoords = (float*) mxGetData(BaryCoords); -// float *beta = (float*) mxGetData(Beta); -// int nip = int(*((float*)mxGetData(NumInterpPoints))); - -// SphereInterpolator *sinterp = new SphereInterpolator(barycoords,indeximg,nip,isize[2],beta[0]); - -// double breakhandle = 0; -// const mxArray *BreakHandle; -// if (nrhs == 8) -// { -// BreakHandle = prhs[pcnt++]; -// breakhandle = *mxGetPr(BreakHandle); -// } - -// #ifdef TIMING -// externalenergy_time.clear(); -// internalenergy_time.clear(); -// odfeval_time.clear(); -// total_time.clear(); - -// shiftproposal_time.clear(); -// birthproposal_time.clear(); -// deathproposal_time.clear(); -// capproposal_time.clear(); -// lenproposal_time.clear(); -// connproposal_time.clear(); - -// deathstats.clear(); -// birthstats.clear(); -// connstats.clear(); -// shiftstats.clear(); -// capstats.clear(); -// lenstats.clear(); -// #endif - - - -// float cellsize = 2*particle_len; -// float curv_hardthres = 0.7; - -// fprintf(stderr,"setting up MH-sampler \n"); fflush(stderr); -// RJMCMC sampler(points,numPoints, dimg, dsize, voxsize, cellsize); -// fprintf(stderr,"setting up Energy-computer \n"); fflush(stderr); -// EnergyComputer encomp(dimg,dsize,voxsize,sinterp,&(sampler.pcontainer),mask,mask_oversamp_mult); - -// fprintf(stderr,"setting up parameters\n"); fflush(stderr); -// sampler.setParameters(Temp,numit,conprob,particle_len,curv_hardthres,chempot_particle); -// sampler.setEnergyComputer(&encomp); -// encomp.setParameters(particle_weight,particle_width,chempot_connection*particle_len*particle_len,particle_len,curv_hardthres,inex_balance,chempot2,meanval_sq); - -// fprintf(stderr,"starting to iterate\n"); fflush(stderr); -// sampler.iterate(breakhandle); - -// int cnt = sampler.pcontainer.pcnt; -// #ifdef TIMING -// mexPrintf("\nEnergy\n------------------------\n"); -// externalenergy_time.report("external "); -// odfeval_time.report("odfeval "); -// internalenergy_time.report("internal "); -// total_time.report_time("total energy comp. "); - -// mexPrintf("overhead for proposals und stuff:%.1fms\n", -// (total_time.time-(externalenergy_time.time+odfeval_time.time+internalenergy_time.time))/1000.0); - -// mexPrintf("\nProposals\n------------------------\n"); -// birthproposal_time.report("birth "); -// deathproposal_time.report("death "); -// shiftproposal_time.report("shift "); -// connproposal_time.report("conne "); -//// capproposal_time.report("capch "); -//// lenproposal_time.report("length "); -// mexPrintf("\n"); -// birthstats.report("birth "); -// deathstats.report("death "); -// shiftstats.report("shift "); -// connstats.report("conne "); -//// lenstats.report("length "); -//// capstats.report("capch "); -// mexPrintf("\n"); - - - -// #endif - - -// int dims[] = {sampler.attrcnt, sampler.pcontainer.pcnt}; -// plhs[0] = mxCreateNumericArray(2,dims,mxGetClassID(Points),mxfloat); -// float *npoints = (float*) mxGetData(plhs[0]); -// sampler.writeout(npoints); - - -// delete sinterp; -// free(indeximg); - - -//} - -#endif - diff --git a/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.cpp b/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.cpp index b74a54a9bf..b4c969180e 100644 --- a/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.cpp +++ b/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.cpp @@ -1,653 +1,409 @@ /*=================================================================== 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 "itkGibbsTrackingFilter.h" -#include - -#include -#include "itkPointShell.h" - -#include "GibbsTracking/BuildFibres.cpp" - -#pragma GCC visibility push(default) -#include -#pragma GCC visibility pop - -#include -#include -#include - -#include -#include -#include +// MITK #include -#include -#include #include +#include +#include +#include +#include +#include -struct LessDereference { - template - bool operator()(const T * lhs, const T * rhs) const { - return *lhs < *rhs; - } -}; +// ITK +#include +#include + +// MISC +#include +#include +#include +#include namespace itk{ -template< class TInputOdfImage, class TInputROIImage > -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::GibbsTrackingFilter(): - m_TempStart(0.1), - m_TempEnd(0.001), - m_NumIt(500000), +template< class ItkQBallImageType > +GibbsTrackingFilter< ItkQBallImageType >::GibbsTrackingFilter(): + m_StartTemperature(0.1), + m_EndTemperature(0.001), + m_Iterations(500000), m_ParticleWeight(0), m_ParticleWidth(0), m_ParticleLength(0), - m_ChempotConnection(10), - m_ChempotParticle(0), + m_ConnectionPotential(10), m_InexBalance(0), - m_Chempot2(0.2), - m_FiberLength(10), + m_ParticlePotential(0.2), + m_MinFiberLength(10), m_AbortTracking(false), m_NumConnections(0), m_NumParticles(0), m_NumAcceptedFibers(0), m_CurrentStep(0), - m_SubtractMean(true), m_BuildFibers(false), - m_Sampler(NULL), m_Steps(10), - m_Memory(0), m_ProposalAcceptance(0), - m_GfaImage(NULL), - m_CurvatureHardThreshold(0.7), - m_Meanval_sq(0.0) + m_CurvatureThreshold(0.7), + m_DuplicateImage(true), + m_RandomSeed(-1), + m_ParameterFile(""), + m_LutPath("") { - //this->m_MeasurementFrame.set_identity(); - this->SetNumberOfRequiredInputs(2); //Filter needs a DWI image + a Mask Image -} -template< class TInputOdfImage, class TInputROIImage > -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::~GibbsTrackingFilter(){ - delete BESSEL_APPROXCOEFF; - if (m_Sampler!=NULL) - delete m_Sampler; } -template< class TInputOdfImage, class TInputROIImage > -void -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::ComputeFiberCorrelationOriginal(){ - float bD = 15; - - vnl_matrix_fixed bDir = - *itk::PointShell >::DistributePointShell(); - - const int N = QBALL_ODFSIZE; - - vnl_matrix_fixed C = bDir.transpose()*bDir; - vnl_matrix_fixed Q = C; - for(int i=0; i P = Q*Q; - - std::vector pointer; - pointer.reserve(N*N); - double * start = C.data_block(); - double * end = start + N*N; - for (double * iter = start; iter != end; ++iter) - { - pointer.push_back(iter); - } - std::sort(pointer.begin(), pointer.end(), LessDereference()); - - vnl_vector_fixed alpha; - vnl_vector_fixed beta; - for (int i=0; i alpha_0; - vnl_vector_fixed alpha_2; - vnl_vector_fixed alpha_4; - vnl_vector_fixed alpha_6; - for(int i=0; i T; - T.set_column(0,alpha_0); - T.set_column(1,alpha_2); - T.set_column(2,alpha_4); - T.set_column(3,alpha_6); - - vnl_vector_fixed coeff = vnl_matrix_inverse(T).pinverse()*beta; - BESSEL_APPROXCOEFF = new float[4]; - BESSEL_APPROXCOEFF[0] = coeff(0); - BESSEL_APPROXCOEFF[1] = coeff(1); - BESSEL_APPROXCOEFF[2] = coeff(2); - BESSEL_APPROXCOEFF[3] = coeff(3); - -// // OLD -// BESSEL_APPROXCOEFF[0] = 0,1982; -// BESSEL_APPROXCOEFF[1] = 0.3415; -// BESSEL_APPROXCOEFF[2] = -0.9515; -// BESSEL_APPROXCOEFF[3] = 1.3423; -} - -template< class TInputOdfImage, class TInputROIImage > -void -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::ComputeFiberCorrelation(){ - -// float bD = 15; - -// vnl_matrix_fixed bDir = -// *itk::PointShell >::DistributePointShell(); - -// const int N = QBALL_ODFSIZE; - -// vnl_matrix_fixed temp = bDir.transpose(); -// vnl_matrix_fixed C = temp*bDir; -// vnl_matrix_fixed Q = C; -// vnl_vector_fixed mean; -// for(int i=0; i repMean; -// for (int i=0; i P = Q*Q; - -// std::vector pointer; -// pointer.reserve(N*N); -// double * start = C.data_block(); -// double * end = start + N*N; -// for (double * iter = start; iter != end; ++iter) -// { -// pointer.push_back(iter); -// } -// std::sort(pointer.begin(), pointer.end(), LessDereference()); - -// vnl_vector_fixed alpha; -// vnl_vector_fixed beta; -// for (int i=0; im_Meanval_sq = (sum*sum)/N; - -// vnl_vector_fixed alpha_0; -// vnl_vector_fixed alpha_2; -// vnl_vector_fixed alpha_4; -// vnl_vector_fixed alpha_6; -// for(int i=0; i T; -// T.set_column(0,alpha_0); -// T.set_column(1,alpha_2); -// T.set_column(2,alpha_4); -// T.set_column(3,alpha_6); - -// vnl_vector_fixed coeff = vnl_matrix_inverse(T).pinverse()*beta; - -// MITK_INFO << "itkGibbsTrackingFilter: Bessel oefficients: " << coeff; - - BESSEL_APPROXCOEFF = new float[4]; - -// BESSEL_APPROXCOEFF[0] = coeff(0); -// BESSEL_APPROXCOEFF[1] = coeff(1); -// BESSEL_APPROXCOEFF[2] = coeff(2); -// BESSEL_APPROXCOEFF[3] = coeff(3); - BESSEL_APPROXCOEFF[0] = -0.1714; - BESSEL_APPROXCOEFF[1] = 0.5332; - BESSEL_APPROXCOEFF[2] = -1.4889; - BESSEL_APPROXCOEFF[3] = 2.0389; -} - -// build fibers from tracking result -template< class TInputOdfImage, class TInputROIImage > -void -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::BuildFibers(float* points, int numPoints) +template< class ItkQBallImageType > +GibbsTrackingFilter< ItkQBallImageType >::~GibbsTrackingFilter() { - double spacing[3]; - spacing[0] = m_ItkQBallImage->GetSpacing().GetElement(0); - spacing[1] = m_ItkQBallImage->GetSpacing().GetElement(1); - spacing[2] = m_ItkQBallImage->GetSpacing().GetElement(2); - - m_FiberPolyData = FiberPolyDataType::New(); - - // initialize array of particles - FiberBuilder fiberBuilder(points, numPoints, spacing, m_ItkQBallImage); - // label the particles according to fiber affiliation and return polydata - m_FiberPolyData = fiberBuilder.iterate(m_FiberLength); - m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines(); - MITK_INFO << "itkGibbsTrackingFilter: " << m_NumAcceptedFibers << " accepted"; } // fill output fiber bundle datastructure -template< class TInputOdfImage, class TInputROIImage > -typename GibbsTrackingFilter< TInputOdfImage, TInputROIImage >::FiberPolyDataType -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::GetFiberBundle() +template< class ItkQBallImageType > +typename GibbsTrackingFilter< ItkQBallImageType >::FiberPolyDataType GibbsTrackingFilter< ItkQBallImageType >::GetFiberBundle() { if (!m_AbortTracking) { m_BuildFibers = true; while (m_BuildFibers){} } return m_FiberPolyData; } -// get memory allocated for particle grid -template< class TInputOdfImage, class TInputROIImage > -float -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::GetMemoryUsage() -{ - if (m_Sampler!=NULL) - return m_Sampler->m_ParticleGrid.GetMemoryUsage(); - return 0; -} - -template< class TInputOdfImage, class TInputROIImage > +template< class ItkQBallImageType > bool -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > +GibbsTrackingFilter< ItkQBallImageType > ::EstimateParticleWeight() { - MITK_INFO << "itkGibbsTrackingFilter: estimating particle weight"; + MITK_INFO << "GibbsTrackingFilter: estimating particle weight"; typedef itk::DiffusionQballGeneralizedFaImageFilter GfaFilterType; GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); - gfaFilter->SetInput(m_ItkQBallImage); + gfaFilter->SetInput(m_QBallImage); gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD); gfaFilter->Update(); - m_GfaImage = gfaFilter->GetOutput(); + ItkFloatImageType::Pointer gfaImage = gfaFilter->GetOutput(); float samplingStart = 1.0; float samplingStop = 0.66; - // copy GFA image (original should not be changed) - typedef itk::ImageDuplicator< GfaImageType > DuplicateFilterType; - DuplicateFilterType::Pointer duplicator = DuplicateFilterType::New(); - duplicator->SetInputImage( m_GfaImage ); - duplicator->Update(); - m_GfaImage = duplicator->GetOutput(); - - //// GFA iterator //// - typedef ImageRegionIterator< GfaImageType > GfaIteratorType; - GfaIteratorType gfaIt(m_GfaImage, m_GfaImage->GetLargestPossibleRegion() ); - - //// Mask iterator //// - typedef ImageRegionConstIterator< MaskImageType > MaskIteratorType; - MaskIteratorType maskIt(m_MaskImage, m_MaskImage->GetLargestPossibleRegion() ); - - // set unmasked region of gfa image to 0 - gfaIt.GoToBegin(); - maskIt.GoToBegin(); - while( !gfaIt.IsAtEnd() ) - { - if(maskIt.Get()<=0) - gfaIt.Set(0); - ++gfaIt; - ++maskIt; - } + // GFA iterator + typedef ImageRegionIterator< ItkFloatImageType > GfaIteratorType; + GfaIteratorType gfaIt(gfaImage, gfaImage->GetLargestPossibleRegion() ); - // rescale gfa image to [0,1] - typedef itk::RescaleIntensityImageFilter< GfaImageType, GfaImageType > RescaleFilterType; - RescaleFilterType::Pointer rescaleFilter = RescaleFilterType::New(); - rescaleFilter->SetInput( m_GfaImage ); - rescaleFilter->SetOutputMaximum( samplingStart ); - rescaleFilter->SetOutputMinimum( 0 ); - rescaleFilter->Update(); - m_GfaImage = rescaleFilter->GetOutput(); - gfaIt = GfaIteratorType(m_GfaImage, m_GfaImage->GetLargestPossibleRegion() ); + // Mask iterator + typedef ImageRegionConstIterator< ItkFloatImageType > MaskIteratorType; + MaskIteratorType mit(m_MaskImage, m_MaskImage->GetLargestPossibleRegion() ); - //// Input iterator //// - typedef ImageRegionConstIterator< InputQBallImageType > InputIteratorType; - InputIteratorType git(m_ItkQBallImage, m_ItkQBallImage->GetLargestPossibleRegion() ); + // Input iterator + typedef ImageRegionConstIterator< ItkQBallImageType > InputIteratorType; + InputIteratorType it(m_QBallImage, m_QBallImage->GetLargestPossibleRegion() ); float upper = 0; int count = 0; for(float thr=samplingStart; thr>samplingStop; thr-=0.01) { - git.GoToBegin(); + it.GoToBegin(); + mit.GoToBegin(); gfaIt.GoToBegin(); + while( !gfaIt.IsAtEnd() ) { - if(gfaIt.Get()>thr) + if(gfaIt.Get()>thr && mit.Get()>0) { - itk::OrientationDistributionFunction odf(git.Get().GetDataPointer()); + itk::OrientationDistributionFunction odf(it.Get().GetDataPointer()); upper += odf.GetMaxValue()-odf.GetMeanValue(); - ++count; } + ++it; + ++mit; ++gfaIt; - ++git; } } + if (count>0) upper /= count; else return false; m_ParticleWeight = upper/6; return true; } // perform global tracking -template< class TInputOdfImage, class TInputROIImage > -void -GibbsTrackingFilter< TInputOdfImage, TInputROIImage > -::GenerateData(){ - - // input qball image - m_ItkQBallImage = dynamic_cast(this->GetInput(0)); - m_NumAcceptedFibers = 0; - - // approximationscoeffizienten der - // teilchenkorrelationen im orientierungsraum - // 4er vektor - //ComputeFiberCorrelationOriginal(); - ComputeFiberCorrelation(); - - // image sizes and spacing - int qBallImageSize[4] = {QBALL_ODFSIZE, - m_ItkQBallImage->GetLargestPossibleRegion().GetSize().GetElement(0), - m_ItkQBallImage->GetLargestPossibleRegion().GetSize().GetElement(1), - m_ItkQBallImage->GetLargestPossibleRegion().GetSize().GetElement(2)}; - double qBallImageSpacing[3] = {m_ItkQBallImage->GetSpacing().GetElement(0),m_ItkQBallImage->GetSpacing().GetElement(1),m_ItkQBallImage->GetSpacing().GetElement(2)}; - - // make sure image has enough slices - if (qBallImageSize[1]<3 || qBallImageSize[2]<3 || qBallImageSize[3]<3) +template< class ItkQBallImageType > +void GibbsTrackingFilter< ItkQBallImageType >::GenerateData() +{ + // check if input is qball or tensor image and generate qball if necessary + if (m_QBallImage.IsNull() && m_TensorImage.IsNotNull()) { - MITK_INFO << "itkGibbsTrackingFilter: image size < 3 not supported"; - m_AbortTracking = true; + TensorImageToQBallImageFilter::Pointer filter = TensorImageToQBallImageFilter::New(); + filter->SetInput( m_TensorImage ); + filter->Update(); + m_QBallImage = filter->GetOutput(); } - - // calculate rotation matrix - vnl_matrix_fixed directionMatrix = m_ItkQBallImage->GetDirection().GetVnlMatrix(); - vnl_vector_fixed d0 = directionMatrix.get_column(0); d0.normalize(); - vnl_vector_fixed d1 = directionMatrix.get_column(1); d1.normalize(); - vnl_vector_fixed d2 = directionMatrix.get_column(2); d2.normalize(); - directionMatrix.set_column(0, d0); - directionMatrix.set_column(1, d1); - directionMatrix.set_column(2, d2); - vnl_matrix_fixed I = directionMatrix*directionMatrix.transpose(); - if(!I.is_identity(mitk::eps)){ - MITK_INFO << "itkGibbsTrackingFilter: image direction is not a rotation matrix. Tracking not possible!"; - m_AbortTracking = true; - } - - // generate local working copy of image buffer - int bufferSize = qBallImageSize[0]*qBallImageSize[1]*qBallImageSize[2]*qBallImageSize[3]; - float* qBallImageBuffer = (float*) m_ItkQBallImage->GetBufferPointer(); - float* workingQballImage = new float[bufferSize]; - for (int i=0; i0 && i%qBallImageSize[0] == 0 && i>0) - { - sum /= qBallImageSize[0]; - for (int j=i-qBallImageSize[0]; j DuplicateFilterType; + typename DuplicateFilterType::Pointer duplicator = DuplicateFilterType::New(); + duplicator->SetInputImage( m_QBallImage ); + duplicator->Update(); + m_QBallImage = duplicator->GetOutput(); } - // mask image - int maskImageSize[3]; - float *mask; - if(m_MaskImage.IsNotNull()) - { - mask = (float*) m_MaskImage->GetBufferPointer(); - maskImageSize[0] = m_MaskImage->GetLargestPossibleRegion().GetSize().GetElement(0); - maskImageSize[1] = m_MaskImage->GetLargestPossibleRegion().GetSize().GetElement(1); - maskImageSize[2] = m_MaskImage->GetLargestPossibleRegion().GetSize().GetElement(2); - } - else - { - mask = 0; - maskImageSize[0] = qBallImageSize[1]; - maskImageSize[1] = qBallImageSize[2]; - maskImageSize[2] = qBallImageSize[3]; - } - int mask_oversamp_mult = maskImageSize[0]/qBallImageSize[1]; - - // load lookuptable - QString applicationDir = QCoreApplication::applicationDirPath(); - applicationDir.append("/"); - mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); - applicationDir.append("../"); - mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); - applicationDir.append("../../"); - mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch( applicationDir.toStdString().c_str(), false ); - - std::string lutPath = mitk::StandardFileLocations::GetInstance()->FindFile("FiberTrackingLUTBaryCoords.bin"); - ifstream BaryCoords; - BaryCoords.open(lutPath.c_str(), ios::in | ios::binary); - float* coords; - if (BaryCoords.is_open()) - { - float tmp; - coords = new float [1630818]; - BaryCoords.seekg (0, ios::beg); - for (int i=0; i<1630818; i++) - { - BaryCoords.read((char *)&tmp, sizeof(tmp)); - coords[i] = tmp; - } - BaryCoords.close(); - } - else + // perform mean subtraction on odfs + typedef ImageRegionIterator< ItkQBallImageType > InputIteratorType; + InputIteratorType it(m_QBallImage, m_QBallImage->GetLargestPossibleRegion() ); + it.GoToBegin(); + while (!it.IsAtEnd()) { - MITK_INFO << "itkGibbsTrackingFilter: unable to open barycoords file"; - m_AbortTracking = true; + itk::OrientationDistributionFunction odf(it.Get().GetDataPointer()); + float mean = odf.GetMeanValue(); + odf -= mean; + it.Set(odf.GetDataPointer()); + ++it; } - ifstream Indices; - lutPath = mitk::StandardFileLocations::GetInstance()->FindFile("FiberTrackingLUTIndices.bin"); - Indices.open(lutPath.c_str(), ios::in | ios::binary); - int* ind; - if (Indices.is_open()) - { - int tmp; - ind = new int [1630818]; - Indices.seekg (0, ios::beg); - for (int i=0; i<1630818; i++) - { - Indices.read((char *)&tmp, 4); - ind[i] = tmp; - } - Indices.close(); - } - else - { - MITK_INFO << "itkGibbsTrackingFilter: unable to open indices file"; - m_AbortTracking = true; - } + // check if mask image is given if it needs resampling + PrepareMaskImage(); - // initialize sphere interpolator with lookuptables - SphereInterpolator *sinterp = new SphereInterpolator(coords, ind, QBALL_ODFSIZE, 301, 0.5); + // load parameter file + LoadParameters(m_ParameterFile); - // get paramters + // prepare parameters float minSpacing; - if(qBallImageSpacing[0]GetSpacing()[0]GetSpacing()[1] && m_QBallImage->GetSpacing()[0]GetSpacing()[2]) + minSpacing = m_QBallImage->GetSpacing()[0]; + else if (m_QBallImage->GetSpacing()[1] < m_QBallImage->GetSpacing()[2]) + minSpacing = m_QBallImage->GetSpacing()[1]; else - minSpacing = qBallImageSpacing[2]; + minSpacing = m_QBallImage->GetSpacing()[2]; if(m_ParticleLength == 0) m_ParticleLength = 1.5*minSpacing; if(m_ParticleWidth == 0) m_ParticleWidth = 0.5*minSpacing; if(m_ParticleWeight == 0) if (!EstimateParticleWeight()) { - MITK_INFO << "itkGibbsTrackingFilter: could not estimate particle weight!"; + MITK_INFO << "GibbsTrackingFilter: could not estimate particle weight. using default value."; m_ParticleWeight = 0.0001; } - MITK_INFO << "itkGibbsTrackingFilter: particle Weight: " << m_ParticleWeight; - MITK_INFO << "itkGibbsTrackingFilter: iterations: " << m_NumIt; - m_CurrentStep = 0; - m_Memory = 0; - - float cellsize = 2*m_ParticleLength; - float alpha = log(m_TempEnd/m_TempStart); - m_Steps = m_NumIt/10000; + float alpha = log(m_EndTemperature/m_StartTemperature); + m_Steps = m_Iterations/10000; if (m_Steps<10) m_Steps = 10; - if (m_Steps>m_NumIt) + if (m_Steps>m_Iterations) { - MITK_INFO << "itkGibbsTrackingFilter: not enough iterations!"; + MITK_INFO << "GibbsTrackingFilter: not enough iterations!"; m_AbortTracking = true; } - MITK_INFO << "itkGibbsTrackingFilter: steps: " << m_Steps; - - if (m_CurvatureHardThreshold < mitk::eps) - m_CurvatureHardThreshold = 0; - MITK_INFO << "itkGibbsTrackingFilter: curvature threshold: " << m_CurvatureHardThreshold; - unsigned long singleIts = (unsigned long)((1.0*m_NumIt) / (1.0*m_Steps)); - - // setup metropolis hastings sampler - MITK_INFO << "itkGibbsTrackingFilter: setting up MH-sampler"; - if (m_Sampler!=NULL) - delete m_Sampler; - m_Sampler = new RJMCMC(NULL, 0, workingQballImage, qBallImageSize, qBallImageSpacing, cellsize); - - // setup energy computer - MITK_INFO << "itkGibbsTrackingFilter: setting up Energy-computer"; - EnergyComputer encomp(workingQballImage,qBallImageSize,qBallImageSpacing,sinterp,&(m_Sampler->m_ParticleGrid),mask,mask_oversamp_mult, directionMatrix); - encomp.setParameters(m_ParticleWeight,m_ParticleWidth,m_ChempotConnection*m_ParticleLength*m_ParticleLength,m_ParticleLength,m_CurvatureHardThreshold,m_InexBalance,m_Chempot2, m_Meanval_sq); - m_Sampler->SetEnergyComputer(&encomp); - m_Sampler->SetParameters(m_TempStart,singleIts,m_ParticleLength,m_CurvatureHardThreshold,m_ChempotParticle); + if (m_CurvatureThreshold < mitk::eps) + m_CurvatureThreshold = 0; + unsigned long singleIts = (unsigned long)((1.0*m_Iterations) / (1.0*m_Steps)); + + // seed random generators + Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New(); + if (m_RandomSeed>-1) + randGen->SetSeed(m_RandomSeed); + else + randGen->SetSeed(); + + // load sphere interpolator to evaluate the ODFs + SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath); + + // initialize the actual tracking components (ParticleGrid, Metropolis Hastings Sampler and Energy Computer) + ParticleGrid* particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength); + + EnergyComputer* encomp = new EnergyComputer(m_QBallImage, m_MaskImage, particleGrid, interpolator, randGen); + encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential); + + MetropolisHastingsSampler* sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold); + + MITK_INFO << "----------------------------------------"; + MITK_INFO << "Iterations: " << m_Iterations; + MITK_INFO << "Steps: " << m_Steps; + MITK_INFO << "Particle length: " << m_ParticleLength; + MITK_INFO << "Particle width: " << m_ParticleWidth; + MITK_INFO << "Particle weight: " << m_ParticleWeight; + MITK_INFO << "Start temperature: " << m_StartTemperature; + MITK_INFO << "End temperature: " << m_EndTemperature; + MITK_INFO << "In/Ex balance: " << m_InexBalance; + MITK_INFO << "Min. fiber length: " << m_MinFiberLength; + MITK_INFO << "Curvature threshold: " << m_CurvatureThreshold; + MITK_INFO << "Random seed: " << m_RandomSeed; + MITK_INFO << "----------------------------------------"; // main loop - for( int step = 0; step < m_Steps; step++ ) + m_NumAcceptedFibers = 0; + unsigned long counter = 1; + for( m_CurrentStep = 1; m_CurrentStep <= m_Steps; m_CurrentStep++ ) { - if (m_AbortTracking) - break; - - m_CurrentStep = step+1; - float temperature = m_TempStart * exp(alpha*(((1.0)*step)/((1.0)*m_Steps))); - - m_Sampler->SetTemperature(temperature); - m_Sampler->Iterate(&m_ProposalAcceptance, &m_NumConnections, &m_NumParticles, &m_AbortTracking); - - MITK_INFO << "itkGibbsTrackingFilter: proposal acceptance: " << 100*m_ProposalAcceptance << "%"; - MITK_INFO << "itkGibbsTrackingFilter: particles: " << m_NumParticles; - MITK_INFO << "itkGibbsTrackingFilter: connections: " << m_NumConnections; - MITK_INFO << "itkGibbsTrackingFilter: progress: " << 100*(float)step/m_Steps << "%"; - - if (m_BuildFibers) - { - int numPoints = m_Sampler->m_ParticleGrid.pcnt; - float* points = new float[numPoints*m_Sampler->m_NumAttributes]; - m_Sampler->WriteOutParticles(points); - BuildFibers(points, numPoints); - delete points; - m_BuildFibers = false; - } - } + // update temperatur for simulated annealing process + float temperature = m_StartTemperature * exp(alpha*(((1.0)*m_CurrentStep)/((1.0)*m_Steps))); + sampler->SetTemperature(temperature); - int numPoints = m_Sampler->m_ParticleGrid.pcnt; - float* points = new float[numPoints*m_Sampler->m_NumAttributes]; - m_Sampler->WriteOutParticles(points); - BuildFibers(points, numPoints); - delete points; + for (unsigned long i=0; iMakeProposal(); + + if (m_BuildFibers || (i==singleIts-1 && m_CurrentStep==m_Steps)) + { + m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/counter; + m_NumParticles = particleGrid->m_NumParticles; + m_NumConnections = particleGrid->m_NumConnections; + + FiberBuilder fiberBuilder(particleGrid, m_MaskImage); + m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength); + m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines(); + m_BuildFibers = false; + } + counter++; + } + + m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/counter; + m_NumParticles = particleGrid->m_NumParticles; + m_NumConnections = particleGrid->m_NumConnections; - delete sinterp; - delete coords; - delete ind; - delete workingQballImage; + MITK_INFO << "GibbsTrackingFilter: proposal acceptance: " << 100*m_ProposalAcceptance << "%"; + MITK_INFO << "GibbsTrackingFilter: particles: " << m_NumParticles; + MITK_INFO << "GibbsTrackingFilter: connections: " << m_NumConnections; + MITK_INFO << "GibbsTrackingFilter: progress: " << 100*(float)m_CurrentStep/m_Steps << "%"; + MITK_INFO << "GibbsTrackingFilter: cell overflows: " << particleGrid->m_NumCellOverflows; + MITK_INFO << "----------------------------------------"; + + if (m_AbortTracking) + break; + } + + delete sampler; + delete encomp; + delete interpolator; + delete particleGrid; m_AbortTracking = true; m_BuildFibers = false; - MITK_INFO << "itkGibbsTrackingFilter: done generate data"; + MITK_INFO << "GibbsTrackingFilter: done generate data"; +} + +template< class ItkQBallImageType > +void GibbsTrackingFilter< ItkQBallImageType >::PrepareMaskImage() +{ + if(m_MaskImage.IsNull()) + { + MITK_INFO << "GibbsTrackingFilter: generating default mask image"; + m_MaskImage = ItkFloatImageType::New(); + m_MaskImage->SetSpacing( m_QBallImage->GetSpacing() ); + m_MaskImage->SetOrigin( m_QBallImage->GetOrigin() ); + m_MaskImage->SetDirection( m_QBallImage->GetDirection() ); + m_MaskImage->SetRegions( m_QBallImage->GetLargestPossibleRegion() ); + m_MaskImage->Allocate(); + m_MaskImage->FillBuffer(1.0); + } + else if ( m_MaskImage->GetLargestPossibleRegion().GetSize()[0]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[0] || + m_MaskImage->GetLargestPossibleRegion().GetSize()[1]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[1] || + m_MaskImage->GetLargestPossibleRegion().GetSize()[2]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[2] || + m_MaskImage->GetSpacing()[0]!=m_QBallImage->GetSpacing()[0] || + m_MaskImage->GetSpacing()[1]!=m_QBallImage->GetSpacing()[1] || + m_MaskImage->GetSpacing()[2]!=m_QBallImage->GetSpacing()[2] ) + { + MITK_INFO << "GibbsTrackingFilter: resampling mask image"; + typedef itk::ResampleImageFilter< ItkFloatImageType, ItkFloatImageType, float > ResamplerType; + ResamplerType::Pointer resampler = ResamplerType::New(); + resampler->SetOutputSpacing( m_QBallImage->GetSpacing() ); + resampler->SetOutputOrigin( m_QBallImage->GetOrigin() ); + resampler->SetOutputDirection( m_QBallImage->GetDirection() ); + resampler->SetSize( m_QBallImage->GetLargestPossibleRegion().GetSize() ); + + resampler->SetInput( m_MaskImage ); + resampler->SetDefaultPixelValue(1.0); + resampler->Update(); + m_MaskImage = resampler->GetOutput(); + MITK_INFO << "GibbsTrackingFilter: resampling finished"; + } +} + +// load current tracking paramters from xml file (.gtp) +template< class ItkQBallImageType > +bool GibbsTrackingFilter< ItkQBallImageType >::LoadParameters(std::string filename) +{ + m_AbortTracking = true; + try + { + if( filename.length()==0 ) + { + m_AbortTracking = false; + return true; + } + + MITK_INFO << "GibbsTrackingFilter: loading parameter file " << filename; + + TiXmlDocument doc( filename ); + doc.LoadFile(); + + TiXmlHandle hDoc(&doc); + TiXmlElement* pElem; + TiXmlHandle hRoot(0); + + pElem = hDoc.FirstChildElement().Element(); + hRoot = TiXmlHandle(pElem); + pElem = hRoot.FirstChildElement("parameter_set").Element(); + + QString iterations(pElem->Attribute("iterations")); + m_Iterations = iterations.toULong(); + + QString particleLength(pElem->Attribute("particle_length")); + m_ParticleLength = particleLength.toFloat(); + + QString particleWidth(pElem->Attribute("particle_width")); + m_ParticleWidth = particleWidth.toFloat(); + + QString partWeight(pElem->Attribute("particle_weight")); + m_ParticleWeight = partWeight.toFloat(); + + QString startTemp(pElem->Attribute("temp_start")); + m_StartTemperature = startTemp.toFloat(); + + QString endTemp(pElem->Attribute("temp_end")); + m_EndTemperature = endTemp.toFloat(); + + QString inExBalance(pElem->Attribute("inexbalance")); + m_InexBalance = inExBalance.toFloat(); + + QString fiberLength(pElem->Attribute("fiber_length")); + m_MinFiberLength = fiberLength.toFloat(); + + QString curvThres(pElem->Attribute("curvature_threshold")); + m_CurvatureThreshold = cos(curvThres.toFloat()*M_PI/180); + m_AbortTracking = false; + MITK_INFO << "GibbsTrackingFilter: parameter file loaded successfully"; + return true; + } + catch(...) + { + MITK_INFO << "GibbsTrackingFilter: could not load parameter file"; + return false; + } } + } diff --git a/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.h b/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.h index bfc43e1b10..81cbe592f0 100644 --- a/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.h +++ b/Modules/DiffusionImaging/Tractography/itkGibbsTrackingFilter.h @@ -1,197 +1,142 @@ /*=================================================================== 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. ===================================================================*/ #ifndef itkGibbsTrackingFilter_h #define itkGibbsTrackingFilter_h -#include "itkProcessObject.h" -#include "itkVectorContainer.h" -#include "itkImage.h" +// MITK +#include -#include "GibbsTracking/pcRJMCMC.cpp" -#include "GibbsTracking/auxilary_classes.cpp" +// ITK +#include +#include +#include +#include -#include -#include +// VTK #include #include #include #include #include namespace itk{ - template< class TInputQBallImage, class TInputROIImage > - class GibbsTrackingFilter : - public ProcessObject{ - public: +template< class ItkQBallImageType > +class GibbsTrackingFilter : public ProcessObject +{ +public: typedef GibbsTrackingFilter Self; typedef ProcessObject Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; - itkNewMacro(Self); - itkTypeMacro( GibbsTrackingFilter, ProcessObject ); + itkNewMacro(Self) + itkTypeMacro( GibbsTrackingFilter, ProcessObject ) + + typedef Image< DiffusionTensor3D, 3 > ItkTensorImage; + typedef typename ItkQBallImageType::Pointer ItkQBallImageTypePointer; + typedef Image< float, 3 > ItkFloatImageType; + typedef vtkSmartPointer< vtkPolyData > FiberPolyDataType; + + // parameter setter + itkSetMacro( StartTemperature, float ) + itkSetMacro( EndTemperature, float ) + itkSetMacro( Iterations, unsigned long ) + itkSetMacro( ParticleWeight, float ) + itkSetMacro( ParticleWidth, float ) + itkSetMacro( ParticleLength, float ) + itkSetMacro( ConnectionPotential, float ) + itkSetMacro( InexBalance, float ) + itkSetMacro( ParticlePotential, float ) + itkSetMacro( MinFiberLength, int ) + itkSetMacro( AbortTracking, bool ) + itkSetMacro( CurvatureThreshold, float) + itkSetMacro( DuplicateImage, bool ) + itkSetMacro( RandomSeed, int ) + itkSetMacro( ParameterFile, std::string ) + itkSetMacro( LutPath, std::string ) + + // getter + itkGetMacro( ParticleWeight, float ) + itkGetMacro( ParticleWidth, float ) + itkGetMacro( ParticleLength, float ) + itkGetMacro( CurrentStep, unsigned long ) + itkGetMacro( NumParticles, int ) + itkGetMacro( NumConnections, int ) + itkGetMacro( NumAcceptedFibers, int ) + itkGetMacro( ProposalAcceptance, float ) + itkGetMacro( Steps, unsigned int) + + // input data + itkSetMacro(QBallImage, typename ItkQBallImageType::Pointer) + itkSetMacro(MaskImage, ItkFloatImageType::Pointer) + itkSetMacro(TensorImage, ItkTensorImage::Pointer) - /** Types for the DWI Input Image **/ - typedef TInputQBallImage InputQBallImageType; - - /** Types for the Mask Image **/ - typedef TInputROIImage MaskImageType; - typedef typename MaskImageType::Pointer MaskImageTypePointer; - - typedef vtkSmartPointer< vtkPolyData > FiberPolyDataType; - - typedef Image< float, 3 > GfaImageType; - typedef typename GfaImageType::Pointer GfaImageTypePointer; - - itkSetMacro( TempStart, float ); - itkGetMacro( TempStart, float ); - - itkSetMacro( TempEnd, float ); - itkGetMacro( TempEnd, float ); - - itkSetMacro( NumIt, unsigned long ); - itkGetMacro( NumIt, unsigned long ); - - itkSetMacro( ParticleWeight, float ); - itkGetMacro( ParticleWeight, float ); - - /** width of particle sigma (std-dev of gaussian around center) **/ - itkSetMacro( ParticleWidth, float ); - itkGetMacro( ParticleWidth, float ); - - /** length of particle from midpoint to ends **/ - itkSetMacro( ParticleLength, float ); - itkGetMacro( ParticleLength, float ); - - itkSetMacro( ChempotConnection, float ); - itkGetMacro( ChempotConnection, float ); - - itkSetMacro( ChempotParticle, float ); - itkGetMacro( ChempotParticle, float ); - - itkSetMacro( InexBalance, float ); - itkGetMacro( InexBalance, float ); - - itkSetMacro( Chempot2, float ); - itkGetMacro( Chempot2, float ); - - itkSetMacro( FiberLength, int ); - itkGetMacro( FiberLength, int ); - - itkSetMacro( AbortTracking, bool ); - itkGetMacro( AbortTracking, bool ); - - itkSetMacro( CurrentStep, unsigned long ); - itkGetMacro( CurrentStep, unsigned long ); - - itkSetMacro( SubtractMean, bool); - itkGetMacro( SubtractMean, bool); - - itkSetMacro( CurvatureHardThreshold, float); - itkGetMacro( CurvatureHardThreshold, float); - - /** Set/Get the Odf Input Image **/ - itkSetInputMacro(OdfImage, InputQBallImageType, 0); - itkGetInputMacro(OdfImage, InputQBallImageType, 0); - - /** Set/Get the Input mask image **/ - itkSetMacro(MaskImage, MaskImageTypePointer); - itkGetMacro(MaskImage, MaskImageTypePointer); - - itkSetMacro(GfaImage, GfaImageTypePointer); - itkGetMacro(GfaImage, GfaImageTypePointer); - - itkGetMacro(NumParticles, unsigned long); - itkGetMacro(NumConnections, unsigned long); - itkGetMacro(NumAcceptedFibers, int); - itkGetMacro(ProposalAcceptance, float); - itkGetMacro(Steps, unsigned int); - - /** Entry Point For the Algorithm: Is invoked when Update() is called - either directly or through itk pipeline propagation - **/ void GenerateData(); - /** override the Process Object Update because we don't have a - dataobject as an outpgnome themeut. We can change this later by wrapping the - tractcontainer in a dataobject decorator and letting the Superclass - know about it. - **/ - struct StochasticTractGenerationCallbackStruct{ - Pointer Filter; - }; - virtual void Update(){ - this->GenerateData(); + this->GenerateData(); } FiberPolyDataType GetFiberBundle(); - float GetMemoryUsage(); - bool EstimateParticleWeight(); - protected: +protected: GibbsTrackingFilter(); virtual ~GibbsTrackingFilter(); - - void ComputeFiberCorrelation(); - void ComputeFiberCorrelationOriginal(); - - void BuildFibers(float* points, int numPoints); + bool EstimateParticleWeight(); + void PrepareMaskImage(); + bool LoadParameters(std::string filename); // Input Images - typename InputQBallImageType::Pointer m_ItkQBallImage; - typename MaskImageType::Pointer m_MaskImage; - typename GfaImageType::Pointer m_GfaImage; + typename ItkQBallImageType::Pointer m_QBallImage; + typename ItkFloatImageType::Pointer m_MaskImage; + typename ItkTensorImage::Pointer m_TensorImage; // Tracking parameters - float m_TempStart; // Start temperature - float m_TempEnd; // End temperature - unsigned long m_NumIt; // Total number of iterations - unsigned long m_CurrentStep; // current tracking step - float m_ParticleWeight; //w (unitless) - float m_ParticleWidth; //sigma (mm) - float m_ParticleLength; // ell (mm) - float m_ChempotConnection; // gross L (chemisches potential) - float m_ChempotParticle;// unbenutzt (immer null, wenn groesser dann insgesamt weniger teilchen) - float m_InexBalance; // gewichtung zwischen den lambdas - // -5 ... 5 -> nur intern ... nur extern,default 0 - float m_Chempot2; // typischerweise 0, - // korrektur fuer das geschaetzte integral - int m_FiberLength; - bool m_AbortTracking; - bool m_SubtractMean; - int m_NumAcceptedFibers; - volatile bool m_BuildFibers; - unsigned int m_Steps; - float m_Memory; - float m_ProposalAcceptance; - float m_CurvatureHardThreshold; - float m_Meanval_sq; - - RJMCMC* m_Sampler; - FiberPolyDataType m_FiberPolyData; - unsigned long m_NumParticles; - unsigned long m_NumConnections; - }; + float m_StartTemperature; // Start temperature + float m_EndTemperature; // End temperature + unsigned long m_Iterations; // Total number of iterations + unsigned long m_CurrentStep; // current tracking step + float m_ParticleWeight; // w (unitless) + float m_ParticleWidth; // sigma (mm) + float m_ParticleLength; // l (mm) + float m_ConnectionPotential; // gross L (chemisches potential, default 10) + float m_InexBalance; // gewichtung zwischen den lambdas; -5 ... 5 -> nur intern ... nur extern,default 0 + float m_ParticlePotential; // default 0.2 + int m_MinFiberLength; // discard all fibers shortan than the specified length in mm + bool m_AbortTracking; // set flag to abort tracking + int m_NumAcceptedFibers; // number of reconstructed fibers generated by the FiberBuilder + volatile bool m_BuildFibers; // set flag to generate fibers from particle grid + unsigned int m_Steps; // number of temperature decrease steps + float m_ProposalAcceptance; // proposal acceptance rate (0-1) + float m_CurvatureThreshold; // curvature threshold in radians (1 -> no curvature is accepted, -1 all curvature angles are accepted) + bool m_DuplicateImage; // generates a working copy of the qball image so that the original image won't be changed by the mean subtraction + int m_NumParticles; // current number of particles in grid + int m_NumConnections; // current number of connections between particles in grid + int m_RandomSeed; // seed value for random generator (-1 for standard seeding) + std::string m_ParameterFile; // filename of parameter file + std::string m_LutPath; // path to lookuptables used by the sphere interpolator + + FiberPolyDataType m_FiberPolyData; // container for reconstructed fibers +}; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkGibbsTrackingFilter.cpp" #endif #endif diff --git a/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.cpp b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.cpp new file mode 100644 index 0000000000..0c9d77c696 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.cpp @@ -0,0 +1,358 @@ +/*=================================================================== + +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 __itkStreamlineTrackingFilter_txx +#define __itkStreamlineTrackingFilter_txx + +#include +#include +#include + +#include "itkStreamlineTrackingFilter.h" +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +namespace itk { + +//#define QBALL_RECON_PI M_PI + +template< class TTensorPixelType, class TPDPixelType> +StreamlineTrackingFilter< TTensorPixelType, +TPDPixelType> +::StreamlineTrackingFilter(): + m_FaThreshold(0.2), + m_StepSize(1), + m_MaxLength(10000), + m_SeedsPerVoxel(1) +{ + // At least 1 inputs is necessary for a vector image. + // For images added one at a time we need at least six + this->SetNumberOfRequiredInputs( 1 ); +} + +template< class TTensorPixelType, + class TPDPixelType> +double StreamlineTrackingFilter< TTensorPixelType, +TPDPixelType> +::RoundToNearest(double num) { + return (num > 0.0) ? floor(num + 0.5) : ceil(num - 0.5); +} + +template< class TTensorPixelType, + class TPDPixelType> +void StreamlineTrackingFilter< TTensorPixelType, +TPDPixelType> +::BeforeThreadedGenerateData() +{ + m_FiberPolyData = FiberPolyDataType::New(); + m_Points = vtkPoints::New(); + m_Cells = vtkCellArray::New(); + + typename InputImageType::Pointer inputImage = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); + m_ImageSize.resize(3); + m_ImageSize[0] = inputImage->GetLargestPossibleRegion().GetSize()[0]; + m_ImageSize[1] = inputImage->GetLargestPossibleRegion().GetSize()[1]; + m_ImageSize[2] = inputImage->GetLargestPossibleRegion().GetSize()[2]; + m_ImageSpacing.resize(3); + m_ImageSpacing[0] = inputImage->GetSpacing()[0]; + m_ImageSpacing[1] = inputImage->GetSpacing()[1]; + m_ImageSpacing[2] = inputImage->GetSpacing()[2]; + + if (m_StepSize<0.005) + { + float minSpacing; + if(m_ImageSpacing[0]::New(); + for (int i=0; iGetNumberOfThreads(); i++) + { + FiberPolyDataType poly = FiberPolyDataType::New(); + m_PolyDataContainer->InsertElement(i, poly); + } + + if (m_MaskImage.IsNull()) + { + itk::Vector spacing = inputImage->GetSpacing(); + itk::Point origin = inputImage->GetOrigin(); + itk::Matrix direction = inputImage->GetDirection(); + ImageRegion<3> imageRegion = inputImage->GetLargestPossibleRegion(); + + // initialize crossings image + m_MaskImage = ItkUcharImgType::New(); + m_MaskImage->SetSpacing( spacing ); + m_MaskImage->SetOrigin( origin ); + m_MaskImage->SetDirection( direction ); + m_MaskImage->SetRegions( imageRegion ); + m_MaskImage->Allocate(); + m_MaskImage->FillBuffer(1); + } + std::cout << "StreamlineTrackingFilter: stepsize: " << m_StepSize << " mm" << std::endl; + std::cout << "StreamlineTrackingFilter: starting streamline tracking" << std::endl; +} + +template< class TTensorPixelType, + class TPDPixelType> +void StreamlineTrackingFilter< TTensorPixelType, +TPDPixelType> +::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, + int threadId) +{ + FiberPolyDataType poly = m_PolyDataContainer->GetElement(threadId); + vtkSmartPointer Points = vtkPoints::New(); + vtkSmartPointer Cells = vtkCellArray::New(); + + typedef itk::DiffusionTensor3D TensorType; + typedef ImageRegionConstIterator< InputImageType > InputIteratorType; + typedef ImageRegionConstIterator< ItkUcharImgType > MaskIteratorType; + typedef typename InputImageType::PixelType InputTensorType; + typename InputImageType::Pointer inputImage = static_cast< InputImageType * >( this->ProcessObject::GetInput(0) ); + + InputIteratorType it(inputImage, outputRegionForThread ); + MaskIteratorType mit(m_MaskImage, outputRegionForThread ); + it.GoToBegin(); + mit.GoToBegin(); + while( !it.IsAtEnd() ) + { + if (mit.Value()==0) + { + ++mit; + ++it; + continue; + } + + typename TensorType::EigenValuesArrayType eigenvalues; + typename TensorType::EigenVectorsMatrixType eigenvectors; + + for (int s=0; s container = vtkSmartPointer::New(); + std::vector< vtkIdType > pointISs; + typename InputImageType::IndexType index = it.GetIndex(); + itk::ContinuousIndex pos; + itk::ContinuousIndex start; + + if (m_SeedsPerVoxel>1) + { + pos[0] = index[0]+(double)(rand()%99-49)/100; + pos[1] = index[1]+(double)(rand()%99-49)/100; + pos[2] = index[2]+(double)(rand()%99-49)/100; + } + else + { + pos[0] = index[0]; + pos[1] = index[1]; + pos[2] = index[2]; + } + start = pos; + + int step = 0; + vnl_vector_fixed dirOld; dirOld.fill(0.0); + // do forward tracking + while (step < m_MaxLength) + { + ++step; + + index[0] = RoundToNearest(pos[0]); + index[1] = RoundToNearest(pos[1]); + index[2] = RoundToNearest(pos[2]); + + if (!inputImage->GetLargestPossibleRegion().IsInside(index)) + break; + + typename InputImageType::PixelType tensor = inputImage->GetPixel(index); + if(tensor.GetTrace()!=0 && tensor.GetFractionalAnisotropy()>m_FaThreshold) + { + tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); + + vnl_vector_fixed dir; + dir[0] = eigenvectors(2, 0); + dir[1] = eigenvectors(2, 1); + dir[2] = eigenvectors(2, 2); + dir.normalize(); + + if (!dirOld.is_zero()) + { + float angle = dot_product(dirOld, dir); + if (angle<0) + dir *= -1; + angle = fabs(dot_product(dirOld, dir)); + if (angle<0.7) + break; + } + dirOld = dir; + + dir *= m_StepSize; + + itk::Point worldPos; + inputImage->TransformContinuousIndexToPhysicalPoint( pos, worldPos ); + + vtkIdType id = Points->InsertNextPoint(worldPos.GetDataPointer()); + pointISs.push_back(id); + counter++; + + pos[0] += dir[0]/m_ImageSpacing[0]; + pos[1] += dir[1]/m_ImageSpacing[1]; + pos[2] += dir[2]/m_ImageSpacing[2]; + } + } + + // insert reverse IDs + while (!pointISs.empty()) + { + container->GetPointIds()->InsertNextId(pointISs.back()); + pointISs.pop_back(); + } + + // do backward tracking + index = it.GetIndex(); + pos = start; + dirOld.fill(0.0); + while (step < m_MaxLength) + { + ++step; + + index[0] = RoundToNearest(pos[0]); + index[1] = RoundToNearest(pos[1]); + index[2] = RoundToNearest(pos[2]); + + if (index[0] < 0 || index[0]>=m_ImageSize[0]) + break; + if (index[1] < 0 || index[1]>=m_ImageSize[1]) + break; + if (index[2] < 0 || index[2]>=m_ImageSize[2]) + break; + + typename InputImageType::PixelType tensor = inputImage->GetPixel(index); + if(tensor.GetTrace()!=0 && tensor.GetFractionalAnisotropy()>m_FaThreshold) + { + tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); + + vnl_vector_fixed dir; + dir[0] = eigenvectors(2, 0); + dir[1] = eigenvectors(2, 1); + dir[2] = eigenvectors(2, 2); + dir.normalize(); + dir *= -1; // reverse direction + + if (!dirOld.is_zero()) + { + float angle = dot_product(dirOld, dir); + if (angle<0) + dir *= -1; + angle = fabs(dot_product(dirOld, dir)); + if (angle<0.7) + break; + } + dirOld = dir; + + dir *= m_StepSize; + + itk::Point worldPos; + inputImage->TransformContinuousIndexToPhysicalPoint( pos, worldPos ); + + vtkIdType id = Points->InsertNextPoint(worldPos.GetDataPointer()); + container->GetPointIds()->InsertNextId(id); + counter++; + + pos[0] += dir[0]/m_ImageSpacing[0]; + pos[1] += dir[1]/m_ImageSpacing[1]; + pos[2] += dir[2]/m_ImageSpacing[2]; + } + } + + if (counter>0) + Cells->InsertNextCell(container); + } + ++mit; + ++it; + } + + poly->SetPoints(Points); + poly->SetLines(Cells); + + std::cout << "Thread " << threadId << " finished tracking" << std::endl; +} + +template< class TTensorPixelType, + class TPDPixelType> +vtkSmartPointer< vtkPolyData > StreamlineTrackingFilter< TTensorPixelType, +TPDPixelType> +::AddPolyData(FiberPolyDataType poly1, FiberPolyDataType poly2) +{ + vtkSmartPointer vNewPolyData = vtkSmartPointer::New(); + vtkSmartPointer vNewLines = poly1->GetLines(); + vtkSmartPointer vNewPoints = poly1->GetPoints(); + + vtkSmartPointer vLines = poly2->GetLines(); + vLines->InitTraversal(); + for( int i=0; iGetNumberOfCells(); i++ ) + { + vtkIdType numPoints(0); + vtkIdType* points(NULL); + vLines->GetNextCell ( numPoints, points ); + + vtkSmartPointer container = vtkSmartPointer::New(); + for( int j=0; jInsertNextPoint(poly2->GetPoint(points[j])); + container->GetPointIds()->InsertNextId(id); + } + vNewLines->InsertNextCell(container); + } + + // initialize polydata + vNewPolyData->SetPoints(vNewPoints); + vNewPolyData->SetLines(vNewLines); + + return vNewPolyData; +} +template< class TTensorPixelType, + class TPDPixelType> +void StreamlineTrackingFilter< TTensorPixelType, +TPDPixelType> +::AfterThreadedGenerateData() +{ + MITK_INFO << "Generating polydata "; + m_FiberPolyData = m_PolyDataContainer->GetElement(0); + for (int i=1; iGetNumberOfThreads(); i++) + { + m_FiberPolyData = AddPolyData(m_FiberPolyData, m_PolyDataContainer->GetElement(i)); + } + MITK_INFO << "done"; +} + +template< class TTensorPixelType, + class TPDPixelType> +void StreamlineTrackingFilter< TTensorPixelType, +TPDPixelType> +::PrintSelf(std::ostream& os, Indent indent) const +{ +} + +} +#endif // __itkDiffusionQballPrincipleDirectionsImageFilter_txx diff --git a/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.h b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.h new file mode 100644 index 0000000000..4d07bc4c01 --- /dev/null +++ b/Modules/DiffusionImaging/Tractography/itkStreamlineTrackingFilter.h @@ -0,0 +1,110 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +/*=================================================================== + +This file is based heavily on a corresponding ITK filter. + +===================================================================*/ +#ifndef __itkStreamlineTrackingFilter_h_ +#define __itkStreamlineTrackingFilter_h_ + +#include "MitkDiffusionImagingExports.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace itk{ + /** \class StreamlineTrackingFilter + */ + + template< class TTensorPixelType, class TPDPixelType=double> + class StreamlineTrackingFilter : + public ImageToImageFilter< Image< DiffusionTensor3D, 3 >, + Image< Vector< TPDPixelType, 3 >, 3 > > + { + + public: + + typedef StreamlineTrackingFilter Self; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + typedef ImageToImageFilter< Image< DiffusionTensor3D, 3 >, Image< Vector< TPDPixelType, 3 >, 3 > > Superclass; + + /** Method for creation through the object factory. */ + itkNewMacro(Self) + + /** Runtime information support. */ + itkTypeMacro(StreamlineTrackingFilter, ImageToImageFilter) + + typedef TTensorPixelType TensorComponentType; + typedef TPDPixelType DirectionPixelType; + typedef typename Superclass::InputImageType InputImageType; + typedef typename Superclass::OutputImageType OutputImageType; + typedef typename Superclass::OutputImageRegionType OutputImageRegionType; + typedef itk::Image ItkUcharImgType; + + typedef vtkSmartPointer< vtkPolyData > FiberPolyDataType; + + itkGetMacro( FiberPolyData, FiberPolyDataType ) + itkSetMacro( MaskImage, ItkUcharImgType::Pointer) + itkSetMacro( SeedsPerVoxel, int) + itkSetMacro( FaThreshold, float) + itkSetMacro( StepSize, float) + + protected: + StreamlineTrackingFilter(); + ~StreamlineTrackingFilter() {} + void PrintSelf(std::ostream& os, Indent indent) const; + + double RoundToNearest(double num); + void BeforeThreadedGenerateData(); + void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, int threadId); + void AfterThreadedGenerateData(); + + FiberPolyDataType AddPolyData(FiberPolyDataType poly1, FiberPolyDataType poly2); + + FiberPolyDataType m_FiberPolyData; + vtkSmartPointer m_Points; + vtkSmartPointer m_Cells; + float m_FaThreshold; + float m_StepSize; + int m_MaxLength; + int m_SeedsPerVoxel; + std::vector< int > m_ImageSize; + std::vector< float > m_ImageSpacing; + ItkUcharImgType::Pointer m_MaskImage; + + itk::VectorContainer< int, FiberPolyDataType >::Pointer m_PolyDataContainer; + + private: + + }; + +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkStreamlineTrackingFilter.cpp" +#endif + +#endif //__itkStreamlineTrackingFilter_h_ + diff --git a/Modules/DiffusionImaging/files.cmake b/Modules/DiffusionImaging/files.cmake index 73b96d2312..0536570f09 100644 --- a/Modules/DiffusionImaging/files.cmake +++ b/Modules/DiffusionImaging/files.cmake @@ -1,227 +1,239 @@ set(CPP_FILES # DicomImport DicomImport/mitkDicomDiffusionImageReader.cpp DicomImport/mitkGroupDiffusionHeadersFilter.cpp DicomImport/mitkDicomDiffusionImageHeaderReader.cpp DicomImport/mitkGEDicomDiffusionImageHeaderReader.cpp DicomImport/mitkPhilipsDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensDicomDiffusionImageHeaderReader.cpp DicomImport/mitkSiemensMosaicDicomDiffusionImageHeaderReader.cpp # DataStructures IODataStructures/mitkDiffusionImagingObjectFactory.cpp # DataStructures -> DWI IODataStructures/DiffusionWeightedImages/mitkDiffusionImageHeaderInformation.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSource.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageReader.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriter.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageIOFactory.cpp IODataStructures/DiffusionWeightedImages/mitkNrrdDiffusionImageWriterFactory.cpp IODataStructures/DiffusionWeightedImages/mitkDiffusionImageSerializer.cpp # DataStructures -> QBall IODataStructures/QBallImages/mitkQBallImageSource.cpp IODataStructures/QBallImages/mitkNrrdQBallImageReader.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriter.cpp IODataStructures/QBallImages/mitkNrrdQBallImageIOFactory.cpp IODataStructures/QBallImages/mitkNrrdQBallImageWriterFactory.cpp IODataStructures/QBallImages/mitkQBallImage.cpp IODataStructures/QBallImages/mitkQBallImageSerializer.cpp # DataStructures -> Tensor IODataStructures/TensorImages/mitkTensorImageSource.cpp IODataStructures/TensorImages/mitkNrrdTensorImageReader.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriter.cpp IODataStructures/TensorImages/mitkNrrdTensorImageIOFactory.cpp IODataStructures/TensorImages/mitkNrrdTensorImageWriterFactory.cpp IODataStructures/TensorImages/mitkTensorImage.cpp IODataStructures/TensorImages/mitkTensorImageSerializer.cpp # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriter.cpp IODataStructures/FiberBundleX/mitkFiberBundleXReader.cpp IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.cpp IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.cpp IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.cpp # DataStructures -> PlanarFigureComposite IODataStructures/PlanarFigureComposite/mitkPlanarFigureComposite.cpp # DataStructures -> Tbss IODataStructures/TbssImages/mitkTbssImageSource.cpp IODataStructures/TbssImages/mitkTbssRoiImageSource.cpp IODataStructures/TbssImages/mitkNrrdTbssImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssImageIOFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageReader.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageIOFactory.cpp IODataStructures/TbssImages/mitkTbssImage.cpp IODataStructures/TbssImages/mitkTbssRoiImage.cpp IODataStructures/TbssImages/mitkNrrdTbssImageWriter.cpp IODataStructures/TbssImages/mitkNrrdTbssImageWriterFactory.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriter.cpp IODataStructures/TbssImages/mitkNrrdTbssRoiImageWriterFactory.cpp IODataStructures/TbssImages/mitkTbssImporter.cpp # DataStructures Connectomics IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkIOFactory.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkSerializer.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriter.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriterFactory.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkDefinitions.cpp IODataStructures/ConnectomicsNetwork/mitkConnectomicsConstantsManager.cpp # Rendering Rendering/vtkMaskedProgrammableGlyphFilter.cpp Rendering/mitkCompositeMapper.cpp Rendering/mitkVectorImageVtkGlyphMapper3D.cpp Rendering/vtkOdfSource.cxx Rendering/vtkThickPlane.cxx Rendering/mitkOdfNormalizationMethodProperty.cpp Rendering/mitkOdfScaleByProperty.cpp Rendering/mitkFiberBundleXMapper2D.cpp Rendering/mitkFiberBundleXMapper3D.cpp Rendering/mitkFiberBundleXThreadMonitorMapper3D.cpp Rendering/mitkTbssImageMapper.cpp Rendering/mitkPlanarCircleMapper3D.cpp Rendering/mitkPlanarPolygonMapper3D.cpp Rendering/mitkConnectomicsNetworkMapper3D.cpp # Interactions Interactions/mitkFiberBundleInteractor.cpp # Algorithms Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.cpp Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.cpp Algorithms/mitkTractAnalyzer.cpp # Algorithms Connectomics Algorithms/Connectomics/mitkConnectomicsNetworkCreator.cpp Algorithms/Connectomics/mitkConnectomicsHistogramBase.cpp Algorithms/Connectomics/mitkConnectomicsDegreeHistogram.cpp Algorithms/Connectomics/mitkConnectomicsShortestPathHistogram.cpp Algorithms/Connectomics/mitkConnectomicsBetweennessHistogram.cpp Algorithms/Connectomics/mitkConnectomicsHistogramCache.cpp Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationBase.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationModularity.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingManager.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionBase.cpp Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionModularity.cpp - # Tractography - Tractography/itkStochasticTractographyFilter.h + # Tractography + Tractography/GibbsTracking/mitkParticleGrid.cpp + Tractography/GibbsTracking/mitkMetropolisHastingsSampler.cpp + Tractography/GibbsTracking/mitkEnergyComputer.cpp + Tractography/GibbsTracking/mitkFiberBuilder.cpp # Function Collection mitkDiffusionFunctionCollection.cpp ) set(H_FILES # function Collection mitkDiffusionFunctionCollection.h - + # Rendering Rendering/mitkDiffusionImageMapper.h Rendering/mitkTbssImageMapper.h Rendering/mitkOdfVtkMapper2D.h Rendering/mitkFiberBundleXMapper3D.h Rendering/mitkFiberBundleXMapper2D.h Rendering/mitkFiberBundleXThreadMonitorMapper3D.h Rendering/mitkPlanarCircleMapper3D.h Rendering/mitkPlanarPolygonMapper3D.h Rendering/mitkConnectomicsNetworkMapper3D.h # Reconstruction Reconstruction/itkDiffusionQballReconstructionImageFilter.h Reconstruction/mitkTeemDiffusionTensor3DReconstructionImageFilter.h Reconstruction/itkAnalyticalDiffusionQballReconstructionImageFilter.h Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h Reconstruction/itkPointShell.h Reconstruction/itkOrientationDistributionFunction.h Reconstruction/itkDiffusionIntravoxelIncoherentMotionReconstructionImageFilter.h Reconstruction/itkRegularizedIVIMLocalVariationImageFilter.h Reconstruction/itkRegularizedIVIMReconstructionFilter.h Reconstruction/itkRegularizedIVIMReconstructionSingleIteration.h # IO Datastructures IODataStructures/DiffusionWeightedImages/mitkDiffusionImage.h IODataStructures/TbssImages/mitkTbssImporter.h # DataStructures -> FiberBundleX IODataStructures/FiberBundleX/mitkFiberBundleX.h IODataStructures/FiberBundleX/mitkFiberBundleXWriter.h IODataStructures/FiberBundleX/mitkFiberBundleXReader.h IODataStructures/FiberBundleX/mitkFiberBundleXIOFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXWriterFactory.h IODataStructures/FiberBundleX/mitkFiberBundleXSerializer.h IODataStructures/FiberBundleX/mitkFiberBundleXThreadMonitor.h # Datastructures Connectomics IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkReader.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkIOFactory.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkSerializer.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriter.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkWriterFactory.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetworkDefinitions.h IODataStructures/ConnectomicsNetwork/mitkConnectomicsConstantsManager.h # Tractography Tractography/itkGibbsTrackingFilter.h Tractography/itkStochasticTractographyFilter.h + Tractography/itkStreamlineTrackingFilter.h + Tractography/GibbsTracking/mitkParticle.h + Tractography/GibbsTracking/mitkParticleGrid.h + Tractography/GibbsTracking/mitkMetropolisHastingsSampler.h + Tractography/GibbsTracking/mitkSimpSamp.h + Tractography/GibbsTracking/mitkEnergyComputer.h + Tractography/GibbsTracking/mitkSphereInterpolator.h + Tractography/GibbsTracking/mitkFiberBuilder.h # Algorithms Algorithms/itkDiffusionQballGeneralizedFaImageFilter.h Algorithms/itkDiffusionQballPrepareVisualizationImageFilter.h Algorithms/itkTensorDerivedMeasurementsFilter.h Algorithms/itkBrainMaskExtractionImageFilter.h Algorithms/itkB0ImageExtractionImageFilter.h Algorithms/itkB0ImageExtractionToSeparateImageFilter.h Algorithms/itkTensorImageToDiffusionImageFilter.h Algorithms/itkTensorToL2NormImageFilter.h Algorithms/itkTractDensityImageFilter.h Algorithms/itkTractsToFiberEndingsImageFilter.h Algorithms/itkTractsToRgbaImageFilter.h Algorithms/itkGaussianInterpolateImageFunction.h Algorithms/mitkPartialVolumeAnalysisHistogramCalculator.h Algorithms/mitkPartialVolumeAnalysisClusteringCalculator.h Algorithms/itkDiffusionTensorPrincipleDirectionImageFilter.h Algorithms/itkCartesianToPolarVectorImageFilter.h Algorithms/itkPolarToCartesianVectorImageFilter.h Algorithms/itkDistanceMapFilter.h Algorithms/itkProjectionFilter.h Algorithms/itkSkeletonizationFilter.h Algorithms/itkReduceDirectionGradientsFilter.h Algorithms/itkResidualImageFilter.h Algorithms/itkExtractChannelFromRgbaImageFilter.h + Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h # Algorithms Connectomics Algorithms/Connectomics/mitkConnectomicsNetworkCreator.h Algorithms/Connectomics/mitkConnectomicsHistogramBase.h Algorithms/Connectomics/mitkConnectomicsDegreeHistogram.h Algorithms/Connectomics/mitkConnectomicsShortestPathHistogram.h Algorithms/Connectomics/mitkConnectomicsBetweennessHistogram.h Algorithms/Connectomics/mitkConnectomicsHistogramCache.h Algorithms/Connectomics/mitkConnectomicsSyntheticNetworkGenerator.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationBase.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingPermutationModularity.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingManager.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionBase.h Algorithms/Connectomics/mitkConnectomicsSimulatedAnnealingCostFunctionModularity.h - Algorithms/itkTensorReconstructionWithEigenvalueCorrectionFilter.h - + + ) set( TOOL_FILES ) if(WIN32) endif(WIN32) #MITK_MULTIPLEX_PICTYPE( Algorithms/mitkImageRegistrationMethod-TYPE.cpp ) diff --git a/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.cpp b/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.cpp index 4db8e9705a..9e9ba9054f 100644 --- a/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.cpp +++ b/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.cpp @@ -1,105 +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. ===================================================================*/ #include "mitkDiffusionFunctionCollection.h" #include #include "mitkVector.h" // for Windows #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // Namespace ::SH #include #include #include // Namespace ::vnl_function -//#include "vnl/vnl_vector.h" +#include "vnl/vnl_vector.h" //------------------------- SH-function ------------------------------------ double mitk::sh::factorial(int number) { if(number <= 1) return 1; double result = 1.0; for(int i=1; i<=number; i++) result *= i; return result; } void mitk::sh::Cart2Sph(double x, double y, double z, double *cart) { double phi, th, rad; rad = sqrt(x*x+y*y+z*z); if( rad < mitk::eps ) { th = M_PI/2; phi = M_PI/2; } else { th = acos(z/rad); phi = atan2(y, x); } cart[0] = phi; cart[1] = th; cart[2] = rad; } double mitk::sh::legendre0(int l) { if( l%2 != 0 ) { return 0; } else { double prod1 = 1.0; for(int i=1;i -vnl_vector mitk::vnl_function::element_cast (vnl_vector const& v1) -{ - vnl_vector result(v1.size()); - - for(int i = 0 ; i < v1.size(); i++) - result[i] = static_cast(v1[i]); - - return result; -} diff --git a/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.h b/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.h index 2c517178b1..8d745a306e 100644 --- a/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.h +++ b/Modules/DiffusionImaging/mitkDiffusionFunctionCollection.h @@ -1,46 +1,39 @@ /*=================================================================== 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 __mitkDiffusionFunctionCollection_h_ #define __mitkDiffusionFunctionCollection_h_ -template -class vnl_vector; -namespace mitk{ - -namespace sh -{ +#include "MitkDiffusionImagingExports.h" -double factorial(int number); -void Cart2Sph(double x, double y, double z, double* cart); -double legendre0(int l); -double spherical_harmonic(int m,int l,double theta,double phi, bool complexPart); -double Yj(int m, int k, double theta, double phi); -} +namespace mitk{ -namespace vnl_function +class MitkDiffusionImaging_EXPORT sh { - -template -vnl_vector element_cast (vnl_vector const& v1); - -} +public: +static double factorial(int number); +static void Cart2Sph(double x, double y, double z, double* cart); +static double legendre0(int l); +static double spherical_harmonic(int m,int l,double theta,double phi, bool complexPart); +static double Yj(int m, int k, double theta, double phi); + +}; } #endif //__mitkDiffusionFunctionCollection_h_ diff --git a/Modules/IGT/Documentation/doxygen/IGTModule.dox b/Modules/IGT/Documentation/doxygen/IGTModule.dox index 14a175c1bd..a7f8491f05 100644 --- a/Modules/IGT/Documentation/doxygen/IGTModule.dox +++ b/Modules/IGT/Documentation/doxygen/IGTModule.dox @@ -1,22 +1,29 @@ /** \page IGTGeneralModulePage The IGT Modules \section IGTGeneralModulePageOverview Overview The module IGT integrates image guided therapy (IGT) functionality to MITK. The main features of MITK-IGT are:
    • handling and processing of medical imaging data which is available through MITK itself
    • support of tracking devices
    • a concept for processing tracking data
    MITK-IGT consists of two layers for hardware control (Tracking Layer) and processing of tracking data (Navigation Layer). Additionally it offers components for rapid development of graphical user interfaces (GUIs) of navigation applications. To seperate UI functionality from the rest of the code UI classes are encapsulated in the seperate module IGT-UI. +\section IGTTutorial IGT Code Tutorial + +If you want to learn more about IGT you can have a look +on the IGT code tutorial, which can be found here: + + \li \subpage IGTCodeTutorialPage + \section IGTGeneralModulePageModuleList List of IGT Plugins There are also a few ready-to-use sample applications available as MITK-Plugins: \li \subpage org_mitk_gui_qt_igtexample \li \subpage org_mitk_gui_qt_igttracking */ \ No newline at end of file diff --git a/Modules/IGT/Documentation/doxygen/IGTTutorialStep1-2.png b/Modules/IGT/Documentation/doxygen/IGTTutorialStep1-2.png new file mode 100644 index 0000000000..f4f678d5a3 Binary files /dev/null and b/Modules/IGT/Documentation/doxygen/IGTTutorialStep1-2.png differ diff --git a/Modules/IGT/Documentation/doxygen/IGTTutorialStep1.dox b/Modules/IGT/Documentation/doxygen/IGTTutorialStep1.dox new file mode 100644 index 0000000000..74448ca274 --- /dev/null +++ b/Modules/IGT/Documentation/doxygen/IGTTutorialStep1.dox @@ -0,0 +1,213 @@ +/** +\page IGTCodeTutorialPage The IGT Code Tutorial + +The IGT tutorial consists of four main parts for construction of a small navigation pipeline using a virtual tracking device. +The virtual tracking device produces random tool data (position and orientation) so no additional hardware is required. + +\section sec1 In Tracking Layer + +Firstly a new object "tracker" of the type mitk::VirtualTrackingDevice is created, then two tools, named "tool1" and "tool2", +are added to this "tracker". Since, the tracking device "tracker" is treated as a virtual tracking +device "tool1" and "tool2" are just added to the object by method AddTool(name). + +\section sec2 In Navigation Layer + +\image html IGTTutorialStep1.png + +Secondly, a new source of the type mitk::TrackingDeviceSource has to be created with outputs for each single tool of a tracker. +The source sets the following tracking device by using method SetTrackingDevice as shown below +\code +source->SetTrackingDevice(tracker); +\endcode +So now, the source is initialized with the virtual tracking device. Next, the source is connected and tracking is started. + +In part II, a displacemt filter (object "displacer") is constructed to change the positions of the filtered NavigationData objects +with an offset for each direction (X,Y,Z). The given filter has inputs and outputs for each tool, in this example we have 2 tools, hence there exists two inputs and outputs. Every output of the displacement filter object is connected to the recorder object in the next part. + +In part III, all the NavigationData is recorded with the NavigationDataRecorder. In order to record, we simply create +an object "recorder" of the type mitk::NavigationDataRecorder and set the appropriate file to it. Now the displacer object is connected to the +recorder object for every output by using a for-loop in the code, the method StartRecording() is called on the next line. Afterwards, +the recorder has to be updated a couple of times. In this example the recorder is updating 100 times through +the second for-loop statement in part III. This can also be seen as a simulation of a timer by using a for-loop. + +Part IV explains how the recoded file can be played for further use. After the object "player" of a type mitk::NavigationDataPlayer +is created, the required file has to be set to the player and playing has to be started. Here, there exists a new pipeline which functions by reading +the recorded file from the harddisc and plays it by using the player as source. During the play, the for-loop makes the file update as in part III. + +\image html IGTTutorialStep1-2.png + +The full code of small navigation pipeline is shown below. + +\code + +int main(int /*argc*/, char* /*argv*/[]) +{ + + + //************************************************************************* + // What we will do... + //************************************************************************* + //In this tutorial we build up a small navigation pipeline with a virtual tracking device + //which produce random positions and orientation so no additional hardware is required. + //The source of the pipeline is a TrackingDeviceSource object. This we connect to a simple + //filter which just displaces the positions with an offset. After that we use a recorder + //to store this new positions and other information to disc in a XML file. After that we use + //another source (NavigationDataPlayer) to replay the recorded data. + + + //************************************************************************* + // Part I: Basic initialization of the source and tracking device + //************************************************************************* + //First of all create a tracking device object and two tools for this "device". + + //Here we take the VirtualTrackingDevice. This is not a real tracking device it just delivers random + //positions and orientations. You can use other/real tracking devices if you replace the following + //code with different tracking devices, e.g. mitk::NDITrackingDevice. The tools represent the + //sensors of the tracking device. The TrackingDevice fills the tools with data. + std::cout << "Generating TrackingDevice ..." << std::endl; + + mitk::VirtualTrackingDevice::Pointer tracker = mitk::VirtualTrackingDevice::New(); + tracker->AddTool("tool1"); + tracker->AddTool("tool2"); + + //The tracking device object is used for the physical connection to the device. To use the + //data inside of our tracking pipeline we need a source. This source encapsulate the tracking device + //and provides objects of the type mitk::NavigationData as output. The NavigationData objects stores + //position, orientation, if the data is valid or not and special error informations in a covariance + //matrix. + // + //Typically the start of our pipeline is a TrackingDeviceSource. To work correct we have to set a + //TrackingDevice object. Attention you have to set the tools before you set the whole TrackingDevice + //object to the TrackingDeviceSource because the source need to know how many outputs should be + //generated. + + std::cout << "Generating Source ..." << std::endl; + + mitk::TrackingDeviceSource::Pointer source = mitk::TrackingDeviceSource::New(); + source->SetTrackingDevice(tracker); //here we set the device for the pipeline source + + source->Connect(); //here we connect to the tracking system + //Note we do not call this on the TrackingDevice object + source->StartTracking(); //start the tracking + //Now the source generates outputs. + + + //************************************************************************* + // Part II: Create a NavigationDataToNavigationDataFilter + //************************************************************************* + + //The next thing we do is using a NavigationDataToNavigationDataFilter. One of these filter is the + //very simple NavigationDataDisplacementFilter. This filter just changes the positions of the input + //NavigationData objects with an offset for each direction (X,Y,Z). The input of this filter is the + //source and the output of this filter is the "displaced" input. + + std::cout << "Generating DisplacementFilter ..." << std::endl; + + mitk::NavigationDataDisplacementFilter::Pointer displacer = mitk::NavigationDataDisplacementFilter::New(); + mitk::Vector3D offset; + mitk::FillVector3D(offset, 10.0, 100.0, 1.0); //initialize the offset + displacer->SetOffset(offset); //now set the offset in the NavigationDataDisplacementFilter object + + //now every output of the source object is connected to the displacer object + for (unsigned int i = 0; i < source->GetNumberOfOutputs(); i++) + { + displacer->SetInput(i, source->GetOutput(i)); //here we connect to the displacement filter + } + + + //************************************************************************* + // Part III: Record the data with the NavigationDataRecorder + //************************************************************************* + + //The next part of our pipeline is the recorder. The recorder needs a filename. Otherwise the output + //is redirected to the console. The input of the recorder is the output of the displacement filter + //and the output is a XML file with the name "Test Output-0.xml". + + std::cout << "Start Recording ..." << std::endl; + + //we need the stringstream for building up our filename + std::stringstream filename; + + //the .xml extension and an counter is added automatically + filename << itksys::SystemTools::GetCurrentWorkingDirectory() << "/Test Output"; + + std::cout << "Record to file: " << filename.str() << "-0.xml ..." << std::endl; + + mitk::NavigationDataRecorder::Pointer recorder = mitk::NavigationDataRecorder::New(); + recorder->SetFileName(filename.str()); + + //now every output of the displacer object is connected to the recorder object + for (unsigned int i = 0; i < displacer->GetNumberOfOutputs(); i++) + { + recorder->AddNavigationData(displacer->GetOutput(i)); // here we connect to the recorder + } + + recorder->StartRecording(); //after finishing the settings you can start the recording mechanism + //now every update of the recorder stores one line into the file for + //each added NavigationData + + + for (unsigned int x = 0; x < 100; x++) //write 100 datasets + { + recorder->Update(); //the update causes one line in the XML file for every tool + //in this case two lines + itksys::SystemTools::Delay(100); //sleep a little + } + recorder->StopRecording(); //to get proper XML files you should stop recording + //if your application crashes during recording no data + //will be lost it is all stored to disc + + + //************************************************************************* + // Part IV: Play the data with the NavigationDataPlayer + //************************************************************************* + + //The recording is finished now so now we can play the data. The NavigationDataPlayer is similar + //to the TrackingDevice source. It also derives from NavigationDataSource. So you can use a player + //instead of a TrackingDeviceSource. The input of this player is the filename and the output are + //NavigationData object. + + filename << "-0.xml"; + std::cout << "Start playing from file: " << filename.str() << " ..." << std::endl; + + + mitk::NavigationDataPlayer::Pointer player = mitk::NavigationDataPlayer::New(); + //this is first part of the file name the .xml extension and an counter is added automatically + player->SetFileName(filename.str()); + player->StartPlaying(); //this starts the player + //From now on the player provides NavigationDatas in the order and + //correct time as they were recorded + + //this connects the outputs of the player to the NavigationData objects + mitk::NavigationData::Pointer nd = player->GetOutput(0); + mitk::NavigationData::Pointer nd2 = player->GetOutput(1); + for (unsigned int x=0; x<100; x++) + { + if (nd.IsNotNull()) //check if the output is not null + { + //With this update the NavigationData object propagates through the pipeline to get a new value. + //In this case we only have a source (NavigationDataPlayer). + nd->Update(); + + std::cout << x << ": 1:" << nd->GetPosition() << std::endl; + std::cout << x << ": 2:" << nd2->GetPosition() << std::endl; + std::cout << x << ": 1:" << nd->GetOrientation() << std::endl; + std::cout << x << ": 2:" << nd2->GetOrientation() << std::endl; + + itksys::SystemTools::Delay(100); //sleep a little like in the recorder part + } + } + player->StopPlaying(); //This stops the player + //With another call of StartPlaying the player will start again at the beginning of the file + + itksys::SystemTools::Delay(2000); + std::cout << "finished" << std::endl; + +} +\endcode + +*/ + + + + \ No newline at end of file diff --git a/Modules/IGT/Documentation/doxygen/IGTTutorialStep1.png b/Modules/IGT/Documentation/doxygen/IGTTutorialStep1.png new file mode 100644 index 0000000000..4fbe88081d Binary files /dev/null and b/Modules/IGT/Documentation/doxygen/IGTTutorialStep1.png differ diff --git a/Modules/IGT/Testing/mitkTimeStampTest.cpp b/Modules/IGT/Testing/mitkTimeStampTest.cpp index 64f3e5da2a..192f702968 100644 --- a/Modules/IGT/Testing/mitkTimeStampTest.cpp +++ b/Modules/IGT/Testing/mitkTimeStampTest.cpp @@ -1,113 +1,117 @@ /*=================================================================== 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 "mitkTimeStamp.h" #include "mitkRealTimeClock.h" #include "mitkTestingMacros.h" #include "itkObject.h" //#include +#ifndef WIN32 +#include +#endif + /** * test for the class "mitkTimeStamp" */ int mitkTimeStampTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("TimeStamp") //create testing objects itk::Object::Pointer tester = itk::Object::New(); itk::Object::Pointer tester2 = itk::Object::New(); //try methods without calling CreateInstance() before (error test cases) MITK_TEST_OUTPUT(<<"Testing behavior of methodes if they are called at the wrong moment (error should occure)."); mitk::TimeStamp::GetInstance()->Start(tester2); mitk::TimeStamp::GetInstance()->Stop(tester2); //create instance of singleton time stamp class MITK_TEST_CONDITION_REQUIRED(mitk::TimeStamp::CreateInstance() != (0), "Testing GetInstance() of Singleton" ); double timestampX = mitk::TimeStamp::GetInstance()->GetElapsed(); MITK_TEST_CONDITION_REQUIRED(timestampX==-1,"..Testing method GetCurrentTimeStamp()."); double offsetX = mitk::TimeStamp::GetInstance()->GetOffset(tester2); MITK_TEST_CONDITION_REQUIRED(offsetX==-1.0,"..Testing method GetOffset()."); //start-tracking sets the reference-time, timestamps are relative to this time mitk::TimeStamp::GetInstance()->Start(tester); MITK_INFO << "first device has started tracking\n"; //sleeps for 20 ms #if defined (WIN32) || defined (_WIN32) Sleep(20); #else usleep(20000); #endif MITK_INFO << "supposed to have waited 20ms \n"; double time_elapsed, relative_time_elapsed; //gets time elapsed since start time_elapsed = mitk::TimeStamp::GetInstance()->GetElapsed(); MITK_INFO << "have actually waited : " << time_elapsed << "ms \n"; //elapsed time is not allowed to be too far from 20 ms (+-10ms)-> debugging "destroys" the correct value of course MITK_TEST_CONDITION_REQUIRED((time_elapsed-20) > -10 , "Testing if elapsed time is correct (-10)"); //MITK_TEST_CONDITION_REQUIRED((time_elapsed-20) < 10 , "Testing if elapsed time is correct (+10)"); //second "device" starts tracking mitk::TimeStamp::GetInstance()->Start(tester2); MITK_INFO << "second device has started\n"; //first device stops mitk::TimeStamp::GetInstance()->Stop(tester); MITK_INFO << "first device has stopped tracking\n"; time_elapsed = mitk::TimeStamp::GetInstance()->GetElapsed(); relative_time_elapsed = mitk::TimeStamp::GetInstance()->GetElapsed(tester2); MITK_INFO << "time elapsed supposed to be greater than 20 ms\n"; MITK_INFO << time_elapsed << " actually elapsed\n"; MITK_INFO << "relative time elapsed supposed to be smaller than absolute time elapsed : \n"; MITK_INFO << relative_time_elapsed << " actually elapsed\n"; //relative timespan must be smaller than absolute timespan MITK_TEST_CONDITION_REQUIRED( time_elapsed > relative_time_elapsed , " testing if relative timespan is shorter than absolute timespan"); //timestamp still has to be valid (tester2 still tracking), and has to be larger than 20ms //MITK_TEST_CONDITION_REQUIRED( time_elapsed > 15 , "testing if second device is still keeping the TimeStamp \"alive\""); mitk::TimeStamp::GetInstance()->Stop(tester2); MITK_INFO << " second device has stopped tracking\n"; time_elapsed = mitk::TimeStamp::GetInstance()->GetElapsed(); //when all devices have stopped, -1 has to be returned MITK_TEST_CONDITION_REQUIRED( time_elapsed == -1 , "testing if -1 is returned after all devices have stopped"); //test set realtime clock mitk::RealTimeClock::Pointer myRealTimeClock = mitk::RealTimeClock::New(); mitk::TimeStamp::GetInstance()->SetRealTimeClock(myRealTimeClock); // always end with this! MITK_TEST_END(); } 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/OpenCVVideoSupport/Testing/CMakeLists.txt b/Modules/OpenCVVideoSupport/Testing/CMakeLists.txt index d45daf111d..5cc25bd8f1 100644 --- a/Modules/OpenCVVideoSupport/Testing/CMakeLists.txt +++ b/Modules/OpenCVVideoSupport/Testing/CMakeLists.txt @@ -1 +1,8 @@ -MITK_CREATE_MODULE_TESTS() \ No newline at end of file +MITK_CREATE_MODULE_TESTS() + + +# if(BUILD_TESTING AND MODULE_IS_ENABLED) +# mitkAddCustomModuleTest(mitkImageToOpenCVImageFilterTest mitkImageToOpenCVImageFilterTest +# ${MITK_DATA_DIR}/RenderingTestData/rgbImage.png +# ) +# endif() diff --git a/Modules/OpenCVVideoSupport/Testing/files.cmake b/Modules/OpenCVVideoSupport/Testing/files.cmake index ab90f6e626..6e2ddb0530 100644 --- a/Modules/OpenCVVideoSupport/Testing/files.cmake +++ b/Modules/OpenCVVideoSupport/Testing/files.cmake @@ -1,11 +1,13 @@ set(MODULE_TESTS ) set(MODULE_IMAGE_TESTS mitkImageToOpenCVImageFilterTest.cpp ) # list of images for which the tests are run set(MODULE_TESTIMAGES Png2D-bw.png -) \ No newline at end of file + RenderingTestData/rgbImage.png +#NrrdWritingTestImage.jpg +) diff --git a/Modules/OpenCVVideoSupport/Testing/mitkImageToOpenCVImageFilterTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkImageToOpenCVImageFilterTest.cpp index 374f3d9dac..e6c43902cf 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkImageToOpenCVImageFilterTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkImageToOpenCVImageFilterTest.cpp @@ -1,125 +1,177 @@ /*=================================================================== 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 "mitkImageToOpenCVImageFilter.h" #include "mitkOpenCVToMitkImageFilter.h" #include #include #include +#include // #include -// #include +#include -/**Documentation - * test for the class "ImageToOpenCVImageFilter". - */ -int mitkImageToOpenCVImageFilterTest(int /*argc*/, char* /*argv*/[]) +mitk::Image::Pointer LoadImage( std::string filename ) { - MITK_TEST_BEGIN("ImageToOpenCVImageFilter") + mitk::ItkImageFileReader::Pointer reader = mitk::ItkImageFileReader::New(); + reader->SetFileName ( filename.c_str() ); + reader->Update(); + if ( reader->GetOutput() == NULL ) + itkGenericExceptionMacro("File "<GetOutput(); + return image; +} - // create itk rgb image - typedef unsigned char PixelType; - typedef itk::Image< itk::RGBPixel, 2 > ImageType; - ImageType::Pointer itkImage = ImageType::New(); - - ImageType::IndexType start; - start[0] = 0; // first index on X - start[1] = 0; // first index on Y - ImageType::SizeType size; - size[0] = 50; // size along X - size[1] = 40; // size along Y - ImageType::RegionType region; - region.SetSize( size ); - region.SetIndex( start ); - itkImage->SetRegions( region ); - itkImage->Allocate(); - - typedef itk::ImageRegionIterator IteratorType; - IteratorType it(itkImage, region); - float twoThirdsTheWidth = size[0] / 4; - unsigned int x=0, y=0; - // create rgb pic - for ( it.GoToBegin(); !it.IsAtEnd(); ++it ) - { - ImageType::PixelType newPixel; - newPixel.SetRed(0); - newPixel.SetGreen(0); - // create asymmetric pic - if( x > twoThirdsTheWidth ) - newPixel.SetBlue(0); - else - newPixel.SetBlue(255); - it.Set(newPixel); - - ++x; - // next line found - if( x == size[0] ) - x = 0; - } - - // debugging -// itk::ImageFileWriter< ImageType >::Pointer writer = itk::ImageFileWriter< ImageType >::New(); -// writer->SetFileName( "c:\\image.png" ); -// writer->SetInput ( itkImage ); -// writer->Update(); - - // import rgb image as MITK image - mitk::Image::Pointer mitkImage = mitk::ImportItkImage( itkImage ); - - mitk::ImageToOpenCVImageFilter::Pointer _ImageToOpenCVImageFilter = - mitk::ImageToOpenCVImageFilter::New(); - - _ImageToOpenCVImageFilter->SetImage( mitkImage ); - - IplImage* openCVImage = _ImageToOpenCVImageFilter->GetOpenCVImage(); - - MITK_TEST_CONDITION_REQUIRED( openCVImage != NULL, "Image returned by filter is not null."); - - // check byte size - const unsigned int expectedSize = size[0] * size[1] * 3 * sizeof( unsigned char);// sizeof( PixelType ); - const unsigned int realSize = openCVImage->width * openCVImage->height * openCVImage->nChannels * sizeof( unsigned char);//* sizeof ( PixelType ); - MITK_TEST_CONDITION_REQUIRED( expectedSize == realSize, "Test expectedSize == realSize"); - - // check pixel values - PixelType expectedBlueValue; - CvScalar s; - for (y = 0; (int)y < openCVImage->height; ++y) - { - for (x = 0; (int)x < openCVImage->width; ++x) +static void testGeneratedImage() +{ + // create itk rgb image + typedef unsigned char PixelType; + typedef itk::Image< itk::RGBPixel, 2 > ImageType; + ImageType::Pointer itkImage = ImageType::New(); + + ImageType::IndexType start; + start[0] = 0; // first index on X + start[1] = 0; // first index on Y + ImageType::SizeType size; + size[0] = 50; // size along X + size[1] = 40; // size along Y + ImageType::RegionType region; + region.SetSize( size ); + region.SetIndex( start ); + itkImage->SetRegions( region ); + itkImage->Allocate(); + + typedef itk::ImageRegionIterator IteratorType; + IteratorType it(itkImage, region); + float twoThirdsTheWidth = size[0] / 4; + unsigned int x=0, y=0; + // create rgb pic + for ( it.GoToBegin(); !it.IsAtEnd(); ++it ) { - expectedBlueValue = 255; - if(x > twoThirdsTheWidth) - expectedBlueValue = 0; + ImageType::PixelType newPixel; + newPixel.SetRed(0); + newPixel.SetGreen(0); + // create asymmetric pic + if( x > twoThirdsTheWidth ) + newPixel.SetBlue(0); + else + newPixel.SetBlue(255); + it.Set(newPixel); + + ++x; + // next line found + if( x == size[0] ) + x = 0; + } + + // debugging + // itk::ImageFileWriter< ImageType >::Pointer writer = itk::ImageFileWriter< ImageType >::New(); + // writer->SetFileName( "c:\\image.png" ); + // writer->SetInput ( itkImage ); + // writer->Update(); + + // import rgb image as MITK image + mitk::Image::Pointer mitkImage = mitk::ImportItkImage( itkImage ); - s = cvGet2D(openCVImage,y,x); - if( s.val[0] != expectedBlueValue || s.val[1] != 0 || s.val[2] != 0 ) + mitk::ImageToOpenCVImageFilter::Pointer _ImageToOpenCVImageFilter = + mitk::ImageToOpenCVImageFilter::New(); + + _ImageToOpenCVImageFilter->SetImage( mitkImage ); + + IplImage* openCVImage = _ImageToOpenCVImageFilter->GetOpenCVImage(); + + MITK_TEST_CONDITION_REQUIRED( openCVImage != NULL, "Image returned by filter is not null."); + + // check byte size + const unsigned int expectedSize = size[0] * size[1] * 3 * sizeof( unsigned char);// sizeof( PixelType ); + const unsigned int realSize = openCVImage->width * openCVImage->height * openCVImage->nChannels * sizeof( unsigned char);//* sizeof ( PixelType ); + MITK_TEST_CONDITION_REQUIRED( expectedSize == realSize, "Test expectedSize == realSize"); + + // check pixel values + PixelType expectedBlueValue; + CvScalar s; + for (y = 0; (int)y < openCVImage->height; ++y) + { + for (x = 0; (int)x < openCVImage->width; ++x) { - std::cout << "Wrong RGB values in created OpenCV image" << std::endl; - throw mitk::TestFailedException(); + expectedBlueValue = 255; + if(x > twoThirdsTheWidth) + expectedBlueValue = 0; + + s = cvGet2D(openCVImage,y,x); + if( s.val[0] != expectedBlueValue || s.val[1] != 0 || s.val[2] != 0 ) + { + std::cout << "Wrong RGB values in created OpenCV image" << std::endl; + throw mitk::TestFailedException(); + } } } - } -// cvNamedWindow( "test" ); -// cvShowImage( "test" , openCVImage ); -// cvWaitKey(); +// cvNamedWindow( "test" ); +// cvShowImage( "test" , openCVImage ); +// cvWaitKey(); + +} + +static void testLoadedImage(std::string mitkImagePath) +{ + mitk::Image::Pointer testImage = LoadImage(mitkImagePath); + mitk::ImageToOpenCVImageFilter::Pointer _ImageToOpenCVImageFilter = + mitk::ImageToOpenCVImageFilter::New(); + + _ImageToOpenCVImageFilter->SetImage( testImage ); + + IplImage* openCVImage = _ImageToOpenCVImageFilter->GetOpenCVImage(); + IplImage* openCVImage_Ref = cvLoadImage(mitkImagePath.c_str()); + + MITK_TEST_CONDITION_REQUIRED( openCVImage != NULL, "Image returned by filter is not null."); + + for(int i = 0 ; iheight ; i++) + { + for(int j = 0 ; jwidth ; j++) + { + CvScalar s; + s=cvGet2D(openCVImage,i,j); // get the (i,j) pixel value + CvScalar sRef; + sRef=cvGet2D(openCVImage_Ref,i,j); // get the (i,j) pixel value + for(int c = 0 ; c < openCVImage->nChannels ; c++) + { + MITK_TEST_CONDITION_REQUIRED( s.val[c] == sRef.val[c] , "All pixel values have to be equal"); + } + } + } + + +// cvNamedWindow( "test" ); +// cvShowImage( "test" , openCVImage ); +// cvWaitKey(); +} + +/**Documentation + * test for the class "ImageToOpenCVImageFilter". + */ +int mitkImageToOpenCVImageFilterTest(int argc, char* argv[]) +{ + MITK_TEST_BEGIN("ImageToOpenCVImageFilter") + testGeneratedImage(); + testLoadedImage(argv[1]); // always end with this! MITK_TEST_END(); } diff --git a/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp b/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp index 73bd9bfb2b..e54d71aed4 100644 --- a/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp +++ b/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp @@ -1,122 +1,125 @@ /*=================================================================== 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 "mitkImageToOpenCVImageFilter.h" #include #include #include mitk::ImageToOpenCVImageFilter::ImageToOpenCVImageFilter() : m_OpenCVImage(0) { } mitk::ImageToOpenCVImageFilter::~ImageToOpenCVImageFilter() { m_OpenCVImage = 0; } int mitk::ImageToOpenCVImageFilter::GetDepth(const std::type_info& typeInfo) const { if(typeInfo == typeid(unsigned char)) { return IPL_DEPTH_8U; } else if(typeInfo == typeid(char)) { return IPL_DEPTH_8S; } else if(typeInfo == typeid(unsigned short)) { return IPL_DEPTH_16U; } else if(typeInfo == typeid(short)) { return IPL_DEPTH_16S; } else if(typeInfo == typeid(int)) { return IPL_DEPTH_32S; } else if(typeInfo == typeid(float)) { return IPL_DEPTH_32F; } else if(typeInfo == typeid(double)) { return IPL_DEPTH_64F; } // rgb image else if(typeInfo == typeid(UCRGBPixelType)) { return IPL_DEPTH_8U; } else if(typeInfo == typeid(USRGBPixelType)) { return IPL_DEPTH_16U; } else if(typeInfo == typeid(FloatRGBPixelType)) { return IPL_DEPTH_64F; } else if(typeInfo == typeid(DoubleRGBPixelType)) { return IPL_DEPTH_64F; } else return -1; } bool mitk::ImageToOpenCVImageFilter::CheckImage( mitk::Image* image ) { if(image == 0) { MITK_WARN << "MITK Image is 0"; return false; } if(image->GetDimension() != 2) { MITK_WARN << "Only 2D Images allowed"; return false; } return true; } IplImage* mitk::ImageToOpenCVImageFilter::GetOpenCVImage() { - if(!m_OpenCVImage && this->CheckImage( m_Image ) == true ) + if(!this->CheckImage( m_Image )) + return 0; + if(!m_OpenCVImage) { try { AccessFixedTypeByItk(m_Image.GetPointer(), ItkImageProcessing, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ // gray image (UCRGBPixelType)(USRGBPixelType)(FloatRGBPixelType)(DoubleRGBPixelType), // rgb image (2) // dimensions ) } catch (const AccessByItkException& e) { std::cout << "Caught exception [from AccessFixedTypeByItk]: \n" << e.what() << "\n"; + return 0; } } return m_OpenCVImage; } void mitk::ImageToOpenCVImageFilter::SetImage( mitk::Image* _Image ) { - if(m_Image == _Image) return; +// if(m_Image == _Image) return; m_Image = _Image; m_OpenCVImage = 0; } diff --git a/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.h b/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.h index 26e909c92c..2153ef6212 100644 --- a/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.h +++ b/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.h @@ -1,141 +1,162 @@ /*=================================================================== 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 mitkImageToOpenCVImageFilter_h #define mitkImageToOpenCVImageFilter_h #include #include #include #include #include #include +#include #include #include +#include +#include #include "mitkOpenCVVideoSupportExports.h" namespace mitk { /** \brief A pseudo-Filter for creating OpenCV images from MITK images with the option of copying data or referencing it Last contributor: $Author: mueller $ */ class MITK_OPENCVVIDEOSUPPORT_EXPORT ImageToOpenCVImageFilter : public itk::Object { public: typedef itk::RGBPixel< unsigned char > UCRGBPixelType; typedef itk::RGBPixel< unsigned short > USRGBPixelType; typedef itk::RGBPixel< float > FloatRGBPixelType; typedef itk::RGBPixel< double > DoubleRGBPixelType; template static mitk::Image::Pointer ConvertIplToMitkImage( const IplImage * input, bool copyBuffer = true ); mitkClassMacro(ImageToOpenCVImageFilter, itk::Object); itkNewMacro(ImageToOpenCVImageFilter); void SetImage( mitk::Image* _Image ); itkGetMacro(Image, mitk::Image*); bool CheckImage(mitk::Image* image); /// /// Get the produced OpenCVImage. /// ATTENTION: Do not forget to release this image again with cvReleaseImage(). /// IplImage* GetOpenCVImage(); protected: ImageToOpenCVImageFilter(); // purposely hidden virtual ~ImageToOpenCVImageFilter(); int GetDepth(const std::type_info& typeInfo) const; template void ItkImageProcessing( itk::Image* image ); template void ItkImageProcessing( itk::Image,VImageDimension>* image ); protected: /// /// Saves if the filter should copy the data or just reference it /// mitk::WeakPointer m_Image; IplImage* m_OpenCVImage; }; template void mitk::ImageToOpenCVImageFilter::ItkImageProcessing( itk::Image* image ) { + PixelType pType = m_Image->GetPixelType(0); typedef itk::Image ImageType; - + const unsigned int numberOfComponents = pType.GetNumberOfComponents(); const unsigned int numberOfPixels = m_Image->GetDimension(0) * m_Image->GetDimension(1); - const unsigned int numberOfBytes = numberOfPixels * sizeof( typename ImageType::PixelType ); + const unsigned int numberOfValues = numberOfPixels * numberOfComponents; - const typename ImageType::PixelType * itkBuffer = image->GetBufferPointer(); +// const unsigned int numberOfBytes = numberOfValues * sizeof( typename ImageType::PixelType ); + const typename ImageType::PixelType * itkBuffer = image->GetBufferPointer(); typename ImageType::SizeType size = image->GetLargestPossibleRegion().GetSize(); // create new opencv image m_OpenCVImage = cvCreateImage( cvSize( size[0], size[1] ) - , GetDepth(typeid(TPixel)), 1 ); - - memcpy( m_OpenCVImage->imageData, itkBuffer, numberOfBytes ); + , GetDepth(typeid(TPixel)), numberOfComponents ); + const unsigned int stepsize = m_OpenCVImage->widthStep; + const unsigned int width = m_OpenCVImage->width; + const unsigned int height = m_OpenCVImage->height; +// memcpy( m_OpenCVImage->imageData, itkBuffer, numberOfBytes ); + TPixel* mitkImagedata = (TPixel*)m_Image->GetData(); + TPixel* cvdata= reinterpret_cast(m_OpenCVImage->imageData); + for(int y = 0 ; y < height; y++) + { + for(int x = 0 ; x < width*numberOfComponents ; x+=numberOfComponents) + { + for(int c = 0 ; c < numberOfComponents ; c++) + { + cvdata[(numberOfComponents-c-1)+x] =mitkImagedata[x+c]; + } + } + cvdata+= stepsize; + mitkImagedata+= width*numberOfComponents; + } } template void mitk::ImageToOpenCVImageFilter::ItkImageProcessing( itk::Image,VImageDimension>* image ) { typedef itk::RGBPixel RGBPixelType; typedef itk::Image RGBImageType; typedef itk::ImageRegionIterator RGBIteratorType; RGBIteratorType it(image, image->GetLargestPossibleRegion()); typename RGBImageType::SizeType size = image->GetLargestPossibleRegion().GetSize(); // create new opencv image m_OpenCVImage = cvCreateImage( cvSize( size[0], size[1] ), GetDepth(typeid(RGBPixelType)), 3 ); unsigned int x = 0,y = 0; CvScalar s; for ( it.GoToBegin(); !it.IsAtEnd(); ++it ) { s.val[0] = it.Value().GetBlue(); s.val[1] = it.Value().GetGreen(); s.val[2] = it.Value().GetRed(); //MITK_DEBUG << "[" << x << "," << y << "] " << s.val[0] << "(B)," << s.val[1] << "(G)," << s.val[2] << "(R)"; cvSet2D(m_OpenCVImage,y,x,s); ++x; // next line found if( x == size[0] ) { x = 0; ++y; } } } } // namespace #endif // mitkImageToOpenCVImageFilter_h 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/QmitkAboutDialog/QmitkAboutDialogGUI.ui b/Modules/QmitkExt/QmitkAboutDialog/QmitkAboutDialogGUI.ui index 2d7cbc7703..52569abe7b 100644 --- a/Modules/QmitkExt/QmitkAboutDialog/QmitkAboutDialogGUI.ui +++ b/Modules/QmitkExt/QmitkAboutDialog/QmitkAboutDialogGUI.ui @@ -1,172 +1,166 @@ QmitkAboutDialog 0 0 534 - 436 + 397 About false 0 0 MS Sans Serif 48 75 true MITK Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft 0 0 Revision %1 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::Horizontal QSizePolicy::Expanding 40 20 84 56 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.dkfz.de/en/mbi/"><span style=" text-decoration: underline; color:#0000ff;"><img src=":/qmitk/Logo_mbiATdkfz_small.png" /></span></a></p></body></html> true true true 0 - 0 - 497 - 349 + -51 + 496 + 375 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt; font-weight:600;">General Information</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">MITK [1] has been developed in joint effort by the Division of Medical and Biological Informatics of the German Cancer Research Center (DKFZ) [2] and Mint Medical GmbH [3].</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">[1] </span><a href="http://www.mitk.org/"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.mitk.org/</span></a></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">[2] </span><a href="http://www.dkfz.de/en/mbi/index.html"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.dkfz.de/en/mbi/index.html</span></a></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">[3] </span><a href="http://mint-medical.com/"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">http://mint-medical.com/</span></a></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; text-decoration: underline; color:#0000ff;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">To contact the MITK Team, see </span><a href="http://www.mitk.org/wiki/Contact"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.mitk.org/wiki/Contact</span></a><span style=" font-size:8pt;">.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:11pt;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">The Team</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Matthias Baumhauer, Markus Fangerau, Alfred Franz, Klaus Fritzsche, Ingmar Gergel, Caspar Goch, Anja Groch, Andreas Fetzer, Philipp Hartmann, Jan Hering, Johannes Kast, Kerstin-Meike Knautz, Lena Maier-Hein, Daniel Maleike, Hans-Peter Meinzer, Michael Müller, Jochen Neuhaus, Marco Nolden, Urte Rietdorf, Thiago dos Santos, Sabrina Schmidt, Tobias Schwarz, Alexander Seitel, Mathias Seitel, Daniel Stein, Johanna Stuke, Diana Wald, Xin Wang, Ingmar Wegner, </span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Jan Wörner, Sascha Zelzer</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">Third Party Libraries</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://docs.mitk.org/nightly-qt4/thirdpartylibs.html"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">http://docs.mitk.org/nightly-qt4/thirdpartylibs.html</span></a></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt; text-decoration: underline; color:#0000ff;"></p></body></html> +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">General Information</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">MITK [1] has been developed by the Division of Medical and Biological Informatics of the German Cancer Research Center (DKFZ).</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">[1] </span><a href="http://www.mitk.org/"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.mitk.org/</span></a></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">[2] </span><a href="http://www.dkfz.de/en/mbi/index.html"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.dkfz.de/en/mbi/index.html</span></a></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">To contact the MITK Team, see </span><a href="http://www.mitk.org/wiki/Contact"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">http://www.mitk.org/wiki/Contact</span></a><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;">Third Party Libraries</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://docs.mitk.org/nightly-qt4/thirdpartylibs.html"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;">http://docs.mitk.org/nightly-qt4/thirdpartylibs.html</span></a></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt; text-decoration: underline; color:#0000ff;"><br /></p></body></html> true true QDialogButtonBox::Close 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/Modules/ToFHardware/Testing/mitkToFCameraPMDCamBoardDeviceTest.cpp b/Modules/ToFHardware/Testing/mitkToFCameraPMDCamBoardDeviceTest.cpp index 225819454e..b373361df2 100644 --- a/Modules/ToFHardware/Testing/mitkToFCameraPMDCamBoardDeviceTest.cpp +++ b/Modules/ToFHardware/Testing/mitkToFCameraPMDCamBoardDeviceTest.cpp @@ -1,63 +1,63 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include /**Documentation * test for the class "ToFCameraPMDCamBoardDevice". */ int mitkToFCameraPMDCamBoardDeviceTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("ToFCameraPMDCamBoardDevice"); mitk::ToFCameraPMDCamBoardDevice::Pointer tofCameraPMDCamBoardDevice = mitk::ToFCameraPMDCamBoardDevice::New(); // No hardware attached for automatic testing -> test correct error handling MITK_TEST_CONDITION_REQUIRED(tofCameraPMDCamBoardDevice->ConnectCamera(), "Test ConnectCamera()"); MITK_TEST_CONDITION_REQUIRED(!tofCameraPMDCamBoardDevice->IsCameraActive(), "Test IsCameraActive() before StartCamera()"); MITK_TEST_OUTPUT(<<"Call StartCamera()"); tofCameraPMDCamBoardDevice->StartCamera(); MITK_TEST_CONDITION_REQUIRED(tofCameraPMDCamBoardDevice->IsCameraActive(), "Test IsCameraActive() after StartCamera()"); MITK_TEST_OUTPUT(<<"Call UpdateCamera()"); tofCameraPMDCamBoardDevice->UpdateCamera(); int numberOfPixels = tofCameraPMDCamBoardDevice->GetCaptureWidth()*tofCameraPMDCamBoardDevice->GetCaptureHeight(); MITK_INFO<GetDistances(distances,imageSequence); tofCameraPMDCamBoardDevice->GetAmplitudes(amplitudes,imageSequence); tofCameraPMDCamBoardDevice->GetIntensities(intensities,imageSequence); - tofCameraPMDCamBoardDevice->GetAllImages(distances,amplitudes,intensities,sourceData,requiredImageSequence,imageSequence); + tofCameraPMDCamBoardDevice->GetAllImages(distances,amplitudes,intensities,sourceData,requiredImageSequence,imageSequence, 0); MITK_TEST_CONDITION_REQUIRED(tofCameraPMDCamBoardDevice->IsCameraActive(), "Test IsCameraActive() before StopCamera()"); MITK_TEST_OUTPUT(<<"Call StopCamera()"); tofCameraPMDCamBoardDevice->StopCamera(); MITK_TEST_CONDITION_REQUIRED(!tofCameraPMDCamBoardDevice->IsCameraActive(), "Test IsCameraActive() after StopCamera()"); MITK_TEST_CONDITION_REQUIRED(tofCameraPMDCamBoardDevice->DisconnectCamera(), "Test DisonnectCamera()"); delete[] distances; delete[] amplitudes; delete[] intensities; delete[] sourceData; MITK_TEST_END(); } diff --git a/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.cpp b/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.cpp index e4002556a9..2f0c125ad5 100644 --- a/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.cpp +++ b/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.cpp @@ -1,334 +1,336 @@ /*=================================================================== 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 "mitkThreadedToFRawDataReconstruction.h" #include "mitkITKImageImport.h" #include "mitkImageDataItem.h" // stl includes #include #include #include // vtk includes #include #include #include // itk includes #include #include #ifdef WIN32 #include #else #include #endif #include #define _USE_MATH_DEFINES #include #define PI M_PI; #define cAir 299704944; #define fMod 20000000; namespace mitk { ThreadedToFRawDataReconstruction::ThreadedToFRawDataReconstruction(): m_Threader(0), m_CISDist(0), m_CISAmpl(0), m_CISInten(0), m_ThreadedCISDist(0), m_ThreadedCISAmpl(0), m_ThreadedCISInten(0), m_Init(0), m_Width(0), m_Height(0), m_SourceDataSize(0), m_ImageSize(0), m_SourceData(0) { m_ThreadData = new ThreadDataStruct; m_ThreadData->m_ModulationFrequency = fMod; m_ThreadData->m_ImageDataMutex = itk::FastMutexLock::New(); m_ThreadData->m_ThreadDataMutex = itk::FastMutexLock::New(); m_StackSize = 1; } ThreadedToFRawDataReconstruction::~ThreadedToFRawDataReconstruction() { if(m_ThreadData != NULL) delete m_ThreadData; if(m_CISDist != NULL) delete[] m_CISDist; if(m_CISAmpl != NULL) delete[] m_CISAmpl; if(m_CISInten != NULL) delete[] m_CISInten; if(m_ThreadedCISInten != NULL) delete[] m_ThreadedCISInten; if(m_ThreadedCISAmpl != NULL) delete[] m_ThreadedCISAmpl; if(m_ThreadedCISDist != NULL) delete[] m_ThreadedCISDist; } -void ThreadedToFRawDataReconstruction::Initialize(int width, int height, int modulationFrequency, int sourceDataSize ) +void ThreadedToFRawDataReconstruction::Initialize(int width, int height, int modulationFrequency, + int sourceDataSize ) { m_Width = width; m_Height = height; m_SourceDataSize = sourceDataSize; m_ImageSize = width * height; m_ThreadData->m_ModulationFrequency = modulationFrequency * 1e6; if(!m_Init) { m_SourceData = vtkShortArray::New(); m_SourceData->SetNumberOfComponents(m_SourceDataSize); m_SourceData->SetNumberOfTuples(4); m_SourceData->Allocate(1); m_CISDist = new float[m_ImageSize]; m_CISAmpl = new float[m_ImageSize]; m_CISInten = new float[m_ImageSize]; m_ThreadedCISDist = new float[m_ImageSize]; m_ThreadedCISAmpl = new float[m_ImageSize]; m_ThreadedCISInten = new float[m_ImageSize]; m_ThreadData->m_OutputData.push_back( m_ThreadedCISDist ); m_ThreadData->m_OutputData.push_back( m_ThreadedCISAmpl ); m_ThreadData->m_OutputData.push_back( m_ThreadedCISInten ); m_Init = true; } } void ThreadedToFRawDataReconstruction::SetChannelData(vtkShortArray* sourceData) { m_SourceData->DeepCopy(sourceData); } void ThreadedToFRawDataReconstruction::GetDistances(float* dist) { memcpy(dist, m_CISDist, m_ImageSize*sizeof(float) ); } void ThreadedToFRawDataReconstruction::GetAmplitudes(float* ampl) { memcpy(ampl, m_CISAmpl, m_ImageSize*sizeof(float)); } void ThreadedToFRawDataReconstruction::GetIntensities(float* inten) { memcpy(inten, m_CISInten, m_ImageSize*sizeof(float)); } void ThreadedToFRawDataReconstruction::GetAllData(float* dist, float* ampl, float* inten) { memcpy(dist, m_CISDist, m_ImageSize*sizeof(float) ); memcpy(ampl, m_CISAmpl, m_ImageSize*sizeof(float)); memcpy(inten, m_CISInten, m_ImageSize*sizeof(float)); } void ThreadedToFRawDataReconstruction::GenerateData() { if(m_Init) { this->BeforeThreadedGenerateData(); } } void ThreadedToFRawDataReconstruction::BeforeThreadedGenerateData() { int sourceDataSize = m_SourceDataSize; int lineWidth = m_Width; int frameHeight = m_Height; int channelSize = lineWidth*frameHeight << 1; int quadChannelSize = channelSize * 0.25; std::vector quad = std::vector(quadChannelSize); // clean the thread data array m_ThreadData->m_InputData.erase(m_ThreadData->m_InputData.begin(),m_ThreadData->m_InputData.end()); int channelNo = 0; while(channelNo < m_SourceData->GetNumberOfTuples()) { short* sourceData = new short[channelSize]; m_SourceData->GetTupleValue(channelNo, sourceData); quad.insert(quad.begin(), sourceData, sourceData+channelSize); m_ThreadData->m_InputData.push_back(quad); delete[]sourceData; ++channelNo; } if(m_Threader.IsNull()) { m_Threader = this->GetMultiThreader(); } int maxThreadNr = 0; if(m_Threader->GetGlobalDefaultNumberOfThreads()> 5) { maxThreadNr = 5; } else if(m_Threader->GetGlobalMaximumNumberOfThreads()>5) { maxThreadNr = 5; } else { maxThreadNr = m_Threader->GetGlobalMaximumNumberOfThreads(); } if ( m_ThreadData->m_Barrier.IsNull()) { m_ThreadData->m_Barrier = itk::Barrier::New(); m_ThreadData->m_Barrier->Initialize(maxThreadNr); // } m_ThreadData->m_DataSize = quadChannelSize; m_ThreadData->m_LineWidth = lineWidth; m_ThreadData->m_FrameHeight = frameHeight * 0.25; std::vector threadIDVector; int threadcounter = 0; while(threadcounter != maxThreadNr-1) { if (m_Threader->GetNumberOfThreads() < m_Threader->GetGlobalMaximumNumberOfThreads()) { int threadID = m_Threader->SpawnThread(this->ThreadedGenerateDataCallbackFunction, m_ThreadData); threadIDVector.push_back(threadID); threadcounter++; } } m_ThreadData->m_Barrier->Wait(); int count = 0; while(count != threadIDVector.size()) { m_Threader->TerminateThread(threadIDVector.at(count)); count++; } m_ThreadData->m_ImageDataMutex->Lock(); memcpy(m_CISDist, m_ThreadData->m_OutputData.at(0), (channelSize * 0.5)*sizeof(float)); memcpy(m_CISAmpl, m_ThreadData->m_OutputData.at(1), (channelSize * 0.5)*sizeof(float)); memcpy(m_CISInten, m_ThreadData->m_OutputData.at(2), (channelSize * 0.5)*sizeof(float)); m_ThreadData->m_ImageDataMutex->Unlock(); } ITK_THREAD_RETURN_TYPE ThreadedToFRawDataReconstruction::ThreadedGenerateDataCallbackFunction(void* data) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)data; if (pInfo == NULL) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == NULL) { return ITK_THREAD_RETURN_VALUE; } int quadrant = pInfo->ThreadID; ThreadDataStruct* threadData = (ThreadDataStruct*) pInfo->UserData; // some needed variables int x = 0; double phi = 0; double phi2 = 0; double A1 = 0; double A2 = 0; double A3 = 0; double A4 = 0; double A5 = 0; double A6 = 0; double A7 = 0; double A8 = 0; double A3m1 = 0; double A4m2 = 0; double A7m5 = 0; double A8m6 = 0; double cair = cAir; double pi = PI; double twoPi = pi + pi; long modFreq = fMod; threadData->m_ThreadDataMutex->Lock(); std::vector quad1 = threadData->m_InputData.at(0); std::vector quad2 = threadData->m_InputData.at(1); std::vector quad3 = threadData->m_InputData.at(2); std::vector quad4 = threadData->m_InputData.at(3); int index = quadrant << 1; int index2 = 3-quadrant; modFreq = threadData->m_ModulationFrequency; int linewidth = threadData->m_LineWidth; int frameheight = threadData->m_FrameHeight; threadData->m_ThreadDataMutex->Unlock(); double intermed1 = cair/(pi*(modFreq << 2)); double intermed2 = intermed1*500; int doubleLwidth = linewidth << 1; int datasize = doubleLwidth*frameheight << 2; do { index += doubleLwidth; x++; do { index -= 8; A1 = htons(quad1.at(index)); A2 = htons(quad2.at(index)); A3 = htons(quad3.at(index)); A4 = htons(quad4.at(index)); A5 = htons(quad1.at(index+1)); A6 = htons(quad2.at(index+1)); A7 = htons(quad3.at(index+1)); A8 = htons(quad4.at(index+1)); phi = atan2((A3 - A1),(A2 - A4)) + pi; phi2 = atan2((A7 - A5),(A6 - A8)); if(phi2<0) phi2 +=twoPi; A3m1 = A3*A3 - 2*A3*A1 + A1*A1; A4m2 = A4*A4 - 2*A4*A2 + A2*A2; A7m5 = A7*A7 - 2*A7*A5 + A5*A5; A8m6 = A8*A8 - 2*A8*A6 + A6*A6; threadData->m_ImageDataMutex->Lock(); threadData->m_OutputData.at(0)[index2] = (phi+phi2)*intermed2; //(((phi*intermed1) + (phi2*intermed1))/2)*1000; // threadData->m_OutputData.at(1)[index2] = (sqrt(A3m1 + A4m2)+sqrt(A7m5 + A8m6))*0.5; //(sqrt(A3m1 + A4m2)/2) + (sqrt(A7m5 + A8m6)/2); threadData->m_OutputData.at(2)[index2] = (A1+A2+A3+A4+A5+A6+A7+A8)*0.125; threadData->m_ImageDataMutex->Unlock(); index2 += 4; }while(index2 <= (x*linewidth) - (1+quadrant)); index += doubleLwidth; }while(index < datasize); threadData->m_Barrier->Wait(); return ITK_THREAD_RETURN_VALUE; } void ThreadedToFRawDataReconstruction::Update() { this->GenerateData(); } + } // end mitk namespace diff --git a/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.h b/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.h index 6a47cbf0bd..c1659f1f97 100644 --- a/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.h +++ b/Modules/ToFHardware/mitkThreadedToFRawDataReconstruction.h @@ -1,114 +1,119 @@ /*=================================================================== 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 __mitkThreadedToFRawDataReconstruction_h #define __mitkThreadedToFRawDataReconstruction_h // mitk includes #include "mitkImageSource.h" #include "mitkToFHardwareExports.h" // itk includes #include #include // vtk includes #include "vtkShortArray.h" namespace mitk { struct ThreadDataStruct { std::vector > m_InputData; std::vector m_OutputData; unsigned int m_DataSize; unsigned int m_LineWidth; unsigned int m_FrameHeight; unsigned int m_ModulationFrequency; itk::Barrier::Pointer m_Barrier; ///< barrier to synchronize ends of threads itk::FastMutexLock::Pointer m_ImageDataMutex; ///< mutex for coordinated access to image data itk::FastMutexLock::Pointer m_ThreadDataMutex; ///< mutex to control access to images }; class MITK_TOFHARDWARE_EXPORT ThreadedToFRawDataReconstruction : public itk::ProcessObject { public: mitkClassMacro( ThreadedToFRawDataReconstruction , itk::ProcessObject ); itkNewMacro( Self ); itkGetMacro(Init, bool); void SetChannelData(vtkShortArray* sourceData); - void Initialize(int width, int height, int modulationFrequency, int sourceDataSize); + void Initialize(int width, int height, int modulationFrequency, int sourceDataSize ); void GetDistances(float* dist); void GetAmplitudes(float* ampl); void GetIntensities(float* inten); void GetAllData(float* dist, float* ampl, float* inten); void Update(); protected: /*! \brief standard constructor */ ThreadedToFRawDataReconstruction(); /*! \brief standard destructor */ ~ThreadedToFRawDataReconstruction(); /*! \brief method generating the outputs of this filter. Called in the updated process of the pipeline. This method generates the two outputs of the ToFImageSource: The distance and the intensity image */ virtual void GenerateData(); /*! \brief method configures the camera output and prepares the thread data struct for threaded data generation */ virtual void BeforeThreadedGenerateData(); /*! \brief threader callback function for multi threaded data generation */ static ITK_THREAD_RETURN_TYPE ThreadedGenerateDataCallbackFunction(void* data); + /*! + \brief Method to reduce image size from original controller size to output size (200x200px) + and rotate the image. This is needed due to defect pixel rows at the rim of the chip + */ + void ResizeOutputImage(float* in, float* out); // member variables int m_StackSize; ///< int m_Width; int m_Height; int m_ImageSize; int m_SourceDataSize; vtkShortArray* m_SourceData; bool m_Init; float* m_CISDist; ///< holds the distance information from for one distance image slice float* m_CISAmpl; ///< holds the amplitude information from for one amplitude image slice float* m_CISInten; ///< holds the intensity information from for one intensity image slice float* m_ThreadedCISDist; float* m_ThreadedCISAmpl; float* m_ThreadedCISInten; itk::MultiThreader::Pointer m_Threader; ThreadDataStruct* m_ThreadData; }; } //end mitk namespace #endif // __mitkThreadedToFRawDataReconstruction_h diff --git a/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.cpp b/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.cpp index fbbfeb5c1d..b6beb65b19 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.cpp +++ b/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.cpp @@ -1,225 +1,306 @@ /*=================================================================== 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 "mitkToFCameraPMDCamBoardController.h" #include "mitkToFConfig.h" #include +// vnl includes +#include "vnl/vnl_matrix.h" + //Plugin defines for CamBoard #define SOURCE_PARAM "" #define PROC_PARAM "" extern PMDHandle m_PMDHandle; //TODO - extern PMDDataDescription m_DataDescription; //TODO +struct SourceDataStruct { + PMDDataDescription dataDescription; + char sourceData; +}; namespace mitk { - ToFCameraPMDCamBoardController::ToFCameraPMDCamBoardController() + ToFCameraPMDCamBoardController::ToFCameraPMDCamBoardController(): m_InternalCaptureWidth(0), + m_InternalCaptureHeight(0), m_InternalPixelNumber(0) { m_SourcePlugin = MITK_TOF_PMDCAMBOARD_SOURCE_PLUGIN; m_SourceParam = SOURCE_PARAM; m_ProcPlugin = MITK_TOF_PMDCAMBOARD_PROCESSING_PLUGIN; m_ProcParam = PROC_PARAM; } ToFCameraPMDCamBoardController::~ToFCameraPMDCamBoardController() { } bool ToFCameraPMDCamBoardController::OpenCameraConnection() { if(!m_ConnectionCheck) { m_PMDRes = pmdOpen (&m_PMDHandle , m_SourcePlugin , m_SourceParam , m_ProcPlugin , m_ProcParam ); m_ConnectionCheck = ErrorText(m_PMDRes); if (!m_ConnectionCheck) { return m_ConnectionCheck; } - // get image properties from camera this->UpdateCamera(); - m_PMDRes = pmdGetSourceDataDescription(m_PMDHandle, &m_DataDescription); - ErrorText(m_PMDRes); - m_CaptureWidth = m_DataDescription.img.numColumns; - m_CaptureHeight = m_DataDescription.img.numRows; - m_PixelNumber = m_CaptureWidth*m_CaptureHeight; - m_NumberOfBytes = m_PixelNumber * sizeof(float); - m_SourceDataSize = m_DataDescription.size; - m_SourceDataStructSize = m_DataDescription.size + sizeof(PMDDataDescription); + this->m_PMDRes = pmdGetSourceDataDescription(m_PMDHandle, &m_DataDescription); + ErrorText(m_PMDRes); + this->m_InternalCaptureWidth = m_DataDescription.img.numColumns; + this->m_CaptureWidth = 200; + this->m_InternalCaptureHeight = m_DataDescription.img.numRows; + this->m_CaptureHeight = 200; + this->m_InternalPixelNumber = m_InternalCaptureWidth*m_InternalCaptureHeight; + this->m_PixelNumber = m_CaptureWidth*m_CaptureHeight; + + this->m_NumberOfBytes = m_InternalPixelNumber * sizeof(float); + this->m_SourceDataSize = m_DataDescription.size; + this->m_SourceDataStructSize = m_DataDescription.size + sizeof(PMDDataDescription); MITK_INFO << "Datasource size: " << this->m_SourceDataSize <GetIntegrationTime(); - MITK_INFO << "Modulation Frequence: " << this->GetModulationFrequency(); - + MITK_INFO << "Modulation Frequency: " << this->GetModulationFrequency(); return m_ConnectionCheck; } else return m_ConnectionCheck; } bool mitk::ToFCameraPMDCamBoardController::SetDistanceOffset( float offset ) { std::stringstream command; command<<"SetSoftOffset "<m_PMDRes = pmdSourceCommand(m_PMDHandle,0,0,command.str().c_str()); return ErrorText(this->m_PMDRes); } float mitk::ToFCameraPMDCamBoardController::GetDistanceOffset() { char offset[16]; this->m_PMDRes = pmdSourceCommand(m_PMDHandle, offset, 16, "GetSoftOffset"); ErrorText(this->m_PMDRes); return atof(offset); } bool mitk::ToFCameraPMDCamBoardController::SetRegionOfInterest( unsigned int leftUpperCornerX, unsigned int leftUpperCornerY, unsigned int width, unsigned int height ) { + // CAVE: This function does not work properly, don't use unless you know what you're doing!! // check if leftUpperCornerX and width are divisible by 3 otherwise round to the next value divisible by 3 unsigned int factor = leftUpperCornerX/3; leftUpperCornerX = 3*factor; factor = width/3; width = 3*factor; std::stringstream command; command<<"SetROI "<m_PMDRes = pmdSourceCommand(m_PMDHandle,0,0,command.str().c_str()); return ErrorText(this->m_PMDRes); } bool mitk::ToFCameraPMDCamBoardController::SetRegionOfInterest( unsigned int roi[4] ) { return this->SetRegionOfInterest(roi[0],roi[1],roi[2],roi[3]); } unsigned int* mitk::ToFCameraPMDCamBoardController::GetRegionOfInterest() { /* char result[64]; this->m_PMDRes = pmdSourceCommand(m_PMDHandle, result, 64, "GetROI"); ErrorText(this->m_PMDRes); // convert char array to uint array unsigned int roi[4]; std::stringstream currentValue; int counter = 0; std::string resultString = result; char blank = ' '; for (int i=0; i<64; i++) { if (result[i]!=blank) { currentValue<m_PMDRes = pmdSourceCommand(m_PMDHandle, 0, 0, "SetExposureMode Normal"); return ErrorText(this->m_PMDRes); } else if (mode==1) // SMB mode { this->m_PMDRes = pmdSourceCommand(m_PMDHandle, 0, 0, "SetExposureMode SMB"); return ErrorText(this->m_PMDRes); } else { MITK_ERROR<<"Specified exposure mode not supported. Exposure mode must be 0 (Normal) or 1 (SMB)"; return false; } } bool mitk::ToFCameraPMDCamBoardController::SetFieldOfView( float fov ) { std::stringstream commandStream; commandStream<<"SetFOV "<m_PMDRes = pmdProcessingCommand(m_PMDHandle, 0, 0, commandStream.str().c_str()); return ErrorText(this->m_PMDRes); } bool mitk::ToFCameraPMDCamBoardController::SetFPNCalibration( bool on ) { if(on) { this->m_PMDRes=pmdSourceCommand(m_PMDHandle,0,0,"SetFPNCalibration On"); return this->ErrorText(this->m_PMDRes); } else { this->m_PMDRes=pmdSourceCommand(m_PMDHandle,0,0,"SetFPNCalibration Off"); return this->ErrorText(this->m_PMDRes); } } bool mitk::ToFCameraPMDCamBoardController::SetFPPNCalibration( bool on ) { if(on) { this->m_PMDRes=pmdProcessingCommand(m_PMDHandle,0,0,"SetFPPNCalibration On"); return this->ErrorText(this->m_PMDRes); } else { this->m_PMDRes=pmdProcessingCommand(m_PMDHandle,0,0,"SetFPPNCalibration Off"); return this->ErrorText(this->m_PMDRes); } } bool mitk::ToFCameraPMDCamBoardController::SetLinearityCalibration( bool on ) { if(on) { this->m_PMDRes=pmdProcessingCommand(m_PMDHandle,0,0,"SetLinearityCalibration On"); return this->ErrorText(this->m_PMDRes); } else { this->m_PMDRes=pmdProcessingCommand(m_PMDHandle,0,0,"SetLinearityCalibration Off"); return this->ErrorText(this->m_PMDRes); } } bool mitk::ToFCameraPMDCamBoardController::SetLensCalibration( bool on ) { if (on) { this->m_PMDRes = pmdProcessingCommand(m_PMDHandle, 0, 0, "SetLensCalibration On"); return ErrorText(this->m_PMDRes); } else { this->m_PMDRes = pmdProcessingCommand(m_PMDHandle, 0, 0, "SetLensCalibration Off"); return ErrorText(this->m_PMDRes); } } + bool ToFCameraPMDCamBoardController::GetAmplitudes(float* amplitudeArray) + { + float* tempArray = new float[m_InternalCaptureWidth*m_InternalCaptureHeight]; + this->m_PMDRes = pmdGetAmplitudes(m_PMDHandle, tempArray, this->m_NumberOfBytes); + this->TransformCameraOutput(tempArray, amplitudeArray, false); + delete[] tempArray; + return ErrorText(this->m_PMDRes); + } + + bool ToFCameraPMDCamBoardController::GetAmplitudes(char* sourceData, float* amplitudeArray) + { + float* tempArray = new float[m_InternalCaptureWidth*m_InternalCaptureHeight]; + this->m_PMDRes = pmdCalcAmplitudes(m_PMDHandle, tempArray, this->m_NumberOfBytes, m_DataDescription, &((SourceDataStruct*)sourceData)->sourceData); + this->TransformCameraOutput(tempArray, amplitudeArray, false); + delete[] tempArray; + return ErrorText(this->m_PMDRes); + } + + bool ToFCameraPMDCamBoardController::GetIntensities(float* intensityArray) + { + float* tempArray = new float[m_InternalCaptureWidth*m_InternalCaptureHeight]; + this->m_PMDRes = pmdGetIntensities(m_PMDHandle, tempArray, this->m_NumberOfBytes); + this->TransformCameraOutput(tempArray, intensityArray, false); + delete[] tempArray; + return ErrorText(this->m_PMDRes); + } + + bool ToFCameraPMDCamBoardController::GetIntensities(char* sourceData, float* intensityArray) + { + float* tempArray = new float[m_InternalCaptureWidth*m_InternalCaptureHeight]; + this->m_PMDRes = pmdCalcIntensities(m_PMDHandle, tempArray, this->m_NumberOfBytes, m_DataDescription, &((SourceDataStruct*)sourceData)->sourceData); + this->TransformCameraOutput(tempArray, intensityArray, false); + delete[] tempArray; + return ErrorText(this->m_PMDRes); + } + + bool ToFCameraPMDCamBoardController::GetDistances(float* distanceArray) + { + float* tempArray = new float[m_InternalCaptureWidth*m_InternalCaptureHeight]; + this->m_PMDRes = pmdGetDistances(m_PMDHandle, tempArray, this->m_NumberOfBytes); + this->TransformCameraOutput(tempArray, distanceArray, true); + delete[] tempArray; + return ErrorText(this->m_PMDRes); + } + + bool ToFCameraPMDCamBoardController::GetDistances(char* sourceData, float* distanceArray) + { + float* tempArray = new float[m_InternalCaptureWidth*m_InternalCaptureHeight]; + this->m_PMDRes = pmdCalcDistances(m_PMDHandle, tempArray, this->m_NumberOfBytes, m_DataDescription, &((SourceDataStruct*)sourceData)->sourceData); + this->TransformCameraOutput(tempArray, distanceArray, true); + delete[] tempArray; + return ErrorText(this->m_PMDRes); + } + + void ToFCameraPMDCamBoardController::TransformCameraOutput( float* in, float* out, bool isDist ) + { + vnl_matrix inMat = vnl_matrix(m_InternalCaptureHeight,m_InternalCaptureWidth); + inMat.copy_in(in); + vnl_matrix outMat = vnl_matrix(m_CaptureHeight, m_CaptureWidth); + vnl_matrix temp = vnl_matrix(m_CaptureHeight, m_CaptureWidth); + temp = inMat.extract(m_CaptureHeight, m_CaptureWidth, 0,1); + outMat = temp.transpose(); + if(isDist) + { + outMat*=1000; + } + outMat.copy_out(out); + inMat.clear(); + outMat.clear(); + temp.clear(); + } } diff --git a/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.h b/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.h index c3ba1d6fd1..acf682ae33 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.h +++ b/Modules/ToFHardware/mitkToFCameraPMDCamBoardController.h @@ -1,116 +1,147 @@ /*=================================================================== 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 __mitkToFCameraPMDCamBoardController_h #define __mitkToFCameraPMDCamBoardController_h #include "mitkToFHardwareExports.h" #include "mitkCommon.h" #include "mitkToFCameraPMDController.h" #include "itkObject.h" #include "itkObjectFactory.h" namespace mitk { /** * @brief Interface to the Time-of-Flight (ToF) camera PMD CamBoard * * * @ingroup ToFHardware */ class MITK_TOFHARDWARE_EXPORT ToFCameraPMDCamBoardController : public mitk::ToFCameraPMDController { public: mitkClassMacro( ToFCameraPMDCamBoardController , mitk::ToFCameraPMDController ); itkNewMacro( Self ); /*! \brief opens a connection to the ToF camera and initializes the hardware specific members \return returns whether the connection was successful (true) or not (false) */ virtual bool OpenCameraConnection(); /*! \brief sets an additional distance offset which will be added to all distance values. \param offset offset in m */ bool SetDistanceOffset( float offset ); /*! \brief returns the currently applied distance offset in m \param offset offset in m */ float GetDistanceOffset(); /*! \brief Setting the region of interest, the camera is configured to only output a certain area of the image. \param leftUpperCornerX x value of left upper corner of region \param leftUpperCornerX y value of left upper corner of region \param width width of region \param height height of region */ bool SetRegionOfInterest( unsigned int leftUpperCornerX, unsigned int leftUpperCornerY, unsigned int width, unsigned int height ); /*! \brief Setting the region of interest, the camera is configured to only output a certain area of the image. \param roi region of interest. roi[0]: x value of left upper corner, roi[1]: y value of left upper corner, roi[2]: width, roi[3]: height */ bool SetRegionOfInterest( unsigned int roi[4] ); /*! \brief returns the region of interest currently set \return currently set region of interest. */ unsigned int* GetRegionOfInterest(); /*! \brief sets the exposure mode of the CamCube \param mode exposure mode. 0: normal mode (one exposure), 1: Suppression of motion blur (SMB), minimizes the time needed to capture a distance image from the camera which results in a reduced amount of motion artifact but may lead to increased noise. */ bool SetExposureMode( int mode ); /*! \brief Sets the field of view of the camera lens. \param fov field of view in degrees. The default value is 40 degrees. */ bool SetFieldOfView( float fov ); /* \brief Enable/Disable PMD fixed pattern noise (FPN) calibration \param on enabled (true), disabled (false) */ bool SetFPNCalibration( bool on ); /* \brief Enable/Disable PMD fixed pattern phase noise (FPPN) calibration \param on enabled (true), disabled (false) */ bool SetFPPNCalibration( bool on ); /* \brief Enable/Disable PMD linearity calibration \param on enabled (true), disabled (false) */ bool SetLinearityCalibration( bool on ); /* \brief Enable/Disable PMD lens calibration \param on enabled (true), disabled (false) */ bool SetLensCalibration( bool on ); + /* + \brief Returns amplitude data, performs camera data transform + \param output data array + */ + bool GetAmplitudes(float* amplitudeArray); + bool GetAmplitudes(char* sourceData, float* amplitudeArray); + /* + \brief Returns intensity data, performs camera data transform + \param output data array + */ + bool GetIntensities(float* intensityArray); + bool GetIntensities(char* sourceData, float* intensityArray); + /* + \brief Returns distance data, performs camera data transform + \param output data array + */ + bool GetDistances(float* distanceArray); + bool GetDistances(char* sourceData, float* distanceArray); + itkGetMacro(InternalCaptureWidth, int); + itkGetMacro(InternalCaptureHeight, int); protected: ToFCameraPMDCamBoardController(); ~ToFCameraPMDCamBoardController(); - private: + /* + \brief Transform the output of the camera, i.e. cut invalid pixels, and rotate 90° + counterclockwise + \param input data array of original size (207x204) + \param rotated output data array of reduced size (200x200) + */ + void TransformCameraOutput(float* in, float* out, bool isDist); + + // member variables + unsigned int m_InternalCaptureWidth; ///< variable holds the original image data width + unsigned int m_InternalCaptureHeight; ///< variable holds the original image data height + unsigned int m_InternalPixelNumber; ///< variable holds the original number of pixel }; } //END mitk namespace #endif diff --git a/Modules/ToFHardware/mitkToFCameraPMDCamBoardControllerStub.cpp b/Modules/ToFHardware/mitkToFCameraPMDCamBoardControllerStub.cpp index b5671e037d..771d86fa0b 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDCamBoardControllerStub.cpp +++ b/Modules/ToFHardware/mitkToFCameraPMDCamBoardControllerStub.cpp @@ -1,102 +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. ===================================================================*/ #include "mitkToFCameraPMDCamBoardController.h" namespace mitk { ToFCameraPMDCamBoardController::ToFCameraPMDCamBoardController() { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; } ToFCameraPMDCamBoardController::~ToFCameraPMDCamBoardController() { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; } bool ToFCameraPMDCamBoardController::OpenCameraConnection() { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } bool mitk::ToFCameraPMDCamBoardController::SetDistanceOffset( float offset ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } float mitk::ToFCameraPMDCamBoardController::GetDistanceOffset() { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return 0.0; } bool mitk::ToFCameraPMDCamBoardController::SetRegionOfInterest( unsigned int leftUpperCornerX, unsigned int leftUpperCornerY, unsigned int width, unsigned int height ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } bool mitk::ToFCameraPMDCamBoardController::SetRegionOfInterest( unsigned int roi[4] ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } unsigned int* mitk::ToFCameraPMDCamBoardController::GetRegionOfInterest() { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return NULL; } bool mitk::ToFCameraPMDCamBoardController::SetExposureMode( int mode ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } bool mitk::ToFCameraPMDCamBoardController::SetFieldOfView( float fov ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } bool mitk::ToFCameraPMDCamBoardController::SetFPNCalibration( bool on ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } bool mitk::ToFCameraPMDCamBoardController::SetFPPNCalibration( bool on ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } bool mitk::ToFCameraPMDCamBoardController::SetLinearityCalibration( bool on ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } bool mitk::ToFCameraPMDCamBoardController::SetLensCalibration( bool on ) { MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; return true; } + + bool ToFCameraPMDCamBoardController::GetAmplitudes(float* amplitudeArray) + { + MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; + return true; + } + + bool ToFCameraPMDCamBoardController::GetAmplitudes(char* sourceData, float* amplitudeArray) + { + MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; + return true; + } + + bool ToFCameraPMDCamBoardController::GetIntensities(float* intensityArray) + { + MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; + return true; + } + + bool ToFCameraPMDCamBoardController::GetIntensities(char* sourceData, float* intensityArray) + { + MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; + return true; + } + + bool ToFCameraPMDCamBoardController::GetDistances(float* distanceArray) + { + MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; + return true; + } + + bool ToFCameraPMDCamBoardController::GetDistances(char* sourceData, float* distanceArray) + { + MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; + return true; + } + + void ToFCameraPMDCamBoardController::TransformCameraOutput(float* in, float* out, bool isDist) + { + MITK_WARN("ToF") << "Error: PMD CamBoard currently not available"; + } + } diff --git a/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.cpp b/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.cpp index 5bbe284f84..ff8417536b 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.cpp +++ b/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.cpp @@ -1,79 +1,128 @@ /*=================================================================== 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 "mitkToFCameraPMDCamBoardDevice.h" #include "mitkToFCameraPMDCamBoardController.h" #include "mitkRealTimeClock.h" #include "itkMultiThreader.h" namespace mitk { ToFCameraPMDCamBoardDevice::ToFCameraPMDCamBoardDevice() { m_Controller = ToFCameraPMDCamBoardController::New(); } ToFCameraPMDCamBoardDevice::~ToFCameraPMDCamBoardDevice() { } void ToFCameraPMDCamBoardDevice::SetRegionOfInterest(unsigned int leftUpperCornerX, unsigned int leftUpperCornerY, unsigned int width, unsigned int height) { if (m_Controller.IsNotNull()) { dynamic_cast(m_Controller.GetPointer())->SetRegionOfInterest(leftUpperCornerX,leftUpperCornerY,width,height); } } void ToFCameraPMDCamBoardDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue ) { this->m_PropertyList->SetProperty(propertyKey, propertyValue); ToFCameraPMDCamBoardController::Pointer myController = dynamic_cast(this->m_Controller.GetPointer()); bool boolValue = false; GetBoolProperty(propertyKey, boolValue); if (strcmp(propertyKey, "SetFPNCalibration") == 0) { myController->SetFPNCalibration(boolValue); } else if (strcmp(propertyKey, "SetFPPNCalibration") == 0) { myController->SetFPPNCalibration(boolValue); } else if (strcmp(propertyKey, "SetLinearityCalibration") == 0) { myController->SetLinearityCalibration(boolValue); } else if (strcmp(propertyKey, "SetLensCalibration") == 0) { myController->SetLensCalibration(boolValue); } else if (strcmp(propertyKey, "SetExposureMode") == 0) { if (boolValue) { myController->SetExposureMode(1); } else { myController->SetExposureMode(0); } } } + void ToFCameraPMDCamBoardDevice::GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, + int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray) + { + if (m_CameraActive) + { + // 1) copy the image buffer + // 2) convert the distance values from m to mm + + // check for empty buffer + if (this->m_ImageSequence < 0) + { + // buffer empty + MITK_INFO << "Buffer empty!! "; + capturedImageSequence = this->m_ImageSequence; + return; + } + // determine position of image in buffer + int pos = 0; + if ((requiredImageSequence < 0) || (requiredImageSequence > this->m_ImageSequence)) + { + capturedImageSequence = this->m_ImageSequence; + pos = this->m_CurrentPos; + //MITK_INFO << "Required image not found! Required: " << requiredImageSequence << " delivered/current: " << this->m_ImageSequence; + } + else if (requiredImageSequence <= this->m_ImageSequence - this->m_BufferSize) + { + capturedImageSequence = (this->m_ImageSequence - this->m_BufferSize) + 1; + pos = (this->m_CurrentPos + 1) % this->m_BufferSize; + //MITK_INFO << "Out of buffer! Required: " << requiredImageSequence << " delivered: " << capturedImageSequence << " current: " << this->m_ImageSequence; + } + else // (requiredImageSequence > this->m_ImageSequence - this->m_BufferSize) && (requiredImageSequence <= this->m_ImageSequence) + { + capturedImageSequence = requiredImageSequence; + pos = (this->m_CurrentPos + (10-(this->m_ImageSequence - requiredImageSequence))) % this->m_BufferSize; + } + + m_ImageMutex->Lock(); + this->m_Controller->GetDistances(this->m_SourceDataBuffer[pos], distanceArray); + this->m_Controller->GetAmplitudes(this->m_SourceDataBuffer[pos], amplitudeArray); + this->m_Controller->GetIntensities(this->m_SourceDataBuffer[pos], intensityArray); + memcpy(sourceDataArray, this->m_SourceDataBuffer[this->m_CurrentPos], this->m_SourceDataSize); + m_ImageMutex->Unlock(); + + } + else + { + MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; + } + } } diff --git a/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.h b/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.h index 3d94778532..e2a1164613 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.h +++ b/Modules/ToFHardware/mitkToFCameraPMDCamBoardDevice.h @@ -1,70 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkToFCameraPMDCamBoardDevice_h #define __mitkToFCameraPMDCamBoardDevice_h #include "mitkToFHardwareExports.h" #include "mitkCommon.h" #include "mitkToFCameraDevice.h" #include "mitkToFCameraPMDDevice.h" #include "mitkToFCameraPMDCamBoardController.h" #include "itkObject.h" #include "itkObjectFactory.h" #include "itkMultiThreader.h" #include "itkFastMutexLock.h" namespace mitk { /** * @brief Device class representing a PMD CamBoard camera * * @ingroup ToFHardware */ class MITK_TOFHARDWARE_EXPORT ToFCameraPMDCamBoardDevice : public ToFCameraPMDDevice { public: mitkClassMacro( ToFCameraPMDCamBoardDevice , ToFCameraPMDDevice ); itkNewMacro( Self ); /*! \brief set a BaseProperty */ virtual void SetProperty( const char *propertyKey, BaseProperty* propertyValue ); /*! \brief Setting the region of interest, the camera is configured to only output a certain area of the image. \param leftUpperCornerX x value of left upper corner of region \param leftUpperCornerX y value of left upper corner of region \param width width of region \param height height of region */ void SetRegionOfInterest( unsigned int leftUpperCornerX, unsigned int leftUpperCornerY, unsigned int width, unsigned int height ); + void GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray); + protected: ToFCameraPMDCamBoardDevice(); ~ToFCameraPMDCamBoardDevice(); - private: }; } //END mitk namespace #endif diff --git a/Modules/ToFHardware/mitkToFCameraPMDController.cpp b/Modules/ToFHardware/mitkToFCameraPMDController.cpp index 96784a72f9..2bceef7292 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDController.cpp +++ b/Modules/ToFHardware/mitkToFCameraPMDController.cpp @@ -1,169 +1,169 @@ /*=================================================================== 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 "mitkToFCameraPMDController.h" #include #include PMDHandle m_PMDHandle; //TODO PMDDataDescription m_DataDescription; //TODO struct SourceDataStruct { PMDDataDescription dataDescription; char sourceData; }; namespace mitk { ToFCameraPMDController::ToFCameraPMDController(): m_PMDRes(0), m_PixelNumber(40000), m_NumberOfBytes(0), m_CaptureWidth(200), m_CaptureHeight(200), m_SourceDataSize(0), m_SourceDataStructSize(0), m_ConnectionCheck(false), m_InputFileName("") { } ToFCameraPMDController::~ToFCameraPMDController() { } bool ToFCameraPMDController::CloseCameraConnection() { m_PMDRes = pmdClose(m_PMDHandle); m_ConnectionCheck = ErrorText(m_PMDRes); m_PMDHandle = 0; return m_ConnectionCheck; } bool ToFCameraPMDController::ErrorText(int error) { if(error != PMD_OK) { pmdGetLastError (m_PMDHandle, m_PMDError, 128); MITK_ERROR << "Camera Error " << m_PMDError; return false; } else return true; } bool ToFCameraPMDController::UpdateCamera() { m_PMDRes = pmdUpdate(m_PMDHandle); return ErrorText(m_PMDRes); } bool ToFCameraPMDController::GetAmplitudes(float* amplitudeArray) { this->m_PMDRes = pmdGetAmplitudes(m_PMDHandle, amplitudeArray, this->m_NumberOfBytes); return ErrorText(this->m_PMDRes); } bool ToFCameraPMDController::GetAmplitudes(char* sourceData, float* amplitudeArray) { this->m_PMDRes = pmdCalcAmplitudes(m_PMDHandle, amplitudeArray, this->m_NumberOfBytes, m_DataDescription, &((SourceDataStruct*)sourceData)->sourceData); return ErrorText(this->m_PMDRes); } bool ToFCameraPMDController::GetIntensities(float* intensityArray) { this->m_PMDRes = pmdGetIntensities(m_PMDHandle, intensityArray, this->m_NumberOfBytes); return ErrorText(this->m_PMDRes); } bool ToFCameraPMDController::GetIntensities(char* sourceData, float* intensityArray) { this->m_PMDRes = pmdCalcIntensities(m_PMDHandle, intensityArray, this->m_NumberOfBytes, m_DataDescription, &((SourceDataStruct*)sourceData)->sourceData); return ErrorText(this->m_PMDRes); } bool ToFCameraPMDController::GetDistances(float* distanceArray) { this->m_PMDRes = pmdGetDistances(m_PMDHandle, distanceArray, this->m_NumberOfBytes); return ErrorText(this->m_PMDRes); } + bool ToFCameraPMDController::GetDistances(char* sourceData, float* distanceArray) + { + this->m_PMDRes = pmdCalcDistances(m_PMDHandle, distanceArray, this->m_NumberOfBytes, m_DataDescription, &((SourceDataStruct*)sourceData)->sourceData); + return ErrorText(this->m_PMDRes); + } + bool ToFCameraPMDController::GetSourceData(char* sourceDataArray) { this->m_PMDRes = pmdGetSourceDataDescription(m_PMDHandle, &m_DataDescription); if (!ErrorText(this->m_PMDRes)) { return false; } memcpy(&((SourceDataStruct*)sourceDataArray)->dataDescription, &m_DataDescription, sizeof(m_DataDescription)); this->m_PMDRes = pmdGetSourceData(m_PMDHandle, &((SourceDataStruct*)sourceDataArray)->sourceData, this->m_SourceDataSize); return ErrorText(this->m_PMDRes); } bool ToFCameraPMDController::GetShortSourceData( short* sourceData) { this->m_PMDRes = pmdGetSourceDataDescription(m_PMDHandle,&m_DataDescription); ErrorText( this->m_PMDRes); this->m_PMDRes = pmdGetSourceData(m_PMDHandle,sourceData,m_DataDescription.size); return ErrorText( this->m_PMDRes); } - bool ToFCameraPMDController::GetDistances(char* sourceData, float* distanceArray) - { - this->m_PMDRes = pmdCalcDistances(m_PMDHandle, distanceArray, this->m_NumberOfBytes, m_DataDescription, &((SourceDataStruct*)sourceData)->sourceData); - return ErrorText(this->m_PMDRes); - } - int ToFCameraPMDController::SetIntegrationTime(unsigned int integrationTime) { unsigned int result; this->m_PMDRes = pmdGetValidIntegrationTime(m_PMDHandle, &result, 0, AtLeast, integrationTime); ErrorText(this->m_PMDRes); if (this->m_PMDRes != 0) { return 0; } this->m_PMDRes = pmdSetIntegrationTime(m_PMDHandle, 0, result); ErrorText(this->m_PMDRes); return result; } int ToFCameraPMDController::GetIntegrationTime() { unsigned int integrationTime = 0; this->m_PMDRes = pmdGetIntegrationTime(m_PMDHandle, &integrationTime, 0); ErrorText(this->m_PMDRes); return integrationTime; } int ToFCameraPMDController::SetModulationFrequency(unsigned int modulationFrequency) { unsigned int result; this->m_PMDRes = pmdGetValidModulationFrequency(m_PMDHandle, &result, 0, AtLeast, (modulationFrequency*1000000)); ErrorText(this->m_PMDRes); if (this->m_PMDRes != 0) { return 0; } this->m_PMDRes = pmdSetModulationFrequency(m_PMDHandle, 0, result); ErrorText(this->m_PMDRes); return (result/1000000);; } int ToFCameraPMDController::GetModulationFrequency() { unsigned int modulationFrequency = 0; this->m_PMDRes = pmdGetModulationFrequency (m_PMDHandle, &modulationFrequency, 0); ErrorText(this->m_PMDRes); return (modulationFrequency/1000000); } void ToFCameraPMDController::SetInputFileName(std::string inputFileName) { this->m_InputFileName = inputFileName; } } diff --git a/Modules/ToFHardware/mitkToFCameraPMDDevice.cpp b/Modules/ToFHardware/mitkToFCameraPMDDevice.cpp index e369ab3c70..2c94d47052 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDDevice.cpp +++ b/Modules/ToFHardware/mitkToFCameraPMDDevice.cpp @@ -1,410 +1,414 @@ /*=================================================================== 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 "mitkToFCameraPMDDevice.h" #include "mitkRealTimeClock.h" #include "itkMultiThreader.h" #include namespace mitk { ToFCameraPMDDevice::ToFCameraPMDDevice() : m_SourceDataBuffer(NULL), m_SourceDataArray(NULL) { } ToFCameraPMDDevice::~ToFCameraPMDDevice() { this->CleanUpSourceData(); CleanupPixelArrays(); } bool ToFCameraPMDDevice::ConnectCamera() { bool ok = false; if (m_Controller) { ok = m_Controller->OpenCameraConnection(); if (ok) { this->m_CaptureWidth = m_Controller->GetCaptureWidth(); this->m_CaptureHeight = m_Controller->GetCaptureHeight(); this->m_SourceDataSize = m_Controller->GetSourceDataStructSize(); this->m_PixelNumber = this->m_CaptureWidth * this->m_CaptureHeight; // allocate buffers - AllocatePixelArrays(); + this->AllocatePixelArrays(); this->AllocateSourceData(); - m_CameraConnected = true; + this->m_CameraConnected = true; } } return ok; } bool ToFCameraPMDDevice::DisconnectCamera() { bool ok = false; if (m_Controller) { ok = m_Controller->CloseCameraConnection(); if (ok) { m_CameraConnected = false; } } return ok; } void ToFCameraPMDDevice::StartCamera() { if (m_CameraConnected) { // get the first image this->m_Controller->UpdateCamera(); this->m_ImageMutex->Lock(); //this->m_Controller->GetSourceData(this->m_SourceDataArray); this->m_Controller->GetSourceData(this->m_SourceDataBuffer[this->m_FreePos]); this->m_FreePos = (this->m_FreePos+1) % this->m_BufferSize; this->m_CurrentPos = (this->m_CurrentPos+1) % this->m_BufferSize; this->m_ImageSequence++; this->m_ImageMutex->Unlock(); this->m_CameraActiveMutex->Lock(); this->m_CameraActive = true; this->m_CameraActiveMutex->Unlock(); this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->Acquire, this); // wait a little to make sure that the thread is started itksys::SystemTools::Delay(10); } else { MITK_INFO<<"Camera not connected"; } } void ToFCameraPMDDevice::StopCamera() { m_CameraActiveMutex->Lock(); m_CameraActive = false; m_CameraActiveMutex->Unlock(); itksys::SystemTools::Delay(100); if (m_MultiThreader.IsNotNull()) { m_MultiThreader->TerminateThread(m_ThreadID); } // wait a little to make sure that the thread is terminated itksys::SystemTools::Delay(10); } bool ToFCameraPMDDevice::IsCameraActive() { m_CameraActiveMutex->Lock(); bool ok = m_CameraActive; m_CameraActiveMutex->Unlock(); return ok; } void ToFCameraPMDDevice::UpdateCamera() { if (m_Controller) { m_Controller->UpdateCamera(); } } ITK_THREAD_RETURN_TYPE ToFCameraPMDDevice::Acquire(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; if (pInfo == NULL) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == NULL) { return ITK_THREAD_RETURN_VALUE; } ToFCameraPMDDevice* toFCameraDevice = (ToFCameraPMDDevice*)pInfo->UserData; if (toFCameraDevice!=NULL) { mitk::RealTimeClock::Pointer realTimeClock; realTimeClock = mitk::RealTimeClock::New(); double t1, t2; t1 = realTimeClock->GetCurrentStamp(); int n = 100; bool overflow = false; bool printStatus = false; while (toFCameraDevice->IsCameraActive()) { // update the ToF camera toFCameraDevice->UpdateCamera(); // get the source data from the camera and write it at the next free position in the buffer toFCameraDevice->m_ImageMutex->Lock(); toFCameraDevice->m_Controller->GetSourceData(toFCameraDevice->m_SourceDataBuffer[toFCameraDevice->m_FreePos]); toFCameraDevice->m_ImageMutex->Unlock(); // call modified to indicate that cameraDevice was modified toFCameraDevice->Modified(); /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TODO Buffer Handling currently only works for buffer size 1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ //toFCameraDevice->m_ImageSequence++; toFCameraDevice->m_FreePos = (toFCameraDevice->m_FreePos+1) % toFCameraDevice->m_BufferSize; toFCameraDevice->m_CurrentPos = (toFCameraDevice->m_CurrentPos+1) % toFCameraDevice->m_BufferSize; toFCameraDevice->m_ImageSequence++; if (toFCameraDevice->m_FreePos == toFCameraDevice->m_CurrentPos) { // buffer overflow //MITK_INFO << "Buffer overflow!! "; //toFCameraDevice->m_CurrentPos = (toFCameraDevice->m_CurrentPos+1) % toFCameraDevice->m_BufferSize; //toFCameraDevice->m_ImageSequence++; overflow = true; } if (toFCameraDevice->m_ImageSequence % n == 0) { printStatus = true; } if (overflow) { //itksys::SystemTools::Delay(10); overflow = false; } /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! END TODO Buffer Handling currently only works for buffer size 1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ // print current framerate if (printStatus) { t2 = realTimeClock->GetCurrentStamp() - t1; //MITK_INFO << "t2: " << t2 <<" Time (s) for 1 image: " << (t2/1000) / n << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFCameraDevice->m_ImageSequence; MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFCameraDevice->m_ImageSequence; t1 = realTimeClock->GetCurrentStamp(); printStatus = false; } } // end of while loop } return ITK_THREAD_RETURN_VALUE; } // TODO: Buffer size currently set to 1. Once Buffer handling is working correctly, method may be reactivated // void ToFCameraPMDDevice::ResetBuffer(int bufferSize) // { // this->m_BufferSize = bufferSize; // this->m_CurrentPos = -1; // this->m_FreePos = 0; // } void ToFCameraPMDDevice::GetAmplitudes(float* amplitudeArray, int& imageSequence) { if (m_CameraActive) { // 1) copy the image buffer // 2) Flip around y- axis (vertical axis) m_ImageMutex->Lock(); this->m_Controller->GetAmplitudes(this->m_SourceDataBuffer[this->m_CurrentPos], this->m_AmplitudeArray); m_ImageMutex->Unlock(); for (int i=0; im_CaptureHeight; i++) { for (int j=0; jm_CaptureWidth; j++) { amplitudeArray[i*this->m_CaptureWidth+j] = this->m_AmplitudeArray[(i+1)*this->m_CaptureWidth-1-j]; } } imageSequence = this->m_ImageSequence; } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } } void ToFCameraPMDDevice::GetIntensities(float* intensityArray, int& imageSequence) { if (m_CameraActive) { // 1) copy the image buffer // 2) Flip around y- axis (vertical axis) m_ImageMutex->Lock(); this->m_Controller->GetIntensities(this->m_SourceDataBuffer[this->m_CurrentPos], this->m_IntensityArray); m_ImageMutex->Unlock(); for (int i=0; im_CaptureHeight; i++) { for (int j=0; jm_CaptureWidth; j++) { intensityArray[i*this->m_CaptureWidth+j] = this->m_IntensityArray[(i+1)*this->m_CaptureWidth-1-j]; } } imageSequence = this->m_ImageSequence; } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } m_ImageMutex->Unlock(); } void ToFCameraPMDDevice::GetDistances(float* distanceArray, int& imageSequence) { if (m_CameraActive) { // 1) copy the image buffer // 2) convert the distance values from m to mm // 3) Flip around y- axis (vertical axis) m_ImageMutex->Lock(); this->m_Controller->GetDistances(this->m_SourceDataBuffer[this->m_CurrentPos], this->m_DistanceArray); m_ImageMutex->Unlock(); for (int i=0; im_CaptureHeight; i++) { for (int j=0; jm_CaptureWidth; j++) { distanceArray[i*this->m_CaptureWidth+j] = 1000 * this->m_DistanceArray[(i+1)*this->m_CaptureWidth-1-j]; } } imageSequence = this->m_ImageSequence; } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } } void ToFCameraPMDDevice::GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray) { if (m_CameraActive) { // 1) copy the image buffer // 2) convert the distance values from m to mm // 3) Flip around y- axis (vertical axis) // check for empty buffer if (this->m_ImageSequence < 0) { // buffer empty MITK_INFO << "Buffer empty!! "; capturedImageSequence = this->m_ImageSequence; return; } // determine position of image in buffer int pos = 0; if ((requiredImageSequence < 0) || (requiredImageSequence > this->m_ImageSequence)) { capturedImageSequence = this->m_ImageSequence; pos = this->m_CurrentPos; //MITK_INFO << "Required image not found! Required: " << requiredImageSequence << " delivered/current: " << this->m_ImageSequence; } else if (requiredImageSequence <= this->m_ImageSequence - this->m_BufferSize) { capturedImageSequence = (this->m_ImageSequence - this->m_BufferSize) + 1; pos = (this->m_CurrentPos + 1) % this->m_BufferSize; //MITK_INFO << "Out of buffer! Required: " << requiredImageSequence << " delivered: " << capturedImageSequence << " current: " << this->m_ImageSequence; } else // (requiredImageSequence > this->m_ImageSequence - this->m_BufferSize) && (requiredImageSequence <= this->m_ImageSequence) { capturedImageSequence = requiredImageSequence; pos = (this->m_CurrentPos + (10-(this->m_ImageSequence - requiredImageSequence))) % this->m_BufferSize; } m_ImageMutex->Lock(); this->m_Controller->GetDistances(this->m_SourceDataBuffer[pos], this->m_DistanceArray); this->m_Controller->GetAmplitudes(this->m_SourceDataBuffer[pos], this->m_AmplitudeArray); this->m_Controller->GetIntensities(this->m_SourceDataBuffer[pos], this->m_IntensityArray); memcpy(sourceDataArray, this->m_SourceDataBuffer[this->m_CurrentPos], this->m_SourceDataSize); m_ImageMutex->Unlock(); - int u, v; - for (int i=0; im_CaptureHeight; i++) - { - for (int j=0; jm_CaptureWidth; j++) - { - u = i*this->m_CaptureWidth+j; - v = (i+1)*this->m_CaptureWidth-1-j; - distanceArray[u] = 1000 * this->m_DistanceArray[v]; // unit in minimeter - //distanceArray[u] = this->m_DistanceArray[v]; // unit in meter - amplitudeArray[u] = this->m_AmplitudeArray[v]; - intensityArray[u] = this->m_IntensityArray[v]; - } - } + memcpy(distanceArray, this->m_DistanceArray, this->m_CaptureWidth*this->m_CaptureHeight*sizeof(float)); + memcpy(intensityArray, this->m_IntensityArray, this->m_CaptureWidth*this->m_CaptureHeight*sizeof(float)); + memcpy(amplitudeArray, this->m_AmplitudeArray, this->m_CaptureWidth*this->m_CaptureHeight*sizeof(float)); + + //int u, v; + //for (int i=0; im_CaptureHeight; i++) + //{ + // for (int j=0; jm_CaptureWidth; j++) + // { + // u = i*this->m_CaptureWidth+j; + // v = (i+1)*this->m_CaptureWidth-1-j; + // distanceArray[u] = 1000 * this->m_DistanceArray[v]; // unit in minimeter + // //distanceArray[u] = this->m_DistanceArray[v]; // unit in meter + // amplitudeArray[u] = this->m_AmplitudeArray[v]; + // intensityArray[u] = this->m_IntensityArray[v]; + // } + //} } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } } ToFCameraPMDController::Pointer ToFCameraPMDDevice::GetController() { return this->m_Controller; } void ToFCameraPMDDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue ) { ToFCameraDevice::SetProperty(propertyKey,propertyValue); this->m_PropertyList->SetProperty(propertyKey, propertyValue); if (strcmp(propertyKey, "ModulationFrequency") == 0) { int modulationFrequency = 0; GetIntProperty(propertyKey, modulationFrequency); m_Controller->SetModulationFrequency(modulationFrequency); } else if (strcmp(propertyKey, "IntegrationTime") == 0) { int integrationTime = 0; GetIntProperty(propertyKey, integrationTime); m_Controller->SetIntegrationTime(integrationTime); } } void ToFCameraPMDDevice::AllocateSourceData() { // clean up if array and data have already been allocated CleanUpSourceData(); // (re-) allocate memory this->m_SourceDataArray = new char[this->m_SourceDataSize]; for(int i=0; im_SourceDataSize; i++) {this->m_SourceDataArray[i]=0;} this->m_SourceDataBuffer = new char*[this->m_MaxBufferSize]; for(int i=0; im_MaxBufferSize; i++) { this->m_SourceDataBuffer[i] = new char[this->m_SourceDataSize]; } } void ToFCameraPMDDevice::CleanUpSourceData() { if (m_SourceDataArray) { delete[] m_SourceDataArray; } if (m_SourceDataBuffer) { for(int i=0; im_MaxBufferSize; i++) { delete[] this->m_SourceDataBuffer[i]; } delete[] this->m_SourceDataBuffer; } } } diff --git a/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.cpp b/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.cpp index b6ebfe6d69..c956bd27ae 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.cpp +++ b/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.cpp @@ -1,102 +1,215 @@ /*=================================================================== 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 "mitkToFCameraPMDRawDataCamBoardDevice.h" #include "mitkToFCameraPMDCamBoardController.h" // vtk includes #include "vtkSmartPointer.h" namespace mitk { ToFCameraPMDRawDataCamBoardDevice::ToFCameraPMDRawDataCamBoardDevice() { m_Controller = ToFCameraPMDCamBoardController::New(); } ToFCameraPMDRawDataCamBoardDevice::~ToFCameraPMDRawDataCamBoardDevice() { } + bool ToFCameraPMDRawDataCamBoardDevice::ConnectCamera() + { + bool ok = Superclass::ConnectCamera(); + if(ok) + { + this->m_OriginControllerHeight = static_cast(m_Controller.GetPointer())->GetInternalCaptureHeight(); + this->m_OriginControllerWidth = static_cast(m_Controller.GetPointer())->GetInternalCaptureWidth(); + this->m_PixelNumber = m_OriginControllerHeight* m_OriginControllerWidth; + this->AllocatePixelArrays(); + this->AllocateSourceData(); + } + return ok; + } + void ToFCameraPMDRawDataCamBoardDevice::GetChannelSourceData(short* sourceData, vtkShortArray* vtkChannelArray ) { int i = 0; //unsigned int channelDataPosition = 0; - unsigned int channelSize = (this->GetCaptureHeight()*this->GetCaptureWidth()*2); + unsigned int channelSize = (this->m_OriginControllerHeight*this->m_OriginControllerWidth*2); this->SetChannelSize(channelSize); signed short* channel1; signed short* channel2; signed short* channel3; signed short* channel4; vtkSmartPointer cvtkChannelArray = vtkShortArray::New(); cvtkChannelArray->SetNumberOfComponents(channelSize); cvtkChannelArray->SetNumberOfTuples(4); cvtkChannelArray->Allocate(1); channel1 = sourceData; cvtkChannelArray->InsertTupleValue(0,channel1); sourceData += channelSize; channel2 = sourceData; cvtkChannelArray->InsertTupleValue(1,channel2); sourceData += channelSize; channel3 = sourceData; cvtkChannelArray->InsertTupleValue(2,channel3); sourceData += channelSize; channel4 = sourceData; cvtkChannelArray->InsertTupleValue(3,channel4); vtkChannelArray->DeepCopy(cvtkChannelArray); cvtkChannelArray->Delete(); } void ToFCameraPMDRawDataCamBoardDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue ) { ToFCameraPMDRawDataDevice::SetProperty(propertyKey,propertyValue); this->m_PropertyList->SetProperty(propertyKey, propertyValue); ToFCameraPMDCamBoardController::Pointer myController = dynamic_cast(this->m_Controller.GetPointer()); bool boolValue = false; GetBoolProperty(propertyKey, boolValue); if (strcmp(propertyKey, "SetFPNCalibration") == 0) { myController->SetFPNCalibration(boolValue); } else if (strcmp(propertyKey, "SetFPPNCalibration") == 0) { myController->SetFPPNCalibration(boolValue); } else if (strcmp(propertyKey, "SetLinearityCalibration") == 0) { myController->SetLinearityCalibration(boolValue); } else if (strcmp(propertyKey, "SetLensCalibration") == 0) { myController->SetLensCalibration(boolValue); } else if (strcmp(propertyKey, "SetExposureMode") == 0) { if (boolValue) { myController->SetExposureMode(1); } else { myController->SetExposureMode(0); } } } + void ToFCameraPMDRawDataCamBoardDevice::GetAmplitudes(float* amplitudeArray, int& imageSequence) + { + if (m_CameraActive) + { + this->ResizeOutputImage(m_AmplitudeArray,amplitudeArray); + imageSequence = this->m_ImageSequence; + } + else + { + MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; + } + } + + void ToFCameraPMDRawDataCamBoardDevice::GetIntensities(float* intensityArray, int& imageSequence) + { + if (m_CameraActive) + { + this->ResizeOutputImage(m_IntensityArray, intensityArray); + imageSequence = this->m_ImageSequence; + } + else + { + MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; + } + } + + void ToFCameraPMDRawDataCamBoardDevice::GetDistances(float* distanceArray, int& imageSequence) + { + if (m_CameraActive) + { + this->ResizeOutputImage(m_DistanceArray, distanceArray); + imageSequence = this->m_ImageSequence; + } + else + { + MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; + } + } + + void ToFCameraPMDRawDataCamBoardDevice::GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, + int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray) + { + if (m_CameraActive) + { + // check for empty buffer + if (this->m_ImageSequence < 0) + { + // buffer empty + MITK_INFO << "Buffer empty!! "; + capturedImageSequence = this->m_ImageSequence; + return; + } + + // determine position of image in buffer + int pos = 0; + if ((requiredImageSequence < 0) || (requiredImageSequence > this->m_ImageSequence)) + { + capturedImageSequence = this->m_ImageSequence; + pos = this->m_CurrentPos; + //MITK_INFO << "Required image not found! Required: " << requiredImageSequence << " delivered/current: " << this->m_ImageSequence; + } + else if (requiredImageSequence <= this->m_ImageSequence - this->m_BufferSize) + { + capturedImageSequence = (this->m_ImageSequence - this->m_BufferSize) + 1; + pos = (this->m_CurrentPos + 1) % this->m_BufferSize; + //MITK_INFO << "Out of buffer! Required: " << requiredImageSequence << " delivered: " << capturedImageSequence << " current: " << this->m_ImageSequence; + } + else // (requiredImageSequence > this->m_ImageSequence - this->m_BufferSize) && (requiredImageSequence <= this->m_ImageSequence) + + { + capturedImageSequence = requiredImageSequence; + pos = (this->m_CurrentPos + (10-(this->m_ImageSequence - requiredImageSequence))) % this->m_BufferSize; + } + + this->ResizeOutputImage(m_DistanceArray, distanceArray); + this->ResizeOutputImage(m_AmplitudeArray, amplitudeArray); + this->ResizeOutputImage(m_IntensityArray, intensityArray); + memcpy(sourceDataArray, this->m_SourceDataBuffer[this->m_CurrentPos], this->m_SourceDataSize); + } + else + { + MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; + } + } + + void ToFCameraPMDRawDataCamBoardDevice::ResizeOutputImage(float* in, float* out) + { + vnl_matrix inMat = vnl_matrix(m_OriginControllerHeight, m_OriginControllerWidth); + inMat.copy_in(in); + vnl_matrix outMat = vnl_matrix(m_CaptureHeight,m_CaptureWidth); + vnl_matrix temp = vnl_matrix(m_CaptureHeight,m_CaptureWidth); + temp = inMat.extract(m_CaptureHeight,m_CaptureWidth, 0, 4); + outMat = temp.transpose(); // rotate the image data + outMat.flipud(); // flip image upside down + outMat.copy_out(out); + inMat.clear(); // clean data + outMat.clear(); + temp.clear(); + } } diff --git a/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.h b/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.h index 4b1400ef9b..3933c1fb0d 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.h +++ b/Modules/ToFHardware/mitkToFCameraPMDRawDataCamBoardDevice.h @@ -1,54 +1,81 @@ /*=================================================================== 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 __mitkToFCameraPMDRawDataCamBoardDevice_h #define __mitkToFCameraPMDRawDataCamBoardDevice_h #include "mitkToFHardwareExports.h" #include "mitkCommon.h" #include "mitkToFCameraDevice.h" #include "mitkToFCameraPMDRawDataDevice.h" namespace mitk { /** * @brief Device class representing a PMD CamCube camera * * * @ingroup ToFHardwareMBI */ class MITK_TOFHARDWARE_EXPORT ToFCameraPMDRawDataCamBoardDevice : public ToFCameraPMDRawDataDevice { public: mitkClassMacro( ToFCameraPMDRawDataCamBoardDevice , ToFCameraPMDRawDataDevice ); itkNewMacro( Self ); - + /*! + \brief set a BaseProperty + */ virtual void SetProperty( const char *propertyKey, BaseProperty* propertyValue ); - + /*! + \brief Transforms the sourceData into an array with four tuples holding the channels for + raw data reconstruction. + */ virtual void GetChannelSourceData(short* sourceData, vtkShortArray* vtkChannelArray ); - + /*! + \brief Establishes camera connection and sets the class variables + */ + bool ConnectCamera(); + /*! + \brief Returns intensity data + */ + void GetIntensities(float* intensityArray, int& imageSequence); + /*! + \brief Returns amplitude data + */ + void GetAmplitudes(float* amplitudeArray, int& imageSequence); + /*! + \brief Returns distance data + */ + void GetDistances(float* distanceArray, int& imageSequence); + /*! + \brief Returns all image data at once. + */ + void GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray); protected: ToFCameraPMDRawDataCamBoardDevice(); - ~ToFCameraPMDRawDataCamBoardDevice(); - private: + private: + /*! + \brief Method performs resizing of the image data and flips it upside down + */ + void ResizeOutputImage(float* in, float* out); }; } //END mitk namespace #endif // __mitkToFCameraPMDRawDataCamBoardDevice_h diff --git a/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.cpp b/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.cpp index 3ac6750a4c..177dae0d8d 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.cpp +++ b/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.cpp @@ -1,465 +1,470 @@ /*=================================================================== 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 "mitkToFCameraPMDRawDataDevice.h" #include "mitkRealTimeClock.h" - +#include "mitkToFCameraPMDCamBoardController.h" #include "itkMultiThreader.h" #include namespace mitk { ToFCameraPMDRawDataDevice::ToFCameraPMDRawDataDevice() : - m_SourceDataBuffer(NULL), m_SourceDataArray(NULL), m_ShortSourceData(NULL) + m_SourceDataBuffer(NULL), m_SourceDataArray(NULL), m_ShortSourceData(NULL), + m_OriginControllerWidth(0), m_OriginControllerHeight(0) { m_RawDataSource = ThreadedToFRawDataReconstruction::New(); - } ToFCameraPMDRawDataDevice::~ToFCameraPMDRawDataDevice() { this->CleanUpSourceData(); } bool ToFCameraPMDRawDataDevice::ConnectCamera() { bool ok = false; if (m_Controller.IsNotNull()) { ok = m_Controller->OpenCameraConnection(); if (ok) { this->m_CaptureWidth = m_Controller->GetCaptureWidth(); this->m_CaptureHeight = m_Controller->GetCaptureHeight(); + this->m_OriginControllerHeight = m_CaptureHeight; + this->m_OriginControllerWidth = m_CaptureWidth; this->m_SourceDataSize = m_Controller->GetSourceDataStructSize(); this->m_PixelNumber = this->m_CaptureWidth * this->m_CaptureHeight; // allocate buffers this->AllocatePixelArrays(); this->AllocateSourceData(); - m_CameraConnected = true; + this->m_CameraConnected = true; } } return ok; } bool ToFCameraPMDRawDataDevice::DisconnectCamera() { bool ok = false; if (m_Controller) { ok = m_Controller->CloseCameraConnection(); if (ok) { m_CameraConnected = false; } } return ok; } void ToFCameraPMDRawDataDevice::StartCamera() { if (m_CameraConnected) { // get the first image this->m_Controller->UpdateCamera(); this->m_ImageMutex->Lock(); this->m_Controller->GetSourceData(this->m_SourceDataBuffer[this->m_FreePos]); this->m_FreePos = (this->m_FreePos+1) % this->m_BufferSize; this->m_CurrentPos = (this->m_CurrentPos+1) % this->m_BufferSize; this->m_ImageSequence++; this->m_ImageMutex->Unlock(); this->m_CameraActiveMutex->Lock(); this->m_CameraActive = true; this->m_CameraActiveMutex->Unlock(); this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->Acquire, this); // wait a little to make sure that the thread is started itksys::SystemTools::Delay(100); } else { MITK_INFO<<"Camera not connected"; } } void ToFCameraPMDRawDataDevice::StopCamera() { m_CameraActiveMutex->Lock(); m_CameraActive = false; m_CameraActiveMutex->Unlock(); itksys::SystemTools::Delay(100); if (m_MultiThreader.IsNotNull()) { m_MultiThreader->TerminateThread(m_ThreadID); } // wait a little to make sure that the thread is terminated itksys::SystemTools::Delay(10); } bool ToFCameraPMDRawDataDevice::IsCameraActive() { m_CameraActiveMutex->Lock(); bool ok = m_CameraActive; m_CameraActiveMutex->Unlock(); return ok; } void ToFCameraPMDRawDataDevice::UpdateCamera() { if (m_Controller) { m_Controller->UpdateCamera(); } } ITK_THREAD_RETURN_TYPE ToFCameraPMDRawDataDevice::Acquire(void* pInfoStruct) { /* extract this pointer from Thread Info structure */ struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; if (pInfo == NULL) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == NULL) { return ITK_THREAD_RETURN_VALUE; } ToFCameraPMDRawDataDevice* toFCameraDevice = (ToFCameraPMDRawDataDevice*)pInfo->UserData; if (toFCameraDevice!=NULL) { mitk::RealTimeClock::Pointer realTimeClock = mitk::RealTimeClock::New(); double t1, t2; t1 = realTimeClock->GetCurrentStamp(); int n = 100; bool overflow = false; bool printStatus = false; while (toFCameraDevice->IsCameraActive()) { // update the ToF camera toFCameraDevice->UpdateCamera(); // get the source data from the camera and write it at the next free position in the buffer // call modified to indicate that cameraDevice was modified toFCameraDevice->Modified(); vtkShortArray* channelData = vtkShortArray::New(); //toFCameraDevice->m_ImageMutex->Lock(); toFCameraDevice->m_Controller->GetShortSourceData(toFCameraDevice->m_ShortSourceData); toFCameraDevice->GetChannelSourceData( toFCameraDevice->m_ShortSourceData, channelData ); //toFCameraDevice->m_ImageMutex->Unlock(); if(!toFCameraDevice->m_RawDataSource->GetInit()) { - toFCameraDevice->m_RawDataSource->Initialize(toFCameraDevice->m_CaptureWidth, toFCameraDevice->m_CaptureHeight, - toFCameraDevice->m_Controller->GetModulationFrequency(), toFCameraDevice->GetChannelSize()); + toFCameraDevice->m_RawDataSource->Initialize(toFCameraDevice->m_OriginControllerWidth, + toFCameraDevice->m_OriginControllerHeight, + toFCameraDevice->m_Controller->GetModulationFrequency(), + toFCameraDevice->GetChannelSize()); } toFCameraDevice->m_RawDataSource->SetChannelData(channelData); toFCameraDevice->m_RawDataSource->Update(); toFCameraDevice->m_ImageMutex->Lock(); toFCameraDevice->m_Controller->GetSourceData(toFCameraDevice->m_SourceDataArray); - toFCameraDevice->m_RawDataSource->GetAllData(toFCameraDevice->m_DistanceArray, toFCameraDevice->m_AmplitudeArray, toFCameraDevice->m_IntensityArray); + toFCameraDevice->m_RawDataSource->GetAllData(toFCameraDevice->m_DistanceArray, + toFCameraDevice->m_AmplitudeArray, toFCameraDevice->m_IntensityArray); toFCameraDevice->m_ImageMutex->Unlock(); /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TODO Buffer Handling currently only works for buffer size 1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ toFCameraDevice->m_FreePos = (toFCameraDevice->m_FreePos+1) % toFCameraDevice->m_BufferSize; toFCameraDevice->m_CurrentPos = (toFCameraDevice->m_CurrentPos+1) % toFCameraDevice->m_BufferSize; toFCameraDevice->m_ImageSequence++; if (toFCameraDevice->m_FreePos == toFCameraDevice->m_CurrentPos) { overflow = true; } if (toFCameraDevice->m_ImageSequence % n == 0) { printStatus = true; } channelData->Delete(); if (overflow) { overflow = false; } /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! END TODO Buffer Handling currently only works for buffer size 1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ - // print current framerate + // print current frame rate if (printStatus) { t2 = realTimeClock->GetCurrentStamp() - t1; //MITK_INFO << "t2: " << t2 <<" Time (s) for 1 image: " << (t2/1000) / n << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFCameraDevice->m_ImageSequence; MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFCameraDevice->m_ImageSequence; t1 = realTimeClock->GetCurrentStamp(); printStatus = false; } } // end of while loop } return ITK_THREAD_RETURN_VALUE; } // TODO: Buffer size currently set to 1. Once Buffer handling is working correctly, method may be reactivated // void ToFCameraPMDDevice::ResetBuffer(int bufferSize) // { // this->m_BufferSize = bufferSize; // this->m_CurrentPos = -1; // this->m_FreePos = 0; // } void ToFCameraPMDRawDataDevice::GetAmplitudes(float* amplitudeArray, int& imageSequence) { if (m_CameraActive) { memcpy(amplitudeArray, this->m_AmplitudeArray, this->m_PixelNumber*sizeof(float)); imageSequence = this->m_ImageSequence; } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } } void ToFCameraPMDRawDataDevice::GetIntensities(float* intensityArray, int& imageSequence) { if (m_CameraActive) { memcpy(intensityArray, this->m_IntensityArray, this->m_PixelNumber*sizeof(float)); imageSequence = this->m_ImageSequence; } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } } void ToFCameraPMDRawDataDevice::GetDistances(float* distanceArray, int& imageSequence) { if (m_CameraActive) { memcpy(distanceArray, this->m_DistanceArray, this->m_PixelNumber*sizeof(float)); imageSequence = this->m_ImageSequence; } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } } void ToFCameraPMDRawDataDevice::GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray) { if (m_CameraActive) { // 1) copy the image buffer // 2) convert the distance values from m to mm // 3) Flip around y- axis (vertical axis) // check for empty buffer if (this->m_ImageSequence < 0) { // buffer empty MITK_INFO << "Buffer empty!! "; capturedImageSequence = this->m_ImageSequence; return; } // determine position of image in buffer int pos = 0; if ((requiredImageSequence < 0) || (requiredImageSequence > this->m_ImageSequence)) { capturedImageSequence = this->m_ImageSequence; pos = this->m_CurrentPos; //MITK_INFO << "Required image not found! Required: " << requiredImageSequence << " delivered/current: " << this->m_ImageSequence; } else if (requiredImageSequence <= this->m_ImageSequence - this->m_BufferSize) { capturedImageSequence = (this->m_ImageSequence - this->m_BufferSize) + 1; pos = (this->m_CurrentPos + 1) % this->m_BufferSize; //MITK_INFO << "Out of buffer! Required: " << requiredImageSequence << " delivered: " << capturedImageSequence << " current: " << this->m_ImageSequence; } else // (requiredImageSequence > this->m_ImageSequence - this->m_BufferSize) && (requiredImageSequence <= this->m_ImageSequence) { capturedImageSequence = requiredImageSequence; pos = (this->m_CurrentPos + (10-(this->m_ImageSequence - requiredImageSequence))) % this->m_BufferSize; } memcpy(distanceArray, this->m_DistanceArray, this->m_PixelNumber*sizeof(float)); memcpy(amplitudeArray, this->m_AmplitudeArray, this->m_PixelNumber*sizeof(float)); memcpy(intensityArray, this->m_IntensityArray, this->m_PixelNumber*sizeof(float)); memcpy(sourceDataArray, this->m_SourceDataBuffer[this->m_CurrentPos], this->m_SourceDataSize); } else { MITK_WARN("ToF") << "Warning: Data can only be acquired if camera is active."; } } void ToFCameraPMDRawDataDevice::XYAxisFlipImage( float* imageData, float* &flippedData, int xAxis, int yAxis, int dimension ) { int captureWidth = this->GetCaptureWidth(); int captureHeight = this->GetCaptureHeight(); // //flips image around x- axis (horizontal axis) // if(xAxis == 1 && yAxis != 1) { for (int i=0; im_Controller; } void ToFCameraPMDRawDataDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue ) { ToFCameraDevice::SetProperty(propertyKey,propertyValue); this->m_PropertyList->SetProperty(propertyKey, propertyValue); if (strcmp(propertyKey, "ModulationFrequency") == 0) { int modulationFrequency = 0; GetIntProperty(propertyKey, modulationFrequency); m_Controller->SetModulationFrequency(modulationFrequency); } else if (strcmp(propertyKey, "IntegrationTime") == 0) { int integrationTime = 0; GetIntProperty(propertyKey, integrationTime); m_Controller->SetIntegrationTime(integrationTime); } } void ToFCameraPMDRawDataDevice::CleanupPixelArrays() { if (m_IntensityArray) { delete [] m_IntensityArray; } if (m_DistanceArray) { delete [] m_DistanceArray; } if (m_AmplitudeArray) { delete [] m_AmplitudeArray; } if (m_ShortSourceData) { delete [] m_ShortSourceData; } } void ToFCameraPMDRawDataDevice::AllocatePixelArrays() { // free memory if it was already allocated CleanupPixelArrays(); // allocate buffer this->m_IntensityArray = new float[this->m_PixelNumber]; for(int i=0; im_PixelNumber; i++) {this->m_IntensityArray[i]=0.0;} this->m_DistanceArray = new float[this->m_PixelNumber]; for(int i=0; im_PixelNumber; i++) {this->m_DistanceArray[i]=0.0;} this->m_AmplitudeArray = new float[this->m_PixelNumber]; for(int i=0; im_PixelNumber; i++) {this->m_AmplitudeArray[i]=0.0;} this->m_ShortSourceData = new short[this->m_SourceDataSize]; for(int i=0; im_SourceDataSize; i++) {this->m_ShortSourceData[i]=0.0;} } void ToFCameraPMDRawDataDevice::AllocateSourceData() { // clean up if array and data have already been allocated CleanUpSourceData(); // (re-) allocate memory this->m_SourceDataArray = new char[this->m_SourceDataSize]; for(int i=0; im_SourceDataSize; i++) {this->m_SourceDataArray[i]=0;} this->m_SourceDataBuffer = new char*[this->m_MaxBufferSize]; for(int i=0; im_MaxBufferSize; i++) { this->m_SourceDataBuffer[i] = new char[this->m_SourceDataSize]; } } void ToFCameraPMDRawDataDevice::CleanUpSourceData() { if (m_SourceDataArray) { delete[] m_SourceDataArray; } if (m_SourceDataBuffer) { for(int i=0; im_MaxBufferSize; i++) { delete[] this->m_SourceDataBuffer[i]; } delete[] this->m_SourceDataBuffer; } } } diff --git a/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.h b/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.h index b687e75a34..b0e45ca237 100644 --- a/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.h +++ b/Modules/ToFHardware/mitkToFCameraPMDRawDataDevice.h @@ -1,175 +1,177 @@ /*=================================================================== 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 __mitkToFCameraPMDRawDataDevice_h #define __mitkToFCameraPMDRawDataDevice_h #include "mitkToFHardwareExports.h" #include "mitkToFCameraDevice.h" #include "mitkToFCameraPMDController.h" #include "mitkThreadedToFRawDataReconstruction.h" namespace mitk { /** * @brief Interface for all representations of PMD ToF devices. * ToFCameraPMDDevice internally holds an instance of ToFCameraPMDController and starts a thread * that continuously grabs images from the controller. A buffer structure buffers the last acquired images * to provide the image data loss-less. * * @ingroup ToFHardware */ class MITK_TOFHARDWARE_EXPORT ToFCameraPMDRawDataDevice : public ToFCameraDevice { public: mitkClassMacro( ToFCameraPMDRawDataDevice , ToFCameraDevice ); itkNewMacro( Self ); itkSetMacro(ChannelSize, int); itkGetMacro(ChannelSize, int); /*! \brief opens a connection to the ToF camera */ virtual bool ConnectCamera(); /*! \brief closes the connection to the camera */ virtual bool DisconnectCamera(); /*! \brief starts the continuous updating of the camera. A separate thread updates the source data, the main thread processes the source data and creates images and coordinates */ virtual void StartCamera(); /*! \brief stops the continuous updating of the camera */ virtual void StopCamera(); /*! \brief updates the camera for image acquisition */ virtual void UpdateCamera(); /*! \brief returns whether the camera is currently active or not */ virtual bool IsCameraActive(); /*! \brief gets the amplitude data from the ToF camera as the strength of the active illumination of every pixel. Caution! The user is responsible for allocating and deleting the images. These values can be used to determine the quality of the distance values. The higher the amplitude value, the higher the accuracy of the according distance value \param imageSequence the actually captured image sequence number \param amplitudeArray contains the returned amplitude data as an array. */ virtual void GetAmplitudes(float* amplitudeArray, int& imageSequence); /*! \brief gets the intensity data from the ToF camera as a greyscale image. Caution! The user is responsible for allocating and deleting the images. \param intensityArray contains the returned intensities data as an array. \param imageSequence the actually captured image sequence number */ virtual void GetIntensities(float* intensityArray, int& imageSequence); /*! \brief gets the distance data from the ToF camera measuring the distance between the camera and the different object points in millimeters. Caution! The user is responsible for allocating and deleting the images. \param distanceArray contains the returned distances data as an array. \param imageSequence the actually captured image sequence number */ virtual void GetDistances(float* distanceArray, int& imageSequence); /*! \brief gets the 3 images (distance, amplitude, intensity) from the ToF camera. Caution! The user is responsible for allocating and deleting the images. \param distanceArray contains the returned distance data as an array. \param amplitudeArray contains the returned amplitude data as an array. \param intensityArray contains the returned intensity data as an array. \param sourceDataArray contains the complete source data from the camera device. \param requiredImageSequence the required image sequence number \param capturedImageSequence the actually captured image sequence number */ virtual void GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray=NULL); // TODO: Buffer size currently set to 1. Once Buffer handling is working correctly, method may be reactivated // /*! // \brief pure virtual method resetting the buffer using the specified bufferSize. Has to be implemented by sub-classes // \param bufferSize buffer size the buffer should be reset to // */ // virtual void ResetBuffer(int bufferSize) = 0; //TODO add/correct documentation for requiredImageSequence and capturedImageSequence in the GetAllImages, GetDistances, GetIntensities and GetAmplitudes methods. /*! \brief returns the corresponding camera controller */ ToFCameraPMDController::Pointer GetController(); virtual void GetChannelSourceData(short* /*sourceData*/, vtkShortArray* /*vtkChannelArray*/ ){}; /*! \brief set a BaseProperty */ virtual void SetProperty( const char *propertyKey, BaseProperty* propertyValue ); protected: ToFCameraPMDRawDataDevice(); ~ToFCameraPMDRawDataDevice(); /*! \brief method for allocating m_SourceDataArray and m_SourceDataBuffer */ virtual void AllocateSourceData(); /*! \brief method for cleaning up memory allocated for m_SourceDataArray and m_SourceDataBuffer */ virtual void CleanUpSourceData(); /*! \brief method for allocating memory for pixel arrays m_IntensityArray, m_DistanceArray and m_AmplitudeArray */ virtual void AllocatePixelArrays(); /*! \brief method for cleanup memory allocated for pixel arrays m_IntensityArray, m_DistanceArray and m_AmplitudeArray */ virtual void CleanupPixelArrays(); /*! \brief Thread method continuously acquiring images from the ToF hardware */ static ITK_THREAD_RETURN_TYPE Acquire(void* pInfoStruct); /*! \brief moves the position pointer m_CurrentPos to the next position in the buffer if that's not the next free position to prevent reading from an empty buffer */ void GetNextPos(); /*! \brief gets the image data and flips it according to user needs Caution! The user is responsible for allocating and deleting the data. \param imageData contains array to the input data. \param flippedData contains flipped output array - Caution! The user is responsible for allocating and deleting the data. Size should be equal to imageData! \param xAxis flag is set to flip around x axis (1 - set, 0 - not set). \param yAxis flag is set to flip around y axis (1 - set, 0 - not set). \param dimension contains the extend of the z dimension (preset is 1) */ void XYAxisFlipImage( float* imageData, float* &flippedData, int xAxis, int yAxis, int dimension = 1 ); + //member variables ToFCameraPMDController::Pointer m_Controller; ///< corresponding CameraController ThreadedToFRawDataReconstruction::Pointer m_RawDataSource; char** m_SourceDataBuffer; ///< buffer holding the last acquired images char* m_SourceDataArray; ///< array holding the current PMD source data short* m_ShortSourceData; ///< array holding the current PMD raw data - private: + unsigned int m_OriginControllerWidth; ///< holds the original controller width + unsigned int m_OriginControllerHeight; ///< holds the original controller height - //member variables + private: int m_ChannelSize; ///< member holds the size of a single raw data channel }; } //END mitk namespace #endif diff --git a/Modules/ToFHardware/mitkToFImageGrabber.cpp b/Modules/ToFHardware/mitkToFImageGrabber.cpp index 992d20bc8f..2ded3427ad 100644 --- a/Modules/ToFHardware/mitkToFImageGrabber.cpp +++ b/Modules/ToFHardware/mitkToFImageGrabber.cpp @@ -1,279 +1,279 @@ /*=================================================================== 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 "mitkToFImageGrabber.h" #include "mitkToFCameraPMDCamCubeDevice.h" #include "itkCommand.h" namespace mitk { ToFImageGrabber::ToFImageGrabber():m_CaptureWidth(204),m_CaptureHeight(204),m_PixelNumber(41616),m_ImageSequence(0), m_IntensityArray(NULL), m_DistanceArray(NULL), m_AmplitudeArray(NULL), m_SourceDataArray(NULL), m_RgbDataArray(NULL) { // Create the output. We use static_cast<> here because we know the default // output must be of type TOutputImage OutputImageType::Pointer output0 = static_cast(this->MakeOutput(0).GetPointer()); OutputImageType::Pointer output1 = static_cast(this->MakeOutput(1).GetPointer()); OutputImageType::Pointer output2 = static_cast(this->MakeOutput(2).GetPointer()); OutputImageType::Pointer output3 = static_cast(this->MakeOutput(3).GetPointer()); mitk::ImageSource::SetNumberOfRequiredOutputs(3); mitk::ImageSource::SetNthOutput(0, output0.GetPointer()); mitk::ImageSource::SetNthOutput(1, output1.GetPointer()); mitk::ImageSource::SetNthOutput(2, output2.GetPointer()); mitk::ImageSource::SetNthOutput(3, output3.GetPointer()); } ToFImageGrabber::~ToFImageGrabber() { if (m_IntensityArray||m_AmplitudeArray||m_DistanceArray||m_RgbDataArray) { if (m_ToFCameraDevice) { m_ToFCameraDevice->RemoveObserver(m_DeviceObserverTag); } this->DisconnectCamera(); this->CleanUpImageArrays(); } } mitk::ImageSource::DataObjectPointer mitk::ImageSource::MakeOutput(unsigned int) { return static_cast(OutputImageType::New().GetPointer()); } void ToFImageGrabber::GenerateData() { int requiredImageSequence = 0; int capturedImageSequence = 0; unsigned int dimensions[3]; dimensions[0] = this->m_ToFCameraDevice->GetCaptureWidth(); dimensions[1] = this->m_ToFCameraDevice->GetCaptureHeight(); dimensions[2] = 1; mitk::PixelType FloatType = MakeScalarPixelType(); // acquire new image data this->m_ToFCameraDevice->GetAllImages(this->m_DistanceArray, this->m_AmplitudeArray, this->m_IntensityArray, this->m_SourceDataArray, requiredImageSequence, this->m_ImageSequence, this->m_RgbDataArray ); mitk::Image::Pointer distanceImage = this->GetOutput(0); if (!distanceImage->IsInitialized()) { distanceImage->ReleaseData(); distanceImage->Initialize(FloatType, 3, dimensions, 1); } mitk::Image::Pointer amplitudeImage = this->GetOutput(1); if (!amplitudeImage->IsInitialized()) { amplitudeImage->ReleaseData(); amplitudeImage->Initialize(FloatType, 3, dimensions, 1); } mitk::Image::Pointer intensityImage = this->GetOutput(2); if (!intensityImage->IsInitialized()) { intensityImage->ReleaseData(); intensityImage->Initialize(FloatType, 3, dimensions, 1); } mitk::Image::Pointer rgbImage = this->GetOutput(3); if (!rgbImage->IsInitialized()) { rgbImage->ReleaseData(); rgbImage->Initialize(mitk::PixelType(MakePixelType, 3>()),3,dimensions,1); } capturedImageSequence = this->m_ImageSequence; if (m_DistanceArray) { distanceImage->SetSlice(this->m_DistanceArray, 0, 0, 0); } if (m_AmplitudeArray) { amplitudeImage->SetSlice(this->m_AmplitudeArray, 0, 0, 0); } if (m_IntensityArray) { intensityImage->SetSlice(this->m_IntensityArray, 0, 0, 0); } if (m_RgbDataArray) { rgbImage->SetSlice(this->m_RgbDataArray, 0, 0, 0); } } bool ToFImageGrabber::ConnectCamera() { bool ok = m_ToFCameraDevice->ConnectCamera(); if (ok) { m_CaptureWidth = m_ToFCameraDevice->GetCaptureWidth(); m_CaptureHeight = m_ToFCameraDevice->GetCaptureHeight(); m_PixelNumber = m_CaptureWidth * m_CaptureHeight; m_SourceDataSize = m_ToFCameraDevice->GetSourceDataSize(); - AllocateImageArrays(); + this->AllocateImageArrays(); } return ok; } bool ToFImageGrabber::DisconnectCamera() { bool success = m_ToFCameraDevice->DisconnectCamera(); return success; } void ToFImageGrabber::StartCamera() { m_ToFCameraDevice->StartCamera(); } void ToFImageGrabber::StopCamera() { m_ToFCameraDevice->StopCamera(); } bool ToFImageGrabber::IsCameraActive() { return m_ToFCameraDevice->IsCameraActive(); } void ToFImageGrabber::SetCameraDevice(ToFCameraDevice* aToFCameraDevice) { m_ToFCameraDevice = aToFCameraDevice; itk::SimpleMemberCommand::Pointer modifiedCommand = itk::SimpleMemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &ToFImageGrabber::OnToFCameraDeviceModified); m_DeviceObserverTag = m_ToFCameraDevice->AddObserver(itk::ModifiedEvent(), modifiedCommand); this->Modified(); } ToFCameraDevice* ToFImageGrabber::GetCameraDevice() { return m_ToFCameraDevice; } int ToFImageGrabber::GetCaptureWidth() { return m_CaptureWidth; } int ToFImageGrabber::GetCaptureHeight() { return m_CaptureHeight; } int ToFImageGrabber::GetPixelNumber() { return m_PixelNumber; } int ToFImageGrabber::SetModulationFrequency(int modulationFrequency) { this->m_ToFCameraDevice->SetProperty("ModulationFrequency",mitk::IntProperty::New(modulationFrequency)); this->Modified(); return modulationFrequency; } int ToFImageGrabber::SetIntegrationTime(int integrationTime) { this->m_ToFCameraDevice->SetProperty("IntegrationTime",mitk::IntProperty::New(integrationTime)); this->Modified(); return integrationTime; } int ToFImageGrabber::GetIntegrationTime() { int integrationTime = 0; this->m_ToFCameraDevice->GetIntProperty("IntegrationTime",integrationTime); return integrationTime; } int ToFImageGrabber::GetModulationFrequency() { int modulationFrequency = 0; this->m_ToFCameraDevice->GetIntProperty("ModulationFrequency",modulationFrequency); return modulationFrequency; } void ToFImageGrabber::SetBoolProperty( const char* propertyKey, bool boolValue ) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void ToFImageGrabber::SetIntProperty( const char* propertyKey, int intValue ) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void ToFImageGrabber::SetFloatProperty( const char* propertyKey, float floatValue ) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void ToFImageGrabber::SetStringProperty( const char* propertyKey, const char* stringValue ) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void ToFImageGrabber::SetProperty( const char *propertyKey, BaseProperty* propertyValue ) { this->m_ToFCameraDevice->SetProperty(propertyKey, propertyValue); } void ToFImageGrabber::OnToFCameraDeviceModified() { this->Modified(); } void ToFImageGrabber::CleanUpImageArrays() { // free buffer if (m_IntensityArray) { delete [] m_IntensityArray; m_IntensityArray = NULL; } if (m_DistanceArray) { delete [] m_DistanceArray; m_DistanceArray = NULL; } if (m_AmplitudeArray) { delete [] m_AmplitudeArray; m_AmplitudeArray = NULL; } if (m_SourceDataArray) { delete [] m_SourceDataArray; m_SourceDataArray = NULL; } if (m_RgbDataArray) { delete [] m_RgbDataArray; m_RgbDataArray = NULL; } } void ToFImageGrabber::AllocateImageArrays() { // cleanup memory if necessary this->CleanUpImageArrays(); // allocate buffer m_IntensityArray = new float[m_PixelNumber]; m_DistanceArray = new float[m_PixelNumber]; m_AmplitudeArray = new float[m_PixelNumber]; m_SourceDataArray = new char[m_SourceDataSize]; m_RgbDataArray = new unsigned char[m_PixelNumber*3]; } } 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.diffusionimaging/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake index 68930fafcd..dcdcef089d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/files.cmake @@ -1,121 +1,124 @@ set(SRC_CPP_FILES QmitkODFDetailsWidget.cpp QmitkODFRenderWidget.cpp QmitkPartialVolumeAnalysisWidget.cpp QmitkIVIMWidget.cpp QmitkTbssRoiAnalysisWidget.cpp QmitkResidualAnalysisWidget.cpp QmitkResidualViewWidget.cpp ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkQBallReconstructionView.cpp QmitkPreprocessingView.cpp QmitkDiffusionDicomImportView.cpp QmitkDiffusionQuantificationView.cpp QmitkTensorReconstructionView.cpp QmitkDiffusionImagingPublicPerspective.cpp QmitkControlVisualizationPropertiesView.cpp QmitkODFDetailsView.cpp QmitkGibbsTrackingView.cpp QmitkStochasticFiberTrackingView.cpp + QmitkStreamlineTrackingView.cpp QmitkFiberProcessingView.cpp QmitkFiberBundleDeveloperView.cpp QmitkPartialVolumeAnalysisView.cpp QmitkIVIMView.cpp QmitkTractbasedSpatialStatisticsView.cpp QmitkTbssTableModel.cpp QmitkTbssMetaTableModel.cpp QmitkTbssSkeletonizationView.cpp Connectomics/QmitkBrainNetworkAnalysisView.cpp Connectomics/QmitkNetworkHistogramCanvas.cpp ) set(UI_FILES src/internal/QmitkQBallReconstructionViewControls.ui src/internal/QmitkPreprocessingViewControls.ui src/internal/QmitkDiffusionDicomImportViewControls.ui src/internal/QmitkDiffusionQuantificationViewControls.ui src/internal/QmitkTensorReconstructionViewControls.ui src/internal/QmitkControlVisualizationPropertiesViewControls.ui src/internal/QmitkODFDetailsViewControls.ui src/internal/QmitkGibbsTrackingViewControls.ui src/internal/QmitkStochasticFiberTrackingViewControls.ui + src/internal/QmitkStreamlineTrackingViewControls.ui src/internal/QmitkFiberProcessingViewControls.ui src/internal/QmitkFiberBundleDeveloperViewControls.ui src/internal/QmitkPartialVolumeAnalysisViewControls.ui src/internal/QmitkIVIMViewControls.ui src/internal/QmitkTractbasedSpatialStatisticsViewControls.ui src/internal/QmitkTbssSkeletonizationViewControls.ui src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkQBallReconstructionView.h src/internal/QmitkPreprocessingView.h src/internal/QmitkDiffusionDicomImportView.h src/internal/QmitkDiffusionImagingPublicPerspective.h src/internal/QmitkDiffusionQuantificationView.h src/internal/QmitkTensorReconstructionView.h src/internal/QmitkControlVisualizationPropertiesView.h src/internal/QmitkODFDetailsView.h src/QmitkODFRenderWidget.h src/QmitkODFDetailsWidget.h src/internal/QmitkGibbsTrackingView.h src/internal/QmitkStochasticFiberTrackingView.h + src/internal/QmitkStreamlineTrackingView.h src/internal/QmitkFiberProcessingView.h src/internal/QmitkFiberBundleDeveloperView.h src/internal/QmitkPartialVolumeAnalysisView.h src/QmitkPartialVolumeAnalysisWidget.h src/internal/QmitkIVIMView.h src/internal/QmitkTractbasedSpatialStatisticsView.h src/internal/QmitkTbssSkeletonizationView.h src/QmitkTbssRoiAnalysisWidget.h src/QmitkResidualAnalysisWidget.h src/QmitkResidualViewWidget.h src/internal/Connectomics/QmitkBrainNetworkAnalysisView.h src/internal/Connectomics/QmitkNetworkHistogramCanvas.h ) set(CACHED_RESOURCE_FILES # list of resource files which can be used by the plug-in # system without loading the plug-ins shared library, # for example the icon used in the menu and tabs for the # plug-in views in the workbench plugin.xml resources/preprocessing.png resources/dwiimport.png resources/quantification.png resources/reconodf.png resources/recontensor.png resources/vizControls.png resources/OdfDetails.png resources/GibbsTracking.png resources/FiberBundleOperations.png resources/PartialVolumeAnalysis_24.png resources/IVIM_48.png resources/stochFB.png resources/tbss.png resources/QmitkBrainNetworkAnalysisViewIcon_48.png resources/arrow.png ) set(QRC_FILES # uncomment the following line if you want to use Qt resources resources/QmitkDiffusionImaging.qrc #resources/QmitkTractbasedSpatialStatisticsView.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml b/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml index e8b9846c4b..76277778da 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/plugin.xml @@ -1,125 +1,132 @@ + + + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.cpp index bf105b937d..ee48aa1c0b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.cpp @@ -1,443 +1,446 @@ /*=================================================================== 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 "QmitkTbssRoiAnalysisWidget.h" #include #include #include #include #include #include #include #include #include #include #include QmitkTbssRoiAnalysisWidget::QmitkTbssRoiAnalysisWidget( QWidget * parent ) : QmitkPlotWidget(parent) { m_PlotPicker = new QwtPlotPicker(m_Plot->canvas()); m_PlotPicker->setSelectionFlags(QwtPicker::PointSelection | QwtPicker::ClickSelection | QwtPicker::DragSelection); m_PlotPicker->setTrackerMode(QwtPicker::ActiveOnly); } std::vector< std::vector > QmitkTbssRoiAnalysisWidget::CalculateGroupProfiles(std::string preprocessed) { MITK_INFO << "make profiles!"; std::vector< std::vector > profiles; //No results were preprocessed, so they must be calculated now. if(preprocessed == "") { // Iterate through the 4th dim (corresponding to subjects) // and create a profile for every subject int size = m_Projections->GetVectorLength(); for(int s=0; s profile; RoiType::iterator it; it = m_Roi.begin(); while(it != m_Roi.end()) { itk::Index<3> ix = *it; profile.push_back(m_Projections->GetPixel(ix).GetElement(s)); it++; } int pSize = profile.size(); profiles.push_back(profile); } } else{ // Use preprocessed results std::ifstream file(preprocessed.c_str()); if(file.is_open()) { std::string line; while(getline(file,line)) { std::vector tokens; Tokenize(line, tokens); std::vector::iterator it; it = tokens.begin(); std::vector< double > profile; while(it != tokens.end()) { std::string s = *it; profile.push_back (atof( s.c_str() ) ); ++it; } profiles.push_back(profile); } } } + + + m_IndividualProfiles = profiles; // Calculate the averages // Here a check could be build in to check whether all profiles have // the same length, but this should normally be the case if the input // data were corrected with the TBSS Module. std::vector< std::vector > groupProfiles; std::vector< std::pair >::iterator it; it = m_Groups.begin(); int c = 0; //the current profile number int nprof = profiles.size(); while(it != m_Groups.end() && profiles.size() > 0) { std::pair p = *it; int size = p.second; //initialize a vector of the right length with zeroes std::vector averageProfile; for(int i=0; iClear(); m_Vals.clear(); std::vector v1; std::vector > groupProfiles = CalculateGroupProfiles(preprocessed); std::vector xAxis; for(int i=0; iSetPlotTitle( title.c_str() ); QPen pen( Qt::SolidLine ); pen.setWidth(2); std::vector< std::pair >::iterator it; it = m_Groups.begin(); int c = 0; //the current profile number QColor colors[4] = {Qt::green, Qt::blue, Qt::yellow, Qt::red}; while(it != m_Groups.end() && groupProfiles.size() > 0) { std::pair< std::string, int > group = *it; pen.setColor(colors[c]); int curveId = this->InsertCurve( group.first.c_str() ); this->SetCurveData( curveId, xAxis, groupProfiles.at(c) ); this->SetCurvePen( curveId, pen ); c++; it++; } QwtLegend *legend = new QwtLegend; this->SetLegend(legend, QwtPlot::RightLegend, 0.5); std::cout << m_Measure << std::endl; this->m_Plot->setAxisTitle(0, m_Measure.c_str()); this->m_Plot->setAxisTitle(3, "Position"); this->Replot(); } void QmitkTbssRoiAnalysisWidget::PlotFiberBundles(TractContainerType tracts, mitk::Image *img) { this->Clear(); std::vector::iterator it = tracts.begin(); // Match points on tracts. Take the smallest tract and match all others on this one int min = std::numeric_limits::max(); TractType smallestTract; while(it != tracts.end()) { TractType tract = *it; if(tract.size() correspondingIndices; TractType correspondingPoints; for(int i=0; i::max(); int correspondingIndex = 0; PointType correspondingPoint; // Search for the point on the second tract with the smallest distance // to p and memorize it for(int j=0; j > profiles; it = tracts.begin(); while(it != tracts.end()) { std::cout << "Tract\n"; TractType tract = *it; TractType::iterator tractIt = tract.begin(); std::vector profile; while(tractIt != tract.end()) { PointType p = *tractIt; std::cout << p[0] << ' ' << p[1] << ' ' << p[2] << '\n'; // Get value from image profile.push_back( (double)img->GetPixelValueByWorldCoordinate(p) ); ++tractIt; } profiles.push_back(profile); std::cout << std::endl; ++it; } std::string title = "Fiber bundle plot"; this->SetPlotTitle( title.c_str() ); QPen pen( Qt::SolidLine ); pen.setWidth(2); std::vector< std::vector >::iterator profit = profiles.begin(); int id=0; while(profit != profiles.end()) { std::vector profile = *profit; std::vector xAxis; for(int i=0; iInsertCurve( QString::number(id).toStdString().c_str() ); this->SetCurveData( curveId, xAxis, profile ); ++profit; id++; } this->Replot(); } void QmitkTbssRoiAnalysisWidget::Boxplots() { this->Clear(); } void QmitkTbssRoiAnalysisWidget::drawBar(int x) { m_Plot->detachItems(QwtPlotItem::Rtti_PlotMarker, true); QwtPlotMarker *mX = new QwtPlotMarker(); //mX->setLabel(QString::fromLatin1("selected point")); mX->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom); mX->setLabelOrientation(Qt::Vertical); mX->setLineStyle(QwtPlotMarker::VLine); mX->setLinePen(QPen(Qt::black, 0, Qt::SolidLine)); mX->setXValue(x); mX->attach(m_Plot); this->Replot(); } QmitkTbssRoiAnalysisWidget::~QmitkTbssRoiAnalysisWidget() { delete m_PlotPicker; } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.h index 90b062a10b..d6afec7444 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/QmitkTbssRoiAnalysisWidget.h @@ -1,157 +1,162 @@ /*=================================================================== 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 QmitkTbssRoiAnalysisWidget_H_ #define QmitkTbssRoiAnalysisWidget_H_ #include "QmitkPlotWidget.h" #include //#include "QmitkHistogram.h" #include "QmitkExtExports.h" #include "mitkImage.h" #include "mitkPlanarFigure.h" #include "itkVectorImage.h" //#include //#include #include #include #include #include #include typedef itk::VectorImage VectorImageType; typedef std::vector< itk::Index<3> > RoiType; typedef itk::Point PointType; typedef std::vector< PointType> TractType; typedef std::vector< TractType > TractContainerType; /** * \brief Widget for displaying boxplots * framework */ class DIFFUSIONIMAGING_EXPORT QmitkTbssRoiAnalysisWidget : public QmitkPlotWidget { Q_OBJECT public: QmitkTbssRoiAnalysisWidget( QWidget * parent); virtual ~QmitkTbssRoiAnalysisWidget(); void SetGroups(std::vector< std::pair > groups) { m_Groups = groups; } void DrawProfiles(std::string preprocessed); void PlotFiberBundles(TractContainerType tracts, mitk::Image* img); void Boxplots(); void SetProjections(VectorImageType::Pointer projections) { m_Projections = projections; } void SetRoi(RoiType roi) { m_Roi = roi; } void SetStructure(std::string structure) { m_Structure = structure; } void SetMeasure(std::string measure) { m_Measure = measure; } QwtPlot* GetPlot() { return m_Plot; } QwtPlotPicker* m_PlotPicker; void drawBar(int x); std::vector > GetVals() { return m_Vals; } + std::vector > GetIndividualProfiles() + { + return m_IndividualProfiles; + } + protected: std::vector< std::vector > m_Vals; - + std::vector< std::vector > m_IndividualProfiles; std::vector< std::vector > CalculateGroupProfiles(std::string preprocessed); void Tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters = " ") { // Skip delimiters at beginning. std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); // Find first "non-delimiter". std::string::size_type pos = str.find_first_of(delimiters, lastPos); while (std::string::npos != pos || std::string::npos != lastPos) { // Found a token, add it to the vector. tokens.push_back(str.substr(lastPos, pos - lastPos)); // Skip delimiters. Note the "not_of" lastPos = str.find_first_not_of(delimiters, pos); // Find next "non-delimiter" pos = str.find_first_of(delimiters, lastPos); } } std::vector< std::pair > m_Groups; VectorImageType::Pointer m_Projections; RoiType m_Roi; std::string m_Structure; std::string m_Measure; }; #endif diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp index b33fe57deb..32ad82d638 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp @@ -1,1773 +1,1774 @@ /*=================================================================== 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 "QmitkControlVisualizationPropertiesView.h" #include "mitkNodePredicateDataType.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "mitkResliceMethodProperty.h" #include "mitkRenderingManager.h" #include "mitkTbssImage.h" #include "mitkPlanarFigure.h" #include "mitkFiberBundleX.h" #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include "mitkFiberBundleInteractor.h" #include "mitkPlanarFigureInteractor.h" #include #include #include #include "mitkGlobalInteraction.h" #include "mitkGeometry2D.h" #include "mitkSegTool2D.h" #include "berryIWorkbenchWindow.h" #include "berryIWorkbenchPage.h" #include "berryISelectionService.h" #include "berryConstants.h" #include "berryPlatformUI.h" #include "itkRGBAPixel.h" #include #include "qwidgetaction.h" #include "qcolordialog.h" const std::string QmitkControlVisualizationPropertiesView::VIEW_ID = "org.mitk.views.controlvisualizationpropertiesview"; using namespace berry; struct CvpSelListener : ISelectionListener { berryObjectMacro(CvpSelListener); CvpSelListener(QmitkControlVisualizationPropertiesView* view) { m_View = view; } void ApplySettings(mitk::DataNode::Pointer node) { bool tex_int; node->GetBoolProperty("texture interpolation", tex_int); if(tex_int) { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexON); m_View->m_Controls->m_TextureIntON->setChecked(true); m_View->m_TexIsOn = true; } else { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexOFF); m_View->m_Controls->m_TextureIntON->setChecked(false); m_View->m_TexIsOn = false; } int val; node->GetIntProperty("ShowMaxNumber", val); m_View->m_Controls->m_ShowMaxNumber->setValue(val); m_View->m_Controls->m_NormalizationDropdown->setCurrentIndex(dynamic_cast(node->GetProperty("Normalization"))->GetValueAsId()); float fval; node->GetFloatProperty("Scaling",fval); m_View->m_Controls->m_ScalingFactor->setValue(fval); m_View->m_Controls->m_AdditionalScaling->setCurrentIndex(dynamic_cast(node->GetProperty("ScaleBy"))->GetValueAsId()); node->GetFloatProperty("IndexParam1",fval); m_View->m_Controls->m_IndexParam1->setValue(fval); node->GetFloatProperty("IndexParam2",fval); m_View->m_Controls->m_IndexParam2->setValue(fval); } void DoSelectionChanged(ISelection::ConstPointer selection) { // save current selection in member variable m_View->m_CurrentSelection = selection.Cast(); m_View->m_Controls->m_VisibleOdfsON_T->setVisible(false); m_View->m_Controls->m_VisibleOdfsON_S->setVisible(false); m_View->m_Controls->m_VisibleOdfsON_C->setVisible(false); m_View->m_Controls->m_TextureIntON->setVisible(false); m_View->m_Controls->m_ImageControlsFrame->setVisible(false); m_View->m_Controls->m_PlanarFigureControlsFrame->setVisible(false); m_View->m_Controls->m_BundleControlsFrame->setVisible(false); m_View->m_SelectedNode = 0; if(m_View->m_CurrentSelection.IsNull()) return; if(m_View->m_CurrentSelection->Size() == 1) { mitk::DataNodeObject::Pointer nodeObj = m_View->m_CurrentSelection->Begin()->Cast(); if(nodeObj.IsNotNull()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); // check if node has data, // if some helper nodes are shown in the DataManager, the GetData() returns 0x0 which would lead to SIGSEV mitk::BaseData* nodeData = node->GetData(); if(nodeData != NULL ) { if(dynamic_cast(nodeData) != 0) { m_View->m_Controls->m_PlanarFigureControlsFrame->setVisible(true); m_View->m_SelectedNode = node; float val; node->GetFloatProperty("planarfigure.line.width", val); m_View->m_Controls->m_PFWidth->setValue((int)(val*10.0)); QString label = "Width %1"; label = label.arg(val); m_View->m_Controls->label_pfwidth->setText(label); float color[3]; node->GetColor( color, NULL, "planarfigure.default.line.color"); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0]*255.0)); styleSheet.append(","); styleSheet.append(QString::number(color[1]*255.0)); styleSheet.append(","); styleSheet.append(QString::number(color[2]*255.0)); styleSheet.append(")"); m_View->m_Controls->m_PFColor->setAutoFillBackground(true); m_View->m_Controls->m_PFColor->setStyleSheet(styleSheet); node->GetColor( color, NULL, "color"); styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0]*255.0)); styleSheet.append(","); styleSheet.append(QString::number(color[1]*255.0)); styleSheet.append(","); styleSheet.append(QString::number(color[2]*255.0)); styleSheet.append(")"); m_View->PlanarFigureFocus(); } if(dynamic_cast(nodeData) != 0) { m_View->m_Controls->m_BundleControlsFrame->setVisible(true); m_View->m_SelectedNode = node; if(m_View->m_CurrentPickingNode != 0 && node.GetPointer() != m_View->m_CurrentPickingNode) { m_View->m_Controls->m_Crosshair->setEnabled(false); } else { m_View->m_Controls->m_Crosshair->setEnabled(true); } float val; node->GetFloatProperty("TubeRadius", val); m_View->m_Controls->m_TubeRadius->setValue((int)(val * 100.0)); QString label = "Radius %1"; label = label.arg(val); m_View->m_Controls->label_tuberadius->setText(label); int width; node->GetIntProperty("LineWidth", width); m_View->m_Controls->m_LineWidth->setValue(width); label = "Width %1"; label = label.arg(width); m_View->m_Controls->label_linewidth->setText(label); float range; node->GetFloatProperty("Fiber2DSliceThickness",range); label = "Range %1"; label = label.arg(range*0.1); m_View->m_Controls->label_range->setText(label); } } // check node data != NULL } } if(m_View->m_CurrentSelection->Size() > 0 && m_View->m_SelectedNode == 0) { m_View->m_Controls->m_ImageControlsFrame->setVisible(true); bool foundDiffusionImage = false; bool foundQBIVolume = false; bool foundTensorVolume = false; bool foundImage = false; bool foundMultipleOdfImages = false; bool foundRGBAImage = false; bool foundTbssImage = false; // do something with the selected items if(m_View->m_CurrentSelection) { // iterate selection for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); i != m_View->m_CurrentSelection->End(); ++i) { // extract datatree node if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); mitk::BaseData* nodeData = node->GetData(); if(nodeData != NULL ) { // only look at interesting types if(QString("DiffusionImage").compare(nodeData->GetNameOfClass())==0) { foundDiffusionImage = true; bool tex_int; node->GetBoolProperty("texture interpolation", tex_int); if(tex_int) { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexON); m_View->m_Controls->m_TextureIntON->setChecked(true); m_View->m_TexIsOn = true; } else { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexOFF); m_View->m_Controls->m_TextureIntON->setChecked(false); m_View->m_TexIsOn = false; } int val; node->GetIntProperty("DisplayChannel", val); m_View->m_Controls->m_DisplayIndex->setValue(val); QString label = "Channel %1"; label = label.arg(val); m_View->m_Controls->label_channel->setText(label); int maxVal = (dynamic_cast* >(nodeData))->GetVectorImage()->GetVectorLength(); m_View->m_Controls->m_DisplayIndex->setMaximum(maxVal-1); } if(QString("TbssImage").compare(nodeData->GetNameOfClass())==0) { foundTbssImage = true; bool tex_int; node->GetBoolProperty("texture interpolation", tex_int); if(tex_int) { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexON); m_View->m_Controls->m_TextureIntON->setChecked(true); m_View->m_TexIsOn = true; } else { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexOFF); m_View->m_Controls->m_TextureIntON->setChecked(false); m_View->m_TexIsOn = false; } int val; node->GetIntProperty("DisplayChannel", val); m_View->m_Controls->m_DisplayIndex->setValue(val); QString label = "Channel %1"; label = label.arg(val); m_View->m_Controls->label_channel->setText(label); int maxVal = (dynamic_cast(nodeData))->GetImage()->GetVectorLength(); m_View->m_Controls->m_DisplayIndex->setMaximum(maxVal-1); } else if(QString("QBallImage").compare(nodeData->GetNameOfClass())==0) { foundMultipleOdfImages = foundQBIVolume || foundTensorVolume; foundQBIVolume = true; ApplySettings(node); } else if(QString("TensorImage").compare(nodeData->GetNameOfClass())==0) { foundMultipleOdfImages = foundQBIVolume || foundTensorVolume; foundTensorVolume = true; ApplySettings(node); } else if(QString("Image").compare(nodeData->GetNameOfClass())==0) { foundImage = true; mitk::Image::Pointer img = dynamic_cast(nodeData); if(img.IsNotNull() && img->GetPixelType().GetPixelTypeId() == typeid(itk::RGBAPixel) ) { foundRGBAImage = true; } bool tex_int; node->GetBoolProperty("texture interpolation", tex_int); if(tex_int) { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexON); m_View->m_Controls->m_TextureIntON->setChecked(true); m_View->m_TexIsOn = true; } else { m_View->m_Controls->m_TextureIntON->setIcon(*m_View->m_IconTexOFF); m_View->m_Controls->m_TextureIntON->setChecked(false); m_View->m_TexIsOn = false; } } } // END CHECK node != NULL } } } m_View->m_FoundSingleOdfImage = (foundQBIVolume || foundTensorVolume) && !foundMultipleOdfImages; m_View->m_Controls->m_NumberGlyphsFrame->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->m_NormalizationDropdown->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->label->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->m_ScalingFactor->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->m_AdditionalScaling->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->m_NormalizationScalingFrame->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->OpacMinFrame->setVisible(foundRGBAImage || m_View->m_FoundSingleOdfImage); // changed for SPIE paper, Principle curvature scaling //m_View->m_Controls->params_frame->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->params_frame->setVisible(false); m_View->m_Controls->m_VisibleOdfsON_T->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->m_VisibleOdfsON_S->setVisible(m_View->m_FoundSingleOdfImage); m_View->m_Controls->m_VisibleOdfsON_C->setVisible(m_View->m_FoundSingleOdfImage); bool foundAnyImage = foundDiffusionImage || foundQBIVolume || foundTensorVolume || foundImage || foundTbssImage; m_View->m_Controls->m_Reinit->setVisible(foundAnyImage); m_View->m_Controls->m_TextureIntON->setVisible(foundAnyImage); m_View->m_Controls->m_TSMenu->setVisible(foundAnyImage); } } void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) { // check, if selection comes from datamanager if (part) { QString partname(part->GetPartName().c_str()); if(partname.compare("Datamanager")==0) { // apply selection DoSelectionChanged(selection); } } } QmitkControlVisualizationPropertiesView* m_View; }; QmitkControlVisualizationPropertiesView::QmitkControlVisualizationPropertiesView() : QmitkFunctionality(), m_Controls(NULL), m_MultiWidget(NULL), m_NodeUsedForOdfVisualization(NULL), m_IconTexOFF(new QIcon(":/QmitkDiffusionImaging/texIntOFFIcon.png")), m_IconTexON(new QIcon(":/QmitkDiffusionImaging/texIntONIcon.png")), m_IconGlyOFF_T(new QIcon(":/QmitkDiffusionImaging/glyphsoff_T.png")), m_IconGlyON_T(new QIcon(":/QmitkDiffusionImaging/glyphson_T.png")), m_IconGlyOFF_C(new QIcon(":/QmitkDiffusionImaging/glyphsoff_C.png")), m_IconGlyON_C(new QIcon(":/QmitkDiffusionImaging/glyphson_C.png")), m_IconGlyOFF_S(new QIcon(":/QmitkDiffusionImaging/glyphsoff_S.png")), m_IconGlyON_S(new QIcon(":/QmitkDiffusionImaging/glyphson_S.png")), m_CurrentSelection(0), m_CurrentPickingNode(0), m_GlyIsOn_S(false), m_GlyIsOn_C(false), m_GlyIsOn_T(false), m_FiberBundleObserverTag(0), m_Color(NULL) { currentThickSlicesMode = 1; m_MyMenu = NULL; } QmitkControlVisualizationPropertiesView::QmitkControlVisualizationPropertiesView(const QmitkControlVisualizationPropertiesView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } QmitkControlVisualizationPropertiesView::~QmitkControlVisualizationPropertiesView() { if(m_SlicesRotationObserverTag1 ) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if( coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag1); } if( m_SlicesRotationObserverTag2) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if( coordinator ) coordinator->RemoveObserver(m_SlicesRotationObserverTag1); } this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->RemovePostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); } void QmitkControlVisualizationPropertiesView::OnThickSlicesModeSelected( QAction* action ) { currentThickSlicesMode = action->data().toInt(); switch(currentThickSlicesMode) { default: case 1: this->m_Controls->m_TSMenu->setText("MIP"); break; case 2: this->m_Controls->m_TSMenu->setText("SUM"); break; case 3: this->m_Controls->m_TSMenu->setText("WEIGH"); break; } mitk::DataNode* n; n = this->m_MultiWidget->GetWidgetPlane1(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); n = this->m_MultiWidget->GetWidgetPlane2(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); n = this->m_MultiWidget->GetWidgetPlane3(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); mitk::BaseRenderer::Pointer renderer = this->GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = this->GetActiveStdMultiWidget()->GetRenderWindow2()->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = this->GetActiveStdMultiWidget()->GetRenderWindow3()->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer->GetRenderingManager()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::OnTSNumChanged(int num) { if(num==0) { mitk::DataNode* n; n = this->m_MultiWidget->GetWidgetPlane1(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( 0 ) ); n = this->m_MultiWidget->GetWidgetPlane2(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( 0 ) ); n = this->m_MultiWidget->GetWidgetPlane3(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( 0 ) ); } else { mitk::DataNode* n; n = this->m_MultiWidget->GetWidgetPlane1(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); n = this->m_MultiWidget->GetWidgetPlane2(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); n = this->m_MultiWidget->GetWidgetPlane3(); if(n) n->SetProperty( "reslice.thickslices", mitk::ResliceMethodProperty::New( currentThickSlicesMode ) ); n = this->m_MultiWidget->GetWidgetPlane1(); if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); n = this->m_MultiWidget->GetWidgetPlane2(); if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); n = this->m_MultiWidget->GetWidgetPlane3(); if(n) n->SetProperty( "reslice.thickslices.num", mitk::IntProperty::New( num ) ); } m_TSLabel->setText(QString::number(num*2+1)); mitk::BaseRenderer::Pointer renderer = this->GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = this->GetActiveStdMultiWidget()->GetRenderWindow2()->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer = this->GetActiveStdMultiWidget()->GetRenderWindow3()->GetRenderer(); if(renderer.IsNotNull()) { renderer->SendUpdateSlice(); } renderer->GetRenderingManager()->RequestUpdateAll(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); } void QmitkControlVisualizationPropertiesView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkControlVisualizationPropertiesViewControls; m_Controls->setupUi(parent); this->CreateConnections(); // hide warning (ODFs in rotated planes) m_Controls->m_lblRotatedPlanesWarning->hide(); m_MyMenu = new QMenu(parent); connect( m_MyMenu, SIGNAL( aboutToShow() ), this, SLOT(OnMenuAboutToShow()) ); // button for changing rotation mode m_Controls->m_TSMenu->setMenu( m_MyMenu ); //m_CrosshairModeButton->setIcon( QIcon( iconCrosshairMode_xpm ) ); m_Controls->params_frame->setVisible(false); QIcon icon5(":/QmitkDiffusionImaging/Refresh_48.png"); m_Controls->m_Reinit->setIcon(icon5); m_Controls->m_Focus->setIcon(icon5); QIcon iconColor(":/QmitkDiffusionImaging/color24.gif"); m_Controls->m_PFColor->setIcon(iconColor); m_Controls->m_Color->setIcon(iconColor); QIcon iconReset(":/QmitkDiffusionImaging/reset.png"); m_Controls->m_ResetColoring->setIcon(iconReset); m_Controls->m_PFColor->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); QIcon iconCrosshair(":/QmitkDiffusionImaging/crosshair.png"); m_Controls->m_Crosshair->setIcon(iconCrosshair); // was is los QIcon iconPaint(":/QmitkDiffusionImaging/paint2.png"); m_Controls->m_TDI->setIcon(iconPaint); QIcon iconFiberFade(":/QmitkDiffusionImaging/MapperEfx2D.png"); m_Controls->m_FiberFading2D->setIcon(iconFiberFade); m_Controls->m_TextureIntON->setCheckable(true); #ifndef DIFFUSION_IMAGING_EXTENDED int size = m_Controls->m_AdditionalScaling->count(); for(int t=0; tm_AdditionalScaling->itemText(t).toStdString() == "Scale by ASR") { m_Controls->m_AdditionalScaling->removeItem(t); } } #endif m_Controls->m_OpacitySlider->setRange(0.0,1.0); m_Controls->m_OpacitySlider->setLowerValue(0.0); m_Controls->m_OpacitySlider->setUpperValue(0.0); m_Controls->m_ScalingFrame->setVisible(false); m_Controls->m_NormalizationFrame->setVisible(false); m_Controls->frame_tube->setVisible(false); m_Controls->frame_wire->setVisible(false); } m_IsInitialized = false; m_SelListener = berry::ISelectionListener::Pointer(new CvpSelListener(this)); this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); m_IsInitialized = true; } void QmitkControlVisualizationPropertiesView::OnMenuAboutToShow () { // THICK SLICE SUPPORT QMenu *myMenu = m_MyMenu; myMenu->clear(); QActionGroup* thickSlicesActionGroup = new QActionGroup(myMenu); thickSlicesActionGroup->setExclusive(true); mitk::BaseRenderer::Pointer renderer = this->GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer(); int currentTSMode = 0; { mitk::ResliceMethodProperty::Pointer m = dynamic_cast(renderer->GetCurrentWorldGeometry2DNode()->GetProperty( "reslice.thickslices" )); if( m.IsNotNull() ) currentTSMode = m->GetValueAsId(); } const int maxTS = 30; int currentNum = 0; { mitk::IntProperty::Pointer m = dynamic_cast(renderer->GetCurrentWorldGeometry2DNode()->GetProperty( "reslice.thickslices.num" )); if( m.IsNotNull() ) { currentNum = m->GetValue(); if(currentNum < 0) currentNum = 0; if(currentNum > maxTS) currentNum = maxTS; } } if(currentTSMode==0) currentNum=0; QSlider *m_TSSlider = new QSlider(myMenu); m_TSSlider->setMinimum(0); m_TSSlider->setMaximum(maxTS-1); m_TSSlider->setValue(currentNum); m_TSSlider->setOrientation(Qt::Horizontal); connect( m_TSSlider, SIGNAL( valueChanged(int) ), this, SLOT( OnTSNumChanged(int) ) ); QHBoxLayout* _TSLayout = new QHBoxLayout; _TSLayout->setContentsMargins(4,4,4,4); _TSLayout->addWidget(m_TSSlider); _TSLayout->addWidget(m_TSLabel=new QLabel(QString::number(currentNum*2+1),myMenu)); QWidget* _TSWidget = new QWidget; _TSWidget->setLayout(_TSLayout); QActionGroup* thickSliceModeActionGroup = new QActionGroup(myMenu); thickSliceModeActionGroup->setExclusive(true); QWidgetAction *m_TSSliderAction = new QWidgetAction(myMenu); m_TSSliderAction->setDefaultWidget(_TSWidget); myMenu->addAction(m_TSSliderAction); QAction* mipThickSlicesAction = new QAction(myMenu); mipThickSlicesAction->setActionGroup(thickSliceModeActionGroup); mipThickSlicesAction->setText("MIP (max. intensity proj.)"); mipThickSlicesAction->setCheckable(true); mipThickSlicesAction->setChecked(currentThickSlicesMode==1); mipThickSlicesAction->setData(1); myMenu->addAction( mipThickSlicesAction ); QAction* sumThickSlicesAction = new QAction(myMenu); sumThickSlicesAction->setActionGroup(thickSliceModeActionGroup); sumThickSlicesAction->setText("SUM (sum intensity proj.)"); sumThickSlicesAction->setCheckable(true); sumThickSlicesAction->setChecked(currentThickSlicesMode==2); sumThickSlicesAction->setData(2); myMenu->addAction( sumThickSlicesAction ); QAction* weightedThickSlicesAction = new QAction(myMenu); weightedThickSlicesAction->setActionGroup(thickSliceModeActionGroup); weightedThickSlicesAction->setText("WEIGHTED (gaussian proj.)"); weightedThickSlicesAction->setCheckable(true); weightedThickSlicesAction->setChecked(currentThickSlicesMode==3); weightedThickSlicesAction->setData(3); myMenu->addAction( weightedThickSlicesAction ); connect( thickSliceModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(OnThickSlicesModeSelected(QAction*)) ); } void QmitkControlVisualizationPropertiesView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SliceRotation ); m_SlicesRotationObserverTag1 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SliceRotation ); m_SlicesRotationObserverTag2 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } } } void QmitkControlVisualizationPropertiesView::SliceRotation(const itk::EventObject&) { // test if plane rotated if( m_GlyIsOn_T || m_GlyIsOn_C || m_GlyIsOn_S ) { if( this->IsPlaneRotated() ) { // show label m_Controls->m_lblRotatedPlanesWarning->show(); } else { //hide label m_Controls->m_lblRotatedPlanesWarning->hide(); } } } void QmitkControlVisualizationPropertiesView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkControlVisualizationPropertiesView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_DisplayIndex), SIGNAL(valueChanged(int)), this, SLOT(DisplayIndexChanged(int)) ); connect( (QObject*)(m_Controls->m_TextureIntON), SIGNAL(clicked()), this, SLOT(TextIntON()) ); connect( (QObject*)(m_Controls->m_Reinit), SIGNAL(clicked()), this, SLOT(Reinit()) ); connect( (QObject*)(m_Controls->m_VisibleOdfsON_T), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_T()) ); connect( (QObject*)(m_Controls->m_VisibleOdfsON_S), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_S()) ); connect( (QObject*)(m_Controls->m_VisibleOdfsON_C), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_C()) ); connect( (QObject*)(m_Controls->m_ShowMaxNumber), SIGNAL(editingFinished()), this, SLOT(ShowMaxNumberChanged()) ); connect( (QObject*)(m_Controls->m_NormalizationDropdown), SIGNAL(currentIndexChanged(int)), this, SLOT(NormalizationDropdownChanged(int)) ); connect( (QObject*)(m_Controls->m_ScalingFactor), SIGNAL(valueChanged(double)), this, SLOT(ScalingFactorChanged(double)) ); connect( (QObject*)(m_Controls->m_AdditionalScaling), SIGNAL(currentIndexChanged(int)), this, SLOT(AdditionalScaling(int)) ); connect( (QObject*)(m_Controls->m_IndexParam1), SIGNAL(valueChanged(double)), this, SLOT(IndexParam1Changed(double)) ); connect( (QObject*)(m_Controls->m_IndexParam2), SIGNAL(valueChanged(double)), this, SLOT(IndexParam2Changed(double)) ); connect( (QObject*)(m_Controls->m_ScalingCheckbox), SIGNAL(clicked()), this, SLOT(ScalingCheckbox()) ); connect( (QObject*)(m_Controls->m_OpacitySlider), SIGNAL(spanChanged(double,double)), this, SLOT(OpacityChanged(double,double)) ); connect((QObject*) m_Controls->m_Wire, SIGNAL(clicked()), (QObject*) this, SLOT(BundleRepresentationWire())); connect((QObject*) m_Controls->m_Tube, SIGNAL(clicked()), (QObject*) this, SLOT(BundleRepresentationTube())); connect((QObject*) m_Controls->m_Color, SIGNAL(clicked()), (QObject*) this, SLOT(BundleRepresentationColor())); connect((QObject*) m_Controls->m_ResetColoring, SIGNAL(clicked()), (QObject*) this, SLOT(BundleRepresentationResetColoring())); connect((QObject*) m_Controls->m_Focus, SIGNAL(clicked()), (QObject*) this, SLOT(PlanarFigureFocus())); connect((QObject*) m_Controls->m_FiberFading2D, SIGNAL(clicked()), (QObject*) this, SLOT( Fiber2DfadingEFX() ) ); connect((QObject*) m_Controls->m_FiberThicknessSlider, SIGNAL(sliderReleased()), (QObject*) this, SLOT( FiberSlicingThickness2D() ) ); connect((QObject*) m_Controls->m_FiberThicknessSlider, SIGNAL(valueChanged(int)), (QObject*) this, SLOT( FiberSlicingUpdateLabel(int) )); connect((QObject*) m_Controls->m_Crosshair, SIGNAL(clicked()), (QObject*) this, SLOT(SetInteractor())); connect((QObject*) m_Controls->m_PFWidth, SIGNAL(valueChanged(int)), (QObject*) this, SLOT(PFWidth(int))); connect((QObject*) m_Controls->m_PFColor, SIGNAL(clicked()), (QObject*) this, SLOT(PFColor())); connect((QObject*) m_Controls->m_TDI, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateTdi())); connect((QObject*) m_Controls->m_LineWidth, SIGNAL(valueChanged(int)), (QObject*) this, SLOT(LineWidthChanged(int))); connect((QObject*) m_Controls->m_TubeRadius, SIGNAL(valueChanged(int)), (QObject*) this, SLOT(TubeRadiusChanged(int))); connect((QObject*) m_Controls->m_Welcome, SIGNAL(clicked()), (QObject*) this, SLOT(Welcome())); } } void QmitkControlVisualizationPropertiesView::Activated() { berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); QmitkFunctionality::Activated(); } void QmitkControlVisualizationPropertiesView::Deactivated() { QmitkFunctionality::Deactivated(); } int QmitkControlVisualizationPropertiesView::GetSizeFlags(bool width) { if(!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkControlVisualizationPropertiesView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if(width==false) { return m_FoundSingleOdfImage ? 120 : 80; } else { return preferredResult; } } // set diffusion image channel to b0 volume void QmitkControlVisualizationPropertiesView::NodeAdded(const mitk::DataNode *node) { mitk::DataNode* notConst = const_cast(node); if (dynamic_cast*>(notConst->GetData())) { mitk::DiffusionImage::Pointer dimg = dynamic_cast*>(notConst->GetData()); // if there is no b0 image in the dataset, the GetB0Indices() returns a vector of size 0 // and hence we cannot set the Property directly to .front() int displayChannelPropertyValue = 0; if( dimg->GetB0Indices().size() > 0) displayChannelPropertyValue = dimg->GetB0Indices().front(); notConst->SetIntProperty("DisplayChannel", displayChannelPropertyValue ); } } /* OnSelectionChanged is registered to SelectionService, therefore no need to implement SelectionService Listener explicitly */ void QmitkControlVisualizationPropertiesView::OnSelectionChanged( std::vector nodes ) { if ( !this->IsVisible() ) { // do nothing if nobody wants to see me :-( return; } // deactivate channel slider if no diffusion weighted image or tbss image is selected m_Controls->m_DisplayIndex->setVisible(false); m_Controls->label_channel->setVisible(false); for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; // check if node has data, // if some helper nodes are shown in the DataManager, the GetData() returns 0x0 which would lead to SIGSEV mitk::BaseData* nodeData = node->GetData(); if(nodeData == NULL) continue; if (node.IsNotNull() && (dynamic_cast(nodeData) || dynamic_cast*>(nodeData))) { m_Controls->m_DisplayIndex->setVisible(true); m_Controls->label_channel->setVisible(true); } else if (node.IsNotNull() && dynamic_cast(node->GetData())) { if (m_Color.IsNotNull()) m_Color->RemoveObserver(m_FiberBundleObserverTag); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SetFiberBundleCustomColor ); m_Color = dynamic_cast(node->GetProperty("color", NULL)); if (m_Color.IsNotNull()) m_FiberBundleObserverTag = m_Color->AddObserver( itk::ModifiedEvent(), command ); } } for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; // check if node has data, // if some helper nodes are shown in the DataManager, the GetData() returns 0x0 which would lead to SIGSEV mitk::BaseData* nodeData = node->GetData(); if(nodeData == NULL) continue; if( node.IsNotNull() && (dynamic_cast(nodeData) || dynamic_cast(nodeData)) ) { if(m_NodeUsedForOdfVisualization.IsNotNull()) { m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", false); } m_NodeUsedForOdfVisualization = node; m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); if(m_MultiWidget) m_MultiWidget->RequestUpdate(); m_Controls->m_TSMenu->setVisible(false); // deactivate mip etc. for tensor and q-ball images break; } else m_Controls->m_TSMenu->setVisible(true); } } mitk::DataStorage::SetOfObjects::Pointer QmitkControlVisualizationPropertiesView::ActiveSet(std::string classname) { if (m_CurrentSelection) { mitk::DataStorage::SetOfObjects::Pointer set = mitk::DataStorage::SetOfObjects::New(); int at = 0; for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); i != m_CurrentSelection->End(); ++i) { if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); // check if node has data, // if some helper nodes are shown in the DataManager, the GetData() returns 0x0 which would lead to SIGSEV const mitk::BaseData* nodeData = node->GetData(); if(nodeData == NULL) continue; if(QString(classname.c_str()).compare(nodeData->GetNameOfClass())==0) { set->InsertElement(at++, node); } } } return set; } return 0; } void QmitkControlVisualizationPropertiesView::SetBoolProp( mitk::DataStorage::SetOfObjects::Pointer set, std::string name, bool value) { if(set.IsNotNull()) { mitk::DataStorage::SetOfObjects::const_iterator itemiter( set->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( set->end() ); while ( itemiter != itemiterend ) { (*itemiter)->SetBoolProperty(name.c_str(), value); ++itemiter; } } } void QmitkControlVisualizationPropertiesView::SetIntProp( mitk::DataStorage::SetOfObjects::Pointer set, std::string name, int value) { if(set.IsNotNull()) { mitk::DataStorage::SetOfObjects::const_iterator itemiter( set->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( set->end() ); while ( itemiter != itemiterend ) { (*itemiter)->SetIntProperty(name.c_str(), value); ++itemiter; } } } void QmitkControlVisualizationPropertiesView::SetFloatProp( mitk::DataStorage::SetOfObjects::Pointer set, std::string name, float value) { if(set.IsNotNull()) { mitk::DataStorage::SetOfObjects::const_iterator itemiter( set->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( set->end() ); while ( itemiter != itemiterend ) { (*itemiter)->SetFloatProperty(name.c_str(), value); ++itemiter; } } } void QmitkControlVisualizationPropertiesView::SetLevelWindowProp( mitk::DataStorage::SetOfObjects::Pointer set, std::string name, mitk::LevelWindow value) { if(set.IsNotNull()) { mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(value); mitk::DataStorage::SetOfObjects::const_iterator itemiter( set->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( set->end() ); while ( itemiter != itemiterend ) { (*itemiter)->SetProperty(name.c_str(), prop); ++itemiter; } } } void QmitkControlVisualizationPropertiesView::SetEnumProp( mitk::DataStorage::SetOfObjects::Pointer set, std::string name, mitk::EnumerationProperty::Pointer value) { if(set.IsNotNull()) { mitk::DataStorage::SetOfObjects::const_iterator itemiter( set->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( set->end() ); while ( itemiter != itemiterend ) { (*itemiter)->SetProperty(name.c_str(), value); ++itemiter; } } } void QmitkControlVisualizationPropertiesView::DisplayIndexChanged(int dispIndex) { QString label = "Channel %1"; label = label.arg(dispIndex); m_Controls->label_channel->setText(label); std::vector sets; sets.push_back("DiffusionImage"); sets.push_back("TbssImage"); std::vector::iterator it = sets.begin(); while(it != sets.end()) { std::string s = *it; mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet(s); if(set.IsNotNull()) { mitk::DataStorage::SetOfObjects::const_iterator itemiter( set->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( set->end() ); while ( itemiter != itemiterend ) { (*itemiter)->SetIntProperty("DisplayChannel", dispIndex); ++itemiter; } //m_MultiWidget->RequestUpdate(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } it++; } } void QmitkControlVisualizationPropertiesView::Reinit() { if (m_CurrentSelection) { mitk::DataNodeObject::Pointer nodeObj = m_CurrentSelection->Begin()->Cast(); mitk::DataNode::Pointer node = nodeObj->GetDataNode(); mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeSlicedGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } void QmitkControlVisualizationPropertiesView::TextIntON() { if(m_TexIsOn) { m_Controls->m_TextureIntON->setIcon(*m_IconTexOFF); } else { m_Controls->m_TextureIntON->setIcon(*m_IconTexON); } mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("DiffusionImage"); SetBoolProp(set,"texture interpolation", !m_TexIsOn); set = ActiveSet("TensorImage"); SetBoolProp(set,"texture interpolation", !m_TexIsOn); set = ActiveSet("QBallImage"); SetBoolProp(set,"texture interpolation", !m_TexIsOn); set = ActiveSet("Image"); SetBoolProp(set,"texture interpolation", !m_TexIsOn); m_TexIsOn = !m_TexIsOn; if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_S() { m_GlyIsOn_S = m_Controls->m_VisibleOdfsON_S->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is NULL"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); VisibleOdfsON(0); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_T() { m_GlyIsOn_T = m_Controls->m_VisibleOdfsON_T->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is NULL"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); VisibleOdfsON(1); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_C() { m_GlyIsOn_C = m_Controls->m_VisibleOdfsON_C->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is NULL"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); VisibleOdfsON(2); } bool QmitkControlVisualizationPropertiesView::IsPlaneRotated() { // for all 2D renderwindows of m_MultiWidget check alignment mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast( m_MultiWidget->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D() ); if (displayPlane.IsNull()) return false; mitk::Image* currentImage = dynamic_cast( m_NodeUsedForOdfVisualization->GetData() ); if( currentImage == NULL ) { MITK_ERROR << " Casting problems. Returning false"; return false; } int affectedDimension(-1); int affectedSlice(-1); return !(mitk::SegTool2D::DetermineAffectedImageSlice( currentImage, displayPlane, affectedDimension, affectedSlice )); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON(int view) { if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::ShowMaxNumberChanged() { int maxNr = m_Controls->m_ShowMaxNumber->value(); if ( maxNr < 1 ) { m_Controls->m_ShowMaxNumber->setValue( 1 ); maxNr = 1; } mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("QBallImage"); SetIntProp(set,"ShowMaxNumber", maxNr); set = ActiveSet("TensorImage"); SetIntProp(set,"ShowMaxNumber", maxNr); if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::NormalizationDropdownChanged(int normDropdown) { typedef mitk::OdfNormalizationMethodProperty PropType; PropType::Pointer normMeth = PropType::New(); switch(normDropdown) { case 0: normMeth->SetNormalizationToMinMax(); break; case 1: normMeth->SetNormalizationToMax(); break; case 2: normMeth->SetNormalizationToNone(); break; case 3: normMeth->SetNormalizationToGlobalMax(); break; default: normMeth->SetNormalizationToMinMax(); } mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("QBallImage"); SetEnumProp(set,"Normalization", normMeth.GetPointer()); set = ActiveSet("TensorImage"); SetEnumProp(set,"Normalization", normMeth.GetPointer()); // if(m_MultiWidget) // m_MultiWidget->RequestUpdate(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::ScalingFactorChanged(double scalingFactor) { mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("QBallImage"); SetFloatProp(set,"Scaling", scalingFactor); set = ActiveSet("TensorImage"); SetFloatProp(set,"Scaling", scalingFactor); if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::AdditionalScaling(int additionalScaling) { typedef mitk::OdfScaleByProperty PropType; PropType::Pointer scaleBy = PropType::New(); switch(additionalScaling) { case 0: scaleBy->SetScaleByNothing(); break; case 1: scaleBy->SetScaleByGFA(); //m_Controls->params_frame->setVisible(true); break; #ifdef DIFFUSION_IMAGING_EXTENDED case 2: scaleBy->SetScaleByPrincipalCurvature(); // commented in for SPIE paper, Principle curvature scaling //m_Controls->params_frame->setVisible(true); break; #endif default: scaleBy->SetScaleByNothing(); } mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("QBallImage"); SetEnumProp(set,"ScaleBy", scaleBy.GetPointer()); set = ActiveSet("TensorImage"); SetEnumProp(set,"ScaleBy", scaleBy.GetPointer()); if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::IndexParam1Changed(double param1) { mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("QBallImage"); SetFloatProp(set,"IndexParam1", param1); set = ActiveSet("TensorImage"); SetFloatProp(set,"IndexParam1", param1); if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::IndexParam2Changed(double param2) { mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("QBallImage"); SetFloatProp(set,"IndexParam2", param2); set = ActiveSet("TensorImage"); SetFloatProp(set,"IndexParam2", param2); if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::OpacityChanged(double l, double u) { mitk::LevelWindow olw; olw.SetRangeMinMax(l*255, u*255); mitk::DataStorage::SetOfObjects::Pointer set = ActiveSet("QBallImage"); SetLevelWindowProp(set,"opaclevelwindow", olw); set = ActiveSet("TensorImage"); SetLevelWindowProp(set,"opaclevelwindow", olw); set = ActiveSet("Image"); SetLevelWindowProp(set,"opaclevelwindow", olw); m_Controls->m_OpacityMinFaLabel->setText(QString::number(l,'f',2) + " : " + QString::number(u,'f',2)); if(m_MultiWidget) m_MultiWidget->RequestUpdate(); } void QmitkControlVisualizationPropertiesView::ScalingCheckbox() { m_Controls->m_ScalingFrame->setVisible( m_Controls->m_ScalingCheckbox->isChecked()); if(!m_Controls->m_ScalingCheckbox->isChecked()) { m_Controls->m_AdditionalScaling->setCurrentIndex(0); m_Controls->m_ScalingFactor->setValue(1.0); } } void QmitkControlVisualizationPropertiesView::Fiber2DfadingEFX() { if (m_SelectedNode) { bool currentMode; m_SelectedNode->GetBoolProperty("Fiber2DfadeEFX", currentMode); m_SelectedNode->SetProperty("Fiber2DfadeEFX", mitk::BoolProperty::New(!currentMode)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } void QmitkControlVisualizationPropertiesView::FiberSlicingThickness2D() { if (m_SelectedNode) { float fibThickness = m_Controls->m_FiberThicknessSlider->value() * 0.1; m_SelectedNode->SetProperty("Fiber2DSliceThickness", mitk::FloatProperty::New(fibThickness)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } void QmitkControlVisualizationPropertiesView::FiberSlicingUpdateLabel(int value) { QString label = "Range %1"; label = label.arg(value * 0.1); m_Controls->label_range->setText(label); } void QmitkControlVisualizationPropertiesView::BundleRepresentationWire() { if(m_SelectedNode) { int width = m_Controls->m_LineWidth->value(); m_SelectedNode->SetProperty("LineWidth",mitk::IntProperty::New(width)); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(15)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(18)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(1)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(2)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(3)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(4)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(0)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } void QmitkControlVisualizationPropertiesView::BundleRepresentationTube() { if(m_SelectedNode) { float radius = m_Controls->m_TubeRadius->value() / 100.0; m_SelectedNode->SetProperty("TubeRadius",mitk::FloatProperty::New(radius)); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(17)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(13)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(16)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); m_SelectedNode->SetProperty("ColorCoding",mitk::IntProperty::New(0)); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } void QmitkControlVisualizationPropertiesView::SetFiberBundleCustomColor(const itk::EventObject& /*e*/) { float color[3]; m_SelectedNode->GetColor(color); m_Controls->m_Color->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0]*255.0)); styleSheet.append(","); styleSheet.append(QString::number(color[1]*255.0)); styleSheet.append(","); styleSheet.append(QString::number(color[2]*255.0)); styleSheet.append(")"); m_Controls->m_Color->setStyleSheet(styleSheet); m_SelectedNode->SetProperty("color",mitk::ColorProperty::New(color[0], color[1], color[2])); mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_CUSTOM); m_SelectedNode->Modified(); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } void QmitkControlVisualizationPropertiesView::BundleRepresentationColor() { if(m_SelectedNode) { QColor color = QColorDialog::getColor(); if (!color.isValid()) return; m_Controls->m_Color->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color.red())); styleSheet.append(","); styleSheet.append(QString::number(color.green())); styleSheet.append(","); styleSheet.append(QString::number(color.blue())); styleSheet.append(")"); m_Controls->m_Color->setStyleSheet(styleSheet); m_SelectedNode->SetProperty("color",mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0)); mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_CUSTOM); m_SelectedNode->Modified(); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } void QmitkControlVisualizationPropertiesView::BundleRepresentationResetColoring() { if(m_SelectedNode) { MITK_INFO << "reset colorcoding to oBased"; m_Controls->m_Color->setAutoFillBackground(true); QString styleSheet = "background-color:rgb(255,255,255)"; m_Controls->m_Color->setStyleSheet(styleSheet); // m_SelectedNode->SetProperty("color",NULL); m_SelectedNode->SetProperty("color",mitk::ColorProperty::New(1.0, 1.0, 1.0)); mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_ORIENTATION_BASED); fib->DoColorCodingOrientationBased(); m_SelectedNode->Modified(); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } } void QmitkControlVisualizationPropertiesView::PlanarFigureFocus() { if(m_SelectedNode) { mitk::PlanarFigure* _PlanarFigure = 0; _PlanarFigure = dynamic_cast (m_SelectedNode->GetData()); if (_PlanarFigure) { QmitkRenderWindow* selectedRenderWindow = 0; bool PlanarFigureInitializedWindow = false; QmitkRenderWindow* RenderWindow1 = this->GetActiveStdMultiWidget()->GetRenderWindow1(); if (m_SelectedNode->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } QmitkRenderWindow* RenderWindow2 = this->GetActiveStdMultiWidget()->GetRenderWindow2(); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } QmitkRenderWindow* RenderWindow3 = this->GetActiveStdMultiWidget()->GetRenderWindow3(); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } QmitkRenderWindow* RenderWindow4 = this->GetActiveStdMultiWidget()->GetRenderWindow4(); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } const mitk::PlaneGeometry * _PlaneGeometry = dynamic_cast (_PlanarFigure->GetGeometry2D()); mitk::VnlVector normal = _PlaneGeometry->GetNormalVnl(); mitk::Geometry2D::ConstPointer worldGeometry1 = RenderWindow1->GetRenderer()->GetCurrentWorldGeometry2D(); mitk::PlaneGeometry::ConstPointer _Plane1 = dynamic_cast( worldGeometry1.GetPointer() ); mitk::VnlVector normal1 = _Plane1->GetNormalVnl(); mitk::Geometry2D::ConstPointer worldGeometry2 = RenderWindow2->GetRenderer()->GetCurrentWorldGeometry2D(); mitk::PlaneGeometry::ConstPointer _Plane2 = dynamic_cast( worldGeometry2.GetPointer() ); mitk::VnlVector normal2 = _Plane2->GetNormalVnl(); mitk::Geometry2D::ConstPointer worldGeometry3 = RenderWindow3->GetRenderer()->GetCurrentWorldGeometry2D(); mitk::PlaneGeometry::ConstPointer _Plane3 = dynamic_cast( worldGeometry3.GetPointer() ); mitk::VnlVector normal3 = _Plane3->GetNormalVnl(); normal[0] = fabs(normal[0]); normal[1] = fabs(normal[1]); normal[2] = fabs(normal[2]); normal1[0] = fabs(normal1[0]); normal1[1] = fabs(normal1[1]); normal1[2] = fabs(normal1[2]); normal2[0] = fabs(normal2[0]); normal2[1] = fabs(normal2[1]); normal2[2] = fabs(normal2[2]); normal3[0] = fabs(normal3[0]); normal3[1] = fabs(normal3[1]); normal3[2] = fabs(normal3[2]); double ang1 = angle(normal, normal1); double ang2 = angle(normal, normal2); double ang3 = angle(normal, normal3); if(ang1 < ang2 && ang1 < ang3) { selectedRenderWindow = RenderWindow1; } else { if(ang2 < ang3) { selectedRenderWindow = RenderWindow2; } else { selectedRenderWindow = RenderWindow3; } } // make node visible if (selectedRenderWindow) { mitk::Point3D centerP = _PlaneGeometry->GetOrigin(); selectedRenderWindow->GetSliceNavigationController()->ReorientSlices( centerP, _PlaneGeometry->GetNormal()); selectedRenderWindow->GetSliceNavigationController()->SelectSliceByPoint( centerP); } } // set interactor for new node (if not already set) mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(m_SelectedNode->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", m_SelectedNode); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); m_SelectedNode->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); } } void QmitkControlVisualizationPropertiesView::SetInteractor() { typedef std::vector Container; Container _NodeSet = this->GetDataManagerSelection(); mitk::DataNode* node = 0; mitk::FiberBundleX* bundle = 0; mitk::FiberBundleInteractor::Pointer bundleInteractor = 0; // finally add all nodes to the model for(Container::const_iterator it=_NodeSet.begin(); it!=_NodeSet.end() ; it++) { node = const_cast(*it); bundle = dynamic_cast(node->GetData()); if(bundle) { bundleInteractor = dynamic_cast(node->GetInteractor()); if(bundleInteractor.IsNotNull()) mitk::GlobalInteraction::GetInstance()->RemoveInteractor(bundleInteractor); if(!m_Controls->m_Crosshair->isChecked()) { m_Controls->m_Crosshair->setChecked(false); this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::ArrowCursor); m_CurrentPickingNode = 0; } else { m_Controls->m_Crosshair->setChecked(true); bundleInteractor = mitk::FiberBundleInteractor::New("FiberBundleInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(bundleInteractor); this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::CrossCursor); m_CurrentPickingNode = node; } } } } void QmitkControlVisualizationPropertiesView::PFWidth(int w) { double width = w/10.0; m_SelectedNode->SetProperty("planarfigure.line.width", mitk::FloatProperty::New(width) ); m_SelectedNode->SetProperty("planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(width) ); m_SelectedNode->SetProperty("planarfigure.outline.width", mitk::FloatProperty::New(width) ); m_SelectedNode->SetProperty("planarfigure.helperline.width", mitk::FloatProperty::New(width) ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); QString label = "Width %1"; label = label.arg(width); m_Controls->label_pfwidth->setText(label); } void QmitkControlVisualizationPropertiesView::PFColor() { QColor color = QColorDialog::getColor(); if (!color.isValid()) return; m_Controls->m_PFColor->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color.red())); styleSheet.append(","); styleSheet.append(QString::number(color.green())); styleSheet.append(","); styleSheet.append(QString::number(color.blue())); styleSheet.append(")"); m_Controls->m_PFColor->setStyleSheet(styleSheet); m_SelectedNode->SetProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0)); m_SelectedNode->SetProperty( "planarfigure.default.outline.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0)); m_SelectedNode->SetProperty( "planarfigure.default.helperline.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0)); m_SelectedNode->SetProperty( "planarfigure.default.markerline.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0)); m_SelectedNode->SetProperty( "planarfigure.default.marker.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0)); m_SelectedNode->SetProperty( "planarfigure.hover.line.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0) ); m_SelectedNode->SetProperty( "planarfigure.hover.outline.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0) ); m_SelectedNode->SetProperty( "planarfigure.hover.helperline.color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0) ); m_SelectedNode->SetProperty( "color", mitk::ColorProperty::New(color.red()/255.0, color.green()/255.0, color.blue()/255.0)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::GenerateTdi() { if(m_SelectedNode) { mitk::FiberBundleX* bundle = dynamic_cast(m_SelectedNode->GetData()); if(!bundle) return; typedef float OutPixType; typedef itk::Image OutImageType; // run generator itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); generator->SetFiberBundle(bundle); - generator->SetUpsamplingFactor(2); + generator->SetOutputAbsoluteValues(true); + generator->SetUpsamplingFactor(1); generator->Update(); // get result OutImageType::Pointer outImg = generator->GetOutput(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); // to datastorage mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); QString name(m_SelectedNode->GetName().c_str()); name += "_TDI"; node->SetName(name.toStdString()); node->SetVisibility(true); GetDataStorage()->Add(node); } } void QmitkControlVisualizationPropertiesView::LineWidthChanged(int w) { QString label = "Width %1"; label = label.arg(w); m_Controls->label_linewidth->setText(label); BundleRepresentationWire(); } void QmitkControlVisualizationPropertiesView::TubeRadiusChanged(int r) { QString label = "Radius %1"; label = label.arg(r / 100.0); m_Controls->label_tuberadius->setText(label); this->BundleRepresentationTube(); } void QmitkControlVisualizationPropertiesView::Welcome() { berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro( GetSite()->GetWorkbenchWindow(), false); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp index 397b9cef81..5da3f432f6 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.cpp @@ -1,1766 +1,1830 @@ /*=================================================================== 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. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkFiberProcessingView.h" #include // Qt #include // MITK #include #include #include #include #include #include #include #include #include #include #include +#include // ITK #include #include #include #include #include #include #include #include const std::string QmitkFiberProcessingView::VIEW_ID = "org.mitk.views.fiberprocessing"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace mitk; QmitkFiberProcessingView::QmitkFiberProcessingView() -: QmitkFunctionality() -, m_Controls( 0 ) -, m_MultiWidget( NULL ) -, m_EllipseCounter(0) -, m_PolygonCounter(0) -, m_UpsamplingFactor(5) + : QmitkFunctionality() + , m_Controls( 0 ) + , m_MultiWidget( NULL ) + , m_EllipseCounter(0) + , m_PolygonCounter(0) + , m_UpsamplingFactor(5) { } // Destructor QmitkFiberProcessingView::~QmitkFiberProcessingView() { } void QmitkFiberProcessingView::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::QmitkFiberProcessingViewControls; - m_Controls->setupUi( parent ); - m_Controls->doExtractFibersButton->setDisabled(true); - m_Controls->PFCompoANDButton->setDisabled(true); - m_Controls->PFCompoORButton->setDisabled(true); - m_Controls->PFCompoNOTButton->setDisabled(true); - m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); - m_Controls->m_RectangleButton->setVisible(false); - - connect( m_Controls->doExtractFibersButton, SIGNAL(clicked()), this, SLOT(DoFiberExtraction()) ); - connect( m_Controls->m_CircleButton, SIGNAL( clicked() ), this, SLOT( OnDrawCircle() ) ); - connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ), this, SLOT( OnDrawPolygon() ) ); - connect(m_Controls->PFCompoANDButton, SIGNAL(clicked()), this, SLOT(GenerateAndComposite()) ); - connect(m_Controls->PFCompoORButton, SIGNAL(clicked()), this, SLOT(GenerateOrComposite()) ); - connect(m_Controls->PFCompoNOTButton, SIGNAL(clicked()), this, SLOT(GenerateNotComposite()) ); - - connect(m_Controls->m_JoinBundles, SIGNAL(clicked()), this, SLOT(JoinBundles()) ); - connect(m_Controls->m_SubstractBundles, SIGNAL(clicked()), this, SLOT(SubstractBundles()) ); - connect(m_Controls->m_GenerateRoiImage, SIGNAL(clicked()), this, SLOT(GenerateRoiImage()) ); - - connect(m_Controls->m_Extract3dButton, SIGNAL(clicked()), this, SLOT(Extract3d())); - connect( m_Controls->m_ProcessFiberBundleButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedBundles()) ); - connect( m_Controls->m_ResampleFibersButton, SIGNAL(clicked()), this, SLOT(ResampleSelectedBundles()) ); - connect(m_Controls->m_FaColorFibersButton, SIGNAL(clicked()), this, SLOT(DoFaColorCoding())); - } + // build up qt view, unless already done + if ( !m_Controls ) + { + // create GUI widgets from the Qt Designer's .ui file + m_Controls = new Ui::QmitkFiberProcessingViewControls; + m_Controls->setupUi( parent ); + m_Controls->doExtractFibersButton->setDisabled(true); + m_Controls->PFCompoANDButton->setDisabled(true); + m_Controls->PFCompoORButton->setDisabled(true); + m_Controls->PFCompoNOTButton->setDisabled(true); + m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); + m_Controls->m_RectangleButton->setVisible(false); + + connect( m_Controls->doExtractFibersButton, SIGNAL(clicked()), this, SLOT(DoFiberExtraction()) ); + connect( m_Controls->m_CircleButton, SIGNAL( clicked() ), this, SLOT( OnDrawCircle() ) ); + connect( m_Controls->m_PolygonButton, SIGNAL( clicked() ), this, SLOT( OnDrawPolygon() ) ); + connect(m_Controls->PFCompoANDButton, SIGNAL(clicked()), this, SLOT(GenerateAndComposite()) ); + connect(m_Controls->PFCompoORButton, SIGNAL(clicked()), this, SLOT(GenerateOrComposite()) ); + connect(m_Controls->PFCompoNOTButton, SIGNAL(clicked()), this, SLOT(GenerateNotComposite()) ); + connect(m_Controls->m_JoinBundles, SIGNAL(clicked()), this, SLOT(JoinBundles()) ); + connect(m_Controls->m_SubstractBundles, SIGNAL(clicked()), this, SLOT(SubstractBundles()) ); + connect(m_Controls->m_GenerateRoiImage, SIGNAL(clicked()), this, SLOT(GenerateRoiImage()) ); + connect(m_Controls->m_Extract3dButton, SIGNAL(clicked()), this, SLOT(Extract3d())); + connect( m_Controls->m_ProcessFiberBundleButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedBundles()) ); + connect( m_Controls->m_ResampleFibersButton, SIGNAL(clicked()), this, SLOT(ResampleSelectedBundles()) ); + connect(m_Controls->m_FaColorFibersButton, SIGNAL(clicked()), this, SLOT(DoFaColorCoding())); + connect( m_Controls->m_PruneFibersButton, SIGNAL(clicked()), this, SLOT(PruneBundle()) ); + connect( m_Controls->m_MirrorFibersButton, SIGNAL(clicked()), this, SLOT(MirrorFibers()) ); + + } } void QmitkFiberProcessingView::Extract3d() { - std::vector nodes = this->GetDataManagerSelection(); - if (nodes.empty()) - return; - - mitk::FiberBundleX::Pointer fib = mitk::FiberBundleX::New(); - mitk::Surface::Pointer roi = mitk::Surface::New(); - bool fibB = false; - bool roiB = false; - for (int i=0; i(nodes.at(i)->GetData())) - { - fib = dynamic_cast(nodes.at(i)->GetData()); - fibB = true; - } - else if (dynamic_cast(nodes.at(i)->GetData())) + std::vector nodes = this->GetDataManagerSelection(); + if (nodes.empty()) + return; + + mitk::FiberBundleX::Pointer fib = mitk::FiberBundleX::New(); + mitk::Surface::Pointer roi = mitk::Surface::New(); + bool fibB = false; + bool roiB = false; + for (int i=0; i(nodes.at(i)->GetData()); - roiB = true; + if (dynamic_cast(nodes.at(i)->GetData())) + { + fib = dynamic_cast(nodes.at(i)->GetData()); + fibB = true; + } + else if (dynamic_cast(nodes.at(i)->GetData())) + { + roi = dynamic_cast(nodes.at(i)->GetData()); + roiB = true; + } } - } - if (!fibB) - return; - if (!roiB) - return; - - vtkSmartPointer polyRoi = roi->GetVtkPolyData(); - vtkSmartPointer polyFib = fib->GetFiberPolyData(); - - vtkSmartPointer selectEnclosedPoints = vtkSmartPointer::New(); - selectEnclosedPoints->SetInput(polyFib); - selectEnclosedPoints->SetSurface(polyRoi); - selectEnclosedPoints->Update(); - - vtkSmartPointer newPoly = vtkSmartPointer::New(); - vtkSmartPointer newCellArray = vtkSmartPointer::New(); - vtkSmartPointer newPoints = vtkSmartPointer::New(); - - vtkSmartPointer newPolyComplement = vtkSmartPointer::New(); - vtkSmartPointer newCellArrayComplement = vtkSmartPointer::New(); - vtkSmartPointer newPointsComplement = vtkSmartPointer::New(); - - vtkSmartPointer vLines = polyFib->GetLines(); - - vLines->InitTraversal(); - int numberOfLines = vLines->GetNumberOfCells(); - // each line - for (int j=0; jGetNextCell ( numPoints, points ); - bool isPassing = false; - - // each point of this line - for (int k=0; k polyRoi = roi->GetVtkPolyData(); + vtkSmartPointer polyFib = fib->GetFiberPolyData(); + + vtkSmartPointer selectEnclosedPoints = vtkSmartPointer::New(); + selectEnclosedPoints->SetInput(polyFib); + selectEnclosedPoints->SetSurface(polyRoi); + selectEnclosedPoints->Update(); + + vtkSmartPointer newPoly = vtkSmartPointer::New(); + vtkSmartPointer newCellArray = vtkSmartPointer::New(); + vtkSmartPointer newPoints = vtkSmartPointer::New(); + + vtkSmartPointer newPolyComplement = vtkSmartPointer::New(); + vtkSmartPointer newCellArrayComplement = vtkSmartPointer::New(); + vtkSmartPointer newPointsComplement = vtkSmartPointer::New(); + + vtkSmartPointer vLines = polyFib->GetLines(); + + vLines->InitTraversal(); + int numberOfLines = vLines->GetNumberOfCells(); + // each line + for (int j=0; jIsInside(points[k])) - { - isPassing = true; - // fill new polydata - vtkSmartPointer container = vtkSmartPointer::New(); + vtkIdType numPoints(0); + vtkIdType* points(NULL); + vLines->GetNextCell ( numPoints, points ); + bool isPassing = false; + + // each point of this line for (int k=0; kGetPoint(points[k]); - vtkIdType pointId = newPoints->InsertNextPoint(point); - container->GetPointIds()->InsertNextId(pointId); + // is point inside polydata ? + if (selectEnclosedPoints->IsInside(points[k])) + { + isPassing = true; + // fill new polydata + vtkSmartPointer container = vtkSmartPointer::New(); + for (int k=0; kGetPoint(points[k]); + vtkIdType pointId = newPoints->InsertNextPoint(point); + container->GetPointIds()->InsertNextId(pointId); + } + newCellArray->InsertNextCell(container); + break; + } + } + if (!isPassing) + { + vtkSmartPointer container = vtkSmartPointer::New(); + for (int k=0; kGetPoint(points[k]); + vtkIdType pointId = newPointsComplement->InsertNextPoint(point); + container->GetPointIds()->InsertNextId(pointId); + } + newCellArrayComplement->InsertNextCell(container); } - newCellArray->InsertNextCell(container); - break; - } - } - if (!isPassing) - { - vtkSmartPointer container = vtkSmartPointer::New(); - for (int k=0; kGetPoint(points[k]); - vtkIdType pointId = newPointsComplement->InsertNextPoint(point); - container->GetPointIds()->InsertNextId(pointId); - } - newCellArrayComplement->InsertNextCell(container); } - } - - newPoly->SetPoints(newPoints); - newPoly->SetLines(newCellArray); - mitk::FiberBundleX::Pointer fb = mitk::FiberBundleX::New(newPoly); - DataNode::Pointer newNode = DataNode::New(); - newNode->SetData(fb); - newNode->SetName("passing surface"); - GetDefaultDataStorage()->Add(newNode); - - newPolyComplement->SetPoints(newPointsComplement); - newPolyComplement->SetLines(newCellArrayComplement); - mitk::FiberBundleX::Pointer fbComplement = mitk::FiberBundleX::New(newPolyComplement); - DataNode::Pointer newNodeComplement = DataNode::New(); - newNodeComplement->SetData(fbComplement); - newNodeComplement->SetName("not passing surface"); - GetDefaultDataStorage()->Add(newNodeComplement); + + newPoly->SetPoints(newPoints); + newPoly->SetLines(newCellArray); + mitk::FiberBundleX::Pointer fb = mitk::FiberBundleX::New(newPoly); + DataNode::Pointer newNode = DataNode::New(); + newNode->SetData(fb); + newNode->SetName("passing surface"); + GetDefaultDataStorage()->Add(newNode); + + newPolyComplement->SetPoints(newPointsComplement); + newPolyComplement->SetLines(newCellArrayComplement); + mitk::FiberBundleX::Pointer fbComplement = mitk::FiberBundleX::New(newPolyComplement); + DataNode::Pointer newNodeComplement = DataNode::New(); + newNodeComplement->SetData(fbComplement); + newNodeComplement->SetName("not passing surface"); + GetDefaultDataStorage()->Add(newNodeComplement); } void QmitkFiberProcessingView::GenerateRoiImage(){ - if (m_SelectedPF.empty()) - return; - - mitk::Geometry3D::Pointer geometry; - if (!m_SelectedFB.empty()) - { - mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedFB.front()->GetData()); - geometry = fib->GetGeometry(); - } - else - return; - - mitk::Vector3D spacing = geometry->GetSpacing(); - spacing /= m_UpsamplingFactor; - - mitk::Point3D newOrigin = geometry->GetOrigin(); - mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); - newOrigin[0] += bounds.GetElement(0); - newOrigin[1] += bounds.GetElement(2); - newOrigin[2] += bounds.GetElement(4); - - itk::Matrix direction; - itk::ImageRegion<3> imageRegion; - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - direction[j][i] = geometry->GetMatrixColumn(i)[j]/spacing[j]; - imageRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); - imageRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); - imageRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); - - m_PlanarFigureImage = itkUCharImageType::New(); - m_PlanarFigureImage->SetSpacing( spacing ); // Set the image spacing - m_PlanarFigureImage->SetOrigin( newOrigin ); // Set the image origin - m_PlanarFigureImage->SetDirection( direction ); // Set the image direction - m_PlanarFigureImage->SetRegions( imageRegion ); - m_PlanarFigureImage->Allocate(); - m_PlanarFigureImage->FillBuffer( 0 ); - - Image::Pointer tmpImage = Image::New(); - tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); - tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); - - for (int i=0; iInitializeByItk(m_PlanarFigureImage.GetPointer()); - tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); - node->SetData(tmpImage); - node->SetName("ROI Image"); - this->GetDefaultDataStorage()->Add(node); + if (m_SelectedPF.empty()) + return; + + mitk::Geometry3D::Pointer geometry; + if (!m_SelectedFB.empty()) + { + mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedFB.front()->GetData()); + geometry = fib->GetGeometry(); + } + else + return; + + mitk::Vector3D spacing = geometry->GetSpacing(); + spacing /= m_UpsamplingFactor; + + mitk::Point3D newOrigin = geometry->GetOrigin(); + mitk::Geometry3D::BoundsArrayType bounds = geometry->GetBounds(); + newOrigin[0] += bounds.GetElement(0); + newOrigin[1] += bounds.GetElement(2); + newOrigin[2] += bounds.GetElement(4); + + itk::Matrix direction; + itk::ImageRegion<3> imageRegion; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + direction[j][i] = geometry->GetMatrixColumn(i)[j]/spacing[j]; + imageRegion.SetSize(0, geometry->GetExtent(0)*m_UpsamplingFactor); + imageRegion.SetSize(1, geometry->GetExtent(1)*m_UpsamplingFactor); + imageRegion.SetSize(2, geometry->GetExtent(2)*m_UpsamplingFactor); + + m_PlanarFigureImage = itkUCharImageType::New(); + m_PlanarFigureImage->SetSpacing( spacing ); // Set the image spacing + m_PlanarFigureImage->SetOrigin( newOrigin ); // Set the image origin + m_PlanarFigureImage->SetDirection( direction ); // Set the image direction + m_PlanarFigureImage->SetRegions( imageRegion ); + m_PlanarFigureImage->Allocate(); + m_PlanarFigureImage->FillBuffer( 0 ); + + Image::Pointer tmpImage = Image::New(); + tmpImage->InitializeByItk(m_PlanarFigureImage.GetPointer()); + tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); + + for (int i=0; iInitializeByItk(m_PlanarFigureImage.GetPointer()); + tmpImage->SetVolume(m_PlanarFigureImage->GetBufferPointer()); + node->SetData(tmpImage); + node->SetName("ROI Image"); + this->GetDefaultDataStorage()->Add(node); } void QmitkFiberProcessingView::CompositeExtraction(mitk::DataNode::Pointer node, mitk::Image* image) { - if (dynamic_cast(node.GetPointer()->GetData()) && !dynamic_cast(node.GetPointer()->GetData())) - { - m_PlanarFigure = dynamic_cast(node.GetPointer()->GetData()); - AccessFixedDimensionByItk_2( - image, - InternalReorientImagePlane, 3, - m_PlanarFigure->GetGeometry(), -1); - -// itk::Image< unsigned char, 3 >::Pointer outimage = itk::Image< unsigned char, 3 >::New(); - -// outimage->SetSpacing( m_PlanarFigure->GetGeometry()->GetSpacing()/m_UpsamplingFactor ); // Set the image spacing - -// mitk::Point3D origin = m_PlanarFigure->GetGeometry()->GetOrigin(); -// mitk::Point3D indexOrigin; -// m_PlanarFigure->GetGeometry()->WorldToIndex(origin, indexOrigin); -// indexOrigin[0] = indexOrigin[0] - .5 * (1.0-1.0/m_UpsamplingFactor); -// indexOrigin[1] = indexOrigin[1] - .5 * (1.0-1.0/m_UpsamplingFactor); -// indexOrigin[2] = indexOrigin[2] - .5 * (1.0-1.0/m_UpsamplingFactor); -// mitk::Point3D newOrigin; -// m_PlanarFigure->GetGeometry()->IndexToWorld(indexOrigin, newOrigin); - -// outimage->SetOrigin( newOrigin ); // Set the image origin -// itk::Matrix matrix; -// for (int i=0; i<3; i++) -// for (int j=0; j<3; j++) -// matrix[j][i] = m_PlanarFigure->GetGeometry()->GetMatrixColumn(i)[j]/m_PlanarFigure->GetGeometry()->GetSpacing().GetElement(i); -// outimage->SetDirection( matrix ); // Set the image direction - -// itk::ImageRegion<3> upsampledRegion; -// upsampledRegion.SetSize(0, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(0)/m_PlanarFigure->GetGeometry()->GetSpacing()[0]); -// upsampledRegion.SetSize(1, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(1)/m_PlanarFigure->GetGeometry()->GetSpacing()[1]); -// upsampledRegion.SetSize(2, 1); - -// typename itk::Image< unsigned char, 3 >::RegionType::SizeType upsampledSize = upsampledRegion.GetSize(); -// for (unsigned int n = 0; n < 2; n++) -// { -// upsampledSize[n] = upsampledSize[n] * m_UpsamplingFactor; -// } -// upsampledRegion.SetSize( upsampledSize ); -// outimage->SetRegions( upsampledRegion ); - -// outimage->Allocate(); - -// this->m_InternalImage = mitk::Image::New(); -// this->m_InternalImage->InitializeByItk( outimage.GetPointer() ); -// this->m_InternalImage->SetVolume( outimage->GetBufferPointer() ); - - AccessFixedDimensionByItk_2( - m_InternalImage, - InternalCalculateMaskFromPlanarFigure, - 3, 2, node->GetName() ); - } + if (dynamic_cast(node.GetPointer()->GetData()) && !dynamic_cast(node.GetPointer()->GetData())) + { + m_PlanarFigure = dynamic_cast(node.GetPointer()->GetData()); + AccessFixedDimensionByItk_2( + image, + InternalReorientImagePlane, 3, + m_PlanarFigure->GetGeometry(), -1); + + // itk::Image< unsigned char, 3 >::Pointer outimage = itk::Image< unsigned char, 3 >::New(); + + // outimage->SetSpacing( m_PlanarFigure->GetGeometry()->GetSpacing()/m_UpsamplingFactor ); // Set the image spacing + + // mitk::Point3D origin = m_PlanarFigure->GetGeometry()->GetOrigin(); + // mitk::Point3D indexOrigin; + // m_PlanarFigure->GetGeometry()->WorldToIndex(origin, indexOrigin); + // indexOrigin[0] = indexOrigin[0] - .5 * (1.0-1.0/m_UpsamplingFactor); + // indexOrigin[1] = indexOrigin[1] - .5 * (1.0-1.0/m_UpsamplingFactor); + // indexOrigin[2] = indexOrigin[2] - .5 * (1.0-1.0/m_UpsamplingFactor); + // mitk::Point3D newOrigin; + // m_PlanarFigure->GetGeometry()->IndexToWorld(indexOrigin, newOrigin); + + // outimage->SetOrigin( newOrigin ); // Set the image origin + // itk::Matrix matrix; + // for (int i=0; i<3; i++) + // for (int j=0; j<3; j++) + // matrix[j][i] = m_PlanarFigure->GetGeometry()->GetMatrixColumn(i)[j]/m_PlanarFigure->GetGeometry()->GetSpacing().GetElement(i); + // outimage->SetDirection( matrix ); // Set the image direction + + // itk::ImageRegion<3> upsampledRegion; + // upsampledRegion.SetSize(0, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(0)/m_PlanarFigure->GetGeometry()->GetSpacing()[0]); + // upsampledRegion.SetSize(1, m_PlanarFigure->GetGeometry()->GetParametricExtentInMM(1)/m_PlanarFigure->GetGeometry()->GetSpacing()[1]); + // upsampledRegion.SetSize(2, 1); + + // typename itk::Image< unsigned char, 3 >::RegionType::SizeType upsampledSize = upsampledRegion.GetSize(); + // for (unsigned int n = 0; n < 2; n++) + // { + // upsampledSize[n] = upsampledSize[n] * m_UpsamplingFactor; + // } + // upsampledRegion.SetSize( upsampledSize ); + // outimage->SetRegions( upsampledRegion ); + + // outimage->Allocate(); + + // this->m_InternalImage = mitk::Image::New(); + // this->m_InternalImage->InitializeByItk( outimage.GetPointer() ); + // this->m_InternalImage->SetVolume( outimage->GetBufferPointer() ); + + AccessFixedDimensionByItk_2( + m_InternalImage, + InternalCalculateMaskFromPlanarFigure, + 3, 2, node->GetName() ); + } } template < typename TPixel, unsigned int VImageDimension > - void QmitkFiberProcessingView::InternalReorientImagePlane( +void QmitkFiberProcessingView::InternalReorientImagePlane( const itk::Image< TPixel, VImageDimension > *image, mitk::Geometry3D* planegeo3D, int additionalIndex ) { - MITK_INFO << "InternalReorientImagePlane() start"; - - typedef itk::Image< TPixel, VImageDimension > ImageType; - typedef itk::Image< float, VImageDimension > FloatImageType; - - typedef itk::ResampleImageFilter ResamplerType; - typename ResamplerType::Pointer resampler = ResamplerType::New(); - - mitk::PlaneGeometry* planegeo = dynamic_cast(planegeo3D); - - float upsamp = m_UpsamplingFactor; - float gausssigma = 0.5; - - // Spacing - typename ResamplerType::SpacingType spacing = planegeo->GetSpacing(); - spacing[0] = image->GetSpacing()[0] / upsamp; - spacing[1] = image->GetSpacing()[1] / upsamp; - spacing[2] = image->GetSpacing()[2]; - resampler->SetOutputSpacing( spacing ); - - // Size - typename ResamplerType::SizeType size; - size[0] = planegeo->GetParametricExtentInMM(0) / spacing[0]; - size[1] = planegeo->GetParametricExtentInMM(1) / spacing[1]; - size[2] = 1; - resampler->SetSize( size ); - - // Origin - typename mitk::Point3D orig = planegeo->GetOrigin(); - typename mitk::Point3D corrorig; - planegeo3D->WorldToIndex(orig,corrorig); - corrorig[0] += 0.5/upsamp; - corrorig[1] += 0.5/upsamp; - corrorig[2] += 0; - planegeo3D->IndexToWorld(corrorig,corrorig); - resampler->SetOutputOrigin(corrorig ); - - // Direction - typename ResamplerType::DirectionType direction; - typename mitk::AffineTransform3D::MatrixType matrix = planegeo->GetIndexToWorldTransform()->GetMatrix(); - for(int c=0; c ImageType; + typedef itk::Image< float, VImageDimension > FloatImageType; + + typedef itk::ResampleImageFilter ResamplerType; + typename ResamplerType::Pointer resampler = ResamplerType::New(); + + mitk::PlaneGeometry* planegeo = dynamic_cast(planegeo3D); + + float upsamp = m_UpsamplingFactor; + float gausssigma = 0.5; + + // Spacing + typename ResamplerType::SpacingType spacing = planegeo->GetSpacing(); + spacing[0] = image->GetSpacing()[0] / upsamp; + spacing[1] = image->GetSpacing()[1] / upsamp; + spacing[2] = image->GetSpacing()[2]; + resampler->SetOutputSpacing( spacing ); + + // Size + typename ResamplerType::SizeType size; + size[0] = planegeo->GetParametricExtentInMM(0) / spacing[0]; + size[1] = planegeo->GetParametricExtentInMM(1) / spacing[1]; + size[2] = 1; + resampler->SetSize( size ); + + // Origin + typename mitk::Point3D orig = planegeo->GetOrigin(); + typename mitk::Point3D corrorig; + planegeo3D->WorldToIndex(orig,corrorig); + corrorig[0] += 0.5/upsamp; + corrorig[1] += 0.5/upsamp; + corrorig[2] += 0; + planegeo3D->IndexToWorld(corrorig,corrorig); + resampler->SetOutputOrigin(corrorig ); + + // Direction + typename ResamplerType::DirectionType direction; + typename mitk::AffineTransform3D::MatrixType matrix = planegeo->GetIndexToWorldTransform()->GetMatrix(); + for(int c=0; cSetOutputDirection( direction ); - - // Gaussian interpolation - if(gausssigma != 0) - { - double sigma[3]; - for( unsigned int d = 0; d < 3; d++ ) + resampler->SetOutputDirection( direction ); + + // Gaussian interpolation + if(gausssigma != 0) { - sigma[d] = gausssigma * image->GetSpacing()[d]; - } - double alpha = 2.0; + double sigma[3]; + for( unsigned int d = 0; d < 3; d++ ) + { + sigma[d] = gausssigma * image->GetSpacing()[d]; + } + double alpha = 2.0; - typedef itk::GaussianInterpolateImageFunction - GaussianInterpolatorType; + typedef itk::GaussianInterpolateImageFunction + GaussianInterpolatorType; - typename GaussianInterpolatorType::Pointer interpolator - = GaussianInterpolatorType::New(); + typename GaussianInterpolatorType::Pointer interpolator + = GaussianInterpolatorType::New(); - interpolator->SetInputImage( image ); - interpolator->SetParameters( sigma, alpha ); + interpolator->SetInputImage( image ); + interpolator->SetParameters( sigma, alpha ); - resampler->SetInterpolator( interpolator ); - } - else - { - // typedef typename itk::BSplineInterpolateImageFunction - // InterpolatorType; - typedef typename itk::LinearInterpolateImageFunction InterpolatorType; + resampler->SetInterpolator( interpolator ); + } + else + { + // typedef typename itk::BSplineInterpolateImageFunction + // InterpolatorType; + typedef typename itk::LinearInterpolateImageFunction InterpolatorType; - typename InterpolatorType::Pointer interpolator - = InterpolatorType::New(); + typename InterpolatorType::Pointer interpolator + = InterpolatorType::New(); - interpolator->SetInputImage( image ); + interpolator->SetInputImage( image ); - resampler->SetInterpolator( interpolator ); + resampler->SetInterpolator( interpolator ); - } + } - // Other resampling options - resampler->SetInput( image ); - resampler->SetDefaultPixelValue(0); + // Other resampling options + resampler->SetInput( image ); + resampler->SetDefaultPixelValue(0); - MITK_INFO << "Resampling requested image plane ... "; - resampler->Update(); - MITK_INFO << " ... done"; + MITK_INFO << "Resampling requested image plane ... "; + resampler->Update(); + MITK_INFO << " ... done"; - if(additionalIndex < 0) - { - this->m_InternalImage = mitk::Image::New(); - this->m_InternalImage->InitializeByItk( resampler->GetOutput() ); - this->m_InternalImage->SetVolume( resampler->GetOutput()->GetBufferPointer() ); - } + if(additionalIndex < 0) + { + this->m_InternalImage = mitk::Image::New(); + this->m_InternalImage->InitializeByItk( resampler->GetOutput() ); + this->m_InternalImage->SetVolume( resampler->GetOutput()->GetBufferPointer() ); + } } template < typename TPixel, unsigned int VImageDimension > - void QmitkFiberProcessingView::InternalCalculateMaskFromPlanarFigure( +void QmitkFiberProcessingView::InternalCalculateMaskFromPlanarFigure( itk::Image< TPixel, VImageDimension > *image, unsigned int axis, std::string nodeName ) { - MITK_INFO << "InternalCalculateMaskFromPlanarFigure() start"; - - typedef itk::Image< TPixel, VImageDimension > ImageType; - typedef itk::CastImageFilter< ImageType, itkUCharImageType > CastFilterType; - - // Generate mask image as new image with same header as input image and - // initialize with "1". - itkUCharImageType::Pointer newMaskImage = itkUCharImageType::New(); - newMaskImage->SetSpacing( image->GetSpacing() ); // Set the image spacing - newMaskImage->SetOrigin( image->GetOrigin() ); // Set the image origin - newMaskImage->SetDirection( image->GetDirection() ); // Set the image direction - newMaskImage->SetRegions( image->GetLargestPossibleRegion() ); - newMaskImage->Allocate(); - newMaskImage->FillBuffer( 1 ); - - // Generate VTK polygon from (closed) PlanarFigure polyline - // (The polyline points are shifted by -0.5 in z-direction to make sure - // that the extrusion filter, which afterwards elevates all points by +0.5 - // in z-direction, creates a 3D object which is cut by the the plane z=0) - const Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); - const PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); - const Geometry3D *imageGeometry3D = m_InternalImage->GetGeometry( 0 ); - - vtkPolyData *polyline = vtkPolyData::New(); - polyline->Allocate( 1, 1 ); - - // Determine x- and y-dimensions depending on principal axis - int i0, i1; - switch ( axis ) - { - case 0: - i0 = 1; - i1 = 2; - break; - - case 1: - i0 = 0; - i1 = 2; - break; - - case 2: - default: - i0 = 0; - i1 = 1; - break; - } - - // Create VTK polydata object of polyline contour - vtkPoints *points = vtkPoints::New(); - PlanarFigure::PolyLineType::const_iterator it; - std::vector indices; - - unsigned int numberOfPoints = 0; - - for ( it = planarFigurePolyline.begin(); - it != planarFigurePolyline.end(); - ++it ) - { - Point3D point3D; - - // Convert 2D point back to the local index coordinates of the selected - // image - Point2D point2D = it->Point; - planarFigureGeometry2D->WorldToIndex(point2D, point2D); - point2D[0] -= 0.5/m_UpsamplingFactor; - point2D[1] -= 0.5/m_UpsamplingFactor; - planarFigureGeometry2D->IndexToWorld(point2D, point2D); - planarFigureGeometry2D->Map( point2D, point3D ); - - // Polygons (partially) outside of the image bounds can not be processed - // further due to a bug in vtkPolyDataToImageStencil - if ( !imageGeometry3D->IsInside( point3D ) ) + MITK_INFO << "InternalCalculateMaskFromPlanarFigure() start"; + + typedef itk::Image< TPixel, VImageDimension > ImageType; + typedef itk::CastImageFilter< ImageType, itkUCharImageType > CastFilterType; + + // Generate mask image as new image with same header as input image and + // initialize with "1". + itkUCharImageType::Pointer newMaskImage = itkUCharImageType::New(); + newMaskImage->SetSpacing( image->GetSpacing() ); // Set the image spacing + newMaskImage->SetOrigin( image->GetOrigin() ); // Set the image origin + newMaskImage->SetDirection( image->GetDirection() ); // Set the image direction + newMaskImage->SetRegions( image->GetLargestPossibleRegion() ); + newMaskImage->Allocate(); + newMaskImage->FillBuffer( 1 ); + + // Generate VTK polygon from (closed) PlanarFigure polyline + // (The polyline points are shifted by -0.5 in z-direction to make sure + // that the extrusion filter, which afterwards elevates all points by +0.5 + // in z-direction, creates a 3D object which is cut by the the plane z=0) + const Geometry2D *planarFigureGeometry2D = m_PlanarFigure->GetGeometry2D(); + const PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); + const Geometry3D *imageGeometry3D = m_InternalImage->GetGeometry( 0 ); + + vtkPolyData *polyline = vtkPolyData::New(); + polyline->Allocate( 1, 1 ); + + // Determine x- and y-dimensions depending on principal axis + int i0, i1; + switch ( axis ) { - float bounds[2] = {0,0}; - bounds[0] = - this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i0); - bounds[1] = - this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i1); - - imageGeometry3D->WorldToIndex( point3D, point3D ); - -// if (point3D[i0]<0) -// point3D[i0] = 0.5; -// else if (point3D[i0]>bounds[0]) -// point3D[i0] = bounds[0]-0.5; - -// if (point3D[i1]<0) -// point3D[i1] = 0.5; -// else if (point3D[i1]>bounds[1]) -// point3D[i1] = bounds[1]-0.5; - - if (point3D[i0]<0) - point3D[i0] = 0.0; - else if (point3D[i0]>bounds[0]) - point3D[i0] = bounds[0]-0.001; - - if (point3D[i1]<0) - point3D[i1] = 0.0; - else if (point3D[i1]>bounds[1]) - point3D[i1] = bounds[1]-0.001; - - points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); - numberOfPoints++; + case 0: + i0 = 1; + i1 = 2; + break; + + case 1: + i0 = 0; + i1 = 2; + break; + + case 2: + default: + i0 = 0; + i1 = 1; + break; } - else + + // Create VTK polydata object of polyline contour + vtkPoints *points = vtkPoints::New(); + PlanarFigure::PolyLineType::const_iterator it; + std::vector indices; + + unsigned int numberOfPoints = 0; + + for ( it = planarFigurePolyline.begin(); + it != planarFigurePolyline.end(); + ++it ) { - imageGeometry3D->WorldToIndex( point3D, point3D ); + Point3D point3D; + + // Convert 2D point back to the local index coordinates of the selected + // image + Point2D point2D = it->Point; + planarFigureGeometry2D->WorldToIndex(point2D, point2D); + point2D[0] -= 0.5/m_UpsamplingFactor; + point2D[1] -= 0.5/m_UpsamplingFactor; + planarFigureGeometry2D->IndexToWorld(point2D, point2D); + planarFigureGeometry2D->Map( point2D, point3D ); + + // Polygons (partially) outside of the image bounds can not be processed + // further due to a bug in vtkPolyDataToImageStencil + if ( !imageGeometry3D->IsInside( point3D ) ) + { + float bounds[2] = {0,0}; + bounds[0] = + this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i0); + bounds[1] = + this->m_InternalImage->GetLargestPossibleRegion().GetSize().GetElement(i1); + + imageGeometry3D->WorldToIndex( point3D, point3D ); + + // if (point3D[i0]<0) + // point3D[i0] = 0.5; + // else if (point3D[i0]>bounds[0]) + // point3D[i0] = bounds[0]-0.5; + + // if (point3D[i1]<0) + // point3D[i1] = 0.5; + // else if (point3D[i1]>bounds[1]) + // point3D[i1] = bounds[1]-0.5; + + if (point3D[i0]<0) + point3D[i0] = 0.0; + else if (point3D[i0]>bounds[0]) + point3D[i0] = bounds[0]-0.001; + + if (point3D[i1]<0) + point3D[i1] = 0.0; + else if (point3D[i1]>bounds[1]) + point3D[i1] = bounds[1]-0.001; + + points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); + numberOfPoints++; + } + else + { + imageGeometry3D->WorldToIndex( point3D, point3D ); - // Add point to polyline array - points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); - numberOfPoints++; + // Add point to polyline array + points->InsertNextPoint( point3D[i0], point3D[i1], -0.5 ); + numberOfPoints++; + } } - } - polyline->SetPoints( points ); - points->Delete(); + polyline->SetPoints( points ); + points->Delete(); - vtkIdType *ptIds = new vtkIdType[numberOfPoints]; - for ( vtkIdType i = 0; i < numberOfPoints; ++i ) - { - ptIds[i] = i; - } - polyline->InsertNextCell( VTK_POLY_LINE, numberOfPoints, ptIds ); + vtkIdType *ptIds = new vtkIdType[numberOfPoints]; + for ( vtkIdType i = 0; i < numberOfPoints; ++i ) + { + ptIds[i] = i; + } + polyline->InsertNextCell( VTK_POLY_LINE, numberOfPoints, ptIds ); - // Extrude the generated contour polygon - vtkLinearExtrusionFilter *extrudeFilter = vtkLinearExtrusionFilter::New(); - extrudeFilter->SetInput( polyline ); - extrudeFilter->SetScaleFactor( 1 ); - extrudeFilter->SetExtrusionTypeToNormalExtrusion(); - extrudeFilter->SetVector( 0.0, 0.0, 1.0 ); + // Extrude the generated contour polygon + vtkLinearExtrusionFilter *extrudeFilter = vtkLinearExtrusionFilter::New(); + extrudeFilter->SetInput( polyline ); + extrudeFilter->SetScaleFactor( 1 ); + extrudeFilter->SetExtrusionTypeToNormalExtrusion(); + extrudeFilter->SetVector( 0.0, 0.0, 1.0 ); - // Make a stencil from the extruded polygon - vtkPolyDataToImageStencil *polyDataToImageStencil = vtkPolyDataToImageStencil::New(); - polyDataToImageStencil->SetInput( extrudeFilter->GetOutput() ); + // Make a stencil from the extruded polygon + vtkPolyDataToImageStencil *polyDataToImageStencil = vtkPolyDataToImageStencil::New(); + polyDataToImageStencil->SetInput( extrudeFilter->GetOutput() ); - // Export from ITK to VTK (to use a VTK filter) - typedef itk::VTKImageImport< itkUCharImageType > ImageImportType; - typedef itk::VTKImageExport< itkUCharImageType > ImageExportType; + // Export from ITK to VTK (to use a VTK filter) + typedef itk::VTKImageImport< itkUCharImageType > ImageImportType; + typedef itk::VTKImageExport< itkUCharImageType > ImageExportType; - typename ImageExportType::Pointer itkExporter = ImageExportType::New(); - itkExporter->SetInput( newMaskImage ); + typename ImageExportType::Pointer itkExporter = ImageExportType::New(); + itkExporter->SetInput( newMaskImage ); - vtkImageImport *vtkImporter = vtkImageImport::New(); - this->ConnectPipelines( itkExporter, vtkImporter ); - vtkImporter->Update(); + vtkImageImport *vtkImporter = vtkImageImport::New(); + this->ConnectPipelines( itkExporter, vtkImporter ); + vtkImporter->Update(); - // Apply the generated image stencil to the input image - vtkImageStencil *imageStencilFilter = vtkImageStencil::New(); - imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); - imageStencilFilter->SetStencil( polyDataToImageStencil->GetOutput() ); - imageStencilFilter->ReverseStencilOff(); - imageStencilFilter->SetBackgroundValue( 0 ); - imageStencilFilter->Update(); + // Apply the generated image stencil to the input image + vtkImageStencil *imageStencilFilter = vtkImageStencil::New(); + imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); + imageStencilFilter->SetStencil( polyDataToImageStencil->GetOutput() ); + imageStencilFilter->ReverseStencilOff(); + imageStencilFilter->SetBackgroundValue( 0 ); + imageStencilFilter->Update(); - // Export from VTK back to ITK - vtkImageExport *vtkExporter = vtkImageExport::New(); - vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); - vtkExporter->Update(); + // Export from VTK back to ITK + vtkImageExport *vtkExporter = vtkImageExport::New(); + vtkExporter->SetInputConnection( imageStencilFilter->GetOutputPort() ); + vtkExporter->Update(); - typename ImageImportType::Pointer itkImporter = ImageImportType::New(); - this->ConnectPipelines( vtkExporter, itkImporter ); - itkImporter->Update(); + typename ImageImportType::Pointer itkImporter = ImageImportType::New(); + this->ConnectPipelines( vtkExporter, itkImporter ); + itkImporter->Update(); - // calculate cropping bounding box - m_InternalImageMask3D = itkImporter->GetOutput(); - m_InternalImageMask3D->SetDirection(image->GetDirection()); + // calculate cropping bounding box + m_InternalImageMask3D = itkImporter->GetOutput(); + m_InternalImageMask3D->SetDirection(image->GetDirection()); - itk::ImageRegionConstIterator - itmask(m_InternalImageMask3D, m_InternalImageMask3D->GetLargestPossibleRegion()); - itk::ImageRegionIterator - itimage(image, image->GetLargestPossibleRegion()); + itk::ImageRegionConstIterator + itmask(m_InternalImageMask3D, m_InternalImageMask3D->GetLargestPossibleRegion()); + itk::ImageRegionIterator + itimage(image, image->GetLargestPossibleRegion()); - itmask = itmask.Begin(); - itimage = itimage.Begin(); + itmask = itmask.Begin(); + itimage = itimage.Begin(); - typename ImageType::SizeType lowersize = {{9999999999,9999999999,9999999999}}; - typename ImageType::SizeType uppersize = {{0,0,0}}; - while( !itmask.IsAtEnd() ) - { - if(itmask.Get() == 0) - { - itimage.Set(0); - } - else + typename ImageType::SizeType lowersize = {{9999999999,9999999999,9999999999}}; + typename ImageType::SizeType uppersize = {{0,0,0}}; + while( !itmask.IsAtEnd() ) { - typename ImageType::IndexType index = itimage.GetIndex(); - typename ImageType::SizeType signedindex; - signedindex[0] = index[0]; - signedindex[1] = index[1]; - signedindex[2] = index[2]; - - lowersize[0] = signedindex[0] < lowersize[0] ? signedindex[0] : lowersize[0]; - lowersize[1] = signedindex[1] < lowersize[1] ? signedindex[1] : lowersize[1]; - lowersize[2] = signedindex[2] < lowersize[2] ? signedindex[2] : lowersize[2]; - - uppersize[0] = signedindex[0] > uppersize[0] ? signedindex[0] : uppersize[0]; - uppersize[1] = signedindex[1] > uppersize[1] ? signedindex[1] : uppersize[1]; - uppersize[2] = signedindex[2] > uppersize[2] ? signedindex[2] : uppersize[2]; + if(itmask.Get() == 0) + { + itimage.Set(0); + } + else + { + typename ImageType::IndexType index = itimage.GetIndex(); + typename ImageType::SizeType signedindex; + signedindex[0] = index[0]; + signedindex[1] = index[1]; + signedindex[2] = index[2]; + + lowersize[0] = signedindex[0] < lowersize[0] ? signedindex[0] : lowersize[0]; + lowersize[1] = signedindex[1] < lowersize[1] ? signedindex[1] : lowersize[1]; + lowersize[2] = signedindex[2] < lowersize[2] ? signedindex[2] : lowersize[2]; + + uppersize[0] = signedindex[0] > uppersize[0] ? signedindex[0] : uppersize[0]; + uppersize[1] = signedindex[1] > uppersize[1] ? signedindex[1] : uppersize[1]; + uppersize[2] = signedindex[2] > uppersize[2] ? signedindex[2] : uppersize[2]; + } + + ++itmask; + ++itimage; } - ++itmask; - ++itimage; - } - - typename ImageType::IndexType index; - index[0] = lowersize[0]; - index[1] = lowersize[1]; - index[2] = lowersize[2]; - - typename ImageType::SizeType size; - size[0] = uppersize[0] - lowersize[0] + 1; - size[1] = uppersize[1] - lowersize[1] + 1; - size[2] = uppersize[2] - lowersize[2] + 1; - - itk::ImageRegion<3> cropRegion = itk::ImageRegion<3>(index, size); - - // crop internal mask - typedef itk::RegionOfInterestImageFilter< itkUCharImageType, itkUCharImageType > ROIMaskFilterType; - typename ROIMaskFilterType::Pointer roi2 = ROIMaskFilterType::New(); - roi2->SetRegionOfInterest(cropRegion); - roi2->SetInput(m_InternalImageMask3D); - roi2->Update(); - m_InternalImageMask3D = roi2->GetOutput(); - - Image::Pointer tmpImage = Image::New(); - tmpImage->InitializeByItk(m_InternalImageMask3D.GetPointer()); - tmpImage->SetVolume(m_InternalImageMask3D->GetBufferPointer()); - - Image::Pointer tmpImage2 = Image::New(); - tmpImage2->InitializeByItk(m_PlanarFigureImage.GetPointer()); - const Geometry3D *pfImageGeometry3D = tmpImage2->GetGeometry( 0 ); - - const Geometry3D *intImageGeometry3D = tmpImage->GetGeometry( 0 ); - - typedef itk::ImageRegionIteratorWithIndex IteratorType; - IteratorType imageIterator (m_InternalImageMask3D, m_InternalImageMask3D->GetRequestedRegion()); - imageIterator.GoToBegin(); - while ( !imageIterator.IsAtEnd() ) - { - unsigned char val = imageIterator.Value(); - if (val>0) + typename ImageType::IndexType index; + index[0] = lowersize[0]; + index[1] = lowersize[1]; + index[2] = lowersize[2]; + + typename ImageType::SizeType size; + size[0] = uppersize[0] - lowersize[0] + 1; + size[1] = uppersize[1] - lowersize[1] + 1; + size[2] = uppersize[2] - lowersize[2] + 1; + + itk::ImageRegion<3> cropRegion = itk::ImageRegion<3>(index, size); + + // crop internal mask + typedef itk::RegionOfInterestImageFilter< itkUCharImageType, itkUCharImageType > ROIMaskFilterType; + typename ROIMaskFilterType::Pointer roi2 = ROIMaskFilterType::New(); + roi2->SetRegionOfInterest(cropRegion); + roi2->SetInput(m_InternalImageMask3D); + roi2->Update(); + m_InternalImageMask3D = roi2->GetOutput(); + + Image::Pointer tmpImage = Image::New(); + tmpImage->InitializeByItk(m_InternalImageMask3D.GetPointer()); + tmpImage->SetVolume(m_InternalImageMask3D->GetBufferPointer()); + + Image::Pointer tmpImage2 = Image::New(); + tmpImage2->InitializeByItk(m_PlanarFigureImage.GetPointer()); + const Geometry3D *pfImageGeometry3D = tmpImage2->GetGeometry( 0 ); + + const Geometry3D *intImageGeometry3D = tmpImage->GetGeometry( 0 ); + + typedef itk::ImageRegionIteratorWithIndex IteratorType; + IteratorType imageIterator (m_InternalImageMask3D, m_InternalImageMask3D->GetRequestedRegion()); + imageIterator.GoToBegin(); + while ( !imageIterator.IsAtEnd() ) { - itk::Index<3> index = imageIterator.GetIndex(); - Point3D point; - point[0] = index[0]; - point[1] = index[1]; - point[2] = index[2]; + unsigned char val = imageIterator.Value(); + if (val>0) + { + itk::Index<3> index = imageIterator.GetIndex(); + Point3D point; + point[0] = index[0]; + point[1] = index[1]; + point[2] = index[2]; - intImageGeometry3D->IndexToWorld(point, point); - pfImageGeometry3D->WorldToIndex(point, point); + intImageGeometry3D->IndexToWorld(point, point); + pfImageGeometry3D->WorldToIndex(point, point); - point[i0] += 0.5; - point[i1] += 0.5; + point[i0] += 0.5; + point[i1] += 0.5; - index[0] = point[0]; - index[1] = point[1]; - index[2] = point[2]; + index[0] = point[0]; + index[1] = point[1]; + index[2] = point[2]; - if (pfImageGeometry3D->IsIndexInside(index)) - m_PlanarFigureImage->SetPixel(index, 1); + if (pfImageGeometry3D->IsIndexInside(index)) + m_PlanarFigureImage->SetPixel(index, 1); + } + ++imageIterator; } - ++imageIterator; - } - - // Clean up VTK objects - polyline->Delete(); - extrudeFilter->Delete(); - polyDataToImageStencil->Delete(); - vtkImporter->Delete(); - imageStencilFilter->Delete(); - //vtkExporter->Delete(); // TODO: crashes when outcommented; memory leak?? - delete[] ptIds; + + // Clean up VTK objects + polyline->Delete(); + extrudeFilter->Delete(); + polyDataToImageStencil->Delete(); + vtkImporter->Delete(); + imageStencilFilter->Delete(); + //vtkExporter->Delete(); // TODO: crashes when outcommented; memory leak?? + delete[] ptIds; } void QmitkFiberProcessingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { - m_MultiWidget = &stdMultiWidget; + m_MultiWidget = &stdMultiWidget; } void QmitkFiberProcessingView::StdMultiWidgetNotAvailable() { - m_MultiWidget = NULL; + m_MultiWidget = NULL; } /* OnSelectionChanged is registered to SelectionService, therefore no need to implement SelectionService Listener explicitly */ void QmitkFiberProcessingView::UpdateGui() { - // are fiber bundles selected? - if ( m_SelectedFB.empty() ) - { - m_Controls->m_JoinBundles->setEnabled(false); - m_Controls->m_SubstractBundles->setEnabled(false); - m_Controls->m_ProcessFiberBundleButton->setEnabled(false); - m_Controls->doExtractFibersButton->setEnabled(false); - m_Controls->m_Extract3dButton->setEnabled(false); - m_Controls->m_ResampleFibersButton->setEnabled(false); - m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); - m_Controls->m_FaColorFibersButton->setEnabled(false); - } - else - { - m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); - m_Controls->m_ProcessFiberBundleButton->setEnabled(true); - m_Controls->m_ResampleFibersButton->setEnabled(true); - - if (m_Surfaces.size()>0) - m_Controls->m_Extract3dButton->setEnabled(true); - - // one bundle and one planar figure needed to extract fibers - if (!m_SelectedPF.empty()) - m_Controls->doExtractFibersButton->setEnabled(true); - - // more than two bundles needed to join/subtract - if (m_SelectedFB.size() > 1) + // are fiber bundles selected? + if ( m_SelectedFB.empty() ) { - m_Controls->m_JoinBundles->setEnabled(true); - m_Controls->m_SubstractBundles->setEnabled(true); + m_Controls->m_JoinBundles->setEnabled(false); + m_Controls->m_SubstractBundles->setEnabled(false); + m_Controls->m_ProcessFiberBundleButton->setEnabled(false); + m_Controls->doExtractFibersButton->setEnabled(false); + m_Controls->m_Extract3dButton->setEnabled(false); + m_Controls->m_ResampleFibersButton->setEnabled(false); + m_Controls->m_PlanarFigureButtonsFrame->setEnabled(false); + m_Controls->m_FaColorFibersButton->setEnabled(false); + m_Controls->m_PruneFibersButton->setEnabled(false); + + if (m_Surfaces.size()>0) + m_Controls->m_MirrorFibersButton->setEnabled(true); + else + m_Controls->m_MirrorFibersButton->setEnabled(false); } else { - m_Controls->m_JoinBundles->setEnabled(false); - m_Controls->m_SubstractBundles->setEnabled(false); - } + m_Controls->m_PlanarFigureButtonsFrame->setEnabled(true); + m_Controls->m_ProcessFiberBundleButton->setEnabled(true); + m_Controls->m_ResampleFibersButton->setEnabled(true); + m_Controls->m_PruneFibersButton->setEnabled(true); - if (m_SelectedImage.IsNotNull()) - m_Controls->m_FaColorFibersButton->setEnabled(true); - } - - // are planar figures selected? - if ( m_SelectedPF.empty() ) - { - m_Controls->doExtractFibersButton->setEnabled(false); - m_Controls->PFCompoANDButton->setEnabled(false); - m_Controls->PFCompoORButton->setEnabled(false); - m_Controls->PFCompoNOTButton->setEnabled(false); - m_Controls->m_GenerateRoiImage->setEnabled(false); - } - else - { - if ( !m_SelectedFB.empty() ) - m_Controls->m_GenerateRoiImage->setEnabled(true); - else - m_Controls->m_GenerateRoiImage->setEnabled(false); + if (m_Surfaces.size()>0) + { + m_Controls->m_Extract3dButton->setEnabled(true); + m_Controls->m_MirrorFibersButton->setEnabled(true); + } + + // one bundle and one planar figure needed to extract fibers + if (!m_SelectedPF.empty()) + m_Controls->doExtractFibersButton->setEnabled(true); + + // more than two bundles needed to join/subtract + if (m_SelectedFB.size() > 1) + { + m_Controls->m_JoinBundles->setEnabled(true); + m_Controls->m_SubstractBundles->setEnabled(true); + } + else + { + m_Controls->m_JoinBundles->setEnabled(false); + m_Controls->m_SubstractBundles->setEnabled(false); + } + + if (m_SelectedImage.IsNotNull()) + m_Controls->m_FaColorFibersButton->setEnabled(true); + } - if (m_SelectedPF.size() > 1) + // are planar figures selected? + if ( m_SelectedPF.empty() ) { - m_Controls->PFCompoANDButton->setEnabled(true); - m_Controls->PFCompoORButton->setEnabled(true); - m_Controls->PFCompoNOTButton->setEnabled(false); + m_Controls->doExtractFibersButton->setEnabled(false); + m_Controls->PFCompoANDButton->setEnabled(false); + m_Controls->PFCompoORButton->setEnabled(false); + m_Controls->PFCompoNOTButton->setEnabled(false); + m_Controls->m_GenerateRoiImage->setEnabled(false); } else { - m_Controls->PFCompoANDButton->setEnabled(false); - m_Controls->PFCompoORButton->setEnabled(false); - m_Controls->PFCompoNOTButton->setEnabled(true); + if ( !m_SelectedFB.empty() ) + m_Controls->m_GenerateRoiImage->setEnabled(true); + else + m_Controls->m_GenerateRoiImage->setEnabled(false); + + if (m_SelectedPF.size() > 1) + { + m_Controls->PFCompoANDButton->setEnabled(true); + m_Controls->PFCompoORButton->setEnabled(true); + m_Controls->PFCompoNOTButton->setEnabled(false); + } + else + { + m_Controls->PFCompoANDButton->setEnabled(false); + m_Controls->PFCompoORButton->setEnabled(false); + m_Controls->PFCompoNOTButton->setEnabled(true); + } } - } } void QmitkFiberProcessingView::OnSelectionChanged( std::vector nodes ) { - if ( !this->IsVisible() ) - return; - - //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection - m_SelectedFB.clear(); - m_SelectedPF.clear(); - m_Surfaces.clear(); - m_SelectedImage = NULL; - - for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) - { - mitk::DataNode::Pointer node = *it; - if ( dynamic_cast(node->GetData()) ) - m_SelectedFB.push_back(node); - else if (dynamic_cast(node->GetData())) - m_SelectedPF.push_back(node); - else if (dynamic_cast(node->GetData())) - m_SelectedImage = dynamic_cast(node->GetData()); - else if (dynamic_cast(node->GetData())) - m_Surfaces.push_back(dynamic_cast(node->GetData())); - } - UpdateGui(); - GenerateStats(); + if ( !this->IsVisible() ) + return; + + //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection + m_SelectedFB.clear(); + m_SelectedPF.clear(); + m_Surfaces.clear(); + m_SelectedImage = NULL; + + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) + { + mitk::DataNode::Pointer node = *it; + if ( dynamic_cast(node->GetData()) ) + m_SelectedFB.push_back(node); + else if (dynamic_cast(node->GetData())) + m_SelectedPF.push_back(node); + else if (dynamic_cast(node->GetData())) + m_SelectedImage = dynamic_cast(node->GetData()); + else if (dynamic_cast(node->GetData())) + m_Surfaces.push_back(dynamic_cast(node->GetData())); + } + UpdateGui(); + GenerateStats(); } void QmitkFiberProcessingView::OnDrawPolygon() { - // bool checked = m_Controls->m_PolygonButton->isChecked(); - // if(!this->AssertDrawingIsPossible(checked)) - // return; - - mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); - figure->ClosedOn(); - this->AddFigureToDataStorage(figure, QString("Polygon%1").arg(++m_PolygonCounter)); + // bool checked = m_Controls->m_PolygonButton->isChecked(); + // if(!this->AssertDrawingIsPossible(checked)) + // return; - MITK_INFO << "PlanarPolygon created ..."; + mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); + figure->ClosedOn(); + this->AddFigureToDataStorage(figure, QString("Polygon%1").arg(++m_PolygonCounter)); - mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); - mitk::DataNode* node = 0; - mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; - mitk::PlanarFigure* figureP = 0; + MITK_INFO << "PlanarPolygon created ..."; - for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() - ; it++) - { - node = const_cast(it->Value().GetPointer()); - figureP = dynamic_cast(node->GetData()); + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); + mitk::DataNode* node = 0; + mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; + mitk::PlanarFigure* figureP = 0; - if(figureP) + for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() + ; it++) { - figureInteractor = dynamic_cast(node->GetInteractor()); + node = const_cast(it->Value().GetPointer()); + figureP = dynamic_cast(node->GetData()); - if(figureInteractor.IsNull()) - figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); + if(figureP) + { + figureInteractor = dynamic_cast(node->GetInteractor()); + + if(figureInteractor.IsNull()) + figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); - mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); + mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); + } } - } } void QmitkFiberProcessingView::OnDrawCircle() { - //bool checked = m_Controls->m_CircleButton->isChecked(); - //if(!this->AssertDrawingIsPossible(checked)) - // return; - - mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); - this->AddFigureToDataStorage(figure, QString("Circle%1").arg(++m_EllipseCounter)); + //bool checked = m_Controls->m_CircleButton->isChecked(); + //if(!this->AssertDrawingIsPossible(checked)) + // return; - this->GetDataStorage()->Modified(); - MITK_INFO << "PlanarCircle created ..."; + mitk::PlanarCircle::Pointer figure = mitk::PlanarCircle::New(); + this->AddFigureToDataStorage(figure, QString("Circle%1").arg(++m_EllipseCounter)); - //call + this->GetDataStorage()->Modified(); + MITK_INFO << "PlanarCircle created ..."; - mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); - mitk::DataNode* node = 0; - mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; - mitk::PlanarFigure* figureP = 0; + //call - for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() - ; it++) - { - node = const_cast(it->Value().GetPointer()); - figureP = dynamic_cast(node->GetData()); + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); + mitk::DataNode* node = 0; + mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; + mitk::PlanarFigure* figureP = 0; - if(figureP) + for(mitk::DataStorage::SetOfObjects::ConstIterator it=_NodeSet->Begin(); it!=_NodeSet->End() + ; it++) { - figureInteractor = dynamic_cast(node->GetInteractor()); + node = const_cast(it->Value().GetPointer()); + figureP = dynamic_cast(node->GetData()); - if(figureInteractor.IsNull()) - figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); + if(figureP) + { + figureInteractor = dynamic_cast(node->GetInteractor()); + + if(figureInteractor.IsNull()) + figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); - mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); + mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); + } } - } } void QmitkFiberProcessingView::Activated() { - MITK_INFO << "FB OPerations ACTIVATED()"; - /* + MITK_INFO << "FB OPerations ACTIVATED()"; + /* mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDefaultDataStorage()->GetAll(); mitk::DataNode* node = 0; mitk::PlanarFigureInteractor::Pointer figureInteractor = 0; mitk::PlanarFigure* figure = 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) { figureInteractor = dynamic_cast(node->GetInteractor()); if(figureInteractor.IsNull()) figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", node); mitk::GlobalInteraction::GetInstance()->AddInteractor(figureInteractor); } } */ } void QmitkFiberProcessingView::AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, - const char *propertyKey, mitk::BaseProperty *property ) + const char *propertyKey, mitk::BaseProperty *property ) { - // initialize figure's geometry with empty geometry - mitk::PlaneGeometry::Pointer emptygeometry = mitk::PlaneGeometry::New(); - figure->SetGeometry2D( emptygeometry ); - - //set desired data to DataNode where Planarfigure is stored - mitk::DataNode::Pointer newNode = mitk::DataNode::New(); - newNode->SetName(name.toStdString()); - newNode->SetData(figure); - - newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,0.0,0.0)); - newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(2.0)); - newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true)); - - newNode->AddProperty( "selected", mitk::BoolProperty::New(true) ); - newNode->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); - newNode->AddProperty( "planarfigure.drawoutline", mitk::BoolProperty::New(true) ); - newNode->AddProperty( "planarfigure.drawquantities", mitk::BoolProperty::New(false) ); - newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true) ); - - newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(3.0) ); - newNode->AddProperty( "planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.outline.width", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.helperline.width", mitk::FloatProperty::New(2.0) ); - - newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); - newNode->AddProperty( "planarfigure.default.line.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.default.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.default.outline.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.default.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.default.helperline.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.default.markerline.color", mitk::ColorProperty::New(0.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.default.markerline.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.default.marker.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); - newNode->AddProperty( "planarfigure.default.marker.opacity",mitk::FloatProperty::New(2.0) ); - - newNode->AddProperty( "planarfigure.hover.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.hover.line.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.hover.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.hover.outline.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.hover.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.hover.helperline.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.hover.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.hover.markerline.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.hover.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.hover.marker.opacity", mitk::FloatProperty::New(2.0) ); - - newNode->AddProperty( "planarfigure.selected.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.selected.line.opacity",mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.selected.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.selected.outline.opacity", mitk::FloatProperty::New(2.0)); - newNode->AddProperty( "planarfigure.selected.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.selected.helperline.opacity",mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.selected.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.selected.markerline.opacity", mitk::FloatProperty::New(2.0) ); - newNode->AddProperty( "planarfigure.selected.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); - newNode->AddProperty( "planarfigure.selected.marker.opacity",mitk::FloatProperty::New(2.0)); - - // figure drawn on the topmost layer / image - newNode->SetColor(1.0,1.0,1.0); - newNode->SetOpacity(0.8); - GetDataStorage()->Add(newNode ); - - std::vector selectedNodes = GetDataManagerSelection(); - for(unsigned int i = 0; i < selectedNodes.size(); i++) - { - selectedNodes[i]->SetSelected(false); - } - - newNode->SetSelected(true); + // initialize figure's geometry with empty geometry + mitk::PlaneGeometry::Pointer emptygeometry = mitk::PlaneGeometry::New(); + figure->SetGeometry2D( emptygeometry ); + + //set desired data to DataNode where Planarfigure is stored + mitk::DataNode::Pointer newNode = mitk::DataNode::New(); + newNode->SetName(name.toStdString()); + newNode->SetData(figure); + + newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,0.0,0.0)); + newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(2.0)); + newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true)); + + newNode->AddProperty( "selected", mitk::BoolProperty::New(true) ); + newNode->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); + newNode->AddProperty( "planarfigure.drawoutline", mitk::BoolProperty::New(true) ); + newNode->AddProperty( "planarfigure.drawquantities", mitk::BoolProperty::New(false) ); + newNode->AddProperty( "planarfigure.drawshadow", mitk::BoolProperty::New(true) ); + + newNode->AddProperty( "planarfigure.line.width", mitk::FloatProperty::New(3.0) ); + newNode->AddProperty( "planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.outline.width", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.helperline.width", mitk::FloatProperty::New(2.0) ); + + newNode->AddProperty( "planarfigure.default.line.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); + newNode->AddProperty( "planarfigure.default.line.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.default.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.default.outline.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.default.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.default.helperline.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.default.markerline.color", mitk::ColorProperty::New(0.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.default.markerline.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.default.marker.color", mitk::ColorProperty::New(1.0,1.0,1.0) ); + newNode->AddProperty( "planarfigure.default.marker.opacity",mitk::FloatProperty::New(2.0) ); + + newNode->AddProperty( "planarfigure.hover.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.hover.line.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.hover.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.hover.outline.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.hover.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.hover.helperline.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.hover.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.hover.markerline.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.hover.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.hover.marker.opacity", mitk::FloatProperty::New(2.0) ); + + newNode->AddProperty( "planarfigure.selected.line.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.selected.line.opacity",mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.selected.outline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.selected.outline.opacity", mitk::FloatProperty::New(2.0)); + newNode->AddProperty( "planarfigure.selected.helperline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.selected.helperline.opacity",mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.selected.markerline.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.selected.markerline.opacity", mitk::FloatProperty::New(2.0) ); + newNode->AddProperty( "planarfigure.selected.marker.color", mitk::ColorProperty::New(1.0,0.0,0.0) ); + newNode->AddProperty( "planarfigure.selected.marker.opacity",mitk::FloatProperty::New(2.0)); + + // figure drawn on the topmost layer / image + newNode->SetColor(1.0,1.0,1.0); + newNode->SetOpacity(0.8); + GetDataStorage()->Add(newNode ); + + std::vector selectedNodes = GetDataManagerSelection(); + for(unsigned int i = 0; i < selectedNodes.size(); i++) + { + selectedNodes[i]->SetSelected(false); + } + + newNode->SetSelected(true); } void QmitkFiberProcessingView::DoFiberExtraction() { - if ( m_SelectedFB.empty() ){ - QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); - MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; - return; - } - - for (int i=0; i(m_SelectedFB.at(i)->GetData()); - mitk::PlanarFigure::Pointer roi = dynamic_cast (m_SelectedPF.at(0)->GetData()); - - mitk::FiberBundleX::Pointer extFB = fib->ExtractFiberSubset(roi); - - if (extFB->GetNumFibers()<=0) - continue; - - mitk::DataNode::Pointer node; - node = mitk::DataNode::New(); - node->SetData(extFB); - QString name(m_SelectedFB.at(i)->GetName().c_str()); - name += "_"; - name += m_SelectedPF.at(0)->GetName().c_str(); - node->SetName(name.toStdString()); - GetDataStorage()->Add(node); - m_SelectedFB.at(i)->SetVisibility(false); - } + if ( m_SelectedFB.empty() ){ + QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); + MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; + return; + } + + for (int i=0; i(m_SelectedFB.at(i)->GetData()); + mitk::PlanarFigure::Pointer roi = dynamic_cast (m_SelectedPF.at(0)->GetData()); + + mitk::FiberBundleX::Pointer extFB = fib->ExtractFiberSubset(roi); + + if (extFB->GetNumFibers()<=0) + continue; + + mitk::DataNode::Pointer node; + node = mitk::DataNode::New(); + node->SetData(extFB); + QString name(m_SelectedFB.at(i)->GetName().c_str()); + name += "_"; + name += m_SelectedPF.at(0)->GetName().c_str(); + node->SetName(name.toStdString()); + GetDataStorage()->Add(node); + m_SelectedFB.at(i)->SetVisibility(false); + } } void QmitkFiberProcessingView::GenerateAndComposite() { - mitk::PlanarFigureComposite::Pointer PFCAnd = mitk::PlanarFigureComposite::New(); - - mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); - PFCAnd->SetGeometry2D(currentGeometry2D); - PFCAnd->setOperationType(mitk::PFCOMPOSITION_AND_OPERATION); - - for( std::vector::iterator it = m_SelectedPF.begin(); - it != m_SelectedPF.end(); ++it ) - { - mitk::DataNode::Pointer nodePF = *it; - mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); - PFCAnd->addPlanarFigure( tmpPF ); - PFCAnd->addDataNode( nodePF ); - PFCAnd->setDisplayName("AND_COMPO"); - } - - AddCompositeToDatastorage(PFCAnd, NULL); + mitk::PlanarFigureComposite::Pointer PFCAnd = mitk::PlanarFigureComposite::New(); + + mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); + PFCAnd->SetGeometry2D(currentGeometry2D); + PFCAnd->setOperationType(mitk::PFCOMPOSITION_AND_OPERATION); + + for( std::vector::iterator it = m_SelectedPF.begin(); + it != m_SelectedPF.end(); ++it ) + { + mitk::DataNode::Pointer nodePF = *it; + mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); + PFCAnd->addPlanarFigure( tmpPF ); + PFCAnd->addDataNode( nodePF ); + PFCAnd->setDisplayName("AND_COMPO"); + } + + AddCompositeToDatastorage(PFCAnd, NULL); } void QmitkFiberProcessingView::GenerateOrComposite() { - mitk::PlanarFigureComposite::Pointer PFCOr = mitk::PlanarFigureComposite::New(); - mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); - PFCOr->SetGeometry2D(currentGeometry2D); - PFCOr->setOperationType(mitk::PFCOMPOSITION_OR_OPERATION); - - for( std::vector::iterator it = m_SelectedPF.begin(); - it != m_SelectedPF.end(); ++it ) - { - mitk::DataNode::Pointer nodePF = *it; - mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); - PFCOr->addPlanarFigure( tmpPF ); - PFCOr->addDataNode( nodePF ); - PFCOr->setDisplayName("OR_COMPO"); - } - - AddCompositeToDatastorage(PFCOr, NULL); + mitk::PlanarFigureComposite::Pointer PFCOr = mitk::PlanarFigureComposite::New(); + mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); + PFCOr->SetGeometry2D(currentGeometry2D); + PFCOr->setOperationType(mitk::PFCOMPOSITION_OR_OPERATION); + + for( std::vector::iterator it = m_SelectedPF.begin(); + it != m_SelectedPF.end(); ++it ) + { + mitk::DataNode::Pointer nodePF = *it; + mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); + PFCOr->addPlanarFigure( tmpPF ); + PFCOr->addDataNode( nodePF ); + PFCOr->setDisplayName("OR_COMPO"); + } + + AddCompositeToDatastorage(PFCOr, NULL); } void QmitkFiberProcessingView::GenerateNotComposite() { - mitk::PlanarFigureComposite::Pointer PFCNot = mitk::PlanarFigureComposite::New(); - mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); - PFCNot->SetGeometry2D(currentGeometry2D); - PFCNot->setOperationType(mitk::PFCOMPOSITION_NOT_OPERATION); - - for( std::vector::iterator it = m_SelectedPF.begin(); - it != m_SelectedPF.end(); ++it ) - { - mitk::DataNode::Pointer nodePF = *it; - mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); - PFCNot->addPlanarFigure( tmpPF ); - PFCNot->addDataNode( nodePF ); - PFCNot->setDisplayName("NOT_COMPO"); - } - - AddCompositeToDatastorage(PFCNot, NULL); + mitk::PlanarFigureComposite::Pointer PFCNot = mitk::PlanarFigureComposite::New(); + mitk::PlaneGeometry* currentGeometry2D = dynamic_cast( const_cast(GetActiveStdMultiWidget()->GetRenderWindow1()->GetRenderer()->GetCurrentWorldGeometry2D())); + PFCNot->SetGeometry2D(currentGeometry2D); + PFCNot->setOperationType(mitk::PFCOMPOSITION_NOT_OPERATION); + + for( std::vector::iterator it = m_SelectedPF.begin(); + it != m_SelectedPF.end(); ++it ) + { + mitk::DataNode::Pointer nodePF = *it; + mitk::PlanarFigure::Pointer tmpPF = dynamic_cast( nodePF->GetData() ); + PFCNot->addPlanarFigure( tmpPF ); + PFCNot->addDataNode( nodePF ); + PFCNot->setDisplayName("NOT_COMPO"); + } + + AddCompositeToDatastorage(PFCNot, NULL); } /* CLEANUP NEEDED */ void QmitkFiberProcessingView::AddCompositeToDatastorage(mitk::PlanarFigureComposite::Pointer pfcomp, mitk::DataNode::Pointer parentDataNode ) { - mitk::DataNode::Pointer newPFCNode; - newPFCNode = mitk::DataNode::New(); - newPFCNode->SetName( pfcomp->getDisplayName() ); - newPFCNode->SetData(pfcomp); - newPFCNode->SetVisibility(true); + mitk::DataNode::Pointer newPFCNode; + newPFCNode = mitk::DataNode::New(); + newPFCNode->SetName( pfcomp->getDisplayName() ); + newPFCNode->SetData(pfcomp); + newPFCNode->SetVisibility(true); - switch (pfcomp->getOperationType()) { + switch (pfcomp->getOperationType()) { case 0: { - if (!parentDataNode.IsNull()) { - GetDataStorage()->Add(newPFCNode, parentDataNode); + if (!parentDataNode.IsNull()) { + GetDataStorage()->Add(newPFCNode, parentDataNode); - } else { - GetDataStorage()->Add(newPFCNode); - } - - //iterate through its childs - for(int i=0; igetNumberOfChildren(); ++i) - { - mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); - mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); - - mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); - if ( !pfcompcast.IsNull() ) - { - // child is of type planar Figure composite - // make new node of the child, cuz later the child has to be removed of its old position in datamanager - // feed new dataNode with information of the savedDataNode, which is gonna be removed soon - mitk::DataNode::Pointer newChildPFCNode; - newChildPFCNode = mitk::DataNode::New(); - newChildPFCNode->SetData(tmpPFchild); - newChildPFCNode->SetName( savedPFchildNode->GetName() ); - pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user - - //update inside vector the dataNodePointer - pfcomp->replaceDataNodeAt(i, newChildPFCNode); - - AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent - - // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager - // without having its parent anymore - //GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; - - }else{ - MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); - } - // remove old child position in dataStorage - GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " still exists"; - } + } else { + GetDataStorage()->Add(newPFCNode); } - else + + //iterate through its childs + for(int i=0; igetNumberOfChildren(); ++i) { - // child is not of type PlanarFigureComposite, so its one of the planarFigures - // create new dataNode containing the data of the old dataNode, but position in dataManager will be - // modified cuz we re setting a (new) parent. - mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); - newPFchildNode->SetName(savedPFchildNode->GetName() ); - newPFchildNode->SetData(tmpPFchild); - newPFchildNode->SetVisibility(true); - - // replace the dataNode in PFComp DataNodeVector - pfcomp->replaceDataNodeAt(i, newPFchildNode); - - - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; - - } - else - { - MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); - } - // remove old child position in dataStorage - GetDataStorage()->Remove(savedPFchildNode); - - if ( GetDataStorage()->Exists(savedPFchildNode)) - { - MITK_INFO << savedPFchildNode->GetName() << " still exists"; - } - - MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); - //add new child to datamanager with its new position as child of newPFCNode parent - GetDataStorage()->Add(newPFchildNode, newPFCNode); + mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); + mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); + + mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); + if ( !pfcompcast.IsNull() ) + { + // child is of type planar Figure composite + // make new node of the child, cuz later the child has to be removed of its old position in datamanager + // feed new dataNode with information of the savedDataNode, which is gonna be removed soon + mitk::DataNode::Pointer newChildPFCNode; + newChildPFCNode = mitk::DataNode::New(); + newChildPFCNode->SetData(tmpPFchild); + newChildPFCNode->SetName( savedPFchildNode->GetName() ); + pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user + + //update inside vector the dataNodePointer + pfcomp->replaceDataNodeAt(i, newChildPFCNode); + + AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent + + // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager + // without having its parent anymore + //GetDataStorage()->Remove(savedPFchildNode); + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; + + }else{ + MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); + } + // remove old child position in dataStorage + GetDataStorage()->Remove(savedPFchildNode); + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " still exists"; + } + } + else + { + // child is not of type PlanarFigureComposite, so its one of the planarFigures + // create new dataNode containing the data of the old dataNode, but position in dataManager will be + // modified cuz we re setting a (new) parent. + mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); + newPFchildNode->SetName(savedPFchildNode->GetName() ); + newPFchildNode->SetData(tmpPFchild); + newPFchildNode->SetVisibility(true); + + // replace the dataNode in PFComp DataNodeVector + pfcomp->replaceDataNodeAt(i, newPFchildNode); + + + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; + + } + else + { + MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); + } + // remove old child position in dataStorage + GetDataStorage()->Remove(savedPFchildNode); + + if ( GetDataStorage()->Exists(savedPFchildNode)) + { + MITK_INFO << savedPFchildNode->GetName() << " still exists"; + } + + MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); + //add new child to datamanager with its new position as child of newPFCNode parent + GetDataStorage()->Add(newPFchildNode, newPFCNode); + } } - } - GetDataStorage()->Modified(); - break; + GetDataStorage()->Modified(); + break; } case 1: { - if (!parentDataNode.IsNull()) { - MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; - GetDataStorage()->Add(newPFCNode, parentDataNode); + if (!parentDataNode.IsNull()) { + MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; + GetDataStorage()->Add(newPFCNode, parentDataNode); - } else { - MITK_INFO << "adding " << newPFCNode->GetName(); - GetDataStorage()->Add(newPFCNode); + } else { + MITK_INFO << "adding " << newPFCNode->GetName(); + GetDataStorage()->Add(newPFCNode); - } + } - for(int i=0; igetNumberOfChildren(); ++i) - { - mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); - mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); + for(int i=0; igetNumberOfChildren(); ++i) + { + mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); + mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); - mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); - if ( !pfcompcast.IsNull() ) - { // child is of type planar Figure composite - // make new node of the child, cuz later the child has to be removed of its old position in datamanager - // feed new dataNode with information of the savedDataNode, which is gonna be removed soon - mitk::DataNode::Pointer newChildPFCNode; - newChildPFCNode = mitk::DataNode::New(); - newChildPFCNode->SetData(tmpPFchild); - newChildPFCNode->SetName( savedPFchildNode->GetName() ); - pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user + mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); + if ( !pfcompcast.IsNull() ) + { // child is of type planar Figure composite + // make new node of the child, cuz later the child has to be removed of its old position in datamanager + // feed new dataNode with information of the savedDataNode, which is gonna be removed soon + mitk::DataNode::Pointer newChildPFCNode; + newChildPFCNode = mitk::DataNode::New(); + newChildPFCNode->SetData(tmpPFchild); + newChildPFCNode->SetName( savedPFchildNode->GetName() ); + pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user - //update inside vector the dataNodePointer - pfcomp->replaceDataNodeAt(i, newChildPFCNode); + //update inside vector the dataNodePointer + pfcomp->replaceDataNodeAt(i, newChildPFCNode); - AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent + AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent - // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager - // without having its parent anymore - //GetDataStorage()->Remove(savedPFchildNode); + // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager + // without having its parent anymore + //GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; - }else{ - MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); + }else{ + MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); - } - // remove old child position in dataStorage - GetDataStorage()->Remove(savedPFchildNode); + } + // remove old child position in dataStorage + GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " still exists"; - } - } else { + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " still exists"; + } + } else { - // child is not of type PlanarFigureComposite, so its one of the planarFigures - // create new dataNode containing the data of the old dataNode, but position in dataManager will be - // modified cuz we re setting a (new) parent. - mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); - newPFchildNode->SetName(savedPFchildNode->GetName() ); - newPFchildNode->SetData(tmpPFchild); - newPFchildNode->SetVisibility(true); + // child is not of type PlanarFigureComposite, so its one of the planarFigures + // create new dataNode containing the data of the old dataNode, but position in dataManager will be + // modified cuz we re setting a (new) parent. + mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); + newPFchildNode->SetName(savedPFchildNode->GetName() ); + newPFchildNode->SetData(tmpPFchild); + newPFchildNode->SetVisibility(true); - // replace the dataNode in PFComp DataNodeVector - pfcomp->replaceDataNodeAt(i, newPFchildNode); + // replace the dataNode in PFComp DataNodeVector + pfcomp->replaceDataNodeAt(i, newPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; - }else{ - MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); + }else{ + MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); - } - // remove old child position in dataStorage - GetDataStorage()->Remove(savedPFchildNode); + } + // remove old child position in dataStorage + GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " still exists"; - } + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " still exists"; + } - MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); - //add new child to datamanager with its new position as child of newPFCNode parent - GetDataStorage()->Add(newPFchildNode, newPFCNode); + MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); + //add new child to datamanager with its new position as child of newPFCNode parent + GetDataStorage()->Add(newPFchildNode, newPFCNode); + } } - } - GetDataStorage()->Modified(); - break; + GetDataStorage()->Modified(); + break; } case 2: { - if (!parentDataNode.IsNull()) { - MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; - GetDataStorage()->Add(newPFCNode, parentDataNode); - } - else - { - MITK_INFO << "adding " << newPFCNode->GetName(); - GetDataStorage()->Add(newPFCNode); - } + if (!parentDataNode.IsNull()) { + MITK_INFO << "adding " << newPFCNode->GetName() << " to " << parentDataNode->GetName() ; + GetDataStorage()->Add(newPFCNode, parentDataNode); + } + else + { + MITK_INFO << "adding " << newPFCNode->GetName(); + GetDataStorage()->Add(newPFCNode); + } - //iterate through its childs + //iterate through its childs - for(int i=0; igetNumberOfChildren(); ++i) - { - mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); - mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); + for(int i=0; igetNumberOfChildren(); ++i) + { + mitk::PlanarFigure::Pointer tmpPFchild = pfcomp->getChildAt(i); + mitk::DataNode::Pointer savedPFchildNode = pfcomp->getDataNodeAt(i); - mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); - if ( !pfcompcast.IsNull() ) - { // child is of type planar Figure composite - // makeRemoveBundle new node of the child, cuz later the child has to be removed of its old position in datamanager - // feed new dataNode with information of the savedDataNode, which is gonna be removed soon - mitk::DataNode::Pointer newChildPFCNode; - newChildPFCNode = mitk::DataNode::New(); - newChildPFCNode->SetData(tmpPFchild); - newChildPFCNode->SetName( savedPFchildNode->GetName() ); - pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user + mitk::PlanarFigureComposite::Pointer pfcompcast= dynamic_cast(tmpPFchild.GetPointer()); + if ( !pfcompcast.IsNull() ) + { // child is of type planar Figure composite + // makeRemoveBundle new node of the child, cuz later the child has to be removed of its old position in datamanager + // feed new dataNode with information of the savedDataNode, which is gonna be removed soon + mitk::DataNode::Pointer newChildPFCNode; + newChildPFCNode = mitk::DataNode::New(); + newChildPFCNode->SetData(tmpPFchild); + newChildPFCNode->SetName( savedPFchildNode->GetName() ); + pfcompcast->setDisplayName( savedPFchildNode->GetName() ); //name might be changed in DataManager by user - //update inside vector the dataNodePointer - pfcomp->replaceDataNodeAt(i, newChildPFCNode); + //update inside vector the dataNodePointer + pfcomp->replaceDataNodeAt(i, newChildPFCNode); - AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent + AddCompositeToDatastorage(pfcompcast, newPFCNode); //the current PFCNode becomes the childs parent - // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager - // without having its parent anymore - //GetDataStorage()->Remove(savedPFchildNode); + // remove savedNode here, cuz otherwise its children will change their position in the dataNodeManager + // without having its parent anymore + //GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; - }else{ - MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); - } - // remove old child position in dataStorage - GetDataStorage()->Remove(savedPFchildNode); + }else{ + MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); + } + // remove old child position in dataStorage + GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " still exists"; - } + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " still exists"; + } - } else { + } else { - // child is not of type PlanarFigureComposite, so its one of the planarFigures - // create new dataNode containing the data of the old dataNode, but position in dataManager will be - // modified cuz we re setting a (new) parent. - mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); - newPFchildNode->SetName(savedPFchildNode->GetName() ); - newPFchildNode->SetData(tmpPFchild); - newPFchildNode->SetVisibility(true); + // child is not of type PlanarFigureComposite, so its one of the planarFigures + // create new dataNode containing the data of the old dataNode, but position in dataManager will be + // modified cuz we re setting a (new) parent. + mitk::DataNode::Pointer newPFchildNode = mitk::DataNode::New(); + newPFchildNode->SetName(savedPFchildNode->GetName() ); + newPFchildNode->SetData(tmpPFchild); + newPFchildNode->SetVisibility(true); - // replace the dataNode in PFComp DataNodeVector - pfcomp->replaceDataNodeAt(i, newPFchildNode); + // replace the dataNode in PFComp DataNodeVector + pfcomp->replaceDataNodeAt(i, newPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " exists in DS...trying to remove it"; - }else{ - MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); + }else{ + MITK_INFO << "[ERROR] does NOT exist, but can I read its Name? " << savedPFchildNode->GetName(); - } - // remove old child position in dataStorage - GetDataStorage()->Remove(savedPFchildNode); + } + // remove old child position in dataStorage + GetDataStorage()->Remove(savedPFchildNode); - if ( GetDataStorage()->Exists(savedPFchildNode)) { - MITK_INFO << savedPFchildNode->GetName() << " still exists"; - } + if ( GetDataStorage()->Exists(savedPFchildNode)) { + MITK_INFO << savedPFchildNode->GetName() << " still exists"; + } - MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); - //add new child to datamanager with its new position as child of newPFCNode parent - GetDataStorage()->Add(newPFchildNode, newPFCNode); + MITK_INFO << "adding " << newPFchildNode->GetName() << " to " << newPFCNode->GetName(); + //add new child to datamanager with its new position as child of newPFCNode parent + GetDataStorage()->Add(newPFchildNode, newPFCNode); + } } - } - GetDataStorage()->Modified(); - break; + GetDataStorage()->Modified(); + break; } default: - MITK_INFO << "we have an UNDEFINED composition... ERROR" ; - break; - } + MITK_INFO << "we have an UNDEFINED composition... ERROR" ; + break; + } } void QmitkFiberProcessingView::JoinBundles() { - if ( m_SelectedFB.size()<2 ){ - QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); - MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; - return; - } - - std::vector::const_iterator it = m_SelectedFB.begin(); - mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); - QString name(""); - name += QString((*it)->GetName().c_str()); - ++it; - for (it; it!=m_SelectedFB.end(); ++it) - { - newBundle = newBundle->AddBundle(dynamic_cast((*it)->GetData())); - name += "+"+QString((*it)->GetName().c_str()); - } - - mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); - fbNode->SetData(newBundle); - fbNode->SetName(name.toStdString()); - fbNode->SetVisibility(true); - GetDataStorage()->Add(fbNode); + if ( m_SelectedFB.size()<2 ){ + QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); + MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; + return; + } + + std::vector::const_iterator it = m_SelectedFB.begin(); + mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); + QString name(""); + name += QString((*it)->GetName().c_str()); + ++it; + for (it; it!=m_SelectedFB.end(); ++it) + { + newBundle = newBundle->AddBundle(dynamic_cast((*it)->GetData())); + name += "+"+QString((*it)->GetName().c_str()); + } + + mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); + fbNode->SetData(newBundle); + fbNode->SetName(name.toStdString()); + fbNode->SetVisibility(true); + GetDataStorage()->Add(fbNode); } void QmitkFiberProcessingView::SubstractBundles() { - if ( m_SelectedFB.size()<2 ){ - QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); - MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; - return; - } - - std::vector::const_iterator it = m_SelectedFB.begin(); - mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); - QString name(""); - name += QString((*it)->GetName().c_str()); - ++it; - for (it; it!=m_SelectedFB.end(); ++it) - { - newBundle = newBundle->SubtractBundle(dynamic_cast((*it)->GetData())); + if ( m_SelectedFB.size()<2 ){ + QMessageBox::information( NULL, "Warning", "Select at least two fiber bundles!"); + MITK_WARN("QmitkFiberProcessingView") << "Select at least two fiber bundles!"; + return; + } + + std::vector::const_iterator it = m_SelectedFB.begin(); + mitk::FiberBundleX::Pointer newBundle = dynamic_cast((*it)->GetData()); + QString name(""); + name += QString((*it)->GetName().c_str()); + ++it; + for (it; it!=m_SelectedFB.end(); ++it) + { + newBundle = newBundle->SubtractBundle(dynamic_cast((*it)->GetData())); + if (newBundle.IsNull()) + break; + name += "-"+QString((*it)->GetName().c_str()); + } if (newBundle.IsNull()) - break; - name += "-"+QString((*it)->GetName().c_str()); - } - if (newBundle.IsNull()) - { - QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers. Did you select the fiber bundles in the correct order? X-Y is not equal to Y-X!"); - return; - } - - mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); - fbNode->SetData(newBundle); - fbNode->SetName(name.toStdString()); - fbNode->SetVisibility(true); - GetDataStorage()->Add(fbNode); + { + QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers. Did you select the fiber bundles in the correct order? X-Y is not equal to Y-X!"); + return; + } + + mitk::DataNode::Pointer fbNode = mitk::DataNode::New(); + fbNode->SetData(newBundle); + fbNode->SetName(name.toStdString()); + fbNode->SetVisibility(true); + GetDataStorage()->Add(fbNode); } -void QmitkFiberProcessingView::GenerateStats() +void QmitkFiberProcessingView::PruneBundle() { - if ( m_SelectedFB.empty() ) - return; - - QString stats(""); - - for( int i=0; i(node->GetData())) + int minLength = this->m_Controls->m_PruneFibersSpinBox->value(); + bool doneSomething = false; + for (int i=0; i0) - stats += "\n-----------------------------\n"; - stats += QString(node->GetName().c_str()) + "\n"; - mitk::FiberBundleX::Pointer fib = dynamic_cast(node->GetData()); - vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); - vtkSmartPointer vLines = fiberPolyData->GetLines(); - vLines->InitTraversal(); - int numberOfLines = vLines->GetNumberOfCells(); - - stats += "Number of fibers: "+ QString::number(numberOfLines) + "\n"; - - float length = 0; - std::vector lengths; - for (int i=0; iGetNextCell ( numPoints, points ); - - float l=0; - for (unsigned int j=0; j p1; - itk::Point p2; - fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); - fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); - - float dist = p1.EuclideanDistanceTo(p2); - length += dist; - l += dist; - } - itk::Point p2; - fiberPolyData->GetPoint(points[numPoints-1], p2.GetDataPointer()); - - lengths.push_back(l); - } + mitk::FiberBundleX::Pointer fib = dynamic_cast(m_SelectedFB.at(i)->GetData()); + if (!fib->RemoveShortFibers(minLength)) + QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers."); + else + doneSomething = true; + } - std::sort(lengths.begin(), lengths.end()); + if (doneSomething) + { + GenerateStats(); + RenderingManager::GetInstance()->RequestUpdateAll(); + } +} - if (numberOfLines>0) - length /= numberOfLines; +void QmitkFiberProcessingView::GenerateStats() +{ + if ( m_SelectedFB.empty() ) + return; - float dev=0; - int count = 0; - vLines->InitTraversal(); - for (int i=0; iGetNextCell ( numPoints, points ); + QString stats(""); - float l=0; - for (unsigned int j=0; j(node->GetData())) { - itk::Point p1; - itk::Point p2; - fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); - fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); - - float dist = p1.EuclideanDistanceTo(p2); - l += dist; + if (i>0) + stats += "\n-----------------------------\n"; + stats += QString(node->GetName().c_str()) + "\n"; + mitk::FiberBundleX::Pointer fib = dynamic_cast(node->GetData()); + vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); + vtkSmartPointer vLines = fiberPolyData->GetLines(); + vLines->InitTraversal(); + int numberOfLines = vLines->GetNumberOfCells(); + + stats += "Number of fibers: "+ QString::number(numberOfLines) + "\n"; + + float length = 0; + std::vector lengths; + for (int i=0; iGetNextCell ( numPoints, points ); + + float l=0; + for (unsigned int j=0; j p1; + itk::Point p2; + fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); + fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); + + float dist = p1.EuclideanDistanceTo(p2); + length += dist; + l += dist; + } + itk::Point p2; + fiberPolyData->GetPoint(points[numPoints-1], p2.GetDataPointer()); + + lengths.push_back(l); + } + + std::sort(lengths.begin(), lengths.end()); + + if (numberOfLines>0) + length /= numberOfLines; + + float dev=0; + int count = 0; + vLines->InitTraversal(); + for (int i=0; iGetNextCell ( numPoints, points ); + + float l=0; + for (unsigned int j=0; j p1; + itk::Point p2; + fiberPolyData->GetPoint(points[j], p1.GetDataPointer()); + fiberPolyData->GetPoint(points[j+1], p2.GetDataPointer()); + + float dist = p1.EuclideanDistanceTo(p2); + l += dist; + } + dev += (length-l)*(length-l); + count++; + } + + if (numberOfLines>1) + dev /= (numberOfLines-1); + else + dev = 0; + + stats += "Min. length: "+ QString::number(lengths.front(),'f',1) + " mm\n"; + stats += "Max. length: "+ QString::number(lengths.back(),'f',1) + " mm\n"; + stats += "Mean length: "+ QString::number(length,'f',1) + " mm\n"; + stats += "Median length: "+ QString::number(lengths.at(lengths.size()/2),'f',1) + " mm\n"; + stats += "Standard deviation: "+ QString::number(sqrt(dev),'f',1) + " mm\n"; } - dev += (length-l)*(length-l); - count++; - } - - if (numberOfLines>1) - dev /= (numberOfLines-1); - else - dev = 0; - - stats += "Min. length: "+ QString::number(lengths.front(),'f',1) + " mm\n"; - stats += "Max. length: "+ QString::number(lengths.back(),'f',1) + " mm\n"; - stats += "Mean length: "+ QString::number(length,'f',1) + " mm\n"; - stats += "Median length: "+ QString::number(lengths.at(lengths.size()/2),'f',1) + " mm\n"; - stats += "Standard deviation: "+ QString::number(sqrt(dev),'f',1) + " mm\n"; } - } - this->m_Controls->m_StatsTextEdit->setText(stats); + this->m_Controls->m_StatsTextEdit->setText(stats); } void QmitkFiberProcessingView::ProcessSelectedBundles() { - if ( m_SelectedFB.empty() ){ - QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); - MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; - return; - } - - int generationMethod = m_Controls->m_GenerationBox->currentIndex(); - - for( int i=0; i(node->GetData())) + if ( m_SelectedFB.empty() ){ + QMessageBox::information( NULL, "Warning", "No fibe bundle selected!"); + MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected"; + return; + } + + int generationMethod = m_Controls->m_GenerationBox->currentIndex(); + + for( int i=0; i(node->GetData()); - QString name(node->GetName().c_str()); - DataNode::Pointer newNode = NULL; - switch(generationMethod){ - case 0: - newNode = GenerateTractDensityImage(fib, false); - name += "_TDI"; - break; - case 1: - newNode = GenerateTractDensityImage(fib, true); - name += "_envelope"; - break; - case 2: - newNode = GenerateColorHeatmap(fib); - break; - case 3: - newNode = GenerateFiberEndingsImage(fib); - name += "_fiber_endings"; - break; - case 4: - newNode = GenerateFiberEndingsPointSet(fib); - name += "_fiber_endings"; - break; - } - if (newNode.IsNotNull()) - { - newNode->SetName(name.toStdString()); - GetDataStorage()->Add(newNode); - } + mitk::DataNode::Pointer node = m_SelectedFB[i]; + if (node.IsNotNull() && dynamic_cast(node->GetData())) + { + mitk::FiberBundleX::Pointer fib = dynamic_cast(node->GetData()); + QString name(node->GetName().c_str()); + DataNode::Pointer newNode = NULL; + switch(generationMethod){ + case 0: + newNode = GenerateTractDensityImage(fib, false, true); + name += "_TDI"; + break; + case 1: + newNode = GenerateTractDensityImage(fib, false, false); + name += "_TDI"; + break; + case 2: + newNode = GenerateTractDensityImage(fib, true, false); + name += "_envelope"; + break; + case 3: + newNode = GenerateColorHeatmap(fib); + break; + case 4: + newNode = GenerateFiberEndingsImage(fib); + name += "_fiber_endings"; + break; + case 5: + newNode = GenerateFiberEndingsPointSet(fib); + name += "_fiber_endings"; + break; + } + if (newNode.IsNotNull()) + { + newNode->SetName(name.toStdString()); + GetDataStorage()->Add(newNode); + } + } } - } } // generate pointset displaying the fiber endings mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsPointSet(mitk::FiberBundleX::Pointer fib) { - mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); - vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); - vtkSmartPointer vLines = fiberPolyData->GetLines(); - vLines->InitTraversal(); - - int count = 0; - int numFibers = fib->GetNumFibers(); - for( int i=0; iGetNextCell ( numPoints, points ); - - if (numPoints>0) + mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); + vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); + vtkSmartPointer vLines = fiberPolyData->GetLines(); + vLines->InitTraversal(); + + int count = 0; + int numFibers = fib->GetNumFibers(); + for( int i=0; iGetPoint(points[0]); - itk::Point itkPoint; - itkPoint[0] = point[0]; - itkPoint[1] = point[1]; - itkPoint[2] = point[2]; - pointSet->InsertPoint(count, itkPoint); - count++; - } - if (numPoints>2) - { - double* point = fiberPolyData->GetPoint(points[numPoints-1]); - itk::Point itkPoint; - itkPoint[0] = point[0]; - itkPoint[1] = point[1]; - itkPoint[2] = point[2]; - pointSet->InsertPoint(count, itkPoint); - count++; + vtkIdType numPoints(0); + vtkIdType* points(NULL); + vLines->GetNextCell ( numPoints, points ); + + if (numPoints>0) + { + double* point = fiberPolyData->GetPoint(points[0]); + itk::Point itkPoint; + itkPoint[0] = point[0]; + itkPoint[1] = point[1]; + itkPoint[2] = point[2]; + pointSet->InsertPoint(count, itkPoint); + count++; + } + if (numPoints>2) + { + double* point = fiberPolyData->GetPoint(points[numPoints-1]); + itk::Point itkPoint; + itkPoint[0] = point[0]; + itkPoint[1] = point[1]; + itkPoint[2] = point[2]; + pointSet->InsertPoint(count, itkPoint); + count++; + } } - } - mitk::DataNode::Pointer node = mitk::DataNode::New(); - node->SetData( pointSet ); - return node; + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData( pointSet ); + return node; } // generate image displaying the fiber endings mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsImage(mitk::FiberBundleX::Pointer fib) { - typedef unsigned char OutPixType; - typedef itk::Image OutImageType; - - typedef itk::TractsToFiberEndingsImageFilter< OutImageType > ImageGeneratorType; - ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); - generator->SetFiberBundle(fib); - generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); - generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); - if (m_SelectedImage.IsNotNull()) - { - OutImageType::Pointer itkImage = OutImageType::New(); - CastToItkImage(m_SelectedImage, itkImage); - generator->SetInputImage(itkImage); - generator->SetUseImageGeometry(true); - } - generator->Update(); - - // get output image - OutImageType::Pointer outImg = generator->GetOutput(); - mitk::Image::Pointer img = mitk::Image::New(); - img->InitializeByItk(outImg.GetPointer()); - img->SetVolume(outImg->GetBufferPointer()); - - // init data node - mitk::DataNode::Pointer node = mitk::DataNode::New(); - node->SetData(img); - return node; + typedef unsigned char OutPixType; + typedef itk::Image OutImageType; + + typedef itk::TractsToFiberEndingsImageFilter< OutImageType > ImageGeneratorType; + ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); + generator->SetFiberBundle(fib); + generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); + generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); + if (m_SelectedImage.IsNotNull()) + { + OutImageType::Pointer itkImage = OutImageType::New(); + CastToItkImage(m_SelectedImage, itkImage); + generator->SetInputImage(itkImage); + generator->SetUseImageGeometry(true); + } + generator->Update(); + + // get output image + OutImageType::Pointer outImg = generator->GetOutput(); + mitk::Image::Pointer img = mitk::Image::New(); + img->InitializeByItk(outImg.GetPointer()); + img->SetVolume(outImg->GetBufferPointer()); + + // init data node + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData(img); + return node; } // generate rgba heatmap from fiber bundle mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateColorHeatmap(mitk::FiberBundleX::Pointer fib) { - typedef itk::RGBAPixel OutPixType; - typedef itk::Image OutImageType; - typedef itk::TractsToRgbaImageFilter< OutImageType > ImageGeneratorType; - ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); - generator->SetFiberBundle(fib); - generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); - if (m_SelectedImage.IsNotNull()) - { - itk::Image::Pointer itkImage = itk::Image::New(); - CastToItkImage(m_SelectedImage, itkImage); - generator->SetInputImage(itkImage); - generator->SetUseImageGeometry(true); - } - generator->Update(); - - // get output image - typedef itk::Image OutType; - OutType::Pointer outImg = generator->GetOutput(); - mitk::Image::Pointer img = mitk::Image::New(); - img->InitializeByItk(outImg.GetPointer()); - img->SetVolume(outImg->GetBufferPointer()); - - // init data node - mitk::DataNode::Pointer node = mitk::DataNode::New(); - node->SetData(img); - return node; + typedef itk::RGBAPixel OutPixType; + typedef itk::Image OutImageType; + typedef itk::TractsToRgbaImageFilter< OutImageType > ImageGeneratorType; + ImageGeneratorType::Pointer generator = ImageGeneratorType::New(); + generator->SetFiberBundle(fib); + generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); + if (m_SelectedImage.IsNotNull()) + { + itk::Image::Pointer itkImage = itk::Image::New(); + CastToItkImage(m_SelectedImage, itkImage); + generator->SetInputImage(itkImage); + generator->SetUseImageGeometry(true); + } + generator->Update(); + + // get output image + typedef itk::Image OutType; + OutType::Pointer outImg = generator->GetOutput(); + mitk::Image::Pointer img = mitk::Image::New(); + img->InitializeByItk(outImg.GetPointer()); + img->SetVolume(outImg->GetBufferPointer()); + + // init data node + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData(img); + return node; } // generate tract density image from fiber bundle -mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateTractDensityImage(mitk::FiberBundleX::Pointer fib, bool binary) +mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateTractDensityImage(mitk::FiberBundleX::Pointer fib, bool binary, bool absolute) { - typedef float OutPixType; - typedef itk::Image OutImageType; - - itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); - generator->SetFiberBundle(fib); - generator->SetBinaryOutput(binary); - generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); - generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); - if (m_SelectedImage.IsNotNull()) - { - OutImageType::Pointer itkImage = OutImageType::New(); - CastToItkImage(m_SelectedImage, itkImage); - generator->SetInputImage(itkImage); - generator->SetUseImageGeometry(true); - } - generator->Update(); - - // get output image - typedef itk::Image OutType; - OutType::Pointer outImg = generator->GetOutput(); - mitk::Image::Pointer img = mitk::Image::New(); - img->InitializeByItk(outImg.GetPointer()); - img->SetVolume(outImg->GetBufferPointer()); - - // init data node - mitk::DataNode::Pointer node = mitk::DataNode::New(); - node->SetData(img); - return node; + typedef float OutPixType; + typedef itk::Image OutImageType; + + itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New(); + generator->SetFiberBundle(fib); + generator->SetBinaryOutput(binary); + generator->SetOutputAbsoluteValues(absolute); + generator->SetInvertImage(m_Controls->m_InvertCheckbox->isChecked()); + generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value()); + if (m_SelectedImage.IsNotNull()) + { + OutImageType::Pointer itkImage = OutImageType::New(); + CastToItkImage(m_SelectedImage, itkImage); + generator->SetInputImage(itkImage); + generator->SetUseImageGeometry(true); + + } + generator->Update(); + + // get output image + typedef itk::Image OutType; + OutType::Pointer outImg = generator->GetOutput(); + mitk::Image::Pointer img = mitk::Image::New(); + img->InitializeByItk(outImg.GetPointer()); + img->SetVolume(outImg->GetBufferPointer()); + + // init data node + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData(img); + return node; } void QmitkFiberProcessingView::ResampleSelectedBundles() { - int factor = this->m_Controls->m_ResampleFibersSpinBox->value(); - for (int i=0; i(m_SelectedFB.at(i)->GetData()); - fib->DoFiberSmoothing(factor); - } + int factor = this->m_Controls->m_ResampleFibersSpinBox->value(); + for (int i=0; i(m_SelectedFB.at(i)->GetData()); + fib->DoFiberSmoothing(factor); + } + GenerateStats(); + RenderingManager::GetInstance()->RequestUpdateAll(); } -void QmitkFiberProcessingView::DoFaColorCoding() +void QmitkFiberProcessingView::MirrorFibers() { - if (m_SelectedImage.IsNull()) - return; -// mitk::PixelType pType = mitk::MakeScalarPixelType(); -// if (m_SelectedImage->GetPixelType()!=pType) -// { -// //mitk::Image bla; bla.GetPixelType().GetNameOfClass() -// MITK_INFO << m_SelectedImage->GetPixelType().GetNameOfClass(); -// QMessageBox::warning(NULL, "Wrong Image Type", "FA/GFA image should be of type float"); -//// return; -// } - - for( int i=0; i(m_SelectedFB.at(i)->GetData()); - fib->SetFAMap(m_SelectedImage); - fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_FA_BASED); - fib->DoColorCodingFaBased(); - } - - if(m_MultiWidget) - m_MultiWidget->RequestUpdate(); + unsigned int axis = this->m_Controls->m_AxisSelectionBox->currentIndex(); + for (int i=0; i(m_SelectedFB.at(i)->GetData()); + fib->MirrorFibers(axis); + } + if (m_SelectedFB.size()>0) + GenerateStats(); + + if (m_Surfaces.size()>0) + { + for (int i=0; i poly = surf->GetVtkPolyData(); + vtkSmartPointer vtkNewPoints = vtkPoints::New(); + + for (int i=0; iGetNumberOfPoints(); i++) + { + double* point = poly->GetPoint(i); + point[axis] *= -1; + vtkNewPoints->InsertNextPoint(point); + } + poly->SetPoints(vtkNewPoints); + surf->CalculateBoundingBox(); + } + } + + RenderingManager::GetInstance()->RequestUpdateAll(); } +void QmitkFiberProcessingView::DoFaColorCoding() +{ + if (m_SelectedImage.IsNull()) + return; + + for( int i=0; i(m_SelectedFB.at(i)->GetData()); + fib->SetFAMap(m_SelectedImage); + fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_FA_BASED); + fib->DoColorCodingFaBased(); + } + + if(m_MultiWidget) + m_MultiWidget->RequestUpdate(); +} diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.h index c1f606e896..4431618a68 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingView.h @@ -1,197 +1,199 @@ /*=================================================================== 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. ===================================================================*/ #ifndef QmitkFiberProcessingView_h #define QmitkFiberProcessingView_h #include #include "ui_QmitkFiberProcessingViewControls.h" #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 /*! \brief QmitkFiberProcessingView \warning View to process fiber bundles. Supplies methods to extract fibers from the bundle, join and subtract bundles, generate images from the selected bundle and much more. \sa QmitkFunctionality \ingroup Functionalities */ class QmitkFiberProcessingView : 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: typedef itk::Image< unsigned char, 3 > itkUCharImageType; typedef itk::Image< float, 3 > itkFloatImageType; static const std::string VIEW_ID; QmitkFiberProcessingView(); virtual ~QmitkFiberProcessingView(); virtual void CreateQtPartControl(QWidget *parent); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); virtual void Activated(); protected slots: void OnDrawCircle(); void OnDrawPolygon(); void DoFiberExtraction(); void GenerateAndComposite(); void GenerateOrComposite(); void GenerateNotComposite(); - + void PruneBundle(); + void MirrorFibers(); void JoinBundles(); void SubstractBundles(); void GenerateRoiImage(); void ProcessSelectedBundles(); void ResampleSelectedBundles(); void DoFaColorCoding(); void Extract3d(); virtual void AddFigureToDataStorage(mitk::PlanarFigure* figure, const QString& name, const char *propertyKey = NULL, mitk::BaseProperty *property = NULL ); protected: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); Ui::QmitkFiberProcessingViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; /** Connection from VTK to ITK */ template void ConnectPipelines(VTK_Exporter* exporter, ITK_Importer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } template void ConnectPipelines(ITK_Exporter exporter, VTK_Importer* importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } template < typename TPixel, unsigned int VImageDimension > void InternalCalculateMaskFromPlanarFigure( itk::Image< TPixel, VImageDimension > *image, unsigned int axis, std::string nodeName ); template < typename TPixel, unsigned int VImageDimension > void InternalReorientImagePlane( const itk::Image< TPixel, VImageDimension > *image, mitk::Geometry3D* planegeo3D, int additionalIndex ); void GenerateStats(); void UpdateGui(); berry::ISelectionListener::Pointer m_SelListener; berry::IStructuredSelection::ConstPointer m_CurrentSelection; private: int m_EllipseCounter; int m_PolygonCounter; //contains the selected FiberBundles std::vector m_SelectedFB; //contains the selected PlanarFigures std::vector m_SelectedPF; mitk::Image::Pointer m_SelectedImage; mitk::Image::Pointer m_InternalImage; mitk::PlanarFigure::Pointer m_PlanarFigure; float m_UpsamplingFactor; itkUCharImageType::Pointer m_InternalImageMask3D; itkUCharImageType::Pointer m_PlanarFigureImage; std::vector m_Surfaces; void AddCompositeToDatastorage(mitk::PlanarFigureComposite::Pointer, mitk::DataNode::Pointer); void debugPFComposition(mitk::PlanarFigureComposite::Pointer , int ); void CompositeExtraction(mitk::DataNode::Pointer node, mitk::Image* image); - mitk::DataNode::Pointer GenerateTractDensityImage(mitk::FiberBundleX::Pointer fib, bool binary); + mitk::DataNode::Pointer GenerateTractDensityImage(mitk::FiberBundleX::Pointer fib, bool binary, bool absolute); mitk::DataNode::Pointer GenerateColorHeatmap(mitk::FiberBundleX::Pointer fib); mitk::DataNode::Pointer GenerateFiberEndingsImage(mitk::FiberBundleX::Pointer fib); mitk::DataNode::Pointer GenerateFiberEndingsPointSet(mitk::FiberBundleX::Pointer fib); }; #endif // _QMITKFIBERTRACKINGVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui index e907ff8063..ffba8ab8ec 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkFiberProcessingViewControls.ui @@ -1,683 +1,801 @@ QmitkFiberProcessingViewControls 0 0 665 - 587 + 671 Form 0 9 3 9 3 Fiber Bundle Modification 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 30 30 Draw circular ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/circle.png:/QmitkDiffusionImaging/circle.png 32 32 false true 30 30 Draw rectangular ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/rectangle.png:/QmitkDiffusionImaging/rectangle.png 32 32 true true 30 30 Draw polygonal ROI. Select reference fiber bundle to execute. :/QmitkDiffusionImaging/polygon.png:/QmitkDiffusionImaging/polygon.png 32 32 true true Qt::Horizontal 40 20 QFrame::NoFrame QFrame::Raised 0 false 0 0 200 16777215 11 Extract fibers passing through selected ROI or composite ROI. Select ROI and fiber bundle to execute. Extract false 0 0 200 16777215 11 Returns all fibers contained in bundle X that are not contained in bundle Y (not commutative!). Select at least two fiber bundles to execute. Substract false 0 0 200 16777215 11 Merge selected fiber bundles. Select at least two fiber bundles to execute. Join Qt::Horizontal 40 20 false 0 0 200 16777215 11 Extract fibers passing through selected surface mesh. Select surface mesh and fiber bundle to execute. Extract 3D false 0 0 16777215 16777215 11 Generate a binary image containing all selected ROIs. Select at least one ROI (planar figure) and a reference fiber bundle or image. ROI Image 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 Qt::Horizontal 40 20 false 60 16777215 Create AND composition with selected ROIs. AND false 60 16777215 Create OR composition with selected ROIs. OR false 60 16777215 Create NOT composition from selected ROI. NOT Fiber Bundle Processing + + QFormLayout::AllNonFixedFieldsGrow + 0 0 - Tract Density Image (TDI) + Tract Density Image + + + + + Tract Density Image (normalize image values) Binary Envelope Fiber Bundle Image Fiber Endings Image Fiber Endings Pointset - - - - Upsampling factor - - - 1 - - - 10 - - - 2 - - - false 0 0 200 16777215 11 Perform selected operation on all selected fiber bundles. Generate If selected operation generates an image, the inverse image is returned. Invert false 0 0 200 16777215 11 Resample fibers using a Kochanek spline interpolation. Smooth Fibers Points per cm 1 50 10 + + + false + + + + 0 + 0 + + + + + 200 + 16777215 + + + + + 11 + + + + Remove fibers shorten than the specified length (in mm). + + + Prune Bundle + + + + + + + Minimum fiber length in mm + + + 0 + + + 1000 + + + 20 + + + + + + + false + + + + 0 + 0 + + + + + 200 + 16777215 + + + + + 11 + + + + Mirror fibers around specified axis. + + + Mirror Fibers + + + + false 0 0 200 16777215 11 Apply float image values (0-1) as color coding to the selected fiber bundle. Color By Scalar Map + + + + 0 + + + 3 + + + 3 + + + + y-z-Plane + + + + + x-z-Plane + + + + + x-y-Plane + + + + + + + + Upsampling factor + + + 1 + + + 0.100000000000000 + + + 10.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + Fiber Bundle Statistics Courier 10 Pitch false true Qt::Vertical 20 40 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.cpp index b1b5eb8239..70cfa80c07 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.cpp @@ -1,770 +1,747 @@ /*=================================================================== 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. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkGibbsTrackingView.h" #include // Qt #include #include #include // MITK #include #include #include #include #include // ITK #include #include #include // MISC #include QmitkTrackingWorker::QmitkTrackingWorker(QmitkGibbsTrackingView* view) - : m_View(view) + : m_View(view) { } void QmitkTrackingWorker::run() { - m_View->m_GlobalTracker = QmitkGibbsTrackingView::GibbsTrackingFilterType::New(); - - MITK_INFO << "Resampling mask images"; - // setup resampler - typedef itk::ResampleImageFilter ResamplerType; - ResamplerType::Pointer resampler = ResamplerType::New(); - resampler->SetOutputSpacing( m_View->m_ItkQBallImage->GetSpacing() ); - resampler->SetOutputOrigin( m_View->m_ItkQBallImage->GetOrigin() ); - resampler->SetOutputDirection( m_View->m_ItkQBallImage->GetDirection() ); - resampler->SetSize( m_View->m_ItkQBallImage->GetLargestPossibleRegion().GetSize() ); - - // resample mask image - resampler->SetInput( m_View->m_MaskImage ); - resampler->SetDefaultPixelValue(0); - resampler->Update(); - m_View->m_MaskImage = resampler->GetOutput(); - - m_View->m_GlobalTracker->SetInput0(m_View->m_ItkQBallImage.GetPointer()); - m_View->m_GlobalTracker->SetMaskImage(m_View->m_MaskImage); - m_View->m_GlobalTracker->SetTempStart((float)m_View->m_Controls->m_StartTempSlider->value()/100); - m_View->m_GlobalTracker->SetTempEnd((float)m_View->m_Controls->m_EndTempSlider->value()/10000); - m_View->m_GlobalTracker->SetNumIt(m_View->m_Iterations); - m_View->m_GlobalTracker->SetParticleWeight((float)m_View->m_Controls->m_ParticleWeightSlider->value()/10000); - m_View->m_GlobalTracker->SetSubtractMean(m_View->m_Controls->m_MeanSubtractionCheckbox->isChecked()); - m_View->m_GlobalTracker->SetParticleWidth((float)(m_View->m_Controls->m_ParticleWidthSlider->value())/10); - m_View->m_GlobalTracker->SetParticleLength((float)(m_View->m_Controls->m_ParticleLengthSlider->value())/10); - m_View->m_GlobalTracker->SetInexBalance((float)m_View->m_Controls->m_InExBalanceSlider->value()/10); - m_View->m_GlobalTracker->SetFiberLength(m_View->m_Controls->m_FiberLengthSlider->value()); - m_View->m_GlobalTracker->SetCurvatureHardThreshold(cos((float)m_View->m_Controls->m_CurvatureThresholdSlider->value()*3.14159265/180)); - - m_View->m_GlobalTracker->Update(); - m_View->m_TrackingThread.quit(); + m_View->m_GlobalTracker = QmitkGibbsTrackingView::GibbsTrackingFilterType::New(); + + m_View->m_GlobalTracker->SetQBallImage(m_View->m_ItkQBallImage); + m_View->m_GlobalTracker->SetTensorImage(m_View->m_ItkTensorImage); + m_View->m_GlobalTracker->SetMaskImage(m_View->m_MaskImage); + m_View->m_GlobalTracker->SetStartTemperature((float)m_View->m_Controls->m_StartTempSlider->value()/100); + m_View->m_GlobalTracker->SetEndTemperature((float)m_View->m_Controls->m_EndTempSlider->value()/10000); + m_View->m_GlobalTracker->SetIterations(m_View->m_Iterations); + m_View->m_GlobalTracker->SetParticleWeight((float)m_View->m_Controls->m_ParticleWeightSlider->value()/10000); + m_View->m_GlobalTracker->SetParticleWidth((float)(m_View->m_Controls->m_ParticleWidthSlider->value())/10); + m_View->m_GlobalTracker->SetParticleLength((float)(m_View->m_Controls->m_ParticleLengthSlider->value())/10); + m_View->m_GlobalTracker->SetInexBalance((float)m_View->m_Controls->m_InExBalanceSlider->value()/10); + m_View->m_GlobalTracker->SetMinFiberLength(m_View->m_Controls->m_FiberLengthSlider->value()); + m_View->m_GlobalTracker->SetCurvatureThreshold(cos((float)m_View->m_Controls->m_CurvatureThresholdSlider->value()*M_PI/180)); + m_View->m_GlobalTracker->SetRandomSeed(m_View->m_Controls->m_RandomSeedSlider->value()); + + m_View->m_GlobalTracker->Update(); + m_View->m_TrackingThread.quit(); } const std::string QmitkGibbsTrackingView::VIEW_ID = -"org.mitk.views.gibbstracking"; + "org.mitk.views.gibbstracking"; QmitkGibbsTrackingView::QmitkGibbsTrackingView() - : QmitkFunctionality() - , m_Controls( 0 ) - , m_MultiWidget( NULL ) - , m_ThreadIsRunning(false) - , m_GlobalTracker(NULL) - , m_QBallImage(NULL) - , m_MaskImage(NULL) - , m_QBallImageNode(NULL) - , m_ItkQBallImage(NULL) - , m_FiberBundleNode(NULL) - , m_MaskImageNode(NULL) - , m_TrackingWorker(this) - , m_Iterations(10000000) - , m_LastStep(0) -{ - m_TrackingWorker.moveToThread(&m_TrackingThread); - connect(&m_TrackingThread, SIGNAL(started()), this, SLOT(BeforeThread())); - connect(&m_TrackingThread, SIGNAL(started()), &m_TrackingWorker, SLOT(run())); - connect(&m_TrackingThread, SIGNAL(finished()), this, SLOT(AfterThread())); - connect(&m_TrackingThread, SIGNAL(terminated()), this, SLOT(AfterThread())); - m_TrackingTimer = new QTimer(this); + : QmitkFunctionality() + , m_Controls( 0 ) + , m_MultiWidget( NULL ) + , m_ThreadIsRunning(false) + , m_GlobalTracker(NULL) + , m_QBallImage(NULL) + , m_MaskImage(NULL) + , m_ImageNode(NULL) + , m_ItkQBallImage(NULL) + , m_ItkTensorImage(NULL) + , m_FiberBundleNode(NULL) + , m_MaskImageNode(NULL) + , m_TrackingWorker(this) + , m_Iterations(10000000) + , m_LastStep(0) +{ + m_TrackingWorker.moveToThread(&m_TrackingThread); + connect(&m_TrackingThread, SIGNAL(started()), this, SLOT(BeforeThread())); + connect(&m_TrackingThread, SIGNAL(started()), &m_TrackingWorker, SLOT(run())); + connect(&m_TrackingThread, SIGNAL(finished()), this, SLOT(AfterThread())); + connect(&m_TrackingThread, SIGNAL(terminated()), this, SLOT(AfterThread())); + m_TrackingTimer = new QTimer(this); } QmitkGibbsTrackingView::~QmitkGibbsTrackingView() { - delete m_TrackingTimer; + delete m_TrackingTimer; } // update tracking status and generate fiber bundle void QmitkGibbsTrackingView::TimerUpdate() { - int currentStep = m_GlobalTracker->GetCurrentStep(); - mitk::ProgressBar::GetInstance()->Progress(currentStep-m_LastStep); - UpdateTrackingStatus(); - GenerateFiberBundle(false); - m_LastStep = currentStep; + int currentStep = m_GlobalTracker->GetCurrentStep(); + mitk::ProgressBar::GetInstance()->Progress(currentStep-m_LastStep); + UpdateTrackingStatus(); + GenerateFiberBundle(false); + m_LastStep = currentStep; } // tell global tractography filter to stop after current step void QmitkGibbsTrackingView::StopGibbsTracking() { - if (m_GlobalTracker.IsNull()) - return; + if (m_GlobalTracker.IsNull()) + return; - //mitk::ProgressBar::GetInstance()->Progress(m_GlobalTracker->GetSteps()-m_LastStep+1); - m_GlobalTracker->SetAbortTracking(true); - m_Controls->m_TrackingStop->setEnabled(false); - m_Controls->m_TrackingStop->setText("Stopping Tractography ..."); + //mitk::ProgressBar::GetInstance()->Progress(m_GlobalTracker->GetSteps()-m_LastStep+1); + m_GlobalTracker->SetAbortTracking(true); + m_Controls->m_TrackingStop->setEnabled(false); + m_Controls->m_TrackingStop->setText("Stopping Tractography ..."); } // update gui elements and generate fiber bundle after tracking is finished void QmitkGibbsTrackingView::AfterThread() { - m_ThreadIsRunning = false; - m_TrackingTimer->stop(); - - mitk::ProgressBar::GetInstance()->Progress(m_GlobalTracker->GetSteps()-m_LastStep+1); - UpdateGUI(); - UpdateTrackingStatus(); - - if(m_Controls->m_ParticleWeightSlider->value()==0) - { - m_Controls->m_ParticleWeightLabel->setText(QString::number(m_GlobalTracker->GetParticleWeight())); - m_Controls->m_ParticleWeightSlider->setValue(m_GlobalTracker->GetParticleWeight()*10000); - } - if(m_Controls->m_ParticleWidthSlider->value()==0) - { - m_Controls->m_ParticleWidthLabel->setText(QString::number(m_GlobalTracker->GetParticleWidth())); - m_Controls->m_ParticleWidthSlider->setValue(m_GlobalTracker->GetParticleWidth()*10); - } - if(m_Controls->m_ParticleLengthSlider->value()==0) - { - m_Controls->m_ParticleLengthLabel->setText(QString::number(m_GlobalTracker->GetParticleLength())); - m_Controls->m_ParticleLengthSlider->setValue(m_GlobalTracker->GetParticleLength()*10); - } - - GenerateFiberBundle(true); - m_FiberBundleNode = NULL; + m_ThreadIsRunning = false; + m_TrackingTimer->stop(); + + mitk::ProgressBar::GetInstance()->Progress(m_GlobalTracker->GetSteps()-m_LastStep+1); + UpdateGUI(); + UpdateTrackingStatus(); + + if(m_Controls->m_ParticleWeightSlider->value()==0) + { + m_Controls->m_ParticleWeightLabel->setText(QString::number(m_GlobalTracker->GetParticleWeight())); + m_Controls->m_ParticleWeightSlider->setValue(m_GlobalTracker->GetParticleWeight()*10000); + } + if(m_Controls->m_ParticleWidthSlider->value()==0) + { + m_Controls->m_ParticleWidthLabel->setText(QString::number(m_GlobalTracker->GetParticleWidth())); + m_Controls->m_ParticleWidthSlider->setValue(m_GlobalTracker->GetParticleWidth()*10); + } + if(m_Controls->m_ParticleLengthSlider->value()==0) + { + m_Controls->m_ParticleLengthLabel->setText(QString::number(m_GlobalTracker->GetParticleLength())); + m_Controls->m_ParticleLengthSlider->setValue(m_GlobalTracker->GetParticleLength()*10); + } + + GenerateFiberBundle(true); + m_FiberBundleNode = NULL; } // start tracking timer and update gui elements before tracking is started void QmitkGibbsTrackingView::BeforeThread() { - m_ThreadIsRunning = true; - m_TrackingTime = QTime::currentTime(); - m_ElapsedTime = 0; - m_TrackingTimer->start(1000); - m_LastStep = 0; + m_ThreadIsRunning = true; + m_TrackingTime = QTime::currentTime(); + m_ElapsedTime = 0; + m_TrackingTimer->start(1000); + m_LastStep = 0; - UpdateGUI(); + UpdateGUI(); } // setup gui elements and signal/slot connections void QmitkGibbsTrackingView::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::QmitkGibbsTrackingViewControls; - m_Controls->setupUi( parent ); - - AdvancedSettings(); - - connect( m_TrackingTimer, SIGNAL(timeout()), this, SLOT(TimerUpdate()) ); - connect( m_Controls->m_TrackingStop, SIGNAL(clicked()), this, SLOT(StopGibbsTracking()) ); - connect( m_Controls->m_TrackingStart, SIGNAL(clicked()), this, SLOT(StartGibbsTracking()) ); - connect( m_Controls->m_AdvancedSettingsCheckbox, SIGNAL(clicked()), this, SLOT(AdvancedSettings()) ); - connect( m_Controls->m_SaveTrackingParameters, SIGNAL(clicked()), this, SLOT(SaveTrackingParameters()) ); - connect( m_Controls->m_LoadTrackingParameters, SIGNAL(clicked()), this, SLOT(LoadTrackingParameters()) ); - connect( m_Controls->m_IterationsSlider, SIGNAL(valueChanged(int)), this, SLOT(SetIterations(int)) ); - connect( m_Controls->m_ParticleWidthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleWidth(int)) ); - connect( m_Controls->m_ParticleLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleLength(int)) ); - connect( m_Controls->m_InExBalanceSlider, SIGNAL(valueChanged(int)), this, SLOT(SetInExBalance(int)) ); - connect( m_Controls->m_FiberLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetFiberLength(int)) ); - connect( m_Controls->m_ParticleWeightSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleWeight(int)) ); - connect( m_Controls->m_StartTempSlider, SIGNAL(valueChanged(int)), this, SLOT(SetStartTemp(int)) ); - connect( m_Controls->m_EndTempSlider, SIGNAL(valueChanged(int)), this, SLOT(SetEndTemp(int)) ); - connect( m_Controls->m_CurvatureThresholdSlider, SIGNAL(valueChanged(int)), this, SLOT(SetCurvatureThreshold(int)) ); - connect( m_Controls->m_OutputFileButton, SIGNAL(clicked()), this, SLOT(SetOutputFile()) ); - } + // build up qt view, unless already done + if ( !m_Controls ) + { + // create GUI widgets from the Qt Designer's .ui file + m_Controls = new Ui::QmitkGibbsTrackingViewControls; + m_Controls->setupUi( parent ); + + AdvancedSettings(); + + connect( m_TrackingTimer, SIGNAL(timeout()), this, SLOT(TimerUpdate()) ); + connect( m_Controls->m_TrackingStop, SIGNAL(clicked()), this, SLOT(StopGibbsTracking()) ); + connect( m_Controls->m_TrackingStart, SIGNAL(clicked()), this, SLOT(StartGibbsTracking()) ); + connect( m_Controls->m_AdvancedSettingsCheckbox, SIGNAL(clicked()), this, SLOT(AdvancedSettings()) ); + connect( m_Controls->m_SaveTrackingParameters, SIGNAL(clicked()), this, SLOT(SaveTrackingParameters()) ); + connect( m_Controls->m_LoadTrackingParameters, SIGNAL(clicked()), this, SLOT(LoadTrackingParameters()) ); + connect( m_Controls->m_IterationsSlider, SIGNAL(valueChanged(int)), this, SLOT(SetIterations(int)) ); + connect( m_Controls->m_ParticleWidthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleWidth(int)) ); + connect( m_Controls->m_ParticleLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleLength(int)) ); + connect( m_Controls->m_InExBalanceSlider, SIGNAL(valueChanged(int)), this, SLOT(SetInExBalance(int)) ); + connect( m_Controls->m_FiberLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(SetFiberLength(int)) ); + connect( m_Controls->m_ParticleWeightSlider, SIGNAL(valueChanged(int)), this, SLOT(SetParticleWeight(int)) ); + connect( m_Controls->m_StartTempSlider, SIGNAL(valueChanged(int)), this, SLOT(SetStartTemp(int)) ); + connect( m_Controls->m_EndTempSlider, SIGNAL(valueChanged(int)), this, SLOT(SetEndTemp(int)) ); + connect( m_Controls->m_CurvatureThresholdSlider, SIGNAL(valueChanged(int)), this, SLOT(SetCurvatureThreshold(int)) ); + connect( m_Controls->m_RandomSeedSlider, SIGNAL(valueChanged(int)), this, SLOT(SetRandomSeed(int)) ); + connect( m_Controls->m_OutputFileButton, SIGNAL(clicked()), this, SLOT(SetOutputFile()) ); + } } void QmitkGibbsTrackingView::SetInExBalance(int value) { - m_Controls->m_InExBalanceLabel->setText(QString::number((float)value/10)); + m_Controls->m_InExBalanceLabel->setText(QString::number((float)value/10)); } void QmitkGibbsTrackingView::SetFiberLength(int value) { - m_Controls->m_FiberLengthLabel->setText(QString::number(value)+"mm"); + m_Controls->m_FiberLengthLabel->setText(QString::number(value)+"mm"); +} + +void QmitkGibbsTrackingView::SetRandomSeed(int value) +{ + if (value>=0) + m_Controls->m_RandomSeedLabel->setText(QString::number(value)); + else + m_Controls->m_RandomSeedLabel->setText("auto"); } void QmitkGibbsTrackingView::SetParticleWeight(int value) { - if (value>0) - m_Controls->m_ParticleWeightLabel->setText(QString::number((float)value/10000)); - else - m_Controls->m_ParticleWeightLabel->setText("auto"); + if (value>0) + m_Controls->m_ParticleWeightLabel->setText(QString::number((float)value/10000)); + else + m_Controls->m_ParticleWeightLabel->setText("auto"); } void QmitkGibbsTrackingView::SetStartTemp(int value) { - m_Controls->m_StartTempLabel->setText(QString::number((float)value/100)); + m_Controls->m_StartTempLabel->setText(QString::number((float)value/100)); } void QmitkGibbsTrackingView::SetEndTemp(int value) { - m_Controls->m_EndTempLabel->setText(QString::number((float)value/10000)); + m_Controls->m_EndTempLabel->setText(QString::number((float)value/10000)); } void QmitkGibbsTrackingView::SetParticleWidth(int value) { - if (value>0) - m_Controls->m_ParticleWidthLabel->setText(QString::number((float)value/10)+" mm"); - else - m_Controls->m_ParticleWidthLabel->setText("auto"); + if (value>0) + m_Controls->m_ParticleWidthLabel->setText(QString::number((float)value/10)+" mm"); + else + m_Controls->m_ParticleWidthLabel->setText("auto"); } void QmitkGibbsTrackingView::SetParticleLength(int value) { - if (value>0) - m_Controls->m_ParticleLengthLabel->setText(QString::number((float)value/10)+" mm"); - else - m_Controls->m_ParticleLengthLabel->setText("auto"); + if (value>0) + m_Controls->m_ParticleLengthLabel->setText(QString::number((float)value/10)+" mm"); + else + m_Controls->m_ParticleLengthLabel->setText("auto"); } void QmitkGibbsTrackingView::SetCurvatureThreshold(int value) { - m_Controls->m_CurvatureThresholdLabel->setText(QString::number(value)+"°"); + m_Controls->m_CurvatureThresholdLabel->setText(QString::number(value)+"°"); } void QmitkGibbsTrackingView::SetIterations(int value) { - switch(value) - { - case 0: - m_Controls->m_IterationsLabel->setText("Iterations: 1x10^4"); - m_Iterations = 10000; - break; - case 1: - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^4"); - m_Iterations = 50000; - break; - case 2: - m_Controls->m_IterationsLabel->setText("Iterations: 1x10^5"); - m_Iterations = 100000; - break; - case 3: - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^5"); - m_Iterations = 500000; - break; - case 4: - m_Controls->m_IterationsLabel->setText("Iterations: 1x10^6"); - m_Iterations = 1000000; - break; - case 5: - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^6"); - m_Iterations = 5000000; - break; - case 6: - m_Controls->m_IterationsLabel->setText("Iterations: 1x10^7"); - m_Iterations = 10000000; - break; - case 7: - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^7"); - m_Iterations = 50000000; - break; - case 8: - m_Controls->m_IterationsLabel->setText("Iterations: 1x10^8"); - m_Iterations = 100000000; - break; - case 9: - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^8"); - m_Iterations = 500000000; - break; - case 10: - m_Controls->m_IterationsLabel->setText("Iterations: 1x10^9"); - m_Iterations = 1000000000; - break; - } + switch(value) + { + case 0: + m_Controls->m_IterationsLabel->setText("Iterations: 1x10^4"); + m_Iterations = 10000; + break; + case 1: + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^4"); + m_Iterations = 50000; + break; + case 2: + m_Controls->m_IterationsLabel->setText("Iterations: 1x10^5"); + m_Iterations = 100000; + break; + case 3: + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^5"); + m_Iterations = 500000; + break; + case 4: + m_Controls->m_IterationsLabel->setText("Iterations: 1x10^6"); + m_Iterations = 1000000; + break; + case 5: + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^6"); + m_Iterations = 5000000; + break; + case 6: + m_Controls->m_IterationsLabel->setText("Iterations: 1x10^7"); + m_Iterations = 10000000; + break; + case 7: + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^7"); + m_Iterations = 50000000; + break; + case 8: + m_Controls->m_IterationsLabel->setText("Iterations: 1x10^8"); + m_Iterations = 100000000; + break; + case 9: + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^8"); + m_Iterations = 500000000; + break; + case 10: + m_Controls->m_IterationsLabel->setText("Iterations: 1x10^9"); + m_Iterations = 1000000000; + break; + } } void QmitkGibbsTrackingView::StdMultiWidgetAvailable(QmitkStdMultiWidget &stdMultiWidget) { - m_MultiWidget = &stdMultiWidget; + m_MultiWidget = &stdMultiWidget; } void QmitkGibbsTrackingView::StdMultiWidgetNotAvailable() { - m_MultiWidget = NULL; + m_MultiWidget = NULL; } // called if datamanager selection changes void QmitkGibbsTrackingView::OnSelectionChanged( std::vector nodes ) { - if (m_ThreadIsRunning) - return; + if (m_ThreadIsRunning) + return; - m_QBallImageNode = NULL; - m_MaskImageNode = NULL; - - // iterate all selected objects - for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) - { - mitk::DataNode::Pointer node = *it; + m_ImageNode = NULL; + m_MaskImageNode = NULL; - if( node.IsNotNull() && dynamic_cast(node->GetData()) ) - m_QBallImageNode = node; - else if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + // iterate all selected objects + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { - bool isBinary = false; - node->GetPropertyValue("binary", isBinary); - if (isBinary) - m_MaskImageNode = node; + mitk::DataNode::Pointer node = *it; + + if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + m_ImageNode = node; + else if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + m_ImageNode = node; + else if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + { + bool isBinary = false; + node->GetPropertyValue("binary", isBinary); + if (isBinary) + m_MaskImageNode = node; + } } - } - UpdateGUI(); + UpdateGUI(); } // update gui elements displaying trackings status void QmitkGibbsTrackingView::UpdateTrackingStatus() { - if (m_GlobalTracker.IsNull()) - return; + if (m_GlobalTracker.IsNull()) + return; - m_ElapsedTime += m_TrackingTime.elapsed()/1000; - m_TrackingTime.restart(); - unsigned long hours = m_ElapsedTime/3600; - unsigned long minutes = (m_ElapsedTime%3600)/60; - unsigned long seconds = m_ElapsedTime%60; + m_ElapsedTime += m_TrackingTime.elapsed()/1000; + m_TrackingTime.restart(); + unsigned long hours = m_ElapsedTime/3600; + unsigned long minutes = (m_ElapsedTime%3600)/60; + unsigned long seconds = m_ElapsedTime%60; - m_Controls->m_ProposalAcceptance->setText(QString::number(m_GlobalTracker->GetProposalAcceptance()*100)+"%"); + m_Controls->m_ProposalAcceptance->setText(QString::number(m_GlobalTracker->GetProposalAcceptance()*100)+"%"); - m_Controls->m_TrackingTimeLabel->setText( QString::number(hours)+QString("h ")+QString::number(minutes)+QString("m ")+QString::number(seconds)+QString("s") ); - m_Controls->m_NumConnectionsLabel->setText( QString::number(m_GlobalTracker->GetNumConnections()) ); - m_Controls->m_NumParticlesLabel->setText( QString::number(m_GlobalTracker->GetNumParticles()) ); - m_Controls->m_CurrentStepLabel->setText( QString::number(100*(float)m_GlobalTracker->GetCurrentStep()/m_GlobalTracker->GetSteps())+"%" ); - m_Controls->m_AcceptedFibersLabel->setText( QString::number(m_GlobalTracker->GetNumAcceptedFibers()) ); + m_Controls->m_TrackingTimeLabel->setText( QString::number(hours)+QString("h ")+QString::number(minutes)+QString("m ")+QString::number(seconds)+QString("s") ); + m_Controls->m_NumConnectionsLabel->setText( QString::number(m_GlobalTracker->GetNumConnections()) ); + m_Controls->m_NumParticlesLabel->setText( QString::number(m_GlobalTracker->GetNumParticles()) ); + m_Controls->m_CurrentStepLabel->setText( QString::number(100*(float)(m_GlobalTracker->GetCurrentStep()-1)/m_GlobalTracker->GetSteps())+"%" ); + m_Controls->m_AcceptedFibersLabel->setText( QString::number(m_GlobalTracker->GetNumAcceptedFibers()) ); } // update gui elements (enable/disable elements and set tooltips) void QmitkGibbsTrackingView::UpdateGUI() { - if (m_QBallImageNode.IsNotNull()) - m_Controls->m_QballImageLabel->setText(m_QBallImageNode->GetName().c_str()); - else - m_Controls->m_QballImageLabel->setText("-"); - if (m_MaskImageNode.IsNotNull()) - m_Controls->m_MaskImageLabel->setText(m_MaskImageNode->GetName().c_str()); - else - m_Controls->m_MaskImageLabel->setText("-"); - - if (!m_ThreadIsRunning && m_QBallImageNode.IsNotNull()) - { - m_Controls->m_TrackingStop->setEnabled(false); - m_Controls->m_TrackingStart->setEnabled(true); - m_Controls->m_LoadTrackingParameters->setEnabled(true); - m_Controls->m_IterationsSlider->setEnabled(true); - m_Controls->m_AdvancedFrame->setEnabled(true); - m_Controls->m_TrackingStop->setText("Stop Tractography"); - m_Controls->m_TrackingStart->setToolTip("Start tractography. No further change of parameters possible."); - m_Controls->m_TrackingStop->setToolTip(""); - } - else if (!m_ThreadIsRunning) - { - m_Controls->m_TrackingStop->setEnabled(false); - m_Controls->m_TrackingStart->setEnabled(false); - m_Controls->m_LoadTrackingParameters->setEnabled(true); - m_Controls->m_IterationsSlider->setEnabled(true); - m_Controls->m_AdvancedFrame->setEnabled(true); - m_Controls->m_TrackingStop->setText("Stop Tractography"); - m_Controls->m_TrackingStart->setToolTip("No Q-Ball image selected."); - m_Controls->m_TrackingStop->setToolTip(""); - } - else - { - m_Controls->m_TrackingStop->setEnabled(true); - m_Controls->m_TrackingStart->setEnabled(false); - m_Controls->m_LoadTrackingParameters->setEnabled(false); - m_Controls->m_IterationsSlider->setEnabled(false); - m_Controls->m_AdvancedFrame->setEnabled(false); - m_Controls->m_AdvancedFrame->setVisible(false); - m_Controls->m_AdvancedSettingsCheckbox->setChecked(false); - m_Controls->m_TrackingStart->setToolTip("Tracking in progress."); - m_Controls->m_TrackingStop->setToolTip("Stop tracking and display results."); - } + if (m_ImageNode.IsNotNull()) + m_Controls->m_QballImageLabel->setText(m_ImageNode->GetName().c_str()); + else + m_Controls->m_QballImageLabel->setText("-"); + if (m_MaskImageNode.IsNotNull()) + m_Controls->m_MaskImageLabel->setText(m_MaskImageNode->GetName().c_str()); + else + m_Controls->m_MaskImageLabel->setText("-"); + + if (!m_ThreadIsRunning && m_ImageNode.IsNotNull()) + { + m_Controls->m_TrackingStop->setEnabled(false); + m_Controls->m_TrackingStart->setEnabled(true); + m_Controls->m_LoadTrackingParameters->setEnabled(true); + m_Controls->m_IterationsSlider->setEnabled(true); + m_Controls->m_AdvancedFrame->setEnabled(true); + m_Controls->m_TrackingStop->setText("Stop Tractography"); + m_Controls->m_TrackingStart->setToolTip("Start tractography. No further change of parameters possible."); + m_Controls->m_TrackingStop->setToolTip(""); + } + else if (!m_ThreadIsRunning) + { + m_Controls->m_TrackingStop->setEnabled(false); + m_Controls->m_TrackingStart->setEnabled(false); + m_Controls->m_LoadTrackingParameters->setEnabled(true); + m_Controls->m_IterationsSlider->setEnabled(true); + m_Controls->m_AdvancedFrame->setEnabled(true); + m_Controls->m_TrackingStop->setText("Stop Tractography"); + m_Controls->m_TrackingStart->setToolTip("No Q-Ball image selected."); + m_Controls->m_TrackingStop->setToolTip(""); + } + else + { + m_Controls->m_TrackingStop->setEnabled(true); + m_Controls->m_TrackingStart->setEnabled(false); + m_Controls->m_LoadTrackingParameters->setEnabled(false); + m_Controls->m_IterationsSlider->setEnabled(false); + m_Controls->m_AdvancedFrame->setEnabled(false); + m_Controls->m_AdvancedFrame->setVisible(false); + m_Controls->m_AdvancedSettingsCheckbox->setChecked(false); + m_Controls->m_TrackingStart->setToolTip("Tracking in progress."); + m_Controls->m_TrackingStop->setToolTip("Stop tracking and display results."); + } } // show/hide advanced settings frame void QmitkGibbsTrackingView::AdvancedSettings() { - m_Controls->m_AdvancedFrame->setVisible(m_Controls->m_AdvancedSettingsCheckbox->isChecked()); + m_Controls->m_AdvancedFrame->setVisible(m_Controls->m_AdvancedSettingsCheckbox->isChecked()); } // set mask image data node void QmitkGibbsTrackingView::SetMask() { - std::vector nodes = GetDataManagerSelection(); - if (nodes.empty()) - { - m_MaskImageNode = NULL; - m_Controls->m_MaskImageLabel->setText("-"); - return; - } - - for( std::vector::iterator it = nodes.begin(); - it != nodes.end(); - ++it ) - { - mitk::DataNode::Pointer node = *it; + std::vector nodes = GetDataManagerSelection(); + if (nodes.empty()) + { + m_MaskImageNode = NULL; + m_Controls->m_MaskImageLabel->setText("-"); + return; + } - if (node.IsNotNull() && dynamic_cast(node->GetData())) + for( std::vector::iterator it = nodes.begin(); + it != nodes.end(); + ++it ) { - m_MaskImageNode = node; - m_Controls->m_MaskImageLabel->setText(node->GetName().c_str()); - return; + mitk::DataNode::Pointer node = *it; + + if (node.IsNotNull() && dynamic_cast(node->GetData())) + { + m_MaskImageNode = node; + m_Controls->m_MaskImageLabel->setText(node->GetName().c_str()); + return; + } } - } } // cast image to float template void QmitkGibbsTrackingView::CastToFloat(InputImageType* image, mitk::Image::Pointer outImage) { - typedef itk::CastImageFilter ItkCastFilter; - typename ItkCastFilter::Pointer itkCaster = ItkCastFilter::New(); - itkCaster->SetInput(image); - itkCaster->Update(); - outImage->InitializeByItk(itkCaster->GetOutput()); - outImage->SetVolume(itkCaster->GetOutput()->GetBufferPointer()); + typedef itk::CastImageFilter ItkCastFilter; + typename ItkCastFilter::Pointer itkCaster = ItkCastFilter::New(); + itkCaster->SetInput(image); + itkCaster->Update(); + outImage->InitializeByItk(itkCaster->GetOutput()); + outImage->SetVolume(itkCaster->GetOutput()->GetBufferPointer()); } // check for mask and qbi and start tracking thread void QmitkGibbsTrackingView::StartGibbsTracking() { - if(m_ThreadIsRunning) - { - MITK_WARN("QmitkGibbsTrackingView")<<"Thread already running!"; - return; - } - - if (m_QBallImageNode.IsNull()) - { - // Nothing selected. Inform the user and return - QMessageBox::information( NULL, "Warning", "Please load and select a qball image before starting image processing."); - return; - } - - // a node itself is not very useful, we need its data item (the image) - mitk::BaseData* data = m_QBallImageNode->GetData(); - if (!data) - return; - - // test if this data item is an image or not (could also be a surface or something totally different) - m_QBallImage = dynamic_cast( data ); - if (m_QBallImage.IsNull()) - return; - - // cast qbi to itk - m_ItkQBallImage = ItkQBallImgType::New(); - mitk::CastToItkImage(m_QBallImage, m_ItkQBallImage); - - // mask image found? - // catch exceptions thrown by the itkAccess macros - try{ - if(m_MaskImageNode.IsNotNull()) + if(m_ThreadIsRunning) { - m_MaskImage = 0; - if (dynamic_cast(m_MaskImageNode->GetData())) + MITK_WARN("QmitkGibbsTrackingView")<<"Thread already running!"; + return; + } - mitk::CastToItkImage(dynamic_cast(m_MaskImageNode->GetData()), - m_MaskImage); + if (m_ImageNode.IsNull()) + { + QMessageBox::information( NULL, "Warning", "Please load and select a qball image before starting image processing."); + return; } - } - catch(...) - { - QMessageBox::warning(NULL, "Warning", "Incompatible mask image chosen. Processing without masking."); - //reset mask image + + if (dynamic_cast(m_ImageNode->GetData())) + m_QBallImage = dynamic_cast(m_ImageNode->GetData()); + else if (dynamic_cast(m_ImageNode->GetData())) + m_TensorImage = dynamic_cast(m_ImageNode->GetData()); + + if (m_QBallImage.IsNull() && m_TensorImage.IsNull()) + return; + + // cast qbi to itk + m_ItkTensorImage = NULL; + m_ItkQBallImage = NULL; m_MaskImage = NULL; - } - - - // if no mask image is selected generate it - if( m_MaskImage.IsNull() ) - { - m_MaskImage = MaskImgType::New(); - m_MaskImage->SetSpacing( m_ItkQBallImage->GetSpacing() ); // Set the image spacing - m_MaskImage->SetOrigin( m_ItkQBallImage->GetOrigin() ); // Set the image origin - m_MaskImage->SetDirection( m_ItkQBallImage->GetDirection() ); // Set the image direction - m_MaskImage->SetLargestPossibleRegion( m_ItkQBallImage->GetLargestPossibleRegion()); - m_MaskImage->SetBufferedRegion( m_ItkQBallImage->GetLargestPossibleRegion() ); - m_MaskImage->Allocate(); - - itk::ImageRegionIterator it (m_MaskImage, m_MaskImage->GetLargestPossibleRegion() ); - for (it = it.Begin(); !it.IsAtEnd(); ++it) + + if (m_QBallImage.IsNotNull()) + { + m_ItkQBallImage = ItkQBallImgType::New(); + mitk::CastToItkImage(m_QBallImage, m_ItkQBallImage); + } + else { - it.Set(1); + m_ItkTensorImage = ItkTensorImage::New(); + mitk::CastToItkImage(m_TensorImage, m_ItkTensorImage); } - } - unsigned int steps = m_Iterations/10000; - if (steps<10) - steps = 10; + // mask image found? + // catch exceptions thrown by the itkAccess macros + try{ + if(m_MaskImageNode.IsNotNull()) + { + if (dynamic_cast(m_MaskImageNode->GetData())) + mitk::CastToItkImage(dynamic_cast(m_MaskImageNode->GetData()), m_MaskImage); + } + } + catch(...){}; + + unsigned int steps = m_Iterations/10000; + if (steps<10) + steps = 10; - m_LastStep = 1; - mitk::ProgressBar::GetInstance()->AddStepsToDo(steps); + m_LastStep = 1; + mitk::ProgressBar::GetInstance()->AddStepsToDo(steps); - // start worker thread - m_TrackingThread.start(QThread::LowestPriority); + // start worker thread + m_TrackingThread.start(QThread::LowestPriority); } // generate mitkFiberBundle from tracking filter output void QmitkGibbsTrackingView::GenerateFiberBundle(bool smoothFibers) { - if (m_GlobalTracker.IsNull() || (!(m_Controls->m_VisualizationCheckbox->isChecked() || m_Controls->m_VisualizeOnceButton->isChecked()) && m_ThreadIsRunning)) - return; - - if (m_Controls->m_VisualizeOnceButton->isChecked()) - m_Controls->m_VisualizeOnceButton->setChecked(false); - - vtkSmartPointer fiberBundle = m_GlobalTracker->GetFiberBundle(); - if ( fiberBundle->GetNumberOfLines()==0 ) - return; - m_FiberBundle = mitk::FiberBundleX::New(fiberBundle); - - if (smoothFibers) - m_FiberBundle->DoFiberSmoothing(10); - - if (m_FiberBundleNode.IsNotNull()){ - GetDefaultDataStorage()->Remove(m_FiberBundleNode); - m_FiberBundleNode = 0; - } - m_FiberBundleNode = mitk::DataNode::New(); - m_FiberBundleNode->SetData(m_FiberBundle); - - QString name(m_QBallImageNode->GetName().c_str()); - name += "_FiberBundle"; - m_FiberBundleNode->SetName(name.toStdString()); - m_FiberBundleNode->SetVisibility(true); - - if (!m_OutputFileName.isEmpty()) - { - QString filename = m_OutputFileName; - mitk::FiberBundleXWriter::Pointer writer = mitk::FiberBundleXWriter::New(); - writer->SetFileName(filename.toStdString()); - writer->SetInputFiberBundleX(m_FiberBundle.GetPointer()); - try - { - writer->Update(); - QMessageBox::information(NULL, "Fiber bundle saved to", filename); + if (m_GlobalTracker.IsNull() || (!(m_Controls->m_VisualizationCheckbox->isChecked() || m_Controls->m_VisualizeOnceButton->isChecked()) && m_ThreadIsRunning)) + return; + + if (m_Controls->m_VisualizeOnceButton->isChecked()) + m_Controls->m_VisualizeOnceButton->setChecked(false); + + vtkSmartPointer fiberBundle = m_GlobalTracker->GetFiberBundle(); + if ( fiberBundle->GetNumberOfLines()==0 ) + return; + m_FiberBundle = mitk::FiberBundleX::New(fiberBundle); + + if (m_FiberBundleNode.IsNotNull()){ + GetDefaultDataStorage()->Remove(m_FiberBundleNode); + m_FiberBundleNode = 0; } - catch (itk::ExceptionObject &ex) - { - QMessageBox::information(NULL, "Fiber bundle could not be saved", QString("%1\n%2\n%3\n%4\n%5\n%6").arg(ex.GetNameOfClass()).arg(ex.GetFile()).arg(ex.GetLine()).arg(ex.GetLocation()).arg(ex.what()).arg(ex.GetDescription())); + m_FiberBundleNode = mitk::DataNode::New(); + m_FiberBundleNode->SetData(m_FiberBundle); + + QString name(m_ImageNode->GetName().c_str()); + name += "_FiberBundle"; + m_FiberBundleNode->SetName(name.toStdString()); + m_FiberBundleNode->SetVisibility(true); - if(m_QBallImageNode.IsNull()) - GetDataStorage()->Add(m_FiberBundleNode); - else - GetDataStorage()->Add(m_FiberBundleNode, m_QBallImageNode); + if (!m_OutputFileName.isEmpty()) + { + QString filename = m_OutputFileName; + mitk::FiberBundleXWriter::Pointer writer = mitk::FiberBundleXWriter::New(); + writer->SetFileName(filename.toStdString()); + writer->SetInputFiberBundleX(m_FiberBundle.GetPointer()); + try + { + writer->Update(); + QMessageBox::information(NULL, "Fiber bundle saved to", filename); + } + catch (itk::ExceptionObject &ex) + { + QMessageBox::information(NULL, "Fiber bundle could not be saved", QString("%1\n%2\n%3\n%4\n%5\n%6").arg(ex.GetNameOfClass()).arg(ex.GetFile()).arg(ex.GetLine()).arg(ex.GetLocation()).arg(ex.what()).arg(ex.GetDescription())); + + if(m_ImageNode.IsNull()) + GetDataStorage()->Add(m_FiberBundleNode); + else + GetDataStorage()->Add(m_FiberBundleNode, m_ImageNode); + } + } + else { + if(m_ImageNode.IsNull()) + GetDataStorage()->Add(m_FiberBundleNode); + else + GetDataStorage()->Add(m_FiberBundleNode, m_ImageNode); } - } - else { - if(m_QBallImageNode.IsNull()) - GetDataStorage()->Add(m_FiberBundleNode); - else - GetDataStorage()->Add(m_FiberBundleNode, m_QBallImageNode); - } } void QmitkGibbsTrackingView::SetOutputFile() { - // SELECT FOLDER DIALOG - m_OutputFileName = QFileDialog::getSaveFileName(0, - tr("Set file name"), - QDir::currentPath()+"/FiberBundle.fib", - tr("Fiber Bundle (*.fib)") ); - if (m_OutputFileName.isEmpty()) - m_Controls->m_OutputFileLabel->setText("N/A"); - else - m_Controls->m_OutputFileLabel->setText(m_OutputFileName); + // SELECT FOLDER DIALOG + m_OutputFileName = QFileDialog::getSaveFileName(0, + tr("Set file name"), + QDir::currentPath()+"/FiberBundle.fib", + tr("Fiber Bundle (*.fib)") ); + if (m_OutputFileName.isEmpty()) + m_Controls->m_OutputFileLabel->setText("N/A"); + else + m_Controls->m_OutputFileLabel->setText(m_OutputFileName); } // save current tracking paramters as xml file (.gtp) void QmitkGibbsTrackingView::SaveTrackingParameters() { - TiXmlDocument documentXML; - TiXmlDeclaration* declXML = new TiXmlDeclaration( "1.0", "", "" ); - documentXML.LinkEndChild( declXML ); - - TiXmlElement* mainXML = new TiXmlElement("global_tracking_parameter_file"); - mainXML->SetAttribute("file_version", "0.1"); - documentXML.LinkEndChild(mainXML); - - TiXmlElement* paramXML = new TiXmlElement("parameter_set"); - paramXML->SetAttribute("iterations", QString::number(m_Iterations).toStdString()); - paramXML->SetAttribute("particle_length", QString::number((float)m_Controls->m_ParticleLengthSlider->value()/10).toStdString()); - paramXML->SetAttribute("particle_width", QString::number((float)m_Controls->m_ParticleWidthSlider->value()/10).toStdString()); - paramXML->SetAttribute("particle_weight", QString::number((float)m_Controls->m_ParticleWeightSlider->value()/10000).toStdString()); - paramXML->SetAttribute("temp_start", QString::number((float)m_Controls->m_StartTempSlider->value()/100).toStdString()); - paramXML->SetAttribute("temp_end", QString::number((float)m_Controls->m_EndTempSlider->value()/10000).toStdString()); - paramXML->SetAttribute("inexbalance", QString::number((float)m_Controls->m_InExBalanceSlider->value()/10).toStdString()); - paramXML->SetAttribute("fiber_length", QString::number(m_Controls->m_FiberLengthSlider->value()).toStdString()); - paramXML->SetAttribute("curvature_threshold", QString::number(m_Controls->m_CurvatureThresholdSlider->value()).toStdString()); - mainXML->LinkEndChild(paramXML); - QString filename = QFileDialog::getSaveFileName( - 0, - tr("Save Parameters"), - QDir::currentPath()+"/param.gtp", - tr("Global Tracking Parameters (*.gtp)") ); - - if(filename.isEmpty() || filename.isNull()) - return; - if(!filename.endsWith(".gtp")) - filename += ".gtp"; - documentXML.SaveFile( filename.toStdString() ); + TiXmlDocument documentXML; + TiXmlDeclaration* declXML = new TiXmlDeclaration( "1.0", "", "" ); + documentXML.LinkEndChild( declXML ); + + TiXmlElement* mainXML = new TiXmlElement("global_tracking_parameter_file"); + mainXML->SetAttribute("file_version", "0.1"); + documentXML.LinkEndChild(mainXML); + + TiXmlElement* paramXML = new TiXmlElement("parameter_set"); + paramXML->SetAttribute("iterations", QString::number(m_Iterations).toStdString()); + paramXML->SetAttribute("particle_length", QString::number((float)m_Controls->m_ParticleLengthSlider->value()/10).toStdString()); + paramXML->SetAttribute("particle_width", QString::number((float)m_Controls->m_ParticleWidthSlider->value()/10).toStdString()); + paramXML->SetAttribute("particle_weight", QString::number((float)m_Controls->m_ParticleWeightSlider->value()/10000).toStdString()); + paramXML->SetAttribute("temp_start", QString::number((float)m_Controls->m_StartTempSlider->value()/100).toStdString()); + paramXML->SetAttribute("temp_end", QString::number((float)m_Controls->m_EndTempSlider->value()/10000).toStdString()); + paramXML->SetAttribute("inexbalance", QString::number((float)m_Controls->m_InExBalanceSlider->value()/10).toStdString()); + paramXML->SetAttribute("fiber_length", QString::number(m_Controls->m_FiberLengthSlider->value()).toStdString()); + paramXML->SetAttribute("curvature_threshold", QString::number(m_Controls->m_CurvatureThresholdSlider->value()).toStdString()); + mainXML->LinkEndChild(paramXML); + QString filename = QFileDialog::getSaveFileName( + 0, + tr("Save Parameters"), + QDir::currentPath()+"/param.gtp", + tr("Global Tracking Parameters (*.gtp)") ); + + if(filename.isEmpty() || filename.isNull()) + return; + if(!filename.endsWith(".gtp")) + filename += ".gtp"; + documentXML.SaveFile( filename.toStdString() ); } void QmitkGibbsTrackingView::UpdateIteraionsGUI(unsigned long iterations) { - switch(iterations) - { - case 10000: - m_Controls->m_IterationsSlider->setValue(0); - m_Controls->m_IterationsLabel->setText("Iterations: 10^4"); - break; - case 50000: - m_Controls->m_IterationsSlider->setValue(1); - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^4"); - break; - case 100000: - m_Controls->m_IterationsSlider->setValue(2); - m_Controls->m_IterationsLabel->setText("Iterations: 10^5"); - break; - case 500000: - m_Controls->m_IterationsSlider->setValue(3); - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^5"); - break; - case 1000000: - m_Controls->m_IterationsSlider->setValue(4); - m_Controls->m_IterationsLabel->setText("Iterations: 10^6"); - break; - case 5000000: - m_Controls->m_IterationsSlider->setValue(5); - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^6"); - break; - case 10000000: - m_Controls->m_IterationsSlider->setValue(6); - m_Controls->m_IterationsLabel->setText("Iterations: 10^7"); - break; - case 50000000: - m_Controls->m_IterationsSlider->setValue(7); - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^7"); - break; - case 100000000: - m_Controls->m_IterationsSlider->setValue(8); - m_Controls->m_IterationsLabel->setText("Iterations: 10^8"); - break; - case 500000000: - m_Controls->m_IterationsSlider->setValue(9); - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^8"); - break; - case 1000000000: - m_Controls->m_IterationsSlider->setValue(10); - m_Controls->m_IterationsLabel->setText("Iterations: 10^9"); - break; - case 5000000000: - m_Controls->m_IterationsSlider->setValue(11); - m_Controls->m_IterationsLabel->setText("Iterations: 5x10^9"); - break; - } + switch(iterations) + { + case 10000: + m_Controls->m_IterationsSlider->setValue(0); + m_Controls->m_IterationsLabel->setText("Iterations: 10^4"); + break; + case 50000: + m_Controls->m_IterationsSlider->setValue(1); + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^4"); + break; + case 100000: + m_Controls->m_IterationsSlider->setValue(2); + m_Controls->m_IterationsLabel->setText("Iterations: 10^5"); + break; + case 500000: + m_Controls->m_IterationsSlider->setValue(3); + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^5"); + break; + case 1000000: + m_Controls->m_IterationsSlider->setValue(4); + m_Controls->m_IterationsLabel->setText("Iterations: 10^6"); + break; + case 5000000: + m_Controls->m_IterationsSlider->setValue(5); + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^6"); + break; + case 10000000: + m_Controls->m_IterationsSlider->setValue(6); + m_Controls->m_IterationsLabel->setText("Iterations: 10^7"); + break; + case 50000000: + m_Controls->m_IterationsSlider->setValue(7); + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^7"); + break; + case 100000000: + m_Controls->m_IterationsSlider->setValue(8); + m_Controls->m_IterationsLabel->setText("Iterations: 10^8"); + break; + case 500000000: + m_Controls->m_IterationsSlider->setValue(9); + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^8"); + break; + case 1000000000: + m_Controls->m_IterationsSlider->setValue(10); + m_Controls->m_IterationsLabel->setText("Iterations: 10^9"); + break; + case 5000000000: + m_Controls->m_IterationsSlider->setValue(11); + m_Controls->m_IterationsLabel->setText("Iterations: 5x10^9"); + break; + } } // load current tracking paramters from xml file (.gtp) void QmitkGibbsTrackingView::LoadTrackingParameters() { - QString filename = QFileDialog::getOpenFileName(0, tr("Load Parameters"), QDir::currentPath(), tr("Global Tracking Parameters (*.gtp)") ); - if(filename.isEmpty() || filename.isNull()) - return; - - TiXmlDocument doc( filename.toStdString() ); - doc.LoadFile(); - - TiXmlHandle hDoc(&doc); - TiXmlElement* pElem; - TiXmlHandle hRoot(0); - - pElem = hDoc.FirstChildElement().Element(); - hRoot = TiXmlHandle(pElem); - pElem = hRoot.FirstChildElement("parameter_set").Element(); - - QString iterations(pElem->Attribute("iterations")); - m_Iterations = iterations.toULong(); - UpdateIteraionsGUI(m_Iterations); - - QString particleLength(pElem->Attribute("particle_length")); - float pLength = particleLength.toFloat(); - QString particleWidth(pElem->Attribute("particle_width")); - float pWidth = particleWidth.toFloat(); - - if (pLength==0) - m_Controls->m_ParticleLengthLabel->setText("auto"); - else - m_Controls->m_ParticleLengthLabel->setText(particleLength+" mm"); - if (pWidth==0) - m_Controls->m_ParticleWidthLabel->setText("auto"); - else - m_Controls->m_ParticleWidthLabel->setText(particleWidth+" mm"); - - m_Controls->m_ParticleWidthSlider->setValue(pWidth*10); - m_Controls->m_ParticleLengthSlider->setValue(pLength*10); - - QString partWeight(pElem->Attribute("particle_weight")); - m_Controls->m_ParticleWeightSlider->setValue(partWeight.toFloat()*10000); - m_Controls->m_ParticleWeightLabel->setText(partWeight); - - QString startTemp(pElem->Attribute("temp_start")); - m_Controls->m_StartTempSlider->setValue(startTemp.toFloat()*100); - m_Controls->m_StartTempLabel->setText(startTemp); - - QString endTemp(pElem->Attribute("temp_end")); - m_Controls->m_EndTempSlider->setValue(endTemp.toFloat()*10000); - m_Controls->m_EndTempLabel->setText(endTemp); - - QString inExBalance(pElem->Attribute("inexbalance")); - m_Controls->m_InExBalanceSlider->setValue(inExBalance.toFloat()*10); - m_Controls->m_InExBalanceLabel->setText(inExBalance); - - QString fiberLength(pElem->Attribute("fiber_length")); - m_Controls->m_FiberLengthSlider->setValue(fiberLength.toInt()); - m_Controls->m_FiberLengthLabel->setText(fiberLength+"mm"); - - QString curvThres(pElem->Attribute("curvature_threshold")); - m_Controls->m_CurvatureThresholdSlider->setValue(curvThres.toInt()); - m_Controls->m_CurvatureThresholdLabel->setText(curvThres+"°"); + QString filename = QFileDialog::getOpenFileName(0, tr("Load Parameters"), QDir::currentPath(), tr("Global Tracking Parameters (*.gtp)") ); + if(filename.isEmpty() || filename.isNull()) + return; + + TiXmlDocument doc( filename.toStdString() ); + doc.LoadFile(); + + TiXmlHandle hDoc(&doc); + TiXmlElement* pElem; + TiXmlHandle hRoot(0); + + pElem = hDoc.FirstChildElement().Element(); + hRoot = TiXmlHandle(pElem); + pElem = hRoot.FirstChildElement("parameter_set").Element(); + + QString iterations(pElem->Attribute("iterations")); + m_Iterations = iterations.toULong(); + UpdateIteraionsGUI(m_Iterations); + + QString particleLength(pElem->Attribute("particle_length")); + float pLength = particleLength.toFloat(); + QString particleWidth(pElem->Attribute("particle_width")); + float pWidth = particleWidth.toFloat(); + + if (pLength==0) + m_Controls->m_ParticleLengthLabel->setText("auto"); + else + m_Controls->m_ParticleLengthLabel->setText(particleLength+" mm"); + if (pWidth==0) + m_Controls->m_ParticleWidthLabel->setText("auto"); + else + m_Controls->m_ParticleWidthLabel->setText(particleWidth+" mm"); + + m_Controls->m_ParticleWidthSlider->setValue(pWidth*10); + m_Controls->m_ParticleLengthSlider->setValue(pLength*10); + + QString partWeight(pElem->Attribute("particle_weight")); + m_Controls->m_ParticleWeightSlider->setValue(partWeight.toFloat()*10000); + m_Controls->m_ParticleWeightLabel->setText(partWeight); + + QString startTemp(pElem->Attribute("temp_start")); + m_Controls->m_StartTempSlider->setValue(startTemp.toFloat()*100); + m_Controls->m_StartTempLabel->setText(startTemp); + + QString endTemp(pElem->Attribute("temp_end")); + m_Controls->m_EndTempSlider->setValue(endTemp.toFloat()*10000); + m_Controls->m_EndTempLabel->setText(endTemp); + + QString inExBalance(pElem->Attribute("inexbalance")); + m_Controls->m_InExBalanceSlider->setValue(inExBalance.toFloat()*10); + m_Controls->m_InExBalanceLabel->setText(inExBalance); + + QString fiberLength(pElem->Attribute("fiber_length")); + m_Controls->m_FiberLengthSlider->setValue(fiberLength.toInt()); + m_Controls->m_FiberLengthLabel->setText(fiberLength+"mm"); + + QString curvThres(pElem->Attribute("curvature_threshold")); + m_Controls->m_CurvatureThresholdSlider->setValue(curvThres.toInt()); + m_Controls->m_CurvatureThresholdLabel->setText(curvThres+"°"); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.h index 74776790f4..cec1298106 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingView.h @@ -1,165 +1,169 @@ /*=================================================================== 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. ===================================================================*/ #ifndef QmitkGibbsTrackingView_h #define QmitkGibbsTrackingView_h #include #include #include "ui_QmitkGibbsTrackingViewControls.h" #include #include #include #include #include #include #include +#include +#include class QmitkGibbsTrackingView; class QmitkTrackingWorker : public QObject { Q_OBJECT public: QmitkTrackingWorker(QmitkGibbsTrackingView* view); public slots: void run(); private: QmitkGibbsTrackingView* m_View; }; /*! \brief QmitkGibbsTrackingView \warning This application module is not yet documented. Use "svn blame/praise/annotate" and ask the author to provide basic documentation. \sa QmitkFunctionality \ingroup Functionalities */ typedef itk::Image< float, 3 > FloatImageType; namespace itk { -template +template class GibbsTrackingFilter; } class QmitkGibbsTrackingView : 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: - typedef itk::Image MaskImgType; - - typedef itk::Vector OdfVectorType; - typedef itk::Image ItkQBallImgType; - - typedef itk::GibbsTrackingFilter GibbsTrackingFilterType; + typedef itk::Image ItkFloatImageType; + typedef itk::Vector OdfVectorType; + typedef itk::Image ItkQBallImgType; + typedef itk::Image< itk::DiffusionTensor3D, 3 > ItkTensorImage; + typedef itk::GibbsTrackingFilter< ItkQBallImgType > GibbsTrackingFilterType; static const std::string VIEW_ID; QmitkGibbsTrackingView(); virtual ~QmitkGibbsTrackingView(); virtual void CreateQtPartControl(QWidget *parent); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); signals: protected slots: void StartGibbsTracking(); void StopGibbsTracking(); void AfterThread(); void BeforeThread(); void TimerUpdate(); void SetMask(); void AdvancedSettings(); void SaveTrackingParameters(); void LoadTrackingParameters(); void SetIterations(int value); void SetParticleWidth(int value); void SetParticleLength(int value); void SetInExBalance(int value); void SetFiberLength(int value); void SetParticleWeight(int value); void SetStartTemp(int value); void SetEndTemp(int value); void SetCurvatureThreshold(int value); + void SetRandomSeed(int value); void SetOutputFile(); private: // Visualization & GUI void GenerateFiberBundle(bool smoothFibers); void UpdateGUI(); void UpdateTrackingStatus(); /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); template void CastToFloat(InputImageType* image, typename mitk::Image::Pointer outImage); void UpdateIteraionsGUI(unsigned long iterations); Ui::QmitkGibbsTrackingViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; // data objects mitk::FiberBundleX::Pointer m_FiberBundle; - MaskImgType::Pointer m_MaskImage; + ItkFloatImageType::Pointer m_MaskImage; + mitk::TensorImage::Pointer m_TensorImage; mitk::QBallImage::Pointer m_QBallImage; ItkQBallImgType::Pointer m_ItkQBallImage; + ItkTensorImage::Pointer m_ItkTensorImage; // data nodes - mitk::DataNode::Pointer m_QBallImageNode; + mitk::DataNode::Pointer m_ImageNode; mitk::DataNode::Pointer m_MaskImageNode; mitk::DataNode::Pointer m_FiberBundleNode; // flags etc. bool m_ThreadIsRunning; QTimer* m_TrackingTimer; QTime m_TrackingTime; unsigned long m_ElapsedTime; unsigned long m_Iterations; int m_LastStep; QString m_OutputFileName; // global tracker and friends itk::SmartPointer m_GlobalTracker; QmitkTrackingWorker m_TrackingWorker; QThread m_TrackingThread; friend class QmitkTrackingWorker; }; #endif // _QMITKGibbsTrackingVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingViewControls.ui index b99d3213ef..239322a77f 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkGibbsTrackingViewControls.ui @@ -1,1045 +1,1073 @@ QmitkGibbsTrackingViewControls 0 0 463 1011 0 0 0 0 QmitkTemplate 0 9 3 9 3 Data - Q-Ball Image: + Q-Ball/Tensor Image: Mandatory input - Mask Image: Optional input to limit the algorithms search space. - Parameters 0 Iterations: 10^7 Specify number of iterations for the tracking algorithm. 10 6 Qt::Horizontal QSlider::TicksBelow true Activate continuous visualization of intermediate results. Visualize Tractography true Visualize intermediate result. :/QmitkDiffusionImaging/Refresh_48.png:/QmitkDiffusionImaging/Refresh_48.png true Advanced Settings Output File: QFrame::NoFrame QFrame::Plain 0 0 0 Select output file name and folder. ... N/A true true QFrame::StyledPanel QFrame::Raised 9 0 9 0 4 - - + + - auto - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Particle Width: - - + + - auto + 0 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - auto = 0.5 * min. spacing; sigma - - - 100 - - - 1 - - - Qt::Horizontal + + + + 0.1 - - QSlider::NoTicks + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + - auto - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Particle Weight: - - - - automatic estimation from gfa map and q-ball data. - + + - 0 + 1 - 1000 + 99 1 - 0 + 10 Qt::Horizontal - - true - QSlider::NoTicks - - + + - 0.1 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Start Temperature: - - + + + + automatic estimation from gfa map and q-ball data. + - 1 + 0 - 100 + 1000 1 - 10 + 0 Qt::Horizontal - - false - - false + true QSlider::NoTicks - - - - 0.001 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + IE Bias < 0 < EE Bias - - - - - 1 + -50 - 99 + 50 1 - - 10 - Qt::Horizontal QSlider::NoTicks - - + + + + 0.001 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + - 0 - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Curvature Threshold: - - + + - IE Bias < 0 < EE Bias - - - -50 - - - 50 + - - 1 + + - - Qt::Horizontal + + - - QSlider::NoTicks + + Balance In/Ex Energy: - - + + - 40mm + 45° Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + - Only fibers longer than specified are accepted. + auto = 0.5 * min. spacing; sigma 100 1 - - 40 - Qt::Horizontal QSlider::NoTicks Particle Length: - - + + - Particle Width: + auto + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + - Particle Weight: + Min. Fiber Length: - - - - Start Temperature: + + + + Only fibers longer than specified are accepted. + + + 500 + + + 1 + + + 40 + + + Qt::Horizontal + + + QSlider::NoTicks + + + + + + + Allow only fiber curvature values smaller than the selected threshold. + + + 180 + + + 1 + + + 45 + + + Qt::Horizontal + + + QSlider::NoTicks End Temperature: - - + + - Balance In/Ex Energy: + 40mm + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + - Min. Fiber Length: + auto + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - true + + + + 1 - - Use mean subtracted ODFs (recommended). + + 100 - - Subtract ODF Mean + + 1 - - true + + 10 - - - - Qt::Horizontal - - QSizePolicy::Fixed + + false + + + false - - - 60 - 20 - + + QSlider::NoTicks - + - - + + - Curvature Threshold: + auto + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + + + auto = 1.5 * min. spacing; l + + + 100 + + + 1 + + + Qt::Horizontal + + + QSlider::NoTicks + + + + + - 45° - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Random Seed - - + + - Allow only fiber curvature values smaller than the selected threshold. - - - 180 + - - 1 + + - - 45 + + - - Qt::Horizontal + + auto - - QSlider::NoTicks + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + auto = 1.5 * min. spacing; l + + -1 + 100 1 + + -1 + Qt::Horizontal QSlider::NoTicks QFrame::NoFrame QFrame::Plain 0 0 0 true Save current parameters as xml (.gtp) Qt::LeftToRight Save Parameters :/qmitk/btnMoveDown.png:/qmitk/btnMoveDown.png true Load parameters from xml file (.gtp) Qt::LeftToRight Load Parameters :/qmitk/btnMoveUp.png:/qmitk/btnMoveUp.png false No Q-Ball image selected. Qt::LeftToRight Start Tractography :/qmitk/play.xpm:/qmitk/play.xpm false Qt::LeftToRight Stop Tractography :/qmitk/stop.xpm:/qmitk/stop.xpm Monitor Progress: - Will only be updated if tracking is visualized Will only be updated if tracking is visualized Accepted Fibers: Connections: Particles: Proposal Acceptance Rate: Tracking Time: Will only be updated if tracking is visualized - - - - - Qt::Vertical QSizePolicy::Expanding 0 0 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp index ab313319e9..73f43cddda 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.cpp @@ -1,459 +1,506 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "QmitkPreprocessingView.h" #include "mitkDiffusionImagingConfigure.h" // qt includes #include // itk includes #include "itkTimeProbe.h" #include "itkB0ImageExtractionImageFilter.h" #include "itkB0ImageExtractionToSeparateImageFilter.h" #include "itkBrainMaskExtractionImageFilter.h" #include "itkCastImageFilter.h" #include "itkVectorContainer.h" #include // mitk includes #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include #include #include const std::string QmitkPreprocessingView::VIEW_ID = "org.mitk.views.preprocessing"; #define DI_INFO MITK_INFO("DiffusionImaging") typedef float TTensorPixelType; QmitkPreprocessingView::QmitkPreprocessingView() : QmitkFunctionality(), m_Controls(NULL), m_MultiWidget(NULL), m_DiffusionImage(NULL) { } QmitkPreprocessingView::QmitkPreprocessingView(const QmitkPreprocessingView& other) { Q_UNUSED(other) throw std::runtime_error("Copy constructor not implemented"); } QmitkPreprocessingView::~QmitkPreprocessingView() { } void QmitkPreprocessingView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkPreprocessingViewControls; m_Controls->setupUi(parent); this->CreateConnections(); m_Controls->m_MeasurementFrameTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch); m_Controls->m_MeasurementFrameTable->verticalHeader()->setResizeMode(QHeaderView::Stretch); } } void QmitkPreprocessingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkPreprocessingView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkPreprocessingView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_ButtonAverageGradients), SIGNAL(clicked()), this, SLOT(AverageGradients()) ); connect( (QObject*)(m_Controls->m_ButtonExtractB0), SIGNAL(clicked()), this, SLOT(ExtractB0()) ); connect( (QObject*)(m_Controls->m_ButtonBrainMask), SIGNAL(clicked()), this, SLOT(BrainMask()) ); connect( (QObject*)(m_Controls->m_ModifyMeasurementFrame), SIGNAL(clicked()), this, SLOT(DoApplyMesurementFrame()) ); connect( (QObject*)(m_Controls->m_ReduceGradientsButton), SIGNAL(clicked()), this, SLOT(DoReduceGradientDirections()) ); connect( (QObject*)(m_Controls->m_ShowGradientsButton), SIGNAL(clicked()), this, SLOT(DoShowGradientDirections()) ); connect( (QObject*)(m_Controls->m_MirrorGradientToHalfSphereButton), SIGNAL(clicked()), this, SLOT(DoHalfSphereGradientDirections()) ); } } void QmitkPreprocessingView::OnSelectionChanged( std::vector nodes ) { bool foundDwiVolume = false; m_DiffusionImage = NULL; m_SelectedDiffusionNodes = mitk::DataStorage::SetOfObjects::New(); // iterate selection for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if( node.IsNotNull() && dynamic_cast*>(node->GetData()) ) { foundDwiVolume = true; m_DiffusionImage = dynamic_cast*>(node->GetData()); m_Controls->m_DiffusionImageLabel->setText(node->GetName().c_str()); m_SelectedDiffusionNodes->push_back(node); } } m_Controls->m_ButtonBrainMask->setEnabled(foundDwiVolume); m_Controls->m_ButtonAverageGradients->setEnabled(foundDwiVolume); m_Controls->m_ButtonExtractB0->setEnabled(foundDwiVolume); m_Controls->m_CheckExtractAll->setEnabled(foundDwiVolume); m_Controls->m_ModifyMeasurementFrame->setEnabled(foundDwiVolume); m_Controls->m_MeasurementFrameTable->setEnabled(foundDwiVolume); m_Controls->m_ReduceGradientsButton->setEnabled(foundDwiVolume); m_Controls->m_ShowGradientsButton->setEnabled(foundDwiVolume); m_Controls->m_MirrorGradientToHalfSphereButton->setEnabled(foundDwiVolume); if (foundDwiVolume) { vnl_matrix_fixed< double, 3, 3 > mf = m_DiffusionImage->GetMeasurementFrame(); for (int r=0; r<3; r++) for (int c=0; c<3; c++) { QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item(r,c); delete item; item = new QTableWidgetItem(); item->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); item->setText(QString::number(mf.get(r,c))); m_Controls->m_MeasurementFrameTable->setItem(r,c,item); } typedef mitk::DiffusionImage::BValueMap BValueMap; typedef mitk::DiffusionImage::BValueMap::iterator BValueMapIterator; BValueMap bValMap = m_DiffusionImage->GetB_ValueMap(); BValueMapIterator it = bValMap.begin(); m_Controls->m_BvalueTable->clear(); m_Controls->m_BvalueTable->setRowCount(bValMap.size() ); QStringList headerList; headerList << "b-Value" << "Number of gradients"; m_Controls->m_BvalueTable->setHorizontalHeaderLabels(headerList); + + + QCheckBox * tmp; + + foreach(QCheckBox * box, m_ReduceGradientCheckboxes) + { + m_Controls->m_ReduceSizeLayout->layout()->removeWidget(box); + delete box; + } + + m_ReduceGradientCheckboxes.clear(); + int i = 0 ; for(;it != bValMap.end(); it++) { m_Controls->m_BvalueTable->setItem(i,0,new QTableWidgetItem(QString::number(it->first))); m_Controls->m_BvalueTable->setItem(i,1,new QTableWidgetItem(QString::number(it->second.size()))); + + // Reduce Gradients GUI adaption + if(it->first != 0 && bValMap.size() > 2){ + tmp = new QCheckBox(QString::number(it->first) + " with " + QString::number(it->second.size()) + " directions"); + tmp->setEnabled(true); + tmp->setChecked(true); + tmp->setCheckable(true); + m_ReduceGradientCheckboxes.push_back(tmp); + m_Controls->m_ReduceSizeLayout->layout()->addWidget(tmp); + } i++; } + } else { for (int r=0; r<3; r++) for (int c=0; c<3; c++) { QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item(r,c); delete item; item = new QTableWidgetItem(); m_Controls->m_MeasurementFrameTable->setItem(r,c,item); } m_Controls->m_BvalueTable->clear(); m_Controls->m_BvalueTable->setRowCount(1); QStringList headerList; headerList << "b-Value" << "Number of gradients"; m_Controls->m_BvalueTable->setHorizontalHeaderLabels(headerList); m_Controls->m_BvalueTable->setItem(0,0,new QTableWidgetItem("-")); m_Controls->m_BvalueTable->setItem(0,1,new QTableWidgetItem("-")); m_Controls->m_DiffusionImageLabel->setText("-"); + + foreach(QCheckBox * box, m_ReduceGradientCheckboxes) + { + m_Controls->m_ReduceSizeLayout->layout()->removeWidget(box); + delete box; + } + m_ReduceGradientCheckboxes.clear(); } } void QmitkPreprocessingView::Activated() { QmitkFunctionality::Activated(); } void QmitkPreprocessingView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkPreprocessingView::DoHalfSphereGradientDirections() { if (m_DiffusionImage.IsNull()) return; GradientDirectionContainerType::Pointer gradientContainer = m_DiffusionImage->GetOriginalDirections(); for (int j=0; jSize(); j++) if (gradientContainer->at(j)[0]<0) gradientContainer->at(j) = -gradientContainer->at(j); } void QmitkPreprocessingView::DoApplyMesurementFrame() { if (m_DiffusionImage.IsNull()) return; vnl_matrix_fixed< double, 3, 3 > mf; for (int r=0; r<3; r++) for (int c=0; c<3; c++) { QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item(r,c); if (!item) return; mf[r][c] = item->text().toDouble(); } m_DiffusionImage->SetMeasurementFrame(mf); } void QmitkPreprocessingView::DoShowGradientDirections() { if (m_DiffusionImage.IsNull()) return; GradientDirectionContainerType::Pointer gradientContainer = m_DiffusionImage->GetOriginalDirections(); mitk::PointSet::Pointer pointset = mitk::PointSet::New(); for (int j=0; jSize(); j++) { mitk::Point3D p; vnl_vector_fixed< double, 3 > v = gradientContainer->at(j); if (fabs(v[0])>0.001 || fabs(v[1])>0.001 || fabs(v[2])>0.001) { p[0] = v[0]; p[1] = v[1]; p[2] = v[2]; pointset->InsertPoint(j, p); } } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(pointset); node->SetName("gradient directions"); node->SetProperty("pointsize", mitk::FloatProperty::New(0.05)); node->SetProperty("color", mitk::ColorProperty::New(1,0,0)); GetDefaultDataStorage()->Add(node); } void QmitkPreprocessingView::DoReduceGradientDirections() { if (m_DiffusionImage.IsNull()) return; typedef mitk::DiffusionImage DiffusionImageType; typedef itk::ReduceDirectionGradientsFilter FilterType; + typedef DiffusionImageType::BValueMap BValueMap; + + // GetShellSelection from GUI + BValueMap shellSlectionMap; + BValueMap originalShellMap = m_DiffusionImage->GetB_ValueMap(); + foreach(QCheckBox * box , m_ReduceGradientCheckboxes) + { + if(box->isChecked()){ + double BValue = (box->text().split(' ')).at(0).toDouble(); + shellSlectionMap[BValue] = originalShellMap[BValue]; + MITK_INFO << BValue; + } + } + + MITK_INFO << shellSlectionMap.size(); GradientDirectionContainerType::Pointer gradientContainer = m_DiffusionImage->GetOriginalDirections(); FilterType::Pointer filter = FilterType::New(); filter->SetInput(m_DiffusionImage->GetVectorImage()); filter->SetOriginalGradientDirections(gradientContainer); filter->SetNumGradientDirections(m_Controls->m_ReduceGradientsBox->value()); + filter->SetOriginalBValueMap(originalShellMap); + filter->SetShellSelectionBValueMap(shellSlectionMap); filter->Update(); DiffusionImageType::Pointer image = DiffusionImageType::New(); image->SetVectorImage( filter->GetOutput() ); image->SetB_Value(m_DiffusionImage->GetB_Value()); image->SetDirections(filter->GetGradientDirections()); image->SetOriginalDirections(filter->GetGradientDirections()); image->SetMeasurementFrame(m_DiffusionImage->GetMeasurementFrame()); image->InitializeFromVectorImage(); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( image ); imageNode->SetName("reduced_image"); GetDefaultDataStorage()->Add(imageNode); } void QmitkPreprocessingView::ExtractB0() { typedef mitk::DiffusionImage DiffusionImageType; typedef DiffusionImageType::GradientDirectionContainerType GradientContainerType; int nrFiles = m_SelectedDiffusionNodes->size(); if (!nrFiles) return; // call the extraction withou averaging if the check-box is checked if( this->m_Controls->m_CheckExtractAll->isChecked() ) { DoExtractBOWithoutAveraging(); return; } mitk::DataStorage::SetOfObjects::const_iterator itemiter( m_SelectedDiffusionNodes->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( m_SelectedDiffusionNodes->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { DiffusionImageType* vols = static_cast( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); // Extract image using found index typedef itk::B0ImageExtractionImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(vols->GetVectorImage()); filter->SetDirections(vols->GetDirections()); filter->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( filter->GetOutput() ); mitkImage->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( mitkImage ); node->SetProperty( "name", mitk::StringProperty::New(nodename + "_B0")); GetDefaultDataStorage()->Add(node); ++itemiter; } } void QmitkPreprocessingView::DoExtractBOWithoutAveraging() { // typedefs typedef mitk::DiffusionImage DiffusionImageType; typedef DiffusionImageType::GradientDirectionContainerType GradientContainerType; typedef itk::B0ImageExtractionToSeparateImageFilter< short, short> FilterType; // check number of selected objects, return if empty int nrFiles = m_SelectedDiffusionNodes->size(); if (!nrFiles) return; mitk::DataStorage::SetOfObjects::const_iterator itemiter( m_SelectedDiffusionNodes->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( m_SelectedDiffusionNodes->end() ); std::vector< mitk::DataNode::Pointer > nodes; while ( itemiter != itemiterend ) // for all items { DiffusionImageType* vols = static_cast( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); // Extract image using found index FilterType::Pointer filter = FilterType::New(); filter->SetInput(vols->GetVectorImage()); filter->SetDirections(vols->GetDirections()); filter->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( filter->GetOutput() ); mitkImage->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( mitkImage ); node->SetProperty( "name", mitk::StringProperty::New(nodename + "_B0_ALL")); GetDefaultDataStorage()->Add(node); ++itemiter; } } void QmitkPreprocessingView::AverageGradients() { int nrFiles = m_SelectedDiffusionNodes->size(); if (!nrFiles) return; mitk::DataStorage::SetOfObjects::const_iterator itemiter( m_SelectedDiffusionNodes->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( m_SelectedDiffusionNodes->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); vols->AverageRedundantGradients(m_Controls->m_Blur->value()); ++itemiter; } } void QmitkPreprocessingView::BrainMask() { int nrFiles = m_SelectedDiffusionNodes->size(); if (!nrFiles) return; mitk::DataStorage::SetOfObjects::const_iterator itemiter( m_SelectedDiffusionNodes->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( m_SelectedDiffusionNodes->end() ); while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = static_cast*>( (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); // Extract image using found index typedef itk::B0ImageExtractionImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(vols->GetVectorImage()); filter->SetDirections(vols->GetDirections()); typedef itk::CastImageFilter, itk::Image > CastFilterType; CastFilterType::Pointer castfilter = CastFilterType::New(); castfilter->SetInput(filter->GetOutput()); typedef itk::BrainMaskExtractionImageFilter MaskFilterType; MaskFilterType::Pointer maskfilter = MaskFilterType::New(); maskfilter->SetInput(castfilter->GetOutput()); maskfilter->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( maskfilter->GetOutput() ); mitkImage->SetVolume( maskfilter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( mitkImage ); node->SetProperty( "name", mitk::StringProperty::New(nodename + "_Mask")); GetDefaultDataStorage()->Add(node); ++itemiter; } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h index 4d6f101bb4..56a1e3565f 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingView.h @@ -1,107 +1,109 @@ /*=================================================================== 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 _QMITKPREPROCESSINGVIEW_H_INCLUDED #define _QMITKPREPROCESSINGVIEW_H_INCLUDED #include #include #include "ui_QmitkPreprocessingViewControls.h" #include "mitkDiffusionImage.h" typedef short DiffusionPixelType; struct PrpSelListener; /*! * \ingroup org_mitk_gui_qt_preprocessing_internal * * \brief QmitkPreprocessingView * * Document your class here. * * \sa QmitkFunctionality */ class QmitkPreprocessingView : public QmitkFunctionality { friend struct PrpSelListener; // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; typedef vnl_vector_fixed< double, 3 > GradientDirectionType; typedef itk::VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; QmitkPreprocessingView(); QmitkPreprocessingView(const QmitkPreprocessingView& other); virtual ~QmitkPreprocessingView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); static const int nrconvkernels; protected slots: void AverageGradients(); void ExtractB0(); void BrainMask(); void DoApplyMesurementFrame(); void DoReduceGradientDirections(); void DoShowGradientDirections(); void DoHalfSphereGradientDirections(); protected: /** Called by ExtractB0 if check-box activated, extracts all b0 images without averaging */ void DoExtractBOWithoutAveraging(); /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); Ui::QmitkPreprocessingViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); mitk::DiffusionImage::Pointer m_DiffusionImage; mitk::DataStorage::SetOfObjects::Pointer m_SelectedDiffusionNodes; + + QList m_ReduceGradientCheckboxes; }; #endif // _QMITKPREPROCESSINGVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui index 7fe02fe94a..b0b23f4c68 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkPreprocessingViewControls.ui @@ -1,555 +1,564 @@ QmitkPreprocessingViewControls 0 0 389 - 984 + 989 0 0 false QmitkPreprocessingViewControls true Data Diffusion Image: - 0 0 Info 0 0 Qt::ScrollBarAsNeeded Qt::ScrollBarAlwaysOff true 100 true false true b-Value Number of gradients false Generate pointset displaying the gradient vectors. Show gradients Qt::Horizontal 40 20 - + Reduce size 0 70 Multiple acquistions of one gradient direction can be averaged. Due to rounding errors, similar gradients often differ in the last decimal positions. The Merge radius allows to average them anyway by taking into account all directions within a certain radius. true QFrame::NoFrame QFrame::Raised 0 Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. 6 2.000000000000000 0.000100000000000 0.001000000000000 Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Accumulates the information that was acquired with multiple repetitions for one gradient. Vectors do not have to be precisely equal in order to be merged, if a "Merge radius" > 0 is configured. Merge radius false Average redundant gradients false Mirror all gradients around one axis. Mirror gradients to half sphere Qt::Horizontal + + + + false + + + Retain only the specified number of gradient directions and according image volumes. The retained directions are spread equally over the half sphere. + + + + + + + + + Reduce number of gradients + + + QFrame::NoFrame QFrame::Raised + + QFormLayout::FieldsStayAtSizeHint + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + 0 0 0 New number of gradients: 1 30 - - - - false - - - Retain only the specified number of gradient directions and according image volumes. The retained directions are spread equally over the half sphere. - - - - - - - - - Reduce number of gradients - - - Non diffusion weighted image 0 30 Average and extract all images that were acquired without diffusion weighting. true false Extract B0 Create a 3D+t data set containing all b0 images as timesteps Extract all B0 without averaging Brain mask false Estimate binary brain mask 0 0 Measurment frame Qt::Horizontal 40 20 false 0 0 10 10 IBeamCursor true Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff true false false true true false true true New Row New Row New Row New Column New Column New Column false Apply new mesurement frame Qt::Vertical 20 40 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp index b9fa6b6cd2..24b1b5255b 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.cpp @@ -1,794 +1,1076 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "QmitkQBallReconstructionView.h" #include "mitkDiffusionImagingConfigure.h" // qt includes #include // itk includes #include "itkTimeProbe.h" // mitk includes #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "QmitkDataStorageComboBox.h" #include "QmitkStdMultiWidget.h" #include "itkDiffusionQballReconstructionImageFilter.h" #include "itkAnalyticalDiffusionQballReconstructionImageFilter.h" +#include "itkDiffusionMultiShellQballReconstructionImageFilter.h" #include "itkVectorContainer.h" #include "mitkQBallImage.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "berryIStructuredSelection.h" #include "berryIWorkbenchWindow.h" #include "berryISelectionService.h" #include const std::string QmitkQBallReconstructionView::VIEW_ID = - "org.mitk.views.qballreconstruction"; + "org.mitk.views.qballreconstruction"; #define DI_INFO MITK_INFO("DiffusionImaging") typedef float TTensorPixelType; const int QmitkQBallReconstructionView::nrconvkernels = 252; + +struct QbrShellSelection +{ + QmitkQBallReconstructionView* m_View; + mitk::DataNode * m_Node; + std::string m_NodeName; + + std::vector m_CheckBoxes; + QLabel * m_Label; + + mitk::DiffusionImage * m_Image; + typedef mitk::DiffusionImage::BValueMap BValueMap; + + QbrShellSelection(QmitkQBallReconstructionView* view, mitk::DataNode * node) + : m_View(view), + m_Node(node), + m_NodeName(node->GetName()) + { + m_Image = dynamic_cast * > (node->GetData()); + if(!m_Image){MITK_INFO << "QmitkQBallReconstructionView::QbrShellSelection : fail to initialize DiffusionImage "; return;} + + GenerateCheckboxes(); + + } + + void GenerateCheckboxes() + { + BValueMap origMap = m_Image->GetB_ValueMap(); + BValueMap::iterator itStart = origMap.begin(); + itStart++; + BValueMap::iterator itEnd = origMap.end(); + + m_Label = new QLabel(m_NodeName.c_str()); + m_Label->setVisible(true); + m_View->m_Controls->m_QBallSelectionBox->layout()->addWidget(m_Label); + + for(BValueMap::iterator it = itStart ; it!= itEnd; it++) + { + QCheckBox * box = new QCheckBox(QString::number(it->first)); + m_View->m_Controls->m_QBallSelectionBox->layout()->addWidget(box); + + box->setChecked(true); + box->setCheckable(true); + // box->setVisible(true); + m_CheckBoxes.push_back(box); + } + } + + void SetVisible(bool vis) + { + foreach(QCheckBox * box, m_CheckBoxes) + { + box->setVisible(vis); + } + } + + BValueMap GetBValueSelctionMap() + { + BValueMap inputMap = m_Image->GetB_ValueMap(); + BValueMap outputMap; + + double val = 0; + + if(inputMap.find(0) == inputMap.end()){ + MITK_INFO << "QbrShellSelection: return empty BValueMap from GUI Selection"; + return outputMap; + }else{ + outputMap[val] = inputMap[val]; + MITK_INFO << val; + } + + foreach(QCheckBox * box, m_CheckBoxes) + { + if(box->isChecked()){ + val = box->text().toDouble(); + outputMap[val] = inputMap[val]; + MITK_INFO << val; + } + } + + return outputMap; + } + + ~QbrShellSelection() + { + m_View->m_Controls->m_QBallSelectionBox->layout()->removeWidget(m_Label); + delete m_Label; + + for(std::vector::iterator it = m_CheckBoxes.begin() ; it!= m_CheckBoxes.end(); it++) + { + m_View->m_Controls->m_QBallSelectionBox->layout()->removeWidget((*it)); + delete (*it); + } + m_CheckBoxes.clear(); + } +}; + using namespace berry; struct QbrSelListener : ISelectionListener { berryObjectMacro(QbrSelListener); QbrSelListener(QmitkQBallReconstructionView* view) { m_View = view; } void DoSelectionChanged(ISelection::ConstPointer selection) { // save current selection in member variable m_View->m_CurrentSelection = selection.Cast(); // do something with the selected items if(m_View->m_CurrentSelection) { bool foundDwiVolume = false; m_View->m_Controls->m_DiffusionImageLabel->setText("-"); + QString selected_images = ""; + + mitk::DataStorage::SetOfObjects::Pointer set = + mitk::DataStorage::SetOfObjects::New(); + + int at = 0; + // iterate selection for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); - i != m_View->m_CurrentSelection->End(); ++i) + i != m_View->m_CurrentSelection->End(); ++i) { // extract datatree node if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); - + mitk::DiffusionImage* diffusionImage; // only look at interesting types - if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) + if(diffusionImage = dynamic_cast * >(node->GetData())) { foundDwiVolume = true; - m_View->m_Controls->m_DiffusionImageLabel->setText(node->GetName().c_str()); + selected_images += QString(node->GetName().c_str()); + if(i + 1 != m_View->m_CurrentSelection->End()) + selected_images += "\n"; + set->InsertElement(at++, node); } } } - + m_View->GenerateShellSelectionUI(set); + m_View->m_Controls->m_DiffusionImageLabel->setText(selected_images); m_View->m_Controls->m_ButtonStandard->setEnabled(foundDwiVolume); } } void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) { // check, if selection comes from datamanager if (part) { QString partname(part->GetPartName().c_str()); if(partname.compare("Datamanager")==0) { // apply selection DoSelectionChanged(selection); } } } QmitkQBallReconstructionView* m_View; }; + +// --------------- QmitkQBallReconstructionView----------------- // + + QmitkQBallReconstructionView::QmitkQBallReconstructionView() -: QmitkFunctionality(), - m_Controls(NULL), - m_MultiWidget(NULL) + : QmitkFunctionality(), + m_Controls(NULL), + m_MultiWidget(NULL) { } QmitkQBallReconstructionView::QmitkQBallReconstructionView(const QmitkQBallReconstructionView& other) { Q_UNUSED(other); throw std::runtime_error("Copy constructor not implemented"); } -//void QmitkQBallReconstructionView::OpactiyChanged(int value) -//{ -// if (m_CurrentSelection) -// { -// if (mitk::DataNodeObject::Pointer nodeObj = m_CurrentSelection->Begin()->Cast()) -// { -// mitk::DataNode::Pointer node = nodeObj->GetDataNode(); -// if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) -// { -// node->SetIntProperty("DisplayChannel", value); -// mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -// } -// } -// } -//} -// -//void QmitkQBallReconstructionView::OpactiyActionChanged() -//{ -// if (m_CurrentSelection) -// { -// if (mitk::DataNodeObject::Pointer nodeObj = m_CurrentSelection->Begin()->Cast()) -// { -// mitk::DataNode::Pointer node = nodeObj->GetDataNode(); -// if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) -// { -// int displayChannel = 0.0; -// if(node->GetIntProperty("DisplayChannel", displayChannel)) -// { -// m_OpacitySlider->setValue(displayChannel); -// } -// } -// } -// } -// -// MITK_INFO << "changed"; -//} - QmitkQBallReconstructionView::~QmitkQBallReconstructionView() { this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->RemovePostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); } void QmitkQBallReconstructionView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkQBallReconstructionViewControls; m_Controls->setupUi(parent); this->CreateConnections(); QStringList items; items << "2" << "4" << "6" << "8" << "10" << "12"; m_Controls->m_QBallReconstructionMaxLLevelComboBox->addItems(items); m_Controls->m_QBallReconstructionMaxLLevelComboBox->setCurrentIndex(1); MethodChoosen(m_Controls->m_QBallReconstructionMethodComboBox->currentIndex()); #ifndef DIFFUSION_IMAGING_EXTENDED m_Controls->m_QBallReconstructionMethodComboBox->removeItem(3); #endif AdvancedCheckboxClicked(); - // define data type for combobox - //m_Controls->m_ImageSelector->SetDataStorage( this->GetDefaultDataStorage() ); - //m_Controls->m_ImageSelector->SetPredicate( mitk::NodePredicateDataType::New("DiffusionImage") ); } m_SelListener = berry::ISelectionListener::Pointer(new QbrSelListener(this)); this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); + this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkQBallReconstructionView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkQBallReconstructionView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkQBallReconstructionView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_ButtonStandard), SIGNAL(clicked()), this, SLOT(ReconstructStandard()) ); connect( (QObject*)(m_Controls->m_AdvancedCheckbox), SIGNAL(clicked()), this, SLOT(AdvancedCheckboxClicked()) ); connect( (QObject*)(m_Controls->m_QBallReconstructionMethodComboBox), SIGNAL(currentIndexChanged(int)), this, SLOT(MethodChoosen(int)) ); } } void QmitkQBallReconstructionView::OnSelectionChanged( std::vector nodes ) { } void QmitkQBallReconstructionView::Activated() { QmitkFunctionality::Activated(); berry::ISelection::ConstPointer sel( - this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); + this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkQBallReconstructionView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkQBallReconstructionView::ReconstructStandard() { int index = m_Controls->m_QBallReconstructionMethodComboBox->currentIndex(); #ifndef DIFFUSION_IMAGING_EXTENDED - if(index>=3) - { - index = index + 1; - } + if(index>=3) + { + index = index + 1; + } #endif switch(index) { case 0: - { - // Numerical - Reconstruct(0,0); - break; - } + { + // Numerical + Reconstruct(0,0); + break; + } case 1: - { - // Standard - Reconstruct(1,0); - break; - } + { + // Standard + Reconstruct(1,0); + break; + } case 2: - { - // Solid Angle - Reconstruct(1,6); - break; - } + { + // Solid Angle + Reconstruct(1,6); + break; + } case 3: - { - // Constrained Solid Angle - Reconstruct(1,7); - break; - } + { + // Constrained Solid Angle + Reconstruct(1,7); + break; + } case 4: - { - // ADC - Reconstruct(1,4); - break; - } + { + // ADC + Reconstruct(1,4); + break; + } case 5: - { - // Raw Signal - Reconstruct(1,5); - break; - } + { + // Raw Signal + Reconstruct(1,5); + break; + } + case 6: + { + // Q-Ball reconstruction + Reconstruct(2,0); + break; + } } } void QmitkQBallReconstructionView::MethodChoosen(int method) { + +#ifndef DIFFUSION_IMAGING_EXTENDED + if(method>=3) + { + method = method + 1; + } +#endif + + m_Controls->m_QBallSelectionBox->setHidden(true); + switch(method) { case 0: m_Controls->m_Description->setText("Numerical recon. (Tuch2004)"); break; case 1: m_Controls->m_Description->setText("Spherical harmonics recon. (Descoteaux2007)"); break; case 2: m_Controls->m_Description->setText("SH recon. with solid angle consideration (Aganj2009)"); break; case 3: m_Controls->m_Description->setText("SH solid angle with non-neg. constraint (Goh2009)"); break; case 4: m_Controls->m_Description->setText("SH recon. of the plain ADC-profiles"); break; case 5: m_Controls->m_Description->setText("SH recon. of the raw diffusion signal"); break; + case 6: + m_Controls->m_Description->setText("SH Multi q-Ball recon. of the multi q-Ball diffusion signal"); + m_Controls->m_QBallSelectionBox->setHidden(false); + break; } } void QmitkQBallReconstructionView::AdvancedCheckboxClicked() { bool check = m_Controls-> - m_AdvancedCheckbox->isChecked(); + m_AdvancedCheckbox->isChecked(); m_Controls->m_QBallReconstructionMaxLLevelTextLabel_2->setVisible(check); m_Controls->m_QBallReconstructionMaxLLevelComboBox->setVisible(check); m_Controls->m_QBallReconstructionLambdaTextLabel_2->setVisible(check); m_Controls->m_QBallReconstructionLambdaLineEdit->setVisible(check); m_Controls->m_QBallReconstructionThresholdLabel_2->setVisible(check); m_Controls->m_QBallReconstructionThreasholdEdit->setVisible(check); m_Controls->m_OutputB0Image->setVisible(check); m_Controls->label_2->setVisible(check); //m_Controls->textLabel1_2->setVisible(check); //m_Controls->m_QBallReconstructionLambdaStepLineEdit->setVisible(check); //m_Controls->textLabel1_3->setVisible(check); m_Controls->frame_2->setVisible(check); } void QmitkQBallReconstructionView::Reconstruct(int method, int normalization) { if (m_CurrentSelection) { mitk::DataStorage::SetOfObjects::Pointer set = - mitk::DataStorage::SetOfObjects::New(); + mitk::DataStorage::SetOfObjects::New(); int at = 0; for (IStructuredSelection::iterator i = m_CurrentSelection->Begin(); - i != m_CurrentSelection->End(); - ++i) + i != m_CurrentSelection->End(); + ++i) { if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); if(QString("DiffusionImage").compare(node->GetData()->GetNameOfClass())==0) { set->InsertElement(at++, node); } } } if(method == 0) { NumericalQBallReconstruction(set, normalization); } else { #if BOOST_VERSION / 100000 > 0 #if BOOST_VERSION / 100 % 1000 > 34 if(method == 1) { AnalyticalQBallReconstruction(set, normalization); } + if(method == 2) + { + MultiQBallReconstruction(set); + } #else std::cout << "ERROR: Boost 1.35 minimum required" << std::endl; QMessageBox::warning(NULL,"ERROR","Boost 1.35 minimum required"); #endif #else std::cout << "ERROR: Boost 1.35 minimum required" << std::endl; QMessageBox::warning(NULL,"ERROR","Boost 1.35 minimum required"); #endif } } } void QmitkQBallReconstructionView::NumericalQBallReconstruction - (mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization) +(mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector nodes; while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = - static_cast*>( - (*itemiter)->GetData()); + static_cast*>( + (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name", nodename); ++itemiter; // QBALL RECONSTRUCTION clock.Start(); MBI_INFO << "QBall reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( - "QBall reconstruction for %s", nodename.c_str()).toAscii()); + "QBall reconstruction for %s", nodename.c_str()).toAscii()); typedef itk::DiffusionQballReconstructionImageFilter - - QballReconstructionImageFilterType; + + QballReconstructionImageFilterType; QballReconstructionImageFilterType::Pointer filter = - QballReconstructionImageFilterType::New(); + QballReconstructionImageFilterType::New(); filter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage() ); filter->SetBValue(vols->GetB_Value()); filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->text().toFloat() ); switch(normalization) { case 0: - { - filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); - break; - } + { + filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); + break; + } case 1: - { - filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO_B_VALUE); - break; - } + { + filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO_B_VALUE); + break; + } case 2: - { - filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO); - break; - } + { + filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_B_ZERO); + break; + } case 3: - { - filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_NONE); - break; - } + { + filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_NONE); + break; + } default: - { - filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); - } + { + filter->SetNormalizationMethod(QballReconstructionImageFilterType::QBR_STANDARD); + } } filter->Update(); clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s." ; // ODFs TO DATATREE mitk::QBallImage::Pointer image = mitk::QBallImage::New(); image->InitializeByItk( filter->GetOutput() ); //image->SetImportVolume( filter->GetOutput()->GetBufferPointer(), 0, 0, mitk::Image::ImportMemoryManagementType::ManageMemory ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_QN%1").arg(normalization); SetDefaultNodeProperties(node, newname.toStdString()); nodes.push_back(node); // B-Zero TO DATATREE if(m_Controls->m_OutputB0Image->isChecked()) { mitk::Image::Pointer image4 = mitk::Image::New(); image4->InitializeByItk( filter->GetBZeroImage().GetPointer() ); image4->SetVolume( filter->GetBZeroImage()->GetBufferPointer() ); mitk::DataNode::Pointer node4=mitk::DataNode::New(); node4->SetData( image4 ); node4->SetProperty( "name", mitk::StringProperty::New( - QString(nodename.c_str()).append("_b0").toStdString()) ); + QString(nodename.c_str()).append("_b0").toStdString()) ); nodes.push_back(node4); } mitk::ProgressBar::GetInstance()->Progress(); } std::vector::iterator nodeIt; for(nodeIt = nodes.begin(); nodeIt != nodes.end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); m_MultiWidget->RequestUpdate(); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } void QmitkQBallReconstructionView::AnalyticalQBallReconstruction( - mitk::DataStorage::SetOfObjects::Pointer inImages, - int normalization) + mitk::DataStorage::SetOfObjects::Pointer inImages, + int normalization) { try { itk::TimeProbe clock; int nrFiles = inImages->size(); if (!nrFiles) return; std::vector lambdas; float minLambda = m_Controls->m_QBallReconstructionLambdaLineEdit->text().toFloat(); lambdas.push_back(minLambda); int nLambdas = lambdas.size(); QString status; mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles*nLambdas); mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); std::vector* nodes - = new std::vector(); + = new std::vector(); while ( itemiter != itemiterend ) // for all items { mitk::DiffusionImage* vols = - static_cast*>( - (*itemiter)->GetData()); + static_cast*>( + (*itemiter)->GetData()); std::string nodename; (*itemiter)->GetStringProperty("name",nodename); itemiter++; // QBALL RECONSTRUCTION clock.Start(); MBI_INFO << "QBall reconstruction "; mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( - "QBall reconstruction for %s", nodename.c_str()).toAscii()); + "QBall reconstruction for %s", nodename.c_str()).toAscii()); for(int i=0; im_QBallReconstructionMaxLLevelComboBox->currentIndex()) { case 0: - { - TemplatedAnalyticalQBallReconstruction<2>(vols, currentLambda, nodename, nodes, normalization); - break; - } + { + TemplatedAnalyticalQBallReconstruction<2>(vols, currentLambda, nodename, nodes, normalization); + break; + } case 1: - { - TemplatedAnalyticalQBallReconstruction<4>(vols, currentLambda, nodename, nodes, normalization); - break; - } + { + TemplatedAnalyticalQBallReconstruction<4>(vols, currentLambda, nodename, nodes, normalization); + break; + } case 2: - { - TemplatedAnalyticalQBallReconstruction<6>(vols, currentLambda, nodename, nodes, normalization); - break; - } + { + TemplatedAnalyticalQBallReconstruction<6>(vols, currentLambda, nodename, nodes, normalization); + break; + } case 3: - { - TemplatedAnalyticalQBallReconstruction<8>(vols, currentLambda, nodename, nodes, normalization); - break; - } + { + TemplatedAnalyticalQBallReconstruction<8>(vols, currentLambda, nodename, nodes, normalization); + break; + } case 4: - { - TemplatedAnalyticalQBallReconstruction<10>(vols, currentLambda, nodename, nodes, normalization); - break; - } + { + TemplatedAnalyticalQBallReconstruction<10>(vols, currentLambda, nodename, nodes, normalization); + break; + } case 5: - { - TemplatedAnalyticalQBallReconstruction<12>(vols, currentLambda, nodename, nodes, normalization); - break; - } + { + TemplatedAnalyticalQBallReconstruction<12>(vols, currentLambda, nodename, nodes, normalization); + break; + } } clock.Stop(); MBI_DEBUG << "took " << clock.GetMeanTime() << "s." ; mitk::ProgressBar::GetInstance()->Progress(); } } std::vector::iterator nodeIt; for(nodeIt = nodes->begin(); nodeIt != nodes->end(); ++nodeIt) GetDefaultDataStorage()->Add(*nodeIt); m_MultiWidget->RequestUpdate(); mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); } catch (itk::ExceptionObject &ex) { MBI_INFO << ex ; return ; } } template void QmitkQBallReconstructionView::TemplatedAnalyticalQBallReconstruction( - mitk::DiffusionImage* vols, float lambda, - std::string nodename, std::vector* nodes, - int normalization) + mitk::DiffusionImage* vols, float lambda, + std::string nodename, std::vector* nodes, + int normalization) { typedef itk::AnalyticalDiffusionQballReconstructionImageFilter - FilterType; + FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage() ); filter->SetBValue(vols->GetB_Value()); filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->text().toFloat() ); filter->SetLambda(lambda); switch(normalization) { case 0: - { - filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); + break; + } case 1: - { - filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO_B_VALUE); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO_B_VALUE); + break; + } case 2: - { - filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_B_ZERO); + break; + } case 3: - { - filter->SetNormalizationMethod(FilterType::QBAR_NONE); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_NONE); + break; + } case 4: - { - filter->SetNormalizationMethod(FilterType::QBAR_ADC_ONLY); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_ADC_ONLY); + break; + } case 5: - { - filter->SetNormalizationMethod(FilterType::QBAR_RAW_SIGNAL); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_RAW_SIGNAL); + break; + } case 6: - { - filter->SetNormalizationMethod(FilterType::QBAR_SOLID_ANGLE); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_SOLID_ANGLE); + break; + } case 7: - { - filter->SetNormalizationMethod(FilterType::QBAR_NONNEG_SOLID_ANGLE); - break; - } + { + filter->SetNormalizationMethod(FilterType::QBAR_NONNEG_SOLID_ANGLE); + break; + } default: - { - filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); - } + { + filter->SetNormalizationMethod(FilterType::QBAR_STANDARD); + } } filter->Update(); // ODFs TO DATATREE mitk::QBallImage::Pointer image = mitk::QBallImage::New(); image->InitializeByItk( filter->GetOutput() ); image->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer node=mitk::DataNode::New(); node->SetData( image ); QString newname; newname = newname.append(nodename.c_str()); newname = newname.append("_QA%1").arg(normalization); SetDefaultNodeProperties(node, newname.toStdString()); nodes->push_back(node); -// mitk::Image::Pointer image5 = mitk::Image::New(); -// image5->InitializeByItk( filter->GetODFSumImage().GetPointer() ); -// image5->SetVolume( filter->GetODFSumImage()->GetBufferPointer() ); -// mitk::DataNode::Pointer node5=mitk::DataNode::New(); -// node5->SetData( image5 ); -// node5->SetProperty( "name", mitk::StringProperty::New( -// QString(nodename.c_str()).append("_ODF").toStdString()) ); -// nodes->push_back(node5); + // mitk::Image::Pointer image5 = mitk::Image::New(); + // image5->InitializeByItk( filter->GetODFSumImage().GetPointer() ); + // image5->SetVolume( filter->GetODFSumImage()->GetBufferPointer() ); + // mitk::DataNode::Pointer node5=mitk::DataNode::New(); + // node5->SetData( image5 ); + // node5->SetProperty( "name", mitk::StringProperty::New( + // QString(nodename.c_str()).append("_ODF").toStdString()) ); + // nodes->push_back(node5); // B-Zero TO DATATREE if(m_Controls->m_OutputB0Image->isChecked()) { mitk::Image::Pointer image4 = mitk::Image::New(); image4->InitializeByItk( filter->GetBZeroImage().GetPointer() ); image4->SetVolume( filter->GetBZeroImage()->GetBufferPointer() ); mitk::DataNode::Pointer node4=mitk::DataNode::New(); node4->SetData( image4 ); node4->SetProperty( "name", mitk::StringProperty::New( - QString(nodename.c_str()).append("_b0").toStdString()) ); + QString(nodename.c_str()).append("_b0").toStdString()) ); nodes->push_back(node4); } + } +void QmitkQBallReconstructionView::MultiQBallReconstruction( + mitk::DataStorage::SetOfObjects::Pointer inImages) +{ + try + { + itk::TimeProbe clock; + + int nrFiles = inImages->size(); + if (!nrFiles) return; + + std::vector lambdas; + float minLambda = m_Controls->m_QBallReconstructionLambdaLineEdit->text().toFloat(); + lambdas.push_back(minLambda); + int nLambdas = lambdas.size(); + + + QString status; + mitk::ProgressBar::GetInstance()->AddStepsToDo(nrFiles*nLambdas); + + mitk::DataStorage::SetOfObjects::const_iterator itemiter( inImages->begin() ); + mitk::DataStorage::SetOfObjects::const_iterator itemiterend( inImages->end() ); + + std::vector* nodes + = new std::vector(); + while ( itemiter != itemiterend ) // for all items + { + + mitk::DiffusionImage* vols = + static_cast*>( + (*itemiter)->GetData()); + + std::string nodename; + (*itemiter)->GetStringProperty("name",nodename); + itemiter++; + + // QBALL RECONSTRUCTION + clock.Start(); + MBI_INFO << "QBall reconstruction "; + mitk::StatusBar::GetInstance()->DisplayText(status.sprintf( + "QBall reconstruction for %s", nodename.c_str()).toAscii()); + for(int i=0; im_QBallReconstructionMaxLLevelComboBox->currentIndex()) + { + case 0: + { + TemplatedMultiQBallReconstruction<2>(vols, currentLambda, nodename, nodes); + break; + } + case 1: + { + TemplatedMultiQBallReconstruction<4>(vols, currentLambda, nodename, nodes); + break; + } + case 2: + { + TemplatedMultiQBallReconstruction<6>(vols, currentLambda, nodename, nodes); + break; + } + case 3: + { + TemplatedMultiQBallReconstruction<8>(vols, currentLambda, nodename, nodes); + break; + } + case 4: + { + TemplatedMultiQBallReconstruction<10>(vols, currentLambda, nodename, nodes); + break; + } + case 5: + { + TemplatedMultiQBallReconstruction<12>(vols, currentLambda, nodename, nodes); + break; + } + } + + clock.Stop(); + MBI_DEBUG << "took " << clock.GetMeanTime() << "s." ; + mitk::ProgressBar::GetInstance()->Progress(); + + } + } + + std::vector::iterator nodeIt; + for(nodeIt = nodes->begin(); nodeIt != nodes->end(); ++nodeIt) + GetDefaultDataStorage()->Add(*nodeIt); + + m_MultiWidget->RequestUpdate(); + + mitk::StatusBar::GetInstance()->DisplayText(status.sprintf("Finished Processing %d Files", nrFiles).toAscii()); + + } + catch (itk::ExceptionObject &ex) + { + MBI_INFO << ex ; + return ; + } +} + +template +void QmitkQBallReconstructionView::TemplatedMultiQBallReconstruction( + mitk::DiffusionImage* vols, float lambda, + std::string nodename, std::vector* nodes) +{ + typedef itk::DiffusionMultiShellQballReconstructionImageFilter + FilterType; + typename FilterType::Pointer filter = FilterType::New(); + + filter->SetBValueMap(m_ShellSelectorMap[nodename]->GetBValueSelctionMap()); + filter->SetGradientImage( vols->GetDirections(), vols->GetVectorImage(), vols->GetB_Value() ); + + //filter->SetBValue(vols->GetB_Value()); + filter->SetThreshold( m_Controls->m_QBallReconstructionThreasholdEdit->text().toFloat() ); + filter->SetLambda(lambda); + + + filter->Update(); + + // ODFs TO DATATREE + mitk::QBallImage::Pointer image = mitk::QBallImage::New(); + image->InitializeByItk( filter->GetOutput() ); + image->SetVolume( filter->GetOutput()->GetBufferPointer() ); + mitk::DataNode::Pointer node=mitk::DataNode::New(); + node->SetData( image ); + QString newname; + newname = newname.append(nodename.c_str()); + newname = newname.append("_QAMultiShell"); + SetDefaultNodeProperties(node, newname.toStdString()); + nodes->push_back(node); + + // B-Zero TO DATATREE + if(m_Controls->m_OutputB0Image->isChecked()) + { + mitk::Image::Pointer image4 = mitk::Image::New(); + image4->InitializeByItk( filter->GetBZeroImage().GetPointer() ); + image4->SetVolume( filter->GetBZeroImage()->GetBufferPointer() ); + mitk::DataNode::Pointer node4=mitk::DataNode::New(); + node4->SetData( image4 ); + node4->SetProperty( "name", mitk::StringProperty::New( + QString(nodename.c_str()).append("_b0").toStdString()) ); + nodes->push_back(node4); + } + + +} void QmitkQBallReconstructionView::SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name) { node->SetProperty( "ShowMaxNumber", mitk::IntProperty::New( 500 ) ); node->SetProperty( "Scaling", mitk::FloatProperty::New( 1.0 ) ); node->SetProperty( "Normalization", mitk::OdfNormalizationMethodProperty::New()); node->SetProperty( "ScaleBy", mitk::OdfScaleByProperty::New()); node->SetProperty( "IndexParam1", mitk::FloatProperty::New(2)); node->SetProperty( "IndexParam2", mitk::FloatProperty::New(1)); node->SetProperty( "visible", mitk::BoolProperty::New( true ) ); node->SetProperty( "VisibleOdfs", mitk::BoolProperty::New( false ) ); node->SetProperty ("layer", mitk::IntProperty::New(100)); node->SetProperty( "DoRefresh", mitk::BoolProperty::New( true ) ); //node->SetProperty( "opacity", mitk::FloatProperty::New(1.0f) ); node->SetProperty( "name", mitk::StringProperty::New(name) ); } - //node->SetProperty( "volumerendering", mitk::BoolProperty::New( false ) ); - //node->SetProperty( "use color", mitk::BoolProperty::New( true ) ); - //node->SetProperty( "texture interpolation", mitk::BoolProperty::New( true ) ); - //node->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); - //node->SetProperty( "layer", mitk::IntProperty::New(0)); - //node->SetProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); - //node->SetOpacity(1.0f); - //node->SetColor(1.0,1.0,1.0); - //node->SetVisibility(true); - //node->SetProperty( "IsQBallVolume", mitk::BoolProperty::New( true ) ); - - //mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); - //mitk::LevelWindow levelwindow; - //// levelwindow.SetAuto( image ); - //levWinProp->SetLevelWindow( levelwindow ); - //node->GetPropertyList()->SetProperty( "levelwindow", levWinProp ); - - //// add a default rainbow lookup table for color mapping - //if(!node->GetProperty("LookupTable")) - //{ - // mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); - // vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); - // vtkLut->SetHueRange(0.6667, 0.0); - // vtkLut->SetTableRange(0.0, 20.0); - // vtkLut->Build(); - // mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); - // mitkLutProp->SetLookupTable(mitkLut); - // node->SetProperty( "LookupTable", mitkLutProp ); - //} - //if(!node->GetProperty("binary")) - // node->SetProperty( "binary", mitk::BoolProperty::New( false ) ); - - //// add a default transfer function - //mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); - //node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); - - //// set foldername as string property - //mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( name ); - //node->SetProperty( "name", nameProp ); +//node->SetProperty( "volumerendering", mitk::BoolProperty::New( false ) ); +//node->SetProperty( "use color", mitk::BoolProperty::New( true ) ); +//node->SetProperty( "texture interpolation", mitk::BoolProperty::New( true ) ); +//node->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New() ); +//node->SetProperty( "layer", mitk::IntProperty::New(0)); +//node->SetProperty( "in plane resample extent by geometry", mitk::BoolProperty::New( false ) ); +//node->SetOpacity(1.0f); +//node->SetColor(1.0,1.0,1.0); +//node->SetVisibility(true); +//node->SetProperty( "IsQBallVolume", mitk::BoolProperty::New( true ) ); + +//mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); +//mitk::LevelWindow levelwindow; +//// levelwindow.SetAuto( image ); +//levWinProp->SetLevelWindow( levelwindow ); +//node->GetPropertyList()->SetPropertx( "levelwindow", levWinProp ); + +//// add a default rainbow lookup table for color mapping +//if(!node->GetProperty("LookupTable")) +//{ +// mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); +// vtkLookupTable* vtkLut = mitkLut->GetVtkLookupTable(); +// vtkLut->SetHueRange(0.6667, 0.0); +// vtkLut->SetTableRange(0.0, 20.0); +// vtkLut->Build(); +// mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); +// mitkLutProp->SetLookupTable(mitkLut); +// node->SetProperty( "LookupTable", mitkLutProp ); +//} +//if(!node->GetProperty("binary")) +// node->SetProperty( "binary", mitk::BoolProperty::New( false ) ); + +//// add a default transfer function +//mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); +//node->SetProperty ( "TransferFunction", mitk::TransferFunctionProperty::New ( tf.GetPointer() ) ); + +//// set foldername as string property +//mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New( name ); +//node->SetProperty( "name", nameProp ); + + +void QmitkQBallReconstructionView::GenerateShellSelectionUI(mitk::DataStorage::SetOfObjects::Pointer set) +{ + + std::map tempMap; + + const mitk::DataStorage::SetOfObjects::iterator setEnd( set->end() ); + mitk::DataStorage::SetOfObjects::iterator NodeIt( set->begin() ); + while(NodeIt != setEnd) + { + //mitk::DiffusionImage* vols = static_cast*>((*NodeIt)->GetData()); + + std::string nodename; + (*NodeIt)->GetStringProperty("name",nodename); + + if(m_ShellSelectorMap.find(nodename) != m_ShellSelectorMap.end()) + { + tempMap[nodename] = m_ShellSelectorMap[nodename]; + m_ShellSelectorMap.erase(nodename); + }else + { + tempMap[nodename] = new QbrShellSelection(this, (*NodeIt) ); + tempMap[nodename]->SetVisible(true); + } + + NodeIt++; + } + + for(std::map::iterator it = m_ShellSelectorMap.begin(); it != m_ShellSelectorMap.end();it ++) + { + delete it->second; + } + m_ShellSelectorMap.clear(); + m_ShellSelectorMap = tempMap; +} diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h index cba5748ff2..ee25ada47a 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionView.h @@ -1,120 +1,136 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED #define _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED #include #include #include "ui_QmitkQBallReconstructionViewControls.h" #include "mitkDiffusionImage.h" #include #include #include typedef short DiffusionPixelType; struct QbrSelListener; +struct QbrShellSelection; + /*! * \ingroup org_mitk_gui_qt_qballreconstruction_internal * * \brief QmitkQBallReconstructionView * * Document your class here. * * \sa QmitkFunctionality */ class QmitkQBallReconstructionView : public QmitkFunctionality { friend struct QbrSelListener; + friend struct QbrShellSelection; + // this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) Q_OBJECT public: static const std::string VIEW_ID; QmitkQBallReconstructionView(); QmitkQBallReconstructionView(const QmitkQBallReconstructionView& other); virtual ~QmitkQBallReconstructionView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); static const int nrconvkernels; protected slots: void ReconstructStandard(); //void ReconstructNormalized1(); //void ReconstructNormalized2(); //void ReconstructNonNormalized(); //void AnalyticallyReconstructStandard(); //void AnalyticallyReconstructSolidAngle(); //void AnalyticallyReconstructNonNegSolidAngle(); //void AnalyticallyReconstructAdc(); //void AnalyticallyReconstructRaw(); void AdvancedCheckboxClicked(); void MethodChoosen(int method); void Reconstruct(int method, int normalization); void NumericalQBallReconstruction(mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization); void AnalyticalQBallReconstruction(mitk::DataStorage::SetOfObjects::Pointer inImages, int normalization); + void MultiQBallReconstruction(mitk::DataStorage::SetOfObjects::Pointer inImages); protected: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); Ui::QmitkQBallReconstructionViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; template void TemplatedAnalyticalQBallReconstruction(mitk::DiffusionImage* vols, float lambda, std::string nodename, std::vector* nodes, int normalization); + template + void TemplatedMultiQBallReconstruction(mitk::DiffusionImage* vols, + float lambda, std::string nodename, std::vector* nodes); + void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); + //void Create + berry::ISelectionListener::Pointer m_SelListener; berry::IStructuredSelection::ConstPointer m_CurrentSelection; + +private: + + std::map< std::string, QbrShellSelection * > m_ShellSelectorMap; + void GenerateShellSelectionUI(mitk::DataStorage::SetOfObjects::Pointer set); }; #endif // _QMITKQBALLRECONSTRUCTIONVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui index f436cbda5a..a85417add3 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkQBallReconstructionViewControls.ui @@ -1,253 +1,275 @@ QmitkQBallReconstructionViewControls 0 0 350 - 385 + 844 0 0 true QmitkQBallReconstructionViewControls Data Diffusion Image: - Reconstruction Advanced Settings QFrame::StyledPanel QFrame::Raised QFormLayout::AllNonFixedFieldsGrow true B0 Threshold false true 0 true Output B0-Image true Spherical Harmonics: true Maximum l-Level false true -1 true Regularization Parameter Lambda false true 0.006 - 2 + 0 Numerical Standard (SH) Solid Angle (SH) Constraint Solid Angle (SH) ADC-Profile only (SH) Raw Signal only (SH) + + + Mulit q-Ball (SH) + + TextLabel false Start Reconstruction + + + + true + + + Qt::LeftToRight + + + false + + + Multi q-Ball reconstruction + + + + Qt::Vertical 20 0 diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.cpp new file mode 100644 index 0000000000..06d6b9dc1a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.cpp @@ -0,0 +1,205 @@ +/*=================================================================== + +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 +#include + +// Qmitk +#include "QmitkStreamlineTrackingView.h" +#include "QmitkStdMultiWidget.h" + +// Qt +#include + +// MITK +#include +#include + +// VTK +#include +#include +#include +#include +#include +#include + + +const std::string QmitkStreamlineTrackingView::VIEW_ID = "org.mitk.views.streamlinetracking"; +const std::string id_DataManager = "org.mitk.views.datamanager"; +using namespace berry; + +QmitkStreamlineTrackingView::QmitkStreamlineTrackingView() + : QmitkFunctionality() + , m_Controls( 0 ) + , m_MultiWidget( NULL ) + , m_TensorImage( NULL ) + , m_SeedRoi( NULL ) +{ +} + +// Destructor +QmitkStreamlineTrackingView::~QmitkStreamlineTrackingView() +{ + +} + +void QmitkStreamlineTrackingView::CreateQtPartControl( QWidget *parent ) +{ + if ( !m_Controls ) + { + // create GUI widgets from the Qt Designer's .ui file + m_Controls = new Ui::QmitkStreamlineTrackingViewControls; + m_Controls->setupUi( parent ); + + connect( m_Controls->commandLinkButton, SIGNAL(clicked()), this, SLOT(DoFiberTracking()) ); + connect( m_Controls->m_SeedsPerVoxelSlider, SIGNAL(valueChanged(int)), this, SLOT(OnSeedsPerVoxelChanged(int)) ); + connect( m_Controls->m_MinTractLengthSlider, SIGNAL(valueChanged(int)), this, SLOT(OnMinTractLengthChanged(int)) ); + connect( m_Controls->m_FaThresholdSlider, SIGNAL(valueChanged(int)), this, SLOT(OnFaThresholdChanged(int)) ); + connect( m_Controls->m_StepsizeSlider, SIGNAL(valueChanged(int)), this, SLOT(OnStepsizeChanged(int)) ); + } +} + +void QmitkStreamlineTrackingView::OnSeedsPerVoxelChanged(int value) +{ + m_Controls->m_SeedsPerVoxelLabel->setText(QString("Seeds per Voxel: ")+QString::number(value)); +} + +void QmitkStreamlineTrackingView::OnMinTractLengthChanged(int value) +{ + m_Controls->m_MinTractLengthLabel->setText(QString("Min. Tract Length: ")+QString::number(value)+QString("mm")); +} + +void QmitkStreamlineTrackingView::OnFaThresholdChanged(int value) +{ + m_Controls->m_FaThresholdLabel->setText(QString("FA Threshold: ")+QString::number((float)value/100)); +} + +void QmitkStreamlineTrackingView::OnStepsizeChanged(int value) +{ + if (value==0) + m_Controls->m_StepsizeLabel->setText(QString("Stepsize: auto")); + else + m_Controls->m_StepsizeLabel->setText(QString("Stepsize: ")+QString::number((float)value/10)+QString("mm")); +} + +void QmitkStreamlineTrackingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) +{ + m_MultiWidget = &stdMultiWidget; +} + + +void QmitkStreamlineTrackingView::StdMultiWidgetNotAvailable() +{ + m_MultiWidget = NULL; +} + +void QmitkStreamlineTrackingView::OnSelectionChanged( std::vector nodes ) +{ + m_TensorImageNode = NULL; + m_TensorImage = NULL; + m_SeedRoi = NULL; + m_Controls->m_TensorImageLabel->setText("-"); + m_Controls->m_RoiImageLabel->setText("-"); + + if(nodes.empty()) + return; + + for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) + { + mitk::DataNode::Pointer node = *it; + + if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + { + if( dynamic_cast(node->GetData()) ) + { + m_TensorImageNode = node; + m_TensorImage = dynamic_cast(node->GetData()); + m_Controls->m_TensorImageLabel->setText(node->GetName().c_str()); + } + else + { + bool isBinary = false; + node->GetPropertyValue("binary", isBinary); + if (isBinary) + { + m_SeedRoi = dynamic_cast(node->GetData()); + m_Controls->m_RoiImageLabel->setText(node->GetName().c_str()); + } + } + } + } + + if(m_TensorImage.IsNotNull()) + m_Controls->commandLinkButton->setEnabled(true); + else + m_Controls->commandLinkButton->setEnabled(false); +} + + + +void QmitkStreamlineTrackingView::DoFiberTracking() +{ + if (m_TensorImage.IsNull()) + return; + + typedef itk::Image< itk::DiffusionTensor3D, 3> TensorImageType; + typedef mitk::ImageToItk CastType; + typedef mitk::ImageToItk CastType2; + + CastType::Pointer caster = CastType::New(); + caster->SetInput(m_TensorImage); + caster->Update(); + TensorImageType::Pointer image = caster->GetOutput(); + + typedef itk::StreamlineTrackingFilter< float > FilterType; + FilterType::Pointer filter = FilterType::New(); + filter->SetInput(image); + filter->SetSeedsPerVoxel(m_Controls->m_SeedsPerVoxelSlider->value()); + filter->SetFaThreshold((float)m_Controls->m_FaThresholdSlider->value()/100); + filter->SetStepSize((float)m_Controls->m_StepsizeSlider->value()/10); + + if (m_SeedRoi.IsNotNull()) + { + CastType2::Pointer caster2 = CastType2::New(); + caster2->SetInput(m_SeedRoi); + caster2->Update(); + ItkUCharImageType::Pointer mask = caster2->GetOutput(); + filter->SetMaskImage(mask); + } + + filter->Update(); + + vtkSmartPointer fiberBundle = filter->GetFiberPolyData(); + if ( fiberBundle->GetNumberOfLines()==0 ) + return; + mitk::FiberBundleX::Pointer fib = mitk::FiberBundleX::New(fiberBundle); + + if (fib->RemoveShortFibers(m_Controls->m_MinTractLengthSlider->value())) + { + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData(fib); + QString name(m_TensorImageNode->GetName().c_str()); + name += "_FiberBundle"; + node->SetName(name.toStdString()); + node->SetVisibility(true); + GetDataStorage()->Add(node); + } +} + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.h new file mode 100644 index 0000000000..582899f462 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingView.h @@ -0,0 +1,88 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QmitkStreamlineTrackingView_h +#define QmitkStreamlineTrackingView_h + +#include + +#include "ui_QmitkStreamlineTrackingViewControls.h" + +#include +#include +#include +#include +#include + + +/*! +\brief QmitkStreamlineTrackingView + +\warning Implements standard streamline tracking as proposed by Mori et al. 1999 "Three-Dimensional Tracking of Axonal Projections in the Brain by Magnetic Resonance Imaging" + +\sa QmitkFunctionality +\ingroup Functionalities +*/ +class QmitkStreamlineTrackingView : 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; + + typedef itk::Image< unsigned char, 3 > ItkUCharImageType; + + QmitkStreamlineTrackingView(); + virtual ~QmitkStreamlineTrackingView(); + + virtual void CreateQtPartControl(QWidget *parent); + + virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); + virtual void StdMultiWidgetNotAvailable(); + + protected slots: + + void DoFiberTracking(); + +protected: + + /// \brief called by QmitkFunctionality when DataManager's selection has changed + virtual void OnSelectionChanged( std::vector nodes ); + Ui::QmitkStreamlineTrackingViewControls* m_Controls; + QmitkStdMultiWidget* m_MultiWidget; + +protected slots: + + void OnSeedsPerVoxelChanged(int value); + void OnMinTractLengthChanged(int value); + void OnFaThresholdChanged(int value); + void OnStepsizeChanged(int value); + +private: + + mitk::Image::Pointer m_SeedRoi; + mitk::TensorImage::Pointer m_TensorImage; + mitk::DataNode::Pointer m_TensorImageNode; + +}; + + + +#endif // _QMITKFIBERTRACKINGVIEW_H_INCLUDED + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingViewControls.ui new file mode 100644 index 0000000000..6ce35d6c92 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkStreamlineTrackingViewControls.ui @@ -0,0 +1,241 @@ + + + QmitkStreamlineTrackingViewControls + + + + 0 + 0 + 480 + 553 + + + + + 0 + 0 + + + + QmitkTemplate + + + + 3 + + + 3 + + + 0 + + + + + Data + + + + + + Tensor Image: + + + + + + + - + + + + + + + Seed ROI Image: + + + + + + + - + + + + + + + + + + Parameters + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 200 + 0 + + + + + + + + Number of tracts started in each voxel of the seed ROI. + + + Seeds per Voxel: 1 + + + + + + + Minimum tract length in mm. + + + 0 + + + 500 + + + 40 + + + Qt::Horizontal + + + + + + + Number of tracts started in each voxel of the seed ROI. + + + 1 + + + 10 + + + Qt::Horizontal + + + + + + + Minimum tract length in mm. + + + Min. Tract Length: 40mm + + + + + + + Minimum tract length in mm. + + + FA Threshold: 0.2 + + + + + + + Fractional Anisotropy Threshold + + + 0 + + + 100 + + + 20 + + + Qt::Horizontal + + + + + + + Minimum tract length in mm. + + + Step Size: auto + + + + + + + Stepsize in mm (auto = 0.5*minimal spacing) + + + 0 + + + 100 + + + 0 + + + Qt::Horizontal + + + + + + + + + + false + + + Start Tracking + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 220 + + + + + + + + + commandLinkButton + + + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.cpp index 8599a6b545..a3d1c13eca 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.cpp @@ -1,1500 +1,1524 @@ /*=================================================================== 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 "berryIWorkbenchWindow.h" #include "berryIWorkbenchPage.h" #include "berryISelectionService.h" #include "berryConstants.h" #include "berryPlatformUI.h" // Qmitk #include "QmitkTractbasedSpatialStatisticsView.h" #include "QmitkStdMultiWidget.h" #include "mitkDataNodeObject.h" #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtkFloatArray.h" #include "vtkLinearTransform.h" #include "vtkPoints.h" #include "mitkSurface.h" #include #include "vtkArrowSource.h" #include "vtkUnstructuredGrid.h" #include "vtkPointData.h" #include #include #include #include #include #include "mitkITKImageImport.h" // #include "mitkImageMapperGL2D.h" #include "mitkVolumeDataVtkMapper3D.h" #include "mitkImageAccessByItk.h" #include "mitkTensorImage.h" #include "itkDiffusionTensor3D.h" #define SEARCHSIGMA 10 /* length in linear voxel dimens { // create new ones m_PointSetNode = mitk::PointSet::New();ions */ #define MAXSEARCHLENGTH (3*SEARCHSIGMA) const std::string QmitkTractbasedSpatialStatisticsView::VIEW_ID = "org.mitk.views.tractbasedspatialstatistics"; using namespace berry; struct TbssSelListener : ISelectionListener { berryObjectMacro(TbssSelListener) TbssSelListener(QmitkTractbasedSpatialStatisticsView* view) { m_View = view; } void DoSelectionChanged(ISelection::ConstPointer selection) { // save current selection in member variable m_View->m_CurrentSelection = selection.Cast(); // do something with the selected items if(m_View->m_CurrentSelection) { bool foundTbssRoi = false; bool foundTbss = false; bool found3dImage = false; bool found4dImage = false; bool foundFiberBundle = false; mitk::TbssRoiImage* roiImage; mitk::TbssImage* image; mitk::Image* img; mitk::FiberBundleX* fib; // iterate selection for (IStructuredSelection::iterator i = m_View->m_CurrentSelection->Begin(); i != m_View->m_CurrentSelection->End(); ++i) { // extract datatree node if (mitk::DataNodeObject::Pointer nodeObj = i->Cast()) { mitk::DataNode::Pointer node = nodeObj->GetDataNode(); // only look at interesting types // check for valid data mitk::BaseData* nodeData = node->GetData(); if( nodeData ) { if(QString("TbssRoiImage").compare(nodeData->GetNameOfClass())==0) { foundTbssRoi = true; roiImage = static_cast(nodeData); } else if (QString("TbssImage").compare(nodeData->GetNameOfClass())==0) { foundTbss = true; image = static_cast(nodeData); } else if(QString("Image").compare(nodeData->GetNameOfClass())==0) { img = static_cast(nodeData); if(img->GetDimension() == 3) { found3dImage = true; } else if(img->GetDimension() == 4) { found4dImage = true; } } else if (QString("FiberBundleX").compare(nodeData->GetNameOfClass())==0) { foundFiberBundle = true; fib = static_cast(nodeData); } } // end CHECK nodeData != NULL } } m_View->m_Controls->m_CreateRoi->setEnabled(found3dImage); m_View->m_Controls->m_ImportFsl->setEnabled(found4dImage); if(found3dImage) { m_View->InitPointsets(); } if(foundTbss && foundTbssRoi) { m_View->Plot(image, roiImage); } if(found3dImage == true && foundFiberBundle) { m_View->PlotFiberBundle(fib, img); } } } void SelectionChanged(IWorkbenchPart::Pointer part, ISelection::ConstPointer selection) { // check, if selection comes from datamanager if (part) { QString partname(part->GetPartName().c_str()); if(partname.compare("Datamanager")==0) { // apply selection DoSelectionChanged(selection); } } } QmitkTractbasedSpatialStatisticsView* m_View; }; QmitkTractbasedSpatialStatisticsView::QmitkTractbasedSpatialStatisticsView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) { } QmitkTractbasedSpatialStatisticsView::~QmitkTractbasedSpatialStatisticsView() { } void QmitkTractbasedSpatialStatisticsView::OnSelectionChanged(std::vector nodes) { //datamanager selection changed if (!this->IsActivated()) return; // Get DataManagerSelection if (!this->GetDataManagerSelection().empty()) { mitk::DataNode::Pointer sourceImageNode = this->GetDataManagerSelection().front(); mitk::Image::Pointer sourceImage = dynamic_cast(sourceImageNode->GetData()); if (!sourceImage) { m_Controls->m_TbssImageLabel->setText( QString( sourceImageNode->GetName().c_str() ) + " is no image" ); return; } // set Text m_Controls->m_TbssImageLabel->setText( QString( sourceImageNode->GetName().c_str() ) + " (" + QString::number(sourceImage->GetDimension()) + "D)" ); } else { m_Controls->m_TbssImageLabel->setText("Please select an image"); } } void QmitkTractbasedSpatialStatisticsView::InitPointsets() { // Check if PointSetStart exsits, if not create it. m_P1 = this->GetDefaultDataStorage()->GetNamedNode("PointSetNode"); if (m_PointSetNode) { //m_PointSetNode = dynamic_cast(m_P1->GetData()); return; } if ((!m_P1) || (!m_PointSetNode)) { // create new ones m_PointSetNode = mitk::PointSet::New(); m_P1 = mitk::DataNode::New(); m_P1->SetData( m_PointSetNode ); m_P1->SetProperty( "name", mitk::StringProperty::New( "PointSet" ) ); m_P1->SetProperty( "opacity", mitk::FloatProperty::New( 1 ) ); m_P1->SetProperty( "helper object", mitk::BoolProperty::New(false) ); // CHANGE if wanted m_P1->SetProperty( "pointsize", mitk::FloatProperty::New( 0.1 ) ); m_P1->SetColor( 1.0, 0.0, 0.0 ); this->GetDefaultDataStorage()->Add(m_P1); m_Controls->m_PointWidget->SetPointSetNode(m_P1); m_Controls->m_PointWidget->SetMultiWidget(GetActiveStdMultiWidget()); } } void QmitkTractbasedSpatialStatisticsView::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::QmitkTractbasedSpatialStatisticsViewControls; m_Controls->setupUi( parent ); this->CreateConnections(); } m_SelListener = berry::ISelectionListener::Pointer(new TbssSelListener(this)); this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(/*"org.mitk.views.datamanager",*/ m_SelListener); berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); m_IsInitialized = false; // Table for the FSL TBSS import m_GroupModel = new QmitkTbssTableModel(); m_Controls->m_GroupInfo->setModel(m_GroupModel); } void QmitkTractbasedSpatialStatisticsView::Activated() { QmitkFunctionality::Activated(); berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkTractbasedSpatialStatisticsView::Deactivated() { QmitkFunctionality::Deactivated(); } void QmitkTractbasedSpatialStatisticsView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(m_Controls->m_CreateRoi), SIGNAL(clicked()), this, SLOT(CreateRoi()) ); connect( (QObject*)(m_Controls->m_ImportFsl), SIGNAL(clicked()), this, SLOT(TbssImport()) ); connect( (QObject*)(m_Controls->m_AddGroup), SIGNAL(clicked()), this, SLOT(AddGroup()) ); connect( (QObject*)(m_Controls->m_RemoveGroup), SIGNAL(clicked()), this, SLOT(RemoveGroup()) ); connect( (QObject*)(m_Controls->m_Clipboard), SIGNAL(clicked()), this, SLOT(CopyToClipboard()) ); + connect( (QObject*)(m_Controls->m_IndividualsClipBoard), SIGNAL(clicked()), this, SLOT(CopyToClipboardIndividuals()) ); connect( m_Controls->m_RoiPlotWidget->m_PlotPicker, SIGNAL(selected(const QwtDoublePoint&)), SLOT(Clicked(const QwtDoublePoint&) ) ); connect( m_Controls->m_RoiPlotWidget->m_PlotPicker, SIGNAL(moved(const QwtDoublePoint&)), SLOT(Clicked(const QwtDoublePoint&) ) ); } } +void QmitkTractbasedSpatialStatisticsView::CopyToClipboardIndividuals() +{ + std::vector > vals = m_Controls->m_RoiPlotWidget->GetIndividualProfiles(); + QString clipboardText; + for (std::vector >::iterator it = vals.begin(); it + != vals.end(); ++it) + { + for (std::vector::iterator it2 = (*it).begin(); it2 != + (*it).end(); ++it2) + { + clipboardText.append(QString("%1 \t").arg(*it2)); + + double d = *it2; + std::cout << d <setText(clipboardText, QClipboard::Clipboard); + + +} + void QmitkTractbasedSpatialStatisticsView::CopyToClipboard() { std::vector > vals = m_Controls->m_RoiPlotWidget->GetVals(); QString clipboardText; for (std::vector >::iterator it = vals.begin(); it != vals.end(); ++it) { for (std::vector::iterator it2 = (*it).begin(); it2 != (*it).end(); ++it2) { clipboardText.append(QString("%1 \t").arg(*it2)); double d = *it2; std::cout << d <setText(clipboardText, QClipboard::Clipboard); } void QmitkTractbasedSpatialStatisticsView::RemoveGroup() { QTableView *temp = static_cast(m_Controls->m_GroupInfo); // QSortFilterProxyModel *proxy = static_cast(temp->model()); QItemSelectionModel *selectionModel = temp->selectionModel(); QModelIndexList indices = selectionModel->selectedRows(); QModelIndex index; foreach(index, indices) { int row = index.row(); m_GroupModel->removeRows(row, 1, QModelIndex()); } } std::string QmitkTractbasedSpatialStatisticsView::ReadFile(std::string whatfile) { std::string s = "Select a" + whatfile; QFileDialog* w = new QFileDialog(this->m_Controls->m_ImportFsl, QString(s.c_str()) ); w->setFileMode(QFileDialog::ExistingFiles); w->setDirectory("/home"); if(whatfile == "gradient image") { w->setNameFilter("Tbss gradient images (*.tgi)"); } // RETRIEVE SELECTION if ( w->exec() != QDialog::Accepted ) { return ""; MITK_INFO << "Failed to load"; } QStringList filenames = w->selectedFiles(); if (filenames.size() > 0) { std::string retval = filenames.at(0).toStdString(); return retval; } return ""; } void QmitkTractbasedSpatialStatisticsView::AddGroup() { QString group("Group"); int number = 0; QPair pair(group, number); QList< QPair >list = m_GroupModel->getList(); if(!list.contains(pair)) { m_GroupModel->insertRows(0, 1, QModelIndex()); QModelIndex index = m_GroupModel->index(0, 0, QModelIndex()); m_GroupModel->setData(index, group, Qt::EditRole); index = m_GroupModel->index(0, 1, QModelIndex()); m_GroupModel->setData(index, number, Qt::EditRole); } else { //QMessageBox::information(this, "Duplicate name"); } } void QmitkTractbasedSpatialStatisticsView::TbssImport() { // Read groups from the interface mitk::TbssImporter::Pointer importer = mitk::TbssImporter::New(); QList< QPair >list = m_GroupModel->getList(); if(list.size() == 0) { QMessageBox msgBox; msgBox.setText("No study group information has been set yet."); msgBox.exec(); return; } std::vector < std::pair > groups; for(int i=0; i pair = list.at(i); std::string s = pair.first.toStdString(); int n = pair.second; std::pair p; p.first = s; p.second = n; groups.push_back(p); } importer->SetGroupInfo(groups); std::string minfo = m_Controls->m_MeasurementInfo->text().toStdString(); importer->SetMeasurementInfo(minfo); std::string name = ""; for (IStructuredSelection::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(); if(QString("Image").compare(node->GetData()->GetNameOfClass())==0) { mitk::Image* img = static_cast(node->GetData()); if(img->GetDimension() == 4) { importer->SetImportVolume(img); name = node->GetName(); } } } } mitk::TbssImage::Pointer tbssImage; tbssImage = importer->Import(); name += "_tbss"; AddTbssToDataStorage(tbssImage, name); } void QmitkTractbasedSpatialStatisticsView::AddTbssToDataStorage(mitk::Image* image, std::string name) { mitk::LevelWindow levelwindow; levelwindow.SetAuto( image ); mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); levWinProp->SetLevelWindow( levelwindow ); mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetProperty( "name", mitk::StringProperty::New(name) ); result->SetData( image ); result->SetProperty( "levelwindow", levWinProp ); // add new image to data storage and set as active to ease further processing GetDefaultDataStorage()->Add( result ); // show the results mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkTractbasedSpatialStatisticsView::Clicked(const QwtDoublePoint& pos) { if(m_Roi.size() > 0 && m_CurrentGeometry != NULL) { int index = (int)pos.x(); index = std::min( (int)m_Roi.size()-1, std::max(0, index) ); itk::Index<3> ix = m_Roi.at(index); mitk::Vector3D i; i[0] = ix[0]; i[1] = ix[1]; i[2] = ix[2]; mitk::Vector3D w; m_CurrentGeometry->IndexToWorld(i, w); mitk::Point3D origin = m_CurrentGeometry->GetOrigin(); mitk::Point3D p; p[0] = w[0] + origin[0]; p[1] = w[1] + origin[1]; p[2] = w[2] + origin[2]; m_MultiWidget->MoveCrossToPosition(p); m_Controls->m_RoiPlotWidget->drawBar(index); } } void QmitkTractbasedSpatialStatisticsView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkTractbasedSpatialStatisticsView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkTractbasedSpatialStatisticsView::AdjustPlotMeasure(const QString & text) { berry::ISelection::ConstPointer sel( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); m_CurrentSelection = sel.Cast(); m_SelListener.Cast()->DoSelectionChanged(sel); } void QmitkTractbasedSpatialStatisticsView::Clustering() { /* // Create a mask using the distance map typedef itk::ImageFileReader< VectorImageType > DirectionReader; DirectionReader::Pointer directionReader = DirectionReader::New(); directionReader->SetFileName(m_TbssWorkspaceManager.GetInputDir().toStdString() + "/tbss/" + m_TbssWorkspaceManager.GetGradient().toStdString()); directionReader->Update(); VectorImageType::Pointer directions = directionReader->GetOutput(); FloatReaderType::Pointer distMapReader = FloatReaderType::New(); distMapReader->SetFileName(m_TbssWorkspaceManager.GetInputDir().toStdString() + "/stats/" + m_TbssWorkspaceManager.GetDistanceMap().toStdString()); distMapReader->Update(); FloatImageType::Pointer distanceMap = distMapReader->GetOutput(); std::string line; std::string path = "/mnt/E130-Projekte/NeuroDiffusion/BRAM DTI/TBSS/rois/cc.txt"; std::ifstream file(path.c_str()); std::vector< itk::Index< 3 > > roi; if(file.is_open()) { while(getline(file,line)) { std::vector tokens; Tokenize(line, tokens); itk::Index<3> ix; ix[0] = atoi(tokens[0].c_str()); ix[1] = atoi(tokens[1].c_str()); ix[2] = atoi(tokens[2].c_str()); roi.push_back(ix); } } if(roi.size() == 0) { return; } // Some code from the projection algorithm of tbss to create a mask std::vector< std::vector< itk::Index< 3 > > > rois; for(int j=0; j > indices; FloatImageType::SizeType size = distanceMap->GetLargestPossibleRegion().GetSize(); bool roiDone = false; while(!roiDone && j ix = roi[j]; int x=ix[0]; int y=ix[1]; int z=ix[2]; VectorImageType::PixelType dir = directions->GetPixel(ix); indices.push_back(ix); for(int iters=0;iters<2;iters++) { float distance=0; for(int d=1;d=size[0] && dy<=size[1] && dz<=size[2]) { d=MAXSEARCHLENGTH; } else if(distanceMap->GetPixel(ix)>=distance) { distance = distanceMap->GetPixel(ix); indices.push_back(ix); } else{ d=MAXSEARCHLENGTH; } } } j++; } // Create a mask from indices UCharImageType::Pointer maskItk = UCharImageType::New(); maskItk->SetRegions(distanceMap->GetRequestedRegion()); maskItk->SetDirection(distanceMap->GetDirection()); maskItk->SetSpacing(distanceMap->GetSpacing()); maskItk->SetOrigin(distanceMap->GetOrigin()); maskItk->Allocate(); // For every point on the roi create a mask and feed it to the partial voluming algorithm //maskItk->FillBuffer(0); // Create a bounding box from current ROI int xMin = numeric_limits::max(); int yMin = numeric_limits::max(); int zMin = numeric_limits::max(); int xMax = numeric_limits::min(); int yMax = numeric_limits::min(); int zMax = numeriUCharImageType::Pointer newMask = UCharImageType::New();c_limits::min(); for(int i=0; i ix = indices[i]; if(ix[0] < xMin) xMin=ix[0]; if(ix[1] < yMin) yMin=ix[1]; if(ix[2] < zMin) zMin=ix[2]; if(ix[0] > xMax) xMax=ix[0]; if(ix[1] > yMax) yMax=ix[1]; if(ix[2] > zMax) zMax=ix[2]; } FloatImageType::PointType origin = distanceMap->GetOrigin(); CharImageType::PointType originMask; originMask[0] = origin[0] + xMin; originMask[1] = origin[1] + -yMin; originMask[2] = origin[2] + zMin; CharImageType::RegionType region; CharImageType::RegionType::SizeType s; s[0] = xMax-xMin + 1; s[1] = yMax-yMin + 1; s[2] = zMax-zMin + 1; region.SetSize(s); UCharImageType::Pointer newMask = UCharImageType::New(); newMask->SetSpacing( distanceMap->GetSpacing() ); // Set the image spacing newMask->SetOrigin( originMask ); // Set the image origin newMask->SetDirection( distanceMap->GetDirection() ); // Set the image direction newMask->SetRegions( region ); newMask->Allocate(); newMask->FillBuffer(0); for(int i=0; i ix = indices[i]; itk::Point< double, 3 > point; itk::Index< 3 > index; distanceMap->TransformIndexToPhysicalPoint (ix, point); newMask->TransformPhysicalPointToIndex(point, index); newMask->SetPixel(index, 1); } */ /* UCharImageType::Pointer newMask = UCharImageType::New(); UCharReaderType::Pointer cReader = UCharReaderType::New(); cReader->SetFileName("/mnt/E130-Projekte/NeuroDiffusion/BRAM DTI/ClusteringFornix/fornix_central_maxFA_path_Dilated_by_3.nrrd"); cReader->Update(); newMask = cReader->GetOutput(); // mitk::DataNode::Pointer maskNode = readNode("itk image/mnt/E130-Projekte/NeuroDiffusion/BRAM DTI/TBSS/clusterMasks/area2.nii"); // mitk::Image::Pointer mask = dynamic_cast(maskNode->GetData()); mitk::Image::Pointer mask; mitk::CastToMitkImage(newMask, mask); typedef mitk::PartialVolumeAnalysisHistogramCalculator HistorgramCalculator; typedef mitk::PartialVolumeAnalysisClusteringCalculator ClusteringType; typedef HistorgramCalculator::HistogramType HistogramType; HistorgramCalculator::Pointer histogramCalculator = HistorgramCalculator::New(); // Make list of subjects std::vector paths; paths.push_back("/mnt/E130-Projekte/NeuroDiffusion/BRAM DTI/TBSS/FA/SORTEDBYCONDITION/FA/subset"); // paths.push_back("/mnt/E130-Projekte/NeuroDiffusion/BRAM DTI/TBSS/FA/SORTEDBYCONDITION/AXD/"); for(int j=0; j values; for(int i=0; i(node->GetData()); histogramCalculator->SetImage(image); histogramCalculator->SetImageMask( mask ); histogramCalculator->SetMaskingModeToImage(); histogramCalculator->SetNumberOfBins(25); histogramCalculator->SetUpsamplingFactor(5); histogramCalculator->SetGaussianSigma(0.0); histogramCalculator->SetForceUpdate(true); bool statisticsChanged = histogramCalculator->ComputeStatistics( ); ClusteringType::ParamsType *cparams = 0; ClusteringType::ClusterResultType *cresult = 0; ClusteringType::HistType *chist = 0; ClusteringType::HelperStructPerformClusteringRetval *currentPerformClusteringResults; try{ mitk::Image* tmpImg = histogramCalculator->GetInternalImage(); mitk::Image::ConstPointer imgToCluster = tmpImg; if(imgToCluster.IsNotNull()) { // perform clustering const HistogramType *histogram = histogramCalculator->GetHistogram( ); ClusteringType::Pointer clusterer = ClusteringType::New(); clusterer->SetStepsNumIntegration(200); clusterer->SetMaxIt(1000); mitk::Image::Pointer pFiberImg; currentPerformClusteringResults = clusterer->PerformClustering(imgToCluster, histogram, 2); pFiberImg = currentPerformClusteringResults->clusteredImage; cparams = currentPerformClusteringResults->params; cresult = currentPerformClusteringResults->result; chist = currentPerformClusteringResults->hist; // m_Controls->m_HistogramWidget->SetParameters( // cparams, cresult, chist ); std::vector *xVals = chist->GetXVals(); std::vector *fiberVals = new std::vector(cresult->GetFiberVals()); double fiberFA = 0.0; double weights = 0.0; // std::cout << "x, y, fiber, nonFiber, mixed, combi" << std::endl; for(int k=0; ksize(); ++k) { fiberFA += xVals->at(k) * fiberVals->at(k); weights += fiberVals->at(k); } fiberFA = fiberFA / weights; std::cout << "FA: " << fiberFA << std::endl; values.push_back(fiberFA); } } catch ( const std::runtime_error &e ) { std::cout << "noooooooooooooooooooooooooooooooo!"; } //MITK_INFO << "number of voxels: " << indices.size(); } std::vector::iterator it = values.begin(); while(it!=values.end()) { std::cout << *it << std::endl; ++it; } }*/ } void QmitkTractbasedSpatialStatisticsView::CreateRoi() { // It is important to load the MeanFASkeletonMask image in MITK to make sure that point selection and // pathfinding is done on the same image //string filename = m_TbssWorkspaceManager.GetInputDir().toStdString() + "/stats/" + m_TbssWorkspaceManager.GetMeanFASkeletonMask().toStdString(); // Implement a way to obtain skeleton and skeletonFA without sml workspace double threshold = QInputDialog::getDouble(m_Controls->m_CreateRoi, tr("Set an FA threshold"), tr("Threshold:"), QLineEdit::Normal, 0.2); mitk::Image::Pointer image; for (IStructuredSelection::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(); if(QString("Image").compare(node->GetData()->GetNameOfClass())==0) { mitk::Image* img = static_cast(node->GetData()); if(img->GetDimension() == 3) { image = img; } } } } if(image.IsNull()) { return; } mitk::TractAnalyzer analyzer; analyzer.SetInputImage(image); analyzer.SetThreshold(threshold); int n = 0; if(m_PointSetNode.IsNotNull()) { n = m_PointSetNode->GetSize(); if(n==0) { QMessageBox msgBox; msgBox.setText("No points have been set yet."); msgBox.exec(); } } else{ QMessageBox msgBox; msgBox.setText("No points have been set yet."); msgBox.exec(); } std::string pathDescription = ""; std::vector< itk::Index<3> > totalPath; if(n>0) { for(int i=0; iGetPoint(i); mitk::Point3D p2 = m_PointSetNode->GetPoint(i+1); itk::Index<3> StartPoint; mitk::ProgressBar::GetInstance()->Progress(); itk::Index<3> EndPoint; image->GetGeometry()->WorldToIndex(p,StartPoint); image->GetGeometry()->WorldToIndex(p2,EndPoint); MITK_INFO << "create roi"; analyzer.BuildGraph(StartPoint, EndPoint); std::vector< itk::Index<3> > path = analyzer.GetPath(); for(std::vector< itk::Index<3> >::iterator it = path.begin(); it != path.end(); it++) { itk::Index<3> ix = *it; if (!(ix==EndPoint)) { mitk::ProgressBar::GetInstance()->Progress(); totalPath.push_back(ix); std::stringstream ss; ss << ix[0] << " " << ix[1] << " " << ix[2] << "\n"; pathDescription += ss.str(); } else { // Only when dealing with the last segment the last point should be added. This one will not occur // as the first point of the next roi segment. if(i == (n-2)) { totalPath.push_back(EndPoint); std::stringstream ss; ss << EndPoint[0] << " " << EndPoint[1] << " " << EndPoint[2] << "\n"; pathDescription += ss.str(); } } } } m_Controls->m_PathTextEdit->setPlainText(QString(pathDescription.c_str())); FloatImageType::Pointer itkImg = FloatImageType::New(); mitk::CastToItkImage(image, itkImg); CharImageType::Pointer roiImg = CharImageType::New(); roiImg->SetRegions(itkImg->GetLargestPossibleRegion().GetSize()); roiImg->SetOrigin(itkImg->GetOrigin()); roiImg->SetSpacing(itkImg->GetSpacing()); roiImg->SetDirection(itkImg->GetDirection()); roiImg->Allocate(); roiImg->FillBuffer(0); std::vector< itk::Index<3> > roi; std::vector< itk::Index<3> >::iterator it; for(it = totalPath.begin(); it != totalPath.end(); it++) { itk::Index<3> ix = *it; roiImg->SetPixel(ix, 1); roi.push_back(ix); } mitk::TbssRoiImage::Pointer tbssRoi = mitk::TbssRoiImage::New(); //mitk::CastToTbssImage(m_CurrentRoi.GetPointer(), tbssRoi); tbssRoi->SetRoi(roi); tbssRoi->SetImage(roiImg); tbssRoi->SetStructure(m_Controls->m_Structure->text().toStdString()); tbssRoi->InitializeFromImage(); // mitk::Image::Pointer tbssRoi = mitk::Image::New(); //mitk::CastToTbssImage(m_CurrentRoi.GetPointer(), tbssRoi); // mitk::CastToMitkImage(roiImg, tbssRoi); AddTbssToDataStorage(tbssRoi, m_Controls->m_RoiName->text().toStdString()); } } void QmitkTractbasedSpatialStatisticsView::PlotFiberBundle(mitk::FiberBundleX *fib, mitk::Image* img) { int num = fib->GetNumFibers(); std::cout << "number of fibers: " << num << std::endl; vtkSmartPointer fiberPolyData = fib->GetFiberPolyData(); vtkCellArray* lines = fiberPolyData->GetLines(); lines->InitTraversal(); int lineSize = lines->GetSize(); std::cout << "line size: " << lineSize << std::cout; typedef itk::Point PointType; typedef std::vector< PointType> TractType; typedef std::vector< TractType > TractContainerType; TractContainerType tracts; for( int fiberID( 0 ); fiberID < num; fiberID++ ) { vtkIdType numPointsInCell(0); vtkIdType* pointsInCell(NULL); lines->GetNextCell ( numPointsInCell, pointsInCell ); TractType singleTract; for( int pointInCellID( 0 ); pointInCellID < numPointsInCell ; pointInCellID++) { // push back point double *p = fiberPolyData->GetPoint( pointsInCell[ pointInCellID ] ); PointType point; point[0] = p[0]; point[1] = p[1]; point[2] = p[2]; singleTract.push_back( point ); } tracts.push_back(singleTract); } m_Controls->m_RoiPlotWidget->PlotFiberBundles(tracts, img); } void QmitkTractbasedSpatialStatisticsView::Plot(mitk::TbssImage* image, mitk::TbssRoiImage* roiImage) { if(m_Controls->m_TabWidget->currentWidget() == m_Controls->m_MeasureTAB) { std::vector< itk::Index<3> > roi = roiImage->GetRoi(); m_Roi = roi; m_CurrentGeometry = image->GetGeometry(); std::string resultfile = ""; /* if(image->GetPreprocessedFA()) { resultFile = image->GetPreprocessedFAFile(); } */ std::string structure = roiImage->GetStructure(); //m_View->m_CurrentGeometry = image->GetGeometry(); m_Controls->m_RoiPlotWidget->SetGroups(image->GetGroupInfo()); // Check for preprocessed results to save time //if(resultfile == "") // { // Need to calculate the results using the 4D volume // Can save the time this takes if there are results available already //std::string type = m_Controls->m_MeasureType->itemText(m_Controls->m_MeasureType->currentIndex()).toStdString(); m_Controls->m_RoiPlotWidget->SetProjections(image->GetImage()); // } m_Controls->m_RoiPlotWidget->SetRoi(roi); m_Controls->m_RoiPlotWidget->SetStructure(structure); m_Controls->m_RoiPlotWidget->SetMeasure( image->GetMeasurementInfo() ); m_Controls->m_RoiPlotWidget->DrawProfiles(resultfile); } } void QmitkTractbasedSpatialStatisticsView::Masking() { //QString filename = m_Controls->m_WorkingDirectory->text(); QString filename = "E:/Experiments/tbss"; QString faFiles = filename + "/AxD"; QString maskFiles = filename + "/bin_masks"; QDirIterator faDirIt(faFiles, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories); QDirIterator maskDirIt(maskFiles, QDir::Files | QDir::NoSymLinks, QDirIterator::Subdirectories); std::vector faFilenames; std::vector maskFilenames; std::vector outputFilenames; while(faDirIt.hasNext() && maskDirIt.hasNext()) { faDirIt.next(); maskDirIt.next(); if((faDirIt.fileInfo().completeSuffix() == "nii" || faDirIt.fileInfo().completeSuffix() == "mhd" || faDirIt.fileInfo().completeSuffix() == "nii.gz") && (maskDirIt.fileInfo().completeSuffix() == "nii" || maskDirIt.fileInfo().completeSuffix() == "mhd" || maskDirIt.fileInfo().completeSuffix() == "nii.gz")) { faFilenames.push_back(faDirIt.filePath().toStdString()); outputFilenames.push_back(faDirIt.fileName().toStdString()); maskFilenames.push_back(maskDirIt.filePath().toStdString()); } } std::vector::iterator faIt = faFilenames.begin(); std::vector::iterator maskIt = maskFilenames.begin(); std::vector::iterator outputIt = outputFilenames.begin(); // Now multiply all FA images with their corresponding masks QString outputDir = filename; while(faIt != faFilenames.end() && maskIt != maskFilenames.end() && outputIt != outputFilenames.end()) { std::cout << "Mask " << *faIt << " with " << *maskIt << std::endl; typedef itk::MultiplyImageFilter MultiplicationFilterType; FloatReaderType::Pointer floatReader = FloatReaderType::New(); CharReaderType::Pointer charReader = CharReaderType::New(); floatReader->SetFileName(*faIt); //floatReader->Update(); //FloatImageType::Pointer faImage = floatReader->GetOutput(); charReader->SetFileName(*maskIt); //charReader->Update(); // CharImageType::Pointer maskImage = charReader->GetOutput(); MultiplicationFilterType::Pointer multiplicationFilter = MultiplicationFilterType::New(); multiplicationFilter->SetInput1(floatReader->GetOutput()); multiplicationFilter->SetInput2(charReader->GetOutput()); multiplicationFilter->Update(); //FloatImageType::Pointer maskedImage = FloatImageType::New(); //maskedImage = MultiplicationFilter->GetOutput(); FloatWriterType::Pointer floatWriter = FloatWriterType::New(); std::string s = faFiles.toStdString().append("/"+*outputIt); floatWriter->SetFileName(s.c_str()); floatWriter->SetInput(multiplicationFilter->GetOutput()); floatWriter->Update(); ++faIt; ++maskIt; ++outputIt; } } VectorImageType::Pointer QmitkTractbasedSpatialStatisticsView::ConvertToVectorImage(mitk::Image::Pointer mitkImage) { VectorImageType::Pointer vecImg = VectorImageType::New(); mitk::Geometry3D* geo = mitkImage->GetGeometry(); mitk::Vector3D spacing = geo->GetSpacing(); mitk::Point3D origin = geo->GetOrigin(); VectorImageType::SpacingType vecSpacing; vecSpacing[0] = spacing[0]; vecSpacing[1] = spacing[1]; vecSpacing[2] = spacing[2]; VectorImageType::PointType vecOrigin; vecOrigin[0] = origin[0]; vecOrigin[1] = origin[1]; vecOrigin[2] = origin[2]; VectorImageType::SizeType size; size[0] = mitkImage->GetDimension(0); size[1] = mitkImage->GetDimension(1); size[2] = mitkImage->GetDimension(2); vecImg->SetSpacing(vecSpacing); vecImg->SetOrigin(vecOrigin); vecImg->SetRegions(size); vecImg->SetVectorLength(mitkImage->GetDimension(3)); vecImg->Allocate(); for(int x=0; x pixel = vecImg->GetPixel(ix); for (int t=0; tGetPixelValueByIndex(ix, t); pixel.SetElement(t, f); } vecImg->SetPixel(ix, pixel); } } } return vecImg; } /* void QmitkTractbasedSpatialStatisticsView::InitializeGridByVectorImage() { // Read vector image from file typedef itk::ImageFileReader< FloatVectorImageType > VectorReaderType; VectorReaderType::Pointer vectorReader = VectorReaderType::New(); vectorReader->SetFileName("E:\\tbss\\testing\\Gradient.mhd"); vectorReader->Update(); FloatVectorImageType::Pointer directions = vectorReader->GetOutput(); // Read roi from file. CharReaderType::Pointer roiReader = CharReaderType::New(); roiReader->SetFileName("E:\\tbss\\testing\\debugging skeletonization\\segment2.mhd"); roiReader->Update(); CharImageType::Pointer roi = roiReader->GetOutput(); DoInitializeGridByVectorImage(directions, roi, std::string("directions")); } void QmitkTractbasedSpatialStatisticsView::DoInitializeGridByVectorImage(FloatVectorImageType::Pointer vectorpic, CharImageType::Pointer roi, std::string name) { //vtkStructuredGrid* grid = vtkStructuredGrid::New(); itk::Matrix itkdirection = vectorpic->GetDirection(); itk::Matrix itkinversedirection = itk::Matrix(itkdirection.GetInverse()); std::vector GridPoints; vtkPoints *points = vtkPoints::New(); mitk::Geometry3D::Pointer geom = mitk::Geometry3D::New(); vtkLinearTransform *vtktransform; vtkLinearTransform *inverse; mitk::Image::Pointer geomget = mitk::Image::New(); geomget->InitializeByItk(vectorpic.GetPointer()); geom = geomget->GetGeometry(); vtktransform = geom->GetVtkTransform(); inverse = vtktransform->GetLinearInverse(); vtkFloatArray * directions = vtkFloatArray::New(); directions->SetName("Vectors"); directions->SetNumberOfComponents(3); // Iterator for the vector image itk::ImageRegionIterator it_input(vectorpic, vectorpic->GetLargestPossibleRegion()); FloatVectorType nullvector; nullvector.Fill(0); double lengthsum = 0; int id = 0; // Iterator for the roi itk::ImageRegionIterator roiIt(roi, roi->GetLargestPossibleRegion()); roiIt.GoToBegin(); for(it_input.GoToBegin(); !( it_input.IsAtEnd() || roiIt.IsAtEnd() ); ++it_input) { //VectorType val = it_input.Value(); if(it_input.Value() != nullvector && roiIt.Get() != 0) { //itk::Point point; mitk::Point3D mitkpoint, mitkworldpoint; mitk::Point3D mitkendpoint, mitkworldendpoint; mitk::Vector3D mitkvector, mitktransvector; itk::Point direction = it_input.Value().GetDataPointer(); //itk::Index<3> in_input = it_input.GetIndex(); //itk::ContinuousIndex cindirection; FloatVectorType transvec = it_input.Value(); mitkvector[0] = transvec[0]; mitkvector[1] = transvec[1]; mitkvector[2] = transvec[2]; //mitkvector[2] = 0.0; mitkpoint[0] = it_input.GetIndex()[0]; mitkpoint[1] = it_input.GetIndex()[1]; mitkpoint[2] = it_input.GetIndex()[2]; mitkendpoint[0] = mitkpoint[0] + mitkvector[0]; mitkendpoint[1] = mitkpoint[1] + mitkvector[1]; mitkendpoint[2] = mitkpoint[2] + mitkvector[2]; //mitkpoint.setXYZ((ScalarType)point[0],(ScalarType)point[1],(ScalarType)point[2]); geom->IndexToWorld(mitkpoint, mitkworldpoint); geom->IndexToWorld(mitkendpoint, mitkworldendpoint); mitktransvector[0] = mitkworldendpoint[0] - mitkworldpoint[0]; mitktransvector[1] = mitkworldendpoint[1] - mitkworldpoint[1]; mitktransvector[2] = mitkworldendpoint[2] - mitkworldpoint[2]; lengthsum += mitktransvector.GetNorm(); directions->InsertTuple3(id,mitktransvector[0],mitktransvector[1],mitktransvector[2]); points->InsertPoint(id,mitkworldpoint[0],mitkworldpoint[1],mitkworldpoint[2]); id++; //for (unsigned short loop = 0; (loop < 20) && (!it_input.IsAtEnd()); loop++) //{ // ++it_input; //} if(it_input.IsAtEnd()) { break; } } ++roiIt; } double meanlength = lengthsum / id; vtkGlyph3D* glyph = vtkGlyph3D::New(); vtkUnstructuredGrid* ugrid = vtkUnstructuredGrid::New(); ugrid->SetPoints(points); ugrid->GetPointData()->SetVectors(directions); glyph->SetInput(ugrid); glyph->SetScaleModeToScaleByVector(); glyph->SetScaleFactor(0.5); glyph->SetColorModeToColorByScalar(); //glyph->ClampingOn(); vtkArrowSource* arrow = vtkArrowSource::New(); if(meanlength > 5) {arrow->SetTipLength(0);arrow->SetTipRadius(0);} arrow->SetShaftRadius(0.03/meanlength); //arrow->SetTipRadius(0.05/meanlength); glyph->SetSource(arrow->GetOutput()); glyph->Update(); mitk::Surface::Pointer glyph_surface = mitk::Surface::New(); glyph_surface->SetVtkPolyData(glyph->GetOutput()); glyph_surface->UpdateOutputInformation(); mitk::DataNode::Pointer gridNode = mitk::DataNode::New(); gridNode->SetProperty( "name", mitk::StringProperty::New(name.c_str()) ); //m_GridNode->SetProperty( "color" , m_GridColor); gridNode->SetProperty( "visible", mitk::BoolProperty::New(true) ); gridNode->SetProperty( "segmentation", mitk::BoolProperty::New(true) ); gridNode->SetProperty( "ID-Tag", mitk::StringProperty::New("grid") ); gridNode->SetProperty( "shader", mitk::StringProperty::New("mitkShaderLightning") ); gridNode->SetData( glyph_surface ); GetDefaultDataStorage()->Add(gridNode); } */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.h index f1f0f0340f..97ad8535a1 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsView.h @@ -1,259 +1,258 @@ /*=================================================================== 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 QmitkTractbasedSpatialStatisticsView_h #define QmitkTractbasedSpatialStatisticsView_h #include #include #include #include #include "ui_QmitkTractbasedSpatialStatisticsViewControls.h" #include #include #include #include #include #include #include #include #include #include "QmitkTbssTableModel.h" #include "QmitkTbssMetaTableModel.h" #include typedef short DiffusionPixelType; typedef itk::Image CharImageType; typedef itk::Image UCharImageType; typedef itk::Image Float4DImageType; typedef itk::Image FloatImageType; typedef itk::Vector IntVectorType; //typedef itk::VectorImage DirectionImageType; typedef itk::VectorImage VectorImageType; typedef itk::ImageFileReader< CharImageType > CharReaderType; typedef itk::ImageFileReader< UCharImageType > UCharReaderType; typedef itk::ImageFileWriter< CharImageType > CharWriterType; typedef itk::ImageFileReader< FloatImageType > FloatReaderType; typedef itk::ImageFileWriter< FloatImageType > FloatWriterType; typedef itk::ImageFileReader< Float4DImageType > Float4DReaderType; typedef itk::ImageFileWriter< Float4DImageType > Float4DWriterType; struct TbssSelListener; /*! \brief QmitkTractbasedSpatialStatisticsView \warning This application module is not yet documented. Use "svn blame/praise/annotate" and ask the author to provide basic documentation. \sa QmitkFunctionalitymitkTbssWorkspaceManager \ingroup Functionalities */ class QmitkTractbasedSpatialStatisticsView : public QmitkFunctionality { friend struct TbssSelListener; // this is needed for all Qt objesetupUicts 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; QmitkTractbasedSpatialStatisticsView(); virtual ~QmitkTractbasedSpatialStatisticsView(); virtual void CreateQtPartControl(QWidget *parent); /// \brief Creation of the connections of main and control widget virtual void CreateConnections(); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); /// \brief Called when the functionality is activated virtual void Activated(); virtual void Deactivated(); protected slots: //void OutputValues(); // void InitializeGridByVectorImage(); void Masking(); void CreateRoi(); void Clustering(); void AdjustPlotMeasure(const QString & text); void Clicked(const QwtDoublePoint& pos); void TbssImport(); void AddGroup(); void RemoveGroup(); void CopyToClipboard(); - - + void CopyToClipboardIndividuals(); protected: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); void Plot(mitk::TbssImage*, mitk::TbssRoiImage*); void PlotFiberBundle(mitk::FiberBundleX* fib, mitk::Image* img); void InitPointsets(); void SetDefaultNodeProperties(mitk::DataNode::Pointer node, std::string name); berry::ISelectionListener::Pointer m_SelListener; berry::IStructuredSelection::ConstPointer m_CurrentSelection; bool m_IsInitialized; mitk::PointSet::Pointer m_PointSetNode; mitk::DataNode::Pointer m_P1; Ui::QmitkTractbasedSpatialStatisticsViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; std::vector SortPoints(CharImageType::Pointer roi, CharImageType::IndexType currentPoint); bool PointVisited(std::vector points, CharImageType::IndexType point); // Modifies the current point by reference and returns true if no more points need to be visited CharImageType::IndexType FindNextPoint(std::vector pointsVisited, CharImageType::IndexType currentPoint, CharImageType::Pointer roi, bool &ready); //void DoInitializeGridByVectorImage(FloatVectorImageType::Pointer vectorpic, CharImageType::Pointer roi ,std::string name); // Tokenizer needed for the roi files void Tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters = " ") { // Skip delimiters at beginning. std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); // Find first "non-delimiter". std::string::size_type pos = str.find_first_of(delimiters, lastPos); while (std::string::npos != pos || std::string::npos != lastPos) { // Found a token, add it to the vector. tokens.push_back(str.substr(lastPos, pos - lastPos)); // Skip delimiters. Note the "not_of" lastPos = str.find_first_not_of(delimiters, pos); // Find next "non-delimiter" pos = str.find_first_of(delimiters, lastPos); } } mitk::DataNode::Pointer readNode(std::string f) { mitk::DataNode::Pointer node; mitk::DataNodeFactory::Pointer nodeReader = mitk::DataNodeFactory::New(); try { nodeReader->SetFileName(f); nodeReader->Update(); node = nodeReader->GetOutput(); } catch(...) { MITK_ERROR << "Could not read file"; return NULL; } return node; } /*template < typename TPixel, unsigned int VImageDimension > void ToITK4D( itk::Image* inputImage, Float4DImageType::Pointer& outputImage );*/ std::string ReadFile(std::string whatfile); std::vector< itk::Index<3> > m_Roi; std::string m_CurrentStructure; mitk::Geometry3D* m_CurrentGeometry; QmitkTbssTableModel* m_GroupModel; void AddTbssToDataStorage(mitk::Image* image, std::string name); mitk::TbssImage::Pointer m_CurrentTbssMetaImage; VectorImageType::Pointer ConvertToVectorImage(mitk::Image::Pointer mitkImg); }; #endif // _QMITKTRACTBASEDSPATIALSTATISTICSVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsViewControls.ui index b6d1b75c69..e079a7d7e1 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkTractbasedSpatialStatisticsViewControls.ui @@ -1,318 +1,325 @@ QmitkTractbasedSpatialStatisticsViewControls 0 0 431 811 0 0 QmitkTemplate FSL import false false Here subject data and tbss meta data can be imported from FSL into the MITK TBSS module 0 Subject data QFrame::StyledPanel QFrame::Raised Group information QAbstractItemView::SelectRows QFrame::StyledPanel QFrame::Raised Add group entry. After that give the group a name and the correct number Add Remove selected entries Remove false Import a 4D image containing group data after group information has been set Import subject data QFrame::StyledPanel QFrame::Raised Diffusion measure Measurement in the to be imported 4D image Fractional Anisotropy Tract-specific analysis true To create a roi first load a tbss meta image into the datamanager - 0 + 1 ROIs QFormLayout::AllNonFixedFieldsGrow current selection mean FA skeleton: Points on Roi 100 100 0 100 Use this widget to create points on the ROI by shift-leftclick on the right positions on the skeleton. Then click Create Roi. The Roi that will be created will pass through the points in the order of occurence in this list false No suitable tbss meta image selected yet. The meta image needs to contain a mean FA skeleton and a skeleton mask Create ROI 0 0 Points on the ROI Name Give a name to the region of interest roiname Structure info On what anatomical structure lies the ROI? Structure Measuring To plot, load a tbss image with subject information and a region of interest corresponding to the study and select them both Copy to clipboard + + + + Copy to clipboard all individuals + + + QmitkPointListWidget QWidget
    QmitkPointListWidget.h
    QmitkTbssRoiAnalysisWidget QWidget
    QmitkTbssRoiAnalysisWidget.h
    1
    diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp index 50f76f44a1..de283acb87 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/mitkPluginActivator.cpp @@ -1,71 +1,73 @@ /*=================================================================== 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 "mitkPluginActivator.h" #include #include "src/internal/QmitkDiffusionImagingPublicPerspective.h" #include "src/internal/QmitkQBallReconstructionView.h" #include "src/internal/QmitkPreprocessingView.h" #include "src/internal/QmitkDiffusionDicomImportView.h" #include "src/internal/QmitkDiffusionQuantificationView.h" #include "src/internal/QmitkTensorReconstructionView.h" #include "src/internal/QmitkControlVisualizationPropertiesView.h" #include "src/internal/QmitkODFDetailsView.h" #include "src/internal/QmitkGibbsTrackingView.h" #include "src/internal/QmitkStochasticFiberTrackingView.h" #include "src/internal/QmitkFiberProcessingView.h" #include "src/internal/QmitkFiberBundleDeveloperView.h" #include "src/internal/QmitkPartialVolumeAnalysisView.h" #include "src/internal/QmitkIVIMView.h" #include "src/internal/QmitkTractbasedSpatialStatisticsView.h" #include "src/internal/QmitkTbssSkeletonizationView.h" +#include "src/internal/QmitkStreamlineTrackingView.h" #include "src/internal/Connectomics/QmitkBrainNetworkAnalysisView.h" namespace mitk { void PluginActivator::start(ctkPluginContext* context) { BERRY_REGISTER_EXTENSION_CLASS(QmitkDiffusionImagingPublicPerspective, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkQBallReconstructionView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkPreprocessingView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkDiffusionDicomImport, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkDiffusionQuantificationView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkTensorReconstructionView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkControlVisualizationPropertiesView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkODFDetailsView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkGibbsTrackingView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkStochasticFiberTrackingView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkFiberProcessingView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkFiberBundleDeveloperView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkPartialVolumeAnalysisView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkIVIMView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkTractbasedSpatialStatisticsView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkTbssSkeletonizationView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkBrainNetworkAnalysisView, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkStreamlineTrackingView, context) } void PluginActivator::stop(ctkPluginContext* context) { Q_UNUSED(context) } } Q_EXPORT_PLUGIN2(org_mitk_gui_qt_diffusionimaging, mitk::PluginActivator) diff --git a/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/Perspectives/QmitkDIAppFiberTractographyPerspective.cpp b/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/Perspectives/QmitkDIAppFiberTractographyPerspective.cpp index e03ad86c59..c476b26af0 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/Perspectives/QmitkDIAppFiberTractographyPerspective.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimagingapp/src/internal/Perspectives/QmitkDIAppFiberTractographyPerspective.cpp @@ -1,56 +1,60 @@ /*=================================================================== 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 "QmitkDIAppFiberTractographyPerspective.h" #include "berryIViewLayout.h" void QmitkDIAppFiberTractographyPerspective::CreateInitialLayout(berry::IPageLayout::Pointer layout) { ///////////////////////////////////////////////////// // all di-app perspectives should have the following: ///////////////////////////////////////////////////// std::string editorArea = layout->GetEditorArea(); layout->AddStandaloneView("org.mitk.views.datamanager", false, berry::IPageLayout::LEFT, 0.3f, editorArea); layout->AddStandaloneView("org.mitk.views.controlvisualizationpropertiesview", false, berry::IPageLayout::BOTTOM, .2f, "org.mitk.views.datamanager"); berry::IFolderLayout::Pointer left = layout->CreateFolder("org.mbi.diffusionimaginginternal.leftcontrols", berry::IPageLayout::BOTTOM, 0.15f, "org.mitk.views.controlvisualizationpropertiesview"); layout->AddStandaloneView("org.mitk.views.imagenavigator", false, berry::IPageLayout::BOTTOM, .4f, "org.mbi.diffusionimaginginternal.leftcontrols"); ///////////////////////////////////////////// // here goes the perspective specific stuff ///////////////////////////////////////////// left->AddView("org.mitk.views.gibbstracking"); berry::IViewLayout::Pointer lo = layout->GetViewLayout("org.mitk.views.gibbstracking"); lo->SetCloseable(false); left->AddView("org.mitk.views.stochasticfibertracking"); lo = layout->GetViewLayout("org.mitk.views.stochasticfibertracking"); lo->SetCloseable(false); + left->AddView("org.mitk.views.streamlinetracking"); + lo = layout->GetViewLayout("org.mitk.views.streamlinetracking"); + lo->SetCloseable(false); + left->AddView("org.mitk.views.fiberprocessing"); lo = layout->GetViewLayout("org.mitk.views.fiberprocessing"); lo->SetCloseable(false); } 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..d5030e6998 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,716 @@ /*=================================================================== 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 +#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) ) ); + currentAction->setCheckable(true); + d->m_DrawPolygon = currentAction; + d->m_DrawActionsToolBar->addAction(currentAction); + d->m_DrawActionsGroup->addAction(currentAction); + // planar figure details text + d->m_SelectedPlanarFiguresText = new QTextBrowser; - QLabel* selectedImageLabel = new QLabel("Selected Image: "); - m_SelectedImage = new QLabel; - m_SelectedImage->setStyleSheet("font-weight: bold;"); - m_SelectedPlanarFiguresText = new QTextBrowser; + // copy to clipboard button + d->m_CopyToClipboard = new QPushButton("Copy to Clipboard"); - m_CopyToClipboard = new QPushButton("Copy to Clipboard"); - QObject::connect( m_CopyToClipboard, SIGNAL( clicked(bool) ) - , this, SLOT( CopyToClipboard(bool) ) ); + 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_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); + d->m_Parent->setLayout(d->m_Layout); - parent->setLayout(m_Layout); - - - m_SelectedPlanarFigures = mitk::DataStorageSelection::New(this->GetDataStorage(), false); - - m_SelectedPlanarFigures->NodeChanged.AddListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeChanged ) ); - - m_SelectedPlanarFigures->NodeRemoved.AddListener( mitk::MessageDelegate1( this, &QmitkMeasurementView::NodeRemoved ) ); - - m_SelectedPlanarFigures->PropertyChanged.AddListener( mitk::MessageDelegate2( this, &QmitkMeasurementView::PropertyChanged ) ); - - m_SelectedImageNode = mitk::DataStorageSelection::New(this->GetDataStorage(), false); - - 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(); + // 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 ); - 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 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 ); - infoText.append(QString("%1
    ").arg(QString::fromStdString( - node->GetName()))); - plainInfoText.append(QString("%1").arg(QString::fromStdString( - node->GetName()))); + // 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 ); - _PlanarFigure = dynamic_cast (node->GetData()); + // 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 ); - planarAngle = dynamic_cast (_PlanarFigure); - if(!planarAngle) - { - planarFourPointAngle = dynamic_cast (_PlanarFigure); - } - - 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(); +} - // for the last selected planar figure ... - if (_PlanarFigure) - { - 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; - } - } - } +void QmitkMeasurementView::CheckForTopMostVisibleImage(mitk::DataNode* _NodeToNeglect) +{ + d->m_SelectedImageNode = this->DetectTopMostVisibleImage().GetPointer(); + if( d->m_SelectedImageNode.GetPointer() == _NodeToNeglect ) + d->m_SelectedImageNode = 0; - // 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); - } + if( d->m_SelectedImageNode.IsNotNull() && d->m_UnintializedPlanarFigure == false ) + { + 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) + std::map::iterator it = + d->m_DataNodeToPlanarFigureData.find(nonConstNode); + + if( it != d->m_DataNodeToPlanarFigureData.end() ) { - mitk::PlanarFigureInteractor::Pointer figureInteractor - = dynamic_cast(node->GetInteractor()); + QmitkPlanarFigureData& data = it->second; - if(figureInteractor.IsNull()) - figureInteractor = mitk::PlanarFigureInteractor::New("PlanarFigureInteractor", nonConstNode); + MEASUREMENT_DEBUG << "removing figure interactor to globalinteraction"; + mitk::Interactor::Pointer oldInteractor = node->GetInteractor(); +// if(oldInteractor.IsNotNull()) +// mitk::GlobalInteraction::GetInstance()->RemoveInteractor(oldInteractor); - // 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); + // 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 ); - this->GetDataStorage()->Remove(m_CurrentFigureNode); - } - - 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") ); + MEASUREMENT_DEBUG << "planar figure initialized"; + d->m_UnintializedPlanarFigure = false; + d->m_DrawActionsToolBar->setEnabled(true); - 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; - - 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); - + mitk::DataNode* node = d->m_CurrentSelection.at(i); - m_CurrentFigureNodeInitialized = false; - m_CurrentFigureNode = newNode; + mitk::PlanarFigure* _PlanarFigure = + _PlanarFigure = dynamic_cast (node->GetData()); - *m_SelectedPlanarFigures = newNode; - - this->RequestRenderWindowUpdate(); -} - -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"; -} + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = this->GetDataStorage()->GetAll(); + const mitk::DataNode* node = 0; -void QmitkMeasurementView::PropertyChanged(const mitk::DataNode* /*node*/, const mitk::BaseProperty* /*prop*/) -{ - 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::NodeChanged(const mitk::DataNode* /*node*/) +mitk::DataNode::Pointer QmitkMeasurementView::DetectTopMostVisibleImage() { - this->PlanarFigureSelectionChanged(); -} + // get all images from the data storage which are not a segmentation + mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); + mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New( isBinary ); + mitk::NodePredicateAnd::Pointer isNormalImage = mitk::NodePredicateAnd::New( isImage, isNotBinary ); -void QmitkMeasurementView::NodeRemoved(const mitk::DataNode* /*node*/) -{ - this->PlanarFigureSelectionChanged(); -} + mitk::DataStorage::SetOfObjects::ConstPointer Images + = this->GetDataStorage()->GetSubset( isNormalImage ); -void QmitkMeasurementView::CopyToClipboard(bool) -{ - std::vector headerRow; - std::vector > rows; - QString featureName; - QString featureQuantity; - std::vector newRow; - headerRow.push_back("Name"); - 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; - - 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*) ) ); - } - } - - if(m_LastRenderWindow) - { - if (!text.isEmpty() && m_SelectedPlanarFigures->GetNode()->IsSelected()) + if (node->IsVisible(NULL) == false) + continue; + int layer = 0; + node->GetIntProperty("layer", layer); + if ( layer < maxLayer ) { - m_MeasurementInfoAnnotation->SetText(1, text.toLatin1().data()); - mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->InsertForegroundRenderer( - m_MeasurementInfoRenderer, true); + continue; } else { - if (mitk::VtkLayerController::GetInstance( - m_LastRenderWindow->GetRenderWindow()) ->IsRendererInserted( - m_MeasurementInfoRenderer)) - mitk::VtkLayerController::GetInstance(m_LastRenderWindow->GetRenderWindow())->RemoveRenderer( - m_MeasurementInfoRenderer); + maxLayer = layer; + currentNode = node; } } + + 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 diff --git a/Utilities/qtsingleapplication/qtlocalpeer.cpp b/Utilities/qtsingleapplication/qtlocalpeer.cpp index 66501cae54..a1fb92e09c 100644 --- a/Utilities/qtsingleapplication/qtlocalpeer.cpp +++ b/Utilities/qtsingleapplication/qtlocalpeer.cpp @@ -1,197 +1,198 @@ /**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of a Qt Solutions component. ** ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor ** the names of its contributors may be used to endorse or promote ** products derived from this software without specific prior written ** permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ****************************************************************************/ #include "qtlocalpeer.h" #include #include #if defined(Q_OS_WIN) #include #include typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); static PProcessIdToSessionId pProcessIdToSessionId = 0; #endif #if defined(Q_OS_UNIX) #include +#include #endif namespace QtLP_Private { #include "qtlockedfile.cpp" #if defined(Q_OS_WIN) #include "qtlockedfile_win.cpp" #else #include "qtlockedfile_unix.cpp" #endif } const char* QtLocalPeer::ack = "ack"; QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) : QObject(parent), id(appId) { QString prefix = id; if (id.isEmpty()) { id = QCoreApplication::applicationFilePath(); #if defined(Q_OS_WIN) id = id.toLower(); #endif prefix = id.section(QLatin1Char('/'), -1); } prefix.remove(QRegExp("[^a-zA-Z]")); prefix.truncate(6); QByteArray idc = id.toUtf8(); quint16 idNum = qChecksum(idc.constData(), idc.size()); socketName = QLatin1String("qtsingleapp-") + prefix + QLatin1Char('-') + QString::number(idNum, 16); #if defined(Q_OS_WIN) if (!pProcessIdToSessionId) { QLibrary lib("kernel32"); pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); } if (pProcessIdToSessionId) { DWORD sessionId = 0; pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); socketName += QLatin1Char('-') + QString::number(sessionId, 16); } #else - socketName += QLatin1Char('-') + QString::number(::getuid(), 16); + socketName += QLatin1Char('-') + QString::number(getuid(), 16); #endif server = new QLocalServer(this); QString lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + socketName + QLatin1String("-lockfile"); lockFile.setFileName(lockName); lockFile.open(QIODevice::ReadWrite); } bool QtLocalPeer::isClient() { if (lockFile.isLocked()) return false; if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) return true; bool res = server->listen(socketName); #if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) // ### Workaround if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); res = server->listen(socketName); } #endif if (!res) qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); return false; } bool QtLocalPeer::sendMessage(const QByteArray &message, int timeout) { if (!isClient()) return false; QLocalSocket socket; bool connOk = false; for(int i = 0; i < 2; i++) { // Try twice, in case the other instance is just starting up socket.connectToServer(socketName); connOk = socket.waitForConnected(timeout/2); if (connOk || i) break; int ms = 250; #if defined(Q_OS_WIN) Sleep(DWORD(ms)); #else struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; nanosleep(&ts, NULL); #endif } if (!connOk) return false; QDataStream ds(&socket); ds.writeBytes(message.constData(), message.size()); bool res = socket.waitForBytesWritten(timeout); if (res) { res &= socket.waitForReadyRead(timeout); // wait for ack if (res) res &= (socket.read(qstrlen(ack)) == ack); } return res; } void QtLocalPeer::receiveConnection() { QLocalSocket* socket = server->nextPendingConnection(); if (!socket) return; while (socket->bytesAvailable() < (int)sizeof(quint32)) socket->waitForReadyRead(); QDataStream ds(socket); QByteArray uMsg; quint32 remaining; ds >> remaining; uMsg.resize(remaining); int got = 0; char* uMsgBuf = uMsg.data(); do { got = ds.readRawData(uMsgBuf, remaining); remaining -= got; uMsgBuf += got; } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); if (got < 0) { qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); delete socket; return; } socket->write(ack, qstrlen(ack)); socket->waitForBytesWritten(1000); delete socket; emit messageReceived(uMsg); //### (might take a long time to return) }