Commit bed72ec34ae1e89713f64d9abd4f96f8b85d644b
Committed by
Thiago Franco de Moraes
1 parent
6a586fc7
Exists in
master
and in
13 other branches
Neuronavigation module update and improvement (#65)
Neuronavigation module updatings and improvements * Revert "Revert "ENH: Update neuronavigator modules"" This reverts commit 2ecc37998e18c762b82413314fcf75d1491ca1be. * ENH: Update corregistration comments. * ENH: Restructuring InVesalius Navigator spatial tracker communication. * ENH: Restructuring the spatial tracker communication. * ENH: Updating navigation tools. * Code refactoring for navigator development - Delete previous git mistakes while tracking files - Code fix to run invesalius navigator and start code refactoring * Update navigator GUI - Improvement on manipulation of markers creation and edition - Buttons and panels size adjustment * Improvement on trackers connection - MicronTracker supports dynamic reference * VTK6 adjustments - SetInputData -Using SetInputData instead SetInput * Update Create Markers and fix sphere creation - Improvement on manipulation of markers creation and edition - Using SetInputConnection(GetOutputPort()) instead SetInputData(GetOutput()) * GUI update and creation of fiducial markers * Fix Fiducial Markers * Update - Load Fiducial points using "Load Markers" * Fix Trackers (PLH) and Navigation * Update ComboBox and trackers connection * Full support for Analyze, NIfTI and PAR/REC images - Support for Analyze, NIfTI, Compressed NIfTI and PAR/REC images - Support for Analyze limited due to lack of orientation info - Image orientation standardized to RAS+ * ComboBox Update - Set "Select tracker" when the tracker is not connected * Fixed usp-navegador version - Changed GetValue to GetValue() - Changed SetInputData to SetInput - Changed SetInputConnection to SetInputData * Manually merged rmatsuda master_merge branch to usp-navegador - Improved navigation GUI - Improved navigation control of spatial trackers - Added management of makers creation - Added dialogs for better neuronavigation control * Added TMS trigger and enhanced tracker device control - Serial communication to detect TMS trigger - Added MicronTracker calibration and marker files - Enhanced tracker manipulation * Navigation cleaning and improvements in viewer volume - Cleaned unecessary navigation functions - Optimized volume camera and ball reference positions - Enhanced markers manipulation - Removed blank lines * Improved task_navigator GUI - Better code using GridBagSizer - Removed useless code * Improvement in coordinates handling for neuronavigation * Significant refactor of navigation pipeline - Improvement in navigation panel controls - Refactoring of volume and slice updates - Improvement in communication with tracking devices - Refactoring of entire navigation pipeline * Improved colour and size of navigation markers * Added trigger and volume camera controls - Reformulated markers creation - Created control of volume camera - Control for external trigger marker creation * Minor code adjustments * GUI improvements * Minor code optimization - Improved load markers
Showing
39 changed files
with
2773 additions
and
825 deletions
Show diff stats
.gitignore
| ... | ... | @@ -8,6 +8,7 @@ invesalius/*.swo |
| 8 | 8 | invesalius/*.swp |
| 9 | 9 | invesalius/data/*.log |
| 10 | 10 | invesalius/data/*.pyc |
| 11 | +invesalius/data/*.pyd | |
| 11 | 12 | invesalius/gui/*.log |
| 12 | 13 | invesalius/gui/*.pyc |
| 13 | 14 | invesalius/gui/widgets/*.log |
| ... | ... | @@ -21,6 +22,7 @@ invesalius/reader/*.pyc |
| 21 | 22 | tags |
| 22 | 23 | *.c |
| 23 | 24 | |
| 25 | +.idea | |
| 24 | 26 | build |
| 25 | 27 | *.patch |
| 26 | 28 | *.tgz |
| ... | ... | @@ -29,4 +31,4 @@ build |
| 29 | 31 | *.cpp |
| 30 | 32 | *.diff |
| 31 | 33 | |
| 32 | 34 | -*.directory |
| 35 | +*.directory | |
| 33 | 36 | \ No newline at end of file | ... | ... |
invesalius/constants.py
| ... | ... | @@ -487,8 +487,9 @@ VTK_WARNING = 0 |
| 487 | 487 | |
| 488 | 488 | [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE, |
| 489 | 489 | ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET, |
| 490 | -ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_ANALYZE_IMPORT, ID_PREFERENCES, | |
| 491 | -ID_DICOM_NETWORK, ID_TIFF_JPG_PNG, ID_VIEW_INTERPOLATED] = [wx.NewId() for number in range(15)] | |
| 490 | +ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_PREFERENCES, ID_DICOM_NETWORK, | |
| 491 | +ID_TIFF_JPG_PNG, ID_VIEW_INTERPOLATED, ID_ANALYZE_IMPORT, ID_NIFTI_IMPORT, | |
| 492 | +ID_PARREC_IMPORT] = [wx.NewId() for number in range(17)] | |
| 492 | 493 | ID_EXIT = wx.ID_EXIT |
| 493 | 494 | ID_ABOUT = wx.ID_ABOUT |
| 494 | 495 | |
| ... | ... | @@ -637,3 +638,53 @@ BOOLEAN_UNION = 1 |
| 637 | 638 | BOOLEAN_DIFF = 2 |
| 638 | 639 | BOOLEAN_AND = 3 |
| 639 | 640 | BOOLEAN_XOR = 4 |
| 641 | + | |
| 642 | +#------------ Navigation defaults ------------------- | |
| 643 | + | |
| 644 | +SELECT = 0 | |
| 645 | +MTC = 1 | |
| 646 | +FASTRAK = 2 | |
| 647 | +ISOTRAKII = 3 | |
| 648 | +PATRIOT = 4 | |
| 649 | +DEBUGTRACK = 5 | |
| 650 | +DISCTRACK = 6 | |
| 651 | +DEFAULT_TRACKER = SELECT | |
| 652 | + | |
| 653 | +TRACKER = [_("Select tracker:"), _("Claron MicronTracker"), | |
| 654 | + _("Polhemus FASTRAK"), _("Polhemus ISOTRAK II"), | |
| 655 | + _("Polhemus PATRIOT"), _("Debug tracker"), | |
| 656 | + _("Disconnect tracker")] | |
| 657 | + | |
| 658 | +STATIC_REF = 0 | |
| 659 | +DYNAMIC_REF = 1 | |
| 660 | +DEFAULT_REF_MODE = DYNAMIC_REF | |
| 661 | +REF_MODE = [_("Static ref."), _("Dynamic ref.")] | |
| 662 | + | |
| 663 | +IR1 = wx.NewId() | |
| 664 | +IR2 = wx.NewId() | |
| 665 | +IR3 = wx.NewId() | |
| 666 | +TR1 = wx.NewId() | |
| 667 | +TR2 = wx.NewId() | |
| 668 | +TR3 = wx.NewId() | |
| 669 | +SET = wx.NewId() | |
| 670 | + | |
| 671 | +BTNS_IMG = {IR1: {0: _('LEI')}, | |
| 672 | + IR2: {1: _('REI')}, | |
| 673 | + IR3: {2: _('NAI')}} | |
| 674 | + | |
| 675 | +TIPS_IMG = [wx.ToolTip(_("Select left ear in image")), | |
| 676 | + wx.ToolTip(_("Select right ear in image")), | |
| 677 | + wx.ToolTip(_("Select nasion in image"))] | |
| 678 | + | |
| 679 | +BTNS_TRK = {TR1: {3: _('LET')}, | |
| 680 | + TR2: {4: _('RET')}, | |
| 681 | + TR3: {5: _('NAT')}, | |
| 682 | + SET: {6: _('SET')}} | |
| 683 | + | |
| 684 | +TIPS_TRK = [wx.ToolTip(_("Select left ear with spatial tracker")), | |
| 685 | + wx.ToolTip(_("Select right ear with spatial tracker")), | |
| 686 | + wx.ToolTip(_("Select nasion with spatial tracker")), | |
| 687 | + wx.ToolTip(_("Show set coordinates in image"))] | |
| 688 | + | |
| 689 | +CAL_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'CalibrationFiles')) | |
| 690 | +MAR_DIR = os.path.abspath(os.path.join(FILE_PATH, '..', 'navigation', 'mtc_files', 'Markers')) | ... | ... |
invesalius/control.py
| ... | ... | @@ -16,11 +16,9 @@ |
| 16 | 16 | # PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais |
| 17 | 17 | # detalhes. |
| 18 | 18 | #-------------------------------------------------------------------------- |
| 19 | -import math | |
| 20 | 19 | import os |
| 21 | 20 | import plistlib |
| 22 | 21 | import wx |
| 23 | -import numpy | |
| 24 | 22 | from wx.lib.pubsub import pub as Publisher |
| 25 | 23 | |
| 26 | 24 | import invesalius.constants as const |
| ... | ... | @@ -32,10 +30,10 @@ import invesalius.data.surface as srf |
| 32 | 30 | import invesalius.data.volume as volume |
| 33 | 31 | import invesalius.gui.dialogs as dialog |
| 34 | 32 | import invesalius.project as prj |
| 35 | -import invesalius.reader.analyze_reader as analyze | |
| 36 | 33 | import invesalius.reader.dicom_grouper as dg |
| 37 | 34 | import invesalius.reader.dicom_reader as dcm |
| 38 | 35 | import invesalius.reader.bitmap_reader as bmp |
| 36 | +import invesalius.reader.others_reader as oth | |
| 39 | 37 | import invesalius.session as ses |
| 40 | 38 | |
| 41 | 39 | |
| ... | ... | @@ -65,6 +63,8 @@ class Controller(): |
| 65 | 63 | Publisher.subscribe(self.OnImportMedicalImages, 'Import directory') |
| 66 | 64 | Publisher.subscribe(self.OnShowDialogImportDirectory, |
| 67 | 65 | 'Show import directory dialog') |
| 66 | + Publisher.subscribe(self.OnShowDialogImportOtherFiles, | |
| 67 | + 'Show import other files dialog') | |
| 68 | 68 | Publisher.subscribe(self.OnShowDialogOpenProject, |
| 69 | 69 | 'Show open project dialog') |
| 70 | 70 | |
| ... | ... | @@ -77,7 +77,9 @@ class Controller(): |
| 77 | 77 | Publisher.subscribe(self.OnOpenDicomGroup, |
| 78 | 78 | 'Open DICOM group') |
| 79 | 79 | Publisher.subscribe(self.OnOpenBitmapFiles, |
| 80 | - 'Open bitmap files') | |
| 80 | + 'Open bitmap files') | |
| 81 | + Publisher.subscribe(self.OnOpenOtherFiles, | |
| 82 | + 'Open other files') | |
| 81 | 83 | Publisher.subscribe(self.Progress, "Update dicom load") |
| 82 | 84 | Publisher.subscribe(self.Progress, "Update bitmap load") |
| 83 | 85 | Publisher.subscribe(self.OnLoadImportPanel, "End dicom load") |
| ... | ... | @@ -88,7 +90,6 @@ class Controller(): |
| 88 | 90 | Publisher.subscribe(self.OnShowDialogCloseProject, 'Close Project') |
| 89 | 91 | Publisher.subscribe(self.OnOpenProject, 'Open project') |
| 90 | 92 | Publisher.subscribe(self.OnOpenRecentProject, 'Open recent project') |
| 91 | - Publisher.subscribe(self.OnShowAnalyzeFile, 'Show analyze dialog') | |
| 92 | 93 | Publisher.subscribe(self.OnShowBitmapFile, 'Show bitmap dialog') |
| 93 | 94 | |
| 94 | 95 | Publisher.subscribe(self.ShowBooleanOpDialog, 'Show boolean dialog') |
| ... | ... | @@ -116,6 +117,10 @@ class Controller(): |
| 116 | 117 | def OnShowDialogImportDirectory(self, pubsub_evt): |
| 117 | 118 | self.ShowDialogImportDirectory() |
| 118 | 119 | |
| 120 | + def OnShowDialogImportOtherFiles(self, pubsub_evt): | |
| 121 | + id_type = pubsub_evt.data | |
| 122 | + self.ShowDialogImportOtherFiles(id_type) | |
| 123 | + | |
| 119 | 124 | def OnShowDialogOpenProject(self, pubsub_evt): |
| 120 | 125 | self.ShowDialogOpenProject() |
| 121 | 126 | |
| ... | ... | @@ -126,15 +131,6 @@ class Controller(): |
| 126 | 131 | def OnShowDialogCloseProject(self, pubsub_evt): |
| 127 | 132 | self.ShowDialogCloseProject() |
| 128 | 133 | |
| 129 | - def OnShowAnalyzeFile(self, pubsub_evt): | |
| 130 | - dirpath = dialog.ShowOpenAnalyzeDialog() | |
| 131 | - imagedata = analyze.ReadAnalyze(dirpath) | |
| 132 | - if imagedata: | |
| 133 | - self.CreateAnalyzeProject(imagedata) | |
| 134 | - | |
| 135 | - self.LoadProject() | |
| 136 | - Publisher.sendMessage("Enable state project", True) | |
| 137 | - | |
| 138 | 134 | def OnShowBitmapFile(self, pubsub_evt): |
| 139 | 135 | self.ShowDialogImportBitmapFile() |
| 140 | 136 | ########################### |
| ... | ... | @@ -163,7 +159,6 @@ class Controller(): |
| 163 | 159 | self.StartImportBitmapPanel(dirpath) |
| 164 | 160 | # Publisher.sendMessage("Load data to import panel", dirpath) |
| 165 | 161 | |
| 166 | - | |
| 167 | 162 | def ShowDialogImportDirectory(self): |
| 168 | 163 | # Offer to save current project if necessary |
| 169 | 164 | session = ses.Session() |
| ... | ... | @@ -186,6 +181,40 @@ class Controller(): |
| 186 | 181 | self.StartImportPanel(dirpath) |
| 187 | 182 | Publisher.sendMessage("Load data to import panel", dirpath) |
| 188 | 183 | |
| 184 | + def ShowDialogImportOtherFiles(self, id_type): | |
| 185 | + # Offer to save current project if necessary | |
| 186 | + session = ses.Session() | |
| 187 | + st = session.project_status | |
| 188 | + if (st == const.PROJ_NEW) or (st == const.PROJ_CHANGE): | |
| 189 | + filename = session.project_path[1] | |
| 190 | + answer = dialog.SaveChangesDialog2(filename) | |
| 191 | + if answer: | |
| 192 | + self.ShowDialogSaveProject() | |
| 193 | + self.CloseProject() | |
| 194 | + # Publisher.sendMessage("Enable state project", False) | |
| 195 | + Publisher.sendMessage('Set project name') | |
| 196 | + Publisher.sendMessage("Stop Config Recording") | |
| 197 | + Publisher.sendMessage("Set slice interaction style", const.STATE_DEFAULT) | |
| 198 | + | |
| 199 | + # Warning for limited support to Analyze format | |
| 200 | + if id_type == const.ID_ANALYZE_IMPORT: | |
| 201 | + dialog.ImportAnalyzeWarning() | |
| 202 | + | |
| 203 | + # Import project treating compressed nifti exception | |
| 204 | + suptype = ('hdr', 'nii', 'nii.gz', 'par') | |
| 205 | + filepath = dialog.ShowImportOtherFilesDialog(id_type) | |
| 206 | + name = filepath.rpartition('\\')[-1].split('.') | |
| 207 | + | |
| 208 | + if name[-1] == 'gz': | |
| 209 | + name[1] = 'nii.gz' | |
| 210 | + | |
| 211 | + filetype = name[1].lower() | |
| 212 | + | |
| 213 | + if filetype in suptype: | |
| 214 | + Publisher.sendMessage("Open other files", filepath) | |
| 215 | + else: | |
| 216 | + dialog.ImportInvalidFiles() | |
| 217 | + | |
| 189 | 218 | def ShowDialogOpenProject(self): |
| 190 | 219 | # Offer to save current project if necessary |
| 191 | 220 | session = ses.Session() |
| ... | ... | @@ -281,8 +310,6 @@ class Controller(): |
| 281 | 310 | else: |
| 282 | 311 | dialog.InexistentPath(filepath) |
| 283 | 312 | |
| 284 | - | |
| 285 | - | |
| 286 | 313 | def OpenProject(self, filepath): |
| 287 | 314 | Publisher.sendMessage('Begin busy cursor') |
| 288 | 315 | path = os.path.abspath(filepath) |
| ... | ... | @@ -342,7 +369,7 @@ class Controller(): |
| 342 | 369 | |
| 343 | 370 | def StartImportPanel(self, path): |
| 344 | 371 | |
| 345 | - # retrieve DICOM files splited into groups | |
| 372 | + # retrieve DICOM files split into groups | |
| 346 | 373 | reader = dcm.ProgressDicomReader() |
| 347 | 374 | reader.SetWindowEvent(self.frame) |
| 348 | 375 | reader.SetDirectoryPath(path) |
| ... | ... | @@ -410,21 +437,33 @@ class Controller(): |
| 410 | 437 | self.ImportMedicalImages(directory) |
| 411 | 438 | |
| 412 | 439 | def ImportMedicalImages(self, directory): |
| 413 | - # OPTION 1: DICOM? | |
| 414 | 440 | patients_groups = dcm.GetDicomGroups(directory) |
| 441 | + name = directory.rpartition('\\')[-1].split('.') | |
| 442 | + print "patients: ", patients_groups | |
| 443 | + | |
| 415 | 444 | if len(patients_groups): |
| 445 | + # OPTION 1: DICOM | |
| 416 | 446 | group = dcm.SelectLargerDicomGroup(patients_groups) |
| 417 | - matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0,0],gui=True) | |
| 447 | + matrix, matrix_filename, dicom = self.OpenDicomGroup(group, 0, [0, 0], gui=True) | |
| 418 | 448 | self.CreateDicomProject(dicom, matrix, matrix_filename) |
| 419 | - # OPTION 2: ANALYZE? | |
| 420 | 449 | else: |
| 421 | - imagedata = analyze.ReadDirectory(directory) | |
| 422 | - if imagedata: | |
| 423 | - self.CreateAnalyzeProject(imagedata) | |
| 424 | - # OPTION 3: Nothing... | |
| 450 | + # OPTION 2: NIfTI, Analyze or PAR/REC | |
| 451 | + if name[-1] == 'gz': | |
| 452 | + name[1] = 'nii.gz' | |
| 453 | + | |
| 454 | + suptype = ('hdr', 'nii', 'nii.gz', 'par') | |
| 455 | + filetype = name[1].lower() | |
| 456 | + | |
| 457 | + if filetype in suptype: | |
| 458 | + group = oth.ReadOthers(directory) | |
| 425 | 459 | else: |
| 426 | 460 | utils.debug("No medical images found on given directory") |
| 427 | 461 | return |
| 462 | + | |
| 463 | + matrix, matrix_filename = self.OpenOtherFiles(group) | |
| 464 | + self.CreateOtherProject(str(name[0]), matrix, matrix_filename) | |
| 465 | + # OPTION 4: Nothing... | |
| 466 | + | |
| 428 | 467 | self.LoadProject() |
| 429 | 468 | Publisher.sendMessage("Enable state project", True) |
| 430 | 469 | |
| ... | ... | @@ -493,43 +532,6 @@ class Controller(): |
| 493 | 532 | |
| 494 | 533 | Publisher.sendMessage('End busy cursor') |
| 495 | 534 | |
| 496 | - def CreateAnalyzeProject(self, imagedata): | |
| 497 | - header = imagedata.get_header() | |
| 498 | - proj = prj.Project() | |
| 499 | - proj.imagedata = None | |
| 500 | - proj.name = _("Untitled") | |
| 501 | - proj.SetAcquisitionModality("MRI") | |
| 502 | - #TODO: Verify if all Analyse are in AXIAL orientation | |
| 503 | - | |
| 504 | - # To get Z, X, Y (used by InVesaliu), not X, Y, Z | |
| 505 | - matrix, matrix_filename = image_utils.analyze2mmap(imagedata) | |
| 506 | - if header['orient'] == 0: | |
| 507 | - proj.original_orientation = const.AXIAL | |
| 508 | - elif header['orient'] == 1: | |
| 509 | - proj.original_orientation = const.CORONAL | |
| 510 | - elif header['orient'] == 2: | |
| 511 | - proj.original_orientation = const.SAGITAL | |
| 512 | - else: | |
| 513 | - proj.original_orientation = const.SAGITAL | |
| 514 | - | |
| 515 | - proj.threshold_range = (int(header['glmin']), | |
| 516 | - int(header['glmax'])) | |
| 517 | - proj.window = proj.threshold_range[1] - proj.threshold_range[0] | |
| 518 | - proj.level = (0.5 * (proj.threshold_range[1] + proj.threshold_range[0])) | |
| 519 | - proj.spacing = header['pixdim'][1:4] | |
| 520 | - proj.matrix_shape = matrix.shape | |
| 521 | - | |
| 522 | - self.Slice = sl.Slice() | |
| 523 | - self.Slice.matrix = matrix | |
| 524 | - self.Slice.matrix_filename = matrix_filename | |
| 525 | - | |
| 526 | - self.Slice.window_level = proj.level | |
| 527 | - self.Slice.window_width = proj.window | |
| 528 | - self.Slice.spacing = header.get_zooms()[:3] | |
| 529 | - | |
| 530 | - Publisher.sendMessage('Update threshold limits list', | |
| 531 | - proj.threshold_range) | |
| 532 | - | |
| 533 | 535 | def CreateDicomProject(self, dicom, matrix, matrix_filename): |
| 534 | 536 | name_to_const = {"AXIAL":const.AXIAL, |
| 535 | 537 | "CORONAL":const.CORONAL, |
| ... | ... | @@ -604,6 +606,40 @@ class Controller(): |
| 604 | 606 | |
| 605 | 607 | dirpath = session.CreateProject(filename) |
| 606 | 608 | |
| 609 | + def CreateOtherProject(self, name, matrix, matrix_filename): | |
| 610 | + name_to_const = {"AXIAL": const.AXIAL, | |
| 611 | + "CORONAL": const.CORONAL, | |
| 612 | + "SAGITTAL": const.SAGITAL} | |
| 613 | + | |
| 614 | + proj = prj.Project() | |
| 615 | + proj.name = name | |
| 616 | + proj.modality = 'MRI' | |
| 617 | + proj.SetAcquisitionModality('MRI') | |
| 618 | + proj.matrix_shape = matrix.shape | |
| 619 | + proj.matrix_dtype = matrix.dtype.name | |
| 620 | + proj.matrix_filename = matrix_filename | |
| 621 | + | |
| 622 | + # Orientation must be CORONAL in order to as_closes_canonical and | |
| 623 | + # swap axis in img2memmap to work in a standardized way. | |
| 624 | + # TODO: Create standard import image for all acquisition orientations | |
| 625 | + orientation = 'CORONAL' | |
| 626 | + | |
| 627 | + proj.original_orientation =\ | |
| 628 | + name_to_const[orientation] | |
| 629 | + | |
| 630 | + proj.window = self.Slice.window_width | |
| 631 | + proj.level = self.Slice.window_level | |
| 632 | + proj.threshold_range = int(matrix.min()), int(matrix.max()) | |
| 633 | + proj.spacing = self.Slice.spacing | |
| 634 | + | |
| 635 | + ###### | |
| 636 | + session = ses.Session() | |
| 637 | + filename = proj.name + ".inv3" | |
| 638 | + | |
| 639 | + filename = filename.replace("/", "") # Fix problem case other/Skull_DICOM | |
| 640 | + | |
| 641 | + dirpath = session.CreateProject(filename) | |
| 642 | + | |
| 607 | 643 | def OnOpenBitmapFiles(self, pubsub_evt): |
| 608 | 644 | rec_data = pubsub_evt.data |
| 609 | 645 | bmp_data = bmp.BitmapData() |
| ... | ... | @@ -688,6 +724,26 @@ class Controller(): |
| 688 | 724 | self.LoadProject() |
| 689 | 725 | Publisher.sendMessage("Enable state project", True) |
| 690 | 726 | |
| 727 | + def OnOpenOtherFiles(self, pubsub_evt): | |
| 728 | + filepath = pubsub_evt.data | |
| 729 | + name = filepath.rpartition('\\')[-1].split('.') | |
| 730 | + | |
| 731 | + if name[-1] == 'gz': | |
| 732 | + name[1] = 'nii.gz' | |
| 733 | + | |
| 734 | + suptype = ('hdr', 'nii', 'nii.gz', 'par') | |
| 735 | + filetype = name[1].lower() | |
| 736 | + | |
| 737 | + if filetype in suptype: | |
| 738 | + group = oth.ReadOthers(filepath) | |
| 739 | + else: | |
| 740 | + dialog.ImportInvalidFiles() | |
| 741 | + | |
| 742 | + matrix, matrix_filename = self.OpenOtherFiles(group) | |
| 743 | + self.CreateOtherProject(str(name[0]), matrix, matrix_filename) | |
| 744 | + self.LoadProject() | |
| 745 | + Publisher.sendMessage("Enable state project", True) | |
| 746 | + | |
| 691 | 747 | def OpenDicomGroup(self, dicom_group, interval, file_range, gui=True): |
| 692 | 748 | # Retrieve general DICOM headers |
| 693 | 749 | dicom = dicom_group.GetDicomSample() |
| ... | ... | @@ -773,6 +829,30 @@ class Controller(): |
| 773 | 829 | |
| 774 | 830 | return self.matrix, self.filename, dicom |
| 775 | 831 | |
| 832 | + def OpenOtherFiles(self, group): | |
| 833 | + # Retreaving matrix from image data | |
| 834 | + self.matrix, scalar_range, self.filename = image_utils.img2memmap(group) | |
| 835 | + | |
| 836 | + hdr = group.header | |
| 837 | + hdr.set_data_dtype('int16') | |
| 838 | + dims = hdr.get_zooms() | |
| 839 | + dimsf = tuple([float(s) for s in dims]) | |
| 840 | + | |
| 841 | + wl = float((scalar_range[0] + scalar_range[1]) * 0.5) | |
| 842 | + ww = float((scalar_range[1] - scalar_range[0])) | |
| 843 | + | |
| 844 | + self.Slice = sl.Slice() | |
| 845 | + self.Slice.matrix = self.matrix | |
| 846 | + self.Slice.matrix_filename = self.filename | |
| 847 | + | |
| 848 | + self.Slice.spacing = dimsf | |
| 849 | + self.Slice.window_level = wl | |
| 850 | + self.Slice.window_width = ww | |
| 851 | + | |
| 852 | + scalar_range = int(scalar_range[0]), int(scalar_range[1]) | |
| 853 | + Publisher.sendMessage('Update threshold limits list', scalar_range) | |
| 854 | + return self.matrix, self.filename | |
| 855 | + | |
| 776 | 856 | def LoadImagedataInfo(self): |
| 777 | 857 | proj = prj.Project() |
| 778 | 858 | ... | ... |
invesalius/data/bases.py
| 1 | -from numpy import * | |
| 2 | 1 | from math import sqrt |
| 2 | +import numpy as np | |
| 3 | 3 | |
| 4 | -class Bases: | |
| 5 | - | |
| 6 | - def __init__(self, p1, p2, p3): | |
| 7 | - | |
| 8 | - self.p1 = array([p1[0], p1[1], p1[2]]) | |
| 9 | - self.p2 = array([p2[0], p2[1], p2[2]]) | |
| 10 | - self.p3 = array([p3[0], p3[1], p3[2]]) | |
| 11 | - | |
| 12 | - print "p1: ", self.p1 | |
| 13 | - print "p2: ", self.p2 | |
| 14 | - print "p3: ", self.p3 | |
| 15 | 4 | |
| 16 | - self.sub1 = self.p2 - self.p1 | |
| 17 | - self.sub2 = self.p3 - self.p1 | |
| 18 | - | |
| 19 | - def Basecreation(self): | |
| 20 | - #g1 | |
| 21 | - g1 = self.sub1 | |
| 22 | - | |
| 23 | - #g2 | |
| 24 | - lamb1 = g1[0]*self.sub2[0] + g1[1]*self.sub2[1] + g1[2]*self.sub2[2] | |
| 25 | - lamb2 = dot(g1, g1) | |
| 26 | - lamb = lamb1/lamb2 | |
| 27 | - | |
| 28 | - #Ponto q | |
| 29 | - q = self.p1 + lamb*self.sub1 | |
| 30 | - | |
| 31 | - #g1 e g2 com origem em q | |
| 32 | - g1 = self.p1 - q | |
| 33 | - g2 = self.p3 - q | |
| 34 | - | |
| 35 | - #testa se o g1 nao eh um vetor nulo | |
| 36 | - if g1.any() == False: | |
| 37 | - g1 = self.p2 - q | |
| 38 | - | |
| 39 | - #g3 - Produto vetorial NumPy | |
| 40 | - g3 = cross(g2, g1) | |
| 41 | - | |
| 42 | - #normalizacao dos vetores | |
| 43 | - g1 = g1/sqrt(lamb2) | |
| 44 | - g2 = g2/sqrt(dot(g2, g2)) | |
| 45 | - g3 = g3/sqrt(dot(g3, g3)) | |
| 46 | - | |
| 47 | - M = matrix([[g1[0],g1[1],g1[2]], [g2[0],g2[1],g2[2]], [g3[0],g3[1],g3[2]]]) | |
| 48 | - q.shape = (3, 1) | |
| 49 | - q = matrix(q.copy()) | |
| 50 | - print"M: ", M | |
| 51 | ||
| 52 | - print"q: ", q | |
| 53 | ||
| 54 | - Minv = M.I | |
| 55 | - | |
| 56 | - return M, q, Minv | |
| 57 | - | |
| 58 | -def FlipX(point): | |
| 59 | - | |
| 60 | - point = matrix(point + (0,)) | |
| 61 | - | |
| 62 | - #inverter o eixo z | |
| 63 | - ## possivel explicacaoo -- origem do eixo do imagedata esta no canto | |
| 64 | - ## superior esquerdo e origem da superfice eh no canto inferior esquerdo | |
| 65 | - ## ou a ordem de empilhamento das fatias | |
| 66 | - | |
| 67 | - point[0, 2] = -point[0, 2] | |
| 68 | - | |
| 69 | - #Flip em y | |
| 70 | - Mrot = matrix([[1.0, 0.0, 0.0, 0.0], | |
| 71 | - [0.0, -1.0, 0.0, 0.0], | |
| 72 | - [0.0, 0.0, -1.0, 0.0], | |
| 73 | - [0.0, 0.0, 0.0, 1.0]]) | |
| 74 | - Mtrans = matrix([[1.0, 0, 0, -point[0, 0]], | |
| 75 | - [0.0, 1.0, 0, -point[0, 1]], | |
| 76 | - [0.0, 0.0, 1.0, -point[0, 2]], | |
| 77 | - [0.0, 0.0, 0.0, 1.0]]) | |
| 78 | - Mtrans_return = matrix([[1.0, 0, 0, point[0, 0]], | |
| 5 | +def angle_calculation(ap_axis, coil_axis): | |
| 6 | + """ | |
| 7 | + Calculate angle between two given axis (in degrees) | |
| 8 | + | |
| 9 | + :param ap_axis: anterior posterior axis represented | |
| 10 | + :param coil_axis: tms coil axis | |
| 11 | + :return: angle between the two given axes | |
| 12 | + """ | |
| 13 | + | |
| 14 | + ap_axis = np.array([ap_axis[0], ap_axis[1]]) | |
| 15 | + coil_axis = np.array([float(coil_axis[0]), float(coil_axis[1])]) | |
| 16 | + angle = np.rad2deg(np.arccos((np.dot(ap_axis, coil_axis))/( | |
| 17 | + np.linalg.norm(ap_axis)*np.linalg.norm(coil_axis)))) | |
| 18 | + | |
| 19 | + return float(angle) | |
| 20 | + | |
| 21 | + | |
| 22 | +def base_creation(fiducials): | |
| 23 | + """ | |
| 24 | + Calculate the origin and matrix for coordinate system | |
| 25 | + transformation. | |
| 26 | + q: origin of coordinate system | |
| 27 | + g1, g2, g3: orthogonal vectors of coordinate system | |
| 28 | + | |
| 29 | + :param fiducials: array of 3 rows (p1, p2, p3) and 3 columns (x, y, z) with fiducials coordinates | |
| 30 | + :return: matrix and origin for base transformation | |
| 31 | + """ | |
| 32 | + | |
| 33 | + p1 = fiducials[0, :] | |
| 34 | + p2 = fiducials[1, :] | |
| 35 | + p3 = fiducials[2, :] | |
| 36 | + | |
| 37 | + sub1 = p2 - p1 | |
| 38 | + sub2 = p3 - p1 | |
| 39 | + lamb = (sub1[0]*sub2[0]+sub1[1]*sub2[1]+sub1[2]*sub2[2])/np.dot(sub1, sub1) | |
| 40 | + | |
| 41 | + q = p1 + lamb*sub1 | |
| 42 | + g1 = p1 - q | |
| 43 | + g2 = p3 - q | |
| 44 | + | |
| 45 | + if not g1.any(): | |
| 46 | + g1 = p2 - q | |
| 47 | + | |
| 48 | + g3 = np.cross(g2, g1) | |
| 49 | + | |
| 50 | + g1 = g1/sqrt(np.dot(g1, g1)) | |
| 51 | + g2 = g2/sqrt(np.dot(g2, g2)) | |
| 52 | + g3 = g3/sqrt(np.dot(g3, g3)) | |
| 53 | + | |
| 54 | + m = np.matrix([[g1[0], g1[1], g1[2]], | |
| 55 | + [g2[0], g2[1], g2[2]], | |
| 56 | + [g3[0], g3[1], g3[2]]]) | |
| 57 | + | |
| 58 | + q.shape = (3, 1) | |
| 59 | + q = np.matrix(q.copy()) | |
| 60 | + m_inv = m.I | |
| 61 | + | |
| 62 | + # print"M: ", m | |
| 63 | + # print"q: ", q | |
| 64 | + | |
| 65 | + return m, q, m_inv | |
| 66 | + | |
| 67 | + | |
| 68 | +def calculate_fre(fiducials, minv, n, q1, q2): | |
| 69 | + """ | |
| 70 | + Calculate the Fiducial Registration Error for neuronavigation. | |
| 71 | + | |
| 72 | + :param fiducials: array of 6 rows (image and tracker fiducials) and 3 columns (x, y, z) with coordinates | |
| 73 | + :param minv: inverse matrix given by base creation | |
| 74 | + :param n: base change matrix given by base creation | |
| 75 | + :param q1: origin of first base | |
| 76 | + :param q2: origin of second base | |
| 77 | + :return: float number of fiducial registration error | |
| 78 | + """ | |
| 79 | + | |
| 80 | + img = np.zeros([3, 3]) | |
| 81 | + dist = np.zeros([3, 1]) | |
| 82 | + | |
| 83 | + p1 = np.mat(fiducials[3, :]).reshape(3, 1) | |
| 84 | + p2 = np.mat(fiducials[4, :]).reshape(3, 1) | |
| 85 | + p3 = np.mat(fiducials[5, :]).reshape(3, 1) | |
| 86 | + | |
| 87 | + img[0, :] = np.asarray((q1 + (minv * n) * (p1 - q2)).reshape(1, 3)) | |
| 88 | + img[1, :] = np.asarray((q1 + (minv * n) * (p2 - q2)).reshape(1, 3)) | |
| 89 | + img[2, :] = np.asarray((q1 + (minv * n) * (p3 - q2)).reshape(1, 3)) | |
| 90 | + | |
| 91 | + dist[0] = np.sqrt(np.sum(np.power((img[0, :] - fiducials[0, :]), 2))) | |
| 92 | + dist[1] = np.sqrt(np.sum(np.power((img[1, :] - fiducials[1, :]), 2))) | |
| 93 | + dist[2] = np.sqrt(np.sum(np.power((img[2, :] - fiducials[2, :]), 2))) | |
| 94 | + | |
| 95 | + return float(np.sqrt(np.sum(dist ** 2) / 3)) | |
| 96 | + | |
| 97 | + | |
| 98 | +def flip_x(point): | |
| 99 | + """ | |
| 100 | + Flip coordinates of a vector according to X axis | |
| 101 | + Coronal Images do not require this transformation - 1 tested | |
| 102 | + and for this case, at navigation, the z axis is inverted | |
| 103 | + | |
| 104 | + It's necessary to multiply the z coordinate by (-1). Possibly | |
| 105 | + because the origin of coordinate system of imagedata is | |
| 106 | + located in superior left corner and the origin of VTK scene coordinate | |
| 107 | + system (polygonal surface) is in the interior left corner. Second | |
| 108 | + possibility is the order of slice stacking | |
| 109 | + | |
| 110 | + :param point: list of coordinates x, y and z | |
| 111 | + :return: flipped coordinates | |
| 112 | + """ | |
| 113 | + | |
| 114 | + # TODO: check if the Flip function is related to the X or Y axis | |
| 115 | + | |
| 116 | + point = np.matrix(point + (0,)) | |
| 117 | + point[0, 2] = -point[0, 2] | |
| 118 | + | |
| 119 | + m_rot = np.matrix([[1.0, 0.0, 0.0, 0.0], | |
| 120 | + [0.0, -1.0, 0.0, 0.0], | |
| 121 | + [0.0, 0.0, -1.0, 0.0], | |
| 122 | + [0.0, 0.0, 0.0, 1.0]]) | |
| 123 | + m_trans = np.matrix([[1.0, 0, 0, -point[0, 0]], | |
| 124 | + [0.0, 1.0, 0, -point[0, 1]], | |
| 125 | + [0.0, 0.0, 1.0, -point[0, 2]], | |
| 126 | + [0.0, 0.0, 0.0, 1.0]]) | |
| 127 | + m_trans_return = np.matrix([[1.0, 0, 0, point[0, 0]], | |
| 79 | 128 | [0.0, 1.0, 0, point[0, 1]], |
| 80 | 129 | [0.0, 0.0, 1.0, point[0, 2]], |
| 81 | 130 | [0.0, 0.0, 0.0, 1.0]]) |
| 82 | 131 | |
| 83 | - point_rot = point*Mtrans*Mrot*Mtrans_return | |
| 84 | - x, y, z = point_rot.tolist()[0][:3] | |
| 85 | - return x, y, z | |
| 132 | + point_rot = point*m_trans*m_rot*m_trans_return | |
| 133 | + x, y, z = point_rot.tolist()[0][:3] | |
| 134 | + | |
| 135 | + return x, y, z | ... | ... |
invesalius/data/co_registration.py
| ... | ... | @@ -1,65 +0,0 @@ |
| 1 | -import threading | |
| 2 | - | |
| 3 | -import serial | |
| 4 | -import wx | |
| 5 | -from wx.lib.pubsub import pub as Publisher | |
| 6 | - | |
| 7 | -from numpy import * | |
| 8 | -from math import sqrt | |
| 9 | -from time import sleep | |
| 10 | - | |
| 11 | -class Corregister(threading.Thread): | |
| 12 | - | |
| 13 | - def __init__(self, bases, flag): | |
| 14 | - threading.Thread.__init__(self) | |
| 15 | - self.Minv = bases[0] | |
| 16 | - self.N = bases[1] | |
| 17 | - self.q1 = bases[2] | |
| 18 | - self.q2 = bases[3] | |
| 19 | - self.flag = flag | |
| 20 | - self._pause_ = 0 | |
| 21 | - self.start() | |
| 22 | - | |
| 23 | - def stop(self): | |
| 24 | - # Stop neuronavigation | |
| 25 | - self._pause_ = 1 | |
| 26 | - | |
| 27 | - def Coordinates(self): | |
| 28 | - #Get Polhemus points for neuronavigation | |
| 29 | - ser = serial.Serial(0) | |
| 30 | - ser.write("Y") | |
| 31 | - ser.write("P") | |
| 32 | - str = ser.readline() | |
| 33 | - ser.write("Y") | |
| 34 | - str = str.replace("\r\n","") | |
| 35 | - str = str.replace("-"," -") | |
| 36 | - aostr = [s for s in str.split()] | |
| 37 | - #aoflt -> 0:letter 1:x 2:y 3:z | |
| 38 | - aoflt = [float(aostr[1]), float(aostr[2]), float(aostr[3]), | |
| 39 | - float(aostr[4]), float(aostr[5]), float(aostr[6])] | |
| 40 | - ser.close() | |
| 41 | - | |
| 42 | - #Unit change: inches to millimeters | |
| 43 | - x = 25.4 | |
| 44 | - y = 25.4 | |
| 45 | - z = -25.4 | |
| 46 | - | |
| 47 | - coord = (aoflt[0]*x, aoflt[1]*y, aoflt[2]*z) | |
| 48 | - return coord | |
| 49 | - | |
| 50 | - def run(self): | |
| 51 | - while self.flag == True: | |
| 52 | - #Neuronavigation with Polhemus | |
| 53 | - trck = self.Coordinates() | |
| 54 | - tracker = matrix([[trck[0]], [trck[1]], [trck[2]]]) | |
| 55 | - img = self.q1 + (self.Minv*self.N)*(tracker - self.q2) | |
| 56 | - coord = [float(img[0]), float(img[1]), float(img[2])] | |
| 57 | - coord_cam = float(img[0]), float(img[1]), float(img[2]) | |
| 58 | - Publisher.sendMessage('Set ball reference position based on bound', coord_cam) | |
| 59 | - Publisher.sendMessage('Set camera in volume', coord_cam) | |
| 60 | - wx.CallAfter(Publisher.sendMessage, 'Render volume viewer') | |
| 61 | - wx.CallAfter(Publisher.sendMessage, 'Co-registered Points', coord) | |
| 62 | - sleep(0.005) | |
| 63 | - | |
| 64 | - if self._pause_: | |
| 65 | - return |
| ... | ... | @@ -0,0 +1,277 @@ |
| 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 | + | |
| 20 | +from math import sin, cos | |
| 21 | +import numpy as np | |
| 22 | + | |
| 23 | +from random import uniform | |
| 24 | + | |
| 25 | + | |
| 26 | +def GetCoordinates(trck_init, trck_id, ref_mode): | |
| 27 | + | |
| 28 | + """ | |
| 29 | + Read coordinates from spatial tracking devices using | |
| 30 | + | |
| 31 | + :param trck_init: Initialization variable of tracking device and connection type. See tracker.py. | |
| 32 | + :param trck_id: ID of tracking device. | |
| 33 | + :param ref_mode: Single or dynamic reference mode of tracking. | |
| 34 | + :return: array of six coordinates (x, y, z, alpha, beta, gamma) | |
| 35 | + """ | |
| 36 | + | |
| 37 | + coord = None | |
| 38 | + if trck_id: | |
| 39 | + getcoord = {1: ClaronCoord, | |
| 40 | + 2: PolhemusCoord, | |
| 41 | + 3: PolhemusCoord, | |
| 42 | + 4: PolhemusCoord, | |
| 43 | + 5: DebugCoord} | |
| 44 | + coord = getcoord[trck_id](trck_init, trck_id, ref_mode) | |
| 45 | + else: | |
| 46 | + print "Select Tracker" | |
| 47 | + | |
| 48 | + return coord | |
| 49 | + | |
| 50 | + | |
| 51 | +def ClaronCoord(trck_init, trck_id, ref_mode): | |
| 52 | + trck = trck_init[0] | |
| 53 | + scale = 10. * np.array([1., 1.0, -1.0]) | |
| 54 | + coord = None | |
| 55 | + k = 0 | |
| 56 | + # TODO: try to replace while and use some Claron internal computation | |
| 57 | + if ref_mode: | |
| 58 | + while k < 20: | |
| 59 | + try: | |
| 60 | + trck.Run() | |
| 61 | + probe = np.array([trck.PositionTooltipX1 * scale[0], trck.PositionTooltipY1 * scale[1], | |
| 62 | + trck.PositionTooltipZ1 * scale[2], trck.AngleX1, trck.AngleY1, trck.AngleZ1]) | |
| 63 | + reference = np.array([trck.PositionTooltipX2 * scale[0], trck.PositionTooltipY2 * scale[1], | |
| 64 | + trck.PositionTooltipZ2 * scale[2], trck.AngleX2, trck.AngleY2, trck.AngleZ2]) | |
| 65 | + k = 30 | |
| 66 | + except AttributeError: | |
| 67 | + k += 1 | |
| 68 | + print "wait, collecting coordinates ..." | |
| 69 | + if k == 30: | |
| 70 | + coord = dynamic_reference(probe, reference) | |
| 71 | + else: | |
| 72 | + while k < 20: | |
| 73 | + try: | |
| 74 | + trck.Run() | |
| 75 | + coord = np.array([trck.PositionTooltipX1 * scale[0], trck.PositionTooltipY1 * scale[1], | |
| 76 | + trck.PositionTooltipZ1 * scale[2], trck.AngleX1, trck.AngleY1, trck.AngleZ1]) | |
| 77 | + k = 30 | |
| 78 | + except AttributeError: | |
| 79 | + k += 1 | |
| 80 | + print "wait, collecting coordinates ..." | |
| 81 | + | |
| 82 | + return coord | |
| 83 | + | |
| 84 | + | |
| 85 | +def PolhemusCoord(trck, trck_id, ref_mode): | |
| 86 | + coord = None | |
| 87 | + | |
| 88 | + if trck[1] == 'serial': | |
| 89 | + coord = PolhemusSerialCoord(trck[0], trck_id, ref_mode) | |
| 90 | + | |
| 91 | + elif trck[1] == 'usb': | |
| 92 | + coord = PolhemusUSBCoord(trck[0], trck_id, ref_mode) | |
| 93 | + | |
| 94 | + elif trck[1] == 'wrapper': | |
| 95 | + coord = PolhemusWrapperCoord(trck[0], trck_id, ref_mode) | |
| 96 | + | |
| 97 | + return coord | |
| 98 | + | |
| 99 | + | |
| 100 | +def PolhemusWrapperCoord(trck, trck_id, ref_mode): | |
| 101 | + if trck_id == 2: | |
| 102 | + scale = 10. * np.array([1., 1.0, -1.0]) | |
| 103 | + else: | |
| 104 | + scale = 25.4 * np.array([1., 1.0, -1.0]) | |
| 105 | + coord = None | |
| 106 | + | |
| 107 | + if ref_mode: | |
| 108 | + trck.Run() | |
| 109 | + probe = np.array([float(trck.PositionTooltipX1) * scale[0], float(trck.PositionTooltipY1) * scale[1], | |
| 110 | + float(trck.PositionTooltipZ1) * scale[2], float(trck.AngleX1), float(trck.AngleY1), | |
| 111 | + float(trck.AngleZ1)]) | |
| 112 | + reference = np.array([float(trck.PositionTooltipX2) * scale[0], float(trck.PositionTooltipY2) * scale[1], | |
| 113 | + float(trck.PositionTooltipZ2) * scale[2], float(trck.AngleX2), float(trck.AngleY2), | |
| 114 | + float(trck.AngleZ2)]) | |
| 115 | + | |
| 116 | + if probe.all() and reference.all(): | |
| 117 | + coord = dynamic_reference(probe, reference) | |
| 118 | + | |
| 119 | + else: | |
| 120 | + trck.Run() | |
| 121 | + coord = np.array([float(trck.PositionTooltipX1) * scale[0], float(trck.PositionTooltipY1) * scale[1], | |
| 122 | + float(trck.PositionTooltipZ1) * scale[2], float(trck.AngleX1), float(trck.AngleY1), | |
| 123 | + float(trck.AngleZ1)]) | |
| 124 | + | |
| 125 | + return coord | |
| 126 | + | |
| 127 | + | |
| 128 | +def PolhemusUSBCoord(trck, trck_id, ref_mode): | |
| 129 | + endpoint = trck[0][(0, 0)][0] | |
| 130 | + # Tried to write some settings to Polhemus in trackers.py while initializing the device. | |
| 131 | + # TODO: Check if it's working properly. | |
| 132 | + trck.write(0x02, "P") | |
| 133 | + if trck_id == 2: | |
| 134 | + scale = 10. * np.array([1., 1.0, -1.0]) | |
| 135 | + else: | |
| 136 | + scale = 25.4 * np.array([1., 1.0, -1.0]) | |
| 137 | + coord = None | |
| 138 | + | |
| 139 | + if ref_mode: | |
| 140 | + | |
| 141 | + data = trck.read(endpoint.bEndpointAddress, 2 * endpoint.wMaxPacketSize) | |
| 142 | + data = str2float(data.tostring()) | |
| 143 | + | |
| 144 | + # six coordinates of first and second sensor: x, y, z and alfa, beta and gama | |
| 145 | + # jump one element for reference to avoid the sensor ID returned by Polhemus | |
| 146 | + probe = data[0] * scale[0], data[1] * scale[1], data[2] * scale[2], \ | |
| 147 | + data[3], data[4], data[5], data[6] | |
| 148 | + reference = data[7] * scale[0], data[8] * scale[1], data[9] * scale[2], data[10], \ | |
| 149 | + data[11], data[12], data[13] | |
| 150 | + | |
| 151 | + if probe.all() and reference.all(): | |
| 152 | + coord = dynamic_reference(probe, reference) | |
| 153 | + | |
| 154 | + return coord | |
| 155 | + | |
| 156 | + else: | |
| 157 | + data = trck.read(endpoint.bEndpointAddress, endpoint.wMaxPacketSize) | |
| 158 | + coord = str2float(data.tostring()) | |
| 159 | + | |
| 160 | + coord = np.array((coord[0] * scale[0], coord[1] * scale[1], coord[2] * scale[2], | |
| 161 | + coord[3], coord[4], coord[5])) | |
| 162 | + | |
| 163 | + return coord | |
| 164 | + | |
| 165 | + | |
| 166 | +def PolhemusSerialCoord(trck_init, trck_id, ref_mode): | |
| 167 | + # mudanca para fastrak - ref 1 tem somente x, y, z | |
| 168 | + # aoflt -> 0:letter 1:x 2:y 3:z | |
| 169 | + # this method is not optimized to work with all trackers, only with ISOTRAK | |
| 170 | + # serial connection is obsolete, remove in future | |
| 171 | + trck_init.write("P") | |
| 172 | + lines = trck_init.readlines() | |
| 173 | + | |
| 174 | + coord = None | |
| 175 | + | |
| 176 | + if lines[0][0] != '0': | |
| 177 | + print "The Polhemus is not connected!" | |
| 178 | + else: | |
| 179 | + for s in lines: | |
| 180 | + if s[1] == '1': | |
| 181 | + data = s | |
| 182 | + elif s[1] == '2': | |
| 183 | + data = s | |
| 184 | + | |
| 185 | + # single ref mode | |
| 186 | + if not ref_mode: | |
| 187 | + data = data.replace('-', ' -') | |
| 188 | + data = [s for s in data.split()] | |
| 189 | + j = 0 | |
| 190 | + while j == 0: | |
| 191 | + try: | |
| 192 | + plh1 = [float(s) for s in data[1:len(data)]] | |
| 193 | + j = 1 | |
| 194 | + except ValueError: | |
| 195 | + trck_init.write("P") | |
| 196 | + data = trck_init.readline() | |
| 197 | + data = data.replace('-', ' -') | |
| 198 | + data = [s for s in data.split()] | |
| 199 | + print "Trying to fix the error!!" | |
| 200 | + | |
| 201 | + coord = data[0:6] | |
| 202 | + return coord | |
| 203 | + | |
| 204 | + | |
| 205 | +def DebugCoord(trk_init, trck_id, ref_mode): | |
| 206 | + """ | |
| 207 | + Method to simulate a tracking device for debug and error check. Generate a random | |
| 208 | + x, y, z, alfa, beta and gama coordinates in interval [1, 200[ | |
| 209 | + :param trk_init: tracker initialization instance | |
| 210 | + :param ref_mode: flag for singular of dynamic reference | |
| 211 | + :param trck_id: id of tracking device | |
| 212 | + :return: six coordinates x, y, z, alfa, beta and gama | |
| 213 | + """ | |
| 214 | + | |
| 215 | + if ref_mode: | |
| 216 | + probe = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), | |
| 217 | + uniform(1, 200), uniform(1, 200), uniform(1, 200)]) | |
| 218 | + reference = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), | |
| 219 | + uniform(1, 200), uniform(1, 200), uniform(1, 200)]) | |
| 220 | + | |
| 221 | + coord = dynamic_reference(probe, reference) | |
| 222 | + | |
| 223 | + else: | |
| 224 | + coord = np.array([uniform(1, 200), uniform(1, 200), uniform(1, 200), | |
| 225 | + uniform(1, 200), uniform(1, 200), uniform(1, 200)]) | |
| 226 | + | |
| 227 | + return coord | |
| 228 | + | |
| 229 | + | |
| 230 | +def dynamic_reference(probe, reference): | |
| 231 | + """ | |
| 232 | + Apply dynamic reference correction to probe coordinates. Uses the alpha, beta and gama | |
| 233 | + rotation angles of reference to rotate the probe coordinate and returns the x, y, z | |
| 234 | + difference between probe and reference. Angles sequences and equation was extracted from | |
| 235 | + Polhemus manual and Attitude matrix in Wikipedia. | |
| 236 | + General equation is: | |
| 237 | + coord = Mrot * (probe - reference) | |
| 238 | + :param probe: sensor one defined as probe | |
| 239 | + :param reference: sensor two defined as reference | |
| 240 | + :return: rotated and translated coordinates | |
| 241 | + """ | |
| 242 | + a, b, g = np.radians(reference[3:6]) | |
| 243 | + | |
| 244 | + vet = probe[0:3] - reference[0:3] | |
| 245 | + vet = np.mat(vet.reshape(3, 1)) | |
| 246 | + | |
| 247 | + # Attitude Matrix given by Patriot Manual | |
| 248 | + Mrot = np.mat([[cos(a) * cos(b), sin(b) * sin(g) * cos(a) - cos(g) * sin(a), | |
| 249 | + cos(a) * sin(b) * cos(g) + sin(a) * sin(g)], | |
| 250 | + [cos(b) * sin(a), sin(b) * sin(g) * sin(a) + cos(g) * cos(a), | |
| 251 | + cos(g) * sin(b) * sin(a) - sin(g) * cos(a)], | |
| 252 | + [-sin(b), sin(g) * cos(b), cos(b) * cos(g)]]) | |
| 253 | + | |
| 254 | + coord_rot = Mrot.T * vet | |
| 255 | + coord_rot = np.squeeze(np.asarray(coord_rot)) | |
| 256 | + | |
| 257 | + return coord_rot[0], coord_rot[1], coord_rot[2], probe[3], probe[4], probe[5] | |
| 258 | + | |
| 259 | + | |
| 260 | +def str2float(data): | |
| 261 | + """ | |
| 262 | + Converts string detected wth Polhemus device to float array of coordinates. THis method applies | |
| 263 | + a correction for the minus sign in string that raises error while splitting the string into coordinates. | |
| 264 | + :param data: string of coordinates read with Polhemus | |
| 265 | + :return: six float coordinates x, y, z, alfa, beta and gama | |
| 266 | + """ | |
| 267 | + | |
| 268 | + count = 0 | |
| 269 | + for i, j in enumerate(data): | |
| 270 | + if j == '-': | |
| 271 | + data = data[:i + count] + ' ' + data[i + count:] | |
| 272 | + count += 1 | |
| 273 | + | |
| 274 | + data = [s for s in data.split()] | |
| 275 | + data = [float(s) for s in data[1:len(data)]] | |
| 276 | + | |
| 277 | + return data | ... | ... |
| ... | ... | @@ -0,0 +1,81 @@ |
| 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 | + | |
| 20 | +import threading | |
| 21 | +from time import sleep | |
| 22 | + | |
| 23 | +from numpy import mat | |
| 24 | +import wx | |
| 25 | +from wx.lib.pubsub import pub as Publisher | |
| 26 | + | |
| 27 | +import invesalius.data.coordinates as dco | |
| 28 | + | |
| 29 | +# TODO: Optimize navigation thread. Remove the infinite loop and optimize sleep. | |
| 30 | + | |
| 31 | + | |
| 32 | +class Coregistration(threading.Thread): | |
| 33 | + """ | |
| 34 | + Thread to update the coordinates with the fiducial points | |
| 35 | + co-registration method while the Navigation Button is pressed. | |
| 36 | + Sleep function in run method is used to avoid blocking GUI and | |
| 37 | + for better real-time navigation | |
| 38 | + """ | |
| 39 | + | |
| 40 | + def __init__(self, bases, nav_id, trck_info): | |
| 41 | + threading.Thread.__init__(self) | |
| 42 | + self.bases = bases | |
| 43 | + self.nav_id = nav_id | |
| 44 | + self.trck_info = trck_info | |
| 45 | + self._pause_ = False | |
| 46 | + self.start() | |
| 47 | + | |
| 48 | + def stop(self): | |
| 49 | + self._pause_ = True | |
| 50 | + | |
| 51 | + def run(self): | |
| 52 | + m_inv = self.bases[0] | |
| 53 | + n = self.bases[1] | |
| 54 | + q1 = self.bases[2] | |
| 55 | + q2 = self.bases[3] | |
| 56 | + trck_init = self.trck_info[0] | |
| 57 | + trck_id = self.trck_info[1] | |
| 58 | + trck_mode = self.trck_info[2] | |
| 59 | + | |
| 60 | + while self.nav_id: | |
| 61 | + trck_coord = dco.GetCoordinates(trck_init, trck_id, trck_mode) | |
| 62 | + trck_xyz = mat([[trck_coord[0]], [trck_coord[1]], [trck_coord[2]]]) | |
| 63 | + | |
| 64 | + img = q1 + (m_inv*n)*(trck_xyz - q2) | |
| 65 | + | |
| 66 | + coord = (float(img[0]), float(img[1]), float(img[2]), trck_coord[3], | |
| 67 | + trck_coord[4], trck_coord[5]) | |
| 68 | + | |
| 69 | + # Tried several combinations and different locations to send the messages, | |
| 70 | + # however only this one does not block the GUI during navigation. | |
| 71 | + wx.CallAfter(Publisher.sendMessage, 'Co-registered points', coord[0:3]) | |
| 72 | + wx.CallAfter(Publisher.sendMessage, 'Set camera in volume', coord[0:3]) | |
| 73 | + | |
| 74 | + # TODO: Optimize the value of sleep for each tracking device. | |
| 75 | + # Debug tracker is not working with 0.175 so changed to 0.2 | |
| 76 | + # However, 0.2 is too low update frequency ~5 Hz. Need optimization URGENTLY. | |
| 77 | + sleep(.3) | |
| 78 | + # sleep(0.175) | |
| 79 | + | |
| 80 | + if self._pause_: | |
| 81 | + return | ... | ... |
invesalius/data/imagedata_utils.py
| ... | ... | @@ -592,41 +592,55 @@ def dcm2memmap(files, slice_size, orientation, resolution_percentage): |
| 592 | 592 | |
| 593 | 593 | return matrix, scalar_range, temp_file |
| 594 | 594 | |
| 595 | -def analyze2mmap(analyze): | |
| 596 | - data = analyze.get_data() | |
| 597 | - header = analyze.get_header() | |
| 595 | + | |
| 596 | +def img2memmap(group): | |
| 597 | + """ | |
| 598 | + From a nibabel image data creates a memmap file in the temp folder and | |
| 599 | + returns it and its related filename. | |
| 600 | + """ | |
| 601 | + | |
| 598 | 602 | temp_file = tempfile.mktemp() |
| 599 | 603 | |
| 600 | - # Sagital | |
| 601 | - if header['orient'] == 2: | |
| 602 | - print "Orientation Sagital" | |
| 603 | - shape = tuple([data.shape[i] for i in (1, 2, 0)]) | |
| 604 | - matrix = numpy.memmap(temp_file, mode='w+', dtype=data.dtype, shape=shape) | |
| 605 | - for n, slice in enumerate(data): | |
| 606 | - matrix[:,:, n] = slice | |
| 607 | - | |
| 608 | - # Coronal | |
| 609 | - elif header['orient'] == 1: | |
| 610 | - print "Orientation coronal" | |
| 611 | - shape = tuple([data.shape[i] for i in (1, 0, 2)]) | |
| 612 | - matrix = numpy.memmap(temp_file, mode='w+', dtype=data.dtype, shape=shape) | |
| 613 | - for n, slice in enumerate(data): | |
| 614 | - matrix[:,n,:] = slice | |
| 615 | - | |
| 616 | - # AXIAL | |
| 617 | - elif header['orient'] == 0: | |
| 618 | - print "no orientation" | |
| 619 | - shape = tuple([data.shape[i] for i in (0, 1, 2)]) | |
| 620 | - matrix = numpy.memmap(temp_file, mode='w+', dtype=data.dtype, shape=shape) | |
| 621 | - for n, slice in enumerate(data): | |
| 622 | - matrix[n] = slice | |
| 604 | + data = group.get_data() | |
| 605 | + # Normalize image pixel values and convert to int16 | |
| 606 | + data = imgnormalize(data) | |
| 623 | 607 | |
| 624 | - else: | |
| 625 | - print "Orientation Sagital" | |
| 626 | - shape = tuple([data.shape[i] for i in (1, 2, 0)]) | |
| 627 | - matrix = numpy.memmap(temp_file, mode='w+', dtype=data.dtype, shape=shape) | |
| 628 | - for n, slice in enumerate(data): | |
| 629 | - matrix[:,:, n] = slice | |
| 608 | + # Convert RAS+ to default InVesalius orientation ZYX | |
| 609 | + data = numpy.swapaxes(data, 0, 2) | |
| 610 | + data = numpy.fliplr(data) | |
| 630 | 611 | |
| 612 | + matrix = numpy.memmap(temp_file, mode='w+', dtype=data.dtype, shape=data.shape) | |
| 613 | + matrix[:] = data[:] | |
| 631 | 614 | matrix.flush() |
| 632 | - return matrix, temp_file | |
| 615 | + | |
| 616 | + scalar_range = numpy.amin(matrix), numpy.amax(matrix) | |
| 617 | + | |
| 618 | + return matrix, scalar_range, temp_file | |
| 619 | + | |
| 620 | + | |
| 621 | +def imgnormalize(data, srange=(0, 255)): | |
| 622 | + """ | |
| 623 | + Normalize image pixel intensity for int16 gray scale values. | |
| 624 | + | |
| 625 | + :param data: image matrix | |
| 626 | + :param srange: range for normalization, default is 0 to 255 | |
| 627 | + :return: normalized pixel intensity matrix | |
| 628 | + """ | |
| 629 | + | |
| 630 | + dataf = numpy.asarray(data) | |
| 631 | + rangef = numpy.asarray(srange) | |
| 632 | + faux = numpy.ravel(dataf).astype(float) | |
| 633 | + minimum = numpy.min(faux) | |
| 634 | + maximum = numpy.max(faux) | |
| 635 | + lower = rangef[0] | |
| 636 | + upper = rangef[1] | |
| 637 | + | |
| 638 | + if minimum == maximum: | |
| 639 | + datan = numpy.ones(dataf.shape)*(upper + lower) / 2. | |
| 640 | + else: | |
| 641 | + datan = (faux-minimum)*(upper-lower) / (maximum-minimum) + lower | |
| 642 | + | |
| 643 | + datan = numpy.reshape(datan, dataf.shape) | |
| 644 | + datan = datan.astype(numpy.int16) | |
| 645 | + | |
| 646 | + return datan | |
| 633 | 647 | \ No newline at end of file | ... | ... |
invesalius/data/slice_.py
| ... | ... | @@ -531,10 +531,12 @@ class Slice(object): |
| 531 | 531 | image = self.do_colour_image(ww_wl_image) |
| 532 | 532 | if self.current_mask and self.current_mask.is_shown: |
| 533 | 533 | if self.buffer_slices[orientation].vtk_mask: |
| 534 | - print "Getting from buffer" | |
| 534 | + # Prints that during navigation causes delay in update | |
| 535 | + # print "Getting from buffer" | |
| 535 | 536 | mask = self.buffer_slices[orientation].vtk_mask |
| 536 | 537 | else: |
| 537 | - print "Do not getting from buffer" | |
| 538 | + # Prints that during navigation causes delay in update | |
| 539 | + # print "Do not getting from buffer" | |
| 538 | 540 | n_mask = self.get_mask_slice(orientation, slice_number) |
| 539 | 541 | mask = converters.to_vtk(n_mask, self.spacing, slice_number, orientation) |
| 540 | 542 | mask = self.do_colour_mask(mask, self.opacity) | ... | ... |
invesalius/data/styles.py
| ... | ... | @@ -230,10 +230,8 @@ class CrossInteractorStyle(DefaultInteractorStyle): |
| 230 | 230 | coord = self.viewer.calcultate_scroll_position(px, py) |
| 231 | 231 | Publisher.sendMessage('Update cross position', (wx, wy, wz)) |
| 232 | 232 | self.ScrollSlice(coord) |
| 233 | - Publisher.sendMessage('Set ball reference position based on bound', | |
| 234 | - (wx, wy, wz)) | |
| 233 | + Publisher.sendMessage('Set ball reference position', (wx, wy, wz)) | |
| 235 | 234 | Publisher.sendMessage('Set camera in volume', (wx, wy, wz)) |
| 236 | - Publisher.sendMessage('Render volume viewer') | |
| 237 | 235 | |
| 238 | 236 | iren.Render() |
| 239 | 237 | ... | ... |
| ... | ... | @@ -0,0 +1,222 @@ |
| 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 | + | |
| 20 | +# TODO: Disconnect tracker when a new one is connected | |
| 21 | +# TODO: Test if there are too many prints when connection fails | |
| 22 | + | |
| 23 | + | |
| 24 | +def TrackerConnection(tracker_id, action): | |
| 25 | + """ | |
| 26 | + Initialize spatial trackers for coordinate detection during navigation. | |
| 27 | + | |
| 28 | + :param tracker_id: ID of tracking device. | |
| 29 | + :param action: string with to decide whether connect or disconnect the selected device. | |
| 30 | + :return spatial tracker initialization instance or None if could not open device. | |
| 31 | + """ | |
| 32 | + | |
| 33 | + if action == 'connect': | |
| 34 | + trck_fcn = {1: ClaronTracker, | |
| 35 | + 2: PolhemusTracker, # FASTRAK | |
| 36 | + 3: PolhemusTracker, # ISOTRAK | |
| 37 | + 4: PolhemusTracker, # PATRIOT | |
| 38 | + 5: DebugTracker} | |
| 39 | + | |
| 40 | + trck_init = trck_fcn[tracker_id](tracker_id) | |
| 41 | + | |
| 42 | + elif action == 'disconnect': | |
| 43 | + trck_init = DisconnectTracker(tracker_id) | |
| 44 | + | |
| 45 | + return trck_init | |
| 46 | + | |
| 47 | + | |
| 48 | +def DefaultTracker(tracker_id): | |
| 49 | + trck_init = None | |
| 50 | + try: | |
| 51 | + # import spatial tracker library | |
| 52 | + print 'Connect to default tracking device.' | |
| 53 | + | |
| 54 | + except: | |
| 55 | + print 'Could not connect to default tracker.' | |
| 56 | + | |
| 57 | + # return tracker initialization variable and type of connection | |
| 58 | + return trck_init, 'wrapper' | |
| 59 | + | |
| 60 | + | |
| 61 | +def ClaronTracker(tracker_id): | |
| 62 | + import invesalius.constants as const | |
| 63 | + | |
| 64 | + trck_init = None | |
| 65 | + try: | |
| 66 | + import pyclaron | |
| 67 | + | |
| 68 | + lib_mode = 'wrapper' | |
| 69 | + trck_init = pyclaron.pyclaron() | |
| 70 | + trck_init.CalibrationDir = const.CAL_DIR | |
| 71 | + trck_init.MarkerDir = const.MAR_DIR | |
| 72 | + trck_init.NumberFramesProcessed = 10 | |
| 73 | + trck_init.FramesExtrapolated = 0 | |
| 74 | + trck_init.Initialize() | |
| 75 | + | |
| 76 | + if trck_init.GetIdentifyingCamera(): | |
| 77 | + trck_init.Run() | |
| 78 | + print "MicronTracker camera identified." | |
| 79 | + else: | |
| 80 | + trck_init = None | |
| 81 | + | |
| 82 | + except ImportError: | |
| 83 | + lib_mode = 'error' | |
| 84 | + print 'The ClaronTracker library is not installed.' | |
| 85 | + | |
| 86 | + return trck_init, lib_mode | |
| 87 | + | |
| 88 | + | |
| 89 | +def PolhemusTracker(tracker_id): | |
| 90 | + trck_init = None | |
| 91 | + try: | |
| 92 | + trck_init = PlhWrapperConnection() | |
| 93 | + lib_mode = 'wrapper' | |
| 94 | + if not trck_init: | |
| 95 | + print 'Could not connect with Polhemus wrapper, trying USB connection...' | |
| 96 | + trck_init = PlhUSBConnection(tracker_id) | |
| 97 | + lib_mode = 'usb' | |
| 98 | + if not trck_init: | |
| 99 | + print 'Could not connect with Polhemus USB, trying serial connection...' | |
| 100 | + trck_init = PlhSerialConnection(tracker_id) | |
| 101 | + lib_mode = 'serial' | |
| 102 | + except: | |
| 103 | + lib_mode = 'error' | |
| 104 | + print 'Could not connect to Polhemus.' | |
| 105 | + | |
| 106 | + return trck_init, lib_mode | |
| 107 | + | |
| 108 | + | |
| 109 | +def DebugTracker(tracker_id): | |
| 110 | + trck_init = True | |
| 111 | + print 'Debug device started.' | |
| 112 | + return trck_init, 'debug' | |
| 113 | + | |
| 114 | + | |
| 115 | +def PlhWrapperConnection(): | |
| 116 | + trck_init = None | |
| 117 | + try: | |
| 118 | + import polhemus | |
| 119 | + | |
| 120 | + trck_init = polhemus.polhemus() | |
| 121 | + trck_check = trck_init.Initialize() | |
| 122 | + | |
| 123 | + if trck_check: | |
| 124 | + # First run is necessary to discard the first coord collection | |
| 125 | + trck_init.Run() | |
| 126 | + except: | |
| 127 | + print 'Could not connect to Polhemus via wrapper.' | |
| 128 | + | |
| 129 | + return trck_init | |
| 130 | + | |
| 131 | + | |
| 132 | +def PlhSerialConnection(tracker_id): | |
| 133 | + trck_init = None | |
| 134 | + try: | |
| 135 | + import serial | |
| 136 | + | |
| 137 | + trck_init = serial.Serial(0, baudrate=115200, timeout=0.2) | |
| 138 | + | |
| 139 | + if tracker_id == 2: | |
| 140 | + # Polhemus FASTRAK needs configurations first | |
| 141 | + trck_init.write(0x02, "u") | |
| 142 | + trck_init.write(0x02, "F") | |
| 143 | + elif tracker_id == 3: | |
| 144 | + # Polhemus ISOTRAK needs to set tracking point from | |
| 145 | + # center to tip. | |
| 146 | + trck_init.write("Y") | |
| 147 | + | |
| 148 | + trck_init.write('P') | |
| 149 | + data = trck_init.readlines() | |
| 150 | + | |
| 151 | + if not data: | |
| 152 | + trck_init = None | |
| 153 | + | |
| 154 | + except: | |
| 155 | + print 'Could not connect to Polhemus serial.' | |
| 156 | + | |
| 157 | + return trck_init | |
| 158 | + | |
| 159 | + | |
| 160 | +def PlhUSBConnection(tracker_id): | |
| 161 | + trck_init = None | |
| 162 | + try: | |
| 163 | + import usb.core as uc | |
| 164 | + trck_init = uc.find(idVendor=0x0F44, idProduct=0x0003) | |
| 165 | + cfg = trck_init.get_active_configuration() | |
| 166 | + for i in cfg: | |
| 167 | + for x in i: | |
| 168 | + # TODO: try better code | |
| 169 | + x = x | |
| 170 | + trck_init.set_configuration() | |
| 171 | + endpoint = trck_init[0][(0, 0)][0] | |
| 172 | + if tracker_id == 2: | |
| 173 | + # Polhemus FASTRAK needs configurations first | |
| 174 | + # TODO: Check configurations to standardize initialization for all Polhemus devices | |
| 175 | + trck_init.write(0x02, "u") | |
| 176 | + trck_init.write(0x02, "F") | |
| 177 | + # First run to confirm that everything is working | |
| 178 | + trck_init.write(0x02, "P") | |
| 179 | + data = trck_init.read(endpoint.bEndpointAddress, | |
| 180 | + endpoint.wMaxPacketSize) | |
| 181 | + if not data: | |
| 182 | + trck_init = None | |
| 183 | + | |
| 184 | + except uc.USBError as err: | |
| 185 | + print 'Could not set configuration %s' % err | |
| 186 | + else: | |
| 187 | + print 'Could not connect to Polhemus USB.' | |
| 188 | + | |
| 189 | + return trck_init | |
| 190 | + | |
| 191 | + | |
| 192 | +def DisconnectTracker(tracker_id): | |
| 193 | + """ | |
| 194 | + Disconnect current spatial tracker | |
| 195 | + | |
| 196 | + :param tracker_id: ID of tracking device. | |
| 197 | + """ | |
| 198 | + trck_init = None | |
| 199 | + # TODO: create individual functions to disconnect each other device, e.g. Polhemus. | |
| 200 | + if tracker_id == 1: | |
| 201 | + try: | |
| 202 | + import pyclaron | |
| 203 | + pyclaron.pyclaron().Close() | |
| 204 | + lib_mode = 'wrapper' | |
| 205 | + except ImportError: | |
| 206 | + lib_mode = 'error' | |
| 207 | + print 'The ClaronTracker library is not installed.' | |
| 208 | + | |
| 209 | + elif tracker_id == 4: | |
| 210 | + try: | |
| 211 | + import polhemus | |
| 212 | + polhemus.polhemus().Close() | |
| 213 | + lib_mode = 'wrapper' | |
| 214 | + except ImportError: | |
| 215 | + lib_mode = 'error' | |
| 216 | + print 'The polhemus library is not installed.' | |
| 217 | + | |
| 218 | + elif tracker_id == 5: | |
| 219 | + print 'Debug tracker disconnected.' | |
| 220 | + lib_mode = 'debug' | |
| 221 | + | |
| 222 | + return trck_init, lib_mode | |
| 0 | 223 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,64 @@ |
| 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 | + | |
| 20 | +import threading | |
| 21 | +from time import sleep | |
| 22 | + | |
| 23 | +import wx | |
| 24 | +from wx.lib.pubsub import pub as Publisher | |
| 25 | + | |
| 26 | + | |
| 27 | +class Trigger(threading.Thread): | |
| 28 | + """ | |
| 29 | + Thread created to use external trigger to interact with software during neuronavigation | |
| 30 | + """ | |
| 31 | + | |
| 32 | + def __init__(self, nav_id): | |
| 33 | + threading.Thread.__init__(self) | |
| 34 | + self.trigger_init = None | |
| 35 | + try: | |
| 36 | + import serial | |
| 37 | + | |
| 38 | + self.trigger_init = serial.Serial('COM1', baudrate=115200, timeout=0) | |
| 39 | + self.nav_id = nav_id | |
| 40 | + self._pause_ = False | |
| 41 | + self.start() | |
| 42 | + | |
| 43 | + except ImportError: | |
| 44 | + print 'PySerial library not installed. Please install to use Trigger option.' | |
| 45 | + | |
| 46 | + except serial.serialutil.SerialException: | |
| 47 | + print 'Connection with port COM1 failed.' | |
| 48 | + | |
| 49 | + def stop(self): | |
| 50 | + if self.trigger_init: | |
| 51 | + self.trigger_init.close() | |
| 52 | + self._pause_ = True | |
| 53 | + | |
| 54 | + def run(self): | |
| 55 | + while self.nav_id: | |
| 56 | + lines = self.trigger_init.readlines() | |
| 57 | + # Following lines can simulate a trigger in 3 sec repetitions | |
| 58 | + # sleep(3) | |
| 59 | + # lines = True | |
| 60 | + if lines: | |
| 61 | + wx.CallAfter(Publisher.sendMessage, 'Create marker') | |
| 62 | + sleep(0.175) | |
| 63 | + if self._pause_: | |
| 64 | + return | ... | ... |
invesalius/data/viewer_slice.py
| ... | ... | @@ -911,17 +911,15 @@ class Viewer(wx.Panel): |
| 911 | 911 | if self.slice_data.cursor: |
| 912 | 912 | self.slice_data.cursor.SetColour(colour_vtk) |
| 913 | 913 | |
| 914 | - def Navigation(self, pubsub_evt): | |
| 914 | + def UpdateSlicesNavigation(self, pubsub_evt): | |
| 915 | 915 | # Get point from base change |
| 916 | - x, y, z = pubsub_evt.data | |
| 917 | - coord_cross = x, y, z | |
| 918 | - position = self.slice_data.actor.GetInput().FindPoint(x, y, z) | |
| 919 | - coord_cross = self.slice_data.actor.GetInput().GetPoint(position) | |
| 920 | - coord = self.calcultate_scroll_position(position) | |
| 921 | - Publisher.sendMessage('Update cross position', coord_cross) | |
| 916 | + wx, wy, wz = pubsub_evt.data | |
| 917 | + px, py = self.get_slice_pixel_coord_by_world_pos(wx, wy, wz) | |
| 918 | + coord = self.calcultate_scroll_position(px, py) | |
| 922 | 919 | |
| 920 | + self.cross.SetFocalPoint((wx, wy, wz)) | |
| 923 | 921 | self.ScrollSlice(coord) |
| 924 | - self.interactor.Render() | |
| 922 | + Publisher.sendMessage('Set ball reference position', (wx, wy, wz)) | |
| 925 | 923 | |
| 926 | 924 | def ScrollSlice(self, coord): |
| 927 | 925 | if self.orientation == "AXIAL": |
| ... | ... | @@ -1165,8 +1163,8 @@ class Viewer(wx.Panel): |
| 1165 | 1163 | self.orientation)) |
| 1166 | 1164 | Publisher.subscribe(self.__update_cross_position, |
| 1167 | 1165 | 'Update cross position') |
| 1168 | - Publisher.subscribe(self.Navigation, | |
| 1169 | - 'Co-registered Points') | |
| 1166 | + Publisher.subscribe(self.UpdateSlicesNavigation, | |
| 1167 | + 'Co-registered points') | |
| 1170 | 1168 | ### |
| 1171 | 1169 | # Publisher.subscribe(self.ChangeBrushColour, |
| 1172 | 1170 | # 'Add mask') | ... | ... |
invesalius/data/viewer_volume.py
| ... | ... | @@ -22,7 +22,8 @@ |
| 22 | 22 | |
| 23 | 23 | import sys |
| 24 | 24 | |
| 25 | -import numpy | |
| 25 | +import numpy as np | |
| 26 | +from numpy.core.umath_tests import inner1d | |
| 26 | 27 | import wx |
| 27 | 28 | import vtk |
| 28 | 29 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
| ... | ... | @@ -45,10 +46,11 @@ class Viewer(wx.Panel): |
| 45 | 46 | |
| 46 | 47 | self.interaction_style = st.StyleStateManager() |
| 47 | 48 | |
| 48 | - self.ball_reference = None | |
| 49 | - self.initial_foco = None | |
| 49 | + self.initial_focus = None | |
| 50 | 50 | |
| 51 | - style = vtk.vtkInteractorStyleTrackballCamera() | |
| 51 | + self.staticballs = [] | |
| 52 | + | |
| 53 | + style = vtk.vtkInteractorStyleTrackballCamera() | |
| 52 | 54 | self.style = style |
| 53 | 55 | |
| 54 | 56 | interactor = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize()) |
| ... | ... | @@ -111,6 +113,9 @@ class Viewer(wx.Panel): |
| 111 | 113 | self.repositioned_coronal_plan = 0 |
| 112 | 114 | self.added_actor = 0 |
| 113 | 115 | |
| 116 | + self.camera_state = True | |
| 117 | + | |
| 118 | + self.ball_actor = None | |
| 114 | 119 | self._mode_cross = False |
| 115 | 120 | self._to_show_ball = 0 |
| 116 | 121 | self._ball_ref_visibility = False |
| ... | ... | @@ -153,6 +158,7 @@ class Viewer(wx.Panel): |
| 153 | 158 | |
| 154 | 159 | Publisher.subscribe(self.ResetCamClippingRange, 'Reset cam clipping range') |
| 155 | 160 | Publisher.subscribe(self.SetVolumeCamera, 'Set camera in volume') |
| 161 | + Publisher.subscribe(self.SetVolumeCameraState, 'Update volume camera state') | |
| 156 | 162 | |
| 157 | 163 | Publisher.subscribe(self.OnEnableStyle, 'Enable style') |
| 158 | 164 | Publisher.subscribe(self.OnDisableStyle, 'Disable style') |
| ... | ... | @@ -174,23 +180,24 @@ class Viewer(wx.Panel): |
| 174 | 180 | Publisher.subscribe(self.OnStartSeed,'Create surface by seeding - start') |
| 175 | 181 | Publisher.subscribe(self.OnEndSeed,'Create surface by seeding - end') |
| 176 | 182 | |
| 177 | - Publisher.subscribe(self.ActivateBallReference, | |
| 178 | - 'Activate ball reference') | |
| 179 | - Publisher.subscribe(self.DeactivateBallReference, | |
| 180 | - 'Deactivate ball reference') | |
| 181 | - Publisher.subscribe(self.SetBallReferencePosition, | |
| 182 | - 'Set ball reference position') | |
| 183 | - Publisher.subscribe(self.SetBallReferencePositionBasedOnBound, | |
| 184 | - 'Set ball reference position based on bound') | |
| 185 | 183 | Publisher.subscribe(self.SetStereoMode, 'Set stereo mode') |
| 186 | 184 | |
| 187 | 185 | Publisher.subscribe(self.Reposition3DPlane, 'Reposition 3D Plane') |
| 188 | 186 | |
| 189 | 187 | Publisher.subscribe(self.RemoveVolume, 'Remove Volume') |
| 190 | 188 | |
| 189 | + Publisher.subscribe(self.SetBallReferencePosition, | |
| 190 | + 'Set ball reference position') | |
| 191 | 191 | Publisher.subscribe(self._check_ball_reference, 'Enable style') |
| 192 | 192 | Publisher.subscribe(self._uncheck_ball_reference, 'Disable style') |
| 193 | 193 | |
| 194 | + # Related to marker creation in navigation tools | |
| 195 | + Publisher.subscribe(self.AddMarker, 'Add marker') | |
| 196 | + Publisher.subscribe(self.HideAllMarkers, 'Hide all markers') | |
| 197 | + Publisher.subscribe(self.ShowAllMarkers, 'Show all markers') | |
| 198 | + Publisher.subscribe(self.RemoveAllMarkers, 'Remove all markers') | |
| 199 | + Publisher.subscribe(self.RemoveMarker, 'Remove marker') | |
| 200 | + | |
| 194 | 201 | def SetStereoMode(self, pubsub_evt): |
| 195 | 202 | mode = pubsub_evt.data |
| 196 | 203 | ren_win = self.interactor.GetRenderWindow() |
| ... | ... | @@ -220,43 +227,6 @@ class Viewer(wx.Panel): |
| 220 | 227 | |
| 221 | 228 | self.interactor.Render() |
| 222 | 229 | |
| 223 | - def CreateBallReference(self): | |
| 224 | - MRAD = 3.0 | |
| 225 | - proj = prj.Project() | |
| 226 | - s = proj.spacing | |
| 227 | - # The sphere's radius will be MRAD times bigger than the media of the | |
| 228 | - # spacing values. | |
| 229 | - r = (s[0] + s[1] + s[2]) / 3.0 * MRAD | |
| 230 | - self.ball_reference = vtk.vtkSphereSource() | |
| 231 | - self.ball_reference.SetRadius(r) | |
| 232 | - | |
| 233 | - mapper = vtk.vtkPolyDataMapper() | |
| 234 | - mapper.SetInputConnection(self.ball_reference.GetOutputPort()) | |
| 235 | - | |
| 236 | - p = vtk.vtkProperty() | |
| 237 | - p.SetColor(1, 0, 0) | |
| 238 | - | |
| 239 | - self.ball_actor = vtk.vtkActor() | |
| 240 | - self.ball_actor.SetMapper(mapper) | |
| 241 | - self.ball_actor.SetProperty(p) | |
| 242 | - | |
| 243 | - def RemoveBallReference(self): | |
| 244 | - self._ball_ref_visibility = False | |
| 245 | - if self.ball_reference: | |
| 246 | - self.ren.RemoveActor(self.ball_actor) | |
| 247 | - | |
| 248 | - def ActivateBallReference(self, pubsub_evt): | |
| 249 | - self._mode_cross = True | |
| 250 | - self._ball_ref_visibility = True | |
| 251 | - if self._to_show_ball: | |
| 252 | - if not self.ball_reference: | |
| 253 | - self.CreateBallReference() | |
| 254 | - self.ren.AddActor(self.ball_actor) | |
| 255 | - | |
| 256 | - def DeactivateBallReference(self, pubsub_evt): | |
| 257 | - self._mode_cross = False | |
| 258 | - self.RemoveBallReference() | |
| 259 | - | |
| 260 | 230 | def _check_ball_reference(self, pubsub_evt): |
| 261 | 231 | st = pubsub_evt.data |
| 262 | 232 | if st == const.SLICE_STATE_CROSS: |
| ... | ... | @@ -279,19 +249,6 @@ class Viewer(wx.Panel): |
| 279 | 249 | self._to_show_ball -= 1 |
| 280 | 250 | self._check_and_set_ball_visibility() |
| 281 | 251 | |
| 282 | - def SetBallReferencePosition(self, pubsub_evt): | |
| 283 | - x, y, z = pubsub_evt.data | |
| 284 | - self.ball_reference.SetCenter(x, y, z) | |
| 285 | - | |
| 286 | - def SetBallReferencePositionBasedOnBound(self, pubsub_evt): | |
| 287 | - if self._to_show_ball: | |
| 288 | - self.ActivateBallReference(None) | |
| 289 | - coord = pubsub_evt.data | |
| 290 | - x, y, z = bases.FlipX(coord) | |
| 291 | - self.ball_reference.SetCenter(x, y, z) | |
| 292 | - else: | |
| 293 | - self.DeactivateBallReference(None) | |
| 294 | - | |
| 295 | 252 | def OnStartSeed(self, pubsub_evt): |
| 296 | 253 | index = pubsub_evt.data |
| 297 | 254 | self.seed_points = [] |
| ... | ... | @@ -402,7 +359,6 @@ class Viewer(wx.Panel): |
| 402 | 359 | actor.PickableOff() |
| 403 | 360 | |
| 404 | 361 | self.ren.AddActor(actor) |
| 405 | - | |
| 406 | 362 | self.points_reference.append(actor) |
| 407 | 363 | |
| 408 | 364 | def RemoveAllPointsReference(self): |
| ... | ... | @@ -418,6 +374,113 @@ class Viewer(wx.Panel): |
| 418 | 374 | actor = self.points_reference.pop(point) |
| 419 | 375 | self.ren.RemoveActor(actor) |
| 420 | 376 | |
| 377 | + def AddMarker(self, pubsub_evt): | |
| 378 | + """ | |
| 379 | + Markers create by navigation tools and | |
| 380 | + rendered in volume viewer. | |
| 381 | + """ | |
| 382 | + self.ball_id = pubsub_evt.data[0] | |
| 383 | + ballsize = pubsub_evt.data[1] | |
| 384 | + ballcolour = pubsub_evt.data[2] | |
| 385 | + coord = pubsub_evt.data[3] | |
| 386 | + x, y, z = bases.flip_x(coord) | |
| 387 | + | |
| 388 | + ball_ref = vtk.vtkSphereSource() | |
| 389 | + ball_ref.SetRadius(ballsize) | |
| 390 | + ball_ref.SetCenter(x, y, z) | |
| 391 | + | |
| 392 | + mapper = vtk.vtkPolyDataMapper() | |
| 393 | + mapper.SetInputConnection(ball_ref.GetOutputPort()) | |
| 394 | + | |
| 395 | + prop = vtk.vtkProperty() | |
| 396 | + prop.SetColor(ballcolour) | |
| 397 | + | |
| 398 | + #adding a new actor for the present ball | |
| 399 | + self.staticballs.append(vtk.vtkActor()) | |
| 400 | + | |
| 401 | + self.staticballs[self.ball_id].SetMapper(mapper) | |
| 402 | + self.staticballs[self.ball_id].SetProperty(prop) | |
| 403 | + | |
| 404 | + self.ren.AddActor(self.staticballs[self.ball_id]) | |
| 405 | + self.ball_id = self.ball_id + 1 | |
| 406 | + self.UpdateRender() | |
| 407 | + | |
| 408 | + def HideAllMarkers(self, pubsub_evt): | |
| 409 | + ballid = pubsub_evt.data | |
| 410 | + for i in range(0, ballid): | |
| 411 | + self.staticballs[i].SetVisibility(0) | |
| 412 | + self.UpdateRender() | |
| 413 | + | |
| 414 | + def ShowAllMarkers(self, pubsub_evt): | |
| 415 | + ballid = pubsub_evt.data | |
| 416 | + for i in range(0, ballid): | |
| 417 | + self.staticballs[i].SetVisibility(1) | |
| 418 | + self.UpdateRender() | |
| 419 | + | |
| 420 | + def RemoveAllMarkers(self, pubsub_evt): | |
| 421 | + ballid = pubsub_evt.data | |
| 422 | + for i in range(0, ballid): | |
| 423 | + self.ren.RemoveActor(self.staticballs[i]) | |
| 424 | + self.staticballs = [] | |
| 425 | + self.UpdateRender() | |
| 426 | + | |
| 427 | + def RemoveMarker(self, pubsub_evt): | |
| 428 | + index = pubsub_evt.data | |
| 429 | + self.ren.RemoveActor(self.staticballs[index]) | |
| 430 | + del self.staticballs[index] | |
| 431 | + self.ball_id = self.ball_id - 1 | |
| 432 | + self.UpdateRender() | |
| 433 | + | |
| 434 | + def CreateBallReference(self): | |
| 435 | + """ | |
| 436 | + Red sphere on volume visualization to reference center of | |
| 437 | + cross in slice planes. | |
| 438 | + The sphere's radius will be scale times bigger than the average of | |
| 439 | + image spacing values. | |
| 440 | + """ | |
| 441 | + scale = 3.0 | |
| 442 | + proj = prj.Project() | |
| 443 | + s = proj.spacing | |
| 444 | + r = (s[0] + s[1] + s[2]) / 3.0 * scale | |
| 445 | + | |
| 446 | + ball_source = vtk.vtkSphereSource() | |
| 447 | + ball_source.SetRadius(r) | |
| 448 | + | |
| 449 | + mapper = vtk.vtkPolyDataMapper() | |
| 450 | + mapper.SetInputConnection(ball_source.GetOutputPort()) | |
| 451 | + | |
| 452 | + self.ball_actor = vtk.vtkActor() | |
| 453 | + self.ball_actor.SetMapper(mapper) | |
| 454 | + self.ball_actor.GetProperty().SetColor(1, 0, 0) | |
| 455 | + | |
| 456 | + self.ren.AddActor(self.ball_actor) | |
| 457 | + | |
| 458 | + def ActivateBallReference(self): | |
| 459 | + self._mode_cross = True | |
| 460 | + self._ball_ref_visibility = True | |
| 461 | + if self._to_show_ball: | |
| 462 | + if not self.ball_actor: | |
| 463 | + self.CreateBallReference() | |
| 464 | + | |
| 465 | + def RemoveBallReference(self): | |
| 466 | + self._mode_cross = False | |
| 467 | + self._ball_ref_visibility = False | |
| 468 | + if self.ball_actor: | |
| 469 | + self.ren.RemoveActor(self.ball_actor) | |
| 470 | + self.ball_actor = None | |
| 471 | + | |
| 472 | + def SetBallReferencePosition(self, pubsub_evt): | |
| 473 | + if self._to_show_ball: | |
| 474 | + if not self.ball_actor: | |
| 475 | + self.ActivateBallReference() | |
| 476 | + | |
| 477 | + coord = pubsub_evt.data | |
| 478 | + x, y, z = bases.flip_x(coord) | |
| 479 | + self.ball_actor.SetPosition(x, y, z) | |
| 480 | + | |
| 481 | + else: | |
| 482 | + self.RemoveBallReference() | |
| 483 | + | |
| 421 | 484 | def __bind_events_wx(self): |
| 422 | 485 | #self.Bind(wx.EVT_SIZE, self.OnSize) |
| 423 | 486 | pass |
| ... | ... | @@ -580,30 +643,38 @@ class Viewer(wx.Panel): |
| 580 | 643 | self.ren.ResetCamera() |
| 581 | 644 | self.ren.ResetCameraClippingRange() |
| 582 | 645 | |
| 646 | + def SetVolumeCameraState(self, pubsub_evt): | |
| 647 | + self.camera_state = pubsub_evt.data | |
| 648 | + | |
| 583 | 649 | def SetVolumeCamera(self, pubsub_evt): |
| 584 | - | |
| 585 | - coord_camera = pubsub_evt.data | |
| 586 | - coord_camera = numpy.array(bases.FlipX(coord_camera)) | |
| 587 | - | |
| 588 | - cam = self.ren.GetActiveCamera() | |
| 589 | - | |
| 590 | - if self.initial_foco is None: | |
| 591 | - self.initial_foco = numpy.array(cam.GetFocalPoint()) | |
| 592 | - | |
| 593 | - cam_initialposition = numpy.array(cam.GetPosition()) | |
| 594 | - cam_initialfoco = numpy.array(cam.GetFocalPoint()) | |
| 595 | - | |
| 596 | - cam_sub = cam_initialposition - cam_initialfoco | |
| 597 | - cam_sub_norm = numpy.linalg.norm(cam_sub) | |
| 598 | - vet1 = cam_sub/cam_sub_norm | |
| 599 | - | |
| 600 | - cam_sub_novo = coord_camera - self.initial_foco | |
| 601 | - cam_sub_novo_norm = numpy.linalg.norm(cam_sub_novo) | |
| 602 | - vet2 = cam_sub_novo/cam_sub_novo_norm | |
| 603 | - vet2 = vet2*cam_sub_norm + coord_camera | |
| 604 | - | |
| 605 | - cam.SetFocalPoint(coord_camera) | |
| 606 | - cam.SetPosition(vet2) | |
| 650 | + if self.camera_state: | |
| 651 | + #TODO: exclude dependency on initial focus | |
| 652 | + cam_focus = np.array(bases.flip_x(pubsub_evt.data)) | |
| 653 | + cam = self.ren.GetActiveCamera() | |
| 654 | + | |
| 655 | + if self.initial_focus is None: | |
| 656 | + self.initial_focus = np.array(cam.GetFocalPoint()) | |
| 657 | + | |
| 658 | + cam_pos0 = np.array(cam.GetPosition()) | |
| 659 | + cam_focus0 = np.array(cam.GetFocalPoint()) | |
| 660 | + | |
| 661 | + v0 = cam_pos0 - cam_focus0 | |
| 662 | + v0n = np.sqrt(inner1d(v0, v0)) | |
| 663 | + | |
| 664 | + v1 = (cam_focus - self.initial_focus) | |
| 665 | + v1n = np.sqrt(inner1d(v1, v1)) | |
| 666 | + if not v1n: | |
| 667 | + v1n = 1.0 | |
| 668 | + cam_pos = (v1/v1n)*v0n + cam_focus | |
| 669 | + | |
| 670 | + cam.SetFocalPoint(cam_focus) | |
| 671 | + cam.SetPosition(cam_pos) | |
| 672 | + | |
| 673 | + # It works without doing the reset. Check with trackers if there is any difference. | |
| 674 | + # Need to be outside condition for sphere marker position update | |
| 675 | + # self.ren.ResetCameraClippingRange() | |
| 676 | + # self.ren.ResetCamera() | |
| 677 | + self.interactor.Render() | |
| 607 | 678 | |
| 608 | 679 | def OnExportSurface(self, pubsub_evt): |
| 609 | 680 | filename, filetype = pubsub_evt.data |
| ... | ... | @@ -706,19 +777,18 @@ class Viewer(wx.Panel): |
| 706 | 777 | self.interactor.Render() |
| 707 | 778 | self._to_show_ball -= 1 |
| 708 | 779 | self._check_and_set_ball_visibility() |
| 709 | - | |
| 780 | + | |
| 710 | 781 | def RemoveAllActor(self, pubsub_evt): |
| 711 | 782 | utils.debug("RemoveAllActor") |
| 712 | 783 | self.ren.RemoveAllProps() |
| 713 | 784 | Publisher.sendMessage('Render volume viewer') |
| 714 | 785 | |
| 715 | - | |
| 716 | 786 | def LoadSlicePlane(self, pubsub_evt): |
| 717 | 787 | self.slice_plane = SlicePlane() |
| 718 | 788 | |
| 719 | 789 | def LoadVolume(self, pubsub_evt): |
| 720 | 790 | self.raycasting_volume = True |
| 721 | - #self._to_show_ball += 1 | |
| 791 | + self._to_show_ball += 1 | |
| 722 | 792 | |
| 723 | 793 | volume = pubsub_evt.data[0] |
| 724 | 794 | colour = pubsub_evt.data[1] |
| ... | ... | @@ -895,14 +965,16 @@ class Viewer(wx.Panel): |
| 895 | 965 | self.repositioned_coronal_plan = 1 |
| 896 | 966 | |
| 897 | 967 | def _check_and_set_ball_visibility(self): |
| 968 | + #TODO: When creating Raycasting volume and cross is pressed, it is not | |
| 969 | + # automatically creating the ball reference. | |
| 898 | 970 | if self._mode_cross: |
| 899 | 971 | if self._to_show_ball > 0 and not self._ball_ref_visibility: |
| 900 | - self.ActivateBallReference(None) | |
| 972 | + self.ActivateBallReference() | |
| 901 | 973 | self.interactor.Render() |
| 902 | 974 | elif not self._to_show_ball and self._ball_ref_visibility: |
| 903 | 975 | self.RemoveBallReference() |
| 904 | 976 | self.interactor.Render() |
| 905 | - | |
| 977 | + | |
| 906 | 978 | |
| 907 | 979 | class SlicePlane: |
| 908 | 980 | def __init__(self): | ... | ... |
invesalius/gui/default_tasks.py
| ... | ... | @@ -250,7 +250,8 @@ class UpperTaskPanel(wx.Panel): |
| 250 | 250 | tasks = [(_("Load data"), importer.TaskPanel), |
| 251 | 251 | (_("Select region of interest"), slice_.TaskPanel), |
| 252 | 252 | (_("Configure 3D surface"), surface.TaskPanel), |
| 253 | - (_("Utilize navigation system"), navigator.TaskPanel)] | |
| 253 | + (_("Export data"), exporter.TaskPanel), | |
| 254 | + (_("Navigation system"), navigator.TaskPanel)] | |
| 254 | 255 | |
| 255 | 256 | for i in xrange(len(tasks)): |
| 256 | 257 | (name, panel) = tasks[i] | ... | ... |
invesalius/gui/dialogs.py
| ... | ... | @@ -22,8 +22,11 @@ import os |
| 22 | 22 | import random |
| 23 | 23 | import sys |
| 24 | 24 | |
| 25 | +import vtk | |
| 25 | 26 | import wx |
| 26 | 27 | import wx.combo |
| 28 | + | |
| 29 | +from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor | |
| 27 | 30 | from wx.lib import masked |
| 28 | 31 | from wx.lib.agw import floatspin |
| 29 | 32 | from wx.lib.wordwrap import wordwrap |
| ... | ... | @@ -206,48 +209,27 @@ class ProgressDialog(object): |
| 206 | 209 | self.dlg.Destroy() |
| 207 | 210 | |
| 208 | 211 | |
| 209 | - | |
| 210 | -#--------- | |
| 211 | -WILDCARD_OPEN = "InVesalius 3 project (*.inv3)|*.inv3|"\ | |
| 212 | +# --------- | |
| 213 | +WILDCARD_OPEN = "InVesalius 3 project (*.inv3)|*.inv3|" \ | |
| 212 | 214 | "All files (*.*)|*.*" |
| 213 | 215 | |
| 214 | -WILDCARD_ANALYZE = "Analyze (*.hdr)|*.hdr|"\ | |
| 215 | - "All files (*.*)|*.*" | |
| 216 | +WILDCARD_ANALYZE = "Analyze 7.5 (*.hdr)|*.hdr|" \ | |
| 217 | + "All files (*.*)|*.*" | |
| 216 | 218 | |
| 217 | -def ShowOpenProjectDialog(): | |
| 218 | - # Default system path | |
| 219 | - current_dir = os.path.abspath(".") | |
| 220 | - dlg = wx.FileDialog(None, message=_("Open InVesalius 3 project..."), | |
| 221 | - defaultDir="", | |
| 222 | - defaultFile="", wildcard=WILDCARD_OPEN, | |
| 223 | - style=wx.FD_OPEN|wx.FD_CHANGE_DIR) | |
| 219 | +WILDCARD_NIFTI = "NIfTI 1 (*.nii)|*.nii|" \ | |
| 220 | + "Compressed NIfTI (*.nii.gz)|*.nii.gz|" \ | |
| 221 | + "All files (*.*)|*.*" | |
| 224 | 222 | |
| 225 | - # inv3 filter is default | |
| 226 | - dlg.SetFilterIndex(0) | |
| 223 | +WILDCARD_PARREC = "PAR/REC (*.par)|*.par|" \ | |
| 224 | + "All files (*.*)|*.*" | |
| 227 | 225 | |
| 228 | - # Show the dialog and retrieve the user response. If it is the OK response, | |
| 229 | - # process the data. | |
| 230 | - filepath = None | |
| 231 | - try: | |
| 232 | - if dlg.ShowModal() == wx.ID_OK: | |
| 233 | - # This returns a Python list of files that were selected. | |
| 234 | - filepath = dlg.GetPath() | |
| 235 | - except(wx._core.PyAssertionError): #FIX: win64 | |
| 236 | - filepath = dlg.GetPath() | |
| 237 | 226 | |
| 238 | - # Destroy the dialog. Don't do this until you are done with it! | |
| 239 | - # BAD things can happen otherwise! | |
| 240 | - dlg.Destroy() | |
| 241 | - os.chdir(current_dir) | |
| 242 | - return filepath | |
| 243 | - | |
| 244 | - | |
| 245 | -def ShowOpenAnalyzeDialog(): | |
| 227 | +def ShowOpenProjectDialog(): | |
| 246 | 228 | # Default system path |
| 247 | 229 | current_dir = os.path.abspath(".") |
| 248 | - dlg = wx.FileDialog(None, message=_("Open Analyze file"), | |
| 230 | + dlg = wx.FileDialog(None, message=_("Open InVesalius 3 project..."), | |
| 249 | 231 | defaultDir="", |
| 250 | - defaultFile="", wildcard=WILDCARD_ANALYZE, | |
| 232 | + defaultFile="", wildcard=WILDCARD_OPEN, | |
| 251 | 233 | style=wx.FD_OPEN|wx.FD_CHANGE_DIR) |
| 252 | 234 | |
| 253 | 235 | # inv3 filter is default |
| ... | ... | @@ -260,7 +242,7 @@ def ShowOpenAnalyzeDialog(): |
| 260 | 242 | if dlg.ShowModal() == wx.ID_OK: |
| 261 | 243 | # This returns a Python list of files that were selected. |
| 262 | 244 | filepath = dlg.GetPath() |
| 263 | - except(wx._core.PyAssertionError): #FIX: win64 | |
| 245 | + except(wx._core.PyAssertionError): # FIX: win64 | |
| 264 | 246 | filepath = dlg.GetPath() |
| 265 | 247 | |
| 266 | 248 | # Destroy the dialog. Don't do this until you are done with it! |
| ... | ... | @@ -353,6 +335,47 @@ def ShowImportBitmapDirDialog(): |
| 353 | 335 | return path |
| 354 | 336 | |
| 355 | 337 | |
| 338 | +def ShowImportOtherFilesDialog(id_type): | |
| 339 | + # Default system path | |
| 340 | + current_dir = os.path.abspath(".") | |
| 341 | + dlg = wx.FileDialog(None, message=_("Import Analyze 7.5 file"), | |
| 342 | + defaultDir="", | |
| 343 | + defaultFile="", wildcard=WILDCARD_ANALYZE, | |
| 344 | + style=wx.FD_OPEN | wx.FD_CHANGE_DIR) | |
| 345 | + | |
| 346 | + if id_type == const.ID_NIFTI_IMPORT: | |
| 347 | + dlg.SetMessage(_("Import NIFTi 1 file")) | |
| 348 | + dlg.SetWildcard(WILDCARD_NIFTI) | |
| 349 | + elif id_type == const.ID_PARREC_IMPORT: | |
| 350 | + dlg.SetMessage(_("Import PAR/REC file")) | |
| 351 | + dlg.SetWildcard(WILDCARD_PARREC) | |
| 352 | + | |
| 353 | + # inv3 filter is default | |
| 354 | + dlg.SetFilterIndex(0) | |
| 355 | + | |
| 356 | + # Show the dialog and retrieve the user response. If it is the OK response, | |
| 357 | + # process the data. | |
| 358 | + filename = None | |
| 359 | + try: | |
| 360 | + if dlg.ShowModal() == wx.ID_OK: | |
| 361 | + # GetPath returns in unicode, if a path has non-ascii characters a | |
| 362 | + # UnicodeEncodeError is raised. To avoid this, path is encoded in utf-8 | |
| 363 | + if sys.platform == "win32": | |
| 364 | + filename = dlg.GetPath() | |
| 365 | + else: | |
| 366 | + filename = dlg.GetPath().encode('utf-8') | |
| 367 | + | |
| 368 | + except(wx._core.PyAssertionError): # TODO: error win64 | |
| 369 | + if (dlg.GetPath()): | |
| 370 | + filename = dlg.GetPath() | |
| 371 | + | |
| 372 | + # Destroy the dialog. Don't do this until you are done with it! | |
| 373 | + # BAD things can happen otherwise! | |
| 374 | + dlg.Destroy() | |
| 375 | + os.chdir(current_dir) | |
| 376 | + return filename | |
| 377 | + | |
| 378 | + | |
| 356 | 379 | def ShowSaveAsProjectDialog(default_filename=None): |
| 357 | 380 | current_dir = os.path.abspath(".") |
| 358 | 381 | dlg = wx.FileDialog(None, |
| ... | ... | @@ -380,10 +403,70 @@ def ShowSaveAsProjectDialog(default_filename=None): |
| 380 | 403 | if filename.split(".")[-1] != extension: |
| 381 | 404 | filename = filename + "." + extension |
| 382 | 405 | |
| 406 | + os.chdir(current_dir) | |
| 407 | + return filename | |
| 408 | + | |
| 409 | + | |
| 410 | +# Dialog for neuronavigation markers | |
| 411 | +def ShowSaveMarkersDialog(default_filename=None): | |
| 412 | + current_dir = os.path.abspath(".") | |
| 413 | + dlg = wx.FileDialog(None, | |
| 414 | + _("Save markers as..."), # title | |
| 415 | + "", # last used directory | |
| 416 | + default_filename, | |
| 417 | + _("Markers (*.txt)|*.txt"), | |
| 418 | + wx.SAVE | wx.OVERWRITE_PROMPT) | |
| 419 | + # dlg.SetFilterIndex(0) # default is VTI | |
| 420 | + | |
| 421 | + filename = None | |
| 422 | + try: | |
| 423 | + if dlg.ShowModal() == wx.ID_OK: | |
| 424 | + filename = dlg.GetPath() | |
| 425 | + ok = 1 | |
| 426 | + else: | |
| 427 | + ok = 0 | |
| 428 | + except(wx._core.PyAssertionError): # TODO: fix win64 | |
| 429 | + filename = dlg.GetPath() | |
| 430 | + ok = 1 | |
| 431 | + | |
| 432 | + if (ok): | |
| 433 | + extension = "txt" | |
| 434 | + if sys.platform != 'win32': | |
| 435 | + if filename.split(".")[-1] != extension: | |
| 436 | + filename = filename + "." + extension | |
| 383 | 437 | |
| 384 | 438 | os.chdir(current_dir) |
| 385 | 439 | return filename |
| 386 | 440 | |
| 441 | + | |
| 442 | +def ShowLoadMarkersDialog(): | |
| 443 | + current_dir = os.path.abspath(".") | |
| 444 | + | |
| 445 | + dlg = wx.FileDialog(None, message=_("Load markers"), | |
| 446 | + defaultDir="", | |
| 447 | + defaultFile="", | |
| 448 | + style=wx.OPEN|wx.CHANGE_DIR) | |
| 449 | + | |
| 450 | + # inv3 filter is default | |
| 451 | + dlg.SetFilterIndex(0) | |
| 452 | + | |
| 453 | + # Show the dialog and retrieve the user response. If it is the OK response, | |
| 454 | + # process the data. | |
| 455 | + filepath = None | |
| 456 | + try: | |
| 457 | + if dlg.ShowModal() == wx.ID_OK: | |
| 458 | + # This returns a Python list of files that were selected. | |
| 459 | + filepath = dlg.GetPath() | |
| 460 | + except(wx._core.PyAssertionError): # FIX: win64 | |
| 461 | + filepath = dlg.GetPath() | |
| 462 | + | |
| 463 | + # Destroy the dialog. Don't do this until you are done with it! | |
| 464 | + # BAD things can happen otherwise! | |
| 465 | + dlg.Destroy() | |
| 466 | + os.chdir(current_dir) | |
| 467 | + return filepath | |
| 468 | + | |
| 469 | + | |
| 387 | 470 | class MessageDialog(wx.Dialog): |
| 388 | 471 | def __init__(self, message): |
| 389 | 472 | pre = wx.PreDialog() |
| ... | ... | @@ -509,6 +592,7 @@ def ImportEmptyDirectory(dirpath): |
| 509 | 592 | dlg.ShowModal() |
| 510 | 593 | dlg.Destroy() |
| 511 | 594 | |
| 595 | + | |
| 512 | 596 | def ImportInvalidFiles(ftype="DICOM"): |
| 513 | 597 | if ftype == "Bitmap": |
| 514 | 598 | msg = _("There are no Bitmap, JPEG, PNG or TIFF files in the selected folder.") |
| ... | ... | @@ -524,6 +608,20 @@ def ImportInvalidFiles(ftype="DICOM"): |
| 524 | 608 | dlg.ShowModal() |
| 525 | 609 | dlg.Destroy() |
| 526 | 610 | |
| 611 | + | |
| 612 | +def ImportAnalyzeWarning(): | |
| 613 | + msg1 = _("Warning! InVesalius has limited support to Analyze format.\n") | |
| 614 | + msg2 = _("Slices may be wrongly oriented and functions may not work properly.") | |
| 615 | + if sys.platform == 'darwin': | |
| 616 | + dlg = wx.MessageDialog(None, "", msg1 + msg2, | |
| 617 | + wx.ICON_INFORMATION | wx.OK) | |
| 618 | + else: | |
| 619 | + dlg = wx.MessageDialog(None, msg1 + msg2, "InVesalius 3", | |
| 620 | + wx.ICON_INFORMATION | wx.OK) | |
| 621 | + dlg.ShowModal() | |
| 622 | + dlg.Destroy() | |
| 623 | + | |
| 624 | + | |
| 527 | 625 | def InexistentMask(): |
| 528 | 626 | msg = _("A mask is needed to create a surface.") |
| 529 | 627 | if sys.platform == 'darwin': |
| ... | ... | @@ -593,6 +691,86 @@ def SurfaceSelectionRequiredForDuplication(): |
| 593 | 691 | dlg.ShowModal() |
| 594 | 692 | dlg.Destroy() |
| 595 | 693 | |
| 694 | + | |
| 695 | +# Dialogs for neuronavigation mode | |
| 696 | +def InvalidFiducials(): | |
| 697 | + msg = _("Fiducials are invalid. Select six coordinates.") | |
| 698 | + if sys.platform == 'darwin': | |
| 699 | + dlg = wx.MessageDialog(None, "", msg, | |
| 700 | + wx.ICON_INFORMATION | wx.OK) | |
| 701 | + else: | |
| 702 | + dlg = wx.MessageDialog(None, msg, "InVesalius 3 - Neuronavigator", | |
| 703 | + wx.ICON_INFORMATION | wx.OK) | |
| 704 | + dlg.ShowModal() | |
| 705 | + dlg.Destroy() | |
| 706 | + | |
| 707 | + | |
| 708 | +def NavigationTrackerWarning(trck_id, lib_mode): | |
| 709 | + """ | |
| 710 | + Spatial Tracker connection error | |
| 711 | + """ | |
| 712 | + trck = {1: 'Claron MicronTracker', | |
| 713 | + 2: 'Polhemus FASTRAK', | |
| 714 | + 3: 'Polhemus ISOTRAK', | |
| 715 | + 4: 'Polhemus PATRIOT', | |
| 716 | + 5: 'Debug tracker device'} | |
| 717 | + | |
| 718 | + if lib_mode == 'choose': | |
| 719 | + msg = _('No tracking device selected') | |
| 720 | + elif lib_mode == 'error': | |
| 721 | + msg = trck[trck_id] + _(' is not installed.') | |
| 722 | + elif lib_mode == 'disconnect': | |
| 723 | + msg = trck[trck_id] + _(' disconnected.') | |
| 724 | + else: | |
| 725 | + msg = trck[trck_id] + _(' is not connected.') | |
| 726 | + | |
| 727 | + if sys.platform == 'darwin': | |
| 728 | + dlg = wx.MessageDialog(None, "", msg, | |
| 729 | + wx.ICON_INFORMATION | wx.OK) | |
| 730 | + else: | |
| 731 | + dlg = wx.MessageDialog(None, msg, "InVesalius 3 - Neuronavigator", | |
| 732 | + wx.ICON_INFORMATION | wx.OK) | |
| 733 | + | |
| 734 | + dlg.ShowModal() | |
| 735 | + dlg.Destroy() | |
| 736 | + | |
| 737 | + | |
| 738 | +def InvalidMarkersFile(): | |
| 739 | + msg = _("The TXT file is invalid.") | |
| 740 | + if sys.platform == 'darwin': | |
| 741 | + dlg = wx.MessageDialog(None, "", msg, | |
| 742 | + wx.ICON_INFORMATION | wx.OK) | |
| 743 | + else: | |
| 744 | + dlg = wx.MessageDialog(None, msg, "InVesalius 3 - Neuronavigator", | |
| 745 | + wx.ICON_INFORMATION | wx.OK) | |
| 746 | + dlg.ShowModal() | |
| 747 | + dlg.Destroy() | |
| 748 | + | |
| 749 | + | |
| 750 | +def NoMarkerSelected(): | |
| 751 | + msg = _("No data selected") | |
| 752 | + if sys.platform == 'darwin': | |
| 753 | + dlg = wx.MessageDialog(None, "", msg, | |
| 754 | + wx.ICON_INFORMATION | wx.OK) | |
| 755 | + else: | |
| 756 | + dlg = wx.MessageDialog(None,msg, "InVesalius 3 - Neuronavigator", | |
| 757 | + wx.ICON_INFORMATION | wx.OK) | |
| 758 | + dlg.ShowModal() | |
| 759 | + dlg.Destroy() | |
| 760 | + | |
| 761 | + | |
| 762 | +def EnterMarkerID(default): | |
| 763 | + msg = _("Edit marker ID") | |
| 764 | + if sys.platform == 'darwin': | |
| 765 | + dlg = wx.TextEntryDialog(None, "", msg, defaultValue=default) | |
| 766 | + else: | |
| 767 | + dlg = wx.TextEntryDialog(None, msg, "InVesalius 3", defaultValue=default) | |
| 768 | + dlg.ShowModal() | |
| 769 | + result = dlg.GetValue() | |
| 770 | + dlg.Destroy() | |
| 771 | + return result | |
| 772 | + | |
| 773 | + | |
| 596 | 774 | class NewMask(wx.Dialog): |
| 597 | 775 | def __init__(self, |
| 598 | 776 | parent=None, |
| ... | ... | @@ -828,6 +1006,8 @@ def ShowAboutDialog(parent): |
| 828 | 1006 | info.Developers = ["Paulo Henrique Junqueira Amorim", |
| 829 | 1007 | "Thiago Franco de Moraes", |
| 830 | 1008 | "Jorge Vicente Lopes da Silva", |
| 1009 | + "Victor Hugo de Oliveira e Souza (navigator)", | |
| 1010 | + "Renan Hiroshi Matsuda (navigator)" | |
| 831 | 1011 | "Tatiana Al-Chueyr (former)", |
| 832 | 1012 | "Guilherme Cesar Soares Ruppert (former)", |
| 833 | 1013 | "Fabio de Souza Azevedo (former)", | ... | ... |
invesalius/gui/frame.py
| ... | ... | @@ -396,7 +396,11 @@ class Frame(wx.Frame): |
| 396 | 396 | elif id == const.ID_PROJECT_OPEN: |
| 397 | 397 | self.ShowOpenProject() |
| 398 | 398 | elif id == const.ID_ANALYZE_IMPORT: |
| 399 | - self.ShowAnalyzeImporter() | |
| 399 | + self.ShowImportOtherFiles(id) | |
| 400 | + elif id == const.ID_NIFTI_IMPORT: | |
| 401 | + self.ShowImportOtherFiles(id) | |
| 402 | + elif id == const.ID_PARREC_IMPORT: | |
| 403 | + self.ShowImportOtherFiles(id) | |
| 400 | 404 | elif id == const.ID_TIFF_JPG_PNG: |
| 401 | 405 | self.ShowBitmapImporter() |
| 402 | 406 | elif id == const.ID_PROJECT_SAVE: |
| ... | ... | @@ -538,6 +542,12 @@ class Frame(wx.Frame): |
| 538 | 542 | Show import DICOM panel. as dicom """ |
| 539 | 543 | Publisher.sendMessage('Show import directory dialog') |
| 540 | 544 | |
| 545 | + def ShowImportOtherFiles(self, id_file): | |
| 546 | + """ | |
| 547 | + Show import Analyze, NiFTI1 or PAR/REC dialog. | |
| 548 | + """ | |
| 549 | + Publisher.sendMessage('Show import other files dialog', id_file) | |
| 550 | + | |
| 541 | 551 | def ShowRetrieveDicomPanel(self): |
| 542 | 552 | Publisher.sendMessage('Show retrieve dicom panel') |
| 543 | 553 | |
| ... | ... | @@ -553,12 +563,6 @@ class Frame(wx.Frame): |
| 553 | 563 | """ |
| 554 | 564 | Publisher.sendMessage('Show save dialog', True) |
| 555 | 565 | |
| 556 | - def ShowAnalyzeImporter(self): | |
| 557 | - """ | |
| 558 | - Show save as dialog. | |
| 559 | - """ | |
| 560 | - Publisher.sendMessage('Show analyze dialog', True) | |
| 561 | - | |
| 562 | 566 | def ShowBitmapImporter(self): |
| 563 | 567 | """ |
| 564 | 568 | Tiff, BMP, JPEG and PNG |
| ... | ... | @@ -672,7 +676,9 @@ class MenuBar(wx.MenuBar): |
| 672 | 676 | |
| 673 | 677 | #Import Others Files |
| 674 | 678 | others_file_menu = wx.Menu() |
| 675 | - others_file_menu.Append(const.ID_ANALYZE_IMPORT, "Analyze") | |
| 679 | + others_file_menu.Append(const.ID_ANALYZE_IMPORT, _("Analyze 7.5")) | |
| 680 | + others_file_menu.Append(const.ID_NIFTI_IMPORT, _("NIfTI 1")) | |
| 681 | + others_file_menu.Append(const.ID_PARREC_IMPORT, _("PAR/REC")) | |
| 676 | 682 | others_file_menu.Append(const.ID_TIFF_JPG_PNG, u"TIFF,BMP,JPG or PNG (\xb5CT)") |
| 677 | 683 | |
| 678 | 684 | # FILE | ... | ... |
invesalius/gui/task_importer.py
| ... | ... | @@ -64,8 +64,8 @@ class InnerTaskPanel(wx.Panel): |
| 64 | 64 | self.float_hyper_list = [] |
| 65 | 65 | |
| 66 | 66 | # Fixed hyperlink items |
| 67 | - tooltip = wx.ToolTip(_("Select DICOM files to be reconstructed")) | |
| 68 | - link_import_local = hl.HyperLinkCtrl(self, -1, _("Import DICOM images...")) | |
| 67 | + tooltip = wx.ToolTip(_("Select DICOM, Analyze, NIfTI or REC/PAR files to be reconstructed")) | |
| 68 | + link_import_local = hl.HyperLinkCtrl(self, -1, _("Import medical images...")) | |
| 69 | 69 | link_import_local.SetUnderlines(False, False, False) |
| 70 | 70 | link_import_local.SetBold(True) |
| 71 | 71 | link_import_local.SetColours("BLACK", "BLACK", "BLACK") | ... | ... |
invesalius/gui/task_navigator.py
| ... | ... | @@ -17,433 +17,727 @@ |
| 17 | 17 | # detalhes. |
| 18 | 18 | #-------------------------------------------------------------------------- |
| 19 | 19 | |
| 20 | -import os | |
| 20 | +from functools import partial | |
| 21 | 21 | import sys |
| 22 | 22 | |
| 23 | -import serial | |
| 23 | +import numpy as np | |
| 24 | 24 | import wx |
| 25 | 25 | import wx.lib.hyperlink as hl |
| 26 | 26 | import wx.lib.masked.numctrl |
| 27 | -import wx.lib.platebtn as pbtn | |
| 28 | 27 | from wx.lib.pubsub import pub as Publisher |
| 29 | 28 | |
| 29 | +import invesalius.constants as const | |
| 30 | 30 | import invesalius.data.bases as db |
| 31 | -import invesalius.data.co_registration as dcr | |
| 32 | -import invesalius.project as project | |
| 33 | -IR1 = wx.NewId() | |
| 34 | -IR2 = wx.NewId() | |
| 35 | -IR3 = wx.NewId() | |
| 36 | -PR1 = wx.NewId() | |
| 37 | -PR2 = wx.NewId() | |
| 38 | -PR3 = wx.NewId() | |
| 39 | -Neuronavigate = wx.NewId() | |
| 40 | -Corregistration = wx.NewId() | |
| 41 | -GetPoint = wx.NewId() | |
| 31 | +import invesalius.data.coordinates as dco | |
| 32 | +import invesalius.data.coregistration as dcr | |
| 33 | +import invesalius.data.trackers as dt | |
| 34 | +import invesalius.data.trigger as trig | |
| 35 | +import invesalius.gui.dialogs as dlg | |
| 36 | +import invesalius.gui.widgets.foldpanelbar as fpb | |
| 37 | +import invesalius.gui.widgets.colourselect as csel | |
| 38 | + | |
| 42 | 39 | |
| 43 | 40 | class TaskPanel(wx.Panel): |
| 44 | - """ | |
| 45 | - This panel works as a "frame", drawing a white margin arround | |
| 46 | - the panel that really matters (InnerTaskPanel). | |
| 47 | - """ | |
| 48 | 41 | def __init__(self, parent): |
| 49 | - # note: don't change this class!!! | |
| 50 | 42 | wx.Panel.__init__(self, parent) |
| 51 | 43 | |
| 52 | 44 | inner_panel = InnerTaskPanel(self) |
| 53 | 45 | |
| 54 | 46 | sizer = wx.BoxSizer(wx.HORIZONTAL) |
| 55 | - sizer.Add(inner_panel, 1, wx.EXPAND | wx.GROW | wx.BOTTOM | wx.RIGHT | | |
| 56 | - wx.LEFT, 8) | |
| 47 | + sizer.Add(inner_panel, 1, wx.EXPAND|wx.GROW|wx.BOTTOM|wx.RIGHT | | |
| 48 | + wx.LEFT, 7) | |
| 57 | 49 | sizer.Fit(self) |
| 58 | 50 | |
| 59 | 51 | self.SetSizer(sizer) |
| 60 | 52 | self.Update() |
| 61 | 53 | self.SetAutoLayout(1) |
| 62 | 54 | |
| 55 | + | |
| 63 | 56 | class InnerTaskPanel(wx.Panel): |
| 57 | + def __init__(self, parent): | |
| 58 | + wx.Panel.__init__(self, parent) | |
| 59 | + default_colour = self.GetBackgroundColour() | |
| 60 | + background_colour = wx.Colour(255,255,255) | |
| 61 | + self.SetBackgroundColour(background_colour) | |
| 62 | + | |
| 63 | + txt_nav = wx.StaticText(self, -1, _('Select fiducials and navigate'), | |
| 64 | + size=wx.Size(90, 20)) | |
| 65 | + txt_nav.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) | |
| 66 | + | |
| 67 | + # Create horizontal sizer to represent lines in the panel | |
| 68 | + txt_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 69 | + txt_sizer.Add(txt_nav, 1, wx.EXPAND|wx.GROW, 5) | |
| 70 | + | |
| 71 | + # Fold panel which contains navigation configurations | |
| 72 | + fold_panel = FoldPanel(self) | |
| 73 | + fold_panel.SetBackgroundColour(default_colour) | |
| 74 | + | |
| 75 | + | |
| 76 | + # Add line sizer into main sizer | |
| 77 | + main_sizer = wx.BoxSizer(wx.VERTICAL) | |
| 78 | + main_sizer.Add(txt_sizer, 0, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT, 5) | |
| 79 | + main_sizer.Add(fold_panel, 1, wx.GROW|wx.EXPAND|wx.LEFT|wx.RIGHT, 5) | |
| 80 | + main_sizer.AddSpacer(5) | |
| 81 | + main_sizer.Fit(self) | |
| 82 | + | |
| 83 | + self.SetSizerAndFit(main_sizer) | |
| 84 | + self.Update() | |
| 85 | + self.SetAutoLayout(1) | |
| 64 | 86 | |
| 87 | + self.sizer = main_sizer | |
| 88 | + | |
| 89 | + | |
| 90 | +class FoldPanel(wx.Panel): | |
| 65 | 91 | def __init__(self, parent): |
| 66 | - wx.Panel.__init__(self, parent, size=wx.Size(320,300)) | |
| 67 | - self.SetBackgroundColour(wx.Colour(221, 221, 221, 255)) | |
| 92 | + wx.Panel.__init__(self, parent) | |
| 93 | + | |
| 94 | + inner_panel = InnerFoldPanel(self) | |
| 95 | + | |
| 96 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
| 97 | + sizer.Add(inner_panel, 0, wx.EXPAND|wx.GROW) | |
| 98 | + sizer.Fit(self) | |
| 99 | + | |
| 100 | + self.SetSizerAndFit(sizer) | |
| 101 | + self.Update() | |
| 68 | 102 | self.SetAutoLayout(1) |
| 69 | - self.__bind_events() | |
| 70 | 103 | |
| 71 | - self.aux_img_ref1 = 0 | |
| 72 | - self.aux_img_ref2 = 0 | |
| 73 | - self.aux_img_ref3 = 0 | |
| 74 | - self.flagpoint = 0 | |
| 75 | - self.aux_plh_ref1 = 1 | |
| 76 | - self.aux_plh_ref2 = 1 | |
| 77 | - self.aux_plh_ref3 = 1 | |
| 78 | - self.a = 0, 0, 0 | |
| 79 | - self.coord1a = (0, 0, 0) | |
| 80 | - self.coord2a = (0, 0, 0) | |
| 81 | - self.coord3a = (0, 0, 0) | |
| 82 | - self.coord1b = (0, 0, 0) | |
| 83 | - self.coord2b = (0, 0, 0) | |
| 84 | - self.coord3b = (0, 0, 0) | |
| 85 | - self.correg = None | |
| 86 | - | |
| 87 | 104 | |
| 88 | - self.button_img_ref1 = wx.ToggleButton(self, IR1, label = 'TEI', size = wx.Size(30,23)) | |
| 89 | - self.button_img_ref1.Bind(wx.EVT_TOGGLEBUTTON, self.Img_Ref_ToggleButton1) | |
| 90 | - | |
| 91 | - self.button_img_ref2 = wx.ToggleButton(self, IR2, label = 'TDI', size = wx.Size(30,23)) | |
| 92 | - self.button_img_ref2.Bind(wx.EVT_TOGGLEBUTTON, self.Img_Ref_ToggleButton2) | |
| 93 | - | |
| 94 | - self.button_img_ref3 = wx.ToggleButton(self, IR3, label = 'FNI', size = wx.Size(30,23)) | |
| 95 | - self.button_img_ref3.Bind(wx.EVT_TOGGLEBUTTON, self.Img_Ref_ToggleButton3) | |
| 96 | - | |
| 97 | - self.button_plh_ref1 = wx.Button(self, PR1, label = 'TEP', size = wx.Size(30,23)) | |
| 98 | - self.button_plh_ref2 = wx.Button(self, PR2, label = 'TDP', size = wx.Size(30,23)) | |
| 99 | - self.button_plh_ref3 = wx.Button(self, PR3, label = 'FNP', size = wx.Size(30,23)) | |
| 100 | - self.button_crg = wx.Button(self, Corregistration, label = 'Corregistrate') | |
| 101 | - self.button_getpoint = wx.Button(self, GetPoint, label = 'GP', size = wx.Size(23,23)) | |
| 102 | - self.Bind(wx.EVT_BUTTON, self.Buttons) | |
| 103 | - | |
| 104 | - self.button_neuronavigate = wx.ToggleButton(self, Neuronavigate, "Neuronavigate") | |
| 105 | - self.button_neuronavigate.Bind(wx.EVT_TOGGLEBUTTON, self.Neuronavigate_ToggleButton) | |
| 106 | - | |
| 107 | - self.numCtrl1a = wx.lib.masked.numctrl.NumCtrl( | |
| 108 | - name='numCtrl1a', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 109 | - self.numCtrl2a = wx.lib.masked.numctrl.NumCtrl( | |
| 110 | - name='numCtrl2a', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 111 | - self.numCtrl3a = wx.lib.masked.numctrl.NumCtrl( | |
| 112 | - name='numCtrl3a', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 113 | - self.numCtrl1b = wx.lib.masked.numctrl.NumCtrl( | |
| 114 | - name='numCtrl1b', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 115 | - self.numCtrl2b = wx.lib.masked.numctrl.NumCtrl( | |
| 116 | - name='numCtrl2b', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 117 | - self.numCtrl3b = wx.lib.masked.numctrl.NumCtrl( | |
| 118 | - name='numCtrl3b', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 119 | - self.numCtrl1c = wx.lib.masked.numctrl.NumCtrl( | |
| 120 | - name='numCtrl1c', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 121 | - self.numCtrl2c = wx.lib.masked.numctrl.NumCtrl( | |
| 122 | - name='numCtrl2c', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 123 | - self.numCtrl3c = wx.lib.masked.numctrl.NumCtrl( | |
| 124 | - name='numCtrl3c', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 125 | - self.numCtrl1d = wx.lib.masked.numctrl.NumCtrl( | |
| 126 | - name='numCtrl1d', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 127 | - self.numCtrl2d = wx.lib.masked.numctrl.NumCtrl( | |
| 128 | - name='numCtrl2d', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 129 | - self.numCtrl3d = wx.lib.masked.numctrl.NumCtrl( | |
| 130 | - name='numCtrl3d', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 131 | - self.numCtrl1e = wx.lib.masked.numctrl.NumCtrl( | |
| 132 | - name='numCtrl1e', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 133 | - self.numCtrl2e = wx.lib.masked.numctrl.NumCtrl( | |
| 134 | - name='numCtrl2e', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 135 | - self.numCtrl3e = wx.lib.masked.numctrl.NumCtrl( | |
| 136 | - name='numCtrl3e', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 137 | - self.numCtrl1f = wx.lib.masked.numctrl.NumCtrl( | |
| 138 | - name='numCtrl1f', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 139 | - self.numCtrl2f = wx.lib.masked.numctrl.NumCtrl( | |
| 140 | - name='numCtrl2f', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 141 | - self.numCtrl3f = wx.lib.masked.numctrl.NumCtrl( | |
| 142 | - name='numCtrl3f', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 143 | - self.numCtrl1g = wx.lib.masked.numctrl.NumCtrl( | |
| 144 | - name='numCtrl1g', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 145 | - self.numCtrl2g = wx.lib.masked.numctrl.NumCtrl( | |
| 146 | - name='numCtrl2g', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 147 | - self.numCtrl3g = wx.lib.masked.numctrl.NumCtrl( | |
| 148 | - name='numCtrl3g', parent=self, integerWidth = 4, fractionWidth = 1) | |
| 149 | - | |
| 150 | - RefImg_sizer1 = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5) | |
| 151 | - RefImg_sizer1.AddMany([ (self.button_img_ref1), | |
| 152 | - (self.numCtrl1a), | |
| 153 | - (self.numCtrl2a), | |
| 154 | - (self.numCtrl3a)]) | |
| 155 | - | |
| 156 | - RefImg_sizer2 = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5) | |
| 157 | - RefImg_sizer2.AddMany([ (self.button_img_ref2), | |
| 158 | - (self.numCtrl1b), | |
| 159 | - (self.numCtrl2b), | |
| 160 | - (self.numCtrl3b)]) | |
| 161 | - | |
| 162 | - RefImg_sizer3 = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5) | |
| 163 | - RefImg_sizer3.AddMany([ (self.button_img_ref3), | |
| 164 | - (self.numCtrl1c), | |
| 165 | - (self.numCtrl2c), | |
| 166 | - (self.numCtrl3c)]) | |
| 167 | - | |
| 168 | - RefPlh_sizer1 = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5) | |
| 169 | - RefPlh_sizer1.AddMany([ (self.button_plh_ref1, 0, wx.GROW|wx.EXPAND), | |
| 170 | - (self.numCtrl1d, wx.RIGHT), | |
| 171 | - (self.numCtrl2d), | |
| 172 | - (self.numCtrl3d, wx.LEFT)]) | |
| 173 | - | |
| 174 | - RefPlh_sizer2 = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5) | |
| 175 | - RefPlh_sizer2.AddMany([ (self.button_plh_ref2, 0, wx.GROW|wx.EXPAND), | |
| 176 | - (self.numCtrl1e, 0, wx.RIGHT), | |
| 177 | - (self.numCtrl2e), | |
| 178 | - (self.numCtrl3e, 0, wx.LEFT)]) | |
| 179 | - | |
| 180 | - RefPlh_sizer3 = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5) | |
| 181 | - RefPlh_sizer3.AddMany([ (self.button_plh_ref3, 0, wx.GROW|wx.EXPAND), | |
| 182 | - (self.numCtrl1f, wx.RIGHT), | |
| 183 | - (self.numCtrl2f), | |
| 184 | - (self.numCtrl3f, wx.LEFT)]) | |
| 185 | - | |
| 186 | - Buttons_sizer4 = wx.FlexGridSizer(rows=1, cols=3, hgap=5, vgap=5) | |
| 187 | - Buttons_sizer4.AddMany([ (self.button_crg, wx.RIGHT), | |
| 188 | - (self.button_neuronavigate, wx.LEFT)]) | |
| 189 | - | |
| 190 | - GetPoint_sizer5 = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5) | |
| 191 | - GetPoint_sizer5.AddMany([ (self.button_getpoint, 0, wx.GROW|wx.EXPAND), | |
| 192 | - (self.numCtrl1g, wx.RIGHT), | |
| 193 | - (self.numCtrl2g), | |
| 194 | - (self.numCtrl3g, wx.LEFT)]) | |
| 195 | - | |
| 196 | - text = wx.StaticText(self, -1, 'Neuronavigator') | |
| 105 | +class InnerFoldPanel(wx.Panel): | |
| 106 | + def __init__(self, parent): | |
| 107 | + wx.Panel.__init__(self, parent) | |
| 108 | + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) | |
| 109 | + self.SetBackgroundColour(default_colour) | |
| 110 | + | |
| 111 | + # Fold panel and its style settings | |
| 112 | + # FIXME: If we dont insert a value in size or if we set wx.DefaultSize, | |
| 113 | + # the fold_panel doesnt show. This means that, for some reason, Sizer | |
| 114 | + # is not working properly in this panel. It might be on some child or | |
| 115 | + # parent panel. Perhaps we need to insert the item into the sizer also... | |
| 116 | + # Study this. | |
| 117 | + | |
| 118 | + fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, | |
| 119 | + (10, 293), 0, fpb.FPB_SINGLE_FOLD) | |
| 120 | + | |
| 121 | + # Fold panel style | |
| 122 | + style = fpb.CaptionBarStyle() | |
| 123 | + style.SetCaptionStyle(fpb.CAPTIONBAR_GRADIENT_V) | |
| 124 | + style.SetFirstColour(default_colour) | |
| 125 | + style.SetSecondColour(default_colour) | |
| 126 | + | |
| 127 | + # Fold 1 - Navigation panel | |
| 128 | + item = fold_panel.AddFoldPanel(_("Neuronavigation"), collapsed=True) | |
| 129 | + ntw = NeuronavigationPanel(item) | |
| 130 | + | |
| 131 | + fold_panel.ApplyCaptionStyle(item, style) | |
| 132 | + fold_panel.AddFoldPanelWindow(item, ntw, Spacing= 0, | |
| 133 | + leftSpacing=0, rightSpacing=0) | |
| 134 | + fold_panel.Expand(fold_panel.GetFoldPanel(0)) | |
| 135 | + | |
| 136 | + # Fold 2 - Markers panel | |
| 137 | + item = fold_panel.AddFoldPanel(_("Extra tools"), collapsed=True) | |
| 138 | + mtw = MarkersPanel(item) | |
| 139 | + | |
| 140 | + fold_panel.ApplyCaptionStyle(item, style) | |
| 141 | + fold_panel.AddFoldPanelWindow(item, mtw, Spacing= 0, | |
| 142 | + leftSpacing=0, rightSpacing=0) | |
| 143 | + | |
| 197 | 144 | |
| 198 | - Ref_sizer = wx.FlexGridSizer(rows=9, cols=1, hgap=5, vgap=5) | |
| 199 | - Ref_sizer.AddGrowableCol(0, 1) | |
| 200 | - Ref_sizer.AddGrowableRow(0, 1) | |
| 201 | - Ref_sizer.AddGrowableRow(1, 1) | |
| 202 | - Ref_sizer.AddGrowableRow(2, 1) | |
| 203 | - Ref_sizer.AddGrowableRow(3, 1) | |
| 204 | - Ref_sizer.AddGrowableRow(4, 1) | |
| 205 | - Ref_sizer.AddGrowableRow(5, 1) | |
| 206 | - Ref_sizer.AddGrowableRow(6, 1) | |
| 207 | - Ref_sizer.AddGrowableRow(7, 1) | |
| 208 | - Ref_sizer.AddGrowableRow(8, 1) | |
| 209 | - Ref_sizer.SetFlexibleDirection(wx.BOTH) | |
| 210 | - Ref_sizer.AddMany([ (text, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 211 | - (RefImg_sizer1, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 212 | - (RefImg_sizer2, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 213 | - (RefImg_sizer3, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 214 | - (RefPlh_sizer1, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 215 | - (RefPlh_sizer2, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 216 | - (RefPlh_sizer3, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 217 | - (Buttons_sizer4, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 218 | - (GetPoint_sizer5, 0, wx.ALIGN_CENTER_HORIZONTAL)]) | |
| 145 | + # Check box for camera update in volume rendering during navigation | |
| 146 | + tooltip = wx.ToolTip(_("Update camera in volume")) | |
| 147 | + checkcamera = wx.CheckBox(self, -1, _('Volume camera')) | |
| 148 | + checkcamera.SetToolTip(tooltip) | |
| 149 | + checkcamera.SetValue(True) | |
| 150 | + checkcamera.Bind(wx.EVT_CHECKBOX, partial(self.UpdateVolumeCamera, ctrl=checkcamera)) | |
| 151 | + | |
| 152 | + # Check box for camera update in volume rendering during navigation | |
| 153 | + tooltip = wx.ToolTip(_("Enable external trigger for creating markers")) | |
| 154 | + checktrigger = wx.CheckBox(self, -1, _('External trigger')) | |
| 155 | + checktrigger.SetToolTip(tooltip) | |
| 156 | + checktrigger.SetValue(False) | |
| 157 | + checktrigger.Bind(wx.EVT_CHECKBOX, partial(self.UpdateExternalTrigger, ctrl=checktrigger)) | |
| 158 | + | |
| 159 | + if sys.platform != 'win32': | |
| 160 | + checkcamera.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) | |
| 161 | + checktrigger.SetWindowVariant(wx.WINDOW_VARIANT_SMALL) | |
| 162 | + | |
| 163 | + line_sizer = wx.BoxSizer(wx.HORIZONTAL) | |
| 164 | + line_sizer.Add(checkcamera, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.LEFT, 5) | |
| 165 | + line_sizer.Add(checktrigger, 1,wx.ALIGN_RIGHT | wx.RIGHT | wx.LEFT, 5) | |
| 166 | + line_sizer.Fit(self) | |
| 167 | + | |
| 168 | + # Panel sizer to expand fold panel | |
| 169 | + sizer = wx.BoxSizer(wx.VERTICAL) | |
| 170 | + sizer.Add(fold_panel, 0, wx.GROW|wx.EXPAND) | |
| 171 | + sizer.Add(line_sizer, 1, wx.GROW | wx.EXPAND) | |
| 172 | + sizer.Fit(self) | |
| 173 | + | |
| 174 | + self.SetSizer(sizer) | |
| 175 | + self.Update() | |
| 176 | + self.SetAutoLayout(1) | |
| 219 | 177 | |
| 178 | + def UpdateExternalTrigger(self, evt, ctrl): | |
| 179 | + Publisher.sendMessage('Update trigger state', ctrl.GetValue()) | |
| 180 | + | |
| 181 | + def UpdateVolumeCamera(self, evt, ctrl): | |
| 182 | + Publisher.sendMessage('Update volume camera state', ctrl.GetValue()) | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | +class NeuronavigationPanel(wx.Panel): | |
| 188 | + def __init__(self, parent): | |
| 189 | + wx.Panel.__init__(self, parent) | |
| 190 | + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) | |
| 191 | + self.SetBackgroundColour(default_colour) | |
| 192 | + | |
| 193 | + self.SetAutoLayout(1) | |
| 194 | + | |
| 195 | + self.__bind_events() | |
| 196 | + | |
| 197 | + # Initialize global variables | |
| 198 | + self.fiducials = np.full([6, 3], np.nan) | |
| 199 | + self.correg = None | |
| 200 | + self.current_coord = 0, 0, 0 | |
| 201 | + self.trk_init = None | |
| 202 | + self.trigger = None | |
| 203 | + self.trigger_state = False | |
| 204 | + | |
| 205 | + self.tracker_id = const.DEFAULT_TRACKER | |
| 206 | + self.ref_mode_id = const.DEFAULT_REF_MODE | |
| 207 | + | |
| 208 | + # Initialize list of buttons and numctrls for wx objects | |
| 209 | + self.btns_coord = [None] * 7 | |
| 210 | + self.numctrls_coord = [list(), list(), list(), list(), list(), list(), list()] | |
| 211 | + | |
| 212 | + # ComboBox for spatial tracker device selection | |
| 213 | + tooltip = wx.ToolTip(_("Choose the tracking device")) | |
| 214 | + choice_trck = wx.ComboBox(self, -1, "", | |
| 215 | + choices=const.TRACKER, style=wx.CB_DROPDOWN|wx.CB_READONLY) | |
| 216 | + choice_trck.SetToolTip(tooltip) | |
| 217 | + choice_trck.SetSelection(const.DEFAULT_TRACKER) | |
| 218 | + choice_trck.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceTracker, ctrl=choice_trck)) | |
| 219 | + | |
| 220 | + # ComboBox for tracker reference mode | |
| 221 | + tooltip = wx.ToolTip(_("Choose the navigation reference mode")) | |
| 222 | + choice_ref = wx.ComboBox(self, -1, "", | |
| 223 | + choices=const.REF_MODE, style=wx.CB_DROPDOWN|wx.CB_READONLY) | |
| 224 | + choice_ref.SetSelection(const.DEFAULT_REF_MODE) | |
| 225 | + choice_ref.SetToolTip(tooltip) | |
| 226 | + choice_ref.Bind(wx.EVT_COMBOBOX, partial(self.OnChoiceRefMode, ctrl=choice_trck)) | |
| 227 | + | |
| 228 | + # Toggle buttons for image fiducials | |
| 229 | + btns_img = const.BTNS_IMG | |
| 230 | + tips_img = const.TIPS_IMG | |
| 231 | + | |
| 232 | + for k in btns_img: | |
| 233 | + n = btns_img[k].keys()[0] | |
| 234 | + lab = btns_img[k].values()[0] | |
| 235 | + self.btns_coord[n] = wx.ToggleButton(self, k, label=lab, size=wx.Size(30, 23)) | |
| 236 | + self.btns_coord[n].SetToolTip(tips_img[n]) | |
| 237 | + self.btns_coord[n].Bind(wx.EVT_TOGGLEBUTTON, self.OnImageFiducials) | |
| 238 | + | |
| 239 | + # Push buttons for tracker fiducials | |
| 240 | + btns_trk = const.BTNS_TRK | |
| 241 | + tips_trk = const.TIPS_TRK | |
| 242 | + | |
| 243 | + for k in btns_trk: | |
| 244 | + n = btns_trk[k].keys()[0] | |
| 245 | + lab = btns_trk[k].values()[0] | |
| 246 | + self.btns_coord[n] = wx.Button(self, k, label=lab, size=wx.Size(30, 23)) | |
| 247 | + self.btns_coord[n].SetToolTip(tips_trk[n-3]) | |
| 248 | + # Excepetion for event of button that set image coordinates | |
| 249 | + if n == 6: | |
| 250 | + self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnSetImageCoordinates) | |
| 251 | + else: | |
| 252 | + self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnTrackerFiducials) | |
| 253 | + | |
| 254 | + # TODO: Find a better allignment between FRE, text and navigate button | |
| 255 | + txt_fre = wx.StaticText(self, -1, _('FRE:')) | |
| 256 | + | |
| 257 | + # Fiducial registration error text box | |
| 258 | + tooltip = wx.ToolTip(_("Fiducial registration error")) | |
| 259 | + txtctrl_fre = wx.TextCtrl(self, value="", size=wx.Size(60, -1), style=wx.TE_CENTRE) | |
| 260 | + txtctrl_fre.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) | |
| 261 | + txtctrl_fre.SetBackgroundColour('WHITE') | |
| 262 | + txtctrl_fre.SetEditable(0) | |
| 263 | + txtctrl_fre.SetToolTip(tooltip) | |
| 264 | + | |
| 265 | + # Toggle button for neuronavigation | |
| 266 | + tooltip = wx.ToolTip(_("Start navigation")) | |
| 267 | + btn_nav = wx.ToggleButton(self, -1, _("Navigate"), size=wx.Size(80, -1)) | |
| 268 | + btn_nav.SetToolTip(tooltip) | |
| 269 | + btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn=(btn_nav, choice_trck, choice_ref, txtctrl_fre))) | |
| 270 | + | |
| 271 | + # Image and tracker coordinates number controls | |
| 272 | + for m in range(0, 7): | |
| 273 | + for n in range(0, 3): | |
| 274 | + self.numctrls_coord[m].append( | |
| 275 | + wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1)) | |
| 276 | + | |
| 277 | + # Sizers to group all GUI objects | |
| 278 | + choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) | |
| 279 | + choice_sizer.AddMany([(choice_trck, wx.LEFT), | |
| 280 | + (choice_ref, wx.RIGHT)]) | |
| 281 | + | |
| 282 | + coord_sizer = wx.GridBagSizer(hgap=5, vgap=5) | |
| 283 | + | |
| 284 | + for m in range(0, 7): | |
| 285 | + coord_sizer.Add(self.btns_coord[m], pos=wx.GBPosition(m, 0)) | |
| 286 | + for n in range(0, 3): | |
| 287 | + coord_sizer.Add(self.numctrls_coord[m][n], pos=wx.GBPosition(m, n+1)) | |
| 288 | + if m in range(1, 6): | |
| 289 | + self.numctrls_coord[m][n].SetEditable(False) | |
| 290 | + | |
| 291 | + nav_sizer = wx.FlexGridSizer(rows=1, cols=3, hgap=5, vgap=5) | |
| 292 | + nav_sizer.AddMany([(txt_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), | |
| 293 | + (txtctrl_fre, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), | |
| 294 | + (btn_nav, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)]) | |
| 295 | + | |
| 296 | + group_sizer = wx.FlexGridSizer(rows=9, cols=1, hgap=5, vgap=5) | |
| 297 | + group_sizer.AddGrowableCol(0, 1) | |
| 298 | + group_sizer.AddGrowableRow(0, 1) | |
| 299 | + group_sizer.AddGrowableRow(1, 1) | |
| 300 | + group_sizer.AddGrowableRow(2, 1) | |
| 301 | + group_sizer.SetFlexibleDirection(wx.BOTH) | |
| 302 | + group_sizer.AddMany([(choice_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 303 | + (coord_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), | |
| 304 | + (nav_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)]) | |
| 305 | + | |
| 220 | 306 | main_sizer = wx.BoxSizer(wx.HORIZONTAL) |
| 221 | - main_sizer.Add(Ref_sizer, 1, wx.ALIGN_CENTER_HORIZONTAL, 10) | |
| 307 | + main_sizer.Add(group_sizer, 1, wx.ALIGN_CENTER_HORIZONTAL, 10) | |
| 222 | 308 | self.sizer = main_sizer |
| 223 | 309 | self.SetSizer(main_sizer) |
| 224 | 310 | self.Fit() |
| 225 | 311 | |
| 226 | - tooltip = wx.ToolTip("Pick the coordinates x, y, z in the image") | |
| 227 | - self.button_img_ref1.SetToolTip(tooltip) | |
| 228 | - tooltip = wx.ToolTip("Pick the coordinates x, y, z in the image") | |
| 229 | - self.button_img_ref2.SetToolTip(tooltip) | |
| 230 | - tooltip = wx.ToolTip("Pick the coordinates x, y, z in the image") | |
| 231 | - self.button_img_ref3.SetToolTip(tooltip) | |
| 232 | - tooltip = wx.ToolTip("Pick the coordinates x, y, z in the space") | |
| 233 | - self.button_plh_ref1.SetToolTip(tooltip) | |
| 234 | - tooltip = wx.ToolTip("Pick the coordinates x, y, z in the space") | |
| 235 | - self.button_plh_ref2.SetToolTip(tooltip) | |
| 236 | - tooltip = wx.ToolTip("Pick the coordinates x, y, z in the space") | |
| 237 | - self.button_plh_ref3.SetToolTip(tooltip) | |
| 238 | - tooltip = wx.ToolTip("X Coordinate") | |
| 239 | - self.numCtrl1a.SetToolTip(tooltip) | |
| 240 | - tooltip = wx.ToolTip("X Coordinate") | |
| 241 | - self.numCtrl1b.SetToolTip(tooltip) | |
| 242 | - tooltip = wx.ToolTip("X Coordinate") | |
| 243 | - self.numCtrl1c.SetToolTip(tooltip) | |
| 244 | - tooltip = wx.ToolTip("X Coordinate") | |
| 245 | - self.numCtrl1d.SetToolTip(tooltip) | |
| 246 | - tooltip = wx.ToolTip("X Coordinate") | |
| 247 | - self.numCtrl1e.SetToolTip(tooltip) | |
| 248 | - tooltip = wx.ToolTip("X Coordinate") | |
| 249 | - self.numCtrl1f.SetToolTip(tooltip) | |
| 250 | - tooltip = wx.ToolTip("X Coordinate") | |
| 251 | - self.numCtrl1g.SetToolTip(tooltip) | |
| 252 | - tooltip = wx.ToolTip("Y Coordinate") | |
| 253 | - self.numCtrl2a.SetToolTip(tooltip) | |
| 254 | - tooltip = wx.ToolTip("Y Coordinate") | |
| 255 | - self.numCtrl2b.SetToolTip(tooltip) | |
| 256 | - tooltip = wx.ToolTip("Y Coordinate") | |
| 257 | - self.numCtrl2c.SetToolTip(tooltip) | |
| 258 | - tooltip = wx.ToolTip("Y Coordinate") | |
| 259 | - self.numCtrl2d.SetToolTip(tooltip) | |
| 260 | - tooltip = wx.ToolTip("Y Coordinate") | |
| 261 | - self.numCtrl2e.SetToolTip(tooltip) | |
| 262 | - tooltip = wx.ToolTip("Y Coordinate") | |
| 263 | - self.numCtrl2f.SetToolTip(tooltip) | |
| 264 | - tooltip = wx.ToolTip("Y Coordinate") | |
| 265 | - self.numCtrl2g.SetToolTip(tooltip) | |
| 266 | - tooltip = wx.ToolTip("Z Coordinate") | |
| 267 | - self.numCtrl3a.SetToolTip(tooltip) | |
| 268 | - tooltip = wx.ToolTip("Z Coordinate") | |
| 269 | - self.numCtrl3b.SetToolTip(tooltip) | |
| 270 | - tooltip = wx.ToolTip("Z Coordinate") | |
| 271 | - self.numCtrl3c.SetToolTip(tooltip) | |
| 272 | - tooltip = wx.ToolTip("Z Coordinate") | |
| 273 | - self.numCtrl3d.SetToolTip(tooltip) | |
| 274 | - tooltip = wx.ToolTip("Z Coordinate") | |
| 275 | - self.numCtrl3e.SetToolTip(tooltip) | |
| 276 | - tooltip = wx.ToolTip("Z Coordinate") | |
| 277 | - self.numCtrl3f.SetToolTip(tooltip) | |
| 278 | - tooltip = wx.ToolTip("Z Coordinate") | |
| 279 | - self.numCtrl3g.SetToolTip(tooltip) | |
| 280 | - tooltip = wx.ToolTip("Corregistration of the real position with the image position") | |
| 281 | - self.button_crg.SetToolTip(tooltip) | |
| 282 | - tooltip = wx.ToolTip("Neuronavigation") | |
| 283 | - self.button_neuronavigate.SetToolTip(tooltip) | |
| 284 | - tooltip = wx.ToolTip("Get Cross Center Coordinates") | |
| 285 | - self.button_getpoint.SetToolTip(tooltip) | |
| 286 | - | |
| 287 | 312 | def __bind_events(self): |
| 288 | - Publisher.subscribe(self.__update_points_img, 'Update cross position') | |
| 289 | - Publisher.subscribe(self.__update_points_plh, 'Update plh position') | |
| 290 | - | |
| 291 | - def __update_points_img(self, pubsub_evt): | |
| 292 | - x, y, z = pubsub_evt.data[1] | |
| 293 | - self.a = x, y, z | |
| 294 | - if self.aux_img_ref1 == 0: | |
| 295 | - self.numCtrl1a.SetValue(x) | |
| 296 | - self.numCtrl2a.SetValue(y) | |
| 297 | - self.numCtrl3a.SetValue(z) | |
| 298 | - if self.aux_img_ref2 == 0: | |
| 299 | - self.numCtrl1b.SetValue(x) | |
| 300 | - self.numCtrl2b.SetValue(y) | |
| 301 | - self.numCtrl3b.SetValue(z) | |
| 302 | - if self.aux_img_ref3 == 0: | |
| 303 | - self.numCtrl1c.SetValue(x) | |
| 304 | - self.numCtrl2c.SetValue(y) | |
| 305 | - self.numCtrl3c.SetValue(z) | |
| 313 | + Publisher.subscribe(self.LoadImageFiducials, 'Load image fiducials') | |
| 314 | + Publisher.subscribe(self.UpdateTriggerState, 'Update trigger state') | |
| 315 | + Publisher.subscribe(self.UpdateImageCoordinates, 'Set ball reference position') | |
| 316 | + | |
| 317 | + def LoadImageFiducials(self, pubsub_evt): | |
| 318 | + marker_id = pubsub_evt.data[0] | |
| 319 | + coord = pubsub_evt.data[1] | |
| 320 | + for n in const.BTNS_IMG: | |
| 321 | + btn_id = const.BTNS_IMG[n].keys()[0] | |
| 322 | + fid_id = const.BTNS_IMG[n].values()[0] | |
| 323 | + if marker_id == fid_id and not self.btns_coord[btn_id].GetValue(): | |
| 324 | + self.btns_coord[btn_id].SetValue(True) | |
| 325 | + self.fiducials[btn_id, :] = coord[0:3] | |
| 326 | + for m in [0, 1, 2]: | |
| 327 | + self.numctrls_coord[btn_id][m].SetValue(coord[m]) | |
| 328 | + | |
| 329 | + def UpdateImageCoordinates(self, pubsub_evt): | |
| 330 | + # TODO: Change from world coordinates to matrix coordinates. They are better for multi software communication. | |
| 331 | + self.current_coord = pubsub_evt.data | |
| 332 | + for m in [0, 1, 2, 6]: | |
| 333 | + if m == 6 and self.btns_coord[m].IsEnabled(): | |
| 334 | + for n in [0, 1, 2]: | |
| 335 | + self.numctrls_coord[m][n].SetValue(self.current_coord[n]) | |
| 336 | + elif m != 6 and not self.btns_coord[m].GetValue(): | |
| 337 | + # btn_state = self.btns_coord[m].GetValue() | |
| 338 | + # if not btn_state: | |
| 339 | + for n in [0, 1, 2]: | |
| 340 | + self.numctrls_coord[m][n].SetValue(self.current_coord[n]) | |
| 341 | + | |
| 342 | + def UpdateTriggerState (self, pubsub_evt): | |
| 343 | + self.trigger_state = pubsub_evt.data | |
| 344 | + | |
| 345 | + def OnChoiceTracker(self, evt, ctrl): | |
| 346 | + if evt: | |
| 347 | + choice = evt.GetSelection() | |
| 348 | + else: | |
| 349 | + choice = self.tracker_id | |
| 350 | + | |
| 351 | + if self.trk_init: | |
| 352 | + trck = self.trk_init[0] | |
| 353 | + else: | |
| 354 | + trck = None | |
| 355 | + | |
| 356 | + # Conditions check if click was on current selection and if any other tracker | |
| 357 | + # has been initialized before | |
| 358 | + if trck and choice != 6: | |
| 359 | + self.ResetTrackerFiducials() | |
| 360 | + self.trk_init = dt.TrackerConnection(self.tracker_id, 'disconnect') | |
| 361 | + self.tracker_id = choice | |
| 362 | + if not self.trk_init[0]: | |
| 363 | + self.trk_init = dt.TrackerConnection(self.tracker_id, 'connect') | |
| 364 | + if not self.trk_init[0]: | |
| 365 | + dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) | |
| 366 | + ctrl.SetSelection(0) | |
| 367 | + print "Tracker not connected!" | |
| 368 | + else: | |
| 369 | + ctrl.SetSelection(self.tracker_id) | |
| 370 | + print "Tracker connected!" | |
| 371 | + elif choice == 6: | |
| 372 | + if trck: | |
| 373 | + self.trk_init = dt.TrackerConnection(self.tracker_id, 'disconnect') | |
| 374 | + if not self.trk_init[0]: | |
| 375 | + dlg.NavigationTrackerWarning(self.tracker_id, 'disconnect') | |
| 376 | + self.tracker_id = 0 | |
| 377 | + ctrl.SetSelection(self.tracker_id) | |
| 378 | + print "Tracker disconnected!" | |
| 379 | + else: | |
| 380 | + print "Tracker still connected!" | |
| 381 | + else: | |
| 382 | + ctrl.SetSelection(self.tracker_id) | |
| 383 | + | |
| 384 | + else: | |
| 385 | + # If trk_init is None try to connect. If doesn't succeed show dialog. | |
| 386 | + if choice: | |
| 387 | + self.tracker_id = choice | |
| 388 | + self.trk_init = dt.TrackerConnection(self.tracker_id, 'connect') | |
| 389 | + if not self.trk_init[0]: | |
| 390 | + dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) | |
| 391 | + self.tracker_id = 0 | |
| 392 | + ctrl.SetSelection(self.tracker_id) | |
| 393 | + | |
| 394 | + def OnChoiceRefMode(self, evt, ctrl): | |
| 395 | + # When ref mode is changed the tracker coords are set to zero | |
| 396 | + self.ref_mode_id = evt.GetSelection() | |
| 397 | + self.ResetTrackerFiducials() | |
| 398 | + # Some trackers do not accept restarting within this time window | |
| 399 | + # TODO: Improve the restarting of trackers after changing reference mode | |
| 400 | + # self.OnChoiceTracker(None, ctrl) | |
| 401 | + print "Reference mode changed!" | |
| 402 | + | |
| 403 | + def OnSetImageCoordinates(self, evt): | |
| 404 | + # FIXME: Cross does not update in last clicked slice, only on the other two | |
| 405 | + btn_id = const.BTNS_TRK[evt.GetId()].keys()[0] | |
| 406 | + | |
| 407 | + wx, wy, wz = self.numctrls_coord[btn_id][0].GetValue(), \ | |
| 408 | + self.numctrls_coord[btn_id][1].GetValue(), \ | |
| 409 | + self.numctrls_coord[btn_id][2].GetValue() | |
| 410 | + | |
| 411 | + Publisher.sendMessage('Set ball reference position', (wx, wy, wz)) | |
| 412 | + Publisher.sendMessage('Set camera in volume', (wx, wy, wz)) | |
| 413 | + Publisher.sendMessage('Co-registered points', (wx, wy, wz)) | |
| 414 | + Publisher.sendMessage('Update cross position', (wx, wy, wz)) | |
| 415 | + | |
| 416 | + def OnImageFiducials(self, evt): | |
| 417 | + btn_id = const.BTNS_IMG[evt.GetId()].keys()[0] | |
| 418 | + marker_id = const.BTNS_IMG[evt.GetId()].values()[0] | |
| 419 | + | |
| 420 | + if self.btns_coord[btn_id].GetValue(): | |
| 421 | + coord = self.numctrls_coord[btn_id][0].GetValue(),\ | |
| 422 | + self.numctrls_coord[btn_id][1].GetValue(),\ | |
| 423 | + self.numctrls_coord[btn_id][2].GetValue() | |
| 424 | + | |
| 425 | + self.fiducials[btn_id, :] = coord[0:3] | |
| 426 | + Publisher.sendMessage('Create marker', (coord, marker_id)) | |
| 427 | + else: | |
| 428 | + for n in [0, 1, 2]: | |
| 429 | + self.numctrls_coord[btn_id][n].SetValue(self.current_coord[n]) | |
| 430 | + | |
| 431 | + self.fiducials[btn_id, :] = np.nan | |
| 432 | + Publisher.sendMessage('Delete fiducial marker', marker_id) | |
| 433 | + | |
| 434 | + def OnTrackerFiducials(self, evt): | |
| 435 | + btn_id = const.BTNS_TRK[evt.GetId()].keys()[0] | |
| 436 | + coord = None | |
| 437 | + | |
| 438 | + if self.trk_init and self.tracker_id: | |
| 439 | + coord = dco.GetCoordinates(self.trk_init, self.tracker_id, self.ref_mode_id) | |
| 440 | + else: | |
| 441 | + dlg.NavigationTrackerWarning(0, 'choose') | |
| 442 | + | |
| 443 | + # Update number controls with tracker coordinates | |
| 444 | + if coord is not None: | |
| 445 | + self.fiducials[btn_id, :] = coord[0:3] | |
| 446 | + for n in [0, 1, 2]: | |
| 447 | + self.numctrls_coord[btn_id][n].SetValue(float(coord[n])) | |
| 448 | + | |
| 449 | + def OnNavigate(self, evt, btn): | |
| 450 | + btn_nav = btn[0] | |
| 451 | + choice_trck = btn[1] | |
| 452 | + choice_ref = btn[2] | |
| 453 | + txtctrl_fre = btn[3] | |
| 454 | + | |
| 455 | + nav_id = btn_nav.GetValue() | |
| 456 | + if nav_id: | |
| 457 | + if np.isnan(self.fiducials).any(): | |
| 458 | + dlg.InvalidFiducials() | |
| 459 | + btn_nav.SetValue(False) | |
| 460 | + | |
| 461 | + else: | |
| 462 | + tooltip = wx.ToolTip(_("Stop neuronavigation")) | |
| 463 | + btn_nav.SetToolTip(tooltip) | |
| 464 | + | |
| 465 | + # Disable all navigation buttons | |
| 466 | + choice_ref.Enable(False) | |
| 467 | + choice_trck.Enable(False) | |
| 468 | + for btn_c in self.btns_coord: | |
| 469 | + btn_c.Enable(False) | |
| 470 | + | |
| 471 | + m, q1, minv = db.base_creation(self.fiducials[0:3, :]) | |
| 472 | + n, q2, ninv = db.base_creation(self.fiducials[3::, :]) | |
| 473 | + | |
| 474 | + tracker_mode = self.trk_init, self.tracker_id, self.ref_mode_id | |
| 475 | + # FIXME: FRE is taking long to calculate so it updates on GUI delayed to navigation - I think its fixed | |
| 476 | + # TODO: Exhibit FRE in a warning dialog and only starts navigation after user clicks ok | |
| 477 | + fre = db.calculate_fre(self.fiducials, minv, n, q1, q2) | |
| 478 | + | |
| 479 | + txtctrl_fre.SetValue(str(round(fre, 2))) | |
| 480 | + if fre <= 3: | |
| 481 | + txtctrl_fre.SetBackgroundColour('GREEN') | |
| 482 | + else: | |
| 483 | + txtctrl_fre.SetBackgroundColour('RED') | |
| 484 | + | |
| 485 | + if self.trigger_state: | |
| 486 | + self.trigger = trig.Trigger(nav_id) | |
| 487 | + | |
| 488 | + self.correg = dcr.Coregistration((minv, n, q1, q2), nav_id, tracker_mode) | |
| 489 | + | |
| 490 | + else: | |
| 491 | + tooltip = wx.ToolTip(_("Start neuronavigation")) | |
| 492 | + btn_nav.SetToolTip(tooltip) | |
| 493 | + | |
| 494 | + # Enable all navigation buttons | |
| 495 | + choice_ref.Enable(True) | |
| 496 | + choice_trck.Enable(True) | |
| 497 | + for btn_c in self.btns_coord: | |
| 498 | + btn_c.Enable(True) | |
| 499 | + | |
| 500 | + if self.trigger_state: | |
| 501 | + self.trigger.stop() | |
| 306 | 502 | |
| 307 | - | |
| 308 | - def __update_points_plh(self, pubsub_evt): | |
| 309 | - coord = pubsub_evt.data | |
| 310 | - if self.aux_plh_ref1 == 0: | |
| 311 | - self.numCtrl1d.SetValue(coord[0]) | |
| 312 | - self.numCtrl2d.SetValue(coord[1]) | |
| 313 | - self.numCtrl3d.SetValue(coord[2]) | |
| 314 | - self.aux_plh_ref1 = 1 | |
| 315 | - if self.aux_plh_ref2 == 0: | |
| 316 | - self.numCtrl1e.SetValue(coord[0]) | |
| 317 | - self.numCtrl2e.SetValue(coord[1]) | |
| 318 | - self.numCtrl3e.SetValue(coord[2]) | |
| 319 | - self.aux_plh_ref2 = 1 | |
| 320 | - if self.aux_plh_ref3 == 0: | |
| 321 | - self.numCtrl1f.SetValue(coord[0]) | |
| 322 | - self.numCtrl2f.SetValue(coord[1]) | |
| 323 | - self.numCtrl3f.SetValue(coord[2]) | |
| 324 | - self.aux_plh_ref3 = 1 | |
| 325 | - | |
| 326 | - def Buttons(self, evt): | |
| 327 | - id = evt.GetId() | |
| 328 | - x, y, z = self.a | |
| 329 | - if id == PR1: | |
| 330 | - self.aux_plh_ref1 = 0 | |
| 331 | - self.coord1b = self.Coordinates() | |
| 332 | - coord = self.coord1b | |
| 333 | - elif id == PR2: | |
| 334 | - self.aux_plh_ref2 = 0 | |
| 335 | - self.coord2b = self.Coordinates() | |
| 336 | - coord = self.coord2b | |
| 337 | - elif id == PR3: | |
| 338 | - self.aux_plh_ref3 = 0 | |
| 339 | - self.coord3b = self.Coordinates() | |
| 340 | - coord = self.coord3b | |
| 341 | - elif id == GetPoint: | |
| 342 | - x, y, z = self.a | |
| 343 | - self.numCtrl1g.SetValue(x) | |
| 344 | - self.numCtrl2g.SetValue(y) | |
| 345 | - self.numCtrl3g.SetValue(z) | |
| 346 | - info = self.a, self.flagpoint | |
| 347 | - self.SaveCoordinates(info) | |
| 348 | - self.flagpoint = 1 | |
| 349 | - elif id == Corregistration and self.aux_img_ref1 == 1 and self.aux_img_ref2 == 1 and self.aux_img_ref3 == 1: | |
| 350 | - print "Coordenadas Imagem: ", self.coord1a, self.coord2a, self.coord3a | |
| 351 | - print "Coordenadas Polhemus: ", self.coord1b, self.coord2b, self.coord3b | |
| 352 | - | |
| 353 | - self.M, self.q1, self.Minv = db.Bases(self.coord1a, self.coord2a, self.coord3a).Basecreation() | |
| 354 | - self.N, self.q2, self.Ninv = db.Bases(self.coord1b, self.coord2b, self.coord3b).Basecreation() | |
| 355 | - | |
| 356 | - if self.aux_plh_ref1 == 0 or self.aux_plh_ref2 == 0 or self.aux_plh_ref3 == 0: | |
| 357 | - Publisher.sendMessage('Update plh position', coord) | |
| 358 | - | |
| 359 | - def Coordinates(self): | |
| 360 | - #Get Polhemus points for base creation | |
| 361 | - ser = serial.Serial(0) | |
| 362 | - ser.write("Y") | |
| 363 | - ser.write("P") | |
| 364 | - str = ser.readline() | |
| 365 | - ser.write("Y") | |
| 366 | - str = str.replace("\r\n","") | |
| 367 | - str = str.replace("-"," -") | |
| 368 | - aostr = [s for s in str.split()] | |
| 369 | - #aoflt -> 0:letter 1:x 2:y 3:z | |
| 370 | - aoflt = [float(aostr[1]), float(aostr[2]), float(aostr[3]), | |
| 371 | - float(aostr[4]), float(aostr[5]), float(aostr[6])] | |
| 372 | - ser.close() | |
| 373 | - #Unit change: inches to millimeters | |
| 374 | - x = 25.4 | |
| 375 | - y = 25.4 | |
| 376 | - z = -25.4 | |
| 377 | - | |
| 378 | - coord = (aoflt[0]*x, aoflt[1]*y, aoflt[2]*z) | |
| 379 | - return coord | |
| 380 | - | |
| 381 | - def Img_Ref_ToggleButton1(self, evt): | |
| 382 | - id = evt.GetId() | |
| 383 | - flag1 = self.button_img_ref1.GetValue() | |
| 384 | - x, y, z = self.a | |
| 385 | - if flag1 == True: | |
| 386 | - self.coord1a = x, y, z | |
| 387 | - self.aux_img_ref1 = 1 | |
| 388 | - elif flag1 == False: | |
| 389 | - self.aux_img_ref1 = 0 | |
| 390 | - self.coord1a = (0, 0, 0) | |
| 391 | - self.numCtrl1a.SetValue(x) | |
| 392 | - self.numCtrl2a.SetValue(y) | |
| 393 | - self.numCtrl3a.SetValue(z) | |
| 394 | - | |
| 395 | - def Img_Ref_ToggleButton2(self, evt): | |
| 396 | - id = evt.GetId() | |
| 397 | - flag2 = self.button_img_ref2.GetValue() | |
| 398 | - x, y, z = self.a | |
| 399 | - if flag2 == True: | |
| 400 | - self.coord2a = x, y, z | |
| 401 | - self.aux_img_ref2 = 1 | |
| 402 | - elif flag2 == False: | |
| 403 | - self.aux_img_ref2 = 0 | |
| 404 | - self.coord2a = (0, 0, 0) | |
| 405 | - self.numCtrl1b.SetValue(x) | |
| 406 | - self.numCtrl2b.SetValue(y) | |
| 407 | - self.numCtrl3b.SetValue(z) | |
| 408 | - | |
| 409 | - def Img_Ref_ToggleButton3(self, evt): | |
| 410 | - id = evt.GetId() | |
| 411 | - flag3 = self.button_img_ref3.GetValue() | |
| 412 | - x, y, z = self.a | |
| 413 | - if flag3 == True: | |
| 414 | - self.coord3a = x, y, z | |
| 415 | - self.aux_img_ref3 = 1 | |
| 416 | - elif flag3 == False: | |
| 417 | - self.aux_img_ref3 = 0 | |
| 418 | - self.coord3a = (0, 0, 0) | |
| 419 | - self.numCtrl1c.SetValue(x) | |
| 420 | - self.numCtrl2c.SetValue(y) | |
| 421 | - self.numCtrl3c.SetValue(z) | |
| 422 | - | |
| 423 | - def Neuronavigate_ToggleButton(self, evt): | |
| 424 | - id = evt.GetId() | |
| 425 | - flag4 = self.button_neuronavigate.GetValue() | |
| 426 | - bases = self.Minv, self.N, self.q1, self.q2 | |
| 427 | - if flag4 == True: | |
| 428 | - self.correg = dcr.Corregister(bases, flag4) | |
| 429 | - elif flag4 == False: | |
| 430 | 503 | self.correg.stop() |
| 504 | + | |
| 505 | + def ResetTrackerFiducials(self): | |
| 506 | + for m in range(3, 6): | |
| 507 | + for n in range(0, 3): | |
| 508 | + self.numctrls_coord[m][n].SetValue(0.0) | |
| 509 | + | |
| 510 | + | |
| 511 | +class MarkersPanel(wx.Panel): | |
| 512 | + def __init__(self, parent): | |
| 513 | + wx.Panel.__init__(self, parent) | |
| 514 | + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) | |
| 515 | + self.SetBackgroundColour(default_colour) | |
| 516 | + | |
| 517 | + self.SetAutoLayout(1) | |
| 518 | + | |
| 519 | + self.__bind_events() | |
| 520 | + | |
| 521 | + self.current_coord = 0, 0, 0 | |
| 522 | + self.list_coord = [] | |
| 523 | + self.marker_ind = 0 | |
| 524 | + | |
| 525 | + self.marker_colour = (0.0, 0.0, 1.) | |
| 526 | + self.marker_size = 4 | |
| 527 | + | |
| 528 | + # Change marker size | |
| 529 | + spin_size = wx.SpinCtrl(self, -1, "", size=wx.Size(40, 23)) | |
| 530 | + spin_size.SetRange(1, 99) | |
| 531 | + spin_size.SetValue(self.marker_size) | |
| 532 | + spin_size.Bind(wx.EVT_TEXT, partial(self.OnSelectSize, ctrl=spin_size)) | |
| 533 | + spin_size.Bind(wx.EVT_SPINCTRL, partial(self.OnSelectSize, ctrl=spin_size)) | |
| 534 | + | |
| 535 | + # Marker colour select | |
| 536 | + select_colour = csel.ColourSelect(self, -1, colour=[255*s for s in self.marker_colour], size=wx.Size(20, 23)) | |
| 537 | + select_colour.Bind(csel.EVT_COLOURSELECT, partial(self.OnSelectColour, ctrl=select_colour)) | |
| 538 | + | |
| 539 | + btn_create = wx.Button(self, -1, label=_('Create marker'), size=wx.Size(135, 23)) | |
| 540 | + btn_create.Bind(wx.EVT_BUTTON, self.OnCreateMarker) | |
| 541 | + | |
| 542 | + sizer_create = wx.FlexGridSizer(rows=1, cols=3, hgap=5, vgap=5) | |
| 543 | + sizer_create.AddMany([(spin_size, 1), | |
| 544 | + (select_colour, 0), | |
| 545 | + (btn_create, 0)]) | |
| 546 | + | |
| 547 | + # Buttons to save and load markers and to change its visibility as well | |
| 548 | + btn_save = wx.Button(self, -1, label=_('Save'), size=wx.Size(65, 23)) | |
| 549 | + btn_save.Bind(wx.EVT_BUTTON, self.OnSaveMarkers) | |
| 550 | + | |
| 551 | + btn_load = wx.Button(self, -1, label=_('Load'), size=wx.Size(65, 23)) | |
| 552 | + btn_load.Bind(wx.EVT_BUTTON, self.OnLoadMarkers) | |
| 553 | + | |
| 554 | + btn_visibility = wx.ToggleButton(self, -1, _("Hide"), size=wx.Size(65, 23)) | |
| 555 | + btn_visibility.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnMarkersVisibility, ctrl=btn_visibility)) | |
| 556 | + | |
| 557 | + sizer_btns = wx.FlexGridSizer(rows=1, cols=3, hgap=5, vgap=5) | |
| 558 | + sizer_btns.AddMany([(btn_save, 1, wx.RIGHT), | |
| 559 | + (btn_load, 0, wx.LEFT | wx.RIGHT), | |
| 560 | + (btn_visibility, 0, wx.LEFT)]) | |
| 561 | + | |
| 562 | + # Buttons to delete or remove markers | |
| 563 | + btn_delete_single = wx.Button(self, -1, label=_('Remove'), size=wx.Size(65, 23)) | |
| 564 | + btn_delete_single.Bind(wx.EVT_BUTTON, self.OnDeleteSingleMarker) | |
| 565 | + | |
| 566 | + btn_delete_all = wx.Button(self, -1, label=_('Delete all markers'), size=wx.Size(135, 23)) | |
| 567 | + btn_delete_all.Bind(wx.EVT_BUTTON, self.OnDeleteAllMarkers) | |
| 568 | + | |
| 569 | + sizer_delete = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) | |
| 570 | + sizer_delete.AddMany([(btn_delete_single, 1, wx.RIGHT), | |
| 571 | + (btn_delete_all, 0, wx.LEFT)]) | |
| 572 | + | |
| 573 | + # List of markers | |
| 574 | + self.lc = wx.ListCtrl(self, -1, style=wx.LC_REPORT, size=wx.Size(0,120)) | |
| 575 | + self.lc.InsertColumn(0, '#') | |
| 576 | + self.lc.InsertColumn(1, 'X') | |
| 577 | + self.lc.InsertColumn(2, 'Y') | |
| 578 | + self.lc.InsertColumn(3, 'Z') | |
| 579 | + self.lc.InsertColumn(4, 'ID') | |
| 580 | + self.lc.SetColumnWidth(0, 28) | |
| 581 | + self.lc.SetColumnWidth(1, 50) | |
| 582 | + self.lc.SetColumnWidth(2, 50) | |
| 583 | + self.lc.SetColumnWidth(3, 50) | |
| 584 | + self.lc.SetColumnWidth(4, 50) | |
| 585 | + self.lc.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListEditMarkerId) | |
| 586 | + | |
| 587 | + # Add all lines into main sizer | |
| 588 | + group_sizer = wx.BoxSizer(wx.VERTICAL) | |
| 589 | + group_sizer.Add(sizer_create, 0, wx.TOP | wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 5) | |
| 590 | + group_sizer.Add(sizer_btns, 0, wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 5) | |
| 591 | + group_sizer.Add(sizer_delete, 0, wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 5) | |
| 592 | + group_sizer.Add(self.lc, 0, wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 5) | |
| 593 | + group_sizer.Fit(self) | |
| 594 | + | |
| 595 | + self.SetSizer(group_sizer) | |
| 596 | + self.Update() | |
| 597 | + | |
| 598 | + def __bind_events(self): | |
| 599 | + Publisher.subscribe(self.UpdateCurrentCoord, 'Set ball reference position') | |
| 600 | + Publisher.subscribe(self.OnDeleteSingleMarker, 'Delete fiducial marker') | |
| 601 | + Publisher.subscribe(self.OnCreateMarker, 'Create marker') | |
| 602 | + | |
| 603 | + def UpdateCurrentCoord(self, pubsub_evt): | |
| 604 | + self.current_coord = pubsub_evt.data | |
| 605 | + | |
| 606 | + def OnListEditMarkerId(self, evt): | |
| 607 | + menu_id = wx.Menu() | |
| 608 | + menu_id.Append(-1, _('Edit ID')) | |
| 609 | + menu_id.Bind(wx.EVT_MENU, self.OnMenuEditMarkerId) | |
| 610 | + self.PopupMenu(menu_id) | |
| 611 | + menu_id.Destroy() | |
| 612 | + | |
| 613 | + def OnMenuEditMarkerId(self, evt): | |
| 614 | + id_label = dlg.EnterMarkerID(self.lc.GetItemText(self.lc.GetFocusedItem(), 4)) | |
| 615 | + list_index = self.lc.GetFocusedItem() | |
| 616 | + self.lc.SetStringItem(list_index, 4, id_label) | |
| 617 | + # Add the new ID to exported list | |
| 618 | + self.list_coord[list_index][7] = str(id_label) | |
| 619 | + | |
| 620 | + def OnDeleteAllMarkers(self, pubsub_evt): | |
| 621 | + self.list_coord = [] | |
| 622 | + self.marker_ind = 0 | |
| 623 | + Publisher.sendMessage('Remove all markers', self.lc.GetItemCount()) | |
| 624 | + self.lc.DeleteAllItems() | |
| 625 | + | |
| 626 | + def OnDeleteSingleMarker(self, evt): | |
| 627 | + # OnDeleteSingleMarker is used for both pubsub and button click events | |
| 628 | + # Pubsub is used for fiducial handle and button click for all others | |
| 629 | + | |
| 630 | + if hasattr(evt, 'data'): | |
| 631 | + marker_id = evt.data | |
| 632 | + if self.lc.GetItemCount(): | |
| 633 | + for id_n in range(self.lc.GetItemCount()): | |
| 634 | + item = self.lc.GetItem(id_n, 4) | |
| 635 | + if item.GetText() == marker_id: | |
| 636 | + if marker_id == "LEI" or marker_id == "REI" or marker_id == "NAI": | |
| 637 | + self.lc.Focus(item.GetId()) | |
| 638 | + break | |
| 639 | + else: | |
| 640 | + if self.lc.GetFocusedItem() is not -1 and self.lc.GetItemCount(): | |
| 641 | + index = self.lc.GetFocusedItem() | |
| 642 | + del self.list_coord[index] | |
| 643 | + self.lc.DeleteItem(index) | |
| 644 | + for n in range(0, self.lc.GetItemCount()): | |
| 645 | + self.lc.SetStringItem(n, 0, str(n+1)) | |
| 646 | + self.marker_ind -= 1 | |
| 647 | + Publisher.sendMessage('Remove marker', index) | |
| 648 | + elif not self.lc.GetItemCount(): | |
| 649 | + pass | |
| 650 | + else: | |
| 651 | + dlg.NoMarkerSelected() | |
| 652 | + | |
| 653 | + def OnCreateMarker(self, evt): | |
| 654 | + # OnCreateMarker is used for both pubsub and button click events | |
| 655 | + # Pubsub is used for markers created with fiducial buttons, trigger and create marker button | |
| 656 | + if hasattr(evt, 'data'): | |
| 657 | + if evt.data: | |
| 658 | + self.CreateMarker(evt.data[0], (0.0, 1.0, 0.0), self.marker_size, evt.data[1]) | |
| 659 | + else: | |
| 660 | + self.CreateMarker(self.current_coord, self.marker_colour, self.marker_size) | |
| 661 | + else: | |
| 662 | + self.CreateMarker(self.current_coord, self.marker_colour, self.marker_size) | |
| 663 | + | |
| 664 | + def OnLoadMarkers(self, evt): | |
| 665 | + filepath = dlg.ShowLoadMarkersDialog() | |
| 666 | + | |
| 667 | + if filepath: | |
| 668 | + try: | |
| 669 | + content = [s.rstrip() for s in open(filepath)] | |
| 670 | + for data in content: | |
| 671 | + line = [s for s in data.split()] | |
| 672 | + coord = float(line[0]), float(line[1]), float(line[2]) | |
| 673 | + colour = float(line[3]), float(line[4]), float(line[5]) | |
| 674 | + size = float(line[6]) | |
| 675 | + | |
| 676 | + if len(line) == 8: | |
| 677 | + if line[7] == "LEI" or line[7] == "REI" or line[7] == "NAI": | |
| 678 | + Publisher.sendMessage('Load image fiducials', (line[7], coord)) | |
| 679 | + else: | |
| 680 | + line.append("") | |
| 681 | + self.CreateMarker(coord, colour, size, line[7]) | |
| 682 | + except: | |
| 683 | + dlg.InvalidMarkersFile() | |
| 684 | + | |
| 685 | + def OnMarkersVisibility(self, evt, ctrl): | |
| 686 | + | |
| 687 | + if ctrl.GetValue(): | |
| 688 | + Publisher.sendMessage('Hide all markers', self.lc.GetItemCount()) | |
| 689 | + ctrl.SetLabel('Show') | |
| 690 | + else: | |
| 691 | + Publisher.sendMessage('Show all markers', self.lc.GetItemCount()) | |
| 692 | + ctrl.SetLabel('Hide') | |
| 693 | + | |
| 694 | + def OnSaveMarkers(self, evt): | |
| 695 | + filename = dlg.ShowSaveMarkersDialog("markers.txt") | |
| 696 | + if filename: | |
| 697 | + if self.list_coord: | |
| 698 | + text_file = open(filename, "w") | |
| 699 | + list_slice1 = self.list_coord[0] | |
| 700 | + coord = str('%.3f' %self.list_coord[0][0]) + "\t" + str('%.3f' %self.list_coord[0][1]) + "\t" + str('%.3f' %self.list_coord[0][2]) | |
| 701 | + properties = str('%.3f' %list_slice1[3]) + "\t" + str('%.3f' %list_slice1[4]) + "\t" + str('%.3f' %list_slice1[5]) + "\t" + str('%.1f' %list_slice1[6]) + "\t" + list_slice1[7] | |
| 702 | + line = coord + "\t" + properties + "\n" | |
| 703 | + list_slice = self.list_coord[1:] | |
| 704 | + | |
| 705 | + for value in list_slice: | |
| 706 | + coord = str('%.3f' %value[0]) + "\t" + str('%.3f' %value[1]) + "\t" + str('%.3f' %value[2]) | |
| 707 | + properties = str('%.3f' %value[3]) + "\t" + str('%.3f' %value[4]) + "\t" + str('%.3f' %value[5]) + "\t" + str('%.1f' %value[6]) + "\t" + value[7] | |
| 708 | + line = line + coord + "\t" + properties + "\n" | |
| 709 | + | |
| 710 | + text_file.writelines(line) | |
| 711 | + text_file.close() | |
| 431 | 712 | |
| 432 | - def SaveCoordinates(self, info): | |
| 433 | - #Create a file and write the points given by getpoint's button | |
| 434 | - x, y, z = info[0] | |
| 435 | - flag = info[1] | |
| 436 | - | |
| 437 | - if flag == 0: | |
| 438 | - text_file = open("points.txt", "w") | |
| 439 | - line = str('%.2f' %x) + "\t" + str('%.2f' %y) + "\t" + str('%.2f' %z) + "\n" | |
| 440 | - text_file.writelines(line) | |
| 441 | - text_file.close() | |
| 713 | + def OnSelectColour(self, evt, ctrl): | |
| 714 | + self.marker_colour = [colour/255.0 for colour in ctrl.GetValue()] | |
| 715 | + | |
| 716 | + def OnSelectSize(self, evt, ctrl): | |
| 717 | + self.marker_size = ctrl.GetValue() | |
| 718 | + | |
| 719 | + def CreateMarker(self, coord, colour, size, marker_id=""): | |
| 720 | + # TODO: Use matrix coordinates and not world coordinates as current method. | |
| 721 | + # This makes easier for inter-software comprehension. | |
| 722 | + | |
| 723 | + Publisher.sendMessage('Add marker', (self.marker_ind, size, colour, coord)) | |
| 724 | + | |
| 725 | + self.marker_ind += 1 | |
| 726 | + | |
| 727 | + # List of lists with coordinates and properties of a marker | |
| 728 | + line = [coord[0], coord[1], coord[2], colour[0], colour[1], colour[2], self.marker_size, marker_id] | |
| 729 | + | |
| 730 | + # Adding current line to a list of all markers already created | |
| 731 | + if not self.list_coord: | |
| 732 | + self.list_coord = [line] | |
| 442 | 733 | else: |
| 443 | - text_file = open("points.txt", "r") | |
| 444 | - filedata = text_file.read() | |
| 445 | - line = filedata + str('%.2f' %x) + "\t" + str('%.2f' %y) + "\t" + str('%.2f' %z) + "\n" | |
| 446 | - text_file = open("points.txt", "w") | |
| 447 | - text_file.write(line) | |
| 448 | - text_file.close() | |
| 449 | - | |
| 734 | + self.list_coord.append(line) | |
| 735 | + | |
| 736 | + # Add item to list control in panel | |
| 737 | + num_items = self.lc.GetItemCount() | |
| 738 | + self.lc.InsertStringItem(num_items, str(num_items + 1)) | |
| 739 | + self.lc.SetStringItem(num_items, 1, str(round(coord[0], 2))) | |
| 740 | + self.lc.SetStringItem(num_items, 2, str(round(coord[1], 2))) | |
| 741 | + self.lc.SetStringItem(num_items, 3, str(round(coord[2], 2))) | |
| 742 | + self.lc.SetStringItem(num_items, 4, str(marker_id)) | |
| 743 | + self.lc.EnsureVisible(num_items) | ... | ... |
invesalius/reader/analyze_reader.py
| ... | ... | @@ -1,43 +0,0 @@ |
| 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 | - | |
| 20 | -import os | |
| 21 | -import multiprocessing | |
| 22 | -import tempfile | |
| 23 | - | |
| 24 | -import vtk | |
| 25 | - | |
| 26 | -from nibabel import AnalyzeImage, squeeze_image | |
| 27 | - | |
| 28 | -def ReadAnalyze(filename): | |
| 29 | - anlz = squeeze_image(AnalyzeImage.from_filename(filename)) | |
| 30 | - return anlz | |
| 31 | - | |
| 32 | -def ReadDirectory(dir_): | |
| 33 | - """ | |
| 34 | - Looking for analyze files in the given directory | |
| 35 | - """ | |
| 36 | - imagedata = None | |
| 37 | - for root, sub_folders, files in os.walk(dir_): | |
| 38 | - for file in files: | |
| 39 | - if file.split(".")[-1] == "hdr": | |
| 40 | - filename = os.path.join(root,file) | |
| 41 | - imagedata = ReadAnalyze(filename) | |
| 42 | - return imagedata | |
| 43 | - return imagedata |
| ... | ... | @@ -0,0 +1,51 @@ |
| 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 | + | |
| 20 | +import os | |
| 21 | + | |
| 22 | +import vtk | |
| 23 | +import nibabel as nib | |
| 24 | + | |
| 25 | +import invesalius.constants as const | |
| 26 | + | |
| 27 | + | |
| 28 | +def ReadOthers(dir_): | |
| 29 | + """ | |
| 30 | + Read the given Analyze, NIfTI, Compressed NIfTI or PAR/REC file, | |
| 31 | + remove singleton image dimensions and convert image orientation to | |
| 32 | + RAS+ canonical coordinate system. Analyze header does not support | |
| 33 | + affine transformation matrix, though cannot be converted automatically | |
| 34 | + to canonical orientation. | |
| 35 | + | |
| 36 | + :param dir_: file path | |
| 37 | + :return: imagedata object | |
| 38 | + """ | |
| 39 | + | |
| 40 | + if not const.VTK_WARNING: | |
| 41 | + log_path = os.path.join(const.LOG_FOLDER, 'vtkoutput.txt') | |
| 42 | + fow = vtk.vtkFileOutputWindow() | |
| 43 | + fow.SetFileName(log_path) | |
| 44 | + ow = vtk.vtkOutputWindow() | |
| 45 | + ow.SetInstance(fow) | |
| 46 | + | |
| 47 | + imagedata = nib.squeeze_image(nib.load(dir_)) | |
| 48 | + imagedata = nib.as_closest_canonical(imagedata) | |
| 49 | + imagedata.update_header() | |
| 50 | + | |
| 51 | + return imagedata | |
| 0 | 52 | \ No newline at end of file | ... | ... |
No preview for this file type
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +SliderControlledXpointsCount=0 | |
| 5 | +Name=1Probe | |
| 6 | + | |
| 7 | +[Facet1] | |
| 8 | +VectorsCount=2 | |
| 9 | + | |
| 10 | +[Facet1V1] | |
| 11 | +EndPos(0,0)=-61.602408326043872 | |
| 12 | +EndPos(0,1)=4.2184025144074082e-016 | |
| 13 | +EndPos(0,2)=-4.556586682229099e-016 | |
| 14 | +EndPos(1,0)=61.602408326043872 | |
| 15 | +EndPos(1,1)=-9.6115500328270063e-017 | |
| 16 | +EndPos(1,2)=-1.1391466705572748e-016 | |
| 17 | + | |
| 18 | +[Facet1V2] | |
| 19 | +EndPos(0,0)=-61.59657019736504 | |
| 20 | +EndPos(0,1)=11.949604571287503 | |
| 21 | +EndPos(0,2)=1.1391466705572748e-016 | |
| 22 | +EndPos(1,0)=-61.602408326043872 | |
| 23 | +EndPos(1,1)=4.2184025144074082e-016 | |
| 24 | +EndPos(1,2)=-4.556586682229099e-016 | |
| 25 | + | |
| 26 | +[Tooltip2MarkerXf] | |
| 27 | +Scale=1. | |
| 28 | +S0=145.66478105979206 | |
| 29 | +R0,0=-8.4560706371216821e-002 | |
| 30 | +R1,0=-0.52109081782374 | |
| 31 | +R2,0=0.84930197604725266 | |
| 32 | +S1=-13.147426563284974 | |
| 33 | +R0,1=4.5008904130328986e-002 | |
| 34 | +R1,1=-0.85348236485506712 | |
| 35 | +R2,1=-0.5191743940434248 | |
| 36 | +S2=9.0630880541399712 | |
| 37 | +R0,2=0.99540126857813949 | |
| 38 | +R1,2=-5.6756022725552024e-003 | |
| 39 | +R2,2=9.5624798310161907e-002 | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +Name=1b | |
| 5 | + | |
| 6 | +[Facet1] | |
| 7 | +VectorsCount=2 | |
| 8 | + | |
| 9 | +[Facet1V1] | |
| 10 | +EndPos(0,0)=-13.977391161539257 | |
| 11 | +EndPos(0,1)=1.2440742880315269e-015 | |
| 12 | +EndPos(0,2)=-9.6300745155986073e-015 | |
| 13 | +EndPos(1,0)=13.977391161539257 | |
| 14 | +EndPos(1,1)=1.2515682934477468e-015 | |
| 15 | +EndPos(1,2)=-9.6789243286821155e-015 | |
| 16 | + | |
| 17 | +[Facet1V2] | |
| 18 | +EndPos(0,0)=13.977391161539257 | |
| 19 | +EndPos(0,1)=1.2515682934477468e-015 | |
| 20 | +EndPos(0,2)=-9.6789243286821155e-015 | |
| 21 | +EndPos(1,0)=14.019670530273789 | |
| 22 | +EndPos(1,1)=15.245637552880362 | |
| 23 | +EndPos(1,2)=-9.7055696812731187e-015 | |
| 24 | + | |
| 25 | +[Tooltip2MarkerXf] | |
| 26 | +Scale=1. | |
| 27 | +S0=0. | |
| 28 | +R0,0=1. | |
| 29 | +R1,0=0. | |
| 30 | +R2,0=0. | |
| 31 | +S1=0. | |
| 32 | +R0,1=0. | |
| 33 | +R1,1=1. | |
| 34 | +R2,1=0. | |
| 35 | +S2=0. | |
| 36 | +R0,2=0. | |
| 37 | +R1,2=0. | |
| 38 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +SliderControlledXpointsCount=0 | |
| 5 | +Name=2Coil | |
| 6 | + | |
| 7 | +[Facet1] | |
| 8 | +VectorsCount=2 | |
| 9 | + | |
| 10 | +[Facet1V1] | |
| 11 | +EndPos(0,0)=-8.9266116684264851 | |
| 12 | +EndPos(0,1)=-7.9898502660127542e-016 | |
| 13 | +EndPos(0,2)=1.0214990704373533e-015 | |
| 14 | +EndPos(1,0)=8.9266116684264833 | |
| 15 | +EndPos(1,1)=-6.360897244681129e-016 | |
| 16 | +EndPos(1,2)=2.6438799470143262e-015 | |
| 17 | + | |
| 18 | +[Facet1V2] | |
| 19 | +EndPos(0,0)=8.9266116684264833 | |
| 20 | +EndPos(0,1)=-6.360897244681129e-016 | |
| 21 | +EndPos(0,2)=2.6438799470143262e-015 | |
| 22 | +EndPos(1,0)=9.0134937152069554 | |
| 23 | +EndPos(1,1)=-12.059011297429047 | |
| 24 | +EndPos(1,2)=3.6653790174516793e-015 | |
| 25 | + | |
| 26 | +[Tooltip2MarkerXf] | |
| 27 | +Scale=1. | |
| 28 | +S0=0.20000000000000079 | |
| 29 | +R0,0=0.96592582628906842 | |
| 30 | +R1,0=-1.457167719820518e-016 | |
| 31 | +R2,0=-0.25881904510252007 | |
| 32 | +S1=-14.79999999999999 | |
| 33 | +R0,1=7.9979483404574392e-002 | |
| 34 | +R1,1=0.95105651629515409 | |
| 35 | +R2,1=0.29848749562898386 | |
| 36 | +S2=99.400000000000091 | |
| 37 | +R0,2=0.24615153938604106 | |
| 38 | +R1,2=-0.30901699437494573 | |
| 39 | +R2,2=0.91865005134999966 | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +Name=2b | |
| 5 | + | |
| 6 | +[Facet1] | |
| 7 | +VectorsCount=2 | |
| 8 | + | |
| 9 | +[Facet1V1] | |
| 10 | +EndPos(0,0)=-13.9690559296461 | |
| 11 | +EndPos(0,1)=-9.08090028293946e-017 | |
| 12 | +EndPos(0,2)=3.7518297647387081e-015 | |
| 13 | +EndPos(1,0)=13.969055929646105 | |
| 14 | +EndPos(1,1)=-1.1456717217429434e-016 | |
| 15 | +EndPos(1,2)=3.7783785761971358e-015 | |
| 16 | + | |
| 17 | +[Facet1V2] | |
| 18 | +EndPos(0,0)=-13.924452046559736 | |
| 19 | +EndPos(0,1)=-15.196209648340083 | |
| 20 | +EndPos(0,2)=3.7349350665378906e-015 | |
| 21 | +EndPos(1,0)=-13.9690559296461 | |
| 22 | +EndPos(1,1)=-9.08090028293946e-017 | |
| 23 | +EndPos(1,2)=3.7518297647387081e-015 | |
| 24 | + | |
| 25 | +[Tooltip2MarkerXf] | |
| 26 | +Scale=1. | |
| 27 | +S0=0. | |
| 28 | +R0,0=1. | |
| 29 | +R1,0=0. | |
| 30 | +R2,0=0. | |
| 31 | +S1=0. | |
| 32 | +R0,1=0. | |
| 33 | +R1,1=1. | |
| 34 | +R2,1=0. | |
| 35 | +S2=0. | |
| 36 | +R0,2=0. | |
| 37 | +R1,2=0. | |
| 38 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +SliderControlledXpointsCount=0 | |
| 5 | +Name=3Coil | |
| 6 | + | |
| 7 | +[Facet1] | |
| 8 | +VectorsCount=2 | |
| 9 | + | |
| 10 | +[Facet1V1] | |
| 11 | +EndPos(0,0)=-8.9434442564413246 | |
| 12 | +EndPos(0,1)=-6.1153268241647966e-016 | |
| 13 | +EndPos(0,2)=3.4944724709513123e-016 | |
| 14 | +EndPos(1,0)=8.9434442564413299 | |
| 15 | +EndPos(1,1)=-8.8089826871897666e-016 | |
| 16 | +EndPos(1,2)=-4.6592966279350834e-016 | |
| 17 | + | |
| 18 | +[Facet1V2] | |
| 19 | +EndPos(0,0)=-8.9439343674574907 | |
| 20 | +EndPos(0,1)=11.937904678416533 | |
| 21 | +EndPos(0,2)=-5.2417087064269685e-016 | |
| 22 | +EndPos(1,0)=-8.9434442564413246 | |
| 23 | +EndPos(1,1)=-6.1153268241647966e-016 | |
| 24 | +EndPos(1,2)=3.4944724709513123e-016 | |
| 25 | + | |
| 26 | +[Tooltip2MarkerXf] | |
| 27 | +Scale=1. | |
| 28 | +S0=0.80000000000000182 | |
| 29 | +R0,0=0.9563047559630351 | |
| 30 | +R1,0=1.8041124150158794e-016 | |
| 31 | +R2,0=-0.29237170472273794 | |
| 32 | +S1=47.299999999999962 | |
| 33 | +R0,1=9.0347825433700193e-002 | |
| 34 | +R1,1=0.95105651629515331 | |
| 35 | +R2,1=0.29551442139416562 | |
| 36 | +S2=42.399999999999991 | |
| 37 | +R0,2=0.27806201495688238 | |
| 38 | +R1,2=-0.30901699437494834 | |
| 39 | +R2,2=0.90949986972269081 | ... | ... |
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +SliderControlledXpointsCount=0 | |
| 5 | +Name=4Coil | |
| 6 | + | |
| 7 | +[Facet1] | |
| 8 | +VectorsCount=2 | |
| 9 | + | |
| 10 | +[Facet1V1] | |
| 11 | +EndPos(0,0)=-8.9585812958876883 | |
| 12 | +EndPos(0,1)=-4.8569430893988515e-016 | |
| 13 | +EndPos(0,2)=2.3154142102162125e-015 | |
| 14 | +EndPos(1,0)=8.95858129588769 | |
| 15 | +EndPos(1,1)=-1.2210192124187057e-016 | |
| 16 | +EndPos(1,2)=4.6308284204324244e-016 | |
| 17 | + | |
| 18 | +[Facet1V2] | |
| 19 | +EndPos(0,0)=8.95858129588769 | |
| 20 | +EndPos(0,1)=-1.2210192124187057e-016 | |
| 21 | +EndPos(0,2)=4.6308284204324244e-016 | |
| 22 | +EndPos(1,0)=8.8914854228233384 | |
| 23 | +EndPos(1,1)=11.981845264775341 | |
| 24 | +EndPos(1,2)=3.4731213153243185e-016 | |
| 25 | + | |
| 26 | +[Tooltip2MarkerXf] | |
| 27 | +Scale=1. | |
| 28 | +S0=-0.40000000000000141 | |
| 29 | +R0,0=1. | |
| 30 | +R1,0=0. | |
| 31 | +R2,0=0. | |
| 32 | +S1=10.700000000000001 | |
| 33 | +R0,1=0. | |
| 34 | +R1,1=1. | |
| 35 | +R2,1=0. | |
| 36 | +S2=110.09999999999999 | |
| 37 | +R0,2=0. | |
| 38 | +R1,2=0. | |
| 39 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | +[Marker] | |
| 2 | +FacetsCount=1 | |
| 3 | +SliderControlledXpointsCount=0 | |
| 4 | +Name=5Ref | |
| 5 | + | |
| 6 | +[Facet1] | |
| 7 | +VectorsCount=2 | |
| 8 | + | |
| 9 | +[Facet1V1] | |
| 10 | +EndPos(0,0)=-8.9120676333490003 | |
| 11 | +EndPos(0,1)=2.3980817331903383e-016 | |
| 12 | +EndPos(0,2)=-1.2505552149377763e-015 | |
| 13 | +EndPos(1,0)=8.9120676333490003 | |
| 14 | +EndPos(1,1)=2.6645352591003756e-017 | |
| 15 | +EndPos(1,2)=0. | |
| 16 | + | |
| 17 | +[Facet1V2] | |
| 18 | +EndPos(0,0)=-8.9951734202602971 | |
| 19 | +EndPos(0,1)=-12.045839883319603 | |
| 20 | +EndPos(0,2)=-4.5474735088646413e-016 | |
| 21 | +EndPos(1,0)=-8.9120676333490003 | |
| 22 | +EndPos(1,1)=2.3980817331903383e-016 | |
| 23 | +EndPos(1,2)=-1.2505552149377763e-015 | |
| 24 | + | |
| 25 | +[Tooltip2MarkerXf] | |
| 26 | +Scale=1. | |
| 27 | +S0=0. | |
| 28 | +R0,0=1. | |
| 29 | +R1,0=0. | |
| 30 | +R2,0=0. | |
| 31 | +S1=0. | |
| 32 | +R0,1=0. | |
| 33 | +R1,1=1. | |
| 34 | +R2,1=0. | |
| 35 | +S2=0. | |
| 36 | +R0,2=0. | |
| 37 | +R1,2=0. | |
| 38 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | +[Marker] | |
| 2 | +FacetsCount=1 | |
| 3 | +Name=COOLCARD | |
| 4 | + | |
| 5 | +[Facet1] | |
| 6 | +VectorsCount=2 | |
| 7 | + | |
| 8 | +[Facet1V1] | |
| 9 | +EndPos(0,0)=-31.763129600371474 | |
| 10 | +EndPos(0,1)=-6.4066430885648868e-016 | |
| 11 | +EndPos(0,2)=1.9712913472123418e-015 | |
| 12 | +EndPos(1,0)=31.763129600371467 | |
| 13 | +EndPos(1,1)=-6.4440270518456277e-016 | |
| 14 | +EndPos(1,2)=1.9656972001890368e-015 | |
| 15 | + | |
| 16 | +[Facet1V2] | |
| 17 | +EndPos(0,0)=-31.746690593113094 | |
| 18 | +EndPos(0,1)=25.423398067377672 | |
| 19 | +EndPos(0,2)=1.9712913472123418e-015 | |
| 20 | +EndPos(1,0)=-31.763129600371474 | |
| 21 | +EndPos(1,1)=-6.4066430885648868e-016 | |
| 22 | +EndPos(1,2)=1.9712913472123418e-015 | |
| 23 | + | |
| 24 | +[Tooltip2MarkerXf] | |
| 25 | +Scale=1. | |
| 26 | +S0=0. | |
| 27 | +R0,0=1. | |
| 28 | +R1,0=0. | |
| 29 | +R2,0=0. | |
| 30 | +S1=0. | |
| 31 | +R0,1=0. | |
| 32 | +R1,1=1. | |
| 33 | +R2,1=0. | |
| 34 | +S2=0. | |
| 35 | +R0,2=0. | |
| 36 | +R1,2=0. | |
| 37 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +SliderControlledXpointsCount=0 | |
| 5 | +Name=Pointer Template | |
| 6 | + | |
| 7 | +[Facet1] | |
| 8 | +VectorsCount=2 | |
| 9 | + | |
| 10 | +[Facet1V1] | |
| 11 | +EndPos(0,0)=-8.9079792482800304 | |
| 12 | +EndPos(0,1)=4.7658354227811601e-016 | |
| 13 | +EndPos(0,2)=-3.8126683382249281e-015 | |
| 14 | +EndPos(1,0)=8.9079792482800322 | |
| 15 | +EndPos(1,1)=-3.754900636130611e-016 | |
| 16 | +EndPos(1,2)=4.6214161675453676e-016 | |
| 17 | + | |
| 18 | +[Facet1V2] | |
| 19 | +EndPos(0,0)=8.9079792482800322 | |
| 20 | +EndPos(0,1)=-3.754900636130611e-016 | |
| 21 | +EndPos(0,2)=4.6214161675453676e-016 | |
| 22 | +EndPos(1,0)=8.97037014055695 | |
| 23 | +EndPos(1,1)=-11.854493390526288 | |
| 24 | +EndPos(1,2)=-5.1990931884885378e-015 | |
| 25 | + | |
| 26 | +[Tooltip2MarkerXf] | |
| 27 | +Scale=1. | |
| 28 | +S0=0. | |
| 29 | +R0,0=1. | |
| 30 | +R1,0=0. | |
| 31 | +R2,0=0. | |
| 32 | +S1=0. | |
| 33 | +R0,1=0. | |
| 34 | +R1,1=1. | |
| 35 | +R2,1=0. | |
| 36 | +S2=0. | |
| 37 | +R0,2=0. | |
| 38 | +R1,2=0. | |
| 39 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +SliderControlledXpointsCount=0 | |
| 5 | +Name=Ref | |
| 6 | + | |
| 7 | +[Facet1] | |
| 8 | +VectorsCount=2 | |
| 9 | + | |
| 10 | +[Facet1V1] | |
| 11 | +EndPos(0,0)=-8.9120676333490003 | |
| 12 | +EndPos(0,1)=2.3980817331903383e-016 | |
| 13 | +EndPos(0,2)=-1.2505552149377763e-015 | |
| 14 | +EndPos(1,0)=8.9120676333490003 | |
| 15 | +EndPos(1,1)=2.6645352591003756e-017 | |
| 16 | +EndPos(1,2)=0. | |
| 17 | + | |
| 18 | +[Facet1V2] | |
| 19 | +EndPos(0,0)=-8.9951734202602971 | |
| 20 | +EndPos(0,1)=-12.045839883319603 | |
| 21 | +EndPos(0,2)=-4.5474735088646413e-016 | |
| 22 | +EndPos(1,0)=-8.9120676333490003 | |
| 23 | +EndPos(1,1)=2.3980817331903383e-016 | |
| 24 | +EndPos(1,2)=-1.2505552149377763e-015 | |
| 25 | + | |
| 26 | +[Tooltip2MarkerXf] | |
| 27 | +Scale=1. | |
| 28 | +S0=0. | |
| 29 | +R0,0=1. | |
| 30 | +R1,0=0. | |
| 31 | +R2,0=0. | |
| 32 | +S1=0. | |
| 33 | +R0,1=0. | |
| 34 | +R1,1=1. | |
| 35 | +R2,1=0. | |
| 36 | +S2=0. | |
| 37 | +R0,2=0. | |
| 38 | +R1,2=0. | |
| 39 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | +[Marker] | |
| 2 | +FacetsCount=1 | |
| 3 | +Name=TTblock | |
| 4 | + | |
| 5 | +[Facet1] | |
| 6 | +VectorsCount=2 | |
| 7 | + | |
| 8 | +[Facet1V1] | |
| 9 | +EndPos(0,0)=-44.89806848621749 | |
| 10 | +EndPos(0,1)=1.5829351718288365e-017 | |
| 11 | +EndPos(0,2)=-2.4650420593630429e-015 | |
| 12 | +EndPos(1,0)=44.898068486217483 | |
| 13 | +EndPos(1,1)=8.5012292344588403e-016 | |
| 14 | +EndPos(1,2)=-2.4138677168217271e-015 | |
| 15 | + | |
| 16 | +[Facet1V2] | |
| 17 | +EndPos(0,0)=-0.25683199956524938 | |
| 18 | +EndPos(0,1)=42.807497943328485 | |
| 19 | +EndPos(0,2)=-8.1564930877711453e-003 | |
| 20 | +EndPos(1,0)=0.23846937714486499 | |
| 21 | +EndPos(1,1)=-42.932871913619174 | |
| 22 | +EndPos(1,2)=-8.1564930877712043e-003 | |
| 23 | + | |
| 24 | +[Tooltip2MarkerXf] | |
| 25 | +Scale=1. | |
| 26 | +S0=0. | |
| 27 | +R0,0=1. | |
| 28 | +R1,0=0. | |
| 29 | +R2,0=0. | |
| 30 | +S1=0. | |
| 31 | +R0,1=0. | |
| 32 | +R1,1=1. | |
| 33 | +R2,1=0. | |
| 34 | +S2=0. | |
| 35 | +R0,2=0. | |
| 36 | +R1,2=0. | |
| 37 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | +[Marker] | |
| 2 | +FacetsCount=1 | |
| 3 | +Name=a | |
| 4 | + | |
| 5 | +[Facet1] | |
| 6 | +VectorsCount=2 | |
| 7 | + | |
| 8 | +[Facet1V1] | |
| 9 | +EndPos(0,0)=-32.971498168704073 | |
| 10 | +EndPos(0,1)=2.5755541490385065e-016 | |
| 11 | +EndPos(0,2)=1.6979881553090628e-016 | |
| 12 | +EndPos(1,0)=32.971498168704088 | |
| 13 | +EndPos(1,1)=3.2816886463184776e-016 | |
| 14 | +EndPos(1,2)=4.4082384801292978e-017 | |
| 15 | + | |
| 16 | +[Facet1V2] | |
| 17 | +EndPos(0,0)=-32.975185360462866 | |
| 18 | +EndPos(0,1)=46.723702252369826 | |
| 19 | +EndPos(0,2)=-8.9797450521152372e-018 | |
| 20 | +EndPos(1,0)=-32.971498168704073 | |
| 21 | +EndPos(1,1)=2.5755541490385065e-016 | |
| 22 | +EndPos(1,2)=1.6979881553090628e-016 | |
| 23 | + | |
| 24 | +[Tooltip2MarkerXf] | |
| 25 | +Scale=1. | |
| 26 | +S0=0. | |
| 27 | +R0,0=1. | |
| 28 | +R1,0=0. | |
| 29 | +R2,0=0. | |
| 30 | +S1=0. | |
| 31 | +R0,1=0. | |
| 32 | +R1,1=1. | |
| 33 | +R2,1=0. | |
| 34 | +S2=0. | |
| 35 | +R0,2=0. | |
| 36 | +R1,2=0. | |
| 37 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +Name=coil | |
| 5 | + | |
| 6 | +[Facet1] | |
| 7 | +VectorsCount=2 | |
| 8 | + | |
| 9 | +[Facet1V1] | |
| 10 | +EndPos(0,0)=-70.299927206216339 | |
| 11 | +EndPos(0,1)=1.1368683772161603e-016 | |
| 12 | +EndPos(0,2)=1.3642420526593924e-015 | |
| 13 | +EndPos(1,0)=70.299927206216339 | |
| 14 | +EndPos(1,1)=-1.1368683772161603e-016 | |
| 15 | +EndPos(1,2)=6.8212102632969619e-016 | |
| 16 | + | |
| 17 | +[Facet1V2] | |
| 18 | +EndPos(0,0)=-69.651993385067129 | |
| 19 | +EndPos(0,1)=-65.744122964094188 | |
| 20 | +EndPos(0,2)=3.410605131648481e-016 | |
| 21 | +EndPos(1,0)=-70.299927206216339 | |
| 22 | +EndPos(1,1)=1.1368683772161603e-016 | |
| 23 | +EndPos(1,2)=1.3642420526593924e-015 | |
| 24 | + | |
| 25 | +[Tooltip2MarkerXf] | |
| 26 | +Scale=1. | |
| 27 | +S0=0. | |
| 28 | +R0,0=1. | |
| 29 | +R1,0=0. | |
| 30 | +R2,0=0. | |
| 31 | +S1=0. | |
| 32 | +R0,1=0. | |
| 33 | +R1,1=1. | |
| 34 | +R2,1=0. | |
| 35 | +S2=0. | |
| 36 | +R0,2=0. | |
| 37 | +R1,2=0. | |
| 38 | +R2,2=1. | ... | ... |
| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +SliderControlledXpointsCount=0 | |
| 5 | +Name=probe | |
| 6 | + | |
| 7 | +[Facet1] | |
| 8 | +VectorsCount=2 | |
| 9 | + | |
| 10 | +[Facet1V1] | |
| 11 | +EndPos(0,0)=-61.602408326043872 | |
| 12 | +EndPos(0,1)=4.2184025144074082e-016 | |
| 13 | +EndPos(0,2)=-4.556586682229099e-016 | |
| 14 | +EndPos(1,0)=61.602408326043872 | |
| 15 | +EndPos(1,1)=-9.6115500328270063e-017 | |
| 16 | +EndPos(1,2)=-1.1391466705572748e-016 | |
| 17 | + | |
| 18 | +[Facet1V2] | |
| 19 | +EndPos(0,0)=-61.59657019736504 | |
| 20 | +EndPos(0,1)=11.949604571287503 | |
| 21 | +EndPos(0,2)=1.1391466705572748e-016 | |
| 22 | +EndPos(1,0)=-61.602408326043872 | |
| 23 | +EndPos(1,1)=4.2184025144074082e-016 | |
| 24 | +EndPos(1,2)=-4.556586682229099e-016 | |
| 25 | + | |
| 26 | +[Tooltip2MarkerXf] | |
| 27 | +Scale=1. | |
| 28 | +S0=145.66478105979206 | |
| 29 | +R0,0=-8.4560706371216821e-002 | |
| 30 | +R1,0=-0.52109081782374 | |
| 31 | +R2,0=0.84930197604725266 | |
| 32 | +S1=-13.147426563284974 | |
| 33 | +R0,1=4.5008904130328986e-002 | |
| 34 | +R1,1=-0.85348236485506712 | |
| 35 | +R2,1=-0.5191743940434248 | |
| 36 | +S2=9.0630880541399712 | |
| 37 | +R0,2=0.99540126857813949 | |
| 38 | +R1,2=-5.6756022725552024e-003 | |
| 39 | +R2,2=9.5624798310161907e-002 | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +Name=sptr | |
| 5 | + | |
| 6 | +[Facet1] | |
| 7 | +VectorsCount=2 | |
| 8 | + | |
| 9 | +[Facet1V1] | |
| 10 | +EndPos(0,0)=-52.19791804784397 | |
| 11 | +EndPos(0,1)=1.2304185897405721e-016 | |
| 12 | +EndPos(0,2)=6.9721287355505385e-016 | |
| 13 | +EndPos(1,0)=52.19791804784397 | |
| 14 | +EndPos(1,1)=-7.4104692177650016e-017 | |
| 15 | +EndPos(1,2)=5.9481366254593747e-016 | |
| 16 | + | |
| 17 | +[Facet1V2] | |
| 18 | +EndPos(0,0)=-52.224959539439432 | |
| 19 | +EndPos(0,1)=11.532261461857525 | |
| 20 | +EndPos(0,2)=6.9918899867979109e-016 | |
| 21 | +EndPos(1,0)=-52.19791804784397 | |
| 22 | +EndPos(1,1)=1.2304185897405721e-016 | |
| 23 | +EndPos(1,2)=6.9721287355505385e-016 | |
| 24 | + | |
| 25 | +[Tooltip2MarkerXf] | |
| 26 | +Scale=1. | |
| 27 | +S0=132.5915439593966 | |
| 28 | +R0,0=-3.3573683844187456e-002 | |
| 29 | +R1,0=-0.99175827123861893 | |
| 30 | +R2,0=0.12364602372465228 | |
| 31 | +S1=-3.8041069565099583 | |
| 32 | +R0,1=-3.9209531312008813e-002 | |
| 33 | +R1,1=-0.12231349340138305 | |
| 34 | +R2,1=-0.9917167045009595 | |
| 35 | +S2=-0.29142356239092942 | |
| 36 | +R0,2=0.99866682152128394 | |
| 37 | +R1,2=-3.8143685738751404e-002 | |
| 38 | +R2,2=-3.4779862432738673e-002 | ... | ... |
| ... | ... | @@ -0,0 +1,38 @@ |
| 1 | + | |
| 2 | +[Marker] | |
| 3 | +FacetsCount=1 | |
| 4 | +Name=try4 | |
| 5 | + | |
| 6 | +[Facet1] | |
| 7 | +VectorsCount=2 | |
| 8 | + | |
| 9 | +[Facet1V1] | |
| 10 | +EndPos(0,0)=-57.030279071976651 | |
| 11 | +EndPos(0,1)=-1.7053025658242405e-016 | |
| 12 | +EndPos(0,2)=-3.1832314562052489e-015 | |
| 13 | +EndPos(1,0)=57.030279071976658 | |
| 14 | +EndPos(1,1)=1.7053025658242405e-016 | |
| 15 | +EndPos(1,2)=-1.8189894035458565e-015 | |
| 16 | + | |
| 17 | +[Facet1V2] | |
| 18 | +EndPos(0,0)=-56.190229598357341 | |
| 19 | +EndPos(0,1)=93.77396683857414 | |
| 20 | +EndPos(0,2)=0. | |
| 21 | +EndPos(1,0)=-57.030279071976651 | |
| 22 | +EndPos(1,1)=-1.7053025658242405e-016 | |
| 23 | +EndPos(1,2)=-3.1832314562052489e-015 | |
| 24 | + | |
| 25 | +[Tooltip2MarkerXf] | |
| 26 | +Scale=1. | |
| 27 | +S0=0. | |
| 28 | +R0,0=1. | |
| 29 | +R1,0=0. | |
| 30 | +R2,0=0. | |
| 31 | +S1=0. | |
| 32 | +R0,1=0. | |
| 33 | +R1,1=1. | |
| 34 | +R2,1=0. | |
| 35 | +S2=0. | |
| 36 | +R0,2=0. | |
| 37 | +R1,2=0. | |
| 38 | +R2,2=1. | ... | ... |