Commit 0f3937003464e9946a3f728ac8e8c35f26c147bc

Authored by Thiago Franco de Moraes
Committed by GitHub
1 parent dfac1497
Exists in master

Brain segmentation in other process (#233)

* Using subprocess to start brain segmenter

* Wrong mode in comm_array opening

* Managing errors

* calling get_plaidml_device out of main process to avoid crash with plaidml

* Trying to use multiprocessing instead of subprocess

* Trying to use multiprocessing instead of subprocess

* Using a class to keep brain segmentation

* Putting probability_array in SegmentProcess

* Some cleaning and style modifications

* Moved brainseg model to models/brain_mri_t1

* Setting _ on children process

* Added option to create or no create a new mask

* Setting lang and _ outside of InVesalius class

* models -> ai
ai/brain_mri_t1/model.h5 0 → 100644
No preview for this file type
ai/brain_mri_t1/model.json 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  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"}
0 \ No newline at end of file 2 \ No newline at end of file
@@ -65,6 +65,7 @@ import invesalius.utils as utils @@ -65,6 +65,7 @@ import invesalius.utils as utils
65 from invesalius import inv_paths 65 from invesalius import inv_paths
66 66
67 FS_ENCODE = sys.getfilesystemencoding() 67 FS_ENCODE = sys.getfilesystemencoding()
  68 +LANG = None
68 69
69 # ------------------------------------------------------------------ 70 # ------------------------------------------------------------------
70 71
@@ -78,6 +79,17 @@ if sys.platform in ('linux2', 'linux', 'win32'): @@ -78,6 +79,17 @@ if sys.platform in ('linux2', 'linux', 'win32'):
78 del tmp_var 79 del tmp_var
79 80
80 81
  82 +session = ses.Session()
  83 +if session.ReadSession():
  84 + lang = session.GetLanguage()
  85 + if lang:
  86 + LANG = lang
  87 + try:
  88 + _ = i18n.InstallLanguage(lang)
  89 + except FileNotFoundError:
  90 + LANG = None
  91 +
  92 +
81 class InVesalius(wx.App): 93 class InVesalius(wx.App):
82 """ 94 """
83 InVesalius wxPython application class. 95 InVesalius wxPython application class.
@@ -119,26 +131,18 @@ class Inv3SplashScreen(SplashScreen): @@ -119,26 +131,18 @@ class Inv3SplashScreen(SplashScreen):
119 """ 131 """
120 def __init__(self): 132 def __init__(self):
121 # Splash screen image will depend on currently language 133 # Splash screen image will depend on currently language
122 - lang = False  
123 - 134 + lang = LANG
124 self.locale = wx.Locale(wx.LANGUAGE_DEFAULT) 135 self.locale = wx.Locale(wx.LANGUAGE_DEFAULT)
125 136
126 # Language information is available in session configuration 137 # Language information is available in session configuration
127 # file. First we need to check if this file exist, if now, it 138 # file. First we need to check if this file exist, if now, it
128 # should be created 139 # should be created
129 - create_session = False  
130 - session = ses.Session()  
131 - if not (session.ReadSession()):  
132 - create_session = True 140 + create_session = LANG is None
133 141
134 install_lang = 0 142 install_lang = 0
135 - lang = session.GetLanguage()  
136 if lang: 143 if lang:
137 - if (lang != "False"):  
138 - _ = i18n.InstallLanguage(lang)  
139 - install_lang = 1  
140 - else:  
141 - install_lang = 0 144 + _ = i18n.InstallLanguage(lang)
  145 + install_lang = 1
142 else: 146 else:
143 install_lang = 0 147 install_lang = 0
144 148
@@ -262,7 +266,10 @@ class Inv3SplashScreen(SplashScreen): @@ -262,7 +266,10 @@ class Inv3SplashScreen(SplashScreen):
262 266
263 267
264 def non_gui_startup(options, args): 268 def non_gui_startup(options, args):
265 - lang = 'en' 269 + if LANG:
  270 + lang = LANG
  271 + else:
  272 + lang = 'en'
266 _ = i18n.InstallLanguage(lang) 273 _ = i18n.InstallLanguage(lang)
267 274
268 from invesalius.control import Controller 275 from invesalius.control import Controller
invesalius/gui/brain_seg_dialog.py
@@ -2,76 +2,85 @@ @@ -2,76 +2,85 @@
2 # -*- coding: UTF-8 -*- 2 # -*- coding: UTF-8 -*-
3 3
4 import importlib 4 import importlib
  5 +import multiprocessing
5 import os 6 import os
6 import pathlib 7 import pathlib
  8 +import subprocess
7 import sys 9 import sys
  10 +import tempfile
8 import time 11 import time
9 12
  13 +import numpy as np
10 import wx 14 import wx
11 from pubsub import pub as Publisher 15 from pubsub import pub as Publisher
12 16
13 -# Linux if installed plaidml with pip3 install --user  
14 -if sys.platform.startswith("linux"):  
15 - local_user_plaidml = pathlib.Path("~/.local/share/plaidml/").expanduser().absolute()  
16 - if local_user_plaidml.exists():  
17 - os.environ["RUNFILES_DIR"] = str(local_user_plaidml)  
18 - os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("~/.local/lib/libplaidml.so").expanduser().absolute())  
19 -# Mac if using python3 from homebrew  
20 -elif sys.platform == "darwin":  
21 - local_user_plaidml = pathlib.Path("/usr/local/share/plaidml")  
22 - if local_user_plaidml.exists():  
23 - os.environ["RUNFILES_DIR"] = str(local_user_plaidml)  
24 - os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("/usr/local/lib/libplaidml.dylib").expanduser().absolute()) 17 +import invesalius.data.slice_ as slc
  18 +from invesalius.segmentation.brain import segment, utils
