Commit 4cca86b9e7131daad20cc6d893f1f27dc324fcfd

Authored by Paulo Henrique Junqueira Amorim
1 parent 5f72b336

ENH: Added interface to image reduction

invesalius/control.py
... ... @@ -19,12 +19,12 @@
19 19 import math
20 20 import os
21 21 import plistlib
22   -
  22 +import wx
23 23 import numpy
24 24 from wx.lib.pubsub import pub as Publisher
25 25  
26 26 import constants as const
27   -import data.imagedata_utils as utils
  27 +import data.imagedata_utils as image_utils
28 28 import data.mask as msk
29 29 import data.measures
30 30 import data.slice_ as sl
... ... @@ -37,7 +37,7 @@ import reader.dicom_grouper as dg
37 37 import reader.dicom_reader as dcm
38 38 import session as ses
39 39  
40   -from utils import debug
  40 +import utils
41 41  
42 42 DEFAULT_THRESH_MODE = 0
43 43  
... ... @@ -171,14 +171,14 @@ class Controller():
171 171 try:
172 172 filename = session.project_path[1]
173 173 except(AttributeError):
174   - debug("Project doesn't exist")
  174 + utils.debug("Project doesn't exist")
175 175 filename = None
176 176  
177 177 if (filename):
178 178 if (st == const.PROJ_NEW) or (st == const.PROJ_CHANGE):
179 179 answer = dialog.SaveChangesDialog(filename, self.frame)
180 180 if not answer:
181   - debug("Close without changes")
  181 + utils.debug("Close without changes")
182 182 self.CloseProject()
183 183 Publisher.sendMessage("Enable state project", False)
184 184 Publisher.sendMessage('Set project name')
... ... @@ -186,14 +186,14 @@ class Controller():
186 186 Publisher.sendMessage("Exit")
187 187 elif answer == 1:
188 188 self.ShowDialogSaveProject()
189   - debug("Save changes and close")
  189 + utils.debug("Save changes and close")
190 190 self.CloseProject()
191 191 Publisher.sendMessage("Enable state project", False)
192 192 Publisher.sendMessage('Set project name')
193 193 Publisher.sendMessage("Stop Config Recording")
194 194 Publisher.sendMessage("Exit")
195 195 elif answer == -1:
196   - debug("Cancel")
  196 + utils.debug("Cancel")
197 197 else:
198 198 self.CloseProject()
199 199 Publisher.sendMessage("Enable state project", False)
... ... @@ -347,7 +347,7 @@ class Controller():
347 347 self.CreateAnalyzeProject(imagedata)
348 348 # OPTION 3: Nothing...
349 349 else:
350   - debug("No medical images found on given directory")
  350 + utils.debug("No medical images found on given directory")
351 351 return
352 352 self.LoadProject()
353 353 Publisher.sendMessage("Enable state project", True)
... ... @@ -420,7 +420,7 @@ class Controller():
420 420 #TODO: Verify if all Analyse are in AXIAL orientation
421 421  
422 422 # To get Z, X, Y (used by InVesaliu), not X, Y, Z
423   - matrix, matrix_filename = utils.analyze2mmap(imagedata)
  423 + matrix, matrix_filename = image_utils.analyze2mmap(imagedata)
424 424 if header['orient'] == 0:
425 425 proj.original_orientation = const.AXIAL
426 426 elif header['orient'] == 1:
... ... @@ -492,7 +492,7 @@ class Controller():
492 492 interval += 1
493 493 filelist = dicom_group.GetFilenameList()[::interval]
494 494 if not filelist:
495   - debug("Not used the IPPSorter")
  495 + utils.debug("Not used the IPPSorter")
496 496 filelist = [i.image.file for i in dicom_group.GetHandSortedList()[::interval]]
497 497  
498 498 if file_range != None and file_range[1] > file_range[0]:
... ... @@ -500,9 +500,6 @@ class Controller():
500 500  
501 501 zspacing = dicom_group.zspacing * interval
502 502  
503   - print "\n======================================="
504   - print ">>>>>>>>>>>>>>>>>> zspacing", zspacing, interval
505   - print "\n======================================="
506 503 size = dicom.image.size
507 504 bits = dicom.image.bits_allocad
508 505 sop_class_uid = dicom.acquisition.sop_class_uid
... ... @@ -514,16 +511,30 @@ class Controller():
514 511 else:
515 512 use_dcmspacing = 0
516 513  
517   - #imagedata = utils.CreateImageData(filelist, zspacing, xyspacing,size,
518   - #bits, use_dcmspacing)
519   -
520 514 imagedata = None
  515 +
  516 + sx, sy = size
  517 + n_slices = len(filelist)
  518 + resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8)
  519 +
  520 + if resolution_percentage < 1.0:
  521 + re_dialog = dialog.ResizeImageDialog()
  522 + re_dialog.SetValue(int(resolution_percentage*100))
  523 + re_dialog_value = re_dialog.ShowModal()
  524 +
  525 + if re_dialog_value == wx.ID_OK:
  526 + percentage = re_dialog.GetValue()
  527 + resolution_percentage = percentage / 100.0
  528 + else:
  529 + return
