Commit d4ff914694d97469f49962b6a39ea8007626811b

Authored by Paulo Henrique Junqueira Amorim
Committed by GitHub
1 parent e586881b

Micro ct improvements (#45)

* FIX: Error in bitmap reader in Win32

* FIX: Fixed error in window and level

* FIX: Fixed problem with inverted slices and uint16 images

* FIX: Fixed problem with resample

* ENH: Reader load spacing from txt file info

* ENH: Dialog to show that have images with different size

* ENH; Enhancemets to sort files in folder

* FIX: Changed message when are not bitmap files

* ENH: Added uCT label in menu

* ENH: Enhancements in bitmap panel

* NEW: Delete image in InVesalius import panel

* ENH: Enhancements in delete window

* ENH: Enhancements in import bitmap panel

* ENH: Enhancements to delete bmp in import bitmap panel

* ENH: Added black slice when delete last slice
invesalius/control.py
@@ -95,6 +95,11 @@ class Controller(): @@ -95,6 +95,11 @@ class Controller():
95 95
96 Publisher.subscribe(self.ApplyReorientation, 'Apply reorientation') 96 Publisher.subscribe(self.ApplyReorientation, 'Apply reorientation')
97 97
  98 + Publisher.subscribe(self.SetBitmapSpacing, 'Set bitmap spacing')
  99 +
  100 + def SetBitmapSpacing(self, pubsub_evt):
  101 + proj = prj.Project()
  102 + proj.spacing = pubsub_evt.data
98 103
99 def OnCancelImport(self, pubsub_evt): 104 def OnCancelImport(self, pubsub_evt):
100 #self.cancel_import = True 105 #self.cancel_import = True
@@ -381,10 +386,9 @@ class Controller(): @@ -381,10 +386,9 @@ class Controller():
381 #Publisher.sendMessage("Load bitmap preview", first_patient) 386 #Publisher.sendMessage("Load bitmap preview", first_patient)
382 if data: 387 if data:
383 Publisher.sendMessage("Load import bitmap panel", data) 388 Publisher.sendMessage("Load import bitmap panel", data)
384 -  
385 return True 389 return True
386 else: 390 else:
387 - dialog.ImportInvalidFiles() 391 + dialog.ImportInvalidFiles("Bitmap")
388 return False 392 return False
389 393
390 394
@@ -395,7 +399,7 @@ class Controller(): @@ -395,7 +399,7 @@ class Controller():
395 Publisher.sendMessage("Load dicom preview", first_patient) 399 Publisher.sendMessage("Load dicom preview", first_patient)
396 return True 400 return True
397 else: 401 else:
398 - dialog.ImportInvalidFiles() 402 + dialog.ImportInvalidFiles("DICOM")
399 return False 403 return False
400 404
401 405
@@ -580,10 +584,11 @@ class Controller(): @@ -580,10 +584,11 @@ class Controller():
580 proj.matrix_filename = matrix_filename 584 proj.matrix_filename = matrix_filename
581 #proj.imagedata = imagedata 585 #proj.imagedata = imagedata
582 #proj.dicom_sample = dicom 586 #proj.dicom_sample = dicom
  587 +
583 proj.original_orientation =\ 588 proj.original_orientation =\
584 name_to_const[orientation.upper()] 589 name_to_const[orientation.upper()]
585 proj.window = float(matrix.max()) 590 proj.window = float(matrix.max())
586 - proj.level = float(matrix.max()/2) 591 + proj.level = float(matrix.max()/4)
587 592
588 proj.threshold_range = int(matrix.min()), int(matrix.max()) 593 proj.threshold_range = int(matrix.min()), int(matrix.max())
589 #const.THRESHOLD_RANGE = proj.threshold_range 594 #const.THRESHOLD_RANGE = proj.threshold_range
@@ -601,91 +606,79 @@ class Controller(): @@ -601,91 +606,79 @@ class Controller():
601 def OnOpenBitmapFiles(self, pubsub_evt): 606 def OnOpenBitmapFiles(self, pubsub_evt):
602 rec_data = pubsub_evt.data 607 rec_data = pubsub_evt.data
603 bmp_data = bmp.BitmapData() 608 bmp_data = bmp.BitmapData()
604 - matrix, matrix_filename = self.OpenBitmapFiles(bmp_data, rec_data)  
605 -  
606 - self.CreateBitmapProject(bmp_data, rec_data, matrix, matrix_filename)  
607 609
608 - self.LoadProject()  
609 - Publisher.sendMessage("Enable state project", True) 610 + if bmp_data.IsAllBitmapSameSize():
610 611
  612 + matrix, matrix_filename = self.OpenBitmapFiles(bmp_data, rec_data)
  613 +
  614 + self.CreateBitmapProject(bmp_data, rec_data, matrix, matrix_filename)
611 615
612 - def OpenBitmapFiles(self, bmp_data, rec_data): 616 + self.LoadProject()
  617 + Publisher.sendMessage("Enable state project", True)
  618 + else:
  619 + dialogs.BitmapNotSameSize()
613 620
614 - if bmp_data.IsAllBitmapSameSize(): 621 + def OpenBitmapFiles(self, bmp_data, rec_data):
615 622
616 - name = rec_data[0]  
617 - orientation = rec_data[1]  
618 - sp_x = float(rec_data[2])  
619 - sp_y = float(rec_data[3])  
620 - sp_z = float(rec_data[4])  
621 - interval = int(rec_data[5])  
622 -  
623 - interval += 1  
624 -  
625 - filelist = bmp_data.GetOnlyBitmapPath()[::interval]  
626 - bits = bmp_data.GetFirstPixelSize() 623 + name = rec_data[0]
  624 + orientation = rec_data[1]
  625 + sp_x = float(rec_data[2])
  626 + sp_y = float(rec_data[3])
  627 + sp_z = float(rec_data[4])
  628 + interval = int(rec_data[5])
  629 +
  630 + interval += 1
  631 +
  632 + filelist = bmp_data.GetOnlyBitmapPath()[::interval]
  633 + bits = bmp_data.GetFirstPixelSize()
627 634
628 - sx, sy = size = bmp_data.GetFirstBitmapSize()  
629 - n_slices = len(filelist)  
630 - resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8)  
631 -  
632 - zspacing = sp_z * interval  
633 - xyspacing = (sp_y, sp_x)  
634 -  
635 - if resolution_percentage < 1.0:  
636 -  
637 - re_dialog = dialog.ResizeImageDialog()  
638 - re_dialog.SetValue(int(resolution_percentage*100))  
639 - re_dialog_value = re_dialog.ShowModal()  
640 - re_dialog.Close()  
641 -  
642 - if re_dialog_value == wx.ID_OK:  
643 - percentage = re_dialog.GetValue()  
644 - resolution_percentage = percentage / 100.0  
645 - else:  
646 - return  
647 -  
648 - xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage 635 + sx, sy = size = bmp_data.GetFirstBitmapSize()
  636 + n_slices = len(filelist)
  637 + resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8)
  638 +
  639 + zspacing = sp_z * interval
  640 + xyspacing = (sp_y, sp_x)
  641 +
  642 + if resolution_percentage < 1.0:
  643 +
  644 + re_dialog = dialog.ResizeImageDialog()
  645 + re_dialog.SetValue(int(resolution_percentage*100))
  646 + re_dialog_value = re_dialog.ShowModal()
  647 + re_dialog.Close()
  648 +
  649 + if re_dialog_value == wx.ID_OK:
  650 + percentage = re_dialog.GetValue()
  651 + resolution_percentage = percentage / 100.0
  652 + else:
  653 + return
  654 +
  655 + xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage
