Skip to content

Commit

Permalink
Merge pull request #64 from ofgulban/devel
Browse files Browse the repository at this point in the history
v1.4.0
  • Loading branch information
ofgulban authored Jan 12, 2018
2 parents fe73491 + 4789318 commit acbc9da
Show file tree
Hide file tree
Showing 17 changed files with 214 additions and 114 deletions.
6 changes: 3 additions & 3 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ build: false

environment:
matrix:
- PYTHON_VERSION: 2.7
- PYTHON_VERSION: 3.6
MINICONDA: C:\Miniconda

init:
Expand All @@ -12,9 +12,9 @@ install:
- "set PATH=%MINICONDA%;%MINICONDA%\\Scripts;%PATH%"
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- "conda env create --force -f environment.yml python=2.7"
- "conda env create --force -f environment.yml python=3.6"
- activate segmentator
- python setup.py develop
- python setup.py install

test_script:
- py.test
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@

# Segmentator

<img src="logo/logo.png" width=420 align="right" />
<img src="visuals/logo.png" width=420 align="right" />

Segmentator is a free and open-source package for multi-dimensional data exploration and segmentation for 3D images. This application is mainly developed and tested using ultra-high field magnetic resonance imaging (MRI) brain data.


The goal is to provide a complementary tool to the already available brain tissue segmentation methods (to the best of our knowledge) in other software packages (FSL, Freesurfer, SPM, Brainvoyager, itk-SNAP, etc.).

