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 95  
96 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 104 def OnCancelImport(self, pubsub_evt):
100 105 #self.cancel_import = True
... ... @@ -381,10 +386,9 @@ class Controller():
381 386 #Publisher.sendMessage("Load bitmap preview", first_patient)
382 387 if data:
383 388 Publisher.sendMessage("Load import bitmap panel", data)
384   -
385 389 return True
386 390 else:
387   - dialog.ImportInvalidFiles()
  391 + dialog.ImportInvalidFiles("Bitmap")
388 392 return False
389 393  
390 394  
... ... @@ -395,7 +399,7 @@ class Controller():
395 399 Publisher.sendMessage("Load dicom preview", first_patient)
396 400 return True
397 401 else:
398   - dialog.ImportInvalidFiles()
  402 + dialog.ImportInvalidFiles("DICOM")
399 403 return False
400 404  
401 405  
... ... @@ -580,10 +584,11 @@ class Controller():
580 584 proj.matrix_filename = matrix_filename
581 585 #proj.imagedata = imagedata
582 586 #proj.dicom_sample = dicom
  587 +
583 588 proj.original_orientation =\
584 589 name_to_const[orientation.upper()]
585 590 proj.window = float(matrix.max())
586   - proj.level = float(matrix.max()/2)
  591 + proj.level = float(matrix.max()/4)
587 592  
588 593 proj.threshold_range = int(matrix.min()), int(matrix.max())
589 594 #const.THRESHOLD_RANGE = proj.threshold_range
... ... @@ -601,91 +606,79 @@ class Controller():
601 606 def OnOpenBitmapFiles(self, pubsub_evt):
602 607 rec_data = pubsub_evt.data
603 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 683 def OnOpenDicomGroup(self, pubsub_evt):
691 684 group, interval, file_range = pubsub_evt.data
... ...
invesalius/data/converters.py
... ... @@ -22,6 +22,10 @@ import vtk
22 22 from vtk.util import numpy_support
23 23  
24 24 def to_vtk(n_array, spacing, slice_number, orientation):
  25 +
  26 + if orientation == "SAGITTAL":
  27 + orientation = "SAGITAL"
  28 +
25 29 try:
26 30 dz, dy, dx = n_array.shape
27 31 except ValueError:
... ...
invesalius/data/imagedata_utils.py
... ... @@ -430,10 +430,10 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
430 430  
431 431 if orientation == 'SAGITTAL':
432 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 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 438 elif orientation == 'CORONAL':
439 439 if resolution_percentage == 1.0:
... ... @@ -448,11 +448,17 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
448 448 shape = len(files), math.ceil(slice_size[1]*resolution_percentage),\
449 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 455 cont = 0
453 456 max_scalar = None
454 457 min_scalar = None
455   -
  458 +
  459 + xy_shape = None
  460 + first_resample_entry = False
  461 +
456 462 for n, f in enumerate(files):
457 463 image_as_array = bitmap_reader.ReadBitmap(f)
458 464  
... ... @@ -461,10 +467,18 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
461 467  
462 468 if resolution_percentage != 1.0:
463 469  
464   -
  470 +