649 656
650 657
651 -  
652 - self.matrix, scalar_range, self.filename = image_utils.bitmap2memmap(filelist, size,  
653 - orientation, (sp_z, sp_y, sp_x),resolution_percentage) 658 +
  659 + self.matrix, scalar_range, self.filename = image_utils.bitmap2memmap(filelist, size,
  660 + orientation, (sp_z, sp_y, sp_x),resolution_percentage)
654 661
655 662
656 - self.Slice = sl.Slice()  
657 - self.Slice.matrix = self.matrix  
658 - self.Slice.matrix_filename = self.filename 663 + self.Slice = sl.Slice()
  664 + self.Slice.matrix = self.matrix
  665 + self.Slice.matrix_filename = self.filename
659 666
660 - if orientation == 'AXIAL':  
661 - self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing  
662 - elif orientation == 'CORONAL':  
663 - self.Slice.spacing = xyspacing[0], zspacing, xyspacing[1]  
664 - elif orientation == 'SAGITTAL':  
665 - self.Slice.spacing = zspacing, xyspacing[1], xyspacing[0]  
666 -  
667 - # 1(a): Fix gantry tilt, if any  
668 - #tilt_value = dicom.acquisition.tilt  
669 - #if (tilt_value) and (gui):  
670 - # # Tell user gantry tilt and fix, according to answer  
671 - # message = _("Fix gantry tilt applying the degrees below")  
672 - # value = -1*tilt_value  
673 - # tilt_value = dialog.ShowNumberDialog(message, value)  
674 - # image_utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value)  
675 - #elif (tilt_value) and not (gui):  
676 - # tilt_value = -1*tilt_value  
677 - # image_utils.FixGantryTilt(self.matrix, self.Slice.spacing, tilt_value)  
678 -  
679 - self.Slice.window_level = float(self.matrix.max()/2)  
680 - self.Slice.window_width = float(self.matrix.max())  
681 -  
682 - scalar_range = int(self.matrix.min()), int(self.matrix.max())  
683 - Publisher.sendMessage('Update threshold limits list', scalar_range)  
684 -  
685 - return self.matrix, self.filename#, dicom 667 + if orientation == 'AXIAL':
  668 + self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing
  669 + elif orientation == 'CORONAL':
  670 + self.Slice.spacing = xyspacing[0], zspacing, xyspacing[1]
  671 + elif orientation == 'SAGITTAL':
  672 + self.Slice.spacing = zspacing, xyspacing[1], xyspacing[0]
  673 +
  674 + self.Slice.window_level = float(self.matrix.max()/4)
  675 + self.Slice.window_width = float(self.matrix.max())
  676 +
  677 + scalar_range = int(self.matrix.min()), int(self.matrix.max())
  678 + Publisher.sendMessage('Update threshold limits list', scalar_range)
  679 +
  680 + return self.matrix, self.filename#, dicom
686 681
687 - else:  
688 - print "Error: All slices must be of the same size."  
689 682
690 def OnOpenDicomGroup(self, pubsub_evt): 683 def OnOpenDicomGroup(self, pubsub_evt):
691 group, interval, file_range = pubsub_evt.data 684 group, interval, file_range = pubsub_evt.data
invesalius/data/converters.py
@@ -22,6 +22,10 @@ import vtk @@ -22,6 +22,10 @@ import vtk
22 from vtk.util import numpy_support 22 from vtk.util import numpy_support
23 23
24 def to_vtk(n_array, spacing, slice_number, orientation): 24 def to_vtk(n_array, spacing, slice_number, orientation):
  25 +
  26 + if orientation == "SAGITTAL":
  27 + orientation = "SAGITAL"
  28 +
