Commit afdb3ce1763e0e37fb385d31236baefa4de23fcd
Committed by
GitHub
1 parent
9a7e323e
Exists in
master
Open Multiframe dicom (#133)
* Loading multiframe dicom using the import gui * Showing multiframe thumbnails * Showing the thumbnail in the slice viewer from preview * Set slide to the number of slices or numberofframes
Showing
7 changed files
with
254 additions
and
104 deletions
Show diff stats
invesalius/control.py
| @@ -404,8 +404,9 @@ class Controller(): | @@ -404,8 +404,9 @@ class Controller(): | ||
| 404 | Publisher.sendMessage('Begin busy cursor') | 404 | Publisher.sendMessage('Begin busy cursor') |
| 405 | else: | 405 | else: |
| 406 | #Is None if user canceled the load | 406 | #Is None if user canceled the load |
| 407 | - self.progress_dialog.Close() | ||
| 408 | - self.progress_dialog = None | 407 | + if self.progress_dialog is not None: |
| 408 | + self.progress_dialog.Close() | ||
| 409 | + self.progress_dialog = None | ||
| 409 | 410 | ||
| 410 | def OnLoadImportPanel(self, evt): | 411 | def OnLoadImportPanel(self, evt): |
| 411 | patient_series = evt.data | 412 | patient_series = evt.data |
| @@ -795,47 +796,53 @@ class Controller(): | @@ -795,47 +796,53 @@ class Controller(): | ||
| 795 | xyspacing = dicom.image.spacing | 796 | xyspacing = dicom.image.spacing |
| 796 | orientation = dicom.image.orientation_label | 797 | orientation = dicom.image.orientation_label |
| 797 | 798 | ||
| 799 | + wl = float(dicom.image.level) | ||
| 800 | + ww = float(dicom.image.window) | ||
| 801 | + | ||
| 798 | if sop_class_uid == '1.2.840.10008.5.1.4.1.1.7': #Secondary Capture Image Storage | 802 | if sop_class_uid == '1.2.840.10008.5.1.4.1.1.7': #Secondary Capture Image Storage |
| 799 | use_dcmspacing = 1 | 803 | use_dcmspacing = 1 |
| 800 | else: | 804 | else: |
| 801 | use_dcmspacing = 0 | 805 | use_dcmspacing = 0 |
| 802 | 806 | ||
| 803 | imagedata = None | 807 | imagedata = None |
| 804 | - | ||
| 805 | - sx, sy = size | ||
| 806 | - n_slices = len(filelist) | ||
| 807 | - resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8) | ||
| 808 | - | ||
| 809 | - if resolution_percentage < 1.0 and gui: | ||
| 810 | - re_dialog = dialog.ResizeImageDialog() | ||
| 811 | - re_dialog.SetValue(int(resolution_percentage*100)) | ||
| 812 | - re_dialog_value = re_dialog.ShowModal() | ||
| 813 | - re_dialog.Close() | ||
| 814 | - | ||
| 815 | - if re_dialog_value == wx.ID_OK: | ||
| 816 | - percentage = re_dialog.GetValue() | ||
| 817 | - resolution_percentage = percentage / 100.0 | ||
| 818 | - else: | ||
| 819 | - return | ||
| 820 | 808 | ||
| 821 | - xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage | ||
| 822 | - | ||
| 823 | - | ||
| 824 | - wl = float(dicom.image.level) | ||
| 825 | - ww = float(dicom.image.window) | ||
| 826 | - self.matrix, scalar_range, self.filename = image_utils.dcm2memmap(filelist, size, | ||
| 827 | - orientation, resolution_percentage) | 809 | + if dicom.image.number_of_frames == 1: |
| 810 | + sx, sy = size | ||
| 811 | + n_slices = len(filelist) | ||
| 812 | + resolution_percentage = utils.calculate_resizing_tofitmemory(int(sx), int(sy), n_slices, bits/8) | ||
| 813 | + | ||
| 814 | + if resolution_percentage < 1.0 and gui: | ||
| 815 | + re_dialog = dialog.ResizeImageDialog() | ||
| 816 | + re_dialog.SetValue(int(resolution_percentage*100)) | ||
| 817 | + re_dialog_value = re_dialog.ShowModal() | ||
| 818 | + re_dialog.Close() | ||
| 819 | + | ||
| 820 | + if re_dialog_value == wx.ID_OK: | ||
| 821 | + percentage = re_dialog.GetValue() | ||
| 822 | + resolution_percentage = percentage / 100.0 | ||
| 823 | + else: | ||
| 824 | + return | ||
| 825 | + | ||
| 826 | + xyspacing = xyspacing[0] / resolution_percentage, xyspacing[1] / resolution_percentage | ||
| 827 | + | ||
| 828 | + self.matrix, scalar_range, self.filename = image_utils.dcm2memmap(filelist, size, | ||
| 829 | + orientation, resolution_percentage) | ||
| 830 | + | ||
| 831 | + print xyspacing, zspacing | ||
| 832 | + if orientation == 'AXIAL': | ||
| 833 | + spacing = xyspacing[0], xyspacing[1], zspacing | ||
| 834 | + elif orientation == 'CORONAL': | ||
| 835 | + spacing = xyspacing[0], zspacing, xyspacing[1] | ||
| 836 | + elif orientation == 'SAGITTAL': | ||
| 837 | + spacing = zspacing, xyspacing[1], xyspacing[0] | ||
| 838 | + else: | ||
| 839 | + self.matrix, spacing, scalar_range, self.filename = image_utils.dcmmf2memmap(filelist[0], orientation) | ||
| 828 | 840 | ||
| 829 | self.Slice = sl.Slice() | 841 | self.Slice = sl.Slice() |
| 830 | self.Slice.matrix = self.matrix | 842 | self.Slice.matrix = self.matrix |
| 831 | self.Slice.matrix_filename = self.filename | 843 | self.Slice.matrix_filename = self.filename |
| 832 | 844 | ||
| 833 | - if orientation == 'AXIAL': | ||
| 834 | - self.Slice.spacing = xyspacing[0], xyspacing[1], zspacing | ||
| 835 | - elif orientation == 'CORONAL': | ||
| 836 | - self.Slice.spacing = xyspacing[0], zspacing, xyspacing[1] | ||
| 837 | - elif orientation == 'SAGITTAL': | ||
| 838 | - self.Slice.spacing = zspacing, xyspacing[1], xyspacing[0] | 845 | + self.Slice.spacing = spacing |
| 839 | 846 | ||
| 840 | # 1(a): Fix gantry tilt, if any | 847 | # 1(a): Fix gantry tilt, if any |
| 841 | tilt_value = dicom.acquisition.tilt | 848 | tilt_value = dicom.acquisition.tilt |
invesalius/data/imagedata_utils.py
| @@ -19,6 +19,7 @@ | @@ -19,6 +19,7 @@ | ||
| 19 | 19 | ||
| 20 | import math | 20 | import math |
| 21 | import os | 21 | import os |
| 22 | +import sys | ||
| 22 | import tempfile | 23 | import tempfile |
| 23 | 24 | ||
| 24 | import gdcm | 25 | import gdcm |
| @@ -35,6 +36,16 @@ from invesalius.data import vtk_utils as vtk_utils | @@ -35,6 +36,16 @@ from invesalius.data import vtk_utils as vtk_utils | ||
| 35 | import invesalius.reader.bitmap_reader as bitmap_reader | 36 | import invesalius.reader.bitmap_reader as bitmap_reader |
| 36 | import invesalius.utils as utils | 37 | import invesalius.utils as utils |
| 37 | import invesalius.data.converters as converters | 38 | import invesalius.data.converters as converters |
| 39 | + | ||
| 40 | +if sys.platform == 'win32': | ||
| 41 | + try: | ||
| 42 | + import win32api | ||
| 43 | + _has_win32api = True | ||
| 44 | + except ImportError: | ||
| 45 | + _has_win32api = False | ||
| 46 | +else: | ||
| 47 | + _has_win32api = False | ||
| 48 | + | ||
| 38 | # TODO: Test cases which are originally in sagittal/coronal orientation | 49 | # TODO: Test cases which are originally in sagittal/coronal orientation |
| 39 | # and have gantry | 50 | # and have gantry |
| 40 | 51 | ||
| @@ -246,11 +257,79 @@ def ExtractVOI(imagedata,xi,xf,yi,yf,zi,zf): | @@ -246,11 +257,79 @@ def ExtractVOI(imagedata,xi,xf,yi,yf,zi,zf): | ||
| 246 | """ | 257 | """ |
| 247 | voi = vtk.vtkExtractVOI() | 258 | voi = vtk.vtkExtractVOI() |
| 248 | voi.SetVOI(xi,xf,yi,yf,zi,zf) | 259 | voi.SetVOI(xi,xf,yi,yf,zi,zf) |
| 249 | - voi.SetInput(imagedata) | 260 | + voi.SetInputData(imagedata) |
| 250 | voi.SetSampleRate(1, 1, 1) | 261 | voi.SetSampleRate(1, 1, 1) |
| 251 | voi.Update() | 262 | voi.Update() |
| 252 | return voi.GetOutput() | 263 | return voi.GetOutput() |
| 253 | 264 | ||
| 265 | + | ||
| 266 | +def create_dicom_thumbnails(filename, window=None, level=None): | ||
| 267 | + rvtk = vtkgdcm.vtkGDCMImageReader() | ||
| 268 | + rvtk.SetFileName(filename) | ||
| 269 | + rvtk.Update() | ||
| 270 | + | ||
| 271 | + img = rvtk.GetOutput() | ||
| 272 | + if window is None or level is None: | ||
| 273 | + _min, _max = img.GetScalarRange() | ||
| 274 | + window = _max - _min | ||
| 275 | + level = _min + window / 2 | ||
| 276 | + | ||
| 277 | + dx, dy, dz = img.GetDimensions() | ||
| 278 | + | ||
| 279 | + if dz > 1: | ||
| 280 | + thumbnail_paths = [] | ||
| 281 | + for i in xrange(dz): | ||
| 282 | + img_slice = ExtractVOI(img, 0, dx-1, 0, dy-1, i, i+1) | ||
| 283 | + | ||
| 284 | + colorer = vtk.vtkImageMapToWindowLevelColors() | ||
| 285 | + colorer.SetInputData(img_slice) | ||
| 286 | + colorer.SetWindow(window) | ||
| 287 | + colorer.SetLevel(level) | ||
| 288 | + colorer.SetOutputFormatToRGB() | ||
| 289 | + colorer.Update() | ||
| 290 | + | ||
| 291 | + resample = vtk.vtkImageResample() | ||
| 292 | + resample.SetInputData(colorer.GetOutput()) | ||
| 293 | + resample.SetAxisMagnificationFactor ( 0, 0.25 ) | ||
| 294 | + resample.SetAxisMagnificationFactor ( 1, 0.25 ) | ||
| 295 | + resample.SetAxisMagnificationFactor ( 2, 1 ) | ||
| 296 | + resample.Update() | ||
| 297 | + | ||
| 298 | + thumbnail_path = tempfile.mktemp() | ||
| 299 | + | ||
| 300 | + write_png = vtk.vtkPNGWriter() | ||
| 301 | + write_png.SetInputData(resample.GetOutput()) | ||
| 302 | + write_png.SetFileName(thumbnail_path) | ||
| 303 | + write_png.Write() | ||
| 304 | + | ||
| 305 | + thumbnail_paths.append(thumbnail_path) | ||
| 306 | + | ||
| 307 | + return thumbnail_paths | ||
| 308 | + else: | ||
| 309 | + colorer = vtk.vtkImageMapToWindowLevelColors() | ||
| 310 | + colorer.SetInputData(img) | ||
| 311 | + colorer.SetWindow(window) | ||
| 312 | + colorer.SetLevel(level) | ||
| 313 | + colorer.SetOutputFormatToRGB() | ||
| 314 | + colorer.Update() | ||
| 315 | + | ||
| 316 | + resample = vtk.vtkImageResample() | ||
| 317 | + resample.SetInputData(colorer.GetOutput()) | ||
| 318 | + resample.SetAxisMagnificationFactor ( 0, 0.25 ) | ||
| 319 | + resample.SetAxisMagnificationFactor ( 1, 0.25 ) | ||
| 320 | + resample.SetAxisMagnificationFactor ( 2, 1 ) | ||
| 321 | + resample.Update() | ||
| 322 | + | ||
| 323 | + thumbnail_path = tempfile.mktemp() | ||
| 324 | + | ||
| 325 | + write_png = vtk.vtkPNGWriter() | ||
| 326 | + write_png.SetInputData(resample.GetOutput()) | ||
| 327 | + write_png.SetFileName(thumbnail_path) | ||
| 328 | + write_png.Write() | ||
| 329 | + | ||
| 330 | + return thumbnail_path | ||
| 331 | + | ||
| 332 | + | ||
| 254 | def CreateImageData(filelist, zspacing, xyspacing,size, | 333 | def CreateImageData(filelist, zspacing, xyspacing,size, |
| 255 | bits, use_dcmspacing): | 334 | bits, use_dcmspacing): |
| 256 | message = _("Generating multiplanar visualization...") | 335 | message = _("Generating multiplanar visualization...") |
| @@ -587,6 +666,29 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): | @@ -587,6 +666,29 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): | ||
| 587 | return matrix, scalar_range, temp_file | 666 | return matrix, scalar_range, temp_file |
| 588 | 667 | ||
| 589 | 668 | ||
| 669 | +def dcmmf2memmap(dcm_file, orientation): | ||
| 670 | + r = vtkgdcm.vtkGDCMImageReader() | ||
| 671 | + r.SetFileName(dcm_file) | ||
| 672 | + r.Update() | ||
| 673 | + | ||
| 674 | + temp_file = tempfile.mktemp() | ||
| 675 | + | ||
| 676 | + o = r.GetOutput() | ||
| 677 | + x, y, z = o.GetDimensions() | ||
| 678 | + spacing = o.GetSpacing() | ||
| 679 | + | ||
| 680 | + matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=(z, y, x)) | ||
| 681 | + | ||
| 682 | + d = numpy_support.vtk_to_numpy(o.GetPointData().GetScalars()) | ||
| 683 | + d.shape = z, y, x | ||
| 684 | + matrix[:] = d | ||
| 685 | + matrix.flush() | ||
| 686 | + | ||
| 687 | + scalar_range = matrix.min(), matrix.max() | ||
| 688 | + | ||
| 689 | + return matrix, spacing, scalar_range, temp_file | ||
| 690 | + | ||
| 691 | + | ||
| 590 | def img2memmap(group): | 692 | def img2memmap(group): |
| 591 | """ | 693 | """ |
| 592 | From a nibabel image data creates a memmap file in the temp folder and | 694 | From a nibabel image data creates a memmap file in the temp folder and |
invesalius/data/vtk_utils.py
| @@ -52,6 +52,8 @@ def ShowProgress(number_of_filters = 1, | @@ -52,6 +52,8 @@ def ShowProgress(number_of_filters = 1, | ||
| 52 | 52 | ||
| 53 | # when the pipeline is larger than 1, we have to consider this object | 53 | # when the pipeline is larger than 1, we have to consider this object |
| 54 | # percentage | 54 | # percentage |
| 55 | + if number_of_filters < 1: | ||
| 56 | + number_of_filters = 1 | ||
| 55 | ratio = (100.0 / number_of_filters) | 57 | ratio = (100.0 / number_of_filters) |
| 56 | 58 | ||
| 57 | def UpdateProgress(obj, label=""): | 59 | def UpdateProgress(obj, label=""): |
invesalius/gui/dicom_preview_panel.py
| @@ -115,7 +115,7 @@ class DicomInfo(object): | @@ -115,7 +115,7 @@ class DicomInfo(object): | ||
| 115 | """ | 115 | """ |
| 116 | Keep the informations and the image used by preview. | 116 | Keep the informations and the image used by preview. |
| 117 | """ | 117 | """ |
| 118 | - def __init__(self, id, dicom, title, subtitle): | 118 | + def __init__(self, id, dicom, title, subtitle, n=0): |
| 119 | self.id = id | 119 | self.id = id |
| 120 | self.dicom = dicom | 120 | self.dicom = dicom |
| 121 | self.title = title | 121 | self.title = title |
| @@ -123,12 +123,15 @@ class DicomInfo(object): | @@ -123,12 +123,15 @@ class DicomInfo(object): | ||
| 123 | self._preview = None | 123 | self._preview = None |
| 124 | self.selected = False | 124 | self.selected = False |
| 125 | self.filename = "" | 125 | self.filename = "" |
| 126 | + self._slice = n | ||
| 126 | 127 | ||
| 127 | @property | 128 | @property |
| 128 | def preview(self): | 129 | def preview(self): |
| 129 | - | ||
| 130 | if not self._preview: | 130 | if not self._preview: |
| 131 | - bmp = wx.Bitmap(self.dicom.image.thumbnail_path, wx.BITMAP_TYPE_PNG) | 131 | + if isinstance(self.dicom.image.thumbnail_path, list): |
| 132 | + bmp = wx.Bitmap(self.dicom.image.thumbnail_path[self._slice], wx.BITMAP_TYPE_PNG) | ||
| 133 | + else: | ||
| 134 | + bmp = wx.Bitmap(self.dicom.image.thumbnail_path, wx.BITMAP_TYPE_PNG) | ||
| 132 | self._preview = bmp.ConvertToImage() | 135 | self._preview = bmp.ConvertToImage() |
| 133 | return self._preview | 136 | return self._preview |
| 134 | 137 | ||
| @@ -539,11 +542,22 @@ class DicomPreviewSlice(wx.Panel): | @@ -539,11 +542,22 @@ class DicomPreviewSlice(wx.Panel): | ||
| 539 | dicom_files = group.GetHandSortedList() | 542 | dicom_files = group.GetHandSortedList() |
| 540 | n = 0 | 543 | n = 0 |
| 541 | for dicom in dicom_files: | 544 | for dicom in dicom_files: |
| 542 | - info = DicomInfo(n, dicom, | ||
| 543 | - _("Image %d") % (dicom.image.number), | ||
| 544 | - "%.2f" % (dicom.image.position[2])) | ||
| 545 | - self.files.append(info) | ||
| 546 | - n+=1 | 545 | + if isinstance(dicom.image.thumbnail_path, list): |
| 546 | + _slice = 0 | ||
| 547 | + for thumbnail in dicom.image.thumbnail_path: | ||
| 548 | + print thumbnail | ||
| 549 | + info = DicomInfo(n, dicom, | ||
| 550 | + _("Image %d") % (n), | ||
| 551 | + "%.2f" % (dicom.image.position[2]), _slice) | ||
| 552 | + self.files.append(info) | ||
| 553 | + n+=1 | ||
| 554 | + _slice += 1 | ||
| 555 | + else: | ||
| 556 | + info = DicomInfo(n, dicom, | ||
| 557 | + _("Image %d") % (dicom.image.number), | ||
| 558 | + "%.2f" % (dicom.image.position[2])) | ||
| 559 | + self.files.append(info) | ||
| 560 | + n+=1 | ||
| 547 | 561 | ||
| 548 | scroll_range = len(self.files)/NCOLS | 562 | scroll_range = len(self.files)/NCOLS |
| 549 | if scroll_range * NCOLS < len(self.files): | 563 | if scroll_range * NCOLS < len(self.files): |
| @@ -560,12 +574,23 @@ class DicomPreviewSlice(wx.Panel): | @@ -560,12 +574,23 @@ class DicomPreviewSlice(wx.Panel): | ||
| 560 | dicom_files = group.GetHandSortedList() | 574 | dicom_files = group.GetHandSortedList() |
| 561 | n = 0 | 575 | n = 0 |
| 562 | for dicom in dicom_files: | 576 | for dicom in dicom_files: |
| 563 | - info = DicomInfo(n, dicom, | ||
| 564 | - _("Image %d") % (dicom.image.number), | ||
| 565 | - "%.2f" % (dicom.image.position[2]), | ||
| 566 | - ) | ||
| 567 | - self.files.append(info) | ||
| 568 | - n+=1 | 577 | + if isinstance(dicom.image.thumbnail_path, list): |
| 578 | + _slice = 0 | ||
| 579 | + for thumbnail in dicom.image.thumbnail_path: | ||
| 580 | + print thumbnail | ||
| 581 | + info = DicomInfo(n, dicom, | ||
| 582 | + _("Image %d") % int(n), | ||
| 583 | + "%.2f" % (dicom.image.position[2]), _slice) | ||
| 584 | + self.files.append(info) | ||
| 585 | + n+=1 | ||
| 586 | + _slice += 1 | ||
| 587 | + else: | ||
| 588 | + info = DicomInfo(n, dicom, | ||
| 589 | + _("Image %d") % int(dicom.image.number), | ||
| 590 | + "%.2f" % (dicom.image.position[2]), | ||
| 591 | + ) | ||
| 592 | + self.files.append(info) | ||
| 593 | + n+=1 | ||
| 569 | 594 | ||
| 570 | scroll_range = len(self.files)/NCOLS | 595 | scroll_range = len(self.files)/NCOLS |
| 571 | if scroll_range * NCOLS < len(self.files): | 596 | if scroll_range * NCOLS < len(self.files): |
| @@ -803,14 +828,20 @@ class SingleImagePreview(wx.Panel): | @@ -803,14 +828,20 @@ class SingleImagePreview(wx.Panel): | ||
| 803 | def SetDicomGroup(self, group): | 828 | def SetDicomGroup(self, group): |
| 804 | self.dicom_list = group.GetHandSortedList() | 829 | self.dicom_list = group.GetHandSortedList() |
| 805 | self.current_index = 0 | 830 | self.current_index = 0 |
| 806 | - self.nimages = len(self.dicom_list) | 831 | + if len(self.dicom_list) > 1: |
| 832 | + self.nimages = len(self.dicom_list) | ||
| 833 | + else: | ||
| 834 | + self.nimages = self.dicom_list[0].image.number_of_frames | ||
| 807 | # GUI | 835 | # GUI |
| 808 | self.slider.SetMax(self.nimages-1) | 836 | self.slider.SetMax(self.nimages-1) |
| 809 | self.slider.SetValue(0) | 837 | self.slider.SetValue(0) |
| 810 | self.ShowSlice() | 838 | self.ShowSlice() |
| 811 | 839 | ||
| 812 | def ShowSlice(self, index = 0): | 840 | def ShowSlice(self, index = 0): |
| 813 | - dicom = self.dicom_list[index] | 841 | + try: |
| 842 | + dicom = self.dicom_list[index] | ||
| 843 | + except IndexError: | ||
| 844 | + dicom = self.dicom_list[0] | ||
| 814 | 845 | ||
| 815 | # UPDATE GUI | 846 | # UPDATE GUI |
| 816 | ## Text related to size | 847 | ## Text related to size |
| @@ -845,28 +876,41 @@ class SingleImagePreview(wx.Panel): | @@ -845,28 +876,41 @@ class SingleImagePreview(wx.Panel): | ||
| 845 | dicom.acquisition.time) | 876 | dicom.acquisition.time) |
| 846 | self.text_acquisition.SetValue(value) | 877 | self.text_acquisition.SetValue(value) |
| 847 | 878 | ||
| 848 | - rdicom = vtkgdcm.vtkGDCMImageReader() | ||
| 849 | - if _has_win32api: | ||
| 850 | - rdicom.SetFileName(win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE)) | 879 | + if isinstance(dicom.image.thumbnail_path, list): |
| 880 | + reader = vtk.vtkPNGReader() | ||
| 881 | + if _has_win32api: | ||
| 882 | + reader.SetFileName(win32api.GetShortPathName(dicom.image.thumbnail_path[index]).encode(const.FS_ENCODE)) | ||
| 883 | + else: | ||
| 884 | + reader.SetFileName(dicom.image.thumbnail_path[index]) | ||
| 885 | + reader.Update() | ||
| 886 | + | ||
| 887 | + image = reader.GetOutput() | ||
| 888 | + | ||
| 851 | else: | 889 | else: |
| 852 | - rdicom.SetFileName(dicom.image.file) | ||
| 853 | - rdicom.Update() | ||
| 854 | - | ||
| 855 | - # ADJUST CONTRAST | ||
| 856 | - window_level = dicom.image.level | ||
| 857 | - window_width = dicom.image.window | ||
| 858 | - colorer = vtk.vtkImageMapToWindowLevelColors() | ||
| 859 | - colorer.SetInputConnection(rdicom.GetOutputPort()) | ||
| 860 | - colorer.SetWindow(float(window_width)) | ||
| 861 | - colorer.SetLevel(float(window_level)) | ||
| 862 | - colorer.Update() | 890 | + rdicom = vtkgdcm.vtkGDCMImageReader() |
| 891 | + if _has_win32api: | ||
| 892 | + rdicom.SetFileName(win32api.GetShortPathName(dicom.image.file).encode(const.FS_ENCODE)) | ||
| 893 | + else: | ||
| 894 | + rdicom.SetFileName(dicom.image.file) | ||
| 895 | + rdicom.Update() | ||
| 896 | + | ||
| 897 | + # ADJUST CONTRAST | ||
| 898 | + window_level = dicom.image.level | ||
| 899 | + window_width = dicom.image.window | ||
| 900 | + colorer = vtk.vtkImageMapToWindowLevelColors() | ||
| 901 | + colorer.SetInputConnection(rdicom.GetOutputPort()) | ||
| 902 | + colorer.SetWindow(float(window_width)) | ||
| 903 | + colorer.SetLevel(float(window_level)) | ||
| 904 | + colorer.Update() | ||
| 905 | + | ||
| 906 | + image = colorer.GetOutput() | ||
| 863 | 907 | ||
| 864 | if self.actor is None: | 908 | if self.actor is None: |
| 865 | self.actor = vtk.vtkImageActor() | 909 | self.actor = vtk.vtkImageActor() |
| 866 | self.renderer.AddActor(self.actor) | 910 | self.renderer.AddActor(self.actor) |
| 867 | 911 | ||
| 868 | # PLOT IMAGE INTO VIEWER | 912 | # PLOT IMAGE INTO VIEWER |
| 869 | - self.actor.SetInputData(colorer.GetOutput()) | 913 | + self.actor.SetInputData(image) |
| 870 | self.renderer.ResetCamera() | 914 | self.renderer.ResetCamera() |
| 871 | self.interactor.Render() | 915 | self.interactor.Render() |
| 872 | 916 |
invesalius/reader/dicom.py
| @@ -1154,8 +1154,20 @@ class Parser(): | @@ -1154,8 +1154,20 @@ class Parser(): | ||
| 1154 | if (data): | 1154 | if (data): |
| 1155 | return int(data) | 1155 | return int(data) |
| 1156 | return "" | 1156 | return "" |
| 1157 | - | ||
| 1158 | - | 1157 | + |
| 1158 | + def GetNumberOfFrames(self): | ||
| 1159 | + """ | ||
| 1160 | + Number of frames in a multi-frame image. | ||
| 1161 | + | ||
| 1162 | + DICOM standard tag (0x0028, 0x0008) was used. | ||
| 1163 | + """ | ||
| 1164 | + try: | ||
| 1165 | + data = self.data_image[str(0x028)][str(0x0008)] | ||
| 1166 | + except KeyError: | ||
| 1167 | + return 1 | ||
| 1168 | + return int(data) | ||
| 1169 | + | ||
| 1170 | + | ||
| 1159 | def GetPatientBirthDate(self): | 1171 | def GetPatientBirthDate(self): |
| 1160 | """ | 1172 | """ |
| 1161 | Return string containing the patient's birth date using the | 1173 | Return string containing the patient's birth date using the |
| @@ -1498,11 +1510,11 @@ class Parser(): | @@ -1498,11 +1510,11 @@ class Parser(): | ||
| 1498 | try: | 1510 | try: |
| 1499 | data = self.data_image[str(0x0020)][str(0x0013)] | 1511 | data = self.data_image[str(0x0020)][str(0x0013)] |
| 1500 | except(KeyError): | 1512 | except(KeyError): |
| 1501 | - return "" | 1513 | + return 0 |
| 1502 | 1514 | ||
| 1503 | if (data): | 1515 | if (data): |
| 1504 | return int(data) | 1516 | return int(data) |
| 1505 | - return "" | 1517 | + return 0 |
| 1506 | 1518 | ||
| 1507 | def GetStudyDescription(self): | 1519 | def GetStudyDescription(self): |
| 1508 | """ | 1520 | """ |
| @@ -1954,6 +1966,8 @@ class Image(object): | @@ -1954,6 +1966,8 @@ class Image(object): | ||
| 1954 | self.bits_allocad = parser._GetBitsAllocated() | 1966 | self.bits_allocad = parser._GetBitsAllocated() |
| 1955 | self.thumbnail_path = parser.thumbnail_path | 1967 | self.thumbnail_path = parser.thumbnail_path |
| 1956 | 1968 | ||
| 1969 | + self.number_of_frames = parser.GetNumberOfFrames() | ||
| 1970 | + | ||
| 1957 | if (parser.GetImageThickness()): | 1971 | if (parser.GetImageThickness()): |
| 1958 | try: | 1972 | try: |
| 1959 | spacing.append(parser.GetImageThickness()) | 1973 | spacing.append(parser.GetImageThickness()) |
invesalius/reader/dicom_grouper.py
| @@ -94,22 +94,22 @@ class DicomGroup: | @@ -94,22 +94,22 @@ class DicomGroup: | ||
| 94 | if not self.dicom: | 94 | if not self.dicom: |
| 95 | self.dicom = dicom | 95 | self.dicom = dicom |
| 96 | 96 | ||
| 97 | - pos = tuple(dicom.image.position) | ||
| 98 | - | 97 | + pos = tuple(dicom.image.position) |
| 98 | + | ||
| 99 | #Case to test: \other\higroma | 99 | #Case to test: \other\higroma |
| 100 | - #condition created, if any dicom with the same | 100 | + #condition created, if any dicom with the same |
| 101 | #position, but 3D, leaving the same series. | 101 | #position, but 3D, leaving the same series. |
| 102 | if not "DERIVED" in dicom.image.type: | 102 | if not "DERIVED" in dicom.image.type: |
| 103 | #if any dicom with the same position | 103 | #if any dicom with the same position |
| 104 | if pos not in self.slices_dict.keys(): | 104 | if pos not in self.slices_dict.keys(): |
| 105 | self.slices_dict[pos] = dicom | 105 | self.slices_dict[pos] = dicom |
| 106 | - self.nslices += 1 | 106 | + self.nslices += dicom.image.number_of_frames |
| 107 | return True | 107 | return True |
| 108 | else: | 108 | else: |
| 109 | return False | 109 | return False |
| 110 | else: | 110 | else: |
| 111 | self.slices_dict[dicom.image.number] = dicom | 111 | self.slices_dict[dicom.image.number] = dicom |
| 112 | - self.nslices += 1 | 112 | + self.nslices += dicom.image.number_of_frames |
| 113 | return True | 113 | return True |
| 114 | 114 | ||
| 115 | def GetList(self): | 115 | def GetList(self): |
invesalius/reader/dicom_reader.py
| @@ -36,6 +36,8 @@ import invesalius.session as session | @@ -36,6 +36,8 @@ import invesalius.session as session | ||
| 36 | import glob | 36 | import glob |
| 37 | import invesalius.utils as utils | 37 | import invesalius.utils as utils |
| 38 | 38 | ||
| 39 | +from invesalius.data import imagedata_utils | ||
| 40 | + | ||
| 39 | import plistlib | 41 | import plistlib |
| 40 | 42 | ||
| 41 | if sys.platform == 'win32': | 43 | if sys.platform == 'win32': |
| @@ -187,45 +189,22 @@ class LoadDicom: | @@ -187,45 +189,22 @@ class LoadDicom: | ||
| 187 | 189 | ||
| 188 | 190 | ||
| 189 | # -------------- To Create DICOM Thumbnail ----------- | 191 | # -------------- To Create DICOM Thumbnail ----------- |
| 190 | - rvtk = vtkgdcm.vtkGDCMImageReader() | ||
| 191 | 192 | ||
| 192 | - if _has_win32api: | ||
| 193 | - print 'dicom', win32api.GetShortPathName(self.filepath) | ||
| 194 | - rvtk.SetFileName(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE)) | ||
| 195 | - else: | ||
| 196 | - rvtk.SetFileName(self.filepath) | ||
| 197 | - rvtk.Update() | ||
| 198 | - | 193 | + |
| 199 | try: | 194 | try: |
| 200 | data = data_dict[str(0x028)][str(0x1050)] | 195 | data = data_dict[str(0x028)][str(0x1050)] |
| 201 | level = [float(value) for value in data.split('\\')][0] | 196 | level = [float(value) for value in data.split('\\')][0] |
| 202 | data = data_dict[str(0x028)][str(0x1051)] | 197 | data = data_dict[str(0x028)][str(0x1051)] |
| 203 | window = [float(value) for value in data.split('\\')][0] | 198 | window = [float(value) for value in data.split('\\')][0] |
| 204 | except(KeyError, ValueError): | 199 | except(KeyError, ValueError): |
| 205 | - level = 300.0 | ||
| 206 | - window = 2000.0 | ||
| 207 | - | ||
| 208 | - colorer = vtk.vtkImageMapToWindowLevelColors() | ||
| 209 | - colorer.SetInputConnection(rvtk.GetOutputPort()) | ||
| 210 | - colorer.SetWindow(float(window)) | ||
| 211 | - colorer.SetLevel(float(level)) | ||
| 212 | - colorer.SetOutputFormatToRGB() | ||
| 213 | - colorer.Update() | ||
| 214 | - | ||
| 215 | - resample = vtk.vtkImageResample() | ||
| 216 | - resample.SetInputConnection(colorer.GetOutputPort()) | ||
| 217 | - resample.SetAxisMagnificationFactor ( 0, 0.25 ) | ||
| 218 | - resample.SetAxisMagnificationFactor ( 1, 0.25 ) | ||
| 219 | - resample.SetAxisMagnificationFactor ( 2, 1 ) | ||
| 220 | - resample.Update() | ||
| 221 | - | ||
| 222 | - thumbnail_path = tempfile.mktemp() | ||
| 223 | - | ||
| 224 | - write_png = vtk.vtkPNGWriter() | ||
| 225 | - write_png.SetInputConnection(resample.GetOutputPort()) | ||
| 226 | - write_png.SetFileName(thumbnail_path) | ||
| 227 | - write_png.Write() | ||
| 228 | - | 200 | + level = None |
| 201 | + window = None | ||
| 202 | + | ||
| 203 | + if _has_win32api: | ||
| 204 | + thumbnail_path = imagedata_utils.create_dicom_thumbnails(win32api.GetShortPathName(self.filepath).encode(const.FS_ENCODE), window, level) | ||
| 205 | + else: | ||
| 206 | + thumbnail_path = imagedata_utils.create_dicom_thumbnails(self.filepath, window, level) | ||
| 207 | + | ||
| 229 | #------ Verify the orientation -------------------------------- | 208 | #------ Verify the orientation -------------------------------- |
| 230 | 209 | ||
| 231 | img = reader.GetImage() | 210 | img = reader.GetImage() |
| @@ -362,12 +341,14 @@ class ProgressDicomReader: | @@ -362,12 +341,14 @@ class ProgressDicomReader: | ||
| 362 | 341 | ||
| 363 | y = yGetDicomGroups(path, recursive) | 342 | y = yGetDicomGroups(path, recursive) |
| 364 | for value_progress in y: | 343 | for value_progress in y: |
| 344 | + print ">>>>", value_progress | ||
| 365 | if not self.running: | 345 | if not self.running: |
| 366 | break | 346 | break |
| 367 | if isinstance(value_progress, tuple): | 347 | if isinstance(value_progress, tuple): |
| 368 | self.UpdateLoadFileProgress(value_progress) | 348 | self.UpdateLoadFileProgress(value_progress) |
| 369 | else: | 349 | else: |
| 370 | self.EndLoadFile(value_progress) | 350 | self.EndLoadFile(value_progress) |
| 351 | + self.UpdateLoadFileProgress(None) | ||
| 371 | 352 | ||
| 372 | #Is necessary in the case user cancel | 353 | #Is necessary in the case user cancel |
| 373 | #the load, ensure that dicomdialog is closed | 354 | #the load, ensure that dicomdialog is closed |