diff --git a/ai/brain_mri_t1/model.h5 b/ai/brain_mri_t1/model.h5 new file mode 100644 index 0000000..13294ca Binary files /dev/null and b/ai/brain_mri_t1/model.h5 differ diff --git a/ai/brain_mri_t1/model.json b/ai/brain_mri_t1/model.json new file mode 100644 index 0000000..d302f7a --- /dev/null +++ b/ai/brain_mri_t1/model.json @@ -0,0 +1 @@ +{"class_name": "Model", "config": {"name": "model_1", "layers": [{"name": "img", "class_name": "InputLayer", "config": {"batch_input_shape": [null, 48, 48, 48, 1], "dtype": "float32", "sparse": false, "name": "img"}, "inbound_nodes": []}, {"name": "conv3d_1", "class_name": "Conv3D", "config": {"name": "conv3d_1", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["img", 0, 0, {}]]]}, {"name": "conv3d_2", "class_name": "Conv3D", "config": {"name": "conv3d_2", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_1", 0, 0, {}]]]}, {"name": "max_pooling3d_1", "class_name": "MaxPooling3D", "config": {"name": "max_pooling3d_1", "trainable": true, "pool_size": [2, 2, 2], "padding": "valid", "strides": [2, 2, 2], "data_format": "channels_last"}, "inbound_nodes": [[["conv3d_2", 0, 0, {}]]]}, {"name": "dropout_1", "class_name": "Dropout", "config": {"name": "dropout_1", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["max_pooling3d_1", 0, 0, {}]]]}, {"name": "conv3d_3", "class_name": "Conv3D", "config": {"name": "conv3d_3", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["dropout_1", 0, 0, {}]]]}, {"name": "conv3d_4", "class_name": "Conv3D", "config": {"name": "conv3d_4", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_3", 0, 0, {}]]]}, {"name": "max_pooling3d_2", "class_name": "MaxPooling3D", "config": {"name": "max_pooling3d_2", "trainable": true, "pool_size": [2, 2, 2], "padding": "valid", "strides": [2, 2, 2], "data_format": "channels_last"}, "inbound_nodes": [[["conv3d_4", 0, 0, {}]]]}, {"name": "dropout_2", "class_name": "Dropout", "config": {"name": "dropout_2", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["max_pooling3d_2", 0, 0, {}]]]}, {"name": "conv3d_5", "class_name": "Conv3D", "config": {"name": "conv3d_5", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["dropout_2", 0, 0, {}]]]}, {"name": "conv3d_6", "class_name": "Conv3D", "config": {"name": "conv3d_6", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_5", 0, 0, {}]]]}, {"name": "max_pooling3d_3", "class_name": "MaxPooling3D", "config": {"name": "max_pooling3d_3", "trainable": true, "pool_size": [2, 2, 2], "padding": "valid", "strides": [2, 2, 2], "data_format": "channels_last"}, "inbound_nodes": [[["conv3d_6", 0, 0, {}]]]}, {"name": "dropout_3", "class_name": "Dropout", "config": {"name": "dropout_3", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["max_pooling3d_3", 0, 0, {}]]]}, {"name": "conv3d_transpose_1", "class_name": "Conv3DTranspose", "config": {"name": "conv3d_transpose_1", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [2, 2, 2], "padding": "same", "data_format": "channels_last", "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null, "output_padding": null}, "inbound_nodes": [[["dropout_3", 0, 0, {}]]]}, {"name": "concatenate_1", "class_name": "Concatenate", "config": {"name": "concatenate_1", "trainable": true, "axis": -1}, "inbound_nodes": [[["conv3d_transpose_1", 0, 0, {}], ["conv3d_6", 0, 0, {}]]]}, {"name": "conv3d_7", "class_name": "Conv3D", "config": {"name": "conv3d_7", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["concatenate_1", 0, 0, {}]]]}, {"name": "dropout_4", "class_name": "Dropout", "config": {"name": "dropout_4", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["conv3d_7", 0, 0, {}]]]}, {"name": "conv3d_transpose_2", "class_name": "Conv3DTranspose", "config": {"name": "conv3d_transpose_2", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [2, 2, 2], "padding": "same", "data_format": "channels_last", "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null, "output_padding": null}, "inbound_nodes": [[["dropout_4", 0, 0, {}]]]}, {"name": "concatenate_2", "class_name": "Concatenate", "config": {"name": "concatenate_2", "trainable": true, "axis": -1}, "inbound_nodes": [[["conv3d_transpose_2", 0, 0, {}], ["conv3d_4", 0, 0, {}]]]}, {"name": "conv3d_8", "class_name": "Conv3D", "config": {"name": "conv3d_8", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["concatenate_2", 0, 0, {}]]]}, {"name": "dropout_5", "class_name": "Dropout", "config": {"name": "dropout_5", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["conv3d_8", 0, 0, {}]]]}, {"name": "conv3d_transpose_3", "class_name": "Conv3DTranspose", "config": {"name": "conv3d_transpose_3", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [2, 2, 2], "padding": "same", "data_format": "channels_last", "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null, "output_padding": null}, "inbound_nodes": [[["dropout_5", 0, 0, {}]]]}, {"name": "concatenate_3", "class_name": "Concatenate", "config": {"name": "concatenate_3", "trainable": true, "axis": -1}, "inbound_nodes": [[["conv3d_transpose_3", 0, 0, {}], ["conv3d_2", 0, 0, {}]]]}, {"name": "conv3d_9", "class_name": "Conv3D", "config": {"name": "conv3d_9", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["concatenate_3", 0, 0, {}]]]}, {"name": "dropout_6", "class_name": "Dropout", "config": {"name": "dropout_6", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["conv3d_9", 0, 0, {}]]]}, {"name": "conv3d_10", "class_name": "Conv3D", "config": {"name": "conv3d_10", "trainable": true, "filters": 1, "kernel_size": [1, 1, 1], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["dropout_6", 0, 0, {}]]]}, {"name": "dense_1", "class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "units": 1, "activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_10", 0, 0, {}]]]}], "input_layers": [["img", 0, 0]], "output_layers": [["dense_1", 0, 0]]}, "keras_version": "2.2.4", "backend": "plaidml.keras.backend"} \ No newline at end of file diff --git a/app.py b/app.py index bd4fb87..2d4b509 100644 --- a/app.py +++ b/app.py @@ -65,6 +65,7 @@ import invesalius.utils as utils from invesalius import inv_paths FS_ENCODE = sys.getfilesystemencoding() +LANG = None # ------------------------------------------------------------------ @@ -78,6 +79,17 @@ if sys.platform in ('linux2', 'linux', 'win32'): del tmp_var +session = ses.Session() +if session.ReadSession(): + lang = session.GetLanguage() + if lang: + LANG = lang + try: + _ = i18n.InstallLanguage(lang) + except FileNotFoundError: + LANG = None + + class InVesalius(wx.App): """ InVesalius wxPython application class. @@ -119,26 +131,18 @@ class Inv3SplashScreen(SplashScreen): """ def __init__(self): # Splash screen image will depend on currently language - lang = False - + lang = LANG self.locale = wx.Locale(wx.LANGUAGE_DEFAULT) # Language information is available in session configuration # file. First we need to check if this file exist, if now, it # should be created - create_session = False - session = ses.Session() - if not (session.ReadSession()): - create_session = True + create_session = LANG is None install_lang = 0 - lang = session.GetLanguage() if lang: - if (lang != "False"): - _ = i18n.InstallLanguage(lang) - install_lang = 1 - else: - install_lang = 0 + _ = i18n.InstallLanguage(lang) + install_lang = 1 else: install_lang = 0 @@ -262,7 +266,10 @@ class Inv3SplashScreen(SplashScreen): def non_gui_startup(options, args): - lang = 'en' + if LANG: + lang = LANG + else: + lang = 'en' _ = i18n.InstallLanguage(lang) from invesalius.control import Controller diff --git a/invesalius/gui/brain_seg_dialog.py b/invesalius/gui/brain_seg_dialog.py index 4536884..0e02520 100644 --- a/invesalius/gui/brain_seg_dialog.py +++ b/invesalius/gui/brain_seg_dialog.py @@ -2,76 +2,85 @@ # -*- coding: UTF-8 -*- import importlib +import multiprocessing import os import pathlib +import subprocess import sys +import tempfile import time +import numpy as np import wx from pubsub import pub as Publisher -# Linux if installed plaidml with pip3 install --user -if sys.platform.startswith("linux"): - local_user_plaidml = pathlib.Path("~/.local/share/plaidml/").expanduser().absolute() - if local_user_plaidml.exists(): - os.environ["RUNFILES_DIR"] = str(local_user_plaidml) - os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("~/.local/lib/libplaidml.so").expanduser().absolute()) -# Mac if using python3 from homebrew -elif sys.platform == "darwin": - local_user_plaidml = pathlib.Path("/usr/local/share/plaidml") - if local_user_plaidml.exists(): - os.environ["RUNFILES_DIR"] = str(local_user_plaidml) - os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("/usr/local/lib/libplaidml.dylib").expanduser().absolute()) +import invesalius.data.slice_ as slc +from invesalius.segmentation.brain import segment, utils HAS_THEANO = bool(importlib.util.find_spec("theano")) HAS_PLAIDML = bool(importlib.util.find_spec("plaidml")) PLAIDML_DEVICES = {} -import invesalius.data.slice_ as slc -from invesalius.segmentation.brain import segment -from invesalius.segmentation.brain import utils - -try: - import theano -except ImportError: - HAS_THEANO = False - -try: - import plaidml -except ImportError: - HAS_PLAIDML = False if HAS_PLAIDML: - try: - PLAIDML_DEVICES = utils.get_plaidml_devices() - except OSError: - HAS_PLAIDML = False + with multiprocessing.Pool(1) as p: + try: + PLAIDML_DEVICES = p.apply(utils.get_plaidml_devices) + except Exception as err: + print(err) + PLAIDML_DEVICES = {} + HAS_PLAIDML = False class BrainSegmenterDialog(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, -1, _(u"Brain segmentation"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) + wx.Dialog.__init__( + self, + parent, + -1, + _(u"Brain segmentation"), + style=wx.DEFAULT_DIALOG_STYLE | wx.FRAME_FLOAT_ON_PARENT, + ) backends = [] if HAS_PLAIDML: backends.append("PlaidML") if HAS_THEANO: backends.append("Theano") - self.segmenter = segment.BrainSegmenter() + # self.segmenter = segment.BrainSegmenter() # self.pg_dialog = None self.plaidml_devices = PLAIDML_DEVICES - self.cb_backends = wx.ComboBox(self, wx.ID_ANY, choices=backends, value=backends[0], style=wx.CB_DROPDOWN | wx.CB_READONLY) + + self.ps = None + self.segmented = False + self.mask = None + + self.cb_backends = wx.ComboBox( + self, + wx.ID_ANY, + choices=backends, + value=backends[0], + style=wx.CB_DROPDOWN | wx.CB_READONLY, + ) w, h = self.CalcSizeFromTextSize("MM" * (1 + max(len(i) for i in backends))) self.cb_backends.SetMinClientSize((w, -1)) self.chk_use_gpu = wx.CheckBox(self, wx.ID_ANY, _("Use GPU")) if HAS_PLAIDML: self.lbl_device = wx.StaticText(self, -1, _("Device")) - self.cb_devices = wx.ComboBox(self, wx.ID_ANY, choices=list(self.plaidml_devices.keys()), value=list(self.plaidml_devices.keys())[0],style=wx.CB_DROPDOWN | wx.CB_READONLY) + self.cb_devices = wx.ComboBox( + self, + wx.ID_ANY, + choices=list(self.plaidml_devices.keys()), + value=list(self.plaidml_devices.keys())[0], + style=wx.CB_DROPDOWN | wx.CB_READONLY, + ) self.sld_threshold = wx.Slider(self, wx.ID_ANY, 75, 0, 100) w, h = self.CalcSizeFromTextSize("M" * 20) self.sld_threshold.SetMinClientSize((w, -1)) self.txt_threshold = wx.TextCtrl(self, wx.ID_ANY, "") w, h = self.CalcSizeFromTextSize("MMMMM") self.txt_threshold.SetMinClientSize((w, -1)) + self.chk_new_mask = wx.CheckBox(self, wx.ID_ANY, _("Create new mask")) + self.chk_new_mask.SetValue(True) self.progress = wx.Gauge(self, -1) self.lbl_progress_caption = wx.StaticText(self, -1, _("Elapsed time:")) self.lbl_time = wx.StaticText(self, -1, _("00:00:00")) @@ -103,18 +112,30 @@ class BrainSegmenterDialog(wx.Dialog): main_sizer.Add(sizer_devices, 0, wx.ALL | wx.EXPAND, 5) label_5 = wx.StaticText(self, wx.ID_ANY, _("Level of certainty")) main_sizer.Add(label_5, 0, wx.ALL, 5) - sizer_3.Add(self.sld_threshold, 1, wx.ALIGN_CENTER | wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.RIGHT, 5) + sizer_3.Add( + self.sld_threshold, + 1, + wx.ALIGN_CENTER | wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.RIGHT, + 5, + ) sizer_3.Add(self.txt_threshold, 0, wx.ALL, 5) main_sizer.Add(sizer_3, 0, wx.EXPAND, 0) + main_sizer.Add(self.chk_new_mask, 0, wx.EXPAND | wx.ALL, 5) main_sizer.Add(self.progress, 0, wx.EXPAND | wx.ALL, 5) time_sizer = wx.BoxSizer(wx.HORIZONTAL) time_sizer.Add(self.lbl_progress_caption, 0, wx.EXPAND, 0) time_sizer.Add(self.lbl_time, 1, wx.EXPAND | wx.LEFT, 5) main_sizer.Add(time_sizer, 0, wx.EXPAND | wx.ALL, 5) sizer_buttons = wx.BoxSizer(wx.HORIZONTAL) - sizer_buttons.Add(self.btn_close, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5) - sizer_buttons.Add(self.btn_stop, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5) - sizer_buttons.Add(self.btn_segment, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5) + sizer_buttons.Add( + self.btn_close, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5 + ) + sizer_buttons.Add( + self.btn_stop, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5 + ) + sizer_buttons.Add( + self.btn_segment, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5 + ) main_sizer.Add(sizer_buttons, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 0) self.SetSizer(main_sizer) main_sizer.Fit(self) @@ -138,6 +159,13 @@ class BrainSegmenterDialog(wx.Dialog): self.elapsed_time_timer.Bind(wx.EVT_TIMER, self.OnTickTimer) self.Bind(wx.EVT_CLOSE, self.OnClose) + def apply_segment_threshold(self): + threshold = self.sld_threshold.GetValue() / 100.0 + if self.ps is not None: + self.ps.apply_segment_threshold(threshold) + slc.Slice().discard_all_buffers() + Publisher.sendMessage("Reload actual slice") + def CalcSizeFromTextSize(self, text): dc = wx.WindowDC(self) dc.SetFont(self.GetFont()) @@ -162,15 +190,12 @@ class BrainSegmenterDialog(wx.Dialog): def OnScrollThreshold(self, evt): value = self.sld_threshold.GetValue() self.txt_threshold.SetValue("{:3d}%".format(self.sld_threshold.GetValue())) - if self.segmenter.segmented: - threshold = value / 100.0 - self.segmenter.set_threshold(threshold) - image = slc.Slice().discard_all_buffers() - Publisher.sendMessage('Reload actual slice') + if self.segmented: + self.apply_segment_threshold() def OnKillFocus(self, evt): value = self.txt_threshold.GetValue() - value = value.replace('%', '') + value = value.replace("%", "") try: value = int(value) except ValueError: @@ -178,11 +203,8 @@ class BrainSegmenterDialog(wx.Dialog): self.sld_threshold.SetValue(value) self.txt_threshold.SetValue("{:3d}%".format(value)) - if self.segmenter.segmented: - threshold = value / 100.0 - self.segmenter.set_threshold(threshold) - image = slc.Slice().discard_all_buffers() - Publisher.sendMessage('Reload actual slice') + if self.segmented: + self.apply_segment_threshold() def OnSegment(self, evt): self.ShowProgress() @@ -194,44 +216,97 @@ class BrainSegmenterDialog(wx.Dialog): device_id = self.plaidml_devices[self.cb_devices.GetValue()] except (KeyError, AttributeError): device_id = "llvm_cpu.0" + create_new_mask = self.chk_new_mask.GetValue() use_gpu = self.chk_use_gpu.GetValue() prob_threshold = self.sld_threshold.GetValue() / 100.0 self.btn_close.Disable() self.btn_stop.Enable() self.btn_segment.Disable() - self.segmenter.segment(image, prob_threshold, backend, device_id, use_gpu, self.SetProgress, self.AfterSegment) + self.chk_new_mask.Disable() + + try: + self.ps = segment.SegmentProcess(image, create_new_mask, backend, device_id, use_gpu) + self.ps.start() + except (multiprocessing.ProcessError, OSError, ValueError) as err: + self.OnStop(None) + self.HideProgress() + dlg = wx.MessageDialog( + None, + "It was not possible to start brain segmentation because:" + + "\n" + + str(err), + "Brain segmentation error", + wx.ICON_ERROR | wx.OK, + ) + dlg.ShowModal() def OnStop(self, evt): - self.segmenter.stop = True + if self.ps is not None: + self.ps.terminate() self.btn_close.Enable() self.btn_stop.Disable() self.btn_segment.Enable() + self.chk_new_mask.Enable() self.elapsed_time_timer.Stop() - evt.Skip() def OnBtnClose(self, evt): self.Close() def AfterSegment(self): + self.segmented = True self.btn_close.Enable() self.btn_stop.Disable() self.btn_segment.Disable() - Publisher.sendMessage('Reload actual slice') + self.chk_new_mask.Disable() self.elapsed_time_timer.Stop() + self.apply_segment_threshold() def SetProgress(self, progress): self.progress.SetValue(progress * 100) wx.GetApp().Yield() def OnTickTimer(self, evt): - fmt='%H:%M:%S' - self.lbl_time.SetLabel(time.strftime(fmt, time.gmtime(time.time()-self.t0))) + fmt = "%H:%M:%S" + self.lbl_time.SetLabel(time.strftime(fmt, time.gmtime(time.time() - self.t0))) + if self.ps is not None: + if not self.ps.is_alive() and self.ps.exception is not None: + error, traceback = self.ps.exception + self.OnStop(None) + self.HideProgress() + dlg = wx.MessageDialog( + None, + "It was not possible to use brain segmentation because:" + + "\n" + + str(error) + + "\n" + + traceback, + "Brain segmentation error", + wx.ICON_ERROR | wx.OK, + ) + dlg.ShowModal() + return + + progress = self.ps.get_completion() + if progress == np.Inf: + progress = 1 + self.AfterSegment() + if progress < 0: + progress = 0 + if progress > 1: + progress = 1 + self.SetProgress(float(progress)) def OnClose(self, evt): - self.segmenter.stop = True + # self.segmenter.stop = True self.btn_stop.Disable() self.btn_segment.Enable() + self.chk_new_mask.Enable() self.progress.SetValue(0) + + if self.ps is not None: + self.ps.terminate() + self.ps = None + self.Destroy() def HideProgress(self): @@ -257,6 +332,7 @@ class MyApp(wx.App): self.dlg_brain_seg.Destroy() return True + if __name__ == "__main__": app = MyApp(0) app.MainLoop() diff --git a/invesalius/inv_paths.py b/invesalius/inv_paths.py index 188e305..96ea744 100644 --- a/invesalius/inv_paths.py +++ b/invesalius/inv_paths.py @@ -46,6 +46,7 @@ RAYCASTING_PRESETS_COLOR_DIRECTORY = INV_TOP_DIR.joinpath( "presets", "raycasting", "color_list" ) +MODELS_DIR = INV_TOP_DIR.joinpath("ai") # Inside the windows executable if hasattr(sys, "frozen") and ( diff --git a/invesalius/segmentation/brain/model.h5 b/invesalius/segmentation/brain/model.h5 deleted file mode 100644 index 13294ca..0000000 Binary files a/invesalius/segmentation/brain/model.h5 and /dev/null differ diff --git a/invesalius/segmentation/brain/model.json b/invesalius/segmentation/brain/model.json deleted file mode 100644 index d302f7a..0000000 --- a/invesalius/segmentation/brain/model.json +++ /dev/null @@ -1 +0,0 @@ -{"class_name": "Model", "config": {"name": "model_1", "layers": [{"name": "img", "class_name": "InputLayer", "config": {"batch_input_shape": [null, 48, 48, 48, 1], "dtype": "float32", "sparse": false, "name": "img"}, "inbound_nodes": []}, {"name": "conv3d_1", "class_name": "Conv3D", "config": {"name": "conv3d_1", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["img", 0, 0, {}]]]}, {"name": "conv3d_2", "class_name": "Conv3D", "config": {"name": "conv3d_2", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_1", 0, 0, {}]]]}, {"name": "max_pooling3d_1", "class_name": "MaxPooling3D", "config": {"name": "max_pooling3d_1", "trainable": true, "pool_size": [2, 2, 2], "padding": "valid", "strides": [2, 2, 2], "data_format": "channels_last"}, "inbound_nodes": [[["conv3d_2", 0, 0, {}]]]}, {"name": "dropout_1", "class_name": "Dropout", "config": {"name": "dropout_1", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["max_pooling3d_1", 0, 0, {}]]]}, {"name": "conv3d_3", "class_name": "Conv3D", "config": {"name": "conv3d_3", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["dropout_1", 0, 0, {}]]]}, {"name": "conv3d_4", "class_name": "Conv3D", "config": {"name": "conv3d_4", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_3", 0, 0, {}]]]}, {"name": "max_pooling3d_2", "class_name": "MaxPooling3D", "config": {"name": "max_pooling3d_2", "trainable": true, "pool_size": [2, 2, 2], "padding": "valid", "strides": [2, 2, 2], "data_format": "channels_last"}, "inbound_nodes": [[["conv3d_4", 0, 0, {}]]]}, {"name": "dropout_2", "class_name": "Dropout", "config": {"name": "dropout_2", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["max_pooling3d_2", 0, 0, {}]]]}, {"name": "conv3d_5", "class_name": "Conv3D", "config": {"name": "conv3d_5", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["dropout_2", 0, 0, {}]]]}, {"name": "conv3d_6", "class_name": "Conv3D", "config": {"name": "conv3d_6", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_5", 0, 0, {}]]]}, {"name": "max_pooling3d_3", "class_name": "MaxPooling3D", "config": {"name": "max_pooling3d_3", "trainable": true, "pool_size": [2, 2, 2], "padding": "valid", "strides": [2, 2, 2], "data_format": "channels_last"}, "inbound_nodes": [[["conv3d_6", 0, 0, {}]]]}, {"name": "dropout_3", "class_name": "Dropout", "config": {"name": "dropout_3", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["max_pooling3d_3", 0, 0, {}]]]}, {"name": "conv3d_transpose_1", "class_name": "Conv3DTranspose", "config": {"name": "conv3d_transpose_1", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [2, 2, 2], "padding": "same", "data_format": "channels_last", "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null, "output_padding": null}, "inbound_nodes": [[["dropout_3", 0, 0, {}]]]}, {"name": "concatenate_1", "class_name": "Concatenate", "config": {"name": "concatenate_1", "trainable": true, "axis": -1}, "inbound_nodes": [[["conv3d_transpose_1", 0, 0, {}], ["conv3d_6", 0, 0, {}]]]}, {"name": "conv3d_7", "class_name": "Conv3D", "config": {"name": "conv3d_7", "trainable": true, "filters": 32, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["concatenate_1", 0, 0, {}]]]}, {"name": "dropout_4", "class_name": "Dropout", "config": {"name": "dropout_4", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["conv3d_7", 0, 0, {}]]]}, {"name": "conv3d_transpose_2", "class_name": "Conv3DTranspose", "config": {"name": "conv3d_transpose_2", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [2, 2, 2], "padding": "same", "data_format": "channels_last", "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null, "output_padding": null}, "inbound_nodes": [[["dropout_4", 0, 0, {}]]]}, {"name": "concatenate_2", "class_name": "Concatenate", "config": {"name": "concatenate_2", "trainable": true, "axis": -1}, "inbound_nodes": [[["conv3d_transpose_2", 0, 0, {}], ["conv3d_4", 0, 0, {}]]]}, {"name": "conv3d_8", "class_name": "Conv3D", "config": {"name": "conv3d_8", "trainable": true, "filters": 16, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["concatenate_2", 0, 0, {}]]]}, {"name": "dropout_5", "class_name": "Dropout", "config": {"name": "dropout_5", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["conv3d_8", 0, 0, {}]]]}, {"name": "conv3d_transpose_3", "class_name": "Conv3DTranspose", "config": {"name": "conv3d_transpose_3", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [2, 2, 2], "padding": "same", "data_format": "channels_last", "activation": "linear", "use_bias": false, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null, "output_padding": null}, "inbound_nodes": [[["dropout_5", 0, 0, {}]]]}, {"name": "concatenate_3", "class_name": "Concatenate", "config": {"name": "concatenate_3", "trainable": true, "axis": -1}, "inbound_nodes": [[["conv3d_transpose_3", 0, 0, {}], ["conv3d_2", 0, 0, {}]]]}, {"name": "conv3d_9", "class_name": "Conv3D", "config": {"name": "conv3d_9", "trainable": true, "filters": 8, "kernel_size": [5, 5, 5], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["concatenate_3", 0, 0, {}]]]}, {"name": "dropout_6", "class_name": "Dropout", "config": {"name": "dropout_6", "trainable": true, "rate": 0.3, "noise_shape": null, "seed": null}, "inbound_nodes": [[["conv3d_9", 0, 0, {}]]]}, {"name": "conv3d_10", "class_name": "Conv3D", "config": {"name": "conv3d_10", "trainable": true, "filters": 1, "kernel_size": [1, 1, 1], "strides": [1, 1, 1], "padding": "same", "data_format": "channels_last", "dilation_rate": [1, 1, 1], "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["dropout_6", 0, 0, {}]]]}, {"name": "dense_1", "class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "units": 1, "activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "VarianceScaling", "config": {"scale": 1.0, "mode": "fan_avg", "distribution": "uniform", "seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "inbound_nodes": [[["conv3d_10", 0, 0, {}]]]}], "input_layers": [["img", 0, 0]], "output_layers": [["dense_1", 0, 0]]}, "keras_version": "2.2.4", "backend": "plaidml.keras.backend"} \ No newline at end of file diff --git a/invesalius/segmentation/brain/segment.py b/invesalius/segmentation/brain/segment.py index f483b1d..778d417 100644 --- a/invesalius/segmentation/brain/segment.py +++ b/invesalius/segmentation/brain/segment.py @@ -1,13 +1,18 @@ import itertools +import multiprocessing import os import pathlib import sys +import tempfile +import traceback import numpy as np from skimage.transform import resize +import invesalius.data.slice_ as slc +from invesalius import inv_paths from invesalius.data import imagedata_utils -from invesalius.utils import timing +from invesalius.utils import new_name_by_pattern from . import utils @@ -17,11 +22,13 @@ OVERLAP = SIZE // 2 + 1 def gen_patches(image, patch_size, overlap): sz, sy, sx = image.shape - i_cuts = list(itertools.product( - range(0, sz, patch_size - OVERLAP), - range(0, sy, patch_size - OVERLAP), - range(0, sx, patch_size - OVERLAP), - )) + i_cuts = list( + itertools.product( + range(0, sz, patch_size - OVERLAP), + range(0, sy, patch_size - OVERLAP), + range(0, sx, patch_size - OVERLAP), + ) + ) sub_image = np.empty(shape=(patch_size, patch_size, patch_size), dtype="float32") for idx, (iz, iy, ix) in enumerate(i_cuts): sub_image[:] = 0 @@ -34,82 +41,128 @@ def gen_patches(image, patch_size, overlap): ey = iy + sy ex = ix + sx - yield (idx + 1.0)/len(i_cuts), sub_image, ((iz, ez), (iy, ey), (ix, ex)) + yield (idx + 1.0) / len(i_cuts), sub_image, ((iz, ez), (iy, ey), (ix, ex)) def predict_patch(sub_image, patch, nn_model, patch_size=SIZE): (iz, ez), (iy, ey), (ix, ex) = patch - sub_mask = nn_model.predict(sub_image.reshape(1, patch_size, patch_size, patch_size, 1)) - return sub_mask.reshape(patch_size, patch_size, patch_size)[0:ez-iz, 0:ey-iy, 0:ex-ix] + sub_mask = nn_model.predict( + sub_image.reshape(1, patch_size, patch_size, patch_size, 1) + ) + return sub_mask.reshape(patch_size, patch_size, patch_size)[ + 0 : ez - iz, 0 : ey - iy, 0 : ex - ix + ] + + +def brain_segment(image, probability_array, comm_array): + import keras + + # Loading model + folder = inv_paths.MODELS_DIR.joinpath("brain_mri_t1") + with open(folder.joinpath("model.json"), "r") as json_file: + model = keras.models.model_from_json(json_file.read()) + model.load_weights(str(folder.joinpath("model.h5"))) + model.compile("Adam", "binary_crossentropy") + + image = imagedata_utils.image_normalize(image, 0.0, 1.0) + sums = np.zeros_like(image) + # segmenting by patches + for completion, sub_image, patch in gen_patches(image, SIZE, OVERLAP): + comm_array[0] = completion + (iz, ez), (iy, ey), (ix, ex) = patch + sub_mask = predict_patch(sub_image, patch, model, SIZE) + probability_array[iz:ez, iy:ey, ix:ex] += sub_mask + sums[iz:ez, iy:ey, ix:ex] += 1 + + probability_array /= sums + comm_array[0] = np.Inf + + +ctx = multiprocessing.get_context('spawn') +class SegmentProcess(ctx.Process): + def __init__(self, image, create_new_mask, backend, device_id, use_gpu): + multiprocessing.Process.__init__(self) + + self._image_filename = image.filename + self._image_dtype = image.dtype + self._image_shape = image.shape + + self._probability_array = np.memmap( + tempfile.mktemp(), shape=image.shape, dtype=np.float32, mode="w+" + ) + self._prob_array_filename = self._probability_array.filename + + self._comm_array = np.memmap( + tempfile.mktemp(), shape=(1,), dtype=np.float32, mode="w+" + ) + self._comm_array_filename = self._comm_array.filename + + self.create_new_mask = create_new_mask + self.backend = backend + self.device_id = device_id + self.use_gpu = use_gpu + + self._pconn, self._cconn = multiprocessing.Pipe() + self._exception = None - -class BrainSegmenter: - def __init__(self): self.mask = None - self.propability_array = None - self.stop = False - self.segmented = False - - def segment(self, image, prob_threshold, backend, device_id, use_gpu, progress_callback=None, after_segment=None): - print("backend", backend) - if backend.lower() == 'plaidml': - os.environ["KERAS_BACKEND"] = "plaidml.keras.backend" - os.environ["PLAIDML_DEVICE_IDS"] = device_id - elif backend.lower() == 'theano': - os.environ["KERAS_BACKEND"] = "theano" - if use_gpu: - os.environ["THEANO_FLAGS"] = "device=cuda0" - print("Use GPU theano", os.environ["THEANO_FLAGS"]) - else: - os.environ["THEANO_FLAGS"] = "device=cpu" + + def run(self): + try: + self._run_segmentation() + self._cconn.send(None) + except Exception as e: + tb = traceback.format_exc() + self._cconn.send((e, tb)) + + def _run_segmentation(self): + image = np.memmap( + self._image_filename, + dtype=self._image_dtype, + shape=self._image_shape, + mode="r", + ) + probability_array = np.memmap( + self._prob_array_filename, + dtype=np.float32, + shape=self._image_shape, + mode="r+", + ) + comm_array = np.memmap( + self._comm_array_filename, dtype=np.float32, shape=(1,), mode="r+" + ) + + utils.prepare_ambient(self.backend, self.device_id, self.use_gpu) + brain_segment(image, probability_array, comm_array) + + @property + def exception(self): + # Based on https://stackoverflow.com/a/33599967 + if self._pconn.poll(): + self._exception = self._pconn.recv() + return self._exception + + def apply_segment_threshold(self, threshold): + if self.create_new_mask: + if self.mask is None: + name = new_name_by_pattern("brainseg_mri_t1") + self.mask = slc.Slice().create_new_mask(name=name) else: - raise TypeError("Wrong backend") - - import keras - import invesalius.data.slice_ as slc - - image = imagedata_utils.image_normalize(image, 0.0, 1.0) - - # Loading model - folder = pathlib.Path(__file__).parent.resolve() - with open(folder.joinpath("model.json"), "r") as json_file: - model = keras.models.model_from_json(json_file.read()) - model.load_weights(str(folder.joinpath("model.h5"))) - model.compile("Adam", "binary_crossentropy") - - # segmenting by patches - msk = np.zeros_like(image, dtype="float32") - sums = np.zeros_like(image) - for completion, sub_image, patch in gen_patches(image, SIZE, OVERLAP): - if self.stop: - self.stop = False - return - - if progress_callback is not None: - progress_callback(completion) - print("completion", completion) - (iz, ez), (iy, ey), (ix, ex) = patch - sub_mask = predict_patch(sub_image, patch, model, SIZE) - msk[iz:ez, iy:ey, ix:ex] += sub_mask - sums[iz:ez, iy:ey, ix:ex] += 1 - - propability_array = msk / sums - - mask = slc.Slice().create_new_mask() - mask.was_edited = True - mask.matrix[:] = 1 - mask.matrix[1:, 1:, 1:] = (propability_array >= prob_threshold) * 255 - - self.mask = mask - self.propability_array = propability_array - self.segmented = True - if after_segment is not None: - after_segment() - - def set_threshold(self, threshold): - if threshold < 0: - threshold = 0 - elif threshold > 1: - threshold = 1 + self.mask = slc.Slice().current_mask + if self.mask is None: + name = new_name_by_pattern("brainseg_mri_t1") + self.mask = slc.Slice().create_new_mask(name=name) + + self.mask.was_edited = True self.mask.matrix[:] = 1 - self.mask.matrix[1:, 1:, 1:] = (self.propability_array >= threshold) * 255 + self.mask.matrix[1:, 1:, 1:] = (self._probability_array >= threshold) * 255 + + def get_completion(self): + return self._comm_array[0] + + def __del__(self): + del self._comm_array + os.remove(self._comm_array_filename) + + del self._probability_array + os.remove(self._prob_array_filename) diff --git a/invesalius/segmentation/brain/utils.py b/invesalius/segmentation/brain/utils.py index a86a142..b1d1bb6 100644 --- a/invesalius/segmentation/brain/utils.py +++ b/invesalius/segmentation/brain/utils.py @@ -1,4 +1,41 @@ +import os +import pathlib +import sys + +def prepare_plaidml(): + # Linux if installed plaidml with pip3 install --user + if sys.platform.startswith("linux"): + local_user_plaidml = pathlib.Path("~/.local/share/plaidml/").expanduser().absolute() + if local_user_plaidml.exists(): + os.environ["RUNFILES_DIR"] = str(local_user_plaidml) + os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("~/.local/lib/libplaidml.so").expanduser().absolute()) + # Mac if using python3 from homebrew + elif sys.platform == "darwin": + local_user_plaidml = pathlib.Path("/usr/local/share/plaidml") + if local_user_plaidml.exists(): + os.environ["RUNFILES_DIR"] = str(local_user_plaidml) + os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("/usr/local/lib/libplaidml.dylib").expanduser().absolute()) + +def prepare_ambient(backend, device_id, use_gpu): + if backend.lower() == 'plaidml': + os.environ["KERAS_BACKEND"] = "plaidml.keras.backend" + os.environ["PLAIDML_DEVICE_IDS"] = device_id + prepare_plaidml() + elif backend.lower() == 'theano': + os.environ["KERAS_BACKEND"] = "theano" + if use_gpu: + os.environ["THEANO_FLAGS"] = "device=cuda0" + print("Use GPU theano", os.environ["THEANO_FLAGS"]) + else: + os.environ["THEANO_FLAGS"] = "device=cpu" + else: + raise TypeError("Wrong backend") + + + def get_plaidml_devices(gpu=False): + prepare_plaidml() + import plaidml ctx = plaidml.Context() diff --git a/invesalius/utils.py b/invesalius/utils.py index d7f9391..9098046 100644 --- a/invesalius/utils.py +++ b/invesalius/utils.py @@ -126,7 +126,16 @@ def next_copy_name(original_name, names_list): if not (next_copy in names_list): got_new_name = True return next_copy - + + +def new_name_by_pattern(pattern): + from invesalius.project import Project + proj = Project() + mask_dict = proj.mask_dict + names_list = [i.name for i in mask_dict.values() if i.name.startswith(pattern + "_")] + count = len(names_list) + 1 + return "{}_{}".format(pattern, count) + def VerifyInvalidPListCharacter(text): #print text -- libgit2 0.21.2