25 try: 29 try:
26 dz, dy, dx = n_array.shape 30 dz, dy, dx = n_array.shape
27 except ValueError: 31 except ValueError:
invesalius/data/imagedata_utils.py
@@ -430,10 +430,10 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage @@ -430,10 +430,10 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
430 430
431 if orientation == 'SAGITTAL': 431 if orientation == 'SAGITTAL':
432 if resolution_percentage == 1.0: 432 if resolution_percentage == 1.0:
433 - shape = slice_size[0], slice_size[1], len(files) 433 + shape = slice_size[1], slice_size[0], len(files)
434 else: 434 else:
435 - shape = math.ceil(slice_size[0]*resolution_percentage),\  
436 - math.ceil(slice_size[1]*resolution_percentage), len(files) 435 + shape = math.ceil(slice_size[1]*resolution_percentage),\
  436 + math.ceil(slice_size[0]*resolution_percentage), len(files)
437 437
438 elif orientation == 'CORONAL': 438 elif orientation == 'CORONAL':
439 if resolution_percentage == 1.0: 439 if resolution_percentage == 1.0:
@@ -448,11 +448,17 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage @@ -448,11 +448,17 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
448 shape = len(files), math.ceil(slice_size[1]*resolution_percentage),\ 448 shape = len(files), math.ceil(slice_size[1]*resolution_percentage),\
449 math.ceil(slice_size[0]*resolution_percentage) 449 math.ceil(slice_size[0]*resolution_percentage)
450 450
451 - matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) 451 +
  452 + if resolution_percentage == 1.0:
  453 + matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape)
  454 +
452 cont = 0 455 cont = 0
453 max_scalar = None 456 max_scalar = None
454 min_scalar = None 457 min_scalar = None
455 - 458 +
  459 + xy_shape = None
  460 + first_resample_entry = False
  461 +
456 for n, f in enumerate(files): 462 for n, f in enumerate(files):
457 image_as_array = bitmap_reader.ReadBitmap(f) 463 image_as_array = bitmap_reader.ReadBitmap(f)
458 464
@@ -461,10 +467,18 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage @@ -461,10 +467,18 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
461 467
462 if resolution_percentage != 1.0: 468 if resolution_percentage != 1.0:
463 469
464 - 470 +
465 image_resized = ResampleImage2D(image, px=None, py=None,\ 471 image_resized = ResampleImage2D(image, px=None, py=None,\
466 resolution_percentage = resolution_percentage, update_progress = None) 472 resolution_percentage = resolution_percentage, update_progress = None)
467 473
  474 + yx_shape = image_resized.GetDimensions()[1], image_resized.GetDimensions()[0]
  475 +
  476 +
  477 + if not(first_resample_entry):
  478 + shape = shape[0], yx_shape[0], yx_shape[1]
  479 + matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape)
  480 + first_resample_entry = True
  481 +
468 image = image_resized 482 image = image_resized
469 483
470 min_aux, max_aux = image.GetScalarRange() 484 min_aux, max_aux = image.GetScalarRange()
@@ -475,23 +489,25 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage @@ -475,23 +489,25 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
475 max_scalar = max_aux 489 max_scalar = max_aux
476 490
477 array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) 491 array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars())
478 - array = array.astype("int16")  
479 492
480 - array = image_as_array 493 + if array.dtype == 'uint16':
  494 + array = array - 32768/2
  495 +
  496 + array = array.astype("int16")
481 497
482 if orientation == 'CORONAL': 498 if orientation == 'CORONAL':
483 array.shape = matrix.shape[0], matrix.shape[2] 499 array.shape = matrix.shape[0], matrix.shape[2]
484 - matrix[:, n, :] = array 500 + matrix[:, n, :] = array[:,::-1]
485 elif orientation == 'SAGITTAL': 501 elif orientation == 'SAGITTAL':
486 array.shape = matrix.shape[0], matrix.shape[1] 502 array.shape = matrix.shape[0], matrix.shape[1]
487 # TODO: Verify if it's necessary to add the slices swapped only in 503 # TODO: Verify if it's necessary to add the slices swapped only in
488 # sagittal rmi or only in # Rasiane's case or is necessary in all 504 # sagittal rmi or only in # Rasiane's case or is necessary in all
489 # sagittal cases. 505 # sagittal cases.
490 - matrix[:, :, n] = array 506 + matrix[:, :, n] = array[:,::-1]
491 else: 507 else:
492 - print array.shape, matrix.shape  
493 array.shape = matrix.shape[1], matrix.shape[2] 508 array.shape = matrix.shape[1], matrix.shape[2]
494 matrix[n] = array 509 matrix[n] = array
  510 +
495 update_progress(cont,message) 511 update_progress(cont,message)
496 cont += 1 512 cont += 1
497 513
invesalius/data/styles.py
@@ -337,8 +337,8 @@ class WWWLInteractorStyle(DefaultInteractorStyle): @@ -337,8 +337,8 @@ class WWWLInteractorStyle(DefaultInteractorStyle):
337 iren = obj.GetInteractor() 337 iren = obj.GetInteractor()
338 self.last_x, self.last_y = iren.GetLastEventPosition() 338 self.last_x, self.last_y = iren.GetLastEventPosition()
339 339
340 - self.acum_achange_window = viewer.slice_.window_width  
341 - self.acum_achange_level = viewer.slice_.window_level 340 + self.acum_achange_window = self.viewer.slice_.window_width
  341 + self.acum_achange_level = self.viewer.slice_.window_level
342 342
343 343
344 class LinearMeasureInteractorStyle(DefaultInteractorStyle): 344 class LinearMeasureInteractorStyle(DefaultInteractorStyle):
invesalius/gui/bitmap_preview_panel.py
@@ -2,6 +2,7 @@ import wx @@ -2,6 +2,7 @@ import wx
2 import vtk 2 import vtk
3 import vtkgdcm 3 import vtkgdcm
4 import time 4 import time
  5 +import numpy
