diff --git a/Modules/DiffusionImaging/DiffusionCore/CMakeLists.txt b/Modules/DiffusionImaging/DiffusionCore/CMakeLists.txt index e8202c1ec7..1b9cedf87a 100644 --- a/Modules/DiffusionImaging/DiffusionCore/CMakeLists.txt +++ b/Modules/DiffusionImaging/DiffusionCore/CMakeLists.txt @@ -1,21 +1,26 @@ # With apple gcc 4.2.1 the following waring leads to an build error if boost is enabled if(APPLE) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=empty-body" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() MITK_CREATE_MODULE( SUBPROJECTS MITK-DTI INCLUDE_DIRS include/ include/Algorithms include/Algorithms/Reconstruction include/Algorithms/Registration include/Algorithms/Reconstruction/MultishellProcessing include/Algorithms/Reconstruction/FittingFunctions include/DicomImport include/IODataStructures/DiffusionWeightedImages include/IODataStructures/Properties include/IODataStructures/OdfImages include/IODataStructures/TensorImages include/IODataStructures include/Rendering ${CMAKE_CURRENT_BINARY_DIR} DEPENDS MitkMapperExt MitkPlanarFigure MitkImageExtraction MitkSceneSerializationBase MitkDICOMReader MitkMatchPointRegistration PACKAGE_DEPENDS PUBLIC ITK|ITKTestKernel+ITKRegistrationCommon+ITKMetricsv4+ITKRegistrationMethodsv4+ITKDistanceMap+ITKLabelVoting+ITKVTK PUBLIC VTK|vtkFiltersProgrammable ) if(MSVC) mitkFunctionCheckCAndCXXCompilerFlags("/wd4005" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() add_subdirectory(Testing) add_subdirectory(cmdapps) add_subdirectory(autoload/IO) + +if(MITK_USE_Python) +MITK_INSTALL(FILES PythonRequirements.txt) +add_subdirectory(cmdapps_python) +endif() diff --git a/Modules/DiffusionImaging/DiffusionCore/PythonRequirements.txt b/Modules/DiffusionImaging/DiffusionCore/PythonRequirements.txt new file mode 100644 index 0000000000..e96d8eff85 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/PythonRequirements.txt @@ -0,0 +1,5 @@ +numpy +SimpleITK +dipy +sklearn +https://github.com/MIC-DKFZ/batchgenerators/archive/master.zip diff --git a/Modules/DiffusionImaging/DiffusionCore/cmdapps_python/BrainExtraction.cpp b/Modules/DiffusionImaging/DiffusionCore/cmdapps_python/BrainExtraction.cpp new file mode 100644 index 0000000000..3c33c6fe99 --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/cmdapps_python/BrainExtraction.cpp @@ -0,0 +1,191 @@ +/*=================================================================== + +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 "mitkCommandLineParser.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +typedef mitk::DiffusionPropertyHelper DPH; +typedef itksys::SystemTools ist; + +std::string GetPythonFile(std::string filename, std::string exec_dir) +{ + std::string out = ""; + for (auto dir : mitk::bet::relative_search_dirs) + { + if ( ist::FileExists( exec_dir + dir + filename) ) + { + out = exec_dir + dir + filename; + return out; + } + if ( ist::FileExists( ist::GetCurrentWorkingDirectory() + dir + filename) ) + { + out = ist::GetCurrentWorkingDirectory() + dir + filename; + return out; + } + } + for (auto dir : mitk::bet::absolute_search_dirs) + { + if ( ist::FileExists( dir + filename) ) + { + out = dir + filename; + return out; + } + } + + return out; +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + + parser.setTitle("BrainExtraction"); + parser.setCategory("Preprocessing Tools"); + parser.setDescription("Performs brain extraction using a deep learning model"); + parser.setContributor("MIC"); + + parser.setArgumentPrefix("--", "-"); + parser.addArgument("", "i", mitkCommandLineParser::InputFile, "Input:", "input image", us::Any(), false); + parser.addArgument("", "o", mitkCommandLineParser::OutputFile, "Output:", "output root", us::Any(), false); + + std::map parsedArgs = parser.parseArguments(argc, argv); + if (parsedArgs.size()==0) + return EXIT_FAILURE; + + // mandatory arguments + std::string i = us::any_cast(parsedArgs["i"]); + std::string o = us::any_cast(parsedArgs["o"]); + + std::string exec_dir = ist::GetFilenamePath(argv[0]); + mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"Diffusion Weighted Images"}, {}); + mitk::Image::Pointer mitk_image = mitk::IOUtil::Load(i, &functor); + + bool missing_file = false; + std::string missing_file_string = ""; + if ( GetPythonFile("run_mitk.py", exec_dir).empty() ) + { + missing_file_string += "Brain extraction script file missing: run_mitk.py\n\n"; + missing_file = true; + } + + if ( GetPythonFile("model_final.model", exec_dir).empty() ) + { + missing_file_string += "Brain extraction model file missing: model_final.model\n\n"; + missing_file = true; + } + + if ( GetPythonFile("basic_config_just_like_braintumor.py", exec_dir).empty() ) + { + missing_file_string += "Config file missing: basic_config_just_like_braintumor.py\n\n"; + missing_file = true; + } + + if (missing_file) + { + mitkThrow() << missing_file_string; + } + + us::ModuleContext* context = us::GetModuleContext(); + us::ServiceReference m_PythonServiceRef = context->GetServiceReference(); + mitk::IPythonService* m_PythonService = dynamic_cast ( context->GetService(m_PythonServiceRef) ); + mitk::IPythonService::ForceLoadModule(); + + // load essential modules + m_PythonService->Execute("import SimpleITK as sitk"); + m_PythonService->Execute("import SimpleITK._SimpleITK as _SimpleITK"); + m_PythonService->Execute("import numpy"); + + // extend python search path + std::string pythonpath = ""; + for (auto dir : mitk::bet::relative_search_dirs) + pythonpath += "','" + ist::GetCurrentWorkingDirectory() + dir; + for (auto dir : mitk::bet::relative_search_dirs) + pythonpath += "','" + exec_dir + dir; + for (auto dir : mitk::bet::absolute_search_dirs) + pythonpath += "','" + dir; + m_PythonService->Execute("paths=['"+pythonpath+"']"); + + // set input files (model and config) + m_PythonService->Execute("model_file=\""+GetPythonFile("model_final.model", exec_dir)+"\""); + m_PythonService->Execute("config_file=\""+GetPythonFile("basic_config_just_like_braintumor.py", exec_dir)+"\""); + + // copy input image to python + m_PythonService->CopyToPythonAsSimpleItkImage( mitk_image, "in_image"); + + // run segmentation script + m_PythonService->ExecuteScript( GetPythonFile("run_mitk.py", exec_dir) ); + + // clean up after running script (better way than deleting individual variables?) + if(m_PythonService->DoesVariableExist("in_image")) + m_PythonService->Execute("del in_image"); + + // check for errors + if(!m_PythonService->GetVariable("error_string").empty()) + mitkThrow() << m_PythonService->GetVariable("error_string"); + + // get output images and add to datastorage + std::string output_variables = m_PythonService->GetVariable("output_variables"); + std::vector outputs; + boost::split(outputs, output_variables, boost::is_any_of(",")); + + std::string output_types = m_PythonService->GetVariable("output_types"); + std::vector types; + boost::split(types, output_types, boost::is_any_of(",")); + + for (unsigned int i=0; iDoesVariableExist(outputs.at(i))) + { + mitk::Image::Pointer image = m_PythonService->CopySimpleItkImageFromPython(outputs.at(i)); + + if(types.at(i)=="input" && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(mitk_image)) + { + mitk::DiffusionPropertyHelper::CopyProperties(mitk_image, image, true); + mitk::DiffusionPropertyHelper::InitializeImage(image); + } + + mitk::DataNode::Pointer corrected_node = mitk::DataNode::New(); + corrected_node->SetData( image ); + std::string name = o + "_"; + name += outputs.at(i); + + if(types.at(i)=="input" && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(mitk_image)) + mitk::IOUtil::Save(image, "application/vnd.mitk.nii.gz", name+".nii.gz"); + else + mitk::IOUtil::Save(image, name+".nii.gz"); + } + } + + MITK_INFO << "Finished brain extraction"; + return EXIT_SUCCESS; +} diff --git a/Modules/DiffusionImaging/DiffusionCore/cmdapps_python/CMakeLists.txt b/Modules/DiffusionImaging/DiffusionCore/cmdapps_python/CMakeLists.txt new file mode 100644 index 0000000000..5cbdd74a2f --- /dev/null +++ b/Modules/DiffusionImaging/DiffusionCore/cmdapps_python/CMakeLists.txt @@ -0,0 +1,34 @@ +option(BUILD_DiffusionPythonCmdApps "Build commandline tools for diffusion with python" OFF) + +if(BUILD_DiffusionPythonCmdApps OR MITK_BUILD_ALL_APPS) + + # needed include directories + include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ) + + # list of diffusion cmdapps + # if an app requires additional dependencies + # they are added after a "^^" and separated by "_" + set( diffusionpythoncmdapps + BrainExtraction^^ + ) + + foreach(diffusionpythoncmdapp ${diffusionpythoncmdapps}) + # extract cmd app name and dependencies + string(REPLACE "^^" "\\;" cmdapp_info ${diffusionpythoncmdapp}) + set(cmdapp_info_list ${cmdapp_info}) + list(GET cmdapp_info_list 0 appname) + list(GET cmdapp_info_list 1 raw_dependencies) + string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") + set(dependencies_list ${dependencies}) + + mitkFunctionCreateCommandLineApp( + NAME ${appname} + DEPENDS CppMicroServices MitkCore MitkDiffusionCore MitkPython ${dependencies_list} + PACKAGE_DEPENDS ITK + ) + endforeach() + +endif() diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.python/CMakeLists.txt b/Plugins/org.mitk.gui.qt.diffusionimaging.python/CMakeLists.txt index f1bb5a1b28..2099cf1457 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/CMakeLists.txt @@ -1,23 +1,17 @@ if(MITK_USE_Python) # The project name must correspond to the directory name of your plug-in # and must not contain periods. project(org_mitk_gui_qt_diffusionimaging_python) mitk_create_plugin( EXPORT_DIRECTIVE DIFFUSIONIMAGING_PREPROCESSING_EXPORT EXPORTED_INCLUDE_SUFFIXES src MODULE_DEPENDS MitkDiffusionCore MitkPython PACKAGE_DEPENDS Qt5|Network BetData ) #set(_install_DESTINATION "BetData") MITK_INSTALL(DIRECTORY ${BetData_DIR}/) -#if(WIN32) -# file(COPY ${BetData_DIR}/ DESTINATION ${CMAKE_BINARY_DIR}/bin/${build_type}/ FILES_MATCHING PATTERN "*") -#else() -# file(COPY ${BetData_DIR}/ DESTINATION ${CMAKE_BINARY_DIR}/bin FILES_MATCHING PATTERN "*") -#endif() - endif() diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp index 58b03e509d..1b53fc2130 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp @@ -1,250 +1,262 @@ /*=================================================================== 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. ===================================================================*/ //misc #define _USE_MATH_DEFINES #include // Blueberry #include #include // Qmitk #include "QmitkBrainExtractionView.h" // MITK #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _USE_MATH_DEFINES #include typedef itksys::SystemTools ist; const std::string QmitkBrainExtractionView::VIEW_ID = "org.mitk.views.brainextraction"; QmitkBrainExtractionView::QmitkBrainExtractionView() : QmitkAbstractView() , m_Controls( 0 ) , m_DiffusionImage( nullptr ) { } // Destructor QmitkBrainExtractionView::~QmitkBrainExtractionView() { } void QmitkBrainExtractionView::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::QmitkBrainExtractionViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_ImageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGUI()) ); connect( m_Controls->m_StartButton, SIGNAL(clicked()), this, SLOT(StartBrainExtraction()) ); this->m_Parent = parent; m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); mitk::NodePredicateDimension::Pointer dimPred = mitk::NodePredicateDimension::New(3); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::New(); m_Controls->m_ImageBox->SetPredicate(isImagePredicate); UpdateGUI(); } } void QmitkBrainExtractionView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& ) { } void QmitkBrainExtractionView::UpdateGUI() { if (m_Controls->m_ImageBox->GetSelectedNode().IsNotNull()) m_Controls->m_StartButton->setEnabled(true); else m_Controls->m_StartButton->setEnabled(false); } void QmitkBrainExtractionView::SetFocus() { UpdateGUI(); m_Controls->m_StartButton->setFocus(); } std::string QmitkBrainExtractionView::GetPythonFile(std::string filename) { std::string out = ""; - + std::string exec_dir = QCoreApplication::applicationDirPath().toStdString(); for (auto dir : mitk::bet::relative_search_dirs) + { if ( ist::FileExists( ist::GetCurrentWorkingDirectory() + dir + filename) ) { out = ist::GetCurrentWorkingDirectory() + dir + filename; return out; } + if ( ist::FileExists( exec_dir + dir + filename) ) + { + out = exec_dir + dir + filename; + return out; + } + } for (auto dir : mitk::bet::absolute_search_dirs) + { if ( ist::FileExists( dir + filename) ) { out = dir + filename; return out; } + } return out; } void QmitkBrainExtractionView::StartBrainExtraction() { mitk::DataNode::Pointer node = m_Controls->m_ImageBox->GetSelectedNode(); mitk::Image::Pointer mitk_image = dynamic_cast(node->GetData()); bool missing_file = false; std::string missing_file_string = ""; if ( GetPythonFile("run_mitk.py").empty() ) { missing_file_string += "Brain extraction script file missing: run_mitk.py\n\n"; missing_file = true; } if ( GetPythonFile("model_final.model").empty() ) { missing_file_string += "Brain extraction model file missing: model_final.model\n\n"; missing_file = true; } if ( GetPythonFile("basic_config_just_like_braintumor.py").empty() ) { missing_file_string += "Config file missing: basic_config_just_like_braintumor.py\n\n"; missing_file = true; } if (missing_file) { QMessageBox::warning(nullptr, "Error", (missing_file_string).c_str(), QMessageBox::Ok); return; } try { us::ModuleContext* context = us::GetModuleContext(); us::ServiceReference m_PythonServiceRef = context->GetServiceReference(); mitk::IPythonService* m_PythonService = dynamic_cast ( context->GetService(m_PythonServiceRef) ); mitk::IPythonService::ForceLoadModule(); // load essential modules m_PythonService->Execute("import SimpleITK as sitk"); m_PythonService->Execute("import SimpleITK._SimpleITK as _SimpleITK"); m_PythonService->Execute("import numpy"); // extend python search path + std::string exec_dir = QCoreApplication::applicationDirPath().toStdString(); std::string pythonpath = ""; for (auto dir : mitk::bet::relative_search_dirs) pythonpath += "','" + ist::GetCurrentWorkingDirectory() + dir; + for (auto dir : mitk::bet::relative_search_dirs) + pythonpath += "','" + exec_dir + dir; for (auto dir : mitk::bet::absolute_search_dirs) pythonpath += "','" + dir; m_PythonService->Execute("paths=['"+pythonpath+"']"); // set input files (model and config) m_PythonService->Execute("model_file=\""+GetPythonFile("model_final.model")+"\""); m_PythonService->Execute("config_file=\""+GetPythonFile("basic_config_just_like_braintumor.py")+"\""); // copy input image to python m_PythonService->CopyToPythonAsSimpleItkImage( mitk_image, "in_image"); // run segmentation script m_PythonService->ExecuteScript( GetPythonFile("run_mitk.py") ); // clean up after running script (better way than deleting individual variables?) if(m_PythonService->DoesVariableExist("in_image")) m_PythonService->Execute("del in_image"); // check for errors if(!m_PythonService->GetVariable("error_string").empty()) { QMessageBox::warning(nullptr, "Error", QString(m_PythonService->GetVariable("error_string").c_str()), QMessageBox::Ok); return; } // get output images and add to datastorage std::string output_variables = m_PythonService->GetVariable("output_variables"); std::vector outputs; boost::split(outputs, output_variables, boost::is_any_of(",")); std::string output_types = m_PythonService->GetVariable("output_types"); std::vector types; boost::split(types, output_types, boost::is_any_of(",")); for (unsigned int i=0; iDoesVariableExist(outputs.at(i))) { mitk::Image::Pointer image = m_PythonService->CopySimpleItkImageFromPython(outputs.at(i)); if(types.at(i)=="input" && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(mitk_image)) { mitk::DiffusionPropertyHelper::CopyProperties(mitk_image, image, true); mitk::DiffusionPropertyHelper::InitializeImage(image); } mitk::DataNode::Pointer corrected_node = mitk::DataNode::New(); corrected_node->SetData( image ); std::string name = node->GetName(); name += "_"; name += outputs.at(i); corrected_node->SetName(name); GetDataStorage()->Add(corrected_node, node); m_PythonService->Execute("del " + outputs.at(i)); mitk::RenderingManager::GetInstance()->InitializeViews( corrected_node->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } } } catch(...) { QMessageBox::warning(nullptr, "Error", "File could not be processed.\nIs pytorch installed on your system?\nDoes your script use the correct input and output variable names (in: in_image & model, out: brain_mask & brain_extracted)?", QMessageBox::Ok); } }