25 19
26 HAS_THEANO = bool(importlib.util.find_spec("theano")) 20 HAS_THEANO = bool(importlib.util.find_spec("theano"))
27 HAS_PLAIDML = bool(importlib.util.find_spec("plaidml")) 21 HAS_PLAIDML = bool(importlib.util.find_spec("plaidml"))
28 PLAIDML_DEVICES = {} 22 PLAIDML_DEVICES = {}
29 23
30 -import invesalius.data.slice_ as slc  
31 -from invesalius.segmentation.brain import segment  
32 -from invesalius.segmentation.brain import utils  
33 -  
34 -try:  
35 - import theano  
36 -except ImportError:  
37 - HAS_THEANO = False  
38 -  
39 -try:  
40 - import plaidml  
41 -except ImportError:  
42 - HAS_PLAIDML = False  
43 24
44 if HAS_PLAIDML: 25 if HAS_PLAIDML:
45 - try:  
46 - PLAIDML_DEVICES = utils.get_plaidml_devices()  
47 - except OSError:  
48 - HAS_PLAIDML = False 26 + with multiprocessing.Pool(1) as p:
  27 + try:
  28 + PLAIDML_DEVICES = p.apply(utils.get_plaidml_devices)
  29 + except Exception as err:
  30 + print(err)
  31 + PLAIDML_DEVICES = {}
  32 + HAS_PLAIDML = False
49 33
50 34
51 class BrainSegmenterDialog(wx.Dialog): 35 class BrainSegmenterDialog(wx.Dialog):
52 def __init__(self, parent): 36 def __init__(self, parent):
53 - wx.Dialog.__init__(self, parent, -1, _(u"Brain segmentation"), style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT) 37 + wx.Dialog.__init__(
  38 + self,
  39 + parent,
  40 + -1,
  41 + _(u"Brain segmentation"),
  42 + style=wx.DEFAULT_DIALOG_STYLE | wx.FRAME_FLOAT_ON_PARENT,
  43 + )
54 backends = [] 44 backends = []
55 if HAS_PLAIDML: 45 if HAS_PLAIDML:
56 backends.append("PlaidML") 46 backends.append("PlaidML")
57 if HAS_THEANO: 47 if HAS_THEANO:
58 backends.append("Theano") 48 backends.append("Theano")
59 - self.segmenter = segment.BrainSegmenter() 49 + # self.segmenter = segment.BrainSegmenter()
60 # self.pg_dialog = None 50 # self.pg_dialog = None
61 self.plaidml_devices = PLAIDML_DEVICES 51 self.plaidml_devices = PLAIDML_DEVICES
62 - self.cb_backends = wx.ComboBox(self, wx.ID_ANY, choices=backends, value=backends[0], style=wx.CB_DROPDOWN | wx.CB_READONLY) 52 +
  53 + self.ps = None
  54 + self.segmented = False
  55 + self.mask = None
  56 +
  57 + self.cb_backends = wx.ComboBox(
  58 + self,
  59 + wx.ID_ANY,
  60 + choices=backends,
  61 + value=backends[0],
  62 + style=wx.CB_DROPDOWN | wx.CB_READONLY,
  63 + )
63 w, h = self.CalcSizeFromTextSize("MM" * (1 + max(len(i) for i in backends))) 64 w, h = self.CalcSizeFromTextSize("MM" * (1 + max(len(i) for i in backends)))
64 self.cb_backends.SetMinClientSize((w, -1)) 65 self.cb_backends.SetMinClientSize((w, -1))
65 self.chk_use_gpu = wx.CheckBox(self, wx.ID_ANY, _("Use GPU")) 66 self.chk_use_gpu = wx.CheckBox(self, wx.ID_ANY, _("Use GPU"))
66 if HAS_PLAIDML: 67 if HAS_PLAIDML:
67 self.lbl_device = wx.StaticText(self, -1, _("Device")) 68 self.lbl_device = wx.StaticText(self, -1, _("Device"))
68 - 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) 69 + self.cb_devices = wx.ComboBox(
  70 + self,
  71 + wx.ID_ANY,
  72 + choices=list(self.plaidml_devices.keys()),
  73 + value=list(self.plaidml_devices.keys())[0],
  74 + style=wx.CB_DROPDOWN | wx.CB_READONLY,
  75 + )