5 6
6 from vtk.util import numpy_support 7 from vtk.util import numpy_support
7 from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor 8 from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor
@@ -271,7 +272,6 @@ class Preview(wx.Panel): @@ -271,7 +272,6 @@ class Preview(wx.Panel):
271 if evt.m_shiftDown: 272 if evt.m_shiftDown:
272 shift_pressed = True 273 shift_pressed = True
273 274
274 - dicom_id = self.bitmap_info.id  
275 self.select_on = True 275 self.select_on = True
276 self.bitmap_info.selected = True 276 self.bitmap_info.selected = True
277 self.Select() 277 self.Select()
@@ -286,10 +286,11 @@ class Preview(wx.Panel): @@ -286,10 +286,11 @@ class Preview(wx.Panel):
286 my_evt.SetEventObject(self) 286 my_evt.SetEventObject(self)
287 self.GetEventHandler().ProcessEvent(my_evt) 287 self.GetEventHandler().ProcessEvent(my_evt)
288 288
  289 + print ">>>",self.bitmap_info.pos, self.bitmap_info.id, self.bitmap_info.data
  290 +
289 Publisher.sendMessage('Set bitmap in preview panel', self.bitmap_info.pos) 291 Publisher.sendMessage('Set bitmap in preview panel', self.bitmap_info.pos)
290 292
291 evt.Skip() 293 evt.Skip()
292 -  
293 294
294 def OnSize(self, evt): 295 def OnSize(self, evt):
295 if self.bitmap_info: 296 if self.bitmap_info:
@@ -350,6 +351,7 @@ class BitmapPreviewSeries(wx.Panel): @@ -350,6 +351,7 @@ class BitmapPreviewSeries(wx.Panel):
350 351
351 self._Add_Panels_Preview() 352 self._Add_Panels_Preview()
352 self._bind_events() 353 self._bind_events()
  354 + self._bind_pub_sub_events()
353 355
354 def _Add_Panels_Preview(self): 356 def _Add_Panels_Preview(self):
355 self.previews = [] 357 self.previews = []
@@ -371,6 +373,10 @@ class BitmapPreviewSeries(wx.Panel): @@ -371,6 +373,10 @@ class BitmapPreviewSeries(wx.Panel):
371 self.Bind(wx.EVT_SCROLL, self.OnScroll) 373 self.Bind(wx.EVT_SCROLL, self.OnScroll)
372 self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) 374 self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel)
373 375
  376 + def _bind_pub_sub_events(self):
  377 + Publisher.subscribe(self.RemovePanel, 'Remove preview panel')
  378 + #Publisher.subscribe(self.GetBmpInfoIdByOrder, 'Set bitmap in thumbnail')
  379 +
374 def OnSelect(self, evt): 380 def OnSelect(self, evt):
375 my_evt = SerieEvent(myEVT_CLICK_SERIE, self.GetId()) 381 my_evt = SerieEvent(myEVT_CLICK_SERIE, self.GetId())
376 my_evt.SetSelectedID(evt.GetSelectID()) 382 my_evt.SetSelectedID(evt.GetSelectID())
@@ -406,7 +412,64 @@ class BitmapPreviewSeries(wx.Panel): @@ -406,7 +412,64 @@ class BitmapPreviewSeries(wx.Panel):
406 self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) 412 self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS)
407 self._display_previews() 413 self._display_previews()
408 414
  415 +
  416 + def RemovePanel(self, pub_sub):
  417 + data = pub_sub.data
  418 + for p, f in zip(self.previews, self.files):
  419 + if p.bitmap_info != None:
  420 + if data.encode('utf-8') in p.bitmap_info.data:
  421 + self.files.remove(f)
  422 + p.Hide()
  423 + self._display_previews()
  424 + Publisher.sendMessage('Update max of slidebar in single preview image', len(self.files))
  425 +
  426 + self.Update()
  427 + self.Layout()
  428 +
  429 + for n, p in enumerate(self.previews):
  430 + if p.bitmap_info != None:
  431 +
  432 + if p.IsShown():
  433 + p.bitmap_info.pos = n
  434 +
  435 + #def GetBmpInfoIdByOrder(self, pub_sub):
  436 + # order = pub_sub.data
  437 + #
  438 + # for p in self.previews:
  439 + # if p.bitmap_info != None:
  440 + # if p.select_on:
  441 + # p.select_on = False
  442 + # p.selected = False
  443 +
  444 + # c = (PREVIEW_BACKGROUND)
  445 + # p.SetBackgroundColour(c)
  446 +
  447 +
  448 + # for p in self.previews:
  449 + # if p.bitmap_info != None:
  450 + # if p.bitmap_info.pos == order:
  451 +
  452 + # p.select_on = True
  453 + # p.selected = True
  454 +
  455 + # p.Select()
  456 +
  457 + # # Generating a EVT_PREVIEW_CLICK event
  458 + # my_evt = SerieEvent(myEVT_PREVIEW_CLICK, p.GetId())
  459 +
  460 + # my_evt.SetSelectedID(p.bitmap_info.id)
  461 + # my_evt.SetItemData(p.bitmap_info.data)
  462 +
  463 + # my_evt.SetShiftStatus(False)
  464 + # my_evt.SetEventObject(p)
  465 + # self.GetEventHandler().ProcessEvent(my_evt)
  466 +
409 467
  468 + # #c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE)
  469 + # #p.SetBackgroundColour(c)
  470 +
  471 + #return p.id
  472 +
410 #def SetPatientGroups(self, patient): 473 #def SetPatientGroups(self, patient):
411 # self.files = [] 474 # self.files = []
412 # self.displayed_position = 0 475 # self.displayed_position = 0
@@ -576,10 +639,13 @@ class SingleImagePreview(wx.Panel): @@ -576,10 +639,13 @@ class SingleImagePreview(wx.Panel):
576 639
577 def __bind_pubsub(self): 640 def __bind_pubsub(self):
578 Publisher.subscribe(self.ShowBitmapByPosition, 'Set bitmap in preview panel') 641 Publisher.subscribe(self.ShowBitmapByPosition, 'Set bitmap in preview panel')
  642 + Publisher.subscribe(self.UpdateMaxValueSliderBar, 'Update max of slidebar in single preview image')
  643 + Publisher.subscribe(self.ShowBlackSlice, 'Show black slice in single preview image')