465 471 image_resized = ResampleImage2D(image, px=None, py=None,\
466 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 482 image = image_resized
469 483  
470 484 min_aux, max_aux = image.GetScalarRange()
... ... @@ -475,23 +489,25 @@ def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage
475 489 max_scalar = max_aux
476 490  
477 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 498 if orientation == 'CORONAL':
483 499 array.shape = matrix.shape[0], matrix.shape[2]
484   - matrix[:, n, :] = array
  500 + matrix[:, n, :] = array[:,::-1]
485 501 elif orientation == 'SAGITTAL':
486 502 array.shape = matrix.shape[0], matrix.shape[1]
487 503 # TODO: Verify if it's necessary to add the slices swapped only in
488 504 # sagittal rmi or only in # Rasiane's case or is necessary in all
489 505 # sagittal cases.
490   - matrix[:, :, n] = array
  506 + matrix[:, :, n] = array[:,::-1]
491 507 else:
492   - print array.shape, matrix.shape
493 508 array.shape = matrix.shape[1], matrix.shape[2]
494 509 matrix[n] = array
  510 +
495 511 update_progress(cont,message)
496 512 cont += 1
497 513  
... ...
invesalius/data/styles.py
... ... @@ -337,8 +337,8 @@ class WWWLInteractorStyle(DefaultInteractorStyle):
337 337 iren = obj.GetInteractor()
338 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 344 class LinearMeasureInteractorStyle(DefaultInteractorStyle):
... ...
invesalius/gui/bitmap_preview_panel.py
... ... @@ -2,6 +2,7 @@ import wx
2 2 import vtk
3 3 import vtkgdcm
4 4 import time
  5 +import numpy
5 6  
6 7 from vtk.util import numpy_support
7 8 from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor
... ... @@ -271,7 +272,6 @@ class Preview(wx.Panel):
271 272 if evt.m_shiftDown:
272 273 shift_pressed = True
273 274  
274   - dicom_id = self.bitmap_info.id
275 275 self.select_on = True
276 276 self.bitmap_info.selected = True
277 277 self.Select()
... ... @@ -286,10 +286,11 @@ class Preview(wx.Panel):
286 286 my_evt.SetEventObject(self)
287 287 self.GetEventHandler().ProcessEvent(my_evt)
288 288  
  289 + print ">>>",self.bitmap_info.pos, self.bitmap_info.id, self.bitmap_info.data
  290 +
289 291 Publisher.sendMessage('Set bitmap in preview panel', self.bitmap_info.pos)
290 292  
291 293 evt.Skip()
292   -
293 294  
294 295 def OnSize(self, evt):
295 296 if self.bitmap_info:
... ... @@ -350,6 +351,7 @@ class BitmapPreviewSeries(wx.Panel):
350 351  
351 352 self._Add_Panels_Preview()
352 353 self._bind_events()
  354 + self._bind_pub_sub_events()
353 355  
354 356 def _Add_Panels_Preview(self):
355 357 self.previews = []
... ... @@ -371,6 +373,10 @@ class BitmapPreviewSeries(wx.Panel):
371 373 self.Bind(wx.EVT_SCROLL, self.OnScroll)
372 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 380 def OnSelect(self, evt):
375 381 my_evt = SerieEvent(myEVT_CLICK_SERIE, self.GetId())
376 382 my_evt.SetSelectedID(evt.GetSelectID())
... ... @@ -406,7 +412,64 @@ class BitmapPreviewSeries(wx.Panel):
406 412 self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS)
407 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 473 #def SetPatientGroups(self, patient):
411 474 # self.files = []
412 475 # self.displayed_position = 0
... ... @@ -576,10 +639,13 @@ class SingleImagePreview(wx.Panel):
576 639  
577 640 def __bind_pubsub(self):
578 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 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 651 def OnSlider(self, evt):
... ... @@ -621,6 +687,36 @@ class SingleImagePreview(wx.Panel):
621 687 self.slider.SetValue(0)
622 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 720 def ShowSlice(self, index = 0):
625 721 bitmap = self.bitmap_list[index]
626 722  
... ...
invesalius/gui/dialogs.py
... ... @@ -510,8 +510,12 @@ def ImportEmptyDirectory(dirpath):
510 510 dlg.ShowModal()
511 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 519 if sys.platform == 'darwin':
516 520 dlg = wx.MessageDialog(None, "", msg,
517 521 wx.ICON_INFORMATION | wx.OK)
... ... @@ -1700,6 +1704,7 @@ class ImportBitmapParameters(wx.Dialog):
1700 1704  
1701 1705 def _init_gui(self):
1702 1706  
  1707 + import project as prj
1703 1708  
1704 1709 p = wx.Panel(self, -1, style = wx.TAB_TRAVERSAL
1705 1710 | wx.CLIP_CHILDREN
... ... @@ -1732,15 +1737,30 @@ class ImportBitmapParameters(wx.Dialog):
1732 1737  
1733 1738 stx_spacing_x = stx_spacing_x = wx.StaticText(p, -1, _(u"X:"))
1734 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 1742 stx_spacing_y = stx_spacing_y = wx.StaticText(p, -1, _(u"Y:"))
1738 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 1746 stx_spacing_z = stx_spacing_z = wx.StaticText(p, -1, _(u"Z:"))
1742 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 1765 gbs_spacing.Add(stx_spacing_x, (0,0))
1746 1766 gbs_spacing.Add(fsp_spacing_x, (0,1))
... ... @@ -1783,9 +1803,27 @@ class ImportBitmapParameters(wx.Dialog):
1783 1803 self.Close()
1784 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 1816 self.fsp_spacing_x.GetValue(), self.fsp_spacing_y.GetValue(),\
1788 1817 self.fsp_spacing_z.GetValue(), self.interval]
1789 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 612 #Import Others Files
613 613 others_file_menu = wx.Menu()
614 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 617 # FILE
618 618 file_menu = wx.Menu()
... ...
invesalius/gui/import_bitmap_panel.py
... ... @@ -24,7 +24,7 @@ import wx.lib.splitter as spl
24 24 import constants as const
25 25 import gui.dialogs as dlg
26 26 import bitmap_preview_panel as bpp
27   -import reader.dicom_grouper as dcm
  27 +import reader.bitmap_reader as bpr
28 28 from dialogs import ImportBitmapParameters
29 29  
30 30 myEVT_SELECT_SERIE = wx.NewEventType()
... ... @@ -207,10 +207,14 @@ class TextPanel(wx.Panel):
207 207 def __init__(self, parent):
208 208 wx.Panel.__init__(self, parent, -1)
209 209  
  210 + self.parent = parent
  211 +
210 212 self._selected_by_user = True
211 213 self.idserie_treeitem = {}
212 214 self.treeitem_idpatient = {}
213 215  
  216 + self.selected_item = None
  217 +
