Skip to content

Commit

Permalink
Export slices mask (invesalius#166)
Browse files Browse the repository at this point in the history
* Export hdf5

* Exporting mask to hdf5

* Exporting all project to hdf5

* better hierachy of tags

* Only exporting project

* Option to export to hdf5 via command line

* suffix

* Added an option to not export masks

* exporting to nifti

* Exporting to nifti

* Doing swap axes to export nii

* Better extension handling

* Better extension handling

* fliping lr when exporting to nii
  • Loading branch information
tfmoraes authored Jan 23, 2019
1 parent e0a64d3 commit 95d0842
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 4 deletions.
18 changes: 18 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,13 @@ def parse_comand_line():
parser.add_option("-a", "--export-to-all",
help="Export to STL for all mask presets.")

parser.add_option("--export-project",
help="Export slices and mask to HDF5 or Nifti file.")

parser.add_option("--no-masks", action="store_false",
dest="save_masks", default=True,
help="Make InVesalius not export mask when exporting project.")

options, args = parser.parse_args()
return options, args

Expand Down Expand Up @@ -429,6 +436,17 @@ def check_for_export(options, suffix='', remove_surfaces=False):
finally:
exit(0)

if options.export_project:
from invesalius.project import Project
prj = Project()
export_filename = options.export_project
if suffix:
export_filename, ext = os.path.splitext(export_filename)
export_filename = u'{}-{}{}'.format(export_filename, suffix, ext)

prj.export_project(export_filename, save_masks=options.save_masks)
print("Saved {}".format(export_filename))


def export(path_, threshold_range, remove_surface=False):
import invesalius.constants as const
Expand Down
9 changes: 5 additions & 4 deletions invesalius/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,10 +506,11 @@
#----------------------------------------------------------

[ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE,
ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET,
ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_PREFERENCES, ID_DICOM_NETWORK,
ID_TIFF_JPG_PNG, ID_VIEW_INTERPOLATED, ID_MODE_NAVIGATION, ID_ANALYZE_IMPORT,
ID_NIFTI_IMPORT, ID_PARREC_IMPORT, ID_MODE_DBS] = [wx.NewId() for number in range(19)]
ID_PROJECT_CLOSE, ID_EXPORT_SLICE, ID_EXPORT_MASK, ID_PROJECT_INFO,
ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET, ID_PRINT_SCREENSHOT,
ID_IMPORT_OTHERS_FILES, ID_PREFERENCES, ID_DICOM_NETWORK, ID_TIFF_JPG_PNG,
ID_VIEW_INTERPOLATED, ID_MODE_NAVIGATION, ID_ANALYZE_IMPORT, ID_NIFTI_IMPORT,
ID_PARREC_IMPORT, ID_MODE_DBS] = [wx.NewId() for number in range(21)]
ID_EXIT = wx.ID_EXIT
ID_ABOUT = wx.ID_ABOUT

Expand Down
20 changes: 20 additions & 0 deletions invesalius/data/slice_.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ def __bind_events(self):
Publisher.subscribe(self.__show_current_mask, 'Show current mask')
Publisher.subscribe(self.__clean_current_mask, 'Clean current mask')

Publisher.subscribe(self.__export_slice, 'Export slice')
Publisher.subscribe(self.__export_actual_mask, 'Export actual mask')

Publisher.subscribe(self.__set_current_mask_threshold_limits,
'Update threshold limits')

Expand Down Expand Up @@ -426,6 +429,23 @@ def __clean_current_mask(self):
session = ses.Session()
session.ChangeProject()

def __export_slice(self, filename):
import h5py
f = h5py.File(filename, 'w')
f['data'] = self.matrix
f['spacing'] = self.spacing
f.flush()
f.close()

def __export_actual_mask(self, filename):
import h5py
f = h5py.File(filename, 'w')
self.do_threshold_to_all_slices()
f['data'] = self.current_mask.matrix[1:, 1:, 1:]
f['spacing'] = self.spacing
f.flush()
f.close()

def create_temp_mask(self):
temp_file = tempfile.mktemp()
shape = self.matrix.shape
Expand Down
35 changes: 35 additions & 0 deletions invesalius/gui/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@
VIEW_TOOLS = [ID_LAYOUT, ID_TEXT] =\
[wx.NewId() for number in range(2)]

WILDCARD_EXPORT_SLICE = "HDF5 (*.hdf5)|*.hdf5|" \
"NIfTI 1 (*.nii)|*.nii|" \
"Compressed NIfTI (*.nii.gz)|*.nii.gz"

IDX_EXT = {
0: '.hdf5',
1: '.nii',
2: '.nii.gz'
}


