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 |