Commit 4cca86b9e7131daad20cc6d893f1f27dc324fcfd
1 parent
5f72b336
Exists in
master
and in
67 other branches
ENH: Added interface to image reduction
Showing
4 changed files
with
129 additions
and
39 deletions
Show diff stats
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): | ... | ... |