214 218 self.__init_gui()
215 219 self.__bind_events_wx()
216 220 self.__bind_pubsub_evt()
... ... @@ -220,6 +224,7 @@ class TextPanel(wx.Panel):
220 224  
221 225 def __bind_events_wx(self):
222 226 self.Bind(wx.EVT_SIZE, self.OnSize)
  227 + self.Bind(wx.EVT_CHAR_HOOK, self.OnKeyPress)
223 228  
224 229 def __init_gui(self):
225 230 tree = gizmos.TreeListCtrl(self, -1, style =
... ... @@ -237,7 +242,6 @@ class TextPanel(wx.Panel):
237 242 tree.AddColumn(_("Type"))
238 243 tree.AddColumn(_("Width x Height"))
239 244  
240   -
241 245 tree.SetMainColumn(0)
242 246 tree.SetColumnWidth(0, 880)
243 247 tree.SetColumnWidth(1, 60)
... ... @@ -246,6 +250,33 @@ class TextPanel(wx.Panel):
246 250 self.root = tree.AddRoot(_("InVesalius Database"))
247 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 280 def SelectSeries(self, pubsub_evt):
250 281 group_index = pubsub_evt.data
251 282  
... ... @@ -263,27 +294,15 @@ class TextPanel(wx.Panel):
263 294  
264 295 Publisher.sendMessage('Load bitmap into import panel', data)
265 296  
266   -
267 297 def OnSelChanged(self, evt):
268 298 item = self.tree.GetSelection()
269 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 306 evt.Skip()
288 307  
289 308 def OnActivate(self, evt):
... ... @@ -295,6 +314,7 @@ class TextPanel(wx.Panel):
295 314  
296 315 def OnSize(self, evt):
297 316 self.tree.SetSize(self.GetSize())
  317 + evt.Skip()
298 318  
299 319 def SelectSerie(self, serie):
300 320 self._selected_by_user = False
... ... @@ -412,7 +432,6 @@ class SeriesPanel(wx.Panel):
412 432  
413 433 def OnSelectSerie(self, evt):
414 434 data = evt.GetItemData()
415   -
416 435 my_evt = SelectEvent(myEVT_SELECT_SERIE, self.GetId())
417 436 my_evt.SetSelectedID(evt.GetSelectID())
418 437 my_evt.SetItemData(evt.GetItemData())
... ...
invesalius/reader/bitmap_reader.py
... ... @@ -34,6 +34,7 @@ from scipy import misc
34 34 import numpy
35 35 import imghdr
36 36  
  37 +import utils
37 38 from data import converters
38 39  
39 40 #flag to control vtk error in read files
... ... @@ -89,7 +90,15 @@ class BitmapData:
89 90  
90 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 103 class BitmapFiles:
95 104  
... ... @@ -100,13 +109,11 @@ class BitmapFiles:
100 109 self.bitmapfiles.append(bmp)
101 110  
102 111 def Sort(self, x):
103   -
104 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 115 else:
109   - return '0'
  116 + return [str(x[6])]
110 117  
111 118 def GetValues(self):
112 119 bmpfile = self.bitmapfiles
... ... @@ -133,6 +140,7 @@ class LoadBitmap:
133 140  
134 141 #----- verify extension ------------------
135 142 #ex = self.filepath.split('.')[-1]
  143 +
136 144 extension = VerifyDataType(self.filepath)
137 145  
138 146 file_name = self.filepath.split(os.path.sep)[-1]
... ... @@ -297,7 +305,6 @@ def ScipyRead(filepath):
297 305 try:
298 306 r = misc.imread(filepath, flatten=True)
299 307 dt = r.dtype
300   -
301 308 if dt == "float" or dt == "float16"\
302 309 or dt == "float32" or dt == "float64":
303 310 shift=-r.max()/2
... ... @@ -355,10 +362,14 @@ def VtkRead(filepath, t):
355 362  
356 363  
357 364 def ReadBitmap(filepath):
358   -
359 365 t = VerifyDataType(filepath)
360 366  
361 367 if t == False:
  368 + measures_info = GetPixelSpacingFromInfoFile(filepath)
  369 +
  370 + if measures_info:
  371 + Publisher.sendMessage('Set bitmap spacing', measures_info)
  372 +
362 373 return False
363 374  
364 375 img_array = VtkRead(filepath, t)
... ... @@ -373,7 +384,54 @@ def ReadBitmap(filepath):
373 384 return False
374 385  
375 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 436 def VtkErrorToPy(obj, evt):
379 437 global no_error
... ... @@ -383,7 +441,10 @@ def VtkErrorToPy(obj, evt):
383 441 def VerifyDataType(filepath):
384 442 try:
385 443 t = imghdr.what(filepath)
386   - return t
  444 + if t:
  445 + return t
  446 + else:
  447 + return False
387 448 except IOError:
388 449 return False
389 450  
... ...