521 530  
522   -
  531 + xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage
  532 +
  533 +
523 534 wl = float(dicom.image.level)
524 535 ww = float(dicom.image.window)
525   - self.matrix, scalar_range, self.filename = utils.dcm2memmap(filelist, size,
526   - orientation)
  536 + self.matrix, scalar_range, self.filename = image_utils.dcm2memmap(filelist, size,
  537 + orientation, resolution_percentage)
527 538  
528 539 self.Slice = sl.Slice()
529 540 self.Slice.matrix = self.matrix
... ... @@ -543,10 +554,10 @@ class Controller():
543 554 message = _("Fix gantry tilt applying the degrees below")
544 555 value = -1*tilt_value
545 556 tilt_value = dialog.ShowNumberDialog(message, value)
546   - utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value)
  557 + image_utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value)
547 558 elif (tilt_value) and not (gui):
548 559 tilt_value = -1*tilt_value
549   - utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value)
  560 + image_utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value)
550 561  
551 562 self.Slice.window_level = wl
552 563 self.Slice.window_width = ww
... ...
invesalius/data/imagedata_utils.py
... ... @@ -57,21 +57,19 @@ def ResampleImage3D(imagedata, value):
57 57  
58 58 return resample.GetOutput()
59 59  
60   -def ResampleImage2D(imagedata, px, py,
  60 +def ResampleImage2D(imagedata, px=None, py=None, resolution_percentage = None,
61 61 update_progress = None):
62 62 """
63 63 Resample vtkImageData matrix.
64 64 """
  65 +
65 66 extent = imagedata.GetExtent()
66 67 spacing = imagedata.GetSpacing()
  68 + dimensions = imagedata.GetDimensions()
67 69  
68   -
69   - #if extent[1]==extent[3]:
70   - # f = extent[1]
71   - #elif extent[1]==extent[5]:
72   - # f = extent[1]
73   - #elif extent[3]==extent[5]:
74   - # f = extent[3]
  70 + if resolution_percentage:
  71 + px = math.ceil(dimensions[0] * resolution_percentage)
  72 + py = math.ceil(dimensions[1] * resolution_percentage)
75 73  
76 74 if abs(extent[1]-extent[3]) < abs(extent[3]-extent[5]):
77 75 f = extent[1]
... ... @@ -418,7 +416,7 @@ class ImageCreator:
418 416  
419 417 return imagedata
420 418  
421   -def dcm2memmap(files, slice_size, orientation):
  419 +def dcm2memmap(files, slice_size, orientation, resolution_percentage):
422 420 """
423 421 From a list of dicom files it creates memmap file in the temp folder and
424 422 returns it and its related filename.
... ... @@ -429,21 +427,43 @@ def dcm2memmap(files, slice_size, orientation):
429 427 temp_file = tempfile.mktemp()
430 428  
431 429 if orientation == 'SAGITTAL':
432   - shape = slice_size[0], slice_size[1], len(files)
  430 + if resolution_percentage == 1.0:
  431 + shape = slice_size[0], slice_size[1], len(files)
  432 + else:
  433 + shape = math.ceil(slice_size[0]*resolution_percentage),\
  434 + math.ceil(slice_size[1]*resolution_percentage), len(files)
  435 +
433 436 elif orientation == 'CORONAL':
434   - shape = slice_size[1], len(files), slice_size[0]
  437 + if resolution_percentage == 1.0:
  438 + shape = slice_size[1], len(files), slice_size[0]
  439 + else:
  440 + shape = math.ceil(slice_size[1]*resolution_percentage), len(files),\
  441 + math.ceil(slice_size[0]*resolution_percentage)
435 442 else:
436   - shape = len(files), slice_size[1], slice_size[0]
  443 + if resolution_percentage == 1.0:
  444 + shape = len(files), slice_size[1], slice_size[0]
  445 + else:
  446 + shape = len(files), math.ceil(slice_size[1]*resolution_percentage),\
  447 + math.ceil(slice_size[0]*resolution_percentage)
  448 +
437 449 matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape)
438 450 dcm_reader = vtkgdcm.vtkGDCMImageReader()
439 451 cont = 0
440 452 max_scalar = None
441 453 min_scalar = None
  454 +
442 455 for n, f in enumerate(files):
443 456 dcm_reader.SetFileName(f)
444 457 dcm_reader.Update()
445 458 image = dcm_reader.GetOutput()
446 459  
  460 + if resolution_percentage != 1.0:
  461 + image_resized = ResampleImage2D(image, px=None, py=None,\
  462 + resolution_percentage = resolution_percentage, update_progress = None)
  463 +
  464 + image = image_resized
  465 + print ">>>>>>>>>", image.GetDimensions()
  466 +
447 467 min_aux, max_aux = image.GetScalarRange()
448 468 if min_scalar is None or min_aux < min_scalar:
449 469 min_scalar = min_aux
... ...
invesalius/gui/dialogs.py
... ... @@ -82,6 +82,65 @@ class NumberDialog(wx.Dialog):
82 82 def GetValue(self):
83 83 return self.num_ctrl.GetValue()
84 84  
  85 +
  86 +class ResizeImageDialog(wx.Dialog):
  87 +
  88 + def __init__(self):#, message, value=0):
  89 + pre = wx.PreDialog()
  90 + pre.Create(None, -1, "InVesalius 3", size=wx.DefaultSize,
  91 + pos=wx.DefaultPosition,
  92 + style=wx.DEFAULT_DIALOG_STYLE)
  93 + self.PostCreate(pre)
  94 +
  95 + lbl_message = wx.StaticText(self, -1, _("Your operational system is 32bits or have \nlow memory. It's recommended to reduce the image resolution."))
  96 +
  97 + icon = wx.ArtProvider.GetBitmap(wx.ART_WARNING, wx.ART_MESSAGE_BOX, (32,32))
  98 + bmp = wx.StaticBitmap(self, -1, icon)
  99 +
  100 + btn_ok = wx.Button(self, wx.ID_OK)
  101 + btn_ok.SetHelpText("Value will be applied.")
  102 + btn_ok.SetDefault()
  103 +
  104 + btn_cancel = wx.Button(self, wx.ID_CANCEL)
  105 + btn_cancel.SetHelpText("Value will not be applied.")
  106 +
  107 + btn_sizer = wx.StdDialogButtonSizer()
  108 + btn_sizer.AddButton(btn_ok)
  109 + btn_sizer.AddButton(btn_cancel)
  110 + btn_sizer.Realize()
  111 +
  112 + lbl_message_percent = wx.StaticText(self, -1, _("Resolution percentage"))
  113 +
  114 + num_ctrl_percent = wx.SpinCtrl(self, -1, "", (30, 50))
  115 + num_ctrl_percent.SetRange(20,100)
  116 + self.num_ctrl_porcent = num_ctrl_percent
  117 +
  118 + sizer_percent = wx.BoxSizer(wx.HORIZONTAL)
  119 + sizer_percent.Add(lbl_message_percent, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
  120 + sizer_percent.Add(num_ctrl_percent, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
  121 +
  122 + sizer_itens = wx.BoxSizer(wx.VERTICAL)
  123 + sizer_itens.Add(lbl_message, 0, wx.ALIGN_CENTRE_VERTICAL|wx.ALL|wx.EXPAND, 5)
  124 + sizer_itens.AddSizer(sizer_percent, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
  125 + sizer_itens.Add(btn_sizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 25)
  126 +
  127 + sizer_general = wx.BoxSizer(wx.HORIZONTAL)
  128 + sizer_general.Add(bmp, 0, wx.ALIGN_CENTRE|wx.ALL, 15)
  129 + sizer_general.AddSizer(sizer_itens, 90, wx.ALIGN_CENTRE|wx.ALL, 10)
  130 +
  131 + self.SetSizer(sizer_general)
  132 + sizer_itens.Fit(self)
  133 + self.Layout()
  134 + self.SetAutoLayout(True)
  135 + self.Centre()
  136 +
  137 + def SetValue(self, value):
  138 + self.num_ctrl_porcent.SetValue(value)
  139 +
  140 + def GetValue(self):
  141 + return self.num_ctrl_porcent.GetValue()
  142 +
  143 +
85 144 def ShowNumberDialog(message, value=0):
86 145 dlg = NumberDialog(message, value)
87 146 dlg.SetValue(value)
... ...
invesalius/utils.py
... ... @@ -213,7 +213,7 @@ def calculate_resizing_tofitmemory(x_size,y_size,n_slices,byte):
213 213 n_slices: number of slices
214 214 byte: bytes allocated for each pixel sample
215 215 """
216   - imagesize = x_size * y_size * n_slices * byte
  216 + imagesize = x_size * y_size * n_slices * byte * 17
217 217  
218 218 sg = sigar.open()
219 219 ram_free = sg.mem().actual_free()
... ... @@ -221,6 +221,9 @@ def calculate_resizing_tofitmemory(x_size,y_size,n_slices,byte):
221 221 swap_free = sg.swap().free()
222 222 sg.close()
223 223  
  224 + print "RAM FREE", ram_free
  225 + print "RAM_TOTAL", ram_total
  226 +
224 227 if (sys.platform == 'win32'):
225 228 if (platform.architecture()[0] == '32bit'):
226 229 if ram_free>1400000000:
... ... @@ -237,14 +240,11 @@ def calculate_resizing_tofitmemory(x_size,y_size,n_slices,byte):
237 240  
238 241 if (swap_free>ram_total):
239 242 swap_free=ram_total
240   - resize = (float((ram_free+0.5*swap_free)/imagesize))
  243 + resize = (float((ram_free+0.5*swap_free)/imagesize))
241 244 resize=math.sqrt(resize) # this gives the "resize" for each axis x and y
242 245 if (resize>1):
243 246 resize=1
244   - return (100*resize)
245   -
246   -
247   -
  247 + return round(resize,2)
248 248  
249 249  
250 250 def predict_memory(nfiles, x, y, p):
... ...