Commit 794ea5034b68ff1c8de8138d88d266bfd9bfbf85
Committed by
GitHub
1 parent
335b3a7c
Exists in
master
Micro ct (#40)
ADD: This branch is for InVesalius to read BMP, TIF, JPEG and PNG files.
Showing
10 changed files
with
2268 additions
and
11 deletions
Show diff stats
invesalius/constants.py
@@ -452,7 +452,7 @@ VTK_WARNING = 0 | @@ -452,7 +452,7 @@ VTK_WARNING = 0 | ||
452 | [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE, | 452 | [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE, |
453 | ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET, | 453 | ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET, |
454 | ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_ANALYZE_IMPORT, ID_PREFERENCES, | 454 | ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_ANALYZE_IMPORT, ID_PREFERENCES, |
455 | -ID_DICOM_NETWORK] = [wx.NewId() for number in range(13)] | 455 | +ID_DICOM_NETWORK, ID_TIFF_JPG_PNG] = [wx.NewId() for number in range(14)] |
456 | ID_EXIT = wx.ID_EXIT | 456 | ID_EXIT = wx.ID_EXIT |
457 | ID_ABOUT = wx.ID_ABOUT | 457 | ID_ABOUT = wx.ID_ABOUT |
458 | 458 |
invesalius/control.py
@@ -35,8 +35,10 @@ import project as prj | @@ -35,8 +35,10 @@ import project as prj | ||
35 | import reader.analyze_reader as analyze | 35 | import reader.analyze_reader as analyze |
36 | import reader.dicom_grouper as dg | 36 | import reader.dicom_grouper as dg |
37 | import reader.dicom_reader as dcm | 37 | import reader.dicom_reader as dcm |
38 | +import reader.bitmap_reader as bmp | ||
38 | import session as ses | 39 | import session as ses |
39 | 40 | ||
41 | + | ||
40 | import utils | 42 | import utils |
41 | import gui.dialogs as dialogs | 43 | import gui.dialogs as dialogs |
42 | import subprocess | 44 | import subprocess |
@@ -74,13 +76,20 @@ class Controller(): | @@ -74,13 +76,20 @@ class Controller(): | ||
74 | 'Save raycasting preset') | 76 | 'Save raycasting preset') |
75 | Publisher.subscribe(self.OnOpenDicomGroup, | 77 | Publisher.subscribe(self.OnOpenDicomGroup, |
76 | 'Open DICOM group') | 78 | 'Open DICOM group') |
79 | + Publisher.subscribe(self.OnOpenBitmapFiles, | ||
80 | + 'Open bitmap files') | ||
77 | Publisher.subscribe(self.Progress, "Update dicom load") | 81 | Publisher.subscribe(self.Progress, "Update dicom load") |
82 | + Publisher.subscribe(self.Progress, "Update bitmap load") | ||
78 | Publisher.subscribe(self.OnLoadImportPanel, "End dicom load") | 83 | Publisher.subscribe(self.OnLoadImportPanel, "End dicom load") |
84 | + Publisher.subscribe(self.OnLoadImportBitmapPanel, "End bitmap load") | ||
79 | Publisher.subscribe(self.OnCancelImport, 'Cancel DICOM load') | 85 | Publisher.subscribe(self.OnCancelImport, 'Cancel DICOM load') |
86 | + Publisher.subscribe(self.OnCancelImportBitmap, 'Cancel bitmap load') | ||
87 | + | ||
80 | Publisher.subscribe(self.OnShowDialogCloseProject, 'Close Project') | 88 | Publisher.subscribe(self.OnShowDialogCloseProject, 'Close Project') |
81 | Publisher.subscribe(self.OnOpenProject, 'Open project') | 89 | Publisher.subscribe(self.OnOpenProject, 'Open project') |
82 | Publisher.subscribe(self.OnOpenRecentProject, 'Open recent project') | 90 | Publisher.subscribe(self.OnOpenRecentProject, 'Open recent project') |
83 | Publisher.subscribe(self.OnShowAnalyzeFile, 'Show analyze dialog') | 91 | Publisher.subscribe(self.OnShowAnalyzeFile, 'Show analyze dialog') |
92 | + Publisher.subscribe(self.OnShowBitmapFile, 'Show bitmap dialog') | ||
84 | 93 | ||
85 | Publisher.subscribe(self.ShowBooleanOpDialog, 'Show boolean dialog') | 94 | Publisher.subscribe(self.ShowBooleanOpDialog, 'Show boolean dialog') |
86 | 95 | ||
@@ -92,7 +101,9 @@ class Controller(): | @@ -92,7 +101,9 @@ class Controller(): | ||
92 | Publisher.sendMessage('Hide import panel') | 101 | Publisher.sendMessage('Hide import panel') |
93 | 102 | ||
94 | 103 | ||
95 | - | 104 | + def OnCancelImportBitmap(self, pubsub_evt): |
105 | + #self.cancel_import = True | ||
106 | + Publisher.sendMessage('Hide import bitmap panel') | ||
96 | 107 | ||
97 | ########################### | 108 | ########################### |
98 | ########################### | 109 | ########################### |
@@ -119,9 +130,35 @@ class Controller(): | @@ -119,9 +130,35 @@ class Controller(): | ||
119 | self.LoadProject() | 130 | self.LoadProject() |
120 | Publisher.sendMessage("Enable state project", True) | 131 | Publisher.sendMessage("Enable state project", True) |
121 | 132 | ||
122 | - | 133 | + def OnShowBitmapFile(self, pubsub_evt): |
134 | + self.ShowDialogImportBitmapFile() | ||
123 | ########################### | 135 | ########################### |
124 | 136 | ||
137 | + def ShowDialogImportBitmapFile(self): | ||
138 | + # Offer to save current project if necessary | ||
139 | + session = ses.Session() | ||
140 | + st = session.project_status | ||
141 | + if (st == const.PROJ_NEW) or (st == const.PROJ_CHANGE): | ||
142 | + filename = session.project_path[1] | ||
143 | + answer = dialog.SaveChangesDialog2(filename) | ||
144 | + if answer: | ||
145 | + self.ShowDialogSaveProject() | ||
146 | + self.CloseProject() | ||
147 | + #Publisher.sendMessage("Enable state project", False) | ||
148 | + Publisher.sendMessage('Set project name') | ||
149 | + Publisher.sendMessage("Stop Config Recording") | ||
150 | + Publisher.sendMessage("Set slice interaction style", const.STATE_DEFAULT) | ||
151 | + | ||
152 | + # Import TIFF, BMP, JPEG or PNG | ||
153 | + dirpath = dialog.ShowImportBitmapDirDialog() | ||
154 | + | ||
155 | + if dirpath and not os.listdir(dirpath): | ||
156 | + dialog.ImportEmptyDirectory(dirpath) | ||
157 | + elif dirpath: | ||
158 | + self.StartImportBitmapPanel(dirpath) | ||
159 | + # Publisher.sendMessage("Load data to import panel", dirpath) | ||
160 | + | ||
161 | + | ||
125 | def ShowDialogImportDirectory(self): | 162 | def ShowDialogImportDirectory(self): |
126 | # Offer to save current project if necessary | 163 | # Offer to save current project if necessary |
127 | session = ses.Session() | 164 | session = ses.Session() |
@@ -291,6 +328,12 @@ class Controller(): | @@ -291,6 +328,12 @@ class Controller(): | ||
291 | 328 | ||
292 | ########################### | 329 | ########################### |
293 | 330 | ||
331 | + def StartImportBitmapPanel(self, path): | ||
332 | + # retrieve DICOM files splited into groups | ||
333 | + reader = bmp.ProgressBitmapReader() | ||
334 | + reader.SetWindowEvent(self.frame) | ||
335 | + reader.SetDirectoryPath(path) | ||
336 | + Publisher.sendMessage('End busy cursor') | ||
294 | 337 | ||
295 | def StartImportPanel(self, path): | 338 | def StartImportPanel(self, path): |
296 | 339 | ||
@@ -324,6 +367,26 @@ class Controller(): | @@ -324,6 +367,26 @@ class Controller(): | ||
324 | Publisher.sendMessage('Show import panel') | 367 | Publisher.sendMessage('Show import panel') |
325 | Publisher.sendMessage("Show import panel in frame") | 368 | Publisher.sendMessage("Show import panel in frame") |
326 | 369 | ||
370 | + def OnLoadImportBitmapPanel(self, evt): | ||
371 | + data = evt.data | ||
372 | + ok = self.LoadImportBitmapPanel(data) | ||
373 | + if ok: | ||
374 | + Publisher.sendMessage('Show import bitmap panel in frame') | ||
375 | + #Publisher.sendMessage("Show import panel in frame") | ||
376 | + | ||
377 | + def LoadImportBitmapPanel(self, data): | ||
378 | + #if patient_series and isinstance(patient_series, list): | ||
379 | + #Publisher.sendMessage("Load import panel", patient_series) | ||
380 | + #first_patient = patient_series[0] | ||
381 | + #Publisher.sendMessage("Load bitmap preview", first_patient) | ||
382 | + if data: | ||
383 | + Publisher.sendMessage("Load import bitmap panel", data) | ||
384 | + | ||
385 | + return True | ||
386 | + else: | ||
387 | + dialog.ImportInvalidFiles() | ||
388 | + return False | ||
389 | + | ||
327 | 390 | ||
328 | def LoadImportPanel(self, patient_series): | 391 | def LoadImportPanel(self, patient_series): |
329 | if patient_series and isinstance(patient_series, list): | 392 | if patient_series and isinstance(patient_series, list): |
@@ -335,6 +398,9 @@ class Controller(): | @@ -335,6 +398,9 @@ class Controller(): | ||
335 | dialog.ImportInvalidFiles() | 398 | dialog.ImportInvalidFiles() |
336 | return False | 399 | return False |
337 | 400 | ||
401 | + | ||
402 | + #----------- to import by command line --------------------------------------------------- | ||
403 | + | ||
338 | def OnImportMedicalImages(self, pubsub_evt): | 404 | def OnImportMedicalImages(self, pubsub_evt): |
339 | directory = pubsub_evt.data | 405 | directory = pubsub_evt.data |
340 | self.ImportMedicalImages(directory) | 406 | self.ImportMedicalImages(directory) |
@@ -358,6 +424,8 @@ class Controller(): | @@ -358,6 +424,8 @@ class Controller(): | ||
358 | self.LoadProject() | 424 | self.LoadProject() |
359 | Publisher.sendMessage("Enable state project", True) | 425 | Publisher.sendMessage("Enable state project", True) |
360 | 426 | ||
427 | + #------------------------------------------------------------------------------------- | ||
428 | + | ||
361 | def LoadProject(self): | 429 | def LoadProject(self): |
362 | proj = prj.Project() | 430 | proj = prj.Project() |
363 | 431 | ||
@@ -401,9 +469,13 @@ class Controller(): | @@ -401,9 +469,13 @@ class Controller(): | ||
401 | Publisher.sendMessage('Show mask', (mask_index, True)) | 469 | Publisher.sendMessage('Show mask', (mask_index, True)) |
402 | else: | 470 | else: |
403 | mask_name = const.MASK_NAME_PATTERN % (1,) | 471 | mask_name = const.MASK_NAME_PATTERN % (1,) |
404 | - thresh = const.THRESHOLD_RANGE | ||
405 | - colour = const.MASK_COLOUR[0] | ||
406 | 472 | ||
473 | + if proj.modality != "UNKNOWN": | ||
474 | + thresh = const.THRESHOLD_RANGE | ||
475 | + else: | ||
476 | + thresh = proj.threshold_range | ||
477 | + | ||
478 | + colour = const.MASK_COLOUR[0] | ||
407 | Publisher.sendMessage('Create new mask', | 479 | Publisher.sendMessage('Create new mask', |
408 | (mask_name, thresh, colour)) | 480 | (mask_name, thresh, colour)) |
409 | 481 | ||
@@ -483,6 +555,138 @@ class Controller(): | @@ -483,6 +555,138 @@ class Controller(): | ||
483 | dirpath = session.CreateProject(filename) | 555 | dirpath = session.CreateProject(filename) |
484 | #proj.SavePlistProject(dirpath, filename) | 556 | #proj.SavePlistProject(dirpath, filename) |
485 | 557 | ||
558 | + | ||
559 | + def CreateBitmapProject(self, bmp_data, rec_data, matrix, matrix_filename): | ||
560 | + name_to_const = {"AXIAL":const.AXIAL, | ||
561 | + "CORONAL":const.CORONAL, | ||
562 | + "SAGITTAL":const.SAGITAL} | ||
563 | + | ||
564 | + name = rec_data[0] | ||
565 | + orientation = rec_data[1] | ||
566 | + sp_x = float(rec_data[2]) | ||
567 | + sp_y = float(rec_data[3]) | ||
568 | + sp_z = float(rec_data[4]) | ||
569 | + interval = int(rec_data[5]) | ||
570 | + | ||
571 | + bits = bmp_data.GetFirstPixelSize() | ||
572 | + sx, sy = size = bmp_data.GetFirstBitmapSize() | ||
573 | + | ||
574 | + proj = prj.Project() | ||
575 | + proj.name = name | ||
576 | + proj.modality = 'UNKNOWN' | ||
577 | + proj.SetAcquisitionModality(proj.modality) | ||
578 | + proj.matrix_shape = matrix.shape | ||
579 | + proj.matrix_dtype = matrix.dtype.name | ||
580 | + proj.matrix_filename = matrix_filename | ||
581 | + #proj.imagedata = imagedata | ||
582 | + #proj.dicom_sample = dicom | ||
583 | + proj.original_orientation =\ | ||
584 | + name_to_const[orientation.upper()] | ||
585 | + proj.window = float(matrix.max()) | ||
586 | + proj.level = float(matrix.max()/2) | ||
587 | + | ||
588 | + proj.threshold_range = int(matrix.min()), int(matrix.max()) | ||
589 | + #const.THRESHOLD_RANGE = proj.threshold_range | ||
590 | + | ||
591 | + proj.spacing = self.Slice.spacing | ||
592 | + | ||
593 | + ###### | ||
594 | + session = ses.Session() | ||
595 | + filename = proj.name+".inv3" | ||
596 | + | ||
597 | + filename = filename.replace("/", "") #Fix problem case other/Skull_DICOM | ||
598 | + | ||
599 | + dirpath = session.CreateProject(filename) | ||
600 | + | ||
601 | + def OnOpenBitmapFiles(self, pubsub_evt): | ||
602 | + rec_data = pubsub_evt.data | ||
603 | + 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 | + | ||
608 | + self.LoadProject() | ||
609 | + Publisher.sendMessage("Enable state project", True) | ||
610 | + | ||
611 | + | ||
612 | + def OpenBitmapFiles(self, bmp_data, rec_data): | ||
613 | + | ||
614 | + if bmp_data.IsAllBitmapSameSize(): | ||
615 | + | ||
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() | ||
627 | + | ||
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 | ||
649 | + | ||
650 | + | ||
651 | + | ||
652 | + self.matrix, scalar_range, self.filename = image_utils.bitmap2memmap(filelist, size, | ||
653 | + orientation, (sp_z, sp_y, sp_x),resolution_percentage) | ||
654 | + | ||
655 | + | ||
656 | + self.Slice = sl.Slice() | ||
657 | + self.Slice.matrix = self.matrix | ||
658 | + self.Slice.matrix_filename = self.filename | ||
659 | + | ||
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 | ||
686 | + | ||
687 | + else: | ||
688 | + print "Error: All slices must be of the same size." | ||
689 | + | ||
486 | def OnOpenDicomGroup(self, pubsub_evt): | 690 | def OnOpenDicomGroup(self, pubsub_evt): |
487 | group, interval, file_range = pubsub_evt.data | 691 | group, interval, file_range = pubsub_evt.data |
488 | matrix, matrix_filename, dicom = self.OpenDicomGroup(group, interval, file_range, gui=True) | 692 | matrix, matrix_filename, dicom = self.OpenDicomGroup(group, interval, file_range, gui=True) |
invesalius/data/imagedata_utils.py
@@ -32,7 +32,9 @@ from vtk.util import numpy_support | @@ -32,7 +32,9 @@ from vtk.util import numpy_support | ||
32 | 32 | ||
33 | import constants as const | 33 | import constants as const |
34 | from data import vtk_utils | 34 | from data import vtk_utils |
35 | +from reader import bitmap_reader | ||
35 | import utils | 36 | import utils |
37 | +import converters | ||
36 | 38 | ||
37 | # TODO: Test cases which are originally in sagittal/coronal orientation | 39 | # TODO: Test cases which are originally in sagittal/coronal orientation |
38 | # and have gantry | 40 | # and have gantry |
@@ -416,6 +418,90 @@ class ImageCreator: | @@ -416,6 +418,90 @@ class ImageCreator: | ||
416 | 418 | ||
417 | return imagedata | 419 | return imagedata |
418 | 420 | ||
421 | +def bitmap2memmap(files, slice_size, orientation, spacing, resolution_percentage): | ||
422 | + """ | ||
423 | + From a list of dicom files it creates memmap file in the temp folder and | ||
424 | + returns it and its related filename. | ||
425 | + """ | ||
426 | + message = _("Generating multiplanar visualization...") | ||
427 | + update_progress= vtk_utils.ShowProgress(len(files) - 1, dialog_type = "ProgressDialog") | ||
428 | + | ||
429 | + temp_file = tempfile.mktemp() | ||
430 | + | ||
431 | + if orientation == 'SAGITTAL': | ||
432 | + if resolution_percentage == 1.0: | ||
433 | + shape = slice_size[0], slice_size[1], len(files) | ||
434 | + else: | ||
435 | + shape = math.ceil(slice_size[0]*resolution_percentage),\ | ||
436 | + math.ceil(slice_size[1]*resolution_percentage), len(files) | ||
437 | + | ||
438 | + elif orientation == 'CORONAL': | ||
439 | + if resolution_percentage == 1.0: | ||
440 | + shape = slice_size[1], len(files), slice_size[0] | ||
441 | + else: | ||
442 | + shape = math.ceil(slice_size[1]*resolution_percentage), len(files),\ | ||
443 | + math.ceil(slice_size[0]*resolution_percentage) | ||
444 | + else: | ||
445 | + if resolution_percentage == 1.0: | ||
446 | + shape = len(files), slice_size[1], slice_size[0] | ||
447 | + else: | ||
448 | + shape = len(files), math.ceil(slice_size[1]*resolution_percentage),\ | ||
449 | + math.ceil(slice_size[0]*resolution_percentage) | ||
450 | + | ||
451 | + matrix = numpy.memmap(temp_file, mode='w+', dtype='int16', shape=shape) | ||
452 | + cont = 0 | ||
453 | + max_scalar = None | ||
454 | + min_scalar = None | ||
455 | + | ||
456 | + for n, f in enumerate(files): | ||
457 | + image_as_array = bitmap_reader.ReadBitmap(f) | ||
458 | + | ||
459 | + image = converters.to_vtk(image_as_array, spacing=spacing,\ | ||
460 | + slice_number=1, orientation=orientation.upper()) | ||
461 | + | ||
462 | + if resolution_percentage != 1.0: | ||
463 | + | ||
464 | + | ||
465 | + image_resized = ResampleImage2D(image, px=None, py=None,\ | ||
466 | + resolution_percentage = resolution_percentage, update_progress = None) | ||
467 | + | ||
468 | + image = image_resized | ||
469 | + | ||
470 | + min_aux, max_aux = image.GetScalarRange() | ||
471 | + if min_scalar is None or min_aux < min_scalar: | ||
472 | + min_scalar = min_aux | ||
473 | + | ||
474 | + if max_scalar is None or max_aux > max_scalar: | ||
475 | + max_scalar = max_aux | ||
476 | + | ||
477 | + array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) | ||
478 | + array = array.astype("int16") | ||
479 | + | ||
480 | + array = image_as_array | ||
481 | + | ||
482 | + if orientation == 'CORONAL': | ||
483 | + array.shape = matrix.shape[0], matrix.shape[2] | ||
484 | + matrix[:, n, :] = array | ||
485 | + elif orientation == 'SAGITTAL': | ||
486 | + array.shape = matrix.shape[0], matrix.shape[1] | ||
487 | + # 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 | ||
489 | + # sagittal cases. | ||
490 | + matrix[:, :, n] = array | ||
491 | + else: | ||
492 | + print array.shape, matrix.shape | ||
493 | + array.shape = matrix.shape[1], matrix.shape[2] | ||
494 | + matrix[n] = array | ||
495 | + update_progress(cont,message) | ||
496 | + cont += 1 | ||
497 | + | ||
498 | + matrix.flush() | ||
499 | + scalar_range = min_scalar, max_scalar | ||
500 | + | ||
501 | + return matrix, scalar_range, temp_file | ||
502 | + | ||
503 | + | ||
504 | + | ||
419 | def dcm2memmap(files, slice_size, orientation, resolution_percentage): | 505 | def dcm2memmap(files, slice_size, orientation, resolution_percentage): |
420 | """ | 506 | """ |
421 | From a list of dicom files it creates memmap file in the temp folder and | 507 | From a list of dicom files it creates memmap file in the temp folder and |
@@ -462,7 +548,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): | @@ -462,7 +548,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): | ||
462 | resolution_percentage = resolution_percentage, update_progress = None) | 548 | resolution_percentage = resolution_percentage, update_progress = None) |
463 | 549 | ||
464 | image = image_resized | 550 | image = image_resized |
465 | - print ">>>>>>>>>", image.GetDimensions() | ||
466 | 551 | ||
467 | min_aux, max_aux = image.GetScalarRange() | 552 | min_aux, max_aux = image.GetScalarRange() |
468 | if min_scalar is None or min_aux < min_scalar: | 553 | if min_scalar is None or min_aux < min_scalar: |
@@ -482,7 +567,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): | @@ -482,7 +567,6 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): | ||
482 | # sagittal cases. | 567 | # sagittal cases. |
483 | matrix[:, :, n] = array | 568 | matrix[:, :, n] = array |
484 | else: | 569 | else: |
485 | - print array.shape, matrix.shape | ||
486 | array.shape = matrix.shape[1], matrix.shape[2] | 570 | array.shape = matrix.shape[1], matrix.shape[2] |
487 | matrix[n] = array | 571 | matrix[n] = array |
488 | update_progress(cont,message) | 572 | update_progress(cont,message) |
@@ -0,0 +1,903 @@ | @@ -0,0 +1,903 @@ | ||
1 | +import wx | ||
2 | +import vtk | ||
3 | +import vtkgdcm | ||
4 | +import time | ||
5 | + | ||
6 | +from vtk.util import numpy_support | ||
7 | +from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor | ||
8 | +from wx.lib.pubsub import pub as Publisher | ||
9 | + | ||
10 | +import constants as const | ||
11 | +import data.vtk_utils as vtku | ||
12 | +from data import converters | ||
13 | +from reader import bitmap_reader | ||
14 | +import utils | ||
15 | + | ||
16 | +NROWS = 3 | ||
17 | +NCOLS = 6 | ||
18 | +NUM_PREVIEWS = NCOLS*NROWS | ||
19 | +PREVIEW_WIDTH = 70 | ||
20 | +PREVIEW_HEIGTH = 70 | ||
21 | + | ||
22 | +PREVIEW_BACKGROUND = (255, 255, 255) # White | ||
23 | + | ||
24 | +STR_SIZE = _("Image size: %d x %d") | ||
25 | +STR_SPC = _("Spacing: %.2f") | ||
26 | +STR_LOCAL = _("Location: %.2f") | ||
27 | +STR_PATIENT = "%s\n%s" | ||
28 | +STR_ACQ = _("%s %s\nMade in InVesalius") | ||
29 | + | ||
30 | +myEVT_PREVIEW_CLICK = wx.NewEventType() | ||
31 | +EVT_PREVIEW_CLICK = wx.PyEventBinder(myEVT_PREVIEW_CLICK, 1) | ||
32 | + | ||
33 | +myEVT_PREVIEW_DBLCLICK = wx.NewEventType() | ||
34 | +EVT_PREVIEW_DBLCLICK = wx.PyEventBinder(myEVT_PREVIEW_DBLCLICK, 1) | ||
35 | + | ||
36 | +myEVT_CLICK_SLICE = wx.NewEventType() | ||
37 | +# This event occurs when the user select a preview | ||
38 | +EVT_CLICK_SLICE = wx.PyEventBinder(myEVT_CLICK_SLICE, 1) | ||
39 | + | ||
40 | +myEVT_CLICK_SERIE = wx.NewEventType() | ||
41 | +# This event occurs when the user select a preview | ||
42 | +EVT_CLICK_SERIE = wx.PyEventBinder(myEVT_CLICK_SERIE, 1) | ||
43 | + | ||
44 | +myEVT_CLICK = wx.NewEventType() | ||
45 | +EVT_CLICK = wx.PyEventBinder(myEVT_CLICK, 1) | ||
46 | + | ||
47 | + | ||
48 | +class SelectionEvent(wx.PyCommandEvent): | ||
49 | + pass | ||
50 | + | ||
51 | + | ||
52 | +class PreviewEvent(wx.PyCommandEvent): | ||
53 | + def __init__(self , evtType, id): | ||
54 | + super(PreviewEvent, self).__init__(evtType, id) | ||
55 | + | ||
56 | + def GetSelectID(self): | ||
57 | + return self.SelectedID | ||
58 | + | ||
59 | + def SetSelectedID(self, id): | ||
60 | + self.SelectedID = id | ||
61 | + | ||
62 | + def GetItemData(self): | ||
63 | + return self.data | ||
64 | + | ||
65 | + def GetPressedShift(self): | ||
66 | + return self.pressed_shift | ||
67 | + | ||
68 | + def SetItemData(self, data): | ||
69 | + self.data = data | ||
70 | + | ||
71 | + def SetShiftStatus(self, status): | ||
72 | + self.pressed_shift = status | ||
73 | + | ||
74 | + | ||
75 | +class SerieEvent(PreviewEvent): | ||
76 | + def __init__(self , evtType, id): | ||
77 | + super(SerieEvent, self).__init__(evtType, id) | ||
78 | + | ||
79 | + | ||
80 | +class BitmapInfo(object): | ||
81 | + """ | ||
82 | + Keep the informations and the image used by preview. | ||
83 | + """ | ||
84 | + def __init__(self, data): | ||
85 | + #self.id = id | ||
86 | + self.id = data[7] | ||
87 | + self.title = data[6] | ||
88 | + self.data = data | ||
89 | + self.pos = data[8] | ||
90 | + #self.subtitle = subtitle | ||
91 | + self._preview = None | ||
92 | + self.selected = False | ||
93 | + #self.filename = "" | ||
94 | + self.thumbnail_path = data[1] | ||
95 | + | ||
96 | + @property | ||
97 | + def preview(self): | ||
98 | + | ||
99 | + if not self._preview: | ||
100 | + bmp = wx.Bitmap(self.thumbnail_path, wx.BITMAP_TYPE_PNG) | ||
101 | + self._preview = bmp.ConvertToImage() | ||
102 | + return self._preview | ||
103 | + | ||
104 | + def release_thumbnail(self): | ||
105 | + self._preview = None | ||
106 | + | ||
107 | +class DicomPaintPanel(wx.Panel): | ||
108 | + def __init__(self, parent): | ||
109 | + super(DicomPaintPanel, self).__init__(parent) | ||
110 | + self._bind_events() | ||
111 | + self.image = None | ||
112 | + self.last_size = (10,10) | ||
113 | + | ||
114 | + def _bind_events(self): | ||
115 | + self.Bind(wx.EVT_PAINT, self.OnPaint) | ||
116 | + self.Bind(wx.EVT_SIZE, self.OnSize) | ||
117 | + | ||
118 | + def _build_bitmap(self, image): | ||
119 | + bmp = wx.BitmapFromImage(image) | ||
120 | + return bmp | ||
121 | + | ||
122 | + def _image_resize(self, image): | ||
123 | + self.Update() | ||
124 | + self.Layout() | ||
125 | + new_size = self.GetSize() | ||
126 | + # This is necessary due to darwin problem # | ||
127 | + if new_size != (0,0): | ||
128 | + self.last_size = new_size | ||
129 | + return image.Scale(*new_size) | ||
130 | + else: | ||
131 | + return image.Scale(*self.last_size) | ||
132 | + | ||
133 | + def SetImage(self, image): | ||
134 | + self.image = image | ||
135 | + r_img = self._image_resize(image) | ||
136 | + self.bmp = self._build_bitmap(r_img) | ||
137 | + self.Refresh() | ||
138 | + | ||
139 | + def OnPaint(self, evt): | ||
140 | + if self.image: | ||
141 | + dc = wx.PaintDC(self) | ||
142 | + dc.Clear() | ||
143 | + dc.DrawBitmap(self.bmp, 0, 0) | ||
144 | + | ||
145 | + def OnSize(self, evt): | ||
146 | + if self.image: | ||
147 | + self.bmp = self._build_bitmap(self._image_resize(self.image)) | ||
148 | + self.Refresh() | ||
149 | + evt.Skip() | ||
150 | + | ||
151 | + | ||
152 | +class Preview(wx.Panel): | ||
153 | + """ | ||
154 | + The little previews. | ||
155 | + """ | ||
156 | + def __init__(self, parent): | ||
157 | + super(Preview, self).__init__(parent) | ||
158 | + # Will it be white? | ||
159 | + self.select_on = False | ||
160 | + self.bitmap_info = None | ||
161 | + self._init_ui() | ||
162 | + self._bind_events() | ||
163 | + | ||
164 | + def _init_ui(self): | ||
165 | + self.SetBackgroundColour(PREVIEW_BACKGROUND) | ||
166 | + | ||
167 | + self.title = wx.StaticText(self, -1, _("Image")) | ||
168 | + self.subtitle = wx.StaticText(self, -1, _("Image")) | ||
169 | + self.image_viewer = DicomPaintPanel(self) | ||
170 | + | ||
171 | + self.sizer = wx.BoxSizer(wx.VERTICAL) | ||
172 | + self.sizer.Add(self.title, 0, | ||
173 | + wx.ALIGN_CENTER_HORIZONTAL) | ||
174 | + self.sizer.Add(self.subtitle, 0, | ||
175 | + wx.ALIGN_CENTER_HORIZONTAL) | ||
176 | + self.sizer.Add(self.image_viewer, 1, wx.ALIGN_CENTRE_HORIZONTAL \ | ||
177 | + | wx.SHAPED | wx.ALL, 5) | ||
178 | + self.sizer.Fit(self) | ||
179 | + | ||
180 | + self.SetSizer(self.sizer) | ||
181 | + | ||
182 | + self.Layout() | ||
183 | + self.Update() | ||
184 | + self.Fit() | ||
185 | + self.SetAutoLayout(1) | ||
186 | + | ||
187 | + def _bind_events(self): | ||
188 | + self.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | ||
189 | + #self.interactor.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | ||
190 | + #self.panel.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | ||
191 | + #self.title.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | ||
192 | + #self.subtitle.Bind( wx.EVT_LEFT_DCLICK, self.OnDClick) | ||
193 | + | ||
194 | + self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | ||
195 | + #self.interactor.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | ||
196 | + #self.panel.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | ||
197 | + #self.title.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | ||
198 | + #self.subtitle.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) | ||
199 | + | ||
200 | + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | ||
201 | + #self.interactor.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | ||
202 | + #self.panel.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | ||
203 | + #self.title.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | ||
204 | + #self.subtitle.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) | ||
205 | + | ||
206 | + self.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | ||
207 | + self.title.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | ||
208 | + self.subtitle.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | ||
209 | + self.image_viewer.Bind(wx.EVT_LEFT_DOWN, self.OnSelect) | ||
210 | + | ||
211 | + #self.Bind(wx.EVT_SIZE, self.OnSize) | ||
212 | + | ||
213 | + def SetBitmapToPreview(self, bitmap_info): | ||
214 | + """ | ||
215 | + Set a dicom to preview. | ||
216 | + """ | ||
217 | + | ||
218 | + """ | ||
219 | + self.dicom_info = dicom_info | ||
220 | + self.SetTitle(dicom_info.title) | ||
221 | + self.SetSubtitle(dicom_info.subtitle) | ||
222 | + self.ID = dicom_info.id | ||
223 | + dicom_info.size = self.image_viewer.GetSize() | ||
224 | + image = dicom_info.preview | ||
225 | + self.image_viewer.SetImage(image) | ||
226 | + self.data = dicom_info.id | ||
227 | + self.select_on = dicom_info.selected | ||
228 | + self.Select() | ||
229 | + self.Update() | ||
230 | + """ | ||
231 | + | ||
232 | + | ||
233 | + | ||
234 | + if self.bitmap_info: | ||
235 | + self.bitmap_info.release_thumbnail() | ||
236 | + | ||
237 | + self.bitmap_info = bitmap_info | ||
238 | + self.SetTitle(self.bitmap_info.title[-10:]) | ||
239 | + self.SetSubtitle('') | ||
240 | + | ||
241 | + ##self.ID = bitmap_info.id | ||
242 | + ##bitmap_info.size = self.image_viewer.GetSize() | ||
243 | + image = self.bitmap_info.preview | ||
244 | + | ||
245 | + self.image_viewer.SetImage(image) | ||
246 | + #self.data = bitmap_info.id | ||
247 | + self.select_on = bitmap_info.selected | ||
248 | + self.Select() | ||
249 | + self.Update() | ||
250 | + | ||
251 | + def SetTitle(self, title): | ||
252 | + self.title.SetLabel(title) | ||
253 | + | ||
254 | + def SetSubtitle(self, subtitle): | ||
255 | + self.subtitle.SetLabel(subtitle) | ||
256 | + | ||
257 | + def OnEnter(self, evt): | ||
258 | + if not self.select_on: | ||
259 | + #c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DHILIGHT) | ||
260 | + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) | ||
261 | + self.SetBackgroundColour(c) | ||
262 | + | ||
263 | + def OnLeave(self, evt): | ||
264 | + if not self.select_on: | ||
265 | + c = (PREVIEW_BACKGROUND) | ||
266 | + self.SetBackgroundColour(c) | ||
267 | + | ||
268 | + def OnSelect(self, evt): | ||
269 | + | ||
270 | + shift_pressed = False | ||
271 | + if evt.m_shiftDown: | ||
272 | + shift_pressed = True | ||
273 | + | ||
274 | + dicom_id = self.bitmap_info.id | ||
275 | + self.select_on = True | ||
276 | + self.bitmap_info.selected = True | ||
277 | + self.Select() | ||
278 | + | ||
279 | + # Generating a EVT_PREVIEW_CLICK event | ||
280 | + my_evt = SerieEvent(myEVT_PREVIEW_CLICK, self.GetId()) | ||
281 | + | ||
282 | + my_evt.SetSelectedID(self.bitmap_info.id) | ||
283 | + my_evt.SetItemData(self.bitmap_info.data) | ||
284 | + | ||
285 | + my_evt.SetShiftStatus(shift_pressed) | ||
286 | + my_evt.SetEventObject(self) | ||
287 | + self.GetEventHandler().ProcessEvent(my_evt) | ||
288 | + | ||
289 | + Publisher.sendMessage('Set bitmap in preview panel', self.bitmap_info.pos) | ||
290 | + | ||
291 | + evt.Skip() | ||
292 | + | ||
293 | + | ||
294 | + def OnSize(self, evt): | ||
295 | + if self.bitmap_info: | ||
296 | + self.SetBitmapToPreview(self.bitmap_info) | ||
297 | + evt.Skip() | ||
298 | + | ||
299 | + def Select(self, on=True): | ||
300 | + if self.select_on: | ||
301 | + c = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) | ||
302 | + else: | ||
303 | + c = (PREVIEW_BACKGROUND) | ||
304 | + self.SetBackgroundColour(c) | ||
305 | + self.Refresh() | ||
306 | + | ||
307 | + def OnDClick(self, evt): | ||
308 | + my_evt = SerieEvent(myEVT_PREVIEW_DBLCLICK, self.GetId()) | ||
309 | + my_evt.SetSelectedID(self.bitmap_info.id) | ||
310 | + my_evt.SetItemData(self.bitmap_info.data) | ||
311 | + my_evt.SetEventObject(self) | ||
312 | + self.GetEventHandler().ProcessEvent(my_evt) | ||
313 | + evt.Skip() | ||
314 | + | ||
315 | + | ||
316 | +class BitmapPreviewSeries(wx.Panel): | ||
317 | + """A dicom series preview panel""" | ||
318 | + def __init__(self, parent): | ||
319 | + super(BitmapPreviewSeries, self).__init__(parent) | ||
320 | + # TODO: 3 pixels between the previews is a good idea? | ||
321 | + # I have to test. | ||
322 | + #self.sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
323 | + #self.SetSizer(self.sizer) | ||
324 | + self.displayed_position = 0 | ||
325 | + self.nhidden_last_display = 0 | ||
326 | + self.selected_dicom = None | ||
327 | + self.selected_panel = None | ||
328 | + self._init_ui() | ||
329 | + | ||
330 | + def _init_ui(self): | ||
331 | + scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) | ||
332 | + self.scroll = scroll | ||
333 | + | ||
334 | + self.grid = wx.GridSizer(rows=NROWS, cols=NCOLS, vgap=3, hgap=3) | ||
335 | + | ||
336 | + sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
337 | + sizer.AddSizer(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | ||
338 | + | ||
339 | + background_sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
340 | + background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | ||
341 | + background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | ||
342 | + self.SetSizer(background_sizer) | ||
343 | + background_sizer.Fit(self) | ||
344 | + | ||
345 | + self.Layout() | ||
346 | + self.Update() | ||
347 | + self.SetAutoLayout(1) | ||
348 | + | ||
349 | + self.sizer = background_sizer | ||
350 | + | ||
351 | + self._Add_Panels_Preview() | ||
352 | + self._bind_events() | ||
353 | + | ||
354 | + def _Add_Panels_Preview(self): | ||
355 | + self.previews = [] | ||
356 | + for i in xrange(NROWS): | ||
357 | + for j in xrange(NCOLS): | ||
358 | + p = Preview(self) | ||
359 | + p.Bind(EVT_PREVIEW_CLICK, self.OnSelect) | ||
360 | + #if (i == j == 0): | ||
361 | + #self._show_shadow(p) | ||
362 | + #p.Hide() | ||
363 | + self.previews.append(p) | ||
364 | + self.grid.Add(p, 1, flag=wx.EXPAND) | ||
365 | + | ||
366 | + #def _show_shadow(self, preview): | ||
367 | + # preview.ShowShadow() | ||
368 | + | ||
369 | + def _bind_events(self): | ||
370 | + # When the user scrolls the window | ||
371 | + self.Bind(wx.EVT_SCROLL, self.OnScroll) | ||
372 | + self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) | ||
373 | + | ||
374 | + def OnSelect(self, evt): | ||
375 | + my_evt = SerieEvent(myEVT_CLICK_SERIE, self.GetId()) | ||
376 | + my_evt.SetSelectedID(evt.GetSelectID()) | ||
377 | + my_evt.SetItemData(evt.GetItemData()) | ||
378 | + | ||
379 | + if self.selected_dicom: | ||
380 | + self.selected_dicom.selected = self.selected_dicom is \ | ||
381 | + evt.GetEventObject().bitmap_info | ||
382 | + self.selected_panel.select_on = self.selected_panel is evt.GetEventObject() | ||
383 | + self.selected_panel.Select() | ||
384 | + self.selected_panel = evt.GetEventObject() | ||
385 | + self.selected_dicom = self.selected_panel.bitmap_info | ||
386 | + self.GetEventHandler().ProcessEvent(my_evt) | ||
387 | + evt.Skip() | ||
388 | + | ||
389 | + def SetBitmapFiles(self, data): | ||
390 | + #self.files = data | ||
391 | + self.files = [] | ||
392 | + | ||
393 | + bitmap = bitmap_reader.BitmapData() | ||
394 | + bitmap.SetData(data) | ||
395 | + | ||
396 | + pos = 0 | ||
397 | + for d in data: | ||
398 | + d.append(pos) | ||
399 | + info = BitmapInfo(d) | ||
400 | + self.files.append(info) | ||
401 | + pos += 1 | ||
402 | + | ||
403 | + scroll_range = len(self.files)/NCOLS | ||
404 | + if scroll_range * NCOLS < len(self.files): | ||
405 | + scroll_range +=1 | ||
406 | + self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | ||
407 | + self._display_previews() | ||
408 | + | ||
409 | + | ||
410 | + #def SetPatientGroups(self, patient): | ||
411 | + # self.files = [] | ||
412 | + # self.displayed_position = 0 | ||
413 | + # self.nhidden_last_display = 0 | ||
414 | + # group_list = patient.GetGroups() | ||
415 | + # self.group_list = group_list | ||
416 | + # n = 0 | ||
417 | + # for group in group_list: | ||
418 | + # info = BitmapInfo((group.dicom.patient.id, | ||
419 | + # group.dicom.acquisition.serie_number), | ||
420 | + # group.dicom, | ||
421 | + # group.title, | ||
422 | + # _("%d images") %(group.nslices)) | ||
423 | + # self.files.append(info) | ||
424 | + # n+=1 | ||
425 | + # scroll_range = len(self.files)/NCOLS | ||
426 | + # if scroll_range * NCOLS < len(self.files): | ||
427 | + # scroll_range +=1 | ||
428 | + # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | ||
429 | + # self._display_previews() | ||
430 | + | ||
431 | + | ||
432 | + def _display_previews(self): | ||
433 | + initial = self.displayed_position * NCOLS | ||
434 | + final = initial + NUM_PREVIEWS | ||
435 | + if len(self.files) < final: | ||
436 | + for i in xrange(final-len(self.files)): | ||
437 | + try: | ||
438 | + self.previews[-i-1].Hide() | ||
439 | + except IndexError: | ||
440 | + utils.debug("doesn't exist!") | ||
441 | + pass | ||
442 | + self.nhidden_last_display = final-len(self.files) | ||
443 | + else: | ||
444 | + if self.nhidden_last_display: | ||
445 | + for i in xrange(self.nhidden_last_display): | ||
446 | + try: | ||
447 | + self.previews[-i-1].Show() | ||
448 | + except IndexError: | ||
449 | + utils.debug("doesn't exist!") | ||
450 | + pass | ||
451 | + self.nhidden_last_display = 0 | ||
452 | + | ||
453 | + for f, p in zip(self.files[initial:final], self.previews): | ||
454 | + p.SetBitmapToPreview(f) | ||
455 | + if f.selected: | ||
456 | + self.selected_panel = p | ||
457 | + | ||
458 | + for f, p in zip(self.files[initial:final], self.previews): | ||
459 | + p.Show() | ||
460 | + | ||
461 | + | ||
462 | + def OnScroll(self, evt=None): | ||
463 | + if evt: | ||
464 | + if self.displayed_position != evt.GetPosition(): | ||
465 | + self.displayed_position = evt.GetPosition() | ||
466 | + else: | ||
467 | + if self.displayed_position != self.scroll.GetThumbPosition(): | ||
468 | + self.displayed_position = self.scroll.GetThumbPosition() | ||
469 | + self._display_previews() | ||
470 | + | ||
471 | + def OnWheel(self, evt): | ||
472 | + d = evt.GetWheelDelta() / evt.GetWheelRotation() | ||
473 | + self.scroll.SetThumbPosition(self.scroll.GetThumbPosition() - d) | ||
474 | + self.OnScroll() | ||
475 | + | ||
476 | + | ||
477 | +class SingleImagePreview(wx.Panel): | ||
478 | + def __init__(self, parent): | ||
479 | + wx.Panel.__init__(self, parent, -1) | ||
480 | + self.actor = None | ||
481 | + self.__init_gui() | ||
482 | + self.__init_vtk() | ||
483 | + self.__bind_evt_gui() | ||
484 | + self.__bind_pubsub() | ||
485 | + self.dicom_list = [] | ||
486 | + self.nimages = 1 | ||
487 | + self.current_index = 0 | ||
488 | + self.window_width = const.WINDOW_LEVEL[_("Bone")][0] | ||
489 | + self.window_level = const.WINDOW_LEVEL[_("Bone")][1] | ||
490 | + | ||
491 | + def __init_vtk(self): | ||
492 | + text_image_size = vtku.Text() | ||
493 | + text_image_size.SetPosition(const.TEXT_POS_LEFT_UP) | ||
494 | + text_image_size.SetValue("") | ||
495 | + text_image_size.SetSize(const.TEXT_SIZE_SMALL) | ||
496 | + self.text_image_size = text_image_size | ||
497 | + | ||
498 | + text_image_location = vtku.Text() | ||
499 | + text_image_location.SetVerticalJustificationToBottom() | ||
500 | + text_image_location.SetPosition(const.TEXT_POS_LEFT_DOWN) | ||
501 | + text_image_location.SetValue("") | ||
502 | + text_image_location.SetSize(const.TEXT_SIZE_SMALL) | ||
503 | + self.text_image_location = text_image_location | ||
504 | + | ||
505 | + text_patient = vtku.Text() | ||
506 | + text_patient.SetJustificationToRight() | ||
507 | + text_patient.SetPosition(const.TEXT_POS_RIGHT_UP) | ||
508 | + text_patient.SetValue("") | ||
509 | + text_patient.SetSize(const.TEXT_SIZE_SMALL) | ||
510 | + self.text_patient = text_patient | ||
511 | + | ||
512 | + text_acquisition = vtku.Text() | ||
513 | + text_acquisition.SetJustificationToRight() | ||
514 | + text_acquisition.SetVerticalJustificationToBottom() | ||
515 | + text_acquisition.SetPosition(const.TEXT_POS_RIGHT_DOWN) | ||
516 | + text_acquisition.SetValue("") | ||
517 | + text_acquisition.SetSize(const.TEXT_SIZE_SMALL) | ||
518 | + self.text_acquisition = text_acquisition | ||
519 | + | ||
520 | + renderer = vtk.vtkRenderer() | ||
521 | + renderer.AddActor(text_image_size.actor) | ||
522 | + renderer.AddActor(text_image_location.actor) | ||
523 | + renderer.AddActor(text_patient.actor) | ||
524 | + renderer.AddActor(text_acquisition.actor) | ||
525 | + self.renderer = renderer | ||
526 | + | ||
527 | + style = vtk.vtkInteractorStyleImage() | ||
528 | + | ||
529 | + interactor = wxVTKRenderWindowInteractor(self.panel, -1, | ||
530 | + size=wx.Size(340,340)) | ||
531 | + interactor.GetRenderWindow().AddRenderer(renderer) | ||
532 | + interactor.SetInteractorStyle(style) | ||
533 | + interactor.Render() | ||
534 | + self.interactor = interactor | ||
535 | + | ||
536 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
537 | + sizer.Add(interactor, 1, wx.GROW|wx.EXPAND) | ||
538 | + sizer.Fit(self.panel) | ||
539 | + self.panel.SetSizer(sizer) | ||
540 | + self.Layout() | ||
541 | + self.Update() | ||
542 | + | ||
543 | + def __init_gui(self): | ||
544 | + self.panel = wx.Panel(self, -1) | ||
545 | + | ||
546 | + slider = wx.Slider(self, | ||
547 | + id=-1, | ||
548 | + value=0, | ||
549 | + minValue=0, | ||
550 | + maxValue=99, | ||
551 | + style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS) | ||
552 | + slider.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) | ||
553 | + slider.SetTickFreq(1, 1) | ||
554 | + self.slider = slider | ||
555 | + | ||
556 | + checkbox = wx.CheckBox(self, -1, _("Auto-play")) | ||
557 | + self.checkbox = checkbox | ||
558 | + | ||
559 | + in_sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
560 | + in_sizer.Add(slider, 1, wx.GROW|wx.EXPAND) | ||
561 | + in_sizer.Add(checkbox, 0) | ||
562 | + | ||
563 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
564 | + sizer.Add(self.panel, 20, wx.GROW|wx.EXPAND) | ||
565 | + sizer.Add(in_sizer, 1, wx.GROW|wx.EXPAND) | ||
566 | + sizer.Fit(self) | ||
567 | + | ||
568 | + self.SetSizer(sizer) | ||
569 | + self.Layout() | ||
570 | + self.Update() | ||
571 | + self.SetAutoLayout(1) | ||
572 | + | ||
573 | + def __bind_evt_gui(self): | ||
574 | + self.slider.Bind(wx.EVT_SLIDER, self.OnSlider) | ||
575 | + self.checkbox.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) | ||
576 | + | ||
577 | + def __bind_pubsub(self): | ||
578 | + Publisher.subscribe(self.ShowBitmapByPosition, 'Set bitmap in preview panel') | ||
579 | + | ||
580 | + def ShowBitmapByPosition(self, evt): | ||
581 | + pos = evt.data | ||
582 | + self.ShowSlice(pos) | ||
583 | + | ||
584 | + | ||
585 | + def OnSlider(self, evt): | ||
586 | + pos = evt.GetInt() | ||
587 | + self.ShowSlice(pos) | ||
588 | + evt.Skip() | ||
589 | + | ||
590 | + def OnCheckBox(self, evt): | ||
591 | + self.ischecked = evt.IsChecked() | ||
592 | + if evt.IsChecked(): | ||
593 | + wx.CallAfter(self.OnRun) | ||
594 | + evt.Skip() | ||
595 | + | ||
596 | + def OnRun(self): | ||
597 | + pos = self.slider.GetValue() | ||
598 | + pos += 1 | ||
599 | + if not (self.nimages- pos): | ||
600 | + pos = 0 | ||
601 | + self.slider.SetValue(pos) | ||
602 | + self.ShowSlice(pos) | ||
603 | + time.sleep(0.2) | ||
604 | + if self.ischecked: | ||
605 | + try: | ||
606 | + wx.Yield() | ||
607 | + #TODO: temporary fix necessary in the Windows XP 64 Bits | ||
608 | + #BUG in wxWidgets http://trac.wxwidgets.org/ticket/10896 | ||
609 | + except(wx._core.PyAssertionError): | ||
610 | + utils.debug("wx._core.PyAssertionError") | ||
611 | + finally: | ||
612 | + wx.CallAfter(self.OnRun) | ||
613 | + | ||
614 | + def SetBitmapFiles(self, data): | ||
615 | + #self.dicom_list = group.GetHandSortedList() | ||
616 | + self.bitmap_list = data | ||
617 | + self.current_index = 0 | ||
618 | + self.nimages = len(data) | ||
619 | + # GUI | ||
620 | + self.slider.SetMax(self.nimages-1) | ||
621 | + self.slider.SetValue(0) | ||
622 | + self.ShowSlice() | ||
623 | + | ||
624 | + def ShowSlice(self, index = 0): | ||
625 | + bitmap = self.bitmap_list[index] | ||
626 | + | ||
627 | + # UPDATE GUI | ||
628 | + ## Text related to size | ||
629 | + value = STR_SIZE %(bitmap[3], bitmap[4]) | ||
630 | + self.text_image_size.SetValue(value) | ||
631 | + | ||
632 | + value1 = '' | ||
633 | + value2 = '' | ||
634 | + | ||
635 | + value = "%s\n%s" %(value1, value2) | ||
636 | + self.text_image_location.SetValue(value) | ||
637 | + | ||
638 | + | ||
639 | + #self.text_patient.SetValue(value) | ||
640 | + self.text_patient.SetValue('') | ||
641 | + | ||
642 | + #self.text_acquisition.SetValue(value) | ||
643 | + self.text_acquisition.SetValue('') | ||
644 | + | ||
645 | + | ||
646 | + | ||
647 | + n_array = bitmap_reader.ReadBitmap(bitmap[0]) | ||
648 | + | ||
649 | + image = converters.to_vtk(n_array, spacing=(1,1,1),\ | ||
650 | + slice_number=1, orientation="AXIAL") | ||
651 | + | ||
652 | + | ||
653 | + # ADJUST CONTRAST | ||
654 | + window_level = n_array.max()/2 | ||
655 | + window_width = n_array.max() | ||
656 | + | ||
657 | + colorer = vtk.vtkImageMapToWindowLevelColors() | ||
658 | + colorer.SetInputData(image) | ||
659 | + colorer.SetWindow(float(window_width)) | ||
660 | + colorer.SetLevel(float(window_level)) | ||
661 | + colorer.Update() | ||
662 | + | ||
663 | + if self.actor is None: | ||
664 | + self.actor = vtk.vtkImageActor() | ||
665 | + self.renderer.AddActor(self.actor) | ||
666 | + | ||
667 | + # PLOT IMAGE INTO VIEWER | ||
668 | + self.actor.SetInputData(colorer.GetOutput()) | ||
669 | + self.renderer.ResetCamera() | ||
670 | + self.interactor.Render() | ||
671 | + | ||
672 | + # Setting slider position | ||
673 | + self.slider.SetValue(index) | ||
674 | + | ||
675 | + | ||
676 | +#class BitmapPreviewSlice(wx.Panel): | ||
677 | +# def __init__(self, parent): | ||
678 | +# super(BitmapPreviewSlice, self).__init__(parent) | ||
679 | +# # TODO: 3 pixels between the previews is a good idea? | ||
680 | +# # I have to test. | ||
681 | +# self.displayed_position = 0 | ||
682 | +# self.nhidden_last_display = 0 | ||
683 | +# self.selected_dicom = None | ||
684 | +# self.selected_panel = None | ||
685 | +# self.first_selection = None | ||
686 | +# self.last_selection = None | ||
687 | +# self._init_ui() | ||
688 | +# | ||
689 | +# def _init_ui(self): | ||
690 | +# scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) | ||
691 | +# self.scroll = scroll | ||
692 | +# | ||
693 | +# self.grid = wx.GridSizer(rows=NROWS, cols=NCOLS, vgap=3, hgap=3) | ||
694 | +# | ||
695 | +# sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
696 | +# sizer.AddSizer(self.grid, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | ||
697 | +# | ||
698 | +# background_sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
699 | +# background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | ||
700 | +# background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | ||
701 | +# self.SetSizer(background_sizer) | ||
702 | +# background_sizer.Fit(self) | ||
703 | +# | ||
704 | +# self.Layout() | ||
705 | +# self.Update() | ||
706 | +# self.SetAutoLayout(1) | ||
707 | +# | ||
708 | +# self.sizer = background_sizer | ||
709 | +# | ||
710 | +# self._Add_Panels_Preview() | ||
711 | +# self._bind_events() | ||
712 | +# | ||
713 | +# def _Add_Panels_Preview(self): | ||
714 | +# self.previews = [] | ||
715 | +# for i in xrange(NROWS): | ||
716 | +# for j in xrange(NCOLS): | ||
717 | +# p = Preview(self) | ||
718 | +# p.Bind(EVT_PREVIEW_CLICK, self.OnPreviewClick) | ||
719 | +# #p.Hide() | ||
720 | +# self.previews.append(p) | ||
721 | +# self.grid.Add(p, 1, flag=wx.EXPAND) | ||
722 | +# | ||
723 | +# def _bind_events(self): | ||
724 | +# # When the user scrolls the window | ||
725 | +# self.Bind(wx.EVT_SCROLL, self.OnScroll) | ||
726 | +# self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel) | ||
727 | +# | ||
728 | +# def SetDicomDirectory(self, directory): | ||
729 | +# utils.debug("Setting Dicom Directory %s" % directory) | ||
730 | +# self.directory = directory | ||
731 | +# self.series = dicom_reader.GetSeries(directory)[0] | ||
732 | +# | ||
733 | +# def SetPatientGroups(self, patient): | ||
734 | +# self.group_list = patient.GetGroups() | ||
735 | +# | ||
736 | +# | ||
737 | +# #def SetDicomSerie(self, pos): | ||
738 | +# # self.files = [] | ||
739 | +# # self.displayed_position = 0 | ||
740 | +# # self.nhidden_last_display = 0 | ||
741 | +# # group = self.group_list[pos] | ||
742 | +# # self.group = group | ||
743 | +# # #dicom_files = group.GetList() | ||
744 | +# # dicom_files = group.GetHandSortedList() | ||
745 | +# # n = 0 | ||
746 | +# # for dicom in dicom_files: | ||
747 | +# # info = BitmapInfo(n, dicom, | ||
748 | +# # _("Image %d") % (dicom.image.number), | ||
749 | +# # "%.2f" % (dicom.image.position[2])) | ||
750 | +# # self.files.append(info) | ||
751 | +# # n+=1 | ||
752 | +# | ||
753 | +# # scroll_range = len(self.files)/NCOLS | ||
754 | +# # if scroll_range * NCOLS < len(self.files): | ||
755 | +# # scroll_range +=1 | ||
756 | +# # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | ||
757 | +# | ||
758 | +# # self._display_previews() | ||
759 | +# | ||
760 | +# #def SetDicomGroup(self, group): | ||
761 | +# # self.files = [] | ||
762 | +# # self.displayed_position = 0 | ||
763 | +# # self.nhidden_last_display = 0 | ||
764 | +# # #dicom_files = group.GetList() | ||
765 | +# # dicom_files = group.GetHandSortedList() | ||
766 | +# # n = 0 | ||
767 | +# # for dicom in dicom_files: | ||
768 | +# # info = BitmapInfo(n, dicom, | ||
769 | +# # _("Image %d") % (dicom.image.number), | ||
770 | +# # "%.2f" % (dicom.image.position[2]), | ||
771 | +# # ) | ||
772 | +# # self.files.append(info) | ||
773 | +# # n+=1 | ||
774 | +# | ||
775 | +# # scroll_range = len(self.files)/NCOLS | ||
776 | +# # if scroll_range * NCOLS < len(self.files): | ||
777 | +# # scroll_range +=1 | ||
778 | +# # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | ||
779 | +# | ||
780 | +# # self._display_previews() | ||
781 | +# | ||
782 | +# #def SetDicomGroup(self, group): | ||
783 | +# # self.files = [] | ||
784 | +# # self.displayed_position = 0 | ||
785 | +# # self.nhidden_last_display = 0 | ||
786 | +# # #dicom_files = group.GetList() | ||
787 | +# # dicom_files = group.GetHandSortedList() | ||
788 | +# # n = 0 | ||
789 | +# # for dicom in dicom_files: | ||
790 | +# # info = BitmapInfo(n, dicom, | ||
791 | +# # _("Image %d") % (dicom.image.number), | ||
792 | +# # "%.2f" % (dicom.image.position[2]), | ||
793 | +# # ) | ||
794 | +# # self.files.append(info) | ||
795 | +# # n+=1 | ||
796 | +# | ||
797 | +# # scroll_range = len(self.files)/NCOLS | ||
798 | +# # if scroll_range * NCOLS < len(self.files): | ||
799 | +# # scroll_range +=1 | ||
800 | +# # self.scroll.SetScrollbar(0, NROWS, scroll_range, NCOLS) | ||
801 | +# | ||
802 | +# # self._display_previews() | ||
803 | +# | ||
804 | +# | ||
805 | +# def _display_previews(self): | ||
806 | +# initial = self.displayed_position * NCOLS | ||
807 | +# final = initial + NUM_PREVIEWS | ||
808 | +# if len(self.files) < final: | ||
809 | +# for i in xrange(final-len(self.files)): | ||
810 | +# try: | ||
811 | +# self.previews[-i-1].Hide() | ||
812 | +# except IndexError: | ||
813 | +# utils.debug("doesn't exist!") | ||
814 | +# self.nhidden_last_display = final-len(self.files) | ||
815 | +# else: | ||
816 | +# if self.nhidden_last_display: | ||
817 | +# for i in xrange(self.nhidden_last_display): | ||
818 | +# try: | ||
819 | +# self.previews[-i-1].Show() | ||
820 | +# except IndexError: | ||
821 | +# utils.debug("doesn't exist!") | ||
822 | +# self.nhidden_last_display = 0 | ||
823 | +# | ||
824 | +# for f, p in zip(self.files[initial:final], self.previews): | ||
825 | +# p.SetBitmapToPreview(f) | ||
826 | +# if f.selected: | ||
827 | +# self.selected_panel = p | ||
828 | +# #p.interactor.Render() | ||
829 | +# | ||
830 | +# for f, p in zip(self.files[initial:final], self.previews): | ||
831 | +# p.Show() | ||
832 | +# | ||
833 | +# def OnPreviewClick(self, evt): | ||
834 | +# | ||
835 | +# dicom_id = evt.GetSelectID() | ||
836 | +# | ||
837 | +# if self.first_selection is None: | ||
838 | +# self.first_selection = dicom_id | ||
839 | +# | ||
840 | +# if self.last_selection is None: | ||
841 | +# self.last_selection = dicom_id | ||
842 | +# | ||
843 | +# | ||
844 | +# if evt.GetPressedShift(): | ||
845 | +# | ||
846 | +# if dicom_id < self.first_selection and dicom_id < self.last_selection: | ||
847 | +# self.first_selection = dicom_id | ||
848 | +# else: | ||
849 | +# self.last_selection = dicom_id | ||
850 | +# else: | ||
851 | +# self.first_selection = dicom_id | ||
852 | +# self.last_selection = dicom_id | ||
853 | +# | ||
854 | +# for i in xrange(len(self.files)): | ||
855 | +# | ||
856 | +# if i == dicom_id: | ||
857 | +# self.files[i].selected = True | ||
858 | +# else: | ||
859 | +# self.files[i].selected = False | ||
860 | +# | ||
861 | +# | ||
862 | +# my_evt = SerieEvent(myEVT_CLICK_SLICE, self.GetId()) | ||
863 | +# my_evt.SetSelectedID(evt.GetSelectID()) | ||
864 | +# my_evt.SetItemData(evt.GetItemData()) | ||
865 | +# | ||
866 | +# if self.selected_dicom: | ||
867 | +# self.selected_dicom.selected = self.selected_dicom is \ | ||
868 | +# evt.GetEventObject().bitmap_info | ||
869 | +# self.selected_panel.select_on = self.selected_panel is evt.GetEventObject() | ||
870 | +# | ||
871 | +# if self.first_selection != self.last_selection: | ||
872 | +# for i in xrange(len(self.files)): | ||
873 | +# if i >= self.first_selection and i <= self.last_selection: | ||
874 | +# self.files[i].selected = True | ||
875 | +# else: | ||
876 | +# self.files[i].selected = False | ||
877 | +# | ||
878 | +# else: | ||
879 | +# self.selected_panel.Select() | ||
880 | +# | ||
881 | +# self._display_previews() | ||
882 | +# self.selected_panel = evt.GetEventObject() | ||
883 | +# self.selected_dicom = self.selected_panel.bitmap_info | ||
884 | +# self.GetEventHandler().ProcessEvent(my_evt) | ||
885 | +# | ||
886 | +# #Publisher.sendMessage("Selected Import Images", [self.first_selection, \ | ||
887 | +# # self.last_selection]) | ||
888 | +# | ||
889 | +# def OnScroll(self, evt=None): | ||
890 | +# if evt: | ||
891 | +# if self.displayed_position != evt.GetPosition(): | ||
892 | +# self.displayed_position = evt.GetPosition() | ||
893 | +# else: | ||
894 | +# if self.displayed_position != self.scroll.GetThumbPosition(): | ||
895 | +# self.displayed_position = self.scroll.GetThumbPosition() | ||
896 | +# self._display_previews() | ||
897 | +# | ||
898 | +# def OnWheel(self, evt): | ||
899 | +# d = evt.GetWheelDelta() / evt.GetWheelRotation() | ||
900 | +# self.scroll.SetThumbPosition(self.scroll.GetThumbPosition() - d) | ||
901 | +# self.OnScroll() | ||
902 | + | ||
903 | + |
invesalius/gui/dialogs.py
@@ -39,6 +39,12 @@ from gui.widgets.clut_imagedata import CLUTImageDataWidget, EVT_CLUT_NODE_CHANGE | @@ -39,6 +39,12 @@ from gui.widgets.clut_imagedata import CLUTImageDataWidget, EVT_CLUT_NODE_CHANGE | ||
39 | 39 | ||
40 | import numpy as np | 40 | import numpy as np |
41 | 41 | ||
42 | +try: | ||
43 | + from agw import floatspin as FS | ||
44 | +except ImportError: # if it's not there locally, try the wxPython lib. | ||
45 | + import wx.lib.agw.floatspin as FS | ||
46 | + | ||
47 | + | ||
42 | class MaskEvent(wx.PyCommandEvent): | 48 | class MaskEvent(wx.PyCommandEvent): |
43 | def __init__(self , evtType, id, mask_index): | 49 | def __init__(self , evtType, id, mask_index): |
44 | wx.PyCommandEvent.__init__(self, evtType, id,) | 50 | wx.PyCommandEvent.__init__(self, evtType, id,) |
@@ -306,6 +312,48 @@ def ShowImportDirDialog(): | @@ -306,6 +312,48 @@ def ShowImportDirDialog(): | ||
306 | os.chdir(current_dir) | 312 | os.chdir(current_dir) |
307 | return path | 313 | return path |
308 | 314 | ||
315 | +def ShowImportBitmapDirDialog(): | ||
316 | + current_dir = os.path.abspath(".") | ||
317 | + | ||
318 | + if (sys.platform == 'win32') or (sys.platform == 'linux2'): | ||
319 | + session = ses.Session() | ||
320 | + | ||
321 | + if (session.GetLastDicomFolder()): | ||
322 | + folder = session.GetLastDicomFolder() | ||
323 | + else: | ||
324 | + folder = '' | ||
325 | + else: | ||
326 | + folder = '' | ||
327 | + | ||
328 | + dlg = wx.DirDialog(None, _("Choose a folder with TIFF, BMP, JPG or PNG:"), folder, | ||
329 | + style=wx.DD_DEFAULT_STYLE | ||
330 | + | wx.DD_DIR_MUST_EXIST | ||
331 | + | wx.DD_CHANGE_DIR) | ||
332 | + | ||
333 | + path = None | ||
334 | + try: | ||
335 | + if dlg.ShowModal() == wx.ID_OK: | ||
336 | + # GetPath returns in unicode, if a path has non-ascii characters a | ||
337 | + # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 | ||
338 | + if sys.platform == "win32": | ||
339 | + path = dlg.GetPath() | ||
340 | + else: | ||
341 | + path = dlg.GetPath().encode('utf-8') | ||
342 | + | ||
343 | + except(wx._core.PyAssertionError): #TODO: error win64 | ||
344 | + if (dlg.GetPath()): | ||
345 | + path = dlg.GetPath() | ||
346 | + | ||
347 | + if (sys.platform != 'darwin'): | ||
348 | + if (path): | ||
349 | + session.SetLastDicomFolder(path) | ||
350 | + | ||
351 | + # Only destroy a dialog after you're done with it. | ||
352 | + dlg.Destroy() | ||
353 | + os.chdir(current_dir) | ||
354 | + return path | ||
355 | + | ||
356 | + | ||
309 | def ShowSaveAsProjectDialog(default_filename=None): | 357 | def ShowSaveAsProjectDialog(default_filename=None): |
310 | current_dir = os.path.abspath(".") | 358 | current_dir = os.path.abspath(".") |
311 | dlg = wx.FileDialog(None, | 359 | dlg = wx.FileDialog(None, |
@@ -1630,3 +1678,114 @@ class ReorientImageDialog(wx.Dialog): | @@ -1630,3 +1678,114 @@ class ReorientImageDialog(wx.Dialog): | ||
1630 | Publisher.sendMessage('Disable style', const.SLICE_STATE_REORIENT) | 1678 | Publisher.sendMessage('Disable style', const.SLICE_STATE_REORIENT) |
1631 | Publisher.sendMessage('Enable style', const.STATE_DEFAULT) | 1679 | Publisher.sendMessage('Enable style', const.STATE_DEFAULT) |
1632 | self.Destroy() | 1680 | self.Destroy() |
1681 | + | ||
1682 | + | ||
1683 | + | ||
1684 | +class ImportBitmapParameters(wx.Dialog): | ||
1685 | + | ||
1686 | + def __init__(self): | ||
1687 | + pre = wx.PreDialog() | ||
1688 | + pre.Create(wx.GetApp().GetTopWindow(), -1, _(u"Parameters"),size=wx.Size(380,230),\ | ||
1689 | + style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) | ||
1690 | + | ||
1691 | + self.interval = 0 | ||
1692 | + | ||
1693 | + self.PostCreate(pre) | ||
1694 | + | ||
1695 | + self._init_gui() | ||
1696 | + | ||
1697 | + self.bind_evts() | ||
1698 | + self.CenterOnScreen() | ||
1699 | + | ||
1700 | + | ||
1701 | + def _init_gui(self): | ||
1702 | + | ||
1703 | + | ||
1704 | + p = wx.Panel(self, -1, style = wx.TAB_TRAVERSAL | ||
1705 | + | wx.CLIP_CHILDREN | ||
1706 | + | wx.FULL_REPAINT_ON_RESIZE) | ||
1707 | + | ||
1708 | + gbs_principal = self.gbs = wx.GridBagSizer(3,1) | ||
1709 | + | ||
1710 | + gbs = self.gbs = wx.GridBagSizer(4, 2) | ||
1711 | + | ||
1712 | + stx_name = wx.StaticText(p, -1, _(u"Project name:")) | ||
1713 | + tx_name = self.tx_name = wx.TextCtrl(p, -1, "InVesalius Bitmap", size=wx.Size(220,-1)) | ||
1714 | + | ||
1715 | + stx_orientation = wx.StaticText(p, -1, _(u"Slices orientation:")) | ||
1716 | + cb_orientation_options = [_(u'Axial'), _(u'Coronal'), _(u'Sagital')] | ||
1717 | + cb_orientation = self.cb_orientation = wx.ComboBox(p, value="Axial", choices=cb_orientation_options,\ | ||
1718 | + size=wx.Size(160,-1), style=wx.CB_DROPDOWN|wx.CB_READONLY) | ||
1719 | + | ||
1720 | + stx_spacing = wx.StaticText(p, -1, _(u"Spacing (mm):")) | ||
1721 | + | ||
1722 | + gbs.Add(stx_name, (0,0)) | ||
1723 | + gbs.Add(tx_name, (0,1)) | ||
1724 | + | ||
1725 | + gbs.Add(stx_orientation, (1,0)) | ||
1726 | + gbs.Add(cb_orientation, (1,1)) | ||
1727 | + | ||
1728 | + gbs.Add(stx_spacing, (2,0)) | ||
1729 | + | ||
1730 | + #--- spacing -------------- | ||
1731 | + gbs_spacing = wx.GridBagSizer(2, 6) | ||
1732 | + | ||
1733 | + 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,\ | ||
1735 | + increment=0.25, value=1.0, digits=6) | ||
1736 | + | ||
1737 | + 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,\ | ||
1739 | + increment=0.25, value=1.0, digits=6) | ||
1740 | + | ||
1741 | + 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,\ | ||
1743 | + increment=0.25, value=1.0, digits=6) | ||
1744 | + | ||
1745 | + gbs_spacing.Add(stx_spacing_x, (0,0)) | ||
1746 | + gbs_spacing.Add(fsp_spacing_x, (0,1)) | ||
1747 | + | ||
1748 | + gbs_spacing.Add(stx_spacing_y, (0,2)) | ||
1749 | + gbs_spacing.Add(fsp_spacing_y, (0,3)) | ||
1750 | + | ||
1751 | + gbs_spacing.Add(stx_spacing_z, (0,4)) | ||
1752 | + gbs_spacing.Add(fsp_spacing_z, (0,5)) | ||
1753 | + | ||
1754 | + #----- buttons ------------------------ | ||
1755 | + gbs_button = wx.GridBagSizer(1, 4) | ||
1756 | + | ||
1757 | + btn_ok = self.btn_ok= wx.Button(p, wx.ID_OK) | ||
1758 | + btn_ok.SetDefault() | ||
1759 | + | ||
1760 | + btn_cancel = wx.Button(p, wx.ID_CANCEL) | ||
1761 | + | ||
1762 | + gbs_button.Add(btn_cancel, (1,2)) | ||
1763 | + gbs_button.Add(btn_ok, (1,3)) | ||
1764 | + | ||
1765 | + | ||
1766 | + gbs_principal.Add(gbs, (0,0)) | ||
1767 | + gbs_principal.Add(gbs_spacing, (1,0)) | ||
1768 | + gbs_principal.Add(gbs_button, (2,0), flag = wx.ALIGN_RIGHT) | ||
1769 | + | ||
1770 | + box = wx.BoxSizer() | ||
1771 | + box.Add(gbs_principal, 1, wx.ALL|wx.EXPAND, 10) | ||
1772 | + | ||
1773 | + p.SetSizer(box) | ||
1774 | + | ||
1775 | + | ||
1776 | + def bind_evts(self): | ||
1777 | + self.btn_ok.Bind(wx.EVT_BUTTON, self.OnOk) | ||
1778 | + | ||
1779 | + def SetInterval(self, v): | ||
1780 | + self.interval = v | ||
1781 | + | ||
1782 | + def OnOk(self, evt): | ||
1783 | + self.Close() | ||
1784 | + self.Destroy() | ||
1785 | + | ||
1786 | + values = [self.tx_name.GetValue(), self.cb_orientation.GetValue().upper(),\ | ||
1787 | + self.fsp_spacing_x.GetValue(), self.fsp_spacing_y.GetValue(),\ | ||
1788 | + self.fsp_spacing_z.GetValue(), self.interval] | ||
1789 | + Publisher.sendMessage('Open bitmap files', values) | ||
1790 | + | ||
1791 | + |
invesalius/gui/frame.py
@@ -36,6 +36,7 @@ import default_tasks as tasks | @@ -36,6 +36,7 @@ import default_tasks as tasks | ||
36 | import default_viewers as viewers | 36 | import default_viewers as viewers |
37 | import gui.dialogs as dlg | 37 | import gui.dialogs as dlg |
38 | import import_panel as imp | 38 | import import_panel as imp |
39 | +import import_bitmap_panel as imp_bmp | ||
39 | import import_network_panel as imp_net | 40 | import import_network_panel as imp_net |
40 | import project as prj | 41 | import project as prj |
41 | import session as ses | 42 | import session as ses |
@@ -126,6 +127,7 @@ class Frame(wx.Frame): | @@ -126,6 +127,7 @@ class Frame(wx.Frame): | ||
126 | sub(self._ShowImportPanel, 'Show import panel in frame') | 127 | sub(self._ShowImportPanel, 'Show import panel in frame') |
127 | #sub(self._ShowHelpMessage, 'Show help message') | 128 | #sub(self._ShowHelpMessage, 'Show help message') |
128 | sub(self._ShowImportNetwork, 'Show retrieve dicom panel') | 129 | sub(self._ShowImportNetwork, 'Show retrieve dicom panel') |
130 | + sub(self._ShowImportBitmap, 'Show import bitmap panel in frame') | ||
129 | sub(self._ShowTask, 'Show task panel') | 131 | sub(self._ShowTask, 'Show task panel') |
130 | sub(self._UpdateAUI, 'Update AUI') | 132 | sub(self._UpdateAUI, 'Update AUI') |
131 | sub(self._Exit, 'Exit') | 133 | sub(self._Exit, 'Exit') |
@@ -170,8 +172,14 @@ class Frame(wx.Frame): | @@ -170,8 +172,14 @@ class Frame(wx.Frame): | ||
170 | # are shown, this should be hiden | 172 | # are shown, this should be hiden |
171 | caption = _("Preview medical data to be reconstructed") | 173 | caption = _("Preview medical data to be reconstructed") |
172 | aui_manager.AddPane(imp.Panel(self), wx.aui.AuiPaneInfo(). | 174 | aui_manager.AddPane(imp.Panel(self), wx.aui.AuiPaneInfo(). |
173 | - Name("Import").Centre().Hide(). | ||
174 | - MaximizeButton(True).Floatable(True). | 175 | + Name("Import").CloseButton(False).Centre().Hide(). |
176 | + MaximizeButton(False).Floatable(True). | ||
177 | + Caption(caption).CaptionVisible(True)) | ||
178 | + | ||
179 | + caption = _("Preview bitmap to be reconstructed") | ||
180 | + aui_manager.AddPane(imp_bmp.Panel(self), wx.aui.AuiPaneInfo(). | ||
181 | + Name("ImportBMP").CloseButton(False).Centre().Hide(). | ||
182 | + MaximizeButton(False).Floatable(True). | ||
175 | Caption(caption).CaptionVisible(True)) | 183 | Caption(caption).CaptionVisible(True)) |
176 | 184 | ||
177 | ncaption = _("Retrieve DICOM from PACS") | 185 | ncaption = _("Retrieve DICOM from PACS") |
@@ -298,6 +306,9 @@ class Frame(wx.Frame): | @@ -298,6 +306,9 @@ class Frame(wx.Frame): | ||
298 | Publisher.sendMessage("Set layout button full") | 306 | Publisher.sendMessage("Set layout button full") |
299 | aui_manager = self.aui_manager | 307 | aui_manager = self.aui_manager |
300 | aui_manager.GetPane("Import").Show(0) | 308 | aui_manager.GetPane("Import").Show(0) |
309 | + | ||
310 | + aui_manager.GetPane("ImportBMP").Show(0) | ||
311 | + | ||
301 | aui_manager.GetPane("Data").Show(1) | 312 | aui_manager.GetPane("Data").Show(1) |
302 | aui_manager.GetPane("Tasks").Show(1) | 313 | aui_manager.GetPane("Tasks").Show(1) |
303 | aui_manager.Update() | 314 | aui_manager.Update() |
@@ -314,6 +325,18 @@ class Frame(wx.Frame): | @@ -314,6 +325,18 @@ class Frame(wx.Frame): | ||
314 | aui_manager.GetPane("Import").Show(0) | 325 | aui_manager.GetPane("Import").Show(0) |
315 | aui_manager.Update() | 326 | aui_manager.Update() |
316 | 327 | ||
328 | + def _ShowImportBitmap(self, evt_pubsub): | ||
329 | + """ | ||
330 | + Show viewers and task, hide import panel. | ||
331 | + """ | ||
332 | + Publisher.sendMessage("Set layout button full") | ||
333 | + aui_manager = self.aui_manager | ||
334 | + aui_manager.GetPane("ImportBMP").Show(1) | ||
335 | + aui_manager.GetPane("Data").Show(0) | ||
336 | + aui_manager.GetPane("Tasks").Show(0) | ||
337 | + aui_manager.GetPane("Import").Show(0) | ||
338 | + aui_manager.Update() | ||
339 | + | ||
317 | def _ShowHelpMessage(self, evt_pubsub): | 340 | def _ShowHelpMessage(self, evt_pubsub): |
318 | aui_manager = self.aui_manager | 341 | aui_manager = self.aui_manager |
319 | pos = aui_manager.GetPane("Data").window.GetScreenPosition() | 342 | pos = aui_manager.GetPane("Data").window.GetScreenPosition() |
@@ -371,6 +394,8 @@ class Frame(wx.Frame): | @@ -371,6 +394,8 @@ class Frame(wx.Frame): | ||
371 | self.ShowOpenProject() | 394 | self.ShowOpenProject() |
372 | elif id == const.ID_ANALYZE_IMPORT: | 395 | elif id == const.ID_ANALYZE_IMPORT: |
373 | self.ShowAnalyzeImporter() | 396 | self.ShowAnalyzeImporter() |
397 | + elif id == const.ID_TIFF_JPG_PNG: | ||
398 | + self.ShowBitmapImporter() | ||
374 | elif id == const.ID_PROJECT_SAVE: | 399 | elif id == const.ID_PROJECT_SAVE: |
375 | session = ses.Session() | 400 | session = ses.Session() |
376 | if session.temp_item: | 401 | if session.temp_item: |
@@ -501,6 +526,12 @@ class Frame(wx.Frame): | @@ -501,6 +526,12 @@ class Frame(wx.Frame): | ||
501 | """ | 526 | """ |
502 | Publisher.sendMessage('Show analyze dialog', True) | 527 | Publisher.sendMessage('Show analyze dialog', True) |
503 | 528 | ||
529 | + def ShowBitmapImporter(self): | ||
530 | + """ | ||
531 | + Tiff, BMP, JPEG and PNG | ||
532 | + """ | ||
533 | + Publisher.sendMessage('Show bitmap dialog', True) | ||
534 | + | ||
504 | def FlipVolume(self, axis): | 535 | def FlipVolume(self, axis): |
505 | Publisher.sendMessage('Flip volume', axis) | 536 | Publisher.sendMessage('Flip volume', axis) |
506 | Publisher.sendMessage('Reload actual slice') | 537 | Publisher.sendMessage('Reload actual slice') |
@@ -581,6 +612,7 @@ class MenuBar(wx.MenuBar): | @@ -581,6 +612,7 @@ class MenuBar(wx.MenuBar): | ||
581 | #Import Others Files | 612 | #Import Others Files |
582 | others_file_menu = wx.Menu() | 613 | others_file_menu = wx.Menu() |
583 | 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") | ||
584 | 616 | ||
585 | # FILE | 617 | # FILE |
586 | file_menu = wx.Menu() | 618 | file_menu = wx.Menu() |
@@ -0,0 +1,484 @@ | @@ -0,0 +1,484 @@ | ||
1 | +#-------------------------------------------------------------------------- | ||
2 | +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas | ||
3 | +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer | ||
4 | +# Homepage: http://www.softwarepublico.gov.br | ||
5 | +# Contact: invesalius@cti.gov.br | ||
6 | +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) | ||
7 | +#-------------------------------------------------------------------------- | ||
8 | +# Este programa e software livre; voce pode redistribui-lo e/ou | ||
9 | +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme | ||
10 | +# publicada pela Free Software Foundation; de acordo com a versao 2 | ||
11 | +# da Licenca. | ||
12 | +# | ||
13 | +# Este programa eh distribuido na expectativa de ser util, mas SEM | ||
14 | +# QUALQUER GARANTIA; sem mesmo a garantia implicita de | ||
15 | +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM | ||
16 | +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais | ||
17 | +# detalhes. | ||
18 | +#-------------------------------------------------------------------------- | ||
19 | +import wx | ||
20 | +import wx.gizmos as gizmos | ||
21 | +from wx.lib.pubsub import pub as Publisher | ||
22 | +import wx.lib.splitter as spl | ||
23 | + | ||
24 | +import constants as const | ||
25 | +import gui.dialogs as dlg | ||
26 | +import bitmap_preview_panel as bpp | ||
27 | +import reader.dicom_grouper as dcm | ||
28 | +from dialogs import ImportBitmapParameters | ||
29 | + | ||
30 | +myEVT_SELECT_SERIE = wx.NewEventType() | ||
31 | +EVT_SELECT_SERIE = wx.PyEventBinder(myEVT_SELECT_SERIE, 1) | ||
32 | + | ||
33 | +myEVT_SELECT_SLICE = wx.NewEventType() | ||
34 | +EVT_SELECT_SLICE = wx.PyEventBinder(myEVT_SELECT_SLICE, 1) | ||
35 | + | ||
36 | +myEVT_SELECT_PATIENT = wx.NewEventType() | ||
37 | +EVT_SELECT_PATIENT = wx.PyEventBinder(myEVT_SELECT_PATIENT, 1) | ||
38 | + | ||
39 | +myEVT_SELECT_SERIE_TEXT = wx.NewEventType() | ||
40 | +EVT_SELECT_SERIE_TEXT = wx.PyEventBinder(myEVT_SELECT_SERIE_TEXT, 1) | ||
41 | + | ||
42 | +class SelectEvent(wx.PyCommandEvent): | ||
43 | + def __init__(self , evtType, id): | ||
44 | + super(SelectEvent, self).__init__(evtType, id) | ||
45 | + | ||
46 | + def GetSelectID(self): | ||
47 | + return self.SelectedID | ||
48 | + | ||
49 | + def SetSelectedID(self, id): | ||
50 | + self.SelectedID = id | ||
51 | + | ||
52 | + def GetItemData(self): | ||
53 | + return self.data | ||
54 | + | ||
55 | + def SetItemData(self, data): | ||
56 | + self.data = data | ||
57 | + | ||
58 | + | ||
59 | +class Panel(wx.Panel): | ||
60 | + def __init__(self, parent): | ||
61 | + wx.Panel.__init__(self, parent, pos=wx.Point(5, 5))#, | ||
62 | + #size=wx.Size(280, 656)) | ||
63 | + | ||
64 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
65 | + sizer.Add(InnerPanel(self), 1, wx.EXPAND|wx.GROW|wx.ALL, 5) | ||
66 | + | ||
67 | + self.SetSizer(sizer) | ||
68 | + sizer.Fit(self) | ||
69 | + | ||
70 | + self.Layout() | ||
71 | + self.Update() | ||
72 | + self.SetAutoLayout(1) | ||
73 | + | ||
74 | + | ||
75 | +# Inner fold panel | ||
76 | +class InnerPanel(wx.Panel): | ||
77 | + def __init__(self, parent): | ||
78 | + wx.Panel.__init__(self, parent, pos=wx.Point(5, 5))#, | ||
79 | + #size=wx.Size(680, 656)) | ||
80 | + | ||
81 | + self.patients = [] | ||
82 | + self.first_image_selection = None | ||
83 | + self.last_image_selection = None | ||
84 | + self._init_ui() | ||
85 | + self._bind_events() | ||
86 | + self._bind_pubsubevt() | ||
87 | + | ||
88 | + def _init_ui(self): | ||
89 | + splitter = spl.MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE) | ||
90 | + splitter.SetOrientation(wx.VERTICAL) | ||
91 | + self.splitter = splitter | ||
92 | + | ||
93 | + panel = wx.Panel(self) | ||
94 | + self.btn_cancel = wx.Button(panel, wx.ID_CANCEL) | ||
95 | + self.btn_ok = wx.Button(panel, wx.ID_OK, _("Import")) | ||
96 | + | ||
97 | + btnsizer = wx.StdDialogButtonSizer() | ||
98 | + btnsizer.AddButton(self.btn_ok) | ||
99 | + btnsizer.AddButton(self.btn_cancel) | ||
100 | + btnsizer.Realize() | ||
101 | + | ||
102 | + self.combo_interval = wx.ComboBox(panel, -1, "", choices=const.IMPORT_INTERVAL, | ||
103 | + style=wx.CB_DROPDOWN|wx.CB_READONLY) | ||
104 | + self.combo_interval.SetSelection(0) | ||
105 | + | ||
106 | + inner_sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
107 | + inner_sizer.AddSizer(btnsizer, 0, wx.LEFT|wx.TOP, 5) | ||
108 | + inner_sizer.Add(self.combo_interval, 0, wx.LEFT|wx.RIGHT|wx.TOP, 5) | ||
109 | + panel.SetSizer(inner_sizer) | ||
110 | + inner_sizer.Fit(panel) | ||
111 | + | ||
112 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
113 | + sizer.Add(splitter, 20, wx.EXPAND) | ||
114 | + sizer.Add(panel, 0, wx.EXPAND|wx.LEFT, 90) | ||
115 | + | ||
116 | + self.text_panel = TextPanel(splitter) | ||
117 | + splitter.AppendWindow(self.text_panel, 250) | ||
118 | + | ||
119 | + self.image_panel = ImagePanel(splitter) | ||
120 | + splitter.AppendWindow(self.image_panel, 250) | ||
121 | + | ||
122 | + self.SetSizer(sizer) | ||
123 | + sizer.Fit(self) | ||
124 | + | ||
125 | + self.Layout() | ||
126 | + self.Update() | ||
127 | + self.SetAutoLayout(1) | ||
128 | + | ||
129 | + def _bind_pubsubevt(self): | ||
130 | + Publisher.subscribe(self.ShowBitmapPreview, "Load import bitmap panel") | ||
131 | + Publisher.subscribe(self.GetSelectedImages ,"Selected Import Images") | ||
132 | + | ||
133 | + def ShowBitmapPreview(self, pubsub_evt): | ||
134 | + data = pubsub_evt.data | ||
135 | + #self.patients.extend(dicom_groups) | ||
136 | + self.text_panel.Populate(data) | ||
137 | + | ||
138 | + def GetSelectedImages(self, pubsub_evt): | ||
139 | + self.first_image_selection = pubsub_evt.data[0] | ||
140 | + self.last_image_selection = pubsub_evt.data[1] | ||
141 | + | ||
142 | + def _bind_events(self): | ||
143 | + self.Bind(EVT_SELECT_SERIE, self.OnSelectSerie) | ||
144 | + self.Bind(EVT_SELECT_SLICE, self.OnSelectSlice) | ||
145 | + self.Bind(EVT_SELECT_PATIENT, self.OnSelectPatient) | ||
146 | + self.btn_ok.Bind(wx.EVT_BUTTON, self.OnClickOk) | ||
147 | + self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnClickCancel) | ||
148 | + self.text_panel.Bind(EVT_SELECT_SERIE_TEXT, self.OnDblClickTextPanel) | ||
149 | + | ||
150 | + def OnSelectSerie(self, evt): | ||
151 | + #patient_id, serie_number = evt.GetSelectID() | ||
152 | + #self.text_panel.SelectSerie(evt.GetSelectID()) | ||
153 | + #for patient in self.patients: | ||
154 | + # if patient_id == patient.GetDicomSample().patient.id: | ||
155 | + # for group in patient.GetGroups(): | ||
156 | + # if serie_number == group.GetDicomSample().acquisition.serie_number: | ||
157 | + # self.image_panel.SetSerie(group) | ||
158 | + | ||
159 | + pass | ||
160 | + | ||
161 | + | ||
162 | + | ||
163 | + def OnSelectSlice(self, evt): | ||
164 | + pass | ||
165 | + | ||
166 | + def OnSelectPatient(self, evt): | ||
167 | + pass | ||
168 | + | ||
169 | + def OnDblClickTextPanel(self, evt): | ||
170 | + group = evt.GetItemData() | ||
171 | + self.LoadDicom(group) | ||
172 | + | ||
173 | + def OnClickOk(self, evt): | ||
174 | + parm = dlg.ImportBitmapParameters() | ||
175 | + parm.SetInterval(self.combo_interval.GetSelection()) | ||
176 | + parm.ShowModal() | ||
177 | + | ||
178 | + group = self.text_panel.GetSelection() | ||
179 | + if group: | ||
180 | + self.LoadDicom(group) | ||
181 | + | ||
182 | + def OnClickCancel(self, evt): | ||
183 | + Publisher.sendMessage("Cancel DICOM load") | ||
184 | + | ||
185 | + def LoadDicom(self, group): | ||
186 | + #interval = self.combo_interval.GetSelection() | ||
187 | + #if not isinstance(group, dcm.DicomGroup): | ||
188 | + # group = max(group.GetGroups(), key=lambda g: g.nslices) | ||
189 | + | ||
190 | + #slice_amont = group.nslices | ||
191 | + #if (self.first_image_selection != None) and (self.first_image_selection != self.last_image_selection): | ||
192 | + # slice_amont = (self.last_image_selection) - self.first_image_selection | ||
193 | + # slice_amont += 1 | ||
194 | + # if slice_amont == 0: | ||
195 | + # slice_amont = group.nslices | ||
196 | + | ||
197 | + #nslices_result = slice_amont / (interval + 1) | ||
198 | + #if (nslices_result > 1): | ||
199 | + # Publisher.sendMessage('Open DICOM group', (group, interval, | ||
200 | + # [self.first_image_selection, self.last_image_selection])) | ||
201 | + #else: | ||
202 | + # dlg.MissingFilesForReconstruction() | ||
203 | + pass | ||
204 | + | ||
205 | + | ||
206 | +class TextPanel(wx.Panel): | ||
207 | + def __init__(self, parent): | ||
208 | + wx.Panel.__init__(self, parent, -1) | ||
209 | + | ||
210 | + self._selected_by_user = True | ||
211 | + self.idserie_treeitem = {} | ||
212 | + self.treeitem_idpatient = {} | ||
213 | + | ||
214 | + self.__init_gui() | ||
215 | + self.__bind_events_wx() | ||
216 | + self.__bind_pubsub_evt() | ||
217 | + | ||
218 | + def __bind_pubsub_evt(self): | ||
219 | + Publisher.subscribe(self.SelectSeries, 'Select series in import panel') | ||
220 | + | ||
221 | + def __bind_events_wx(self): | ||
222 | + self.Bind(wx.EVT_SIZE, self.OnSize) | ||
223 | + | ||
224 | + def __init_gui(self): | ||
225 | + tree = gizmos.TreeListCtrl(self, -1, style = | ||
226 | + wx.TR_DEFAULT_STYLE | ||
227 | + | wx.TR_HIDE_ROOT | ||
228 | + | wx.TR_ROW_LINES | ||
229 | + | wx.TR_COLUMN_LINES | ||
230 | + | wx.TR_FULL_ROW_HIGHLIGHT | ||
231 | + | wx.TR_SINGLE | ||
232 | + | wx.TR_HIDE_ROOT | ||
233 | + ) | ||
234 | + | ||
235 | + | ||
236 | + tree.AddColumn(_("Path")) | ||
237 | + tree.AddColumn(_("Type")) | ||
238 | + tree.AddColumn(_("Width x Height")) | ||
239 | + | ||
240 | + | ||
241 | + tree.SetMainColumn(0) | ||
242 | + tree.SetColumnWidth(0, 880) | ||
243 | + tree.SetColumnWidth(1, 60) | ||
244 | + tree.SetColumnWidth(2, 130) | ||
245 | + | ||
246 | + self.root = tree.AddRoot(_("InVesalius Database")) | ||
247 | + self.tree = tree | ||
248 | + | ||
249 | + def SelectSeries(self, pubsub_evt): | ||
250 | + group_index = pubsub_evt.data | ||
251 | + | ||
252 | + def Populate(self, data): | ||
253 | + tree = self.tree | ||
254 | + for value in data: | ||
255 | + parent = tree.AppendItem(self.root, value[0]) | ||
256 | + self.tree.SetItemText(parent, value[2], 1) | ||
257 | + self.tree.SetItemText(parent, value[5], 2) | ||
258 | + | ||
259 | + tree.Expand(self.root) | ||
260 | + #tree.SelectItem(parent_select) | ||
261 | + tree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnActivate) | ||
262 | + tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged) | ||
263 | + | ||
264 | + Publisher.sendMessage('Load bitmap into import panel', data) | ||
265 | + | ||
266 | + | ||
267 | + def OnSelChanged(self, evt): | ||
268 | + item = self.tree.GetSelection() | ||
269 | + 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) | ||
287 | + evt.Skip() | ||
288 | + | ||
289 | + def OnActivate(self, evt): | ||
290 | + item = evt.GetItem() | ||
291 | + group = self.tree.GetItemPyData(item) | ||
292 | + my_evt = SelectEvent(myEVT_SELECT_SERIE_TEXT, self.GetId()) | ||
293 | + my_evt.SetItemData(group) | ||
294 | + self.GetEventHandler().ProcessEvent(my_evt) | ||
295 | + | ||
296 | + def OnSize(self, evt): | ||
297 | + self.tree.SetSize(self.GetSize()) | ||
298 | + | ||
299 | + def SelectSerie(self, serie): | ||
300 | + self._selected_by_user = False | ||
301 | + item = self.idserie_treeitem[serie] | ||
302 | + self.tree.SelectItem(item) | ||
303 | + self._selected_by_user = True | ||
304 | + | ||
305 | + def GetSelection(self): | ||
306 | + """Get selected item""" | ||
307 | + item = self.tree.GetSelection() | ||
308 | + group = self.tree.GetItemPyData(item) | ||
309 | + return group | ||
310 | + | ||
311 | + | ||
312 | +class ImagePanel(wx.Panel): | ||
313 | + def __init__(self, parent): | ||
314 | + wx.Panel.__init__(self, parent, -1) | ||
315 | + self._init_ui() | ||
316 | + self._bind_events() | ||
317 | + | ||
318 | + def _init_ui(self): | ||
319 | + splitter = spl.MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE) | ||
320 | + splitter.SetOrientation(wx.HORIZONTAL) | ||
321 | + self.splitter = splitter | ||
322 | + | ||
323 | + splitter.ContainingSizer = wx.BoxSizer(wx.HORIZONTAL) | ||
324 | + | ||
325 | + sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
326 | + sizer.Add(splitter, 1, wx.EXPAND) | ||
327 | + self.SetSizer(sizer) | ||
328 | + | ||
329 | + self.text_panel = SeriesPanel(splitter) | ||
330 | + splitter.AppendWindow(self.text_panel, 600) | ||
331 | + | ||
332 | + self.image_panel = SlicePanel(splitter) | ||
333 | + splitter.AppendWindow(self.image_panel, 250) | ||
334 | + | ||
335 | + self.SetSizer(sizer) | ||
336 | + sizer.Fit(self) | ||
337 | + | ||
338 | + self.Layout() | ||
339 | + self.Update() | ||
340 | + self.SetAutoLayout(1) | ||
341 | + | ||
342 | + def _bind_events(self): | ||
343 | + self.text_panel.Bind(EVT_SELECT_SERIE, self.OnSelectSerie) | ||
344 | + self.text_panel.Bind(EVT_SELECT_SLICE, self.OnSelectSlice) | ||
345 | + | ||
346 | + def OnSelectSerie(self, evt): | ||
347 | + evt.Skip() | ||
348 | + | ||
349 | + def OnSelectSlice(self, evt): | ||
350 | + self.image_panel.bitmap_preview.ShowSlice(evt.GetSelectID()) | ||
351 | + evt.Skip() | ||
352 | + | ||
353 | + def SetSerie(self, serie): | ||
354 | + self.image_panel.bitmap_preview.SetDicomGroup(serie) | ||
355 | + | ||
356 | + | ||
357 | +class SeriesPanel(wx.Panel): | ||
358 | + def __init__(self, parent): | ||
359 | + wx.Panel.__init__(self, parent, -1) | ||
360 | + #self.SetBackgroundColour((0,0,0)) | ||
361 | + | ||
362 | + self.thumbnail_preview = bpp.BitmapPreviewSeries(self) | ||
363 | + #self.bitmap_preview = bpp.BitmapPreviewSlice(self) | ||
364 | + #self.bitmap_preview.Show(0) | ||
365 | + | ||
366 | + | ||
367 | + self.sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
368 | + self.sizer.Add(self.thumbnail_preview, 1, wx.EXPAND | wx.ALL, 5) | ||
369 | + #self.sizer.Add(self.bitmap_preview, 1, wx.EXPAND | wx.ALL, 5) | ||
370 | + self.sizer.Fit(self) | ||
371 | + | ||
372 | + self.SetSizer(self.sizer) | ||
373 | + | ||
374 | + self.Layout() | ||
375 | + self.Update() | ||
376 | + self.SetAutoLayout(1) | ||
377 | + | ||
378 | + self.__bind_evt() | ||
379 | + self._bind_gui_evt() | ||
380 | + | ||
381 | + def __bind_evt(self): | ||
382 | + #Publisher.subscribe(self.ShowDicomSeries, 'Load bitmap preview') | ||
383 | + #Publisher.subscribe(self.SetDicomSeries, 'Load group into import panel') | ||
384 | + Publisher.subscribe(self.SetBitmapFiles, 'Load bitmap into import panel') | ||
385 | + | ||
386 | + def _bind_gui_evt(self): | ||
387 | + self.thumbnail_preview.Bind(bpp.EVT_CLICK_SERIE, self.OnSelectSerie) | ||
388 | + #self.bitmap_preview.Bind(bpp.EVT_CLICK_SLICE, self.OnSelectSlice) | ||
389 | + | ||
390 | + #def SetDicomSeries(self, pubsub_evt): | ||
391 | + # group = pubsub_evt.data | ||
392 | + # self.bitmap_preview.SetDicomGroup(group) | ||
393 | + # self.bitmap_preview.Show(1) | ||
394 | + # self.thumbnail_preview.Show(0) | ||
395 | + # self.sizer.Layout() | ||
396 | + # self.Update() | ||
397 | + | ||
398 | + def GetSelectedImagesRange(self): | ||
399 | + return [self.bitmap_preview.first_selected, self.dicom_preview_last_selection] | ||
400 | + | ||
401 | + def SetBitmapFiles(self, pubsub_evt): | ||
402 | + | ||
403 | + | ||
404 | + bitmap = pubsub_evt.data | ||
405 | + #self.bitmap_preview.Show(0) | ||
406 | + self.thumbnail_preview.Show(1) | ||
407 | + | ||
408 | + self.thumbnail_preview.SetBitmapFiles(bitmap) | ||
409 | + #self.bitmap_preview.SetPatientGroups(patient) | ||
410 | + | ||
411 | + self.Update() | ||
412 | + | ||
413 | + def OnSelectSerie(self, evt): | ||
414 | + data = evt.GetItemData() | ||
415 | + | ||
416 | + my_evt = SelectEvent(myEVT_SELECT_SERIE, self.GetId()) | ||
417 | + my_evt.SetSelectedID(evt.GetSelectID()) | ||
418 | + my_evt.SetItemData(evt.GetItemData()) | ||
419 | + self.GetEventHandler().ProcessEvent(my_evt) | ||
420 | + | ||
421 | + #self.bitmap_preview.SetDicomGroup(data) | ||
422 | + #self.bitmap_preview.Show(1) | ||
423 | + #self.thumbnail_preview.Show(0) | ||
424 | + self.sizer.Layout() | ||
425 | + self.Show() | ||
426 | + self.Update() | ||
427 | + | ||
428 | + def OnSelectSlice(self, evt): | ||
429 | + my_evt = SelectEvent(myEVT_SELECT_SLICE, self.GetId()) | ||
430 | + my_evt.SetSelectedID(evt.GetSelectID()) | ||
431 | + my_evt.SetItemData(evt.GetItemData()) | ||
432 | + self.GetEventHandler().ProcessEvent(my_evt) | ||
433 | + | ||
434 | + | ||
435 | + #def ShowDicomSeries(self, pubsub_evt): | ||
436 | + # patient = pubsub_evt.data | ||
437 | + # if isinstance(patient, dcm.PatientGroup): | ||
438 | + # self.thumbnail_preview.SetPatientGroups(patient) | ||
439 | + # self.bitmap_preview.SetPatientGroups(patient) | ||
440 | + | ||
441 | + | ||
442 | +class SlicePanel(wx.Panel): | ||
443 | + def __init__(self, parent): | ||
444 | + wx.Panel.__init__(self, parent, -1) | ||
445 | + self.__init_gui() | ||
446 | + self.__bind_evt() | ||
447 | + | ||
448 | + def __bind_evt(self): | ||
449 | + #Publisher.subscribe(self.ShowDicomSeries, 'Load bitmap preview') | ||
450 | + #Publisher.subscribe(self.SetDicomSeries, 'Load group into import panel') | ||
451 | + Publisher.subscribe(self.SetBitmapFiles, 'Load bitmap into import panel') | ||
452 | + | ||
453 | + def __init_gui(self): | ||
454 | + self.SetBackgroundColour((255,255,255)) | ||
455 | + self.bitmap_preview = bpp.SingleImagePreview(self) | ||
456 | + | ||
457 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
458 | + sizer.Add(self.bitmap_preview, 1, wx.GROW|wx.EXPAND) | ||
459 | + sizer.Fit(self) | ||
460 | + self.SetSizer(sizer) | ||
461 | + self.Layout() | ||
462 | + self.Update() | ||
463 | + self.SetAutoLayout(1) | ||
464 | + self.sizer = sizer | ||
465 | + | ||
466 | + def SetBitmapFiles(self, pubsub_evt): | ||
467 | + data = pubsub_evt.data | ||
468 | + self.bitmap_preview.SetBitmapFiles(data) | ||
469 | + self.sizer.Layout() | ||
470 | + self.Update() | ||
471 | + | ||
472 | + #def SetDicomSeries(self, evt): | ||
473 | + # group = evt.data | ||
474 | + # self.bitmap_preview.SetDicomGroup(group) | ||
475 | + # self.sizer.Layout() | ||
476 | + # self.Update() | ||
477 | + | ||
478 | + #def ShowDicomSeries(self, pubsub_evt): | ||
479 | + # patient = pubsub_evt.data | ||
480 | + # group = patient.GetGroups()[0] | ||
481 | + # self.bitmap_preview.SetDicomGroup(group) | ||
482 | + # self.sizer.Layout() | ||
483 | + # self.Update() | ||
484 | + |
invesalius/gui/task_slice.py
@@ -524,6 +524,7 @@ class MaskProperties(wx.Panel): | @@ -524,6 +524,7 @@ class MaskProperties(wx.Panel): | ||
524 | self.bind_evt_gradient = False | 524 | self.bind_evt_gradient = False |
525 | self.gradient.SetMinValue(thresh_min) | 525 | self.gradient.SetMinValue(thresh_min) |
526 | self.gradient.SetMaxValue(thresh_max) | 526 | self.gradient.SetMaxValue(thresh_max) |
527 | + print ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", thresh_min, thresh_max | ||
527 | self.bind_evt_gradient = True | 528 | self.bind_evt_gradient = True |
528 | thresh = (thresh_min, thresh_max) | 529 | thresh = (thresh_min, thresh_max) |
529 | if thresh in Project().threshold_modes.values(): | 530 | if thresh in Project().threshold_modes.values(): |
invesalius/presets.py
@@ -73,12 +73,13 @@ class Presets(): | @@ -73,12 +73,13 @@ class Presets(): | ||
73 | def UpdateThresholdModes(self, evt): | 73 | def UpdateThresholdModes(self, evt): |
74 | 74 | ||
75 | thresh_min, thresh_max = evt.data | 75 | thresh_min, thresh_max = evt.data |
76 | - | ||
77 | presets_list = (self.thresh_ct, self.thresh_mri) | 76 | presets_list = (self.thresh_ct, self.thresh_mri) |
78 | 77 | ||
79 | for presets in presets_list: | 78 | for presets in presets_list: |
80 | for key in presets: | 79 | for key in presets: |
81 | (t_min, t_max) = presets[key] | 80 | (t_min, t_max) = presets[key] |
81 | + | ||
82 | + | ||
82 | if (t_min is None) or (t_max is None): # setting custom preset | 83 | if (t_min is None) or (t_max is None): # setting custom preset |
83 | t_min = thresh_min | 84 | t_min = thresh_min |
84 | t_max = thresh_max | 85 | t_max = thresh_max |
@@ -93,7 +94,7 @@ class Presets(): | @@ -93,7 +94,7 @@ class Presets(): | ||
93 | t_min = thresh_min | 94 | t_min = thresh_min |
94 | if (t_max < thresh_min): | 95 | if (t_max < thresh_min): |
95 | t_max = thresh_max | 96 | t_max = thresh_max |
96 | - | 97 | + |
97 | presets[key] = (t_min, t_max) | 98 | presets[key] = (t_min, t_max) |
98 | 99 | ||
99 | Publisher.sendMessage('Update threshold limits', (thresh_min, | 100 | Publisher.sendMessage('Update threshold limits', (thresh_min, |
@@ -0,0 +1,389 @@ | @@ -0,0 +1,389 @@ | ||
1 | +#-------------------------------------------------------------------------- | ||
2 | +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas | ||
3 | +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer | ||
4 | +# Homepage: http://www.softwarepublico.gov.br | ||
5 | +# Contact: invesalius@cti.gov.br | ||
6 | +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) | ||
7 | +#-------------------------------------------------------------------------- | ||
8 | +# Este programa e software livre; voce pode redistribui-lo e/ou | ||
9 | +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme | ||
10 | +# publicada pela Free Software Foundation; de acordo com a versao 2 | ||
11 | +# da Licenca. | ||
12 | +# | ||
13 | +# Este programa eh distribuido na expectativa de ser util, mas SEM | ||
14 | +# QUALQUER GARANTIA; sem mesmo a garantia implicita de | ||
15 | +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM | ||
16 | +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais | ||
17 | +# detalhes. | ||
18 | +#-------------------------------------------------------------------------- | ||
19 | +import os | ||
20 | +import Queue | ||
21 | +import threading | ||
22 | +import tempfile | ||
23 | +import sys | ||
24 | +import vtk | ||
25 | +import re | ||
26 | +import constants as const | ||
27 | +import wx | ||
28 | + | ||
29 | +from wx.lib.pubsub import pub as Publisher | ||
30 | +from multiprocessing import cpu_count | ||
31 | + | ||
32 | +from vtk.util import numpy_support | ||
33 | +from scipy import misc | ||
34 | +import numpy | ||
35 | +import imghdr | ||
36 | + | ||
37 | +from data import converters | ||
38 | + | ||
39 | +#flag to control vtk error in read files | ||
40 | +no_error = True | ||
41 | +vtk_error = False | ||
42 | + | ||
43 | +class Singleton: | ||
44 | + | ||
45 | + def __init__(self,klass): | ||
46 | + self.klass = klass | ||
47 | + self.instance = None | ||
48 | + | ||
49 | + def __call__(self,*args,**kwds): | ||
50 | + if self.instance == None: | ||
51 | + self.instance = self.klass(*args,**kwds) | ||
52 | + return self.instance | ||
53 | + | ||
54 | +@Singleton | ||
55 | +class BitmapData: | ||
56 | + | ||
57 | + def __init__(self): | ||
58 | + self.data = None | ||
59 | + | ||
60 | + def GetData(self): | ||
61 | + return self.data | ||
62 | + | ||
63 | + def SetData(self, data): | ||
64 | + self.data = data | ||
65 | + | ||
66 | + def GetOnlyBitmapPath(self): | ||
67 | + paths = [item[0] for item in self.data] | ||
68 | + return paths | ||
69 | + | ||
70 | + def GetFirstBitmapSize(self): | ||
71 | + return (self.data[0][3], self.data[0][4]) | ||
72 | + | ||
73 | + def IsAllBitmapSameSize(self): | ||
74 | + sizes = [item[5] for item in self.data] | ||
75 | + | ||
76 | + k = {} | ||
77 | + for v in sizes: | ||
78 | + k[v] = '' | ||
79 | + | ||
80 | + if len(k.keys()) > 1: | ||
81 | + return False | ||
82 | + else: | ||
83 | + return True | ||
84 | + | ||
85 | + def GetFirstPixelSize(self): | ||
86 | + | ||
87 | + path = self.data[0][0] | ||
88 | + size = ReadBitmap(path).dtype.itemsize * 8 | ||
89 | + | ||
90 | + return size | ||
91 | + | ||
92 | + | ||
93 | + | ||
94 | +class BitmapFiles: | ||
95 | + | ||
96 | + def __init__(self): | ||
97 | + self.bitmapfiles = [] | ||
98 | + | ||
99 | + def Add(self, bmp): | ||
100 | + self.bitmapfiles.append(bmp) | ||
101 | + | ||
102 | + def Sort(self, x): | ||
103 | + | ||
104 | + c_re = re.compile('\d+') | ||
105 | + | ||
106 | + if len(c_re.findall(x[0])) > 0: | ||
107 | + return c_re.findall(x[0])[-1] | ||
108 | + else: | ||
109 | + return '0' | ||
110 | + | ||
111 | + def GetValues(self): | ||
112 | + bmpfile = self.bitmapfiles | ||
113 | + bmpfile.sort(key = self.Sort) | ||
114 | + | ||
115 | + bmp_data = BitmapData() | ||
116 | + bmp_data.data = bmpfile | ||
117 | + | ||
118 | + return bmpfile | ||
119 | + | ||
120 | +class LoadBitmap: | ||
121 | + | ||
122 | + def __init__(self, bmp_file, filepath): | ||
123 | + self.bmp_file = bmp_file | ||
124 | + if sys.platform == 'win32': | ||
125 | + self.filepath = filepath.encode(utils.get_system_encoding()) | ||
126 | + else: | ||
127 | + self.filepath = filepath | ||
128 | + | ||
129 | + self.run() | ||
130 | + | ||
131 | + def run(self): | ||
132 | + global vtk_error | ||
133 | + | ||
134 | + #----- verify extension ------------------ | ||
135 | + #ex = self.filepath.split('.')[-1] | ||
136 | + extension = VerifyDataType(self.filepath) | ||
137 | + | ||
138 | + file_name = self.filepath.split(os.path.sep)[-1] | ||
139 | + | ||
140 | + #if extension == 'bmp': | ||
141 | + # reader = vtk.vtkBMPReader() | ||
142 | + n_array = ReadBitmap(self.filepath) | ||
143 | + | ||
144 | + if not(isinstance(n_array, numpy.ndarray)): | ||
145 | + return False | ||
146 | + | ||
147 | + image = converters.to_vtk(n_array, spacing=(1,1,1),\ | ||
148 | + slice_number=1, orientation="AXIAL") | ||
149 | + | ||
150 | + | ||
151 | + #reader.SetFileName(self.filepath) | ||
152 | + #reader.Update() | ||
153 | + | ||
154 | + dim = image.GetDimensions() | ||
155 | + x = dim[0] | ||
156 | + y = dim[1] | ||
157 | + | ||
158 | + img = vtk.vtkImageResample() | ||
159 | + img.SetInputData(image) | ||
160 | + img.SetAxisMagnificationFactor ( 0, 0.25 ) | ||
161 | + img.SetAxisMagnificationFactor ( 1, 0.25 ) | ||
162 | + img.SetAxisMagnificationFactor ( 2, 1 ) | ||
163 | + img.Update() | ||
164 | + | ||
165 | + tp = img.GetOutput().GetScalarTypeAsString() | ||
166 | + | ||
167 | + image_copy = vtk.vtkImageData() | ||
168 | + image_copy.DeepCopy(img.GetOutput()) | ||
169 | + | ||
170 | + thumbnail_path = tempfile.mktemp() | ||
171 | + | ||
172 | + write_png = vtk.vtkPNGWriter() | ||
173 | + write_png.SetInputConnection(img.GetOutputPort()) | ||
174 | + write_png.AddObserver("WarningEvent", VtkErrorPNGWriter) | ||
175 | + write_png.SetFileName(thumbnail_path) | ||
176 | + write_png.Write() | ||
177 | + | ||
178 | + if vtk_error: | ||
179 | + img = vtk.vtkImageCast() | ||
180 | + img.SetInputData(image_copy) | ||
181 | + img.SetOutputScalarTypeToUnsignedShort() | ||
182 | + #img.SetClampOverflow(1) | ||
183 | + img.Update() | ||
184 | + | ||
185 | + write_png = vtk.vtkPNGWriter() | ||
186 | + write_png.SetInputConnection(img.GetOutputPort()) | ||
187 | + write_png.SetFileName(thumbnail_path) | ||
188 | + write_png.Write() | ||
189 | + | ||
190 | + vtk_error = False | ||
191 | + | ||
192 | + id = wx.NewId() | ||
193 | + | ||
194 | + bmp_item = [self.filepath, thumbnail_path, extension, x, y,\ | ||
195 | + str(x) + ' x ' + str(y), file_name, id] | ||
196 | + self.bmp_file.Add(bmp_item) | ||
197 | + | ||
198 | + | ||
199 | +def yGetBitmaps(directory, recursive=True, gui=True): | ||
200 | + """ | ||
201 | + Return all full paths to DICOM files inside given directory. | ||
202 | + """ | ||
203 | + nfiles = 0 | ||
204 | + # Find total number of files | ||
205 | + if recursive: | ||
206 | + for dirpath, dirnames, filenames in os.walk(directory): | ||
207 | + nfiles += len(filenames) | ||
208 | + else: | ||
209 | + dirpath, dirnames, filenames = os.walk(directory) | ||
210 | + nfiles = len(filenames) | ||
211 | + | ||
212 | + | ||
213 | + counter = 0 | ||
214 | + bmp_file = BitmapFiles() | ||
215 | + | ||
216 | + # Retrieve only TIFF, BMP, JPEG and PNG files | ||
217 | + if recursive: | ||
218 | + for dirpath, dirnames, filenames in os.walk(directory): | ||
219 | + for name in filenames: | ||
220 | + filepath = os.path.join(dirpath, name) | ||
221 | + counter += 1 | ||
222 | + if gui: | ||
223 | + yield (counter,nfiles) | ||
224 | + #LoadDicom(grouper, filepath) | ||
225 | + LoadBitmap(bmp_file, filepath) | ||
226 | + else: | ||
227 | + dirpath, dirnames, filenames = os.walk(directory) | ||
228 | + for name in filenames: | ||
229 | + filepath = str(os.path.join(dirpath, name)) | ||
230 | + counter += 1 | ||
231 | + if gui: | ||
232 | + yield (counter,nfiles) | ||
233 | + #q.put(filepath) | ||
234 | + | ||
235 | + #for t in threads: | ||
236 | + # q.put(0) | ||
237 | + | ||
238 | + #for t in threads: | ||
239 | + # t.join() | ||
240 | + | ||
241 | + #TODO: Is this commented update necessary? | ||
242 | + #grouper.Update() | ||
243 | + yield bmp_file.GetValues() | ||
244 | + | ||
245 | + | ||
246 | +class ProgressBitmapReader: | ||
247 | + def __init__(self): | ||
248 | + Publisher.subscribe(self.CancelLoad, "Cancel bitmap load") | ||
249 | + | ||
250 | + def CancelLoad(self, evt_pubsub): | ||
251 | + self.running = False | ||
252 | + self.stoped = True | ||
253 | + | ||
254 | + def SetWindowEvent(self, frame): | ||
255 | + self.frame = frame | ||
256 | + | ||
257 | + def SetDirectoryPath(self, path,recursive=True): | ||
258 | + self.running = True | ||
259 | + self.stoped = False | ||
260 | + self.GetBitmaps(path,recursive) | ||
261 | + | ||
262 | + def UpdateLoadFileProgress(self,cont_progress): | ||
263 | + Publisher.sendMessage("Update bitmap load", cont_progress) | ||
264 | + | ||
265 | + def EndLoadFile(self, bitmap_list): | ||
266 | + Publisher.sendMessage("End bitmap load", bitmap_list) | ||
267 | + | ||
268 | + def GetBitmaps(self, path, recursive): | ||
269 | + | ||
270 | + #if not const.VTK_WARNING: | ||
271 | + # log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | ||
272 | + # fow = vtk.vtkFileOutputWindow() | ||
273 | + # fow.SetFileName(log_path) | ||
274 | + # ow = vtk.vtkOutputWindow() | ||
275 | + # ow.SetInstance(fow) | ||
276 | + | ||
277 | + y = yGetBitmaps(path, recursive) | ||
278 | + for value_progress in y: | ||
279 | + if not self.running: | ||
280 | + break | ||
281 | + if isinstance(value_progress, tuple): | ||
282 | + self.UpdateLoadFileProgress(value_progress) | ||
283 | + else: | ||
284 | + self.EndLoadFile(value_progress) | ||
285 | + | ||
286 | + #Is necessary in the case user cancel | ||
287 | + #the load, ensure that dicomdialog is closed | ||
288 | + if(self.stoped): | ||
289 | + self.UpdateLoadFileProgress(None) | ||
290 | + self.stoped = False | ||
291 | + | ||
292 | +def VtkErrorPNGWriter(obj, f): | ||
293 | + global vtk_error | ||
294 | + vtk_error = True | ||
295 | + | ||
296 | +def ScipyRead(filepath): | ||
297 | + try: | ||
298 | + r = misc.imread(filepath, flatten=True) | ||
299 | + dt = r.dtype | ||
300 | + | ||
301 | + if dt == "float" or dt == "float16"\ | ||
302 | + or dt == "float32" or dt == "float64": | ||
303 | + shift=-r.max()/2 | ||
304 | + simage = numpy.zeros_like(r, dtype='int16') | ||
305 | + simage[:] = r.astype('int32') + shift | ||
306 | + | ||
307 | + return simage | ||
308 | + else: | ||
309 | + return r | ||
310 | + except(IOError): | ||
311 | + return False | ||
312 | + | ||
313 | +def VtkRead(filepath, t): | ||
314 | + | ||
315 | + global no_error | ||
316 | + | ||
317 | + if t == "bmp": | ||
318 | + reader = vtk.vtkBMPReader() | ||
319 | + | ||
320 | + elif t == "tiff" or t == "tif": | ||
321 | + reader = vtk.vtkTIFFReader() | ||
322 | + | ||
323 | + elif t == "png": | ||
324 | + reader = vtk.vtkPNGReader() | ||
325 | + | ||
326 | + elif t == "jpeg" or t == "jpg": | ||
327 | + reader = vtk.vtkJPEGReader() | ||
328 | + | ||
329 | + else: | ||
330 | + return False | ||
331 | + | ||
332 | + reader.AddObserver("ErrorEvent", VtkErrorToPy) | ||
333 | + reader.SetFileName(filepath) | ||
334 | + reader.Update() | ||
335 | + | ||
336 | + if no_error: | ||
337 | + image = reader.GetOutput() | ||
338 | + dim = image.GetDimensions() | ||
339 | + | ||
340 | + if reader.GetNumberOfScalarComponents() > 1: | ||
341 | + luminanceFilter = vtk.vtkImageLuminance() | ||
342 | + luminanceFilter.SetInputData(image) | ||
343 | + luminanceFilter.Update() | ||
344 | + | ||
345 | + image = vtk.vtkImageData() | ||
346 | + image.DeepCopy(luminanceFilter.GetOutput()) | ||
347 | + | ||
348 | + img_array = numpy_support.vtk_to_numpy(image.GetPointData().GetScalars()) | ||
349 | + img_array.shape = (dim[1], dim[0]) | ||
350 | + | ||
351 | + return img_array | ||
352 | + else: | ||
353 | + no_error = True | ||
354 | + return False | ||
355 | + | ||
356 | + | ||
357 | +def ReadBitmap(filepath): | ||
358 | + | ||
359 | + t = VerifyDataType(filepath) | ||
360 | + | ||
361 | + if t == False: | ||
362 | + return False | ||
363 | + | ||
364 | + img_array = VtkRead(filepath, t) | ||
365 | + | ||
366 | + if not(isinstance(img_array, numpy.ndarray)): | ||
367 | + | ||
368 | + no_error = True | ||
369 | + | ||
370 | + img_array = ScipyRead(filepath) | ||
371 | + | ||
372 | + if not(isinstance(img_array, numpy.ndarray)): | ||
373 | + return False | ||
374 | + | ||
375 | + return img_array | ||
376 | + | ||
377 | + | ||
378 | +def VtkErrorToPy(obj, evt): | ||
379 | + global no_error | ||
380 | + no_error = False | ||
381 | + | ||
382 | + | ||
383 | +def VerifyDataType(filepath): | ||
384 | + try: | ||
385 | + t = imghdr.what(filepath) | ||
386 | + return t | ||
387 | + except IOError: | ||
388 | + return False | ||
389 | + |