### Citation:
- Our preprint can be accessed from __[this link.](http://biorxiv.org/cgi/content/short/245738v1)__
- Released versions of this package can be cited by using our __[Zenodo DOI](https://zenodo.org/badge/latestdoi/59303623).__

<img src="visuals/animation_01.gif" width=840 align="center" />

## Core dependencies
[**Python 2.7**](https://www.python.org/download/releases/2.7/)

Expand Down Expand Up @@ -43,7 +49,7 @@ segmentator /path/to/file.nii.gz
segmentator --help
```

Visit [our wiki](https://github.com/ofgulban/segmentator/wiki/Installation) to see alternative installation methods.
Check out __[our wiki](https://github.com/ofgulban/segmentator/wiki)__ for further details such as [GUI controls](https://github.com/ofgulban/segmentator/wiki/Controls), [alternative installation methods](https://github.com/ofgulban/segmentator/wiki/Installation) and more...

## Support
Please use [GitHub issues](https://github.com/ofgulban/segmentator/issues) for questions, bug reports or feature requests.
Expand Down
23 changes: 13 additions & 10 deletions segmentator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,19 @@
TODO: Argument parsing can be better structured, maybe by using parents. help
looks a bit messy as is.
"""

import sys
import argparse
import config as cfg
from segmentator import __version__


def main(args=None):
def main():
"""Command line call argument parsing."""
if args is None:
args = sys.argv[1:]
# Main arguments
# Instantiate argument parser object:
parser = argparse.ArgumentParser()

# Add arguments to namespace:
parser.add_argument(
'filename', metavar='path',
help="Path to input. Mostly a nifti file with image data."
Expand All @@ -39,7 +37,7 @@ def main(args=None):
parser.add_argument(
"--scale", metavar=str(cfg.scale), required=False, type=float,
default=cfg.scale,
help="Data is scaled from 0 to this number."
help="Determines nr of bins. Data is scaled between 0 to this number."
)
parser.add_argument(
"--percmin", metavar=str(cfg.perc_min), required=False, type=float,
Expand Down Expand Up @@ -70,6 +68,10 @@ def main(args=None):
"--include_zeros", action='store_true',
help="Include image zeros in histograms. Not used by default."
)
parser.add_argument(
"--export_gramag", action='store_true',
help="Export the gradient magnitude image. Not used by default."
)

# used in ncut preparation (TODO: not yet tested after restructuring.)
parser.add_argument(
Expand Down Expand Up @@ -115,6 +117,7 @@ def main(args=None):
cfg.cbar_init = args.cbar_init
if args.include_zeros:
cfg.discard_zeros = False
cfg.export_gramag = args.export_gramag
# used in ncut preparation
cfg.ncut_figs = args.ncut_figs
cfg.max_rec = args.ncut_maxRec
Expand All @@ -129,13 +132,13 @@ def main(args=None):

# Call other scripts with import method (couldn't find a better way).
if args.nogui:
print '--No GUI option is selected. Saving 2D histogram image...'
print('--No GUI option is selected. Saving 2D histogram image...')
import hist2d_counts
elif args.ncut_prepare:
print '--Preparing N-cut related files...'
print('--Preparing N-cut related files...')
import ncut_prepare
elif args.ncut:
print '--Experimental N-cut feature is selected.'
print('--Experimental N-cut feature is selected.')
import segmentator_ncut
else:
import segmentator_main
Expand Down
1 change: 1 addition & 0 deletions segmentator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
cbar_max = 5.0
cbar_init = 3.0
discard_zeros = True
export_gramag = False

# possible gradient magnitude computation keyword options
gramag_options = ['scharr', 'sobel', 'prewitt', 'numpy']
Expand Down
6 changes: 3 additions & 3 deletions segmentator/future/deriche.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
gra_z = np.transpose(gra_z, (2, 0, 1))

end = time.time()
print 'Gradients are computed in:', (end - start), 'seconds'
print('Gradients are computed in:', (end - start), 'seconds')

print 'Saving the gradient magnitude image.'
print('Saving the gradient magnitude image.')
# put the data in 4D format and save
temp = np.array([gra_x, gra_y, gra_z])
temp = np.transpose(temp, (1, 2, 3, 0))
Expand All @@ -71,4 +71,4 @@
outName = basename+'_deriche_a' + str(alpha) + '_graMag'
outName = outName.replace('.', 'pt')
save(out, outName + '.nii.gz')
print 'Saved as:', outName
print('Saved as:', outName)
File renamed without changes.
File renamed without changes.
69 changes: 43 additions & 26 deletions segmentator/gui_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,22 @@ def remapMsks(self, remap_slice=True):
self.volHistMaskH.set_extent((0, self.nrBins, self.nrBins, 0))
# histogram to image mapping
if remap_slice:
self.imaSlcMsk = map_2D_hist_to_ima(
self.invHistVolume[:, :, self.sliceNr], self.volHistMask)
temp_slice = self.invHistVolume[:, :, self.sliceNr]
image_slice_shape = self.invHistVolume[:, :, self.sliceNr].shape
if cfg.discard_zeros:
zmask = temp_slice != 0
image_slice_mask = map_2D_hist_to_ima(temp_slice[zmask],
self.volHistMask)
# reshape to image slice shape
self.imaSlcMsk = np.zeros(image_slice_shape)
self.imaSlcMsk[zmask] = image_slice_mask
else:
image_slice_mask = map_2D_hist_to_ima(temp_slice.flatten(),
self.volHistMask)
# reshape to image slice shape
self.imaSlcMsk = image_slice_mask.reshape(image_slice_shape)

# for optional border visualization
if self.borderSwitch == 1:
self.imaSlcMsk = self.calcImaMaskBrd()

Expand Down Expand Up @@ -149,7 +163,7 @@ def findVoxInHist(self, event):
pixel_x = int(np.floor(event.xdata))
pixel_y = int(np.floor(event.ydata))
aoi = self.invHistVolume[:, :, self.sliceNr] # array of interest
# Check rotation (TODO: code repetition!)
# Check rotation
cyc_rot = self.cycRotHistory[self.cycleCount][1]
if cyc_rot == 1: # 90
aoi = np.rot90(aoi, axes=(0, 1))
Expand Down Expand Up @@ -181,7 +195,7 @@ def on_press(self, event):
if self.ctrlHeld is False: # ctrl no
contains = self.contains(event)
if not contains:
print 'cursor outside circle mask'
print('cursor outside circle mask')
if not contains:
return
# get sector centre x and y positions
Expand Down Expand Up @@ -235,12 +249,12 @@ def on_press(self, event):
# the first click the entire field constitutes the subfield
counter = int(self.counterField[ybin][xbin])
if counter+1 >= self.ima_ncut_labels.shape[2]:
print "already at maximum ncut dimension"
print("already at maximum ncut dimension")
return
self.counterField[(
self.ima_ncut_labels[:, :, counter] ==
self.ima_ncut_labels[[ybin], [xbin], counter])] += 1
print "counter:" + str(counter+1)
print("counter:" + str(counter+1))
# define arrays with old and new labels for later indexing
oLabels = self.ima_ncut_labels[:, :, counter]
nLabels = self.ima_ncut_labels[:, :, counter+1]
Expand Down Expand Up @@ -373,7 +387,7 @@ def checkRotation(self):

def exportNifti(self, event):
"""Export labels in the image browser as a nifti file."""
print "start exporting labels..."
print("Start exporting labels...")
# put the permuted indices back to their original format
cycBackPerm = (self.cycleCount, (self.cycleCount+1) % 3,
(self.cycleCount+2) % 3)
Expand All @@ -384,11 +398,19 @@ def exportNifti(self, event):
for label, newLabel in zip(labels, intLabels):
out_volHistMask[out_volHistMask == label] = intLabels[newLabel]
# get 3D brain mask
temp = np.transpose(self.invHistVolume, cycBackPerm)
outNii = map_2D_hist_to_ima(temp, out_volHistMask)
outNii = outNii.reshape(temp.shape)
volume_image = np.transpose(self.invHistVolume, cycBackPerm)
if cfg.discard_zeros:
zmask = volume_image != 0
temp_labeled_image = map_2D_hist_to_ima(volume_image[zmask],
out_volHistMask)
out_nii = np.zeros(volume_image.shape)
out_nii[zmask] = temp_labeled_image # put back flat labels
else:
out_nii = map_2D_hist_to_ima(volume_image.flatten(),
out_volHistMask)
out_nii = out_nii.reshape(volume_image.shape)
# save mask image as nii
new_image = Nifti1Image(outNii, header=self.nii.get_header(),
new_image = Nifti1Image(out_nii, header=self.nii.get_header(),
affine=self.nii.get_affine())
# get new flex file name and check for overwriting
self.nrExports = 0
Expand All @@ -397,8 +419,8 @@ def exportNifti(self, event):
self.nrExports += 1
self.flexfilename = '_labels_' + str(self.nrExports) + '.nii.gz'
save(new_image, self.basename + self.flexfilename)
print "successfully exported image labels as: \n" + \
self.basename + self.flexfilename
print("successfully exported image labels as: \n"
+ self.basename + self.flexfilename)

def clearOverlays(self):
"""Clear overlaid items such as circle highlights."""
Expand Down Expand Up @@ -478,14 +500,14 @@ def exportNyp(self, event):
outFileName = outFileName.replace('identifier', 'volHistLabels')
outFileName = outFileName.replace('.', 'pt')
np.save(outFileName, self.volHistMask)
print "successfully exported histogram colors as: \n" + \
outFileName
print("Successfully exported histogram colors as: \n"
+ outFileName)
elif self.segmType == 'main':
outFileName = outFileName.replace('identifier', 'volHist')
outFileName = outFileName.replace('.', 'pt')
np.save(outFileName, self.counts)
print "successfully exported histogram counts as: \n" + \
outFileName
print("successfully exported histogram counts as: \n"
+ outFileName)
else:
return

Expand Down Expand Up @@ -655,14 +677,9 @@ def contains(self, event):

def draw(self, ax, cmap='Reds', alpha=0.2, vmin=0.1,
interpolation='nearest', origin='lower', extent=[0, 100, 0, 100]):
"""Draw stuff."""
"""Draw sector mask."""
BinMask = self.binaryMask()
FigObj = ax.imshow(
BinMask,
cmap=cmap,
alpha=alpha,
vmin=vmin,
interpolation=interpolation,
origin=origin,
extent=extent)
FigObj = ax.imshow(BinMask, cmap=cmap, alpha=alpha, vmin=vmin,
interpolation=interpolation, origin=origin,
extent=extent)
return (FigObj, BinMask)
4 changes: 2 additions & 2 deletions segmentator/hist2d_counts.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

# data processing
orig = np.squeeze(nii.get_data())
orig = truncate_range(orig, percMin=cfg.perc_min, percMax=cfg.perc_max)
orig, _, _ = truncate_range(orig, percMin=cfg.perc_min, percMax=cfg.perc_max)
orig = scale_range(orig, scale_factor=cfg.scale, delta=0.0001)
gra = set_gradient_magnitude(orig, cfg.gramag)

Expand All @@ -44,4 +44,4 @@
)
outName = outName.replace('.', 'pt')
np.save(outName, counts)
print '----Image saved as:\n ' + outName
print('----Image saved as:\n ' + outName)
Loading

0 comments on commit acbc9da

Please sign in to comment.