diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index aeec51a9f9..c68db26580 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,102 +1,103 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF #org.blueberry.test:ON #org.blueberry.uitest:ON #Testing/org.blueberry.core.runtime.tests:ON #Testing/org.blueberry.osgi.tests:ON org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.mxnmultiwidgeteditor:OFF org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.chartExample:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.datastorageviewertest:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicom:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.lasercontrol:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF org.mitk.gui.qt.semanticrelations:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.photoacoustics.pausviewer:OFF org.mitk.gui.qt.photoacoustics.pausmotioncompensation:OFF org.mitk.gui.qt.photoacoustics.imageprocessing:OFF org.mitk.gui.qt.photoacoustics.simulation:OFF org.mitk.gui.qt.photoacoustics.spectralunmixing:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.gui.qt.multilabelsegmentation:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.algorithm.batch:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.radiomics:OFF org.mitk.gui.qt.cest:OFF org.mitk.gui.qt.fit.demo:OFF org.mitk.gui.qt.fit.inspector:OFF org.mitk.gui.qt.fit.genericfitting:OFF org.mitk.gui.qt.pharmacokinetics.mri:OFF org.mitk.gui.qt.pharmacokinetics.pet:OFF org.mitk.gui.qt.pharmacokinetics.simulation:OFF org.mitk.gui.qt.pharmacokinetics.curvedescriptor:OFF org.mitk.gui.qt.pharmacokinetics.concentration.mri:OFF + org.mitk.gui.qt.bonesegmentationrework:ON ) diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/CMakeLists.txt b/Plugins/org.mitk.gui.qt.bonesegmentationrework/CMakeLists.txt new file mode 100644 index 0000000000..ee0557331c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/CMakeLists.txt @@ -0,0 +1,13 @@ +project(org_mitk_gui_qt_bonesegmentationrework) + +mitk_create_plugin( + EXPORT_DIRECTIVE BONESEGMENTATIONREWORK_EXPORT + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDS MitkQtWidgetsExt MitkPython +PACKAGE_DEPENDS + PUBLIC Qt5|Widgets CTK|CTKScriptingPythonCore+CTKScriptingPythonWidgets +) +if(TARGET ${PLUGIN_TARGET}) + target_link_libraries(${PLUGIN_TARGET} PUBLIC Python3::NumPy) + configure_file(PythonPath.h.in "${CMAKE_CURRENT_BINARY_DIR}/PythonPath.h" @ONLY) +endif() diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/PythonPath.h.in b/Plugins/org.mitk.gui.qt.bonesegmentationrework/PythonPath.h.in new file mode 100644 index 0000000000..28581baec4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/PythonPath.h.in @@ -0,0 +1,16 @@ +#ifdef _DEBUG +#define PYTHON_PATH_BUILD_TYPE "/Debug" +#else +#define PYTHON_PATH_BUILD_TYPE "/Release" +#endif + +#define PYTHON_LIBRARY "@PYTHON_LIBRARY@" + +#ifdef WIN32 +//Todo: windows system python +#define EXTERNAL_SITE_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/site-packages" +#define EXTERNAL_DIST_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/dist-packages" +#else +#define EXTERNAL_SITE_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/site-packages" +#define EXTERNAL_DIST_PACKAGES "@MITK_EXTERNAL_PROJECT_PREFIX@/lib/python@PYTHON_VERSION_MAJOR@.@PYTHON_VERSION_MINOR@/dist-packages" +#endif diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/UserManual/Manual.dox new file mode 100644 index 0000000000..3e05d63e27 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/UserManual/Manual.dox @@ -0,0 +1,17 @@ +/** +\page org_mitk_gui_qt_bonesegmentationrework The Bonesegmentation Rework + +\imageMacro{icon.png,"Icon of Bonesegmentation Rework",2.00} + +\tableofcontents + +\section org_mitk_gui_qt_bonesegmentationreworkOverview Overview +Describe the features of your awesome plugin here + + +*/ diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/UserManual/icon.xpm b/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/UserManual/icon.xpm new file mode 100644 index 0000000000..9057c20bc6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/UserManual/icon.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static const char * icon_xpm[] = { +"16 16 2 1", +" c #FF0000", +". c #000000", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/doxygen/modules.dox b/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/doxygen/modules.dox new file mode 100644 index 0000000000..e87862429c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/documentation/doxygen/modules.dox @@ -0,0 +1,16 @@ +/** + \defgroup org_mitk_gui_qt_bonesegmentationrework org.mitk.gui.qt.bonesegmentationrework + \ingroup MITKPlugins + + \brief Describe your plugin here. + +*/ + +/** + \defgroup org_mitk_gui_qt_bonesegmentationrework_internal Internal + \ingroup org_mitk_gui_qt_bonesegmentationrework + + \brief This subcategory includes the internal classes of the org.mitk.gui.qt.bonesegmentationrework plugin. Other + plugins must not rely on these classes. They contain implementation details and their interface + may change at any time. We mean it. +*/ diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/files.cmake b/Plugins/org.mitk.gui.qt.bonesegmentationrework/files.cmake new file mode 100644 index 0000000000..bb1698267d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/files.cmake @@ -0,0 +1,44 @@ +set(SRC_CPP_FILES + +) + +set(INTERNAL_CPP_FILES + org_mitk_gui_qt_bonesegmentationrework_Activator.cpp + BoneSegmentationRework.cpp + ImageTransferPython.cpp +) + +set(UI_FILES + src/internal/BoneSegmentationReworkControls.ui +) + +set(MOC_H_FILES + src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h + src/internal/BoneSegmentationRework.h + src/internal/ImageTransferPython.h +) + +# 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 +set(CACHED_RESOURCE_FILES + resources/icon.svg + plugin.xml +) + +# list of Qt .qrc files which contain additional resources +# specific to this plugin +set(QRC_FILES + resources/boneseg.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.bonesegmentationrework/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.bonesegmentationrework/manifest_headers.cmake new file mode 100644 index 0000000000..936e4f88ac --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "Bonesegmentation Rework") +set(Plugin-Version "0.1") +set(Plugin-Vendor "DKFZ") +set(Plugin-ContactAddress "") +set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/plugin.xml b/Plugins/org.mitk.gui.qt.bonesegmentationrework/plugin.xml new file mode 100644 index 0000000000..e531c5883d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/plugin.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/boneseg.qrc b/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/boneseg.qrc new file mode 100644 index 0000000000..b1940d6050 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/boneseg.qrc @@ -0,0 +1,5 @@ + + + segment.py + + diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/icon.svg b/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/icon.svg new file mode 100644 index 0000000000..4d4941c235 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/icon.svg @@ -0,0 +1,2413 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/segment.py b/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/segment.py new file mode 100644 index 0000000000..219a7c8c63 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/resources/segment.py @@ -0,0 +1,385 @@ +import os +import numpy as np +import SimpleITK +import torch +import torch.cuda + +print('Started python script...') + +import SimpleITK as sitk +from skimage.transform import resize +import sys + + +def resize_image(image, old_spacing, new_spacing, order=3): + new_shape = (int(np.round(old_spacing[0]/new_spacing[0]*float(image.shape[0]))), + int(np.round(old_spacing[1]/new_spacing[1]*float(image.shape[1]))), + int(np.round(old_spacing[2]/new_spacing[2]*float(image.shape[2])))) + return resize(image, new_shape, order=order, mode='edge') + + +def cut_off_values_upper_lower_percentile(image, mask=None, percentile_lower=0.2, percentile_upper=99.8): + if mask is None: + mask = image!=image[0,0,0] + cut_off_lower = np.percentile(image[mask!=0].ravel(), percentile_lower) + cut_off_upper = np.percentile(image[mask!=0].ravel(), percentile_upper) + res = np.copy(image) + res[(res < cut_off_lower) & (mask !=0 )] = cut_off_lower + res[(res > cut_off_upper) & (mask !=0 )] = cut_off_upper + return image + + +def preprocess_image(itk_image, is_seg=False, spacing_target=(1, 0.5, 0.5)): + spacing = np.array(itk_image.GetSpacing())[[2, 1, 0]] + image = sitk.GetArrayFromImage(itk_image).astype(float) + if not is_seg: + image = resize_image(image, spacing, spacing_target).astype(np.float32) + # cut off outliers + image = cut_off_values_upper_lower_percentile(image, np.ones(image.shape), 1., 99.) + #subtract mean, divide by std. use heuristic masking + image -= image.mean() + image /= image.std() + else: + image = resize_image(image, spacing, spacing_target, 0) + return image + + +def load_and_preprocess(in_image): + images = {} + + np_array = sitk.GetArrayFromImage(in_image).astype(float) + if len(np_array.shape) > 3: + b0 = sitk.GetImageFromArray(np_array[:,:,:,0]) + b0.SetSpacing(in_image.GetSpacing()) + b0.SetOrigin(in_image.GetOrigin()) + b0.SetDirection(in_image.GetDirection()) + images["T1"] = b0 + elif len(np_array.shape)==3 : + images["T1"] = in_image + + properties_dict = { + "spacing": in_image.GetSpacing(), + "direction": in_image.GetDirection(), + "size": in_image.GetSize(), + "origin": in_image.GetOrigin() + } + + for k in images.keys(): + images[k] = preprocess_image(images[k], is_seg=False, spacing_target=(1.5, 1.5, 1.5)) + + properties_dict['size_before_cropping'] = images["T1"].shape + + imgs = [] + for seq in ['T1']: + imgs.append(images[seq][None]) + all_data = np.vstack(imgs) + return all_data, properties_dict + +def get_sitk_from_nparray(segmentation, original_image, dct) : + ''' + segmentation must have the same spacing as the original nifti (for now). segmentation may have been cropped out + of the original image + :param segmentation: + :param dct: + :return: + ''' + old_size = np.array(dct['size_before_cropping']) + bbox = dct.get('brain_bbox') + if bbox is not None: + seg_old_size = np.zeros(old_size) + for c in range(3): + bbox[c][1] = np.min((bbox[c][0] + segmentation.shape[c], old_size[c])) + seg_old_size[bbox[0][0]:bbox[0][1], + bbox[1][0]:bbox[1][1], + bbox[2][0]:bbox[2][1]] = segmentation + else: + seg_old_size = segmentation + + seg_old_spacing = resize_segmentation(seg_old_size, np.array(dct['size'])[[2, 1, 0]], order=3) + seg_resized_itk = sitk.GetImageFromArray(seg_old_spacing.astype(np.uint8)) + seg_resized_itk.SetSpacing(np.array(dct['spacing'])[[0, 1, 2]]) + seg_resized_itk.SetOrigin(dct['origin']) + seg_resized_itk.SetDirection(dct['direction']) + + if original_image is not None : + image = sitk.GetArrayFromImage(original_image).astype(float) + + if len(image.shape) > 3 : + for i in range(image.shape[3]) : + image[:,:,:,i] *= seg_old_spacing + else : + image *= seg_old_spacing + + brain_extracted = sitk.GetImageFromArray(image.astype(np.float32)) + brain_extracted.SetSpacing(np.array(dct['spacing'])[[0, 1, 2]]) + brain_extracted.SetOrigin(dct['origin']) + brain_extracted.SetDirection(dct['direction']) + else : + brain_extracted = None + + return seg_resized_itk, brain_extracted + +def save_segmentation_nifti(segmentation, dct, out_fname): + ''' + segmentation must have the same spacing as the original nifti (for now). segmentation may have been cropped out + of the original image + :param segmentation: + :param dct: + :param out_fname: + :return: + ''' + old_size = np.array(dct['size_before_cropping']) + bbox = dct.get('brain_bbox') + if bbox is not None: + seg_old_size = np.zeros(old_size) + for c in range(3): + bbox[c][1] = np.min((bbox[c][0] + segmentation.shape[c], old_size[c])) + seg_old_size[bbox[0][0]:bbox[0][1], + bbox[1][0]:bbox[1][1], + bbox[2][0]:bbox[2][1]] = segmentation + else: + seg_old_size = segmentation + + seg_old_spacing = resize_segmentation(seg_old_size, np.array(dct['size'])[[2, 1, 0]], order=3) + seg_resized_itk = sitk.GetImageFromArray(seg_old_spacing.astype(np.uint8)) + seg_resized_itk.SetSpacing(np.array(dct['spacing'])[[0, 1, 2]]) + seg_resized_itk.SetOrigin(dct['origin']) + seg_resized_itk.SetDirection(dct['direction']) + sitk.WriteImage(seg_resized_itk, out_fname) + + +def resize_segmentation(segmentation, new_shape, order=3): + unique_labels = np.unique(segmentation) + assert len(segmentation.shape) == len(new_shape), "new shape must have same dimensionality as segmentation" + reshaped_multihot = np.zeros([len(unique_labels)] + list(new_shape), dtype=float) + for i, c in enumerate(unique_labels): + reshaped_multihot[i] = resize((segmentation == c).astype(float), new_shape, order, mode="constant", cval=0, clip=True) + reshaped = unique_labels[np.argmax(reshaped_multihot, 0)] + return reshaped.astype(segmentation.dtype) + + +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2017 Division of Medical Image Computing, German Cancer Research Center (DKFZ) +# +# 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. + +import numpy as np + + +def reshape(orig_img, append_value=-1024, new_shape=(512, 512, 512)): + reshaped_image = np.zeros(new_shape) + reshaped_image[...] = append_value + x_offset = 0 + y_offset = 0 # (new_shape[1] - orig_img.shape[1]) // 2 + z_offset = 0 # (new_shape[2] - orig_img.shape[2]) // 2 + + reshaped_image[x_offset:orig_img.shape[0]+x_offset, y_offset:orig_img.shape[1]+y_offset, z_offset:orig_img.shape[2]+z_offset] = orig_img + # insert temp_img.min() as background value + + return reshaped_image + + +def crop_image_to_orig_size(image, orig_shape): + x_offset = 0 + y_offset = 0 # (512 - orig_shape[1]) // 2 + z_offset = 0 # (512 - orig_shape[2]) // 2 + + return image[x_offset:orig_shape[0] + x_offset, y_offset:orig_shape[1] + y_offset, z_offset:orig_shape[2] + z_offset] + +def next_power_of_2(x): + return 1 if x == 0 else 2**(x - 1).bit_length() + +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2017 Division of Medical Image Computing, German Cancer Research Center (DKFZ) +# +# 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. + +# Defines the Unet. +# |num_downs|: number of downsamplings in UNet. For example, +# if |num_downs| == 7, image of size 128x128 will become of size 1x1 at the bottleneck + +# recursive implementation of Unet +import torch + +from torch import nn + + +class UNet(nn.Module): + def __init__(self, num_classes=3, in_channels=1, initial_filter_size=64, kernel_size=3, num_downs=4, norm_layer=nn.InstanceNorm2d): + # norm_layer=nn.BatchNorm2d, use_dropout=False): + super(UNet, self).__init__() + + # construct unet structure + unet_block = UnetSkipConnectionBlock(in_channels=initial_filter_size * 2 ** (num_downs-1), out_channels=initial_filter_size * 2 ** num_downs, + num_classes=num_classes, kernel_size=kernel_size, norm_layer=norm_layer, innermost=True) + for i in range(1, num_downs): + unet_block = UnetSkipConnectionBlock(in_channels=initial_filter_size * 2 ** (num_downs-(i+1)), + out_channels=initial_filter_size * 2 ** (num_downs-i), + num_classes=num_classes, kernel_size=kernel_size, submodule=unet_block, norm_layer=norm_layer) + unet_block = UnetSkipConnectionBlock(in_channels=in_channels, out_channels=initial_filter_size, + num_classes=num_classes, kernel_size=kernel_size, submodule=unet_block, norm_layer=norm_layer, + outermost=True) + + self.model = unet_block + + def forward(self, x): + return self.model(x) + + +# Defines the submodule with skip connection. +# X -------------------identity---------------------- X +# |-- downsampling -- |submodule| -- upsampling --| +class UnetSkipConnectionBlock(nn.Module): + def __init__(self, in_channels=None, out_channels=None, num_classes=1, kernel_size=3, + submodule=None, outermost=False, innermost=False, norm_layer=nn.InstanceNorm2d, use_dropout=False): + super(UnetSkipConnectionBlock, self).__init__() + self.outermost = outermost + # downconv + pool = nn.MaxPool2d(2, stride=2) + conv1 = self.contract(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, norm_layer=norm_layer) + conv2 = self.contract(in_channels=out_channels, out_channels=out_channels, kernel_size=kernel_size, norm_layer=norm_layer) + + # upconv + conv3 = self.expand(in_channels=out_channels*2, out_channels=out_channels, kernel_size=kernel_size) + conv4 = self.expand(in_channels=out_channels, out_channels=out_channels, kernel_size=kernel_size) + + if outermost: + final = nn.Conv2d(out_channels, num_classes, kernel_size=1) + down = [conv1, conv2] + up = [conv3, conv4, final] + model = down + [submodule] + up + elif innermost: + upconv = nn.ConvTranspose2d(in_channels*2, in_channels, + kernel_size=2, stride=2) + model = [pool, conv1, conv2, upconv] + else: + upconv = nn.ConvTranspose2d(in_channels*2, in_channels, kernel_size=2, stride=2) + + down = [pool, conv1, conv2] + up = [conv3, conv4, upconv] + + if use_dropout: + model = down + [submodule] + up + [nn.Dropout(0.5)] + else: + model = down + [submodule] + up + + self.model = nn.Sequential(*model) + + @staticmethod + def contract(in_channels, out_channels, kernel_size=3, norm_layer=nn.InstanceNorm2d): + layer = nn.Sequential( + nn.Conv2d(in_channels, out_channels, kernel_size, padding=1), + norm_layer(out_channels), + nn.LeakyReLU(inplace=True)) + return layer + + @staticmethod + def expand(in_channels, out_channels, kernel_size=3): + layer = nn.Sequential( + nn.Conv2d(in_channels, out_channels, kernel_size, padding=1), + nn.LeakyReLU(inplace=True), + ) + return layer + + @staticmethod + def center_crop(layer, target_width, target_height): + batch_size, n_channels, layer_width, layer_height = layer.size() + xy1 = (layer_width - target_width) // 2 + xy2 = (layer_height - target_height) // 2 + return layer[:, :, xy1:(xy1 + target_width), xy2:(xy2 + target_height)] + + def forward(self, x): + if self.outermost: + return self.model(x) + else: + crop = self.center_crop(self.model(x), x.size()[2], x.size()[3]) + return torch.cat([x, crop], 1) + +batch_size = 8 +num_classes = 2 + +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + + +model = UNet(num_classes=num_classes, in_channels=1) +model.load_state_dict(torch.load( + seg_load_network_path, map_location=device)) +print(device) + +model.eval() +model.to(device) + +result = [] +first = True +print('Segmenting...') + +input_image_array = SimpleITK.GetArrayFromImage(nrrd_image) +append_empty_slices = batch_size - (input_image_array.shape[0]%batch_size) +print("A") +y = next_power_of_2(input_image_array.shape[1]) +z = next_power_of_2(input_image_array.shape[2]) +print("B") +if y > 512 or z > 512: + batch_size = 2 +temp_array = np.ones((input_image_array.shape[0] + append_empty_slices, y, z))*(-1024) +temp_array[0:input_image_array.shape[0], 0:input_image_array.shape[1], 0:input_image_array.shape[2]] = input_image_array +print("C") +with torch.no_grad(): + print("here1") + start = 0 + end = start+batch_size + while end <= temp_array.shape[0]: + print("here2") + pred = model(torch.from_numpy(np.expand_dims( + temp_array[start:end], axis=1).astype(np.float32)).to(device)) + print("D") + if first: + result = pred.detach().data.cpu() + first = False + print("if") + else: + result = torch.cat((result, pred.detach().data.cpu())) + print("else") + print(result.shape) + start = end + end = start + batch_size +result = torch.argmax(result, dim=1, keepdim=True) +print("E") +array_to_write = result.data.numpy().squeeze() +array_to_write = array_to_write[0:input_image_array.shape[0], 0:input_image_array.shape[1], 0:input_image_array.shape[2]] +array_to_write = crop_image_to_orig_size( array_to_write, SimpleITK.GetArrayFromImage(nrrd_image).shape) +print(array_to_write.shape) + +image_to_write = SimpleITK.GetImageFromArray(array_to_write) + +image_to_write.SetSpacing(nrrd_image.GetSpacing()) +image_to_write.SetOrigin(nrrd_image.GetOrigin()) +image_to_write.SetDirection(nrrd_image.GetDirection()) +image_to_write = SimpleITK.Cast(image_to_write, SimpleITK.sitkUInt8) + +SimpleITK.Cast(image_to_write, SimpleITK.sitkUInt8) +print('Ended python script...') diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.cpp b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.cpp new file mode 100644 index 0000000000..0e6ad0ac8f --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.cpp @@ -0,0 +1,253 @@ +/*=================================================================== + +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 "BoneSegmentationRework.h" + +// Qt +#include +#include +#include + +// mitk image +#include +#include + +#include "internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h" +#include +#include +#include + +#include +#include + +#include +#include + +#include"ImageTransferPython.h" + +const std::string BoneSegmentationRework::VIEW_ID = "org.mitk.views.bonesegmentationrework"; + +BoneSegmentationRework::~BoneSegmentationRework() +{ + if (Py_IsInitialized()) + { + Py_Finalize(); + } +} + +void BoneSegmentationRework::SetFocus() +{ + m_Controls.buttonPerformImageProcessing->setFocus(); +} + +void BoneSegmentationRework::CreateQtPartControl(QWidget *parent) +{ + m_Controls.setupUi(parent); + m_Controls.buttonPerformImageProcessing->setText("Start Bone Segmentation"); + m_Controls.buttonPerformImageProcessing->setEnabled(false); + + m_Controls.m_ImageSelector->SetDataStorage(GetDataStorage()); + m_Controls.m_ImageSelector->SetPredicate(GetImagePredicate()); + + m_Controls.labelLoadTrainedNet->setText("Please load a trained network!"); + + // Segmentation Thread + connect(&m_SegmentationWatcher, SIGNAL(finished()), this, SLOT(DoBoneSegmentationProcessFinished())); + + // Connects + connect( + m_Controls.buttonPerformImageProcessing, &QPushButton::clicked, this, &BoneSegmentationRework::DoImageProcessing); + connect(m_Controls.m_ButtonLoadTrainedNet, &QPushButton::clicked, this, &BoneSegmentationRework::DoLoadTrainedNet); + connect(this->m_Controls.m_ImageSelector, + static_cast(&QComboBox::currentIndexChanged), + this, + &BoneSegmentationRework::OnImageSelectorChanged); +} + +void BoneSegmentationRework::OnImageSelectorChanged() +{ + auto selectedImageNode = m_Controls.m_ImageSelector->GetSelectedNode(); + if (selectedImageNode != m_selectedImageNode) + { + m_selectedImageNode = selectedImageNode; + if (m_selectedImageNode.IsNotNull()) + { + m_Controls.labelWarning->setVisible(false); + if (m_TrainedNet != "") + { + m_Controls.buttonPerformImageProcessing->setEnabled(true); + } + return; + } + m_Controls.labelWarning->setText("Please select an image!"); + m_Controls.labelWarning->setVisible(true); + m_Controls.buttonPerformImageProcessing->setEnabled(false); + } +} + +void BoneSegmentationRework::DoLoadTrainedNet() +{ + QString tempPath = QString::fromStdString(mitk::IOUtil::GetTempPathA()); + QString pretrainedNetResourcesPath = + QFileDialog::getOpenFileName(nullptr, tr("Open File"), tempPath, tr("Images (*.pth.tar)")); + + m_TrainedNet = pretrainedNetResourcesPath.toStdString(); + + if (m_TrainedNet != "") + { + m_Controls.labelLoadTrainedNet->setText("Network loaded!"); + if (m_selectedImageNode.IsNotNull()) + { + m_Controls.buttonPerformImageProcessing->setEnabled(true); + } + } +} + +void BoneSegmentationRework::DoImageProcessing() +{ + m_Controls.labelWarning->setVisible(true); + m_Controls.labelWarning->setText("This might take a while.\nDepending ob your machine up to 20 minutes or more."); + m_Controls.buttonPerformImageProcessing->setEnabled(false); + m_Controls.m_ButtonLoadTrainedNet->setEnabled(false); + m_Controls.m_ImageSelector->setEnabled(false); + this->DoStartBoneSegmentationProcess(); +} + +void BoneSegmentationRework::DoStartBoneSegmentationProcess() +{ + MITK_INFO << "[Start] DoStartBoneSegmentationProcess()"; + m_SegmentationFuture = QtConcurrent::run(this, &BoneSegmentationRework::DoRunPythonScript); + m_SegmentationWatcher.setFuture(m_SegmentationFuture); + MITK_INFO << "[END] DoStartBoneSegmentationProcess()"; +} + +void BoneSegmentationRework::DoRunPythonScript() +{ + QString pythonFileName(":/" + m_PythonFileName); + std::string fileName = mitk::StandardFileLocations::GetInstance()->FindFile( + "segment.py", "Plugins/org.mitk.gui.qt.bonesegmentationrework/resources"); + std::replace(fileName.begin(), fileName.end(), '\\', '/'); + MITK_INFO << "Python file to execute: " << fileName; + QString data; + QFile file(QString::fromUtf8(fileName.c_str())); + if (!file.open(QIODevice::ReadOnly)) + { + qDebug() << "filenot opened" << endl; + } + else + { + qDebug() << "file opened" << endl; + data = file.readAll(); + } + + file.close(); + std::string dataString = data.toStdString(); + if (!Py_IsInitialized()) + { + Py_Initialize(); + } + try + { + std::string pythonEnv = ImageTransferPython::SetUpPythonEnvironment(); + if (PyRun_SimpleString(pythonEnv.c_str()) == -1) + { + mitkThrow() << "An error occured while setting up the Python environment"; + } + // Set path for the trained network in python + std::string networkPath = "seg_load_network_path=\"" + m_TrainedNet + "\""; + if (PyRun_SimpleString(networkPath.c_str()) == -1) + { + mitkThrow() << "An error occured while setting the network path in Python"; + } + // Copy image for segmentation in Python + std::string copyImage = ImageTransferPython::CopyToPythonAsSimpleItkImage( + dynamic_cast(m_selectedImageNode->GetData()), "nrrd_image"); + if (PyRun_SimpleString(copyImage.c_str()) == -1) + { + mitkThrow() << "An error occured while copying the input image path to Python"; + } + // Execute the python script + if (PyRun_SimpleString(dataString.c_str()) == -1) + { + mitkThrow() << "An error occured while running the Python script"; + } + //Get the result of the segmentation + m_OutputImage = ImageTransferPython::CopySimpleItkImageFromPython("image_to_write"); + + m_SuccessfulProcessing = true; + } + catch (const mitk::Exception &e) + { + MITK_ERROR << e.GetDescription(); + m_SuccessfulProcessing = false; + return; + } +} + +void BoneSegmentationRework::DoLoadOutputImage() +{ + try + { + mitk::DataNode::Pointer outputNode = mitk::DataNode::New(); + outputNode->SetName("Bone_seg_" + m_selectedImageNode->GetName()); + outputNode->SetData(m_OutputImage); + GetDataStorage().GetPointer()->Add(outputNode, m_selectedImageNode); + } + catch (...) + { + MITK_WARN << "Error loading output file. Maybe it does not exist."; + } +} + +void BoneSegmentationRework::DoBoneSegmentationProcessFinished() +{ + if (!m_SuccessfulProcessing) + { + MITK_INFO << "Failed during calculation thread."; + QMessageBox::warning(nullptr, + "Error in segmentation", + "There was an error in the segmentation process. No resulting segmentation can be loaded."); + } + else + { + MITK_INFO << "Ended calculation thread."; + this->DoLoadOutputImage(); + } + m_Controls.buttonPerformImageProcessing->setText("Start Bone Segmentation"); + m_Controls.buttonPerformImageProcessing->setEnabled(true); + m_Controls.m_ButtonLoadTrainedNet->setEnabled(true); + m_Controls.labelWarning->setVisible(false); + m_Controls.m_ImageSelector->setEnabled(true); +} + +mitk::NodePredicateBase::Pointer BoneSegmentationRework::GetImagePredicate() +{ + auto isImage = mitk::NodePredicateDataType::New("Image"); + auto hasBinaryProperty = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + auto isNotBinary = mitk::NodePredicateNot::New(hasBinaryProperty); + auto isNotBinaryImage = mitk::NodePredicateAnd::New(isImage, isNotBinary); + auto hasHelperObjectProperty = mitk::NodePredicateProperty::New("helper object", nullptr); + auto isNoHelperObject = mitk::NodePredicateNot::New(hasHelperObjectProperty); + auto isNoHelperObjectPredicate = isNoHelperObject.GetPointer(); + + auto isImageForImageStatistics = mitk::NodePredicateAnd::New(isNotBinaryImage, isNoHelperObjectPredicate); + return isImageForImageStatistics.GetPointer(); +} diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.h b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.h new file mode 100644 index 0000000000..9c4b2a1c30 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationRework.h @@ -0,0 +1,85 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef BoneSegmentationRework_h +#define BoneSegmentationRework_h + +#include + +#include + +#include "ui_BoneSegmentationReworkControls.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +/** + \brief BoneSegmentationRework + + \warning The BoneSegmentationRework plugin provides an automatic bone segmentation for CT images based on deep learning. + At this moment, only CT images can be segmented. The segmentation is done on 2D axial slices. The network was trained + using data from the multiple myeloma project. It was trained on a very limited amount of training data and needs to + be tested in "real world scenarios". + + \sa QmitkAbstractView + \ingroup ${plugin_target}_internal +*/ +class BoneSegmentationRework : public QmitkAbstractView +{ + // 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; + void DoRunPythonScript(); + ~BoneSegmentationRework(); + +signals: + void Operate(QString); + +protected slots: + /// \brief Called when the user clicks the GUI button + void OnImageSelectorChanged(); + void DoImageProcessing(); + void DoStartBoneSegmentationProcess(); + void DoBoneSegmentationProcessFinished(); + void DoLoadTrainedNet(); + void DoLoadOutputImage(); + +protected: + virtual void CreateQtPartControl(QWidget *parent) override; + virtual void SetFocus() override; + + // Predicate helper + mitk::NodePredicateBase::Pointer GetImagePredicate(); + Ui::BoneSegmentationReworkControls m_Controls; + mitk::DataNode::Pointer m_selectedImageNode = nullptr; + std::string m_TrainedNet; + const QString m_PythonFileName="segment.py"; + QFutureWatcher m_SegmentationWatcher; + QFuture m_SegmentationFuture; + bool m_SuccessfulProcessing; + mitk::Image::Pointer m_OutputImage; +}; + +#endif // BoneSegmentationRework_h diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationReworkControls.ui b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationReworkControls.ui new file mode 100644 index 0000000000..423938e46a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/BoneSegmentationReworkControls.ui @@ -0,0 +1,88 @@ + + + BoneSegmentationReworkControls + + + + 0 + 0 + 222 + 161 + + + + + 0 + 0 + + + + QmitkTemplate + + + + + + Please load a trained network! + + + + + + + Load trained network + + + + + + + QLabel { color: rgb(255, 0, 0) } + + + Please select an image! + + + + + + + + + + Do image processing + + + Do Something + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 220 + + + + + + + + + + QmitkDataStorageComboBoxWithSelectNone + QComboBox +
QmitkDataStorageComboBoxWithSelectNone.h
+
+
+ + +
diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/ImageTransferPython.cpp b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/ImageTransferPython.cpp new file mode 100644 index 0000000000..ac434a6780 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/ImageTransferPython.cpp @@ -0,0 +1,537 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ +#include "ImageTransferPython.h" +#include +#include +#include +#include +#include + +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include +#include + +std::string ImageTransferPython::SetUpPythonEnvironment() +{ + std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/"; + QString pythonCommand; + pythonCommand.append(QString("import site, sys\n")); + pythonCommand.append(QString("import SimpleITK as sitk\n")); + pythonCommand.append(QString("import SimpleITK._SimpleITK as _SimpleITK\n")); + pythonCommand.append(QString("import numpy\n")); + + pythonCommand.append(QString("sys.path.append('')\n")); + pythonCommand.append(QString("sys.path.append('%1')\n").arg(programPath.c_str())); + pythonCommand.append(QString("sys.path.append('%1')\n").arg(EXTERNAL_DIST_PACKAGES)); + pythonCommand.append(QString("\nsite.addsitedir('%1')").arg(EXTERNAL_SITE_PACKAGES)); + + return pythonCommand.toStdString(); +} + +std::string ImageTransferPython::CopyToPythonAsSimpleItkImage(mitk::Image *image, const std::string &stdvarName) +{ + QString varName = QString::fromStdString(stdvarName); + QString command; + unsigned int *imgDim = image->GetDimensions(); + int npy_nd = 1; + + // access python module + PyObject *pyMod = PyImport_AddModule("__main__"); + // global dictionary + PyObject *pyDict = PyModule_GetDict(pyMod); + const mitk::Vector3D spacing = image->GetGeometry()->GetSpacing(); + const mitk::Point3D origin = image->GetGeometry()->GetOrigin(); + mitk::PixelType pixelType = image->GetPixelType(); + itk::ImageIOBase::IOPixelType ioPixelType = image->GetPixelType().GetPixelType(); + PyObject *npyArray = nullptr; + mitk::ImageReadAccessor racc(image); + void *array = const_cast(racc.GetData()); + + mitk::Vector3D xDirection; + mitk::Vector3D yDirection; + mitk::Vector3D zDirection; + const vnl_matrix_fixed &transform = + image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); + + mitk::Vector3D s = image->GetGeometry()->GetSpacing(); + + // ToDo: Check if this is a collumn or row vector from the matrix. + // right now it works but not sure for rotated geometries + mitk::FillVector3D(xDirection, transform[0][0] / s[0], transform[0][1] / s[1], transform[0][2] / s[2]); + mitk::FillVector3D(yDirection, transform[1][0] / s[0], transform[1][1] / s[1], transform[1][2] / s[2]); + mitk::FillVector3D(zDirection, transform[2][0] / s[0], transform[2][1] / s[1], transform[2][2] / s[2]); + + // save the total number of elements here (since the numpy array is one dimensional) + npy_intp *npy_dims = new npy_intp[1]; + npy_dims[0] = imgDim[0]; + + /** + * Build a string in the format [1024,1028,1] + * to describe the dimensionality. This is needed for simple itk + * to know the dimensions of the image + */ + QString dimensionString; + dimensionString.append(QString("[")); + dimensionString.append(QString::number(imgDim[0])); + for (unsigned i = 1; i < 3; ++i) + // always three because otherwise the 3d-geometry gets destroyed + // (relevant for backtransformation of simple itk image to mitk. + { + dimensionString.append(QString(",")); + dimensionString.append(QString::number(imgDim[i])); + npy_dims[0] *= imgDim[i]; + } + dimensionString.append("]"); + + // the next line is necessary for vectorimages + npy_dims[0] *= pixelType.GetNumberOfComponents(); + + // default pixeltype: unsigned short + NPY_TYPES npy_type = NPY_USHORT; + std::string sitk_type = "sitkUInt8"; + if (ioPixelType == itk::ImageIOBase::SCALAR) + { + if (pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE) + { + npy_type = NPY_DOUBLE; + sitk_type = "sitkFloat64"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::FLOAT) + { + npy_type = NPY_FLOAT; + sitk_type = "sitkFloat32"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::SHORT) + { + npy_type = NPY_SHORT; + sitk_type = "sitkInt16"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::CHAR) + { + npy_type = NPY_BYTE; + sitk_type = "sitkInt8"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::INT) + { + npy_type = NPY_INT; + sitk_type = "sitkInt32"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::LONG) + { + npy_type = NPY_LONG; + sitk_type = "sitkInt64"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::UCHAR) + { + npy_type = NPY_UBYTE; + sitk_type = "sitkUInt8"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::UINT) + { + npy_type = NPY_UINT; + sitk_type = "sitkUInt32"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::ULONG) + { + npy_type = NPY_LONG; + sitk_type = "sitkUInt64"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::USHORT) + { + npy_type = NPY_USHORT; + sitk_type = "sitkUInt16"; + } + } + else if (ioPixelType == itk::ImageIOBase::VECTOR || ioPixelType == itk::ImageIOBase::RGB || + ioPixelType == itk::ImageIOBase::RGBA) + { + if (pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE) + { + npy_type = NPY_DOUBLE; + sitk_type = "sitkVectorFloat64"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::FLOAT) + { + npy_type = NPY_FLOAT; + sitk_type = "sitkVectorFloat32"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::SHORT) + { + npy_type = NPY_SHORT; + sitk_type = "sitkVectorInt16"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::CHAR) + { + npy_type = NPY_BYTE; + sitk_type = "sitkVectorInt8"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::INT) + { + npy_type = NPY_INT; + sitk_type = "sitkVectorInt32"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::LONG) + { + npy_type = NPY_LONG; + sitk_type = "sitkVectorInt64"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::UCHAR) + { + npy_type = NPY_UBYTE; + sitk_type = "sitkVectorUInt8"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::UINT) + { + npy_type = NPY_UINT; + sitk_type = "sitkVectorUInt32"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::ULONG) + { + npy_type = NPY_LONG; + sitk_type = "sitkVectorUInt64"; + } + else if (pixelType.GetComponentType() == itk::ImageIOBase::USHORT) + { + npy_type = NPY_USHORT; + sitk_type = "sitkVectorUInt16"; + } + } + else + { + MITK_WARN << "not a recognized pixeltype"; + return false; + } + + // creating numpy array + import_array1("true"); + npyArray = PyArray_SimpleNewFromData(npy_nd, npy_dims, npy_type, array); + + // add temp array it to the python dictionary to access it in python code + const int status = + PyDict_SetItemString(pyDict, QString("%1_numpy_array").arg(varName).toStdString().c_str(), npyArray); + + // sanity check + if (status != 0) + return false; + + command.append(QString("%1 = sitk.Image(%2,sitk.%3,%4)\n") + .arg(varName) + .arg(dimensionString) + .arg(QString(sitk_type.c_str())) + .arg(QString::number(pixelType.GetNumberOfComponents()))); + command.append(QString("%1.SetSpacing([%2,%3,%4])\n") + .arg(varName) + .arg(QString::number(spacing[0])) + .arg(QString::number(spacing[1])) + .arg(QString::number(spacing[2]))); + command.append(QString("%1.SetOrigin([%2,%3,%4])\n") + .arg(varName) + .arg(QString::number(origin[0])) + .arg(QString::number(origin[1])) + .arg(QString::number(origin[2]))); + command.append(QString("%1.SetDirection([%2,%3,%4,%5,%6,%7,%8,%9,%10])\n") + .arg(varName) + .arg(QString::number(xDirection[0])) + .arg(QString::number(xDirection[1])) + .arg(QString::number(xDirection[2])) + .arg(QString::number(yDirection[0])) + .arg(QString::number(yDirection[1])) + .arg(QString::number(yDirection[2])) + .arg(QString::number(zDirection[0])) + .arg(QString::number(zDirection[1])) + .arg(QString::number(zDirection[2]))); + // directly access the cpp api from the lib + command.append(QString("_SimpleITK._SetImageFromArray(%1_numpy_array,%1)\n").arg(varName)); + command.append(QString("del %1_numpy_array").arg(varName)); + + return command.toStdString(); +} + +mitk::PixelType DeterminePixelType(const std::string &pythonPixeltype, unsigned long nrComponents, int dimensions) +{ + typedef itk::RGBPixel UCRGBPixelType; + typedef itk::RGBPixel USRGBPixelType; + typedef itk::RGBPixel FloatRGBPixelType; + typedef itk::RGBPixel DoubleRGBPixelType; + typedef itk::Image UCRGBImageType; + typedef itk::Image USRGBImageType; + typedef itk::Image FloatRGBImageType; + typedef itk::Image DoubleRGBImageType; + typedef itk::RGBAPixel UCRGBAPixelType; + typedef itk::RGBAPixel USRGBAPixelType; + typedef itk::RGBAPixel FloatRGBAPixelType; + typedef itk::RGBAPixel DoubleRGBAPixelType; + typedef itk::Image UCRGBAImageType; + typedef itk::Image USRGBAImageType; + typedef itk::Image FloatRGBAImageType; + typedef itk::Image DoubleRGBAImageType; + + auto pixelType = mitk::MakePixelType(nrComponents); + + if (nrComponents == 1) + { + if (pythonPixeltype.compare("float64") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("float32") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("int16") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("int8") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("int32") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("int64") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("uint8") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("uint32") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("uint64") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else if (pythonPixeltype.compare("uint16") == 0) + { + pixelType = mitk::MakePixelType(nrComponents); + } + else + { + mitkThrow() << "unknown scalar PixelType"; + } + } + else if (nrComponents == 3 && dimensions == 2) + { + if (pythonPixeltype.compare("float64") == 0) + { + pixelType = mitk::MakePixelType(); + } + else if (pythonPixeltype.compare("float32") == 0) + { + pixelType = mitk::MakePixelType(); + } + else if (pythonPixeltype.compare("uint8") == 0) + { + pixelType = mitk::MakePixelType(); + } + else if (pythonPixeltype.compare("uint16") == 0) + { + pixelType = mitk::MakePixelType(); + } + } + else if ((nrComponents == 4) && dimensions == 2) + { + if (pythonPixeltype.compare("float64") == 0) + { + pixelType = mitk::MakePixelType(); + } + else if (pythonPixeltype.compare("float32") == 0) + { + pixelType = mitk::MakePixelType(); + } + else if (pythonPixeltype.compare("uint8") == 0) + { + pixelType = mitk::MakePixelType(); + } + else if (pythonPixeltype.compare("uint16") == 0) + { + pixelType = mitk::MakePixelType(); + } + } + else + { + if (pythonPixeltype.compare("float64") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("float32") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("int16") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("int8") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("int32") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("int64") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("uint8") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("uint16") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("uint32") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else if (pythonPixeltype.compare("uint64") == 0) + { + pixelType = mitk::MakePixelType>(nrComponents); + } + else + { + mitkThrow() << "unknown vectorial PixelType"; + } + } + + return pixelType; +} + +mitk::Image::Pointer ImageTransferPython::CopySimpleItkImageFromPython(const std::string &stdvarName) +{ + if (!Py_IsInitialized()) + { + MITK_INFO << "init python"; + Py_Initialize(); + } + double *ds = nullptr; + // access python module + PyObject *pyMod = PyImport_AddModule("__main__"); + // global dictionarry + PyObject *pyDict = PyModule_GetDict(pyMod); + mitk::Image::Pointer mitkImage = mitk::Image::New(); + mitk::Vector3D spacing; + mitk::Point3D origin; + QString command; + QString varName = QString::fromStdString(stdvarName); + + command.append(QString("%1_numpy_array = sitk.GetArrayFromImage(%1)\n").arg(varName)); + command.append(QString("%1_spacing = numpy.asarray(%1.GetSpacing())\n").arg(varName)); + command.append(QString("%1_origin = numpy.asarray(%1.GetOrigin())\n").arg(varName)); + command.append(QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName)); + command.append(QString("%1_direction = numpy.asarray(%1.GetDirection())\n").arg(varName)); + command.append(QString("%1_nrComponents = numpy.asarray(%1.GetNumberOfComponentsPerPixel())\n").arg(varName)); + command.append(QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName)); + + MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); + + if (PyRun_SimpleString(command.toStdString().c_str()) == -1) + { + mitkThrow() << "Something went wrong while getting the Image from Python"; + } + + PyObject *py_dtype = PyDict_GetItemString(pyDict, QString("%1_dtype").arg(varName).toStdString().c_str()); + std::string dtype = PyString_AsString(py_dtype); + PyArrayObject *py_data = + (PyArrayObject *)PyDict_GetItemString(pyDict, QString("%1_numpy_array").arg(varName).toStdString().c_str()); + PyArrayObject *py_spacing = + (PyArrayObject *)PyDict_GetItemString(pyDict, QString("%1_spacing").arg(varName).toStdString().c_str()); + PyArrayObject *py_origin = + (PyArrayObject *)PyDict_GetItemString(pyDict, QString("%1_origin").arg(varName).toStdString().c_str()); + PyArrayObject *py_direction = + (PyArrayObject *)PyDict_GetItemString(pyDict, QString("%1_direction").arg(varName).toStdString().c_str()); + + PyArrayObject *py_nrComponents = + (PyArrayObject *)PyDict_GetItemString(pyDict, QString("%1_nrComponents").arg(varName).toStdString().c_str()); + + unsigned int nr_Components = *(reinterpret_cast(PyArray_DATA(py_nrComponents))); + + unsigned int nr_dimensions = PyArray_NDIM(py_data); + if (nr_Components > 1) // for VectorImages the last dimension in the numpy array are the vector components. + { + --nr_dimensions; + } + + mitk::PixelType pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions); + + unsigned int *dimensions = new unsigned int[nr_dimensions]; + // fill backwards , nd data saves dimensions in opposite direction + for (unsigned i = 0; i < nr_dimensions; ++i) + { + dimensions[i] = PyArray_DIMS(py_data)[nr_dimensions - 1 - i]; + } + + mitkImage->Initialize(pixelType, nr_dimensions, dimensions); + + mitkImage->SetChannel(PyArray_DATA(py_data)); + + ds = reinterpret_cast(PyArray_DATA(py_spacing)); + spacing[0] = ds[0]; + spacing[1] = ds[1]; + spacing[2] = ds[2]; + + mitkImage->GetGeometry()->SetSpacing(spacing); + + ds = reinterpret_cast(PyArray_DATA(py_origin)); + origin[0] = ds[0]; + origin[1] = ds[1]; + origin[2] = ds[2]; + mitkImage->GetGeometry()->SetOrigin(origin); + + itk::Matrix py_transform; + + ds = reinterpret_cast(PyArray_DATA(py_direction)); + py_transform[0][0] = ds[0]; + py_transform[0][1] = ds[1]; + py_transform[0][2] = ds[2]; + + py_transform[1][0] = ds[3]; + py_transform[1][1] = ds[4]; + py_transform[1][2] = ds[5]; + + py_transform[2][0] = ds[6]; + py_transform[2][1] = ds[7]; + py_transform[2][2] = ds[8]; + + mitk::AffineTransform3D::Pointer affineTransform = mitkImage->GetGeometry()->GetIndexToWorldTransform(); + + itk::Matrix transform = py_transform * affineTransform->GetMatrix(); + + affineTransform->SetMatrix(transform); + + mitkImage->GetGeometry()->SetIndexToWorldTransform(affineTransform); + + // cleanup + command.clear(); + command.append(QString("del %1_numpy_array\n").arg(varName)); + command.append(QString("del %1_dtype\n").arg(varName)); + command.append(QString("del %1_spacing\n").arg(varName)); + command.append(QString("del %1_origin\n").arg(varName)); + command.append(QString("del %1_direction\n").arg(varName)); + command.append(QString("del %1_nrComponents\n").arg(varName)); + MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); + if (PyRun_SimpleString(command.toStdString().c_str()) == -1) + { + mitkThrow() << "Something went wrong while cleaning up the variables from getting the Image from Python"; + } + delete[] dimensions; + + MITK_INFO << "img dim" << mitkImage->GetDimension(); + return mitkImage; +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/ImageTransferPython.h b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/ImageTransferPython.h new file mode 100644 index 0000000000..d9ff24a6a2 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/ImageTransferPython.h @@ -0,0 +1,26 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ +#ifndef ImageTransferPython_h +#define ImageTransferPython_h + +#include +#include + +class ImageTransferPython +{ + public: + static std::string SetUpPythonEnvironment(); + static std::string CopyToPythonAsSimpleItkImage(mitk::Image *image, const std::string &stdvarName); + static mitk::Image::Pointer CopySimpleItkImageFromPython(const std::string &varName); +}; + +#endif \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.cpp b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.cpp new file mode 100644 index 0000000000..ddda51cffc --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.cpp @@ -0,0 +1,42 @@ +/*=================================================================== + +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 "org_mitk_gui_qt_bonesegmentationrework_Activator.h" +#include "BoneSegmentationRework.h" +#include + +US_INITIALIZE_MODULE + +ctkPluginContext *mitk::org_mitk_gui_qt_bonesegmentationrework_Activator::m_Context = nullptr; + +void mitk::org_mitk_gui_qt_bonesegmentationrework_Activator::start(ctkPluginContext *context) +{ + BERRY_REGISTER_EXTENSION_CLASS(BoneSegmentationRework, context) + this->m_Context = context; +} + +void mitk::org_mitk_gui_qt_bonesegmentationrework_Activator::stop(ctkPluginContext *context) +{ + Q_UNUSED(context) + m_Context = nullptr; +} + +ctkPluginContext *mitk::org_mitk_gui_qt_bonesegmentationrework_Activator::getContext() +{ + return m_Context; +} + diff --git a/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h new file mode 100644 index 0000000000..6c2ad7e759 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentationrework/src/internal/org_mitk_gui_qt_bonesegmentationrework_Activator.h @@ -0,0 +1,47 @@ +/*=================================================================== + +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 MITKPLUGINACTIVATOR_H +#define MITKPLUGINACTIVATOR_H + +#include + +namespace mitk +{ + + class org_mitk_gui_qt_bonesegmentationrework_Activator : public QObject, public ctkPluginActivator + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_bonesegmentationrework") + Q_INTERFACES(ctkPluginActivator) + + public: + void start(ctkPluginContext *context) override; + void stop(ctkPluginContext *context) override; + + static ctkPluginContext* getContext(); + + private: + + static ctkPluginContext* m_Context; + }; // org_mitk_gui_qt_bonesegmentationrework_Activator + + typedef org_mitk_gui_qt_bonesegmentationrework_Activator PluginActivator; + +} + +#endif // MITKPLUGINACTIVATOR_H