class MessageWatershed(wx.PopupWindow):
Expand Down Expand Up @@ -419,6 +428,8 @@ def OnMenuClick(self, evt):
self.SaveProject()
elif id == const.ID_PROJECT_SAVE_AS:
self.ShowSaveAsProject()
elif id == const.ID_EXPORT_SLICE:
self.ExportProject()
elif id == const.ID_PROJECT_CLOSE:
self.CloseProject()
elif id == const.ID_EXIT:
Expand Down Expand Up @@ -629,6 +640,28 @@ def ShowSaveAsProject(self):
"""
Publisher.sendMessage('Show save dialog', save_as=True)

def ExportProject(self):
"""
Show save dialog to export slice.
"""
p = prj.Project()

session = ses.Session()
last_directory = session.get('paths', 'last_directory_export_prj', '')
dlg = wx.FileDialog(None,
"Export slice ...",
last_directory, # last used directory
os.path.split(p.name)[-1], # initial filename
WILDCARD_EXPORT_SLICE,
wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
if dlg.ShowModal() == wx.ID_OK:
filename = dlg.GetPath()
ext = IDX_EXT[dlg.GetFilterIndex()]
if not filename.endswith(ext):
filename += ext
p.export_project(filename)
session['paths']['last_directory_export_prj'] = os.path.split(filename)[0]

def ShowBitmapImporter(self):
"""
Tiff, BMP, JPEG and PNG
Expand Down Expand Up @@ -702,6 +735,7 @@ def __init__(self, parent):
# not. Eg. save should only be available if a project is open
self.enable_items = [const.ID_PROJECT_SAVE,
const.ID_PROJECT_SAVE_AS,
const.ID_EXPORT_SLICE,
const.ID_PROJECT_CLOSE,
const.ID_REORIENT_IMG,
const.ID_FLOODFILL_MASK,
Expand Down Expand Up @@ -771,6 +805,7 @@ def __init_items(self):
app(const.ID_PROJECT_OPEN, _("Open project...\tCtrl+O"))
app(const.ID_PROJECT_SAVE, _("Save project\tCtrl+S"))
app(const.ID_PROJECT_SAVE_AS, _("Save project as...\tCtrl+Shift+S"))
app(const.ID_EXPORT_SLICE, _("Export project"))
app(const.ID_PROJECT_CLOSE, _("Close project"))
file_menu.AppendSeparator()
#app(const.ID_PROJECT_INFO, _("Project Information..."))
Expand Down
65 changes: 65 additions & 0 deletions invesalius/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import tarfile
import tempfile

import numpy as np
import wx
from wx.lib.pubsub import pub as Publisher
import vtk
Expand Down Expand Up @@ -349,6 +350,70 @@ def OpenPlistProject(self, filename):
measure.Load(measurements[index])
self.measurement_dict[int(index)] = measure

def export_project(self, filename, save_masks=True):
if filename.lower().endswith('.hdf5') or filename.lower().endswith('.h5'):
self.export_project_to_hdf5(filename, save_masks)
elif filename.lower().endswith('.nii') or filename.lower().endswith('.nii.gz'):
self.export_project_to_nifti(filename, save_masks)

def export_project_to_hdf5(self, filename, save_masks=True):
import h5py
import invesalius.data.slice_ as slc
s = slc.Slice()
with h5py.File(filename, 'w') as f:
f['image'] = s.matrix
f['spacing'] = s.spacing

f["invesalius_version"] = const.INVESALIUS_VERSION
f["date"] = datetime.datetime.now().isoformat()
f["compress"] = self.compress
f["name"] = self.name # patient's name
f["modality"] = self.modality # CT, RMI, ...
f["orientation"] = self.original_orientation
f["window_width"] = self.window
f["window_level"] = self.level
f["scalar_range"] = self.threshold_range

if save_masks:
for index in self.mask_dict:
mask = self.mask_dict[index]
s.do_threshold_to_all_slices(mask)
key = 'masks/{}'.format(index)
f[key + '/name'] = mask.name
f[key + '/matrix'] = mask.matrix[1:, 1:, 1:]
f[key + '/colour'] = mask.colour[:3]
f[key + '/opacity'] = mask.opacity
f[key + '/threshold_range'] = mask.threshold_range
f[key + '/edition_threshold_range'] = mask.edition_threshold_range
f[key + '/visible'] = mask.is_shown
f[key + '/edited'] = mask.was_edited

def export_project_to_nifti(self, filename, save_masks=True):
import invesalius.data.slice_ as slc
import nibabel as nib
s = slc.Slice()
img_nifti = nib.Nifti1Image(np.swapaxes(np.fliplr(s.matrix), 0, 2), None)
img_nifti.header.set_zooms(s.spacing)
img_nifti.header.set_dim_info(slice=0)
nib.save(img_nifti, filename)
if save_masks:
for index in self.mask_dict:
mask = self.mask_dict[index]
s.do_threshold_to_all_slices(mask)
mask_nifti = nib.Nifti1Image(np.swapaxes(np.fliplr(mask.matrix), 0, 2), None)
mask_nifti.header.set_zooms(s.spacing)
if filename.lower().endswith('.nii'):
basename = filename[:-4]
ext = filename[-4::]
elif filename.lower().endswith('.nii.gz'):
basename = filename[:-7]
ext = filename[-7::]
else:
ext = '.nii'
basename = filename
nib.save(mask_nifti, "{}_mask_{}_{}{}".format(basename, mask.index, mask.name, ext))


def Compress(folder, filename, filelist, compress=False):
tmpdir, tmpdir_ = os.path.split(folder)
current_dir = os.path.abspath(".")
Expand Down

0 comments on commit 95d0842

Please sign in to comment.