Skip to content

Commit 0f7adfb

Browse files
committed
ENH: Add rtkprojectionmatrix python application
1 parent 41597d8 commit 0f7adfb

2 files changed

Lines changed: 116 additions & 0 deletions

File tree

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#!/usr/bin/env python
2+
import argparse
3+
import sys
4+
import itk
5+
from itk import RTK as rtk
6+
7+
8+
def build_parser():
9+
parser = rtk.RTKArgumentParser(
10+
description="Saves the sparse system matrix of rtk::JosephBackProjectionImageFilter in a file. "
11+
"Only works in 2D."
12+
)
13+
14+
# General options
15+
parser.add_argument(
16+
"--geometry",
17+
"-g",
18+
help="XML geometry file name",
19+
type=str,
20+
required=True,
21+
)
22+
parser.add_argument(
23+
"--output",
24+
"-o",
25+
help="Output file name for the sparse matrix",
26+
type=str,
27+
required=True,
28+
)
29+
30+
# RTK specific groups
31+
rtk.add_rtkinputprojections_group(parser)
32+
rtk.add_rtk3Doutputimage_group(parser)
33+
34+
return parser
35+
36+
37+
def process(args_info: argparse.Namespace):
38+
OutputPixelType = itk.F
39+
Dimension = 3
40+
OutputImageType = itk.Image[OutputPixelType, Dimension]
41+
42+
reader = rtk.ProjectionsReader[OutputImageType].New()
43+
rtk.SetProjectionsReaderFromArgParse(reader, args_info)
44+
45+
if args_info.verbose:
46+
print("Reading projections...")
47+
reader.Update()
48+
49+
# Create back projection image filter
50+
if reader.GetOutput().GetLargestPossibleRegion().GetSize()[1] != 1:
51+
print(
52+
"This tool has been designed for 2D, i.e., with one row in the sinogram only."
53+
)
54+
sys.exit(1)
55+
56+
direction = reader.GetOutput().GetDirection()
57+
if abs(direction[0, 0]) != 1.0 or abs(direction[1, 1]) != 1.0:
58+
print("Projections with non-diagonal Direction is not handled.")
59+
sys.exit(1)
60+
61+
if args_info.verbose:
62+
print(f"Reading geometry information from {args_info.geometry}...")
63+
geometry = rtk.ReadGeometry(args_info.geometry)
64+
65+
constant_image_source = rtk.ConstantImageSource[OutputImageType].New()
66+
rtk.SetConstantImageSourceFromArgParse(constant_image_source, args_info)
67+
constant_image_source.Update()
68+
69+
if constant_image_source.GetOutput().GetLargestPossibleRegion().GetSize()[1] != 3:
70+
print(
71+
"This tool has been designed for 2D with Joseph project. "
72+
"Joseph requires at least 2 slices in the y direction for bilinear interpolation. "
73+
"To have one slice exactly in front of the row, use 3 slices in the volume "
74+
"with the central slice in front of the single projection row."
75+
)
76+
sys.exit(1)
77+
78+
direction = constant_image_source.GetOutput().GetDirection()
79+
if not direction.GetVnlMatrix().is_identity():
80+
print("Volume with non-identity Direction is not handled.")
81+
sys.exit(1)
82+
83+
if reader.GetOutput().GetLargestPossibleRegion().GetSize()[2] != len(
84+
geometry.GetGantryAngles()
85+
):
86+
print("Number of projections in the geometry and in the stack do not match.")
87+
sys.exit(1)
88+
89+
if args_info.verbose:
90+
print("Backprojecting volume and recording matrix values...")
91+
92+
backProjection = rtk.JosephBackProjectionImageFilter[
93+
OutputImageType,
94+
OutputImageType,
95+
].New()
96+
backProjection.SetInput(constant_image_source.GetOutput())
97+
backProjection.SetInput(1, reader.GetOutput())
98+
backProjection.SetGeometry(geometry)
99+
backProjection.Update()
100+
101+
if args_info.verbose:
102+
print("Writing matrix to disk...")
103+
104+
matlab_matrix = rtk.MatlabSparseMatrix[OutputImageType].New()
105+
matlab_matrix.Save(args_info.output)
106+
107+
108+
def main(argv=None):
109+
parser = build_parser()
110+
args_info = parser.parse_args(argv)
111+
process(args_info)
112+
113+
114+
if __name__ == "__main__":
115+
main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
itk_wrap_simple_class("rtk::MatlabSparseMatrix" POINTER)

0 commit comments

Comments
 (0)