Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4DCT / Sequential 3DCT #75

Merged
merged 10 commits into from
Apr 18, 2024
304 changes: 231 additions & 73 deletions AutoscoperM/AutoscoperM.py

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions AutoscoperM/AutoscoperMLib/IO.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ def generateCameraCalibrationFile(camera: Camera, filename: str):
contents["view-angle"] = camera.vtkCamera.GetViewAngle()
contents["image-width"] = camera.imageSize[0]
contents["image-height"] = camera.imageSize[1]
# The clipping-range field is not used by Autoscoper, it is used to communicate
# information between AutoscoperM and VirtualRadiographGeneration modules within Slicer.
contents["clipping-range"] = camera.vtkCamera.GetClippingRange()

contents_json = json.dumps(contents, indent=4)

Expand Down
7 changes: 3 additions & 4 deletions AutoscoperM/AutoscoperMLib/RadiographGeneration.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ def optimizeCameras(

:return: Optimized cameras
"""
import glob
import os

if not progressCallback:
Expand All @@ -213,11 +212,11 @@ def progressCallback(_x):
cliNodes = []
for i in range(len(cameras)):
camera = cameras[i]
vrgFName = glob.glob(os.path.join(cameraDir, f"cam{camera.id}", "*.tif"))[0]
vrgDirName = os.path.join(cameraDir, f"cam{camera.id}")
cliNode = slicer.cli.run(
cliModule,
None,
{"whiteRadiographFileName": vrgFName},
{"whiteRadiographDirName": vrgDirName},
wait_for_completion=False,
)
cliNodes.append(cliNode)
Expand All @@ -231,7 +230,7 @@ def progressCallback(_x):
slicer.mrmlScene.RemoveNode(cliNode)
raise ValueError("CLI execution failed: " + errorText)
cameras[i].DID = float(cliNodes[i].GetOutputText()) # cliNodes[i].GetParameterAsString("dataIntensityDensity")
progress = ((i + 1) / len(cameras)) * 50 + 40
progress = ((i + 1) / len(cameras)) * 40 + 50
progressCallback(progress)
slicer.mrmlScene.RemoveNode(cliNodes[i])

Expand Down
5 changes: 2 additions & 3 deletions AutoscoperM/AutoscoperMLib/SubVolumeExtraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,8 @@ def _getItemFromFolder(folderName: str) -> slicer.vtkMRMLNode:
shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
folderId = shNode.GetItemByName(folderName)
nodeId = shNode.GetItemByPositionUnderParent(folderId, 0)
nodeName = shNode.GetItemName(nodeId)

folderPlugin.setDisplayVisibility(folderId, 1)
slicer.mrmlScene.RemoveNode(slicer.util.getNode(folderName)) # remove the folder
slicer.mrmlScene.RemoveNode(shNode.GetDisplayNodeForItem(folderId)) # remove the folder

return slicer.util.getNode(nodeName) # return the node in the folder
return shNode.GetItemDataNode(nodeId) # return the node in the folder
105 changes: 61 additions & 44 deletions AutoscoperM/Resources/UI/AutoscoperM.ui
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@
<property name="nodeTypes">
<stringlist notr="true">
<string>vtkMRMLScalarVolumeNode</string>
<string>vtkMRMLSequenceNode</string>
</stringlist>
</property>
<property name="hideChildNodeTypes">
Expand Down Expand Up @@ -210,41 +211,24 @@
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="5" column="2">
<widget class="QSpinBox" name="vrgRes_width">
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>1760</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Virtual Radiograph Subdirectory:</string>
</property>
</widget>
</item>
<item row="6" column="2" colspan="2">
<widget class="QLineEdit" name="vrgTempDir">
<item row="3" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>VRG-Temp</string>
<string>Camera Subdirectory:</string>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QLineEdit" name="tfmSubDir">
<item row="1" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Transforms</string>
<string>Partial Volume Transforms Subdirectory:</string>
</property>
</widget>
</item>
<item row="3" column="2" colspan="2">
<widget class="QLineEdit" name="cameraSubDir">
<item row="2" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Calibration</string>
<string>Virtual Radiograph Subdirectory:</string>
</property>
</widget>
</item>
Expand All @@ -255,24 +239,24 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_16">
<item row="5" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>VRG Temp Subdirectory:</string>
<string>VRG Resolution: (width,height)</string>
</property>
</widget>
</item>
<item row="2" column="2" colspan="2">
<widget class="QLineEdit" name="vrgSubDir">
<item row="6" column="2" colspan="2">
<widget class="QLineEdit" name="vrgTempDir">
<property name="text">
<string>RadiographImages</string>
<string>VRG-Temp</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_15">
<item row="2" column="2" colspan="2">
<widget class="QLineEdit" name="vrgSubDir">
<property name="text">
<string>VRG Resolution: (width,height)</string>
<string>RadiographImages</string>
</property>
</widget>
</item>
Expand All @@ -292,17 +276,20 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<item row="6" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Partial Volume Subdirectory:</string>
<string>VRG Temp Subdirectory:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_20">
<item row="3" column="2" colspan="2">
<widget class="QLineEdit" name="cameraSubDir">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Partial Volume Transforms Subdirectory:</string>
<string>Calibration</string>
</property>
</widget>
</item>
Expand All @@ -316,10 +303,27 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_14">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Camera Subdirectory:</string>
<string>Partial Volume Subdirectory:</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QSpinBox" name="vrgRes_width">
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>1760</number>
</property>
</widget>
</item>
<item row="1" column="2" colspan="2">
<widget class="QLineEdit" name="tfmSubDir">
<property name="text">
<string>Transforms</string>
</property>
</widget>
</item>
Expand All @@ -333,6 +337,13 @@
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="idxOnly">
<property name="text">
<string>Only use indices for radiograph filename</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
Expand Down Expand Up @@ -715,6 +726,9 @@
<property name="text">
<string>Flip Y</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="3">
Expand Down Expand Up @@ -749,6 +763,9 @@
<property name="text">
<string>Flip X</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
Expand Down
52 changes: 33 additions & 19 deletions CalculateDataIntensityDensity/CalculateDataIntensityDensity.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
#!/usr/bin/env python-real

import concurrent.futures as cf
import glob
import os
import sys

import numpy as np
import SimpleITK as sitk

def main(whiteRadiographFName: str) -> float:
"""
Calculates the data intensity density of the given camera on its corresponding white radiograph.
Internal function used by :func:`optimizeCameras`.

:param whiteRadiographFName: White radiograph file name

return Data intensity density
"""

import numpy as np
import SimpleITK as sitk

MEAN_COMPARISON = 170 # 255 / 3 * 2

def calcDID(whiteRadiographFName):
MEAN_COMPARISON = 185
# Read in the white radiograph
if not isinstance(whiteRadiographFName, str):
raise TypeError(f"whiteRadiographFName must be a string, not {type(whiteRadiographFName)}")
if not os.path.isfile(whiteRadiographFName):
raise FileNotFoundError(f"File {whiteRadiographFName} not found.")
whiteRadiograph = sitk.ReadImage(whiteRadiographFName)

# Superpixel Segmentation
slicImageFilter = sitk.SLICImageFilter()
slicImageFilter.SetSuperGridSize([15, 15, 15]) # smaller grid size = finer grid overall default is [50,50,50]
slicImageFilter.SetSuperGridSize([85, 85, 85])
labelImage = slicImageFilter.Execute(whiteRadiograph)

# Get the mean pixel value for each label
Expand Down Expand Up @@ -66,6 +54,32 @@ def main(whiteRadiographFName: str) -> float:
return np.sum(sitk.GetArrayFromImage(largest))


def main(whiteRadiographDirName: str) -> float:
"""
Calculates the data intensity density of the given camera on its corresponding white radiograph.
Internal function used by :func:`optimizeCameras`.

:param whiteRadiographFName: White radiograph file name

return Data intensity density
"""
whiteRadiographFiles = glob.glob(os.path.join(whiteRadiographDirName, "*.tif"))

if not isinstance(whiteRadiographDirName, str):
raise TypeError(f"whiteRadiographDirName must be a string, not {type(whiteRadiographDirName)}")
if not os.path.isdir(whiteRadiographDirName):
raise FileNotFoundError(f"Directory {whiteRadiographDirName} not found.")
if len(whiteRadiographFiles) == 0:
raise FileNotFoundError(f"No white radiographs found in {whiteRadiographDirName}")

dids = []
with cf.ThreadPoolExecutor() as executor:
futures = [executor.submit(calcDID, wrFName) for wrFName in whiteRadiographFiles]
for future in cf.as_completed(futures):
dids.append(future.result())
return np.mean(dids)


if __name__ == "__main__":
expected_args = [
"whiteRadiographFileName",
Expand Down
13 changes: 10 additions & 3 deletions CalculateDataIntensityDensity/CalculateDataIntensityDensity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
<acknowledgements><![CDATA[The SlicerAutoscoperM developers gratefully acknowledge funding for this project provided by NIH NIAMS R01AR078924 (Multi-modal Tracking of In Vivo Skeletal Structures and Implants).]]></acknowledgements>
<parameters>
<string>
<name>whiteRadiographFileName</name>
<label>White Radiograph File Name</label>
<name>whiteRadiographDirName</name>
<label>White Radiograph Directory Name</label>
<index>1</index>
<description><![CDATA[Path to the white radiograph used to calculate data intensity density]]></description>
<description><![CDATA[Path to the directory that stores the white radiographs for the current camera, used to calculate data intensity density]]></description>
<channel>input</channel>
</string>
<float>
Expand All @@ -28,6 +28,13 @@
<description><![CDATA[Calculated data intensity density]]></description>
<channel>output</channel>
</float>
<float>
<name>outliers</name>
<label>DID Outliers</label>
<index>3</index>
<description><![CDATA[List of indices that fall outside of the interquartile range]]></description>
<channel>output</channel>
</float>
<label>IO</label>
<description><![CDATA[Input/output parameters]]></description>
</parameters>
Expand Down
Loading
Loading