69 self.sld_threshold = wx.Slider(self, wx.ID_ANY, 75, 0, 100) 76 self.sld_threshold = wx.Slider(self, wx.ID_ANY, 75, 0, 100)
70 w, h = self.CalcSizeFromTextSize("M" * 20) 77 w, h = self.CalcSizeFromTextSize("M" * 20)
71 self.sld_threshold.SetMinClientSize((w, -1)) 78 self.sld_threshold.SetMinClientSize((w, -1))
72 self.txt_threshold = wx.TextCtrl(self, wx.ID_ANY, "") 79 self.txt_threshold = wx.TextCtrl(self, wx.ID_ANY, "")
73 w, h = self.CalcSizeFromTextSize("MMMMM") 80 w, h = self.CalcSizeFromTextSize("MMMMM")
74 self.txt_threshold.SetMinClientSize((w, -1)) 81 self.txt_threshold.SetMinClientSize((w, -1))
  82 + self.chk_new_mask = wx.CheckBox(self, wx.ID_ANY, _("Create new mask"))
  83 + self.chk_new_mask.SetValue(True)
75 self.progress = wx.Gauge(self, -1) 84 self.progress = wx.Gauge(self, -1)
76 self.lbl_progress_caption = wx.StaticText(self, -1, _("Elapsed time:")) 85 self.lbl_progress_caption = wx.StaticText(self, -1, _("Elapsed time:"))
77 self.lbl_time = wx.StaticText(self, -1, _("00:00:00")) 86 self.lbl_time = wx.StaticText(self, -1, _("00:00:00"))
@@ -103,18 +112,30 @@ class BrainSegmenterDialog(wx.Dialog): @@ -103,18 +112,30 @@ class BrainSegmenterDialog(wx.Dialog):
103 main_sizer.Add(sizer_devices, 0, wx.ALL | wx.EXPAND, 5) 112 main_sizer.Add(sizer_devices, 0, wx.ALL | wx.EXPAND, 5)
104 label_5 = wx.StaticText(self, wx.ID_ANY, _("Level of certainty")) 113 label_5 = wx.StaticText(self, wx.ID_ANY, _("Level of certainty"))
105 main_sizer.Add(label_5, 0, wx.ALL, 5) 114 main_sizer.Add(label_5, 0, wx.ALL, 5)
106 - sizer_3.Add(self.sld_threshold, 1, wx.ALIGN_CENTER | wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.RIGHT, 5) 115 + sizer_3.Add(
  116 + self.sld_threshold,
  117 + 1,
  118 + wx.ALIGN_CENTER | wx.BOTTOM | wx.EXPAND | wx.LEFT | wx.RIGHT,
  119 + 5,
  120 + )
107 sizer_3.Add(self.txt_threshold, 0, wx.ALL, 5) 121 sizer_3.Add(self.txt_threshold, 0, wx.ALL, 5)
108 main_sizer.Add(sizer_3, 0, wx.EXPAND, 0) 122 main_sizer.Add(sizer_3, 0, wx.EXPAND, 0)
  123 + main_sizer.Add(self.chk_new_mask, 0, wx.EXPAND | wx.ALL, 5)
109 main_sizer.Add(self.progress, 0, wx.EXPAND | wx.ALL, 5) 124 main_sizer.Add(self.progress, 0, wx.EXPAND | wx.ALL, 5)
110 time_sizer = wx.BoxSizer(wx.HORIZONTAL) 125 time_sizer = wx.BoxSizer(wx.HORIZONTAL)
111 time_sizer.Add(self.lbl_progress_caption, 0, wx.EXPAND, 0) 126 time_sizer.Add(self.lbl_progress_caption, 0, wx.EXPAND, 0)
112 time_sizer.Add(self.lbl_time, 1, wx.EXPAND | wx.LEFT, 5) 127 time_sizer.Add(self.lbl_time, 1, wx.EXPAND | wx.LEFT, 5)
113 main_sizer.Add(time_sizer, 0, wx.EXPAND | wx.ALL, 5) 128 main_sizer.Add(time_sizer, 0, wx.EXPAND | wx.ALL, 5)
114 sizer_buttons = wx.BoxSizer(wx.HORIZONTAL) 129 sizer_buttons = wx.BoxSizer(wx.HORIZONTAL)
115 - sizer_buttons.Add(self.btn_close, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5)  
116 - sizer_buttons.Add(self.btn_stop, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5)  
117 - sizer_buttons.Add(self.btn_segment, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5) 130 + sizer_buttons.Add(
  131 + self.btn_close, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5
  132 + )
  133 + sizer_buttons.Add(
  134 + self.btn_stop, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5
  135 + )
  136 + sizer_buttons.Add(
  137 + self.btn_segment, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 5
  138 + )