579 644
580 def ShowBitmapByPosition(self, evt): 645 def ShowBitmapByPosition(self, evt):
581 - pos = evt.data  
582 - self.ShowSlice(pos) 646 + pos = evt.data
  647 + if pos != None:
  648 + self.ShowSlice(int(pos))
583 649
584 650
585 def OnSlider(self, evt): 651 def OnSlider(self, evt):
@@ -621,6 +687,36 @@ class SingleImagePreview(wx.Panel): @@ -621,6 +687,36 @@ class SingleImagePreview(wx.Panel):
621 self.slider.SetValue(0) 687 self.slider.SetValue(0)
622 self.ShowSlice() 688 self.ShowSlice()
623 689
  690 + def UpdateMaxValueSliderBar(self, pub_sub):
  691 + self.slider.SetMax(pub_sub.data - 1)
  692 + self.slider.Refresh()
  693 + self.slider.Update()
  694 +
  695 + def ShowBlackSlice(self, pub_sub):
  696 + n_array = numpy.zeros((100,100))
  697 +
  698 + self.text_image_size.SetValue('')
  699 +
  700 + image = converters.to_vtk(n_array, spacing=(1,1,1),\
  701 + slice_number=1, orientation="AXIAL")
  702 +
  703 + colorer = vtk.vtkImageMapToWindowLevelColors()
  704 + colorer.SetInputData(image)
  705 + colorer.Update()
  706 +
  707 + if self.actor is None:
  708 + self.actor = vtk.vtkImageActor()
  709 + self.renderer.AddActor(self.actor)
  710 +
  711 + # PLOT IMAGE INTO VIEWER
  712 + self.actor.SetInputData(colorer.GetOutput())
  713 + self.renderer.ResetCamera()
  714 + self.interactor.Render()
  715 +
  716 + # Setting slider position
  717 + self.slider.SetValue(0)
  718 +
  719 +
624 def ShowSlice(self, index = 0): 720 def ShowSlice(self, index = 0):
625 bitmap = self.bitmap_list[index] 721 bitmap = self.bitmap_list[index]
626 722
invesalius/gui/dialogs.py
@@ -510,8 +510,12 @@ def ImportEmptyDirectory(dirpath): @@ -510,8 +510,12 @@ def ImportEmptyDirectory(dirpath):
510 dlg.ShowModal() 510 dlg.ShowModal()
511 dlg.Destroy() 511 dlg.Destroy()
512 512
513 -def ImportInvalidFiles():  
514 - msg = _("There are no DICOM files in the selected folder.") 513 +def ImportInvalidFiles(ftype="DICOM"):
  514 + if ftype == "Bitmap":
  515 + msg = _("There are no Bitmap, JPEG, PNG or TIFF files in the selected folder.")
  516 + else:
  517 + msg = _("There are no DICOM files in the selected folder.")
  518 +
515 if sys.platform == 'darwin': 519 if sys.platform == 'darwin':
516 dlg = wx.MessageDialog(None, "", msg, 520 dlg = wx.MessageDialog(None, "", msg,
517 wx.ICON_INFORMATION | wx.OK) 521 wx.ICON_INFORMATION | wx.OK)
@@ -1700,6 +1704,7 @@ class ImportBitmapParameters(wx.Dialog): @@ -1700,6 +1704,7 @@ class ImportBitmapParameters(wx.Dialog):
1700 1704
1701 def _init_gui(self): 1705 def _init_gui(self):
1702 1706
  1707 + import project as prj
