|
a |
|
b/CLI/DensityAnalysis/DensityAnalysis.py |
|
|
1 |
#!/usr/bin/env python-real |
|
|
2 |
|
|
|
3 |
import sys |
|
|
4 |
import os |
|
|
5 |
from datetime import date |
|
|
6 |
|
|
|
7 |
import numpy as np |
|
|
8 |
|
|
|
9 |
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
|
10 |
from MusculoskeletalAnalysisCLITools import ( |
|
|
11 |
crop, |
|
|
12 |
densityMap, |
|
|
13 |
readImg, |
|
|
14 |
readMask, |
|
|
15 |
writeReport, |
|
|
16 |
) |
|
|
17 |
|
|
|
18 |
|
|
|
19 |
OUTPUT_FIELDS = [ |
|
|
20 |
("Date Analysis Performed", "The current date"), |
|
|
21 |
("Input Volume", "Name of the input volume"), |
|
|
22 |
("Area by Slice", "The area of the segment in each slice of each slice"), |
|
|
23 |
("Mean Density by Slice", "The average density of the segmented area each slice"), |
|
|
24 |
("Standard Deviation of Density by Slice", "The standard deviation of the density of each slice"), |
|
|
25 |
("Min Density by Slice", "The lowest density in the segment of each slice"), |
|
|
26 |
("Max Density by Slice", "The highest density in the segment of each slice"), |
|
|
27 |
("Mean Area", "The mean area of the segment of all slices"), |
|
|
28 |
("Standard Deviation of Area", "The standard deviation of the segmented area of all slices"), |
|
|
29 |
("Min Area", "The area of the smallest segment slice"), |
|
|
30 |
("Max Area", "The area of the largest segment slice"), |
|
|
31 |
("Mean Density", "The average density of the entire segmented volume"), |
|
|
32 |
("Standard Deviation of Density", "The standard deviation of density of the segmented volume"), |
|
|
33 |
("Min Density", "The minimum density of the segmented volume"), |
|
|
34 |
("Max Density", "The maximum density of the segmented volume"), |
|
|
35 |
] |
|
|
36 |
|
|
|
37 |
|
|
|
38 |
# Performs analysis on cortical bone |
|
|
39 |
# image: 3D image black and white of bone |
|
|
40 |
# mask: 3D labelmap of bone area, including cavities |
|
|
41 |
# voxSize: The physical side length of the voxels, in mm |
|
|
42 |
# slope, intercept and scale: parameters of the equation for converting image values to mgHA/ccm |
|
|
43 |
# output: The name of the output directory |
|
|
44 |
def main(inputImg, inputMask, voxSize, slope, intercept, name, output): |
|
|
45 |
imgData = readImg(inputImg) |
|
|
46 |
(_, maskData) = readMask(inputMask) |
|
|
47 |
(maskData, imgData) = crop(maskData, imgData) |
|
|
48 |
if np.count_nonzero(maskData) == 0: |
|
|
49 |
raise Exception("Segmentation mask is empty.") |
|
|
50 |
density = densityMap(imgData, slope, intercept) |
|
|
51 |
|
|
|
52 |
c = density.shape[2] |
|
|
53 |
area = np.zeros(c); |
|
|
54 |
meanDens = np.zeros(c); |
|
|
55 |
stdDens = np.zeros(c); |
|
|
56 |
minDens = np.zeros(c); |
|
|
57 |
maxDens = np.zeros(c); |
|
|
58 |
print("""<filter-progress>{}</filter-progress>""".format(.25)) |
|
|
59 |
sys.stdout.flush() |
|
|
60 |
# Get stats for each slice |
|
|
61 |
for sliceNum in range(c): |
|
|
62 |
sliceDensity = density[:,:,sliceNum] |
|
|
63 |
maskDensity = sliceDensity[maskData[:,:,sliceNum]] |
|
|
64 |
area[sliceNum] = len(maskDensity) * voxSize**2 |
|
|
65 |
if(area[sliceNum] != 0): |
|
|
66 |
meanDens[sliceNum] = np.mean(maskDensity) |
|
|
67 |
stdDens[sliceNum] = np.std(maskDensity) |
|
|
68 |
minDens[sliceNum] = np.min(maskDensity) |
|
|
69 |
maxDens[sliceNum] = np.max(maskDensity) |
|
|
70 |
print("""<filter-progress>{}</filter-progress>""".format(.50)) |
|
|
71 |
sys.stdout.flush() |
|
|
72 |
# Get average area of all slices |
|
|
73 |
meanArea = np.mean(area) |
|
|
74 |
stdArea = np.std(area) |
|
|
75 |
minArea = np.min(area) |
|
|
76 |
maxArea = np.max(area) |
|
|
77 |
print("""<filter-progress>{}</filter-progress>""".format(.75)) |
|
|
78 |
sys.stdout.flush() |
|
|
79 |
# Get average density of entire bone |
|
|
80 |
fullDensity = density[maskData] |
|
|
81 |
meanDensity = np.mean(fullDensity) |
|
|
82 |
stdDensity = np.std(fullDensity) |
|
|
83 |
minDensity = np.min(fullDensity) |
|
|
84 |
maxDensity = np.max(fullDensity) |
|
|
85 |
|
|
|
86 |
fPath = os.path.join(output, "density.txt") |
|
|
87 |
|
|
|
88 |
header = [field[0] for field in OUTPUT_FIELDS] |
|
|
89 |
|
|
|
90 |
data = [ |
|
|
91 |
date.today(), |
|
|
92 |
name, |
|
|
93 |
area, |
|
|
94 |
meanDens, |
|
|
95 |
stdDens, |
|
|
96 |
minDens, |
|
|
97 |
maxDens, |
|
|
98 |
meanArea, |
|
|
99 |
stdArea, |
|
|
100 |
minArea, |
|
|
101 |
maxArea, |
|
|
102 |
meanDensity, |
|
|
103 |
stdDensity, |
|
|
104 |
minDensity, |
|
|
105 |
maxDensity |
|
|
106 |
] |
|
|
107 |
writeReport(fPath, header, data) |
|
|
108 |
|
|
|
109 |
|
|
|
110 |
|
|
|
111 |
if __name__ == "__main__": |
|
|
112 |
if len(sys.argv) < 8: |
|
|
113 |
print(sys.argv) |
|
|
114 |
print("Usage: CancellousAnalysis <input> <mask> <voxelSize> <slope> <intercept> <name> <output>") |
|
|
115 |
sys.exit(1) |
|
|
116 |
main(sys.argv[1], sys.argv[2], float(sys.argv[3]), float(sys.argv[4]), float(sys.argv[5]), str(sys.argv[6]), str(sys.argv[7])) |