118 main_sizer.Add(sizer_buttons, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 0) 139 main_sizer.Add(sizer_buttons, 0, wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, 0)
119 self.SetSizer(main_sizer) 140 self.SetSizer(main_sizer)
120 main_sizer.Fit(self) 141 main_sizer.Fit(self)
@@ -138,6 +159,13 @@ class BrainSegmenterDialog(wx.Dialog): @@ -138,6 +159,13 @@ class BrainSegmenterDialog(wx.Dialog):
138 self.elapsed_time_timer.Bind(wx.EVT_TIMER, self.OnTickTimer) 159 self.elapsed_time_timer.Bind(wx.EVT_TIMER, self.OnTickTimer)
139 self.Bind(wx.EVT_CLOSE, self.OnClose) 160 self.Bind(wx.EVT_CLOSE, self.OnClose)
140 161
  162 + def apply_segment_threshold(self):
  163 + threshold = self.sld_threshold.GetValue() / 100.0
  164 + if self.ps is not None:
  165 + self.ps.apply_segment_threshold(threshold)
  166 + slc.Slice().discard_all_buffers()
  167 + Publisher.sendMessage("Reload actual slice")
  168 +
141 def CalcSizeFromTextSize(self, text): 169 def CalcSizeFromTextSize(self, text):
142 dc = wx.WindowDC(self) 170 dc = wx.WindowDC(self)
143 dc.SetFont(self.GetFont()) 171 dc.SetFont(self.GetFont())
@@ -162,15 +190,12 @@ class BrainSegmenterDialog(wx.Dialog): @@ -162,15 +190,12 @@ class BrainSegmenterDialog(wx.Dialog):
162 def OnScrollThreshold(self, evt): 190 def OnScrollThreshold(self, evt):
163 value = self.sld_threshold.GetValue() 191 value = self.sld_threshold.GetValue()
164 self.txt_threshold.SetValue("{:3d}%".format(self.sld_threshold.GetValue())) 192 self.txt_threshold.SetValue("{:3d}%".format(self.sld_threshold.GetValue()))
165 - if self.segmenter.segmented:  
166 - threshold = value / 100.0  
167 - self.segmenter.set_threshold(threshold)  
168 - image = slc.Slice().discard_all_buffers()  
169 - Publisher.sendMessage('Reload actual slice') 193 + if self.segmented:
  194 + self.apply_segment_threshold()
170 195
171 def OnKillFocus(self, evt): 196 def OnKillFocus(self, evt):
172 value = self.txt_threshold.GetValue() 197 value = self.txt_threshold.GetValue()
173 - value = value.replace('%', '') 198 + value = value.replace("%", "")
174 try: 199 try:
175 value = int(value) 200 value = int(value)
176 except ValueError: 201 except ValueError:
@@ -178,11 +203,8 @@ class BrainSegmenterDialog(wx.Dialog): @@ -178,11 +203,8 @@ class BrainSegmenterDialog(wx.Dialog):
178 self.sld_threshold.SetValue(value) 203 self.sld_threshold.SetValue(value)
179 self.txt_threshold.SetValue("{:3d}%".format(value)) 204 self.txt_threshold.SetValue("{:3d}%".format(value))
180 205
181 - if self.segmenter.segmented:  
182 - threshold = value / 100.0  
183 - self.segmenter.set_threshold(threshold)  
184 - image = slc.Slice().discard_all_buffers()  
185 - Publisher.sendMessage('Reload actual slice') 206 + if self.segmented:
  207 + self.apply_segment_threshold()
186 208
187 def OnSegment(self, evt): 209 def OnSegment(self, evt):
188 self.ShowProgress() 210 self.ShowProgress()
@@ -194,44 +216,97 @@ class BrainSegmenterDialog(wx.Dialog): @@ -194,44 +216,97 @@ class BrainSegmenterDialog(wx.Dialog):
194 device_id = self.plaidml_devices[self.cb_devices.GetValue()] 216 device_id = self.plaidml_devices[self.cb_devices.GetValue()]
195 except (KeyError, AttributeError): 217 except (KeyError, AttributeError):
196 device_id = "llvm_cpu.0" 218 device_id = "llvm_cpu.0"
  219 + create_new_mask = self.chk_new_mask.GetValue()
197 use_gpu = self.chk_use_gpu.GetValue() 220 use_gpu = self.chk_use_gpu.GetValue()
198 prob_threshold = self.sld_threshold.GetValue() / 100.0 221 prob_threshold = self.sld_threshold.GetValue() / 100.0
199 self.btn_close.Disable() 222 self.btn_close.Disable()
200 self.btn_stop.Enable() 223 self.btn_stop.Enable()
201 self.btn_segment.Disable() 224 self.btn_segment.Disable()
202 - self.segmenter.segment(image, prob_threshold, backend, device_id, use_gpu, self.SetProgress, self.AfterSegment) 225 + self.chk_new_mask.Disable()
  226 +
  227 + try:
  228 + self.ps = segment.SegmentProcess(image, create_new_mask, backend, device_id, use_gpu)
  229 + self.ps.start()
  230 + except (multiprocessing.ProcessError, OSError, ValueError) as err:
  231 + self.OnStop(None)
  232 + self.HideProgress()
  233 + dlg = wx.MessageDialog(
  234 + None,
  235 + "It was not possible to start brain segmentation because:"
  236 + + "\n"
  237 + + str(err),
  238 + "Brain segmentation error",
  239 + wx.ICON_ERROR | wx.OK,
  240 + )
  241 + dlg.ShowModal()
