diff --git a/AutoscoperM/AutoscoperM.py b/AutoscoperM/AutoscoperM.py
index 80ca66e..4da7c31 100644
--- a/AutoscoperM/AutoscoperM.py
+++ b/AutoscoperM/AutoscoperM.py
@@ -211,11 +211,21 @@ def setup(self):
self.ui.ankleSampleButton.connect("clicked(bool)", lambda: self.onSampleDataButtonClicked("2023-08-01-Ankle"))
# Pre-processing Library Buttons
+ self.ui.volumeSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onCurrentNodeChanged)
+ # segmentation and PV generation
self.ui.tiffGenButton.connect("clicked(bool)", self.onGeneratePartialVolumes)
- self.ui.configGenButton.connect("clicked(bool)", self.onGenerateConfig)
- self.ui.segmentationButton.connect("clicked(bool)", self.onSegmentation)
-
+ self.ui.segGen_segmentationButton.connect("clicked(bool)", self.onSegmentation)
+ self.ui.segSTL_importModelsButton.connect("clicked(bool)", self.onImportModels)
self.ui.loadPVButton.connect("clicked(bool)", self.onLoadPV)
+ # config generation
+ self.ui.populateCameraCalListButton.connect("clicked(bool)", self.onPopulateCameraCalList)
+ self.ui.stageCameraCalFileButton.setIcon(qt.QApplication.style().standardIcon(qt.QStyle.SP_ArrowRight))
+ self.ui.stageCameraCalFileButton.connect("clicked(bool)", self.onStageCameraCalFile)
+ self.ui.populateTrialNameListButton.connect("clicked(bool)", self.onPopulateTrialNameList)
+ self.ui.stageTrialDirButton.setIcon(qt.QApplication.style().standardIcon(qt.QStyle.SP_ArrowRight))
+ self.ui.stageTrialDirButton.connect("clicked(bool)", self.onStageTrialDir)
+ self.ui.populatePartialVolumeListButton.connect("clicked(bool)", self.onPopulatePartialVolumeList)
+ self.ui.configGenButton.connect("clicked(bool)", self.onGenerateConfig)
# Default output directory
self.ui.mainOutputSelector.setCurrentPath(
@@ -225,6 +235,9 @@ def setup(self):
# Make sure parameter node is initialized (needed for module reload)
self.initializeParameterNode()
+ # Trigger any required UI updates based on the volume node selected by default
+ self.onCurrentNodeChanged()
+
def cleanup(self):
"""
Called when the application closes and the module widget is destroyed.
@@ -410,6 +423,18 @@ def onSampleDataButtonClicked(self, dataType):
for cam in range(numCams):
self.logic.AutoscoperSocket.loadFilters(cam, filterSettings)
+ def onCurrentNodeChanged(self):
+ """
+ Updates and UI components that correspond to the selected input volume node
+ """
+ volumeNode = self.ui.volumeSelector.currentNode()
+ if volumeNode:
+ with slicer.util.tryWithErrorDisplay("Failed to grab volume node information", waitCursor=True):
+ vSizeX, vSizeY, vSizeZ = self.logic.GetVolumeSpacing(volumeNode)
+ self.ui.voxelSizeX.value = vSizeX
+ self.ui.voxelSizeY.value = vSizeY
+ self.ui.voxelSizeZ.value = vSizeZ
+
def onGeneratePartialVolumes(self):
"""
This function creates partial volumes for each segment in the segmentation node for the selected volume node.
@@ -465,36 +490,90 @@ def onGenerateConfig(self):
with slicer.util.tryWithErrorDisplay("Failed to compute results", waitCursor=True):
volumeNode = self.ui.volumeSelector.currentNode()
mainOutputDir = self.ui.mainOutputSelector.currentPath
- trialName = self.ui.trialName.text
- width = self.ui.vrgRes_width.value
- height = self.ui.vrgRes_height.value
+ configFileName = self.ui.configFileName.text
+
+ configPath = os.path.join(mainOutputDir, f"{configFileName}.cfg")
tiffSubDir = self.ui.tiffSubDir.text
- vrgSubDir = self.ui.vrgSubDir.text
+ radiographSubDir = self.ui.radiographSubDir.text
calibrationSubDir = self.ui.cameraSubDir.text
+ trialList = self.ui.trialList
+ partialVolumeList = self.ui.partialVolumeList
+ camCalList = self.ui.camCalList
+
# Validate the inputs
if not self.logic.validateInputs(
volumeNode=volumeNode,
mainOutputDir=mainOutputDir,
- trialName=trialName,
- width=width,
- height=height,
- volumeSubDir=tiffSubDir,
- vrgSubDir=vrgSubDir,
+ configFileName=configFileName,
+ tiffSubDir=tiffSubDir,
+ radiographSubDir=radiographSubDir,
calibrationSubDir=calibrationSubDir,
+ trialList=trialList,
+ partialVolumeList=partialVolumeList,
+ camCalList=camCalList,
):
raise ValueError("Invalid inputs")
return
if not self.logic.validatePaths(
mainOutputDir=mainOutputDir,
tiffDir=os.path.join(mainOutputDir, tiffSubDir),
- vrgDir=os.path.join(mainOutputDir, vrgSubDir),
+ radiographSubDir=os.path.join(mainOutputDir, radiographSubDir),
calibDir=os.path.join(mainOutputDir, calibrationSubDir),
):
raise ValueError("Invalid paths")
return
+ def get_staged_items(listWidget):
+ staged_items = []
+ for row in range(listWidget.count):
+ item = listWidget.item(row)
+ widget = listWidget.itemWidget(item)
+
+ # try to find the label of this item
+ label = widget.findChild(qt.QLabel) if widget else None
+ if not label:
+ raise ValueError(f"Could not extract item label from list at index {row}")
+ staged_items.append(label.text)
+
+ return staged_items
+
+ # extract filenames from UI lists, and use them to construct the paths relative to mainOutputDir.
+ # NOTE: We rely here on the order of the files as constructed by the user in the UI. The order of items
+ # in the staged camera files list and the radiograph root dirs list are expected to match.
+ camCalFiles = [os.path.join(calibrationSubDir, item) for item in get_staged_items(camCalList)]
+ trialDirs = [os.path.join(radiographSubDir, item) for item in get_staged_items(trialList)]
+
+ if len(camCalFiles) == 0:
+ raise ValueError(
+ "Invalid inputs: must select at least one camera calibration file, but zero were provided."
+ )
+
+ if len(trialDirs) == 0:
+ raise ValueError(
+ "Invalid inputs: must select at least one radiograph subdirectory, but zero were provided."
+ )
+
+ if len(camCalFiles) != len(trialDirs):
+ raise ValueError(
+ "Invalid inputs: number of selected trial directories must match the number "
+ f"of camera calibration files: {len(camCalFiles)} != {len(trialDirs)}"
+ )
+
+ def get_checked_items(listWidget):
+ checked_items = []
+ for idx in range(listWidget.count):
+ item = listWidget.item(idx)
+ if item.checkState() == qt.Qt.Checked:
+ checked_items.append(item.text())
+ return checked_items
+
+ partialVolumeFiles = [os.path.join(tiffSubDir, item) for item in get_checked_items(partialVolumeList)]
+
+ if len(partialVolumeFiles) == 0:
+ raise ValueError("Invalid inputs: at least one volume file must be selected!")
+
optimizationOffsets = [
self.ui.optOffX.value,
self.ui.optOffY.value,
@@ -503,30 +582,91 @@ def onGenerateConfig(self):
self.ui.optOffPitch.value,
self.ui.optOffRoll.value,
]
+
volumeFlip = [
int(self.ui.flipX.isChecked()),
int(self.ui.flipY.isChecked()),
int(self.ui.flipZ.isChecked()),
]
- voxel_spacing = self.logic.GetVolumeSpacing(volumeNode)
+ renderResolution = [
+ self.ui.configRes_width.value,
+ self.ui.configRes_height.value,
+ ]
+
+ voxel_spacing = [
+ self.ui.voxelSizeX.value,
+ self.ui.voxelSizeY.value,
+ self.ui.voxelSizeZ.value,
+ ]
+
+ # Validate the inputs
+ if not self.logic.validateInputs(
+ *trialDirs,
+ *partialVolumeFiles,
+ *camCalFiles,
+ *optimizationOffsets,
+ *volumeFlip,
+ *renderResolution,
+ *voxel_spacing,
+ ):
+ raise ValueError("Invalid inputs")
+
# generate the config file
- configFilePath = IO.generateConfigFile(
- mainOutputDir,
- [tiffSubDir, vrgSubDir, calibrationSubDir],
- trialName,
+ IO.generateConfigFile(
+ outputConfigPath=configPath,
+ trialName=configFileName,
+ camCalFiles=camCalFiles,
+ camRootDirs=trialDirs,
+ volumeFiles=partialVolumeFiles,
volumeFlip=volumeFlip,
voxelSize=voxel_spacing,
- renderResolution=[int(width / 2), int(height / 2)],
+ renderResolution=renderResolution,
optimizationOffsets=optimizationOffsets,
)
- self.ui.configSelector.setCurrentPath(configFilePath)
+ # Set the path to this newly created config file in the "Config File" field in the Autoscoper Control UI
+ self.ui.configSelector.setCurrentPath(configPath)
+ slicer.util.messageBox("Success!")
+
+ def onImportModels(self):
+ """
+ Imports Models from a directory- converts to Segmentation Nodes
+ """
+ with slicer.util.tryWithErrorDisplay("Failed to compute results", waitCursor=True):
+ self.ui.progressBar.setValue(0)
+ self.ui.progressBar.setMaximum(100)
+
+ volumeNode = self.ui.volumeSelector.currentNode()
+
+ if not self.logic.validateInputs(voluemNode=volumeNode):
+ raise ValueError("Invalid inputs")
+ return
+
+ if self.ui.segSTL_loadRadioButton.isChecked():
+ segmentationFileDir = self.ui.segSTL_modelsDir.currentPath
+ if not self.logic.validatePaths(segmentationFileDir=segmentationFileDir):
+ raise ValueError("Invalid paths")
+ return
+ segmentationFiles = glob.glob(os.path.join(segmentationFileDir, "*.*"))
+ segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
+ segmentationNode.CreateDefaultDisplayNodes()
+ for idx, file in enumerate(segmentationFiles):
+ returnedNode = IO.loadSegmentation(segmentationNode, file)
+ if returnedNode:
+ # get the segment from the returned node and add it to the segmentation node
+ segment = returnedNode.GetSegmentation().GetNthSegment(0)
+ segmentationNode.GetSegmentation().AddSegment(segment)
+ slicer.mrmlScene.RemoveNode(returnedNode)
+ self.ui.progressBar.setValue((idx + 1) / len(segmentationFiles) * 100)
+ else: # Should never happen but just in case
+ raise Exception("Please select the 'Segmentation From Model' option in order to import models")
+ return
slicer.util.messageBox("Success!")
def onSegmentation(self):
"""
- Either launches the automatic segmentation process or loads in a set of segmentations from a directory
+ Launches the automatic segmentation process
"""
with slicer.util.tryWithErrorDisplay("Failed to compute results", waitCursor=True):
self.ui.progressBar.setValue(0)
@@ -551,7 +691,7 @@ def onSegmentation(self):
self.logic.cleanFilename(currentVolumeNode.GetName(), i)
segmentationNode = SubVolumeExtraction.automaticSegmentation(
currentVolumeNode,
- self.ui.segGen_ThresholdSpinBox.value,
+ self.ui.segGen_thresholdSpinBox.value,
self.ui.segGen_marginSizeSpin.value,
progressCallback=self.updateProgressBar,
)
@@ -561,24 +701,8 @@ def onSegmentation(self):
segmentationSequenceNode.SetDataNodeAtValue(segmentationNode, str(i))
slicer.mrmlScene.RemoveNode(segmentationNode)
currentVolumeNode = self.logic.getNextItemInSequence(volumeNode)
- elif self.ui.segGen_fileRadioButton.isChecked():
- segmentationFileDir = self.ui.segGen_lineEdit.currentPath
- if not self.logic.validatePaths(segmentationFileDir=segmentationFileDir):
- raise ValueError("Invalid paths")
- return
- segmentationFiles = glob.glob(os.path.join(segmentationFileDir, "*.*"))
- segmentationNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLSegmentationNode")
- segmentationNode.CreateDefaultDisplayNodes()
- for idx, file in enumerate(segmentationFiles):
- returnedNode = IO.loadSegmentation(segmentationNode, file)
- if returnedNode:
- # get the segment from the returned node and add it to the segmentation node
- segment = returnedNode.GetSegmentation().GetNthSegment(0)
- segmentationNode.GetSegmentation().AddSegment(segment)
- slicer.mrmlScene.RemoveNode(returnedNode)
- self.ui.progressBar.setValue((idx + 1) / len(segmentationFiles) * 100)
else: # Should never happen but just in case
- raise Exception("No segmentation method selected")
+ raise Exception("Please select the 'Automatic Segmentation' option in order to generate segmentations")
return
slicer.util.messageBox("Success!")
@@ -649,6 +773,136 @@ def onLoadPV(self):
slicer.util.messageBox("Success!")
+ def onPopulateTrialNameList(self):
+ """
+ Populates trial name UI list using files from the selected radiograph directory
+ """
+ with slicer.util.tryWithErrorDisplay("Failed to compute results.", waitCursor=True):
+ self.populateListFromOutputSubDir(self.ui.trialCandidateList, self.ui.radiographSubDir.text, itemType="dir")
+
+ def onPopulatePartialVolumeList(self):
+ """
+ Populates partial volumes UI list using files from the selected PV directory
+ """
+ with slicer.util.tryWithErrorDisplay("Failed to compute results.", waitCursor=True):
+ self.populateListFromOutputSubDir(self.ui.partialVolumeList, self.ui.tiffSubDir.text)
+
+ def onPopulateCameraCalList(self):
+ """
+ Populates camera calibration UI list using files from the selected camera directory
+ """
+ with slicer.util.tryWithErrorDisplay("Failed to compute results.", waitCursor=True):
+ self.populateListFromOutputSubDir(self.ui.camCalCandidateList, self.ui.cameraSubDir.text)
+
+ def populateListFromOutputSubDir(self, listWidget, fileSubDir, itemType="file"):
+ """
+ Populates input UI list with files/directories that exist in the given input directory
+ """
+ listWidget.clear()
+
+ mainOutputDir = self.ui.mainOutputSelector.currentPath
+ if not self.logic.validateInputs(
+ listWidget=listWidget,
+ mainOutputDir=mainOutputDir,
+ fileSubDir=fileSubDir,
+ ):
+ raise ValueError("Invalid inputs")
+ return
+
+ fileDir = os.path.join(mainOutputDir, fileSubDir)
+ if not self.logic.validatePaths(fileDir=fileDir):
+ raise ValueError(f"Invalid input: subdirectory '{fileDir}' does not exist.")
+ return
+
+ if itemType == "file":
+ listFiles = [f.name for f in os.scandir(fileDir) if os.path.isfile(f)]
+ elif itemType == "dir":
+ listFiles = [f.name for f in os.scandir(fileDir) if os.path.isdir(f)]
+ else:
+ raise ValueError(
+ "Invalid input: can either search for type 'file' or 'dir' "
+ f"in specified path, but given itemType='{itemType}'"
+ )
+ return
+
+ for file in sorted(listFiles):
+ fileItem = qt.QListWidgetItem(file)
+ fileItem.setFlags(fileItem.flags() & ~qt.Qt.ItemIsSelectable) # Remove the selectable flag
+ fileItem.setCheckState(qt.Qt.Unchecked)
+ listWidget.addItem(fileItem)
+
+ def onStageCameraCalFile(self):
+ """
+ Adds selected items from the camera calibration list to the staged files list
+ """
+ with slicer.util.tryWithErrorDisplay("Failed to compute results.", waitCursor=True):
+ self.stageSelectedFiles(self.ui.camCalCandidateList, self.ui.camCalList)
+
+ def onStageTrialDir(self):
+ """
+ Adds selected items from the radiograph subdirectories list to the staged subdirs list
+ """
+ with slicer.util.tryWithErrorDisplay("Failed to compute results.", waitCursor=True):
+ self.stageSelectedFiles(self.ui.trialCandidateList, self.ui.trialList)
+
+ def stageSelectedFiles(self, candidateListWidget, listWidget):
+ """
+ Stages chosen files into listWidget based on the selected items
+ in candidateListWidget which contains all candidate file names
+ """
+ # gether checked items from the input candidate list
+ checked_items = []
+ for idx in range(candidateListWidget.count):
+ item = candidateListWidget.item(idx)
+ if item.checkState() == qt.Qt.Checked:
+ checked_items.append(item.text())
+ item.setCheckState(qt.Qt.Unchecked)
+
+ if len(checked_items) == 0:
+ raise ValueError("No items were selected.")
+
+ def stagedItemExists(itemText):
+ # iterate over the list items and see if item with the given label already exists
+ for row in range(listWidget.count):
+ item = listWidget.item(row)
+ widget = listWidget.itemWidget(item)
+ if widget:
+ # extract label to compare the text in the item
+ label = widget.findChild(qt.QLabel)
+ if label and label.text == itemText:
+ return True
+ return False
+
+ # stage all selected items if they're not already in the target list
+ for file in checked_items:
+ if not stagedItemExists(file):
+ # create item widget with text and a delete button
+ itemBaseWidget = qt.QWidget()
+ itemLayout = qt.QHBoxLayout()
+ itemLabel = qt.QLabel(file)
+ itemDeleteButton = qt.QPushButton("Delete")
+
+ # set styling attributes to make it look nice in the UI
+ itemLayout.setContentsMargins(3, 1, 3, 1)
+ itemLayout.setSpacing(3)
+ itemDeleteButton.setSizePolicy(qt.QSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Fixed))
+
+ itemLayout.addWidget(itemLabel)
+ # add spacing so that the delete button is always aligned to the right
+ itemLayout.addItem(qt.QSpacerItem(0, 0, qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum))
+ itemLayout.addWidget(itemDeleteButton)
+ itemBaseWidget.setLayout(itemLayout)
+ itemWidget = qt.QListWidgetItem(listWidget)
+ itemWidget.setFlags(itemWidget.flags() & ~qt.Qt.ItemIsSelectable)
+
+ # finally, add the composite widget as an item to the list
+ listWidget.setItemWidget(itemWidget, itemBaseWidget)
+
+ # add delete functionality to the button
+ itemDeleteButton.clicked.connect(lambda _, item=itemWidget: listWidget.takeItem(listWidget.row(item)))
+ else:
+ logging.info(f"Skipped adding the item '{file}' as it already exists in the target list.")
+
#
# AutoscoperMLogic
diff --git a/AutoscoperM/AutoscoperMLib/IO.py b/AutoscoperM/AutoscoperMLib/IO.py
index 01bf5c5..890e7ba 100644
--- a/AutoscoperM/AutoscoperMLib/IO.py
+++ b/AutoscoperM/AutoscoperMLib/IO.py
@@ -1,4 +1,3 @@
-import glob
import logging
import os
from itertools import product
@@ -34,53 +33,34 @@ def loadSegmentation(segmentationNode: slicer.vtkMRMLSegmentationNode, filename:
def generateConfigFile(
- mainDirectory: str,
- subDirectories: list[str],
+ outputConfigPath: str,
trialName: str,
+ camCalFiles: list[str],
+ camRootDirs: list[str],
+ volumeFiles: list[str],
volumeFlip: list[int],
voxelSize: list[float],
renderResolution: list[int],
optimizationOffsets: list[float],
-) -> str:
+) -> None:
"""
Generates the v1.1 config file for the trial
- :param mainDirectory: Main directory
- :param subDirectories: Sub directories
+ :param outputConfigPath: The absolute path to the config file to be generated
:param trialName: Trial name
- :param volumeFlip: Volume flip
- :param voxelSize: Voxel size
- :param renderResolution: Render resolution
- :param optimizationOffsets: Optimization offsets
+ :param camCalFiles: The list of camera calibration file paths, relative to the main output dir
+ :param camRootDirs: The list of the radiograph directory paths, relative to the main output dir
+ :param volumeFiles: The list of tiff volume files, relative to the main output dir
+ :param volumeFlip: The flip settings for each of the volumes
+ :param voxelSize: The voxel size of each of the the volumes
+ :param renderResolution: The resolution of the 2D rendering of each of the volumes
+ :param optimizationOffsets: The offsets for the optimization
:return: Path to the config file
"""
import datetime
- # Get the camera calibration files, camera root directories, and volumes
- volumes = glob.glob(os.path.join(mainDirectory, subDirectories[0], "*.tif"))
- cameraRootDirs = glob.glob(os.path.join(mainDirectory, subDirectories[1], "*"))
- calibrationFiles = glob.glob(os.path.join(mainDirectory, subDirectories[2], "*.json"))
-
- # Check that we have the same number of camera calibration files and camera root directories
- if len(calibrationFiles) != len(cameraRootDirs):
- logging.error(
- "Number of camera calibration files and camera root directories do not match: "
- " {len(calibrationFiles)} != {len(cameraRootDirs)}"
- )
- return None
-
- # Check that we have at least one volume
- if len(volumes) == 0:
- logging.error("No volumes found!")
- return None
-
- # Transform the paths to be relative to the main directory
- calibrationFiles = [os.path.relpath(calibrationFile, mainDirectory) for calibrationFile in calibrationFiles]
- cameraRootDirs = [os.path.relpath(cameraRootDir, mainDirectory) for cameraRootDir in cameraRootDirs]
- volumes = [os.path.relpath(volume, mainDirectory) for volume in volumes]
-
- with open(os.path.join(mainDirectory, trialName + ".cfg"), "w+") as f:
+ with open(outputConfigPath, "w+") as f:
# Trial Name as comment
f.write(f"# {trialName} configuration file\n")
f.write(
@@ -94,19 +74,19 @@ def generateConfigFile(
# Camera Calibration Files
f.write("# Camera Calibration Files\n")
- for calibrationFile in calibrationFiles:
+ for calibrationFile in camCalFiles:
f.write("mayaCam_csv " + calibrationFile + "\n")
f.write("\n")
# Camera Root Directories
f.write("# Camera Root Directories\n")
- for cameraRootDir in cameraRootDirs:
+ for cameraRootDir in camRootDirs:
f.write("CameraRootDir " + cameraRootDir + "\n")
f.write("\n")
# Volumes
f.write("# Volumes\n")
- for volume in volumes:
+ for volume in volumeFiles:
f.write("VolumeFile " + volume + "\n")
f.write("VolumeFlip " + " ".join([str(x) for x in volumeFlip]) + "\n")
f.write("VoxelSize " + " ".join([str(x) for x in voxelSize]) + "\n")
@@ -122,8 +102,6 @@ def generateConfigFile(
f.write("OptimizationOffsets " + " ".join([str(x) for x in optimizationOffsets]) + "\n")
f.write("\n")
- return os.path.join(mainDirectory, trialName + ".cfg")
-
def writeVolume(volumeNode: slicer.vtkMRMLVolumeNode, filename: str):
"""
diff --git a/AutoscoperM/Resources/UI/AutoscoperM.ui b/AutoscoperM/Resources/UI/AutoscoperM.ui
index 09d347a..6d8625a 100644
--- a/AutoscoperM/Resources/UI/AutoscoperM.ui
+++ b/AutoscoperM/Resources/UI/AutoscoperM.ui
@@ -9,8 +9,8 @@
0
0
- 1018
- 860
+ 659
+ 1566
@@ -120,6 +120,13 @@
+ -
+
+
+ Qt::Vertical
+
+
+
@@ -128,254 +135,145 @@
-
-
+
+
+ 0
+
+
+ false
+
+
+
+ -
+
+
-
+
+
+ Output Directory:
+
+
+
+ -
+
+
+ ctkPathLineEdit::Dirs|ctkPathLineEdit::Executable|ctkPathLineEdit::NoDot|ctkPathLineEdit::NoDotDot|ctkPathLineEdit::Writable
+
+
+
+ -
+
+
+ Volume Node:
+
+
+
+ -
+
+
+ true
+
+
+
+ vtkMRMLScalarVolumeNode
+ vtkMRMLSequenceNode
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
- General Input
+ Segmentation Generation
true
-
+
+ false
+
+
-
-
+
- Volume Node:
-
-
-
- -
-
-
- false
-
-
-
- vtkMRMLScalarVolumeNode
- vtkMRMLSequenceNode
-
-
-
-
+ Automatic Segmentation
-
-
+
+ true
- -
-
+
-
+
- Output Directory:
-
-
-
- -
-
-
- ctkPathLineEdit::Dirs|ctkPathLineEdit::Executable|ctkPathLineEdit::NoDot|ctkPathLineEdit::NoDotDot|ctkPathLineEdit::Writable
+ Threshold Value:
- -
-
-
- true
+
-
+
+
+ 10000
-
- Trial Name:
+
+ 700
- -
-
-
- true
+
-
+
+
+ Generate Segmentations
- -
-
-
- true
-
-
- Advanced Options
-
-
- true
-
-
- true
-
-
- false
+
-
+
+
+ Margin Size:
-
-
-
-
-
- Camera Subdirectory:
-
-
-
- -
-
-
- Radiograph Subdirectory:
-
-
-
- -
-
-
- true
-
-
- Calibration
-
-
-
- -
-
-
- Tracking Subdirectory:
-
-
-
- -
-
-
- VRG Resolution: (width,height)
-
-
-
- -
-
-
- Transforms
-
-
-
- -
-
-
- Volumes
-
-
-
- -
-
-
- 999999999
-
-
- 1760
-
-
-
- -
-
-
- Partial Volume Transforms Subdirectory:
-
-
-
- -
-
-
- RadiographImages
-
-
-
- -
-
-
- 999999999
-
-
- 1760
-
-
-
- -
-
-
- Partial Volume Subdirectory:
-
-
-
- -
-
-
- Model Subdirectory:
-
-
-
- -
-
-
- Tracking
-
-
-
- -
-
-
- Models
-
-
-
-
- -
-
+
-
+
- 0
-
-
- false
+ 2.000000000000000
-
-
-
- -
-
-
- Segmentation Generation
-
-
- false
-
-
-
-
+
- Threshold Value:
+ OR
- -
-
+
-
+
- Automatic Segmentation
-
-
- true
+ Segmentation from Model
- -
-
+
-
+
+
+ false
+
- Batch Load from File
+ STL Models Directory:
- -
-
+
-
+
false
@@ -384,41 +282,13 @@
- -
-
-
- 10000
-
-
- 700
-
-
-
- -
-
-
- Segmentation File Directory:
-
-
-
- -
-
-
- Generate Segmentations
+
-
+
+
+ false
-
-
- -
-
- Margin Size:
-
-
-
- -
-
-
- 2.000000000000000
+ Import Models
@@ -431,10 +301,20 @@
Partial Volume Generation
+ true
+
+
false
- -
+
-
+
+
+ Segmentation Node:
+
+
+
+ -
true
@@ -452,21 +332,14 @@
- -
-
-
- Segmentation Node:
-
-
-
- -
+
-
Generate Partial Volumes
- -
+
-
Load Partial Volumes
@@ -477,148 +350,557 @@
-
-
+
true
- Generate Config
+ Default Subdirectories
+
+
+ true
- false
+ true
true
-
-
-
-
+
+
-
+
- Flip Y
-
-
- false
+ Partial Volume Subdirectory:
- -
-
-
- 0.100000000000000
-
-
- 0.100000000000000
+
-
+
+
+ Volumes
- -
-
+
-
+
- Generate Config File
+ Partial Volume Transforms Subdirectory:
- -
-
-
- 0.100000000000000
-
-
- 0.100000000000000
+
-
+
+
+ Transforms
- -
-
-
- Flip X
+
-
+
+
+
+ 75
+ true
+
-
- false
+
+ Radiograph Subdirectory:
- -
-
-
- 0.100000000000000
+
-
+
+
+
+ 75
+ true
+
-
- 0.100000000000000
+
+ RadiographImages
- -
-
-
- 0.100000000000000
+
-
+
+
+
+ 75
+ true
+
-
- 0.100000000000000
+
+ Camera Subdirectory:
- -
-
+
-
+
+
+ true
+
+
+
+ 75
+ true
+
+
- Flip Z
+ Calibration
- -
-
-
- 0.100000000000000
-
-
- 0.100000000000000
+
-
+
+
+ Tracking Subdirectory:
- -
-
-
- 0.100000000000000
-
-
- 0.100000000000000
+
-
+
+
+ Tracking
- -
-
+
-
+
- Optimization Offsets:
+ Model Subdirectory:
- -
-
+
-
+
- Volume Flip:
+ Models
+ -
+
+
+ true
+
+
+ Generate Config
+
+
+ true
+
+
+ false
+
+
+
-
+
+
-
+
+
+ Config Trial Name:
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ .cfg
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 0
+ 10
+
+
+
+
+ -
+
+
+ Select Paired Camera Calibrations:
+
+
+
+ -
+
+
+
+ QListWidget::indicator:unchecked {
+ background-color: palette(alternate-base);
+ }
+
+
+
+
+ -
+
+
+
+
+
+ Add selected camera calibration file as next in order
+
+
+
+ 40
+ 16777215
+
+
+
+
+ -
+
+
+ -
+
+
+ Populate From Camera Subdirectory
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 0
+ 10
+
+
+
+
+ -
+
+
+ Select Paired Radiograph Subirectories:
+
+
+
+ -
+
+
+
+ QListWidget::indicator:unchecked {
+ background-color: palette(alternate-base);
+ }
+
+
+
+
+ -
+
+
+
+
+
+ Add selected radiograph subdirectory as next in order
+
+
+
+ 40
+ 16777215
+
+
+
+
+ -
+
+
+ -
+
+
+ Populate From Radiographs Subdirectory
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 0
+ 10
+
+
+
+
+ -
+
+
+ Select Partial Volumes:
+
+
+
+ -
+
+
+
+ QListWidget::indicator:unchecked {
+ background-color: palette(alternate-base);
+ }
+
+
+
+
+ -
+
+
+ Populate From Volume Subdirectory
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 0
+ 10
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Optimization Offsets:
+
+
+
+ -
+
+
+ 0.100000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+ 0.100000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+ 0.100000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+ 0.100000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+ 0.100000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+ 0.100000000000000
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+ Volume Flip:
+
+
+
+ -
+
+
+ Flip X
+
+
+ false
+
+
+
+ -
+
+
+ Flip Y
+
+
+ false
+
+
+
+ -
+
+
+ Flip Z
+
+
+
+ -
+
+
+ Render Resolution: (width,height)
+
+
+
+ -
+
+
+ 999999999
+
+
+ 1760
+
+
+
+ -
+
+
+ 999999999
+
+
+ 1760
+
+
+
+ -
+
+
+ Voxel Size:
+
+
+
+ -
+
+
+ true
+
+
+ 3
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ true
+
+
+ 3
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ true
+
+
+ 3
+
+
+ 1.000000000000000
+
+
+
+ -
+
+
+ Generate Config File
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
@@ -639,17 +921,6 @@
1
-
- ctkCollapsibleGroupBox
- QGroupBox
-
- 1
-
-
- ctkDoubleRangeSlider
- QWidget
-
-
ctkPathLineEdit
QWidget
@@ -677,7 +948,7 @@
segGen_autoRadioButton
toggled(bool)
- segGen_ThresholdSpinBox
+ segGen_thresholdSpinBox
setEnabled(bool)
@@ -691,9 +962,9 @@
- segGen_fileRadioButton
+ segSTL_loadRadioButton
toggled(bool)
- segGen_lineEdit
+ segSTL_modelsDir
setEnabled(bool)
@@ -738,5 +1009,85 @@
+
+ segSTL_loadRadioButton
+ toggled(bool)
+ segSTL_importModelsButton
+ setEnabled(bool)
+
+
+ 122
+ 276
+
+
+ 698
+ 204
+
+
+
+
+ segGen_autoRadioButton
+ toggled(bool)
+ segGen_segmentationButton
+ setEnabled(bool)
+
+
+ 122
+ 189
+
+
+ 698
+ 276
+
+
+
+
+ segSTL_loadRadioButton
+ toggled(bool)
+ segSTL_modelsDirLabel
+ setEnabled(bool)
+
+
+ 122
+ 276
+
+
+ 279
+ 276
+
+
+
+
+ segGen_autoRadioButton
+ toggled(bool)
+ segGen_thresholdLabel
+ setEnabled(bool)
+
+
+ 122
+ 189
+
+
+ 279
+ 189
+
+
+
+
+ segGen_autoRadioButton
+ toggled(bool)
+ segGen_marginSizeLabel
+ setEnabled(bool)
+
+
+ 122
+ 189
+
+
+ 279
+ 220
+
+
+