1703 1708
1704 p = wx.Panel(self, -1, style = wx.TAB_TRAVERSAL 1709 p = wx.Panel(self, -1, style = wx.TAB_TRAVERSAL
1705 | wx.CLIP_CHILDREN 1710 | wx.CLIP_CHILDREN
@@ -1732,15 +1737,30 @@ class ImportBitmapParameters(wx.Dialog): @@ -1732,15 +1737,30 @@ class ImportBitmapParameters(wx.Dialog):
1732 1737
1733 stx_spacing_x = stx_spacing_x = wx.StaticText(p, -1, _(u"X:")) 1738 stx_spacing_x = stx_spacing_x = wx.StaticText(p, -1, _(u"X:"))
1734 fsp_spacing_x = self.fsp_spacing_x = FS.FloatSpin(p, -1, min_val=0,\ 1739 fsp_spacing_x = self.fsp_spacing_x = FS.FloatSpin(p, -1, min_val=0,\
1735 - increment=0.25, value=1.0, digits=6) 1740 + increment=0.25, value=1.0, digits=8)
1736 1741
1737 stx_spacing_y = stx_spacing_y = wx.StaticText(p, -1, _(u"Y:")) 1742 stx_spacing_y = stx_spacing_y = wx.StaticText(p, -1, _(u"Y:"))
1738 fsp_spacing_y = self.fsp_spacing_y = FS.FloatSpin(p, -1, min_val=0,\ 1743 fsp_spacing_y = self.fsp_spacing_y = FS.FloatSpin(p, -1, min_val=0,\
1739 - increment=0.25, value=1.0, digits=6) 1744 + increment=0.25, value=1.0, digits=8)
1740 1745
1741 stx_spacing_z = stx_spacing_z = wx.StaticText(p, -1, _(u"Z:")) 1746 stx_spacing_z = stx_spacing_z = wx.StaticText(p, -1, _(u"Z:"))
1742 fsp_spacing_z = self.fsp_spacing_z = FS.FloatSpin(p, -1, min_val=0,\ 1747 fsp_spacing_z = self.fsp_spacing_z = FS.FloatSpin(p, -1, min_val=0,\
1743 - increment=0.25, value=1.0, digits=6) 1748 + increment=0.25, value=1.0, digits=8)
  1749 +
  1750 +
  1751 + try:
  1752 + proj = prj.Project()
  1753 +
  1754 + sx = proj.spacing[0]
  1755 + sy = proj.spacing[1]
  1756 + sz = proj.spacing[2]
  1757 +
  1758 + fsp_spacing_x.SetValue(sx)
  1759 + fsp_spacing_y.SetValue(sy)
  1760 + fsp_spacing_z.SetValue(sz)
  1761 +
  1762 + except(AttributeError):
  1763 + pass
1744 1764
1745 gbs_spacing.Add(stx_spacing_x, (0,0)) 1765 gbs_spacing.Add(stx_spacing_x, (0,0))
1746 gbs_spacing.Add(fsp_spacing_x, (0,1)) 1766 gbs_spacing.Add(fsp_spacing_x, (0,1))
@@ -1783,9 +1803,27 @@ class ImportBitmapParameters(wx.Dialog): @@ -1783,9 +1803,27 @@ class ImportBitmapParameters(wx.Dialog):
1783 self.Close() 1803 self.Close()
1784 self.Destroy() 1804 self.Destroy()
1785 1805
1786 - values = [self.tx_name.GetValue(), self.cb_orientation.GetValue().upper(),\ 1806 + orient_selection = self.cb_orientation.GetSelection()
  1807 +
  1808 + if(orient_selection == 1):
  1809 + orientation = u"CORONAL"
  1810 + elif(orient_selection == 2):
  1811 + orientation = u"SAGITTAL"
  1812 + else:
  1813 + orientation = u"AXIAL"
  1814 +
  1815 + values = [self.tx_name.GetValue(), orientation,\
1787 self.fsp_spacing_x.GetValue(), self.fsp_spacing_y.GetValue(),\ 1816 self.fsp_spacing_x.GetValue(), self.fsp_spacing_y.GetValue(),\
1788 self.fsp_spacing_z.GetValue(), self.interval] 1817 self.fsp_spacing_z.GetValue(), self.interval]
1789 Publisher.sendMessage('Open bitmap files', values) 1818 Publisher.sendMessage('Open bitmap files', values)
1790 1819
1791 1820
  1821 +def BitmapNotSameSize():
  1822 +
  1823 + dlg = wx.MessageDialog(None,_("All bitmaps files must be the same width and height size"), 'Error',\
  1824 + wx.OK | wx.ICON_ERROR
  1825 + #wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION
  1826 + )
  1827 +
  1828 + dlg.ShowModal()
  1829 + dlg.Destroy()
invesalius/gui/frame.py
@@ -612,7 +612,7 @@ class MenuBar(wx.MenuBar): @@ -612,7 +612,7 @@ class MenuBar(wx.MenuBar):
612 #Import Others Files 612 #Import Others Files
613 others_file_menu = wx.Menu() 613 others_file_menu = wx.Menu()
614 others_file_menu.Append(const.ID_ANALYZE_IMPORT, "Analyze") 614 others_file_menu.Append(const.ID_ANALYZE_IMPORT, "Analyze")
615 - others_file_menu.Append(const.ID_TIFF_JPG_PNG, "TIFF,BMP,JPG or PNG") 615 + others_file_menu.Append(const.ID_TIFF_JPG_PNG, u"TIFF,BMP,JPG or PNG (\xb5CT)")
616 616
617 # FILE 617 # FILE
618 file_menu = wx.Menu() 618 file_menu = wx.Menu()
invesalius/gui/import_bitmap_panel.py
@@ -24,7 +24,7 @@ import wx.lib.splitter as spl @@ -24,7 +24,7 @@ import wx.lib.splitter as spl
24 import constants as const 24 import constants as const
25 import gui.dialogs as dlg 25 import gui.dialogs as dlg
26 import bitmap_preview_panel as bpp 26 import bitmap_preview_panel as bpp
27 -import reader.dicom_grouper as dcm 27 +import reader.bitmap_reader as bpr
28 from dialogs import ImportBitmapParameters 28 from dialogs import ImportBitmapParameters
29 29
30 myEVT_SELECT_SERIE = wx.NewEventType() 30 myEVT_SELECT_SERIE = wx.NewEventType()
@@ -207,10 +207,14 @@ class TextPanel(wx.Panel): @@ -207,10 +207,14 @@ class TextPanel(wx.Panel):
207 def __init__(self, parent): 207 def __init__(self, parent):
208 wx.Panel.__init__(self, parent, -1) 208 wx.Panel.__init__(self, parent, -1)
209 209
  210 + self.parent = parent
  211 +
210 self._selected_by_user = True 212 self._selected_by_user = True
211 self.idserie_treeitem = {} 213 self.idserie_treeitem = {}
212 self.treeitem_idpatient = {} 214 self.treeitem_idpatient = {}
213 215
  216 + self.selected_item = None
  217 +
214 self.__init_gui() 218 self.__init_gui()
215 self.__bind_events_wx() 219 self.__bind_events_wx()
216 self.__bind_pubsub_evt() 220 self.__bind_pubsub_evt()
@@ -220,6 +224,7 @@ class TextPanel(wx.Panel): @@ -220,6 +224,7 @@ class TextPanel(wx.Panel):
220 224
221 def __bind_events_wx(self): 225 def __bind_events_wx(self):
222 self.Bind(wx.EVT_SIZE, self.OnSize) 226 self.Bind(wx.EVT_SIZE, self.OnSize)
  227 + self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyPress)
223 228
224 def __init_gui(self): 229 def __init_gui(self):
225 tree = gizmos.TreeListCtrl(self, -1, style = 230 tree = gizmos.TreeListCtrl(self, -1, style =
@@ -237,7 +242,6 @@ class TextPanel(wx.Panel): @@ -237,7 +242,6 @@ class TextPanel(wx.Panel):
237 tree.AddColumn(_("Type")) 242 tree.AddColumn(_("Type"))
238 tree.AddColumn(_("Width x Height")) 243 tree.AddColumn(_("Width x Height"))
239 244
240 -  
241 tree.SetMainColumn(0) 245 tree.SetMainColumn(0)
242 tree.SetColumnWidth(0, 880) 246 tree.SetColumnWidth(0, 880)
243 tree.SetColumnWidth(1, 60) 247 tree.SetColumnWidth(1, 60)
@@ -246,6 +250,33 @@ class TextPanel(wx.Panel): @@ -246,6 +250,33 @@ class TextPanel(wx.Panel):
246 self.root = tree.AddRoot(_("InVesalius Database")) 250 self.root = tree.AddRoot(_("InVesalius Database"))
247 self.tree = tree 251 self.tree = tree
248 252
  253 + def OnKeyPress(self, evt):
  254 + key_code = evt.GetKeyCode()
  255 + if key_code == wx.WXK_DELETE or key_code == wx.WXK_NUMPAD_DELETE:
  256 + if self.selected_item != self.tree.GetRootItem():
  257 + text_item = self.tree.GetItemText(self.selected_item)
  258 +
  259 + index = bpr.BitmapData().GetIndexByPath(text_item)
  260 +
  261 + bpr.BitmapData().RemoveFileByPath(text_item)
  262 +
  263 + data_size = len(bpr.BitmapData().GetData())
  264 +
  265 + if index >= 0 and index < data_size:
  266 + Publisher.sendMessage('Set bitmap in preview panel', index)
  267 + elif index == data_size and data_size > 0:
  268 + Publisher.sendMessage('Set bitmap in preview panel', index - 1)
  269 + elif data_size == 1:
  270 + Publisher.sendMessage('Set bitmap in preview panel', 0)
  271 + else:
  272 + Publisher.sendMessage('Show black slice in single preview image')
  273 +
  274 + self.tree.Delete(self.selected_item)
  275 + self.tree.Update()
  276 + self.tree.Refresh()
  277 + Publisher.sendMessage('Remove preview panel', text_item)
  278 + evt.Skip()
  279 +
249 def SelectSeries(self, pubsub_evt): 280 def SelectSeries(self, pubsub_evt):
250 group_index = pubsub_evt.data 281 group_index = pubsub_evt.data
251 282
@@ -263,27 +294,15 @@ class TextPanel(wx.Panel): @@ -263,27 +294,15 @@ class TextPanel(wx.Panel):
263 294
264 Publisher.sendMessage('Load bitmap into import panel', data) 295 Publisher.sendMessage('Load bitmap into import panel', data)
265 296
266 -  
267 def OnSelChanged(self, evt): 297 def OnSelChanged(self, evt):
268 item = self.tree.GetSelection() 298 item = self.tree.GetSelection()
269 if self._selected_by_user: 299 if self._selected_by_user:
270 - group = self.tree.GetItemPyData(item)  
271 - if isinstance(group, dcm.DicomGroup):  
272 - Publisher.sendMessage('Load group into import panel',  
273 - group)  
274 -  
275 - elif isinstance(group, dcm.PatientGroup):  
276 - id = group.GetDicomSample().patient.id  
277 - my_evt = SelectEvent(myEVT_SELECT_PATIENT, self.GetId())  
278 - my_evt.SetSelectedID(id)  
279 - self.GetEventHandler().ProcessEvent(my_evt)  
280 -  
281 - Publisher.sendMessage('Load bitmap into import panel',  
282 -  
283 - group)  
284 - else:  
285 - parent_id = self.tree.GetItemParent(item)  
286 - self.tree.Expand(parent_id) 300 + self.selected_item = item
  301 +
  302 + text_item = self.tree.GetItemText(self.selected_item)
  303 + index = bpr.BitmapData().GetIndexByPath(text_item)
  304 + Publisher.sendMessage('Set bitmap in preview panel', index)
  305 +
287 evt.Skip() 306 evt.Skip()
288 307
289 def OnActivate(self, evt): 308 def OnActivate(self, evt):
@@ -295,6 +314,7 @@ class TextPanel(wx.Panel): @@ -295,6 +314,7 @@ class TextPanel(wx.Panel):
295 314
296 def OnSize(self, evt): 315 def OnSize(self, evt):
297 self.tree.SetSize(self.GetSize()) 316 self.tree.SetSize(self.GetSize())
  317 + evt.Skip()
298 318
299 def SelectSerie(self, serie): 319 def SelectSerie(self, serie):
300 self._selected_by_user = False 320 self._selected_by_user = False
@@ -412,7 +432,6 @@ class SeriesPanel(wx.Panel): @@ -412,7 +432,6 @@ class SeriesPanel(wx.Panel):
412 432
413 def OnSelectSerie(self, evt): 433 def OnSelectSerie(self, evt):
414 data = evt.GetItemData() 434 data = evt.GetItemData()
415 -  
416 my_evt = SelectEvent(myEVT_SELECT_SERIE, self.GetId()) 435 my_evt = SelectEvent(myEVT_SELECT_SERIE, self.GetId())
417 my_evt.SetSelectedID(evt.GetSelectID()) 436 my_evt.SetSelectedID(evt.GetSelectID())
418 my_evt.SetItemData(evt.GetItemData()) 437 my_evt.SetItemData(evt.GetItemData())
invesalius/reader/bitmap_reader.py
@@ -34,6 +34,7 @@ from scipy import misc @@ -34,6 +34,7 @@ from scipy import misc
34 import numpy 34 import numpy
35 import imghdr 35 import imghdr
36 36
  37 +import utils
37 from data import converters 38 from data import converters
38 39
39 #flag to control vtk error in read files 40 #flag to control vtk error in read files
@@ -89,7 +90,15 @@ class BitmapData: @@ -89,7 +90,15 @@ class BitmapData:
89 90
90 return size 91 return size
91 92
  93 + def RemoveFileByPath(self, path):
  94 + for d in self.data:
  95 + if path.encode('utf-8') in d:
  96 + self.data.remove(d)
92 97
  98 + def GetIndexByPath(self, path):
  99 + for i, v in enumerate(self.data):
  100 + if path.encode('utf-8') in v:
  101 + return i
93 102
94 class BitmapFiles: 103 class BitmapFiles:
95 104
@@ -100,13 +109,11 @@ class BitmapFiles: @@ -100,13 +109,11 @@ class BitmapFiles:
100 self.bitmapfiles.append(bmp) 109 self.bitmapfiles.append(bmp)
101 110
102 def Sort(self, x): 111 def Sort(self, x):
103 -  
104 c_re = re.compile('\d+') 112 c_re = re.compile('\d+')
105 -  
106 - if len(c_re.findall(x[0])) > 0:  
107 - return c_re.findall(x[0])[-1] 113 + if len(c_re.findall(x[6])) > 0:
  114 + return [int(i) for i in c_re.findall(x[6])]
108 else: 115 else:
109 - return '0' 116 + return [str(x[6])]
110 117
111 def GetValues(self): 118 def GetValues(self):
112 bmpfile = self.bitmapfiles 119 bmpfile = self.bitmapfiles
@@ -133,6 +140,7 @@ class LoadBitmap: @@ -133,6 +140,7 @@ class LoadBitmap:
133 140
134 #----- verify extension ------------------ 141 #----- verify extension ------------------
135 #ex = self.filepath.split('.')[-1] 142 #ex = self.filepath.split('.')[-1]
  143 +
136 extension = VerifyDataType(self.filepath) 144 extension = VerifyDataType(self.filepath)
137 145
138 file_name = self.filepath.split(os.path.sep)[-1] 146 file_name = self.filepath.split(os.path.sep)[-1]
@@ -297,7 +305,6 @@ def ScipyRead(filepath): @@ -297,7 +305,6 @@ def ScipyRead(filepath):
297 try: 305 try:
298 r = misc.imread(filepath, flatten=True) 306 r = misc.imread(filepath, flatten=True)
299 dt = r.dtype 307 dt = r.dtype
300 -  
301 if dt == "float" or dt == "float16"\ 308 if dt == "float" or dt == "float16"\
302 or dt == "float32" or dt == "float64": 309 or dt == "float32" or dt == "float64":
303 shift=-r.max()/2 310 shift=-r.max()/2
@@ -355,10 +362,14 @@ def VtkRead(filepath, t): @@ -355,10 +362,14 @@ def VtkRead(filepath, t):
355 362
356 363
357 def ReadBitmap(filepath): 364 def ReadBitmap(filepath):
358 -  
359 t = VerifyDataType(filepath) 365 t = VerifyDataType(filepath)
360 366
361 if t == False: 367 if t == False:
  368 + measures_info = GetPixelSpacingFromInfoFile(filepath)
  369 +
  370 + if measures_info:
  371 + Publisher.sendMessage('Set bitmap spacing', measures_info)
  372 +
362 return False 373 return False
363 374
364 img_array = VtkRead(filepath, t) 375 img_array = VtkRead(filepath, t)
@@ -373,7 +384,54 @@ def ReadBitmap(filepath): @@ -373,7 +384,54 @@ def ReadBitmap(filepath):
373 return False 384 return False
374 385
375 return img_array 386 return img_array
376 - 387 +
  388 +
  389 +def GetPixelSpacingFromInfoFile(filepath):
  390 +
  391 + fi = open(filepath, 'r')
  392 + lines = fi.readlines()
  393 + measure_scale = 'mm'
  394 + values = []
  395 +
  396 + if len(lines) > 0:
  397 + #info text from avizo
  398 + if '# Avizo Stacked Slices' in lines[0]:
  399 + value = lines[2].split(' ')
  400 + spx = float(value[1])
  401 + spy = float(value[2])
  402 + value = lines[5].split(' ')
  403 + spz = float(value[1])
  404 +
  405 + return [spx * 0.001, spy * 0.001, spz * 0.001]
  406 + else:
  407 + #info text from skyscan
  408 + for l in lines:
  409 + if 'Pixel Size' in l:
  410 + if 'um' in l:
  411 + measure_scale = 'um'
  412 +
  413 + value = l.split("=")[-1]
  414 + values.append(value)
  415 +
  416 + if len(values) > 0:
  417 + value = values[-1]
  418 +
  419 + value = value.replace('\n','')
  420 + value = value.replace('\r','')
  421 +
  422 + #convert um to mm (InVesalius default)
  423 + if measure_scale == 'um':
  424 + value = float(value) * 0.001
  425 + measure_scale = 'mm'
  426 +
  427 + elif measure_scale == 'nm':
  428 + value = float(value) * 0.000001
  429 +
  430 + return [value, value, value]
  431 + else:
  432 + return False
  433 + else:
  434 + return False
377 435
378 def VtkErrorToPy(obj, evt): 436 def VtkErrorToPy(obj, evt):
379 global no_error 437 global no_error
@@ -383,7 +441,10 @@ def VtkErrorToPy(obj, evt): @@ -383,7 +441,10 @@ def VtkErrorToPy(obj, evt):
383 def VerifyDataType(filepath): 441 def VerifyDataType(filepath):
384 try: 442 try:
385 t = imghdr.what(filepath) 443 t = imghdr.what(filepath)
386 - return t 444 + if t:
  445 + return t
  446 + else:
  447 + return False
387 except IOError: 448 except IOError:
388 return False 449 return False
389 450