203 242
204 def OnStop(self, evt): 243 def OnStop(self, evt):
205 - self.segmenter.stop = True 244 + if self.ps is not None:
  245 + self.ps.terminate()
206 self.btn_close.Enable() 246 self.btn_close.Enable()
207 self.btn_stop.Disable() 247 self.btn_stop.Disable()
208 self.btn_segment.Enable() 248 self.btn_segment.Enable()
  249 + self.chk_new_mask.Enable()
209 self.elapsed_time_timer.Stop() 250 self.elapsed_time_timer.Stop()
210 - evt.Skip()  
211 251
212 def OnBtnClose(self, evt): 252 def OnBtnClose(self, evt):
213 self.Close() 253 self.Close()
214 254
215 def AfterSegment(self): 255 def AfterSegment(self):
  256 + self.segmented = True
216 self.btn_close.Enable() 257 self.btn_close.Enable()
217 self.btn_stop.Disable() 258 self.btn_stop.Disable()
218 self.btn_segment.Disable() 259 self.btn_segment.Disable()
219 - Publisher.sendMessage('Reload actual slice') 260 + self.chk_new_mask.Disable()
220 self.elapsed_time_timer.Stop() 261 self.elapsed_time_timer.Stop()
  262 + self.apply_segment_threshold()
221 263
222 def SetProgress(self, progress): 264 def SetProgress(self, progress):
223 self.progress.SetValue(progress * 100) 265 self.progress.SetValue(progress * 100)
224 wx.GetApp().Yield() 266 wx.GetApp().Yield()
225 267
226 def OnTickTimer(self, evt): 268 def OnTickTimer(self, evt):
227 - fmt='%H:%M:%S'  
228 - self.lbl_time.SetLabel(time.strftime(fmt, time.gmtime(time.time()-self.t0))) 269 + fmt = "%H:%M:%S"
  270 + self.lbl_time.SetLabel(time.strftime(fmt, time.gmtime(time.time() - self.t0)))
  271 + if self.ps is not None:
  272 + if not self.ps.is_alive() and self.ps.exception is not None:
  273 + error, traceback = self.ps.exception
  274 + self.OnStop(None)
  275 + self.HideProgress()
  276 + dlg = wx.MessageDialog(
  277 + None,
  278 + "It was not possible to use brain segmentation because:"
  279 + + "\n"
  280 + + str(error)
  281 + + "\n"
  282 + + traceback,
  283 + "Brain segmentation error",
  284 + wx.ICON_ERROR | wx.OK,
  285 + )
  286 + dlg.ShowModal()
  287 + return
  288 +
  289 + progress = self.ps.get_completion()
  290 + if progress == np.Inf:
  291 + progress = 1
  292 + self.AfterSegment()
  293 + if progress < 0:
  294 + progress = 0
  295 + if progress > 1:
  296 + progress = 1
  297 + self.SetProgress(float(progress))
229 298
230 def OnClose(self, evt): 299 def OnClose(self, evt):
231 - self.segmenter.stop = True 300 + # self.segmenter.stop = True
232 self.btn_stop.Disable() 301 self.btn_stop.Disable()
233 self.btn_segment.Enable() 302 self.btn_segment.Enable()
  303 + self.chk_new_mask.Enable()
234 self.progress.SetValue(0) 304 self.progress.SetValue(0)
  305 +
  306 + if self.ps is not None:
  307 + self.ps.terminate()
  308 + self.ps = None
  309 +
235 self.Destroy() 310 self.Destroy()
236 311
237 def HideProgress(self): 312 def HideProgress(self):
@@ -257,6 +332,7 @@ class MyApp(wx.App): @@ -257,6 +332,7 @@ class MyApp(wx.App):
257 self.dlg_brain_seg.Destroy() 332 self.dlg_brain_seg.Destroy()
258 return True 333 return True
259 334
  335 +
260 if __name__ == "__main__": 336 if __name__ == "__main__":
261 app = MyApp(0) 337 app = MyApp(0)
262 app.MainLoop() 338 app.MainLoop()
invesalius/inv_paths.py
@@ -46,6 +46,7 @@ RAYCASTING_PRESETS_COLOR_DIRECTORY = INV_TOP_DIR.joinpath( @@ -46,6 +46,7 @@ RAYCASTING_PRESETS_COLOR_DIRECTORY = INV_TOP_DIR.joinpath(
46 "presets", "raycasting", "color_list" 46 "presets", "raycasting", "color_list"
47 ) 47 )
48 48
  49 +MODELS_DIR = INV_TOP_DIR.joinpath("ai")
