-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
ms
authored and
ms
committed
Dec 16, 2019
1 parent
7f39f3d
commit a63eeac
Showing
12 changed files
with
444 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
## DeepBleed | ||
|
||
ICH volumetric estimation is a task routinely performed in clinical practice. This is the first publicly available deep neural network model to perform the task of ICH segmentation and volume estimation. The model was originally developed in testing the hypothesis that an ICH segmentation deep neural network could be trained in an earlier clinical trial phase (MISTIE Phase II) and validated by a later phase (MISTIE Phase III), with results on par or better than customized architectures and models trained on significantly larger, curated single center datasets. The implication being that such a model could be used to derive metrics for a multicenter clinical trial. | ||
|
||
We provide the 3D model from our paper "High-Throughput 3D Segmentation of Intracerebral Hemorrhage: Development and Validation within a Clinical Trial Series". The model version 1.0 will perform binary segmentation of ICH and will include areas of IVH if present. The original model expects non-contrast CT with prior preprocessing described in our paper, including a validated brain extraction method and spatial normalization via registration to a 1.5mm x 1.5mm x 1.5mm template with a shape of (128, 128, 128). Support for deployment on a local machine or on the cloud via docker image is provided. | ||
|
||
Please read the software license, this is not intended for any clinical or commercial use. | ||
|
||
|
||
To install move to the directory of DeepBleed that was just cloned and type: | ||
``` | ||
$ python setup.py install | ||
``` | ||
If it happens to be missing some dependencies listed above, you may install them with pip: <br/> | ||
``` | ||
$ pip install tensorflow-gpu==2.0.0 | ||
$ pip install nibabel | ||
$ ... | ||
``` | ||
Alternatively, you can run the program in docker. You can directly pull the docker image from dockerhub and avoid installing dependencies. (For tutorials on docker, see [docker](https://docs.docker.com/install/) and [nvidia-docker](https://github.com/NVIDIA/nvidia-docker)) | ||
|
||
``` | ||
$ docker pull msharrock/neuroimage:tf-2.0 | ||
$ docker run -it --rm msharrock/neuroimage:tf-2.0 | ||
``` | ||
The underlying deep neural network architecture is based on the VNET by Milletari et al. at https://github.com/faustomilletari/VNet | ||
|
||
|
||
|
||
## Authors | ||
|
||
* **Matthew Sharrock** - *Study Design, Neural Network Dev/Training* | ||
* **John Muschelli** - *Study Design, Preprocessing, Statistical Validation* | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# @author: msharrock | ||
# version: 0.0.1 | ||
|
||
""" | ||
VNet Blocks for DeepBleed | ||
tensorflow version 2.0 | ||
""" | ||
|
||
import tensorflow as tf | ||
from tensorflow.keras import layers | ||
|
||
class VNetInBlock(layers.Layer): | ||
def __init__(self, shape): | ||
super(VNetInBlock, self).__init__() | ||
self.shape = shape | ||
self.add = layers.Add() | ||
self.inlayer = layers.Input(shape = self.shape) | ||
self.concatenate = layers.Concatenate() | ||
self.convolution = layers.Conv3D(filters=16, kernel_size=(5,5,5), strides=1, | ||
padding='same', kernel_initializer='he_normal', activation='relu') | ||
|
||
def call(self, inputs): | ||
#x_in = self.inlayer(inputs) | ||
x = self.convolution(inputs) | ||
d = self.concatenate(16 * [inputs]) | ||
x = self.add([x, d]) | ||
return x | ||
|
||
class VNetDownBlock(layers.Layer): | ||
def __init__(self, channels, n_convs): | ||
super(VNetDownBlock, self).__init__() | ||
self.channels = channels | ||
self.n_convs = n_convs | ||
self.add = layers.Add() | ||
self.downsample = layers.Conv3D(filters=self.channels, kernel_size=(2,2,2), strides=2, | ||
padding='valid', kernel_initializer='he_normal', activation='relu') | ||
self.convolution = layers.Conv3D(filters=self.channels, kernel_size=(5,5,5), strides=1, | ||
padding='same', kernel_initializer='he_normal', activation='relu') | ||
|
||
def call(self, inputs): | ||
d = self.downsample(inputs) | ||
|
||
for _ in range(self.n_convs): | ||
x = self.convolution(d) | ||
|
||
x = self.add([x, d]) | ||
|
||
return x | ||
|
||
class VNetUpBlock(layers.Layer): | ||
def __init__(self, channels, n_convs): | ||
super(VNetUpBlock, self).__init__() | ||
self.channels = channels | ||
self.n_convs = n_convs | ||
self.add = layers.Add() | ||
self.concatenate = layers.Concatenate() | ||
self.upsample = layers.Conv3DTranspose(filters=self.channels//2, kernel_size=(2,2,2), strides=2, | ||
padding='valid', kernel_initializer='he_normal', activation='relu') | ||
self.convolution = layers.Conv3D(filters=self.channels, kernel_size=(5,5,5), strides=1, | ||
padding='same', kernel_initializer='he_normal', activation='relu') | ||
|
||
def call(self, inputs, skip): | ||
|
||
x = self.upsample(inputs) | ||
cat = self.concatenate([x, skip]) | ||
|
||
for _ in range(self.n_convs): | ||
x = self.convolution(cat) | ||
|
||
x = self.add([x, cat]) | ||
|
||
return x | ||
|
||
class VNetOutBlock(layers.Layer): | ||
|
||
def __init__(self, in_chns): | ||
super(VNetOutBlock, self).__init__() | ||
self.in_chns = in_chns | ||
self.final = layers.Conv3D(filters=2, kernel_size=(1,1,1), strides=1, | ||
padding='valid', kernel_initializer='he_normal', activation='relu') | ||
|
||
self.binary = layers.Conv3D(filters=1, kernel_size=(1,1,1), strides=1, | ||
padding='valid', kernel_initializer='he_normal', activation='sigmoid') | ||
#self.softmax = layers.Softmax() | ||
self.argmax = | ||
|
||
def call(self, inputs): | ||
x = self.final(inputs) | ||
x = self.binary(x) | ||
return x | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
By using this software, you are agreeing to the following terms and conditions. | ||
|
||
- Permission is granted to use this software without charge for non-commercial research purposes only. | ||
- Other than the rights granted herein, the authors retain all rights, title, and interest in the software and Technology, and You retain all rights, title, and interest in Your Modifications and associated specifications, subject to the terms of this License. | ||
- You may make verbatim copies of this software for personal use, or for use within your organization, provided that you duplicate all of the original copyright notices and associated disclaimers. If you provide the use of the software to other users within your organization, they also must comply with all the terms of this Software Distribution Agreement. | ||
- You must not remove or alter any copyright or other proprietary notices in the software. | ||
- Software has not been reviewed or approved by the Food and Drug Administration, and is for non-clinical, IRB-approved Research Use Only. In no event shall data or images generated through the use of the Software be used in the provision of patient care. | ||
- THE SOFTWARE IS PROVIDED “AS IS,” AND THE AUTHORS AND COLLABORATORS DO NOT MAKE ANY WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE. | ||
- This software is for research purposes only and has not been approved for clinical use. | ||
- You may publish papers and books using results produced using software provided by this site provided that you contact the authors and reference the appropriate citations. A list of citations is available here. | ||
- You agree to comply Trademark Usage Requirements, as modified from time to time, described in the “Use of Materials Limitations” section of the LONI Terms of Use Agreement. | ||
- All Technology and technical data delivered under this Agreement are subject to US export control laws and may be subject to export or import regulations in other countries. You agree to comply strictly with all such laws and regulations and acknowledge that you have the responsibility to obtain such licenses to export, re-export, or import as may be required after delivery to you. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
|
||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# @author: msharrock | ||
# version: 0.0.1 | ||
|
||
""" | ||
Neural Net Models for DeepBleed | ||
tensorflow version 2.0 | ||
""" | ||
|
||
import tensorflow as tf | ||
from tensorflow.keras import layers | ||
from blocks import vnet | ||
|
||
""" | ||
Model below is the VNet architecture for volumetric anatomic segmentation, | ||
originally by Milletari et al. | ||
'V-Net: Fully Convolutional Neural Networks for Volumetric Medical Image Segmentation' | ||
https://arxiv.org/abs/1606.04797 | ||
""" | ||
|
||
|
||
class VNet(keras.Model): | ||
def __init__(self): | ||
super(VNet, self).__init__() | ||
input_layer = VNetInBlock(16) | ||
down_1 = VNetDownBlock(32, 2) | ||
down_2 = VNetDownBlock(64, 3) | ||
down_3 = VNetDownBlock(128, 3) | ||
down_4 = VNetDownBlock(256, 3) | ||
up_4 = VNetUpBlock(256, 3) | ||
up_3 = VNetUpBlock(128, 3) | ||
up_2 = VNetUpBlock(64, 2) | ||
up_1 = VNetUpBlock(32, 1) | ||
outblock = VNetOutBlock(32) | ||
|
||
def call(self, inputs, shape): | ||
inputs = layers.Input(shape = shape) | ||
x_16 = input_layer(inputs) | ||
x_32 = down_1(x_16) | ||
x_64 = down_2(x_32) | ||
x_128 = down_3(x_64) | ||
x_256 = down_4(x_128) | ||
|
||
x = up_4(x_256, skip=x_128) | ||
x = up_3(x, skip=x_64) | ||
x = up_2(x, skip=x_32) | ||
x = up_1(x, skip=x_16) | ||
outputs = outblock(x) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# @author: msharrock | ||
# version: 0.0.1 | ||
|
||
""" | ||
Prediction Script for DeepBleed | ||
Command Line Arguments: | ||
--indir: string, location to perform prediction | ||
--outdir: string, location to save predictions | ||
--weights: string, optional, location of model weights | ||
--cpus: int, optional, number of cpu cores to utilize | ||
--gpus: int, optional, number of gpus to utilize | ||
""" | ||
|
||
import os | ||
import shutil | ||
|
||
import fsl | ||
import ants | ||
import nibabel as nib | ||
import tensorflow as tf | ||
|
||
from tools import parse | ||
from preprocess import extract, register, convert | ||
from models import vnet | ||
|
||
# load command line arguments | ||
setup = parse.args('predict') | ||
if setup.CPUS == None: | ||
setup.CPUS = '1' | ||
# environmental variable setup | ||
os.environ["ANTS_RANDOM_SEED"] = '1' | ||
os.environ['FSLOUTPUTTYPE'] = 'NIFTI_GZ' | ||
|
||
if setup.CPUS == None: | ||
pass | ||
else: | ||
os.environ["ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS"] = setup.CPUS | ||
|
||
if setup.GPUS == None: | ||
os.environ['CUDA_VISIBLE_DEVICES'] = '0' | ||
else: | ||
os.environ['CUDA_VISIBLE_DEVICES'] = ','.join(map(str,range(setup.GPUS))) | ||
|
||
# set paths to scripts, templates, weights etc. | ||
TEMPLATE_PATH = os.path.join('templates', 'scct_unsmooth_SS_0.01_128x128x128.nii.gz') | ||
WEIGHT_PATH = setup.weights | ||
|
||
# setup directory trees | ||
IN_DIR = setup.IN_DIR | ||
OUT_DIR = setup.OUT_DIR | ||
|
||
# load the model and weights | ||
model = VNet() | ||
model.load_weights() | ||
|
||
# load input data | ||
files = sorted(next(os.walk(IN_DIR)))[2] | ||
template = ants.image_read(TEMPLATE_PATH, pixeltype = 'float') | ||
|
||
for filename in files: | ||
|
||
# preprocessing | ||
image = nib.load(filename) | ||
image = extract.brain(image) | ||
image = convert.nii2ants(image) | ||
image, transforms = register.rigid(template, image) | ||
image = convert.ants2tf(image) | ||
|
||
# neural net prediction | ||
prediction = model.predict(image) | ||
|
||
# invert registration | ||
image = ants.img_read(filename) | ||
prediction = convert.tf2ants(prediction) | ||
prediction = register.invert(image, prediction, transforms) | ||
prediction = convert.ants2nii(prediction) | ||
nib.save(prediction, os.path.join(OUT_DIR, filename)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# @author: msharrock | ||
# version: 0.0.1 | ||
|
||
""" | ||
Image Format Conversion for DeepBleed | ||
""" | ||
import.tf | ||
import ants | ||
import nibabel as nib | ||
|
||
def ants2nii(image): | ||
array_data = image.numpy() | ||
affine = np.hstack([image.direction*np.diag(image.spacing),np.array(image.origin).reshape(3,1)]) | ||
affine = np.vstack([affine, np.array([0,0,0,1.])]) | ||
nii_image = nib.Nifti1Image(array_data, affine) | ||
return nii_image | ||
|
||
def nii2ants(image): | ||
from tempfile import mktemp | ||
tmpfile = mktemp(suffix='.nii.gz') | ||
image.to_filename(tmpfile) | ||
image = ants.image_read(tmpfile, pixeltype = 'float') | ||
os.remove(tmpfile) | ||
return ants_image | ||
|
||
def ants2tf(image): | ||
image = image.numpy() | ||
tf_image = tf.convert_to_tensor(image, dtype=tf.float32) | ||
tf_image = tf.expand_dims(tf_image, -1) | ||
return tf_image |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# @author: msharrock | ||
# version: 0.0.1 | ||
|
||
''' | ||
Extraction methods for DeepBleed | ||
''' | ||
|
||
import os | ||
|
||
from fsl.wrappers import fslmaths, bet | ||
|
||
|
||
def brain(image): | ||
|
||
''' | ||
Brain Extraction with FSL | ||
Params: | ||
- image: nifti object, scan to brain extract | ||
Output: | ||
- brain_image: nifti object, extracted brain | ||
''' | ||
tmpfile = 'tmpfile.nii.gz' | ||
image.to_filename(tmpfile) | ||
mask = fslmaths(image).thr('0.000000').uthr('100.000000').bin().fillh().run() | ||
fslmaths(image).mas(mask).run(tmpfile) | ||
bet(tmpfile, tmpfile, fracintensity = 0.01) | ||
mask = fslmaths(tmpfile).bin().fillh().run() | ||
brain_image = fslmaths(image).mas(mask).run() | ||
os.remove(tmpfile) | ||
|
||
return brain_image | ||
|
Oops, something went wrong.