49 50
50 # Inside the windows executable 51 # Inside the windows executable
51 if hasattr(sys, "frozen") and ( 52 if hasattr(sys, "frozen") and (
invesalius/segmentation/brain/model.h5
No preview for this file type
invesalius/segmentation/brain/model.json
@@ -1 +0,0 @@ @@ -1 +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"}  
2 \ No newline at end of file 0 \ No newline at end of file
invesalius/segmentation/brain/segment.py
1 import itertools 1 import itertools
  2 +import multiprocessing
2 import os 3 import os
3 import pathlib 4 import pathlib
4 import sys 5 import sys
  6 +import tempfile
  7 +import traceback
5 8
6 import numpy as np 9 import numpy as np
7 from skimage.transform import resize 10 from skimage.transform import resize
8 11
  12 +import invesalius.data.slice_ as slc
  13 +from invesalius import inv_paths
9 from invesalius.data import imagedata_utils 14 from invesalius.data import imagedata_utils
10 -from invesalius.utils import timing 15 +from invesalius.utils import new_name_by_pattern
11 16
12 from . import utils 17 from . import utils
13 18
@@ -17,11 +22,13 @@ OVERLAP = SIZE // 2 + 1 @@ -17,11 +22,13 @@ OVERLAP = SIZE // 2 + 1
17 22
18 def gen_patches(image, patch_size, overlap): 23 def gen_patches(image, patch_size, overlap):
19 sz, sy, sx = image.shape 24 sz, sy, sx = image.shape
20 - i_cuts = list(itertools.product(  
21 - range(0, sz, patch_size - OVERLAP),  
22 - range(0, sy, patch_size - OVERLAP),  
23 - range(0, sx, patch_size - OVERLAP),  
24 - )) 25 + i_cuts = list(
  26 + itertools.product(
  27 + range(0, sz, patch_size - OVERLAP),
  28 + range(0, sy, patch_size - OVERLAP),
  29 + range(0, sx, patch_size - OVERLAP),
  30 + )
  31 + )
25 sub_image = np.empty(shape=(patch_size, patch_size, patch_size), dtype="float32") 32 sub_image = np.empty(shape=(patch_size, patch_size, patch_size), dtype="float32")
26 for idx, (iz, iy, ix) in enumerate(i_cuts): 33 for idx, (iz, iy, ix) in enumerate(i_cuts):
27 sub_image[:] = 0 34 sub_image[:] = 0
@@ -34,82 +41,128 @@ def gen_patches(image, patch_size, overlap): @@ -34,82 +41,128 @@ def gen_patches(image, patch_size, overlap):
34 ey = iy + sy 41 ey = iy + sy
35 ex = ix + sx 42 ex = ix + sx
36 43
37 - yield (idx + 1.0)/len(i_cuts), sub_image, ((iz, ez), (iy, ey), (ix, ex)) 44 + yield (idx + 1.0) / len(i_cuts), sub_image, ((iz, ez), (iy, ey), (ix, ex))
38 45
39 46
40 def predict_patch(sub_image, patch, nn_model, patch_size=SIZE): 47 def predict_patch(sub_image, patch, nn_model, patch_size=SIZE):
41 (iz, ez), (iy, ey), (ix, ex) = patch 48 (iz, ez), (iy, ey), (ix, ex) = patch
42 - sub_mask = nn_model.predict(sub_image.reshape(1, patch_size, patch_size, patch_size, 1))  
43 - return sub_mask.reshape(patch_size, patch_size, patch_size)[0:ez-iz, 0:ey-iy, 0:ex-ix] 49 + sub_mask = nn_model.predict(
  50 + sub_image.reshape(1, patch_size, patch_size, patch_size, 1)
  51 + )
  52 + return sub_mask.reshape(patch_size, patch_size, patch_size)[
  53 + 0 : ez - iz, 0 : ey - iy, 0 : ex - ix
  54 + ]
  55 +
  56 +
  57 +def brain_segment(image, probability_array, comm_array):
  58 + import keras
  59 +
  60 + # Loading model
  61 + folder = inv_paths.MODELS_DIR.joinpath("brain_mri_t1")
  62 + with open(folder.joinpath("model.json"), "r") as json_file:
  63 + model = keras.models.model_from_json(json_file.read())
  64 + model.load_weights(str(folder.joinpath("model.h5")))
  65 + model.compile("Adam", "binary_crossentropy")
  66 +
  67 + image = imagedata_utils.image_normalize(image, 0.0, 1.0)
  68 + sums = np.zeros_like(image)
  69 + # segmenting by patches
  70 + for completion, sub_image, patch in gen_patches(image, SIZE, OVERLAP):
  71 + comm_array[0] = completion
  72 + (iz, ez), (iy, ey), (ix, ex) = patch
  73 + sub_mask = predict_patch(sub_image, patch, model, SIZE)
  74 + probability_array[iz:ez, iy:ey, ix:ex] += sub_mask
  75 + sums[iz:ez, iy:ey, ix:ex] += 1
  76 +
  77 + probability_array /= sums
  78 + comm_array[0] = np.Inf
  79 +
  80 +
  81 +ctx = multiprocessing.get_context('spawn')
  82 +class SegmentProcess(ctx.Process):
  83 + def __init__(self, image, create_new_mask, backend, device_id, use_gpu):
  84 + multiprocessing.Process.__init__(self)
  85 +
  86 + self._image_filename = image.filename
  87 + self._image_dtype = image.dtype
  88 + self._image_shape = image.shape
  89 +
  90 + self._probability_array = np.memmap(
  91 + tempfile.mktemp(), shape=image.shape, dtype=np.float32, mode="w+"
  92 + )
  93 + self._prob_array_filename = self._probability_array.filename
  94 +
  95 + self._comm_array = np.memmap(
  96 + tempfile.mktemp(), shape=(1,), dtype=np.float32, mode="w+"
  97 + )
  98 + self._comm_array_filename = self._comm_array.filename
  99 +
  100 + self.create_new_mask = create_new_mask
  101 + self.backend = backend
  102 + self.device_id = device_id
  103 + self.use_gpu = use_gpu
  104 +
  105 + self._pconn, self._cconn = multiprocessing.Pipe()
  106 + self._exception = None
44 107
45 -  
46 -class BrainSegmenter:  
47 - def __init__(self):  
48 self.mask = None 108 self.mask = None
49 - self.propability_array = None  
50 - self.stop = False  
51 - self.segmented = False  
52 -  
53 - def segment(self, image, prob_threshold, backend, device_id, use_gpu, progress_callback=None, after_segment=None):  
54 - print("backend", backend)  
55 - if backend.lower() == 'plaidml':  
56 - os.environ["KERAS_BACKEND"] = "plaidml.keras.backend"  
57 - os.environ["PLAIDML_DEVICE_IDS"] = device_id  
58 - elif backend.lower() == 'theano':  
59 - os.environ["KERAS_BACKEND"] = "theano"  
60 - if use_gpu:  
61 - os.environ["THEANO_FLAGS"] = "device=cuda0"  
62 - print("Use GPU theano", os.environ["THEANO_FLAGS"])  
63 - else:  
64 - os.environ["THEANO_FLAGS"] = "device=cpu" 109 +
  110 + def run(self):
  111 + try:
  112 + self._run_segmentation()
  113 + self._cconn.send(None)
  114 + except Exception as e:
  115 + tb = traceback.format_exc()
  116 + self._cconn.send((e, tb))
  117 +
  118 + def _run_segmentation(self):
  119 + image = np.memmap(
  120 + self._image_filename,
  121 + dtype=self._image_dtype,
  122 + shape=self._image_shape,
  123 + mode="r",
  124 + )
  125 + probability_array = np.memmap(
  126 + self._prob_array_filename,
  127 + dtype=np.float32,
  128 + shape=self._image_shape,
  129 + mode="r+",
  130 + )
  131 + comm_array = np.memmap(
  132 + self._comm_array_filename, dtype=np.float32, shape=(1,), mode="r+"
  133 + )
  134 +
  135 + utils.prepare_ambient(self.backend, self.device_id, self.use_gpu)
  136 + brain_segment(image, probability_array, comm_array)
  137 +
  138 + @property
  139 + def exception(self):
  140 + # Based on https://stackoverflow.com/a/33599967
  141 + if self._pconn.poll():
  142 + self._exception = self._pconn.recv()
  143 + return self._exception
  144 +
  145 + def apply_segment_threshold(self, threshold):
  146 + if self.create_new_mask:
  147 + if self.mask is None:
  148 + name = new_name_by_pattern("brainseg_mri_t1")
  149 + self.mask = slc.Slice().create_new_mask(name=name)
65 else: 150 else:
66 - raise TypeError("Wrong backend")  
67 -  
68 - import keras  
69 - import invesalius.data.slice_ as slc  
70 -  
71 - image = imagedata_utils.image_normalize(image, 0.0, 1.0)  
72 -  
73 - # Loading model  
74 - folder = pathlib.Path(__file__).parent.resolve()  
75 - with open(folder.joinpath("model.json"), "r") as json_file:  
76 - model = keras.models.model_from_json(json_file.read())  
77 - model.load_weights(str(folder.joinpath("model.h5")))  
78 - model.compile("Adam", "binary_crossentropy")  
79 -  
80 - # segmenting by patches  
81 - msk = np.zeros_like(image, dtype="float32")  
82 - sums = np.zeros_like(image)  
83 - for completion, sub_image, patch in gen_patches(image, SIZE, OVERLAP):  
84 - if self.stop:  
85 - self.stop = False  
86 - return  
87 -  
88 - if progress_callback is not None:  
89 - progress_callback(completion)  
90 - print("completion", completion)  
91 - (iz, ez), (iy, ey), (ix, ex) = patch  
92 - sub_mask = predict_patch(sub_image, patch, model, SIZE)  
93 - msk[iz:ez, iy:ey, ix:ex] += sub_mask  
94 - sums[iz:ez, iy:ey, ix:ex] += 1  
95 -  
96 - propability_array = msk / sums  
97 -  
98 - mask = slc.Slice().create_new_mask()  
99 - mask.was_edited = True  
100 - mask.matrix[:] = 1  
101 - mask.matrix[1:, 1:, 1:] = (propability_array >= prob_threshold) * 255  
102 -  
103 - self.mask = mask  
104 - self.propability_array = propability_array  
105 - self.segmented = True  
106 - if after_segment is not None:  
107 - after_segment()  
108 -  
109 - def set_threshold(self, threshold):  
110 - if threshold < 0:  
111 - threshold = 0  
112 - elif threshold > 1:  
113 - threshold = 1 151 + self.mask = slc.Slice().current_mask
  152 + if self.mask is None:
  153 + name = new_name_by_pattern("brainseg_mri_t1")
  154 + self.mask = slc.Slice().create_new_mask(name=name)
  155 +
  156 + self.mask.was_edited = True
114 self.mask.matrix[:] = 1 157 self.mask.matrix[:] = 1
115 - self.mask.matrix[1:, 1:, 1:] = (self.propability_array >= threshold) * 255 158 + self.mask.matrix[1:, 1:, 1:] = (self._probability_array >= threshold) * 255
  159 +
  160 + def get_completion(self):
  161 + return self._comm_array[0]
  162 +
  163 + def __del__(self):
  164 + del self._comm_array
  165 + os.remove(self._comm_array_filename)
  166 +
  167 + del self._probability_array
  168 + os.remove(self._prob_array_filename)
invesalius/segmentation/brain/utils.py
  1 +import os
  2 +import pathlib
  3 +import sys
  4 +
  5 +def prepare_plaidml():
  6 + # Linux if installed plaidml with pip3 install --user
  7 + if sys.platform.startswith("linux"):
  8 + local_user_plaidml = pathlib.Path("~/.local/share/plaidml/").expanduser().absolute()
  9 + if local_user_plaidml.exists():
  10 + os.environ["RUNFILES_DIR"] = str(local_user_plaidml)
  11 + os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("~/.local/lib/libplaidml.so").expanduser().absolute())
  12 + # Mac if using python3 from homebrew
  13 + elif sys.platform == "darwin":
  14 + local_user_plaidml = pathlib.Path("/usr/local/share/plaidml")
  15 + if local_user_plaidml.exists():
  16 + os.environ["RUNFILES_DIR"] = str(local_user_plaidml)
  17 + os.environ["PLAIDML_NATIVE_PATH"] = str(pathlib.Path("/usr/local/lib/libplaidml.dylib").expanduser().absolute())
  18 +
  19 +def prepare_ambient(backend, device_id, use_gpu):
  20 + if backend.lower() == 'plaidml':
  21 + os.environ["KERAS_BACKEND"] = "plaidml.keras.backend"
  22 + os.environ["PLAIDML_DEVICE_IDS"] = device_id
  23 + prepare_plaidml()
  24 + elif backend.lower() == 'theano':
  25 + os.environ["KERAS_BACKEND"] = "theano"
  26 + if use_gpu:
  27 + os.environ["THEANO_FLAGS"] = "device=cuda0"
  28 + print("Use GPU theano", os.environ["THEANO_FLAGS"])
  29 + else:
  30 + os.environ["THEANO_FLAGS"] = "device=cpu"
  31 + else:
  32 + raise TypeError("Wrong backend")
  33 +
  34 +
  35 +
1 def get_plaidml_devices(gpu=False): 36 def get_plaidml_devices(gpu=False):
  37 + prepare_plaidml()
  38 +
2 import plaidml 39 import plaidml
3 40
4 ctx = plaidml.Context() 41 ctx = plaidml.Context()
invesalius/utils.py
@@ -126,7 +126,16 @@ def next_copy_name(original_name, names_list): @@ -126,7 +126,16 @@ def next_copy_name(original_name, names_list):
126 if not (next_copy in names_list): 126 if not (next_copy in names_list):
127 got_new_name = True 127 got_new_name = True
128 return next_copy 128 return next_copy
129 - 129 +
  130 +
  131 +def new_name_by_pattern(pattern):
  132 + from invesalius.project import Project
  133 + proj = Project()
  134 + mask_dict = proj.mask_dict
  135 + names_list = [i.name for i in mask_dict.values() if i.name.startswith(pattern + "_")]
  136 + count = len(names_list) + 1
  137 + return "{}_{}".format(pattern, count)
  138 +
130 139
131 def VerifyInvalidPListCharacter(text): 140 def VerifyInvalidPListCharacter(text):
132 #print text 141 #print text