dbdicom 0.2.0__py3-none-any.whl → 0.3.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dbdicom/__init__.py +3 -25
- dbdicom/api.py +496 -0
- dbdicom/const.py +144 -0
- dbdicom/database.py +133 -0
- dbdicom/dataset.py +471 -0
- dbdicom/dbd.py +1290 -0
- dbdicom/external/__pycache__/__init__.cpython-311.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-311.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-311.pyc +0 -0
- dbdicom/external/dcm4che/bin/emf2sf +57 -57
- dbdicom/register.py +402 -0
- dbdicom/{ds/types → sop_classes}/ct_image.py +2 -16
- dbdicom/{ds/types → sop_classes}/enhanced_mr_image.py +206 -160
- dbdicom/sop_classes/mr_image.py +338 -0
- dbdicom/sop_classes/parametric_map.py +381 -0
- dbdicom/sop_classes/secondary_capture.py +140 -0
- dbdicom/sop_classes/segmentation.py +311 -0
- dbdicom/{ds/types → sop_classes}/ultrasound_multiframe_image.py +1 -15
- dbdicom/{ds/types → sop_classes}/xray_angiographic_image.py +2 -17
- dbdicom/utils/arrays.py +142 -0
- dbdicom/utils/files.py +0 -20
- dbdicom/utils/image.py +43 -466
- dbdicom/utils/pydicom_dataset.py +386 -0
- dbdicom-0.3.16.dist-info/METADATA +26 -0
- dbdicom-0.3.16.dist-info/RECORD +54 -0
- {dbdicom-0.2.0.dist-info → dbdicom-0.3.16.dist-info}/WHEEL +1 -1
- dbdicom/create.py +0 -450
- dbdicom/ds/__init__.py +0 -10
- dbdicom/ds/create.py +0 -63
- dbdicom/ds/dataset.py +0 -841
- dbdicom/ds/dictionaries.py +0 -620
- dbdicom/ds/types/mr_image.py +0 -267
- dbdicom/ds/types/parametric_map.py +0 -226
- dbdicom/external/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/lib/linux-x86/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/linux-x86-64/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/linux-x86-64/libopencv_java.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis2.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis2.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-x86/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-x86-64/libclib_jiio.so +0 -0
- dbdicom/manager.py +0 -2077
- dbdicom/message.py +0 -119
- dbdicom/record.py +0 -1526
- dbdicom/types/database.py +0 -107
- dbdicom/types/instance.py +0 -184
- dbdicom/types/patient.py +0 -40
- dbdicom/types/series.py +0 -816
- dbdicom/types/study.py +0 -58
- dbdicom/utils/variables.py +0 -155
- dbdicom/utils/vreg.py +0 -2626
- dbdicom/wrappers/__init__.py +0 -7
- dbdicom/wrappers/dipy.py +0 -462
- dbdicom/wrappers/elastix.py +0 -855
- dbdicom/wrappers/numpy.py +0 -119
- dbdicom/wrappers/scipy.py +0 -1413
- dbdicom/wrappers/skimage.py +0 -1030
- dbdicom/wrappers/sklearn.py +0 -151
- dbdicom/wrappers/vreg.py +0 -273
- dbdicom-0.2.0.dist-info/METADATA +0 -276
- dbdicom-0.2.0.dist-info/RECORD +0 -81
- {dbdicom-0.2.0.dist-info → dbdicom-0.3.16.dist-info/licenses}/LICENSE +0 -0
- {dbdicom-0.2.0.dist-info → dbdicom-0.3.16.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
# -------------------------------------------------------------------------
|
|
3
|
-
# emf2sf Launcher
|
|
4
|
-
# -------------------------------------------------------------------------
|
|
5
|
-
|
|
6
|
-
MAIN_CLASS=org.dcm4che3.tool.emf2sf.Emf2sf
|
|
7
|
-
MAIN_JAR=dcm4che-tool-emf2sf-5.23.1.jar
|
|
8
|
-
|
|
9
|
-
DIRNAME="`dirname "$0"`"
|
|
10
|
-
|
|
11
|
-
# OS specific support (must be 'true' or 'false').
|
|
12
|
-
cygwin=false;
|
|
13
|
-
case "`uname`" in
|
|
14
|
-
CYGWIN*)
|
|
15
|
-
cygwin=true
|
|
16
|
-
;;
|
|
17
|
-
esac
|
|
18
|
-
|
|
19
|
-
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
|
20
|
-
if $cygwin ; then
|
|
21
|
-
[ -n "$DCM4CHE_HOME" ] &&
|
|
22
|
-
DCM4CHE_HOME=`cygpath --unix "$DCM4CHE_HOME"`
|
|
23
|
-
[ -n "$JAVA_HOME" ] &&
|
|
24
|
-
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# Setup DCM4CHE_HOME
|
|
28
|
-
if [ "x$DCM4CHE_HOME" = "x" ]; then
|
|
29
|
-
DCM4CHE_HOME=`cd "$DIRNAME"/..; pwd`
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
# Setup the JVM
|
|
33
|
-
if [ "x$JAVA_HOME" != "x" ]; then
|
|
34
|
-
JAVA=$JAVA_HOME/bin/java
|
|
35
|
-
else
|
|
36
|
-
JAVA="java"
|
|
37
|
-
fi
|
|
38
|
-
|
|
39
|
-
# Setup the classpath
|
|
40
|
-
CP="$DCM4CHE_HOME/etc/emf2sf/"
|
|
41
|
-
CP="$CP:$DCM4CHE_HOME/lib/$MAIN_JAR"
|
|
42
|
-
CP="$CP:$DCM4CHE_HOME/lib/dcm4che-core-5.23.1.jar"
|
|
43
|
-
CP="$CP:$DCM4CHE_HOME/lib/dcm4che-emf-5.23.1.jar"
|
|
44
|
-
CP="$CP:$DCM4CHE_HOME/lib/dcm4che-tool-common-5.23.1.jar"
|
|
45
|
-
CP="$CP:$DCM4CHE_HOME/lib/slf4j-api-1.7.30.jar"
|
|
46
|
-
CP="$CP:$DCM4CHE_HOME/lib/slf4j-log4j12-1.7.30.jar"
|
|
47
|
-
CP="$CP:$DCM4CHE_HOME/lib/log4j-1.2.17.jar"
|
|
48
|
-
CP="$CP:$DCM4CHE_HOME/lib/commons-cli-1.4.jar"
|
|
49
|
-
|
|
50
|
-
# For Cygwin, switch paths to Windows format before running java
|
|
51
|
-
if $cygwin; then
|
|
52
|
-
JAVA=`cygpath --path --windows "$JAVA"`
|
|
53
|
-
CP=`cygpath --path --windows "$CP"`
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# Execute the JVM
|
|
57
|
-
exec "$JAVA" $JAVA_OPTS -cp "$CP" $MAIN_CLASS "$@"
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# -------------------------------------------------------------------------
|
|
3
|
+
# emf2sf Launcher
|
|
4
|
+
# -------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
MAIN_CLASS=org.dcm4che3.tool.emf2sf.Emf2sf
|
|
7
|
+
MAIN_JAR=dcm4che-tool-emf2sf-5.23.1.jar
|
|
8
|
+
|
|
9
|
+
DIRNAME="`dirname "$0"`"
|
|
10
|
+
|
|
11
|
+
# OS specific support (must be 'true' or 'false').
|
|
12
|
+
cygwin=false;
|
|
13
|
+
case "`uname`" in
|
|
14
|
+
CYGWIN*)
|
|
15
|
+
cygwin=true
|
|
16
|
+
;;
|
|
17
|
+
esac
|
|
18
|
+
|
|
19
|
+
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
|
20
|
+
if $cygwin ; then
|
|
21
|
+
[ -n "$DCM4CHE_HOME" ] &&
|
|
22
|
+
DCM4CHE_HOME=`cygpath --unix "$DCM4CHE_HOME"`
|
|
23
|
+
[ -n "$JAVA_HOME" ] &&
|
|
24
|
+
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Setup DCM4CHE_HOME
|
|
28
|
+
if [ "x$DCM4CHE_HOME" = "x" ]; then
|
|
29
|
+
DCM4CHE_HOME=`cd "$DIRNAME"/..; pwd`
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Setup the JVM
|
|
33
|
+
if [ "x$JAVA_HOME" != "x" ]; then
|
|
34
|
+
JAVA=$JAVA_HOME/bin/java
|
|
35
|
+
else
|
|
36
|
+
JAVA="java"
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Setup the classpath
|
|
40
|
+
CP="$DCM4CHE_HOME/etc/emf2sf/"
|
|
41
|
+
CP="$CP:$DCM4CHE_HOME/lib/$MAIN_JAR"
|
|
42
|
+
CP="$CP:$DCM4CHE_HOME/lib/dcm4che-core-5.23.1.jar"
|
|
43
|
+
CP="$CP:$DCM4CHE_HOME/lib/dcm4che-emf-5.23.1.jar"
|
|
44
|
+
CP="$CP:$DCM4CHE_HOME/lib/dcm4che-tool-common-5.23.1.jar"
|
|
45
|
+
CP="$CP:$DCM4CHE_HOME/lib/slf4j-api-1.7.30.jar"
|
|
46
|
+
CP="$CP:$DCM4CHE_HOME/lib/slf4j-log4j12-1.7.30.jar"
|
|
47
|
+
CP="$CP:$DCM4CHE_HOME/lib/log4j-1.2.17.jar"
|
|
48
|
+
CP="$CP:$DCM4CHE_HOME/lib/commons-cli-1.4.jar"
|
|
49
|
+
|
|
50
|
+
# For Cygwin, switch paths to Windows format before running java
|
|
51
|
+
if $cygwin; then
|
|
52
|
+
JAVA=`cygpath --path --windows "$JAVA"`
|
|
53
|
+
CP=`cygpath --path --windows "$CP"`
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Execute the JVM
|
|
57
|
+
exec "$JAVA" $JAVA_OPTS -cp "$CP" $MAIN_CLASS "$@"
|
dbdicom/register.py
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import csv
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AmbiguousError(Exception):
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def add_instance(dbtree:list, attr, rel_path):
|
|
11
|
+
|
|
12
|
+
# Get patient and create if needed
|
|
13
|
+
pts = [pt for pt in sorted(dbtree, key=lambda pt: pt['PatientID']) if pt['PatientID']==attr['PatientID']]
|
|
14
|
+
if pts==[]:
|
|
15
|
+
pt = {
|
|
16
|
+
'PatientName': attr['PatientName'],
|
|
17
|
+
'PatientID': attr['PatientID'],
|
|
18
|
+
'studies': [],
|
|
19
|
+
}
|
|
20
|
+
dbtree.append(pt)
|
|
21
|
+
else:
|
|
22
|
+
pt = pts[0]
|
|
23
|
+
|
|
24
|
+
# Get study and create if needed
|
|
25
|
+
sts = [st for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']) if st['StudyInstanceUID']==attr['StudyInstanceUID']]
|
|
26
|
+
if sts==[]:
|
|
27
|
+
st = {
|
|
28
|
+
'StudyDescription': attr['StudyDescription'],
|
|
29
|
+
'StudyID': attr['StudyID'],
|
|
30
|
+
'StudyInstanceUID': attr['StudyInstanceUID'],
|
|
31
|
+
'series': [],
|
|
32
|
+
}
|
|
33
|
+
pt['studies'].append(st)
|
|
34
|
+
else:
|
|
35
|
+
st = sts[0]
|
|
36
|
+
|
|
37
|
+
# Get series and create if needed
|
|
38
|
+
srs = [sr for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']) if sr['SeriesInstanceUID']==attr['SeriesInstanceUID']]
|
|
39
|
+
if srs==[]:
|
|
40
|
+
sr = {
|
|
41
|
+
'SeriesNumber': attr['SeriesNumber'],
|
|
42
|
+
'SeriesDescription': attr['SeriesDescription'],
|
|
43
|
+
'SeriesInstanceUID': attr['SeriesInstanceUID'],
|
|
44
|
+
'instances': {},
|
|
45
|
+
}
|
|
46
|
+
st['series'].append(sr)
|
|
47
|
+
else:
|
|
48
|
+
sr = srs[0]
|
|
49
|
+
|
|
50
|
+
# Add instance
|
|
51
|
+
sr['instances'][attr['InstanceNumber']] = rel_path
|
|
52
|
+
|
|
53
|
+
return dbtree
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def files(dbtree, entity):
|
|
57
|
+
# Raises an error if the entity does not exist or has no files
|
|
58
|
+
relpath = index(dbtree, entity)
|
|
59
|
+
if relpath==[]:
|
|
60
|
+
raise ValueError(f'No files in entity {entity}')
|
|
61
|
+
if isinstance(entity, str):
|
|
62
|
+
return [os.path.join(entity, str(Path(*f))) for f in relpath]
|
|
63
|
+
else:
|
|
64
|
+
return [os.path.join(entity[0], str(Path(*f))) for f in relpath]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def index(dbtree, entity):
|
|
68
|
+
if isinstance(entity, str):
|
|
69
|
+
idx = []
|
|
70
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
71
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
72
|
+
for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']):
|
|
73
|
+
idx += list(sr['instances'].values())
|
|
74
|
+
return idx
|
|
75
|
+
elif len(entity)==2:
|
|
76
|
+
patient_id = entity[1]
|
|
77
|
+
idx = []
|
|
78
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
79
|
+
if pt['PatientID'] == patient_id:
|
|
80
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
81
|
+
for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']):
|
|
82
|
+
idx += list(sr['instances'].values())
|
|
83
|
+
return idx
|
|
84
|
+
raise ValueError(f'Patient {patient_id} not found')
|
|
85
|
+
elif len(entity)==3:
|
|
86
|
+
study_uid = uid(dbtree, entity)
|
|
87
|
+
idx = []
|
|
88
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
89
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
90
|
+
if st['StudyInstanceUID'] == study_uid:
|
|
91
|
+
for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']):
|
|
92
|
+
idx += list(sr['instances'].values())
|
|
93
|
+
return idx
|
|
94
|
+
raise ValueError(f'Study {study_uid} not found')
|
|
95
|
+
elif len(entity)==4:
|
|
96
|
+
series_uid = uid(dbtree, entity)
|
|
97
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
98
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
99
|
+
for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']):
|
|
100
|
+
if sr['SeriesInstanceUID'] == series_uid:
|
|
101
|
+
return list(sr['instances'].values())
|
|
102
|
+
raise ValueError(f'Series {series_uid} not found')
|
|
103
|
+
|
|
104
|
+
def remove(dbtree, entity):
|
|
105
|
+
if len(entity)==2:
|
|
106
|
+
patient_id = entity[1]
|
|
107
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
108
|
+
if pt['PatientID'] == patient_id:
|
|
109
|
+
dbtree.remove(pt)
|
|
110
|
+
elif len(entity)==3:
|
|
111
|
+
study_uid = uid(dbtree, entity)
|
|
112
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
113
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
114
|
+
if st['StudyInstanceUID'] == study_uid:
|
|
115
|
+
pt['studies'].remove(st)
|
|
116
|
+
elif len(entity)==4:
|
|
117
|
+
series_uid = uid(dbtree, entity)
|
|
118
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
119
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
120
|
+
for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']):
|
|
121
|
+
if sr['SeriesInstanceUID'] == series_uid:
|
|
122
|
+
st['series'].remove(sr)
|
|
123
|
+
return dbtree
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def drop(dbtree, relpaths):
|
|
127
|
+
for pt in sorted(dbtree[:], key=lambda pt: pt['PatientID']):
|
|
128
|
+
for st in sorted(pt['studies'][:], key=lambda st: st['StudyInstanceUID']):
|
|
129
|
+
for sr in sorted(st['series'][:], key=lambda sr: sr['SeriesNumber']):
|
|
130
|
+
for nr, relpath in list(sr['instances'].items()):
|
|
131
|
+
if relpath in relpaths:
|
|
132
|
+
del sr['instances'][nr]
|
|
133
|
+
if sr['instances'] == []:
|
|
134
|
+
st['series'].remove(sr)
|
|
135
|
+
if st['series'] == []:
|
|
136
|
+
pt['studies'].remove(st)
|
|
137
|
+
return dbtree
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def uid(dbtree, entity): # uid from entity
|
|
142
|
+
if len(entity)==2:
|
|
143
|
+
return entity[1]
|
|
144
|
+
if len(entity)==3:
|
|
145
|
+
return study_uid(dbtree, entity)
|
|
146
|
+
if len(entity)==4:
|
|
147
|
+
return series_uid(dbtree, entity)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def study_uid(dbtree, study):
|
|
151
|
+
patient_id, study = study[1], study[2]
|
|
152
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
153
|
+
if pt['PatientID'] == patient_id:
|
|
154
|
+
|
|
155
|
+
uid_studies = {}
|
|
156
|
+
study_idx = {}
|
|
157
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
158
|
+
study_desc = st['StudyDescription']
|
|
159
|
+
uid_study = st['StudyInstanceUID']
|
|
160
|
+
if study_desc in study_idx:
|
|
161
|
+
study_idx[study_desc] += 1
|
|
162
|
+
else:
|
|
163
|
+
study_idx[study_desc] = 0
|
|
164
|
+
study_desc = (study_desc, study_idx[study_desc])
|
|
165
|
+
if study == study_desc:
|
|
166
|
+
return uid_study
|
|
167
|
+
uid_studies[study_desc] = uid_study
|
|
168
|
+
|
|
169
|
+
if isinstance(study, str):
|
|
170
|
+
studies_list = [s for s in uid_studies.keys() if s[0]==study]
|
|
171
|
+
if len(studies_list) == 1:
|
|
172
|
+
return uid_studies[(study, 0)]
|
|
173
|
+
elif len(studies_list) > 1:
|
|
174
|
+
raise AmbiguousError(
|
|
175
|
+
f"Multiple studies with name {study}. "
|
|
176
|
+
f"Please specify the index along with the description. "
|
|
177
|
+
f"For instance ({study}, {len(uid_studies)-1})'. "
|
|
178
|
+
)
|
|
179
|
+
raise ValueError(f"Study {study} not found in patient {patient_id}.")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def series_uid(dbtree, series): # absolute path to series
|
|
183
|
+
uid_study = study_uid(dbtree, series[:-1])
|
|
184
|
+
study, sery = series[2], series[3]
|
|
185
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
186
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
187
|
+
if st['StudyInstanceUID'] == uid_study:
|
|
188
|
+
|
|
189
|
+
series = {}
|
|
190
|
+
series_idx = {}
|
|
191
|
+
for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']):
|
|
192
|
+
series_desc = sr['SeriesDescription']
|
|
193
|
+
uid_series = sr['SeriesInstanceUID']
|
|
194
|
+
if series_desc in series_idx:
|
|
195
|
+
series_idx[series_desc] += 1
|
|
196
|
+
else:
|
|
197
|
+
series_idx[series_desc] = 0
|
|
198
|
+
series_desc = (series_desc, series_idx[series_desc])
|
|
199
|
+
if sery == series_desc:
|
|
200
|
+
return uid_series
|
|
201
|
+
series[series_desc] = uid_series
|
|
202
|
+
|
|
203
|
+
if isinstance(sery, str):
|
|
204
|
+
series_list = [s for s in series.keys() if s[0]==sery]
|
|
205
|
+
if len(series_list) == 1:
|
|
206
|
+
return series[(sery, 0)]
|
|
207
|
+
elif len(series_list) > 1:
|
|
208
|
+
raise AmbiguousError(
|
|
209
|
+
f"Multiple series with name {sery}. "
|
|
210
|
+
f"Please specify the index along with the description. "
|
|
211
|
+
f"For instance ({sery}, {len(series)-1})'. "
|
|
212
|
+
)
|
|
213
|
+
raise ValueError(f"Series {sery} not found in study {study}.")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def patients(dbtree, database, name=None, contains=None, isin=None):
|
|
217
|
+
|
|
218
|
+
patients = []
|
|
219
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
220
|
+
patient_name = pt['PatientName']
|
|
221
|
+
append = True
|
|
222
|
+
if name is not None:
|
|
223
|
+
append = append and (patient_name==name)
|
|
224
|
+
if contains is not None:
|
|
225
|
+
append = append and (contains in patient_name)
|
|
226
|
+
if isin is not None:
|
|
227
|
+
append = append and (patient_name in isin)
|
|
228
|
+
if append:
|
|
229
|
+
patients.append(pt['PatientID'])
|
|
230
|
+
|
|
231
|
+
return [[database, p] for p in patients]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def studies(dbtree, pat, desc=None, contains=None, isin=None):
|
|
235
|
+
database, patient_id = pat[0], pat[1]
|
|
236
|
+
studies = []
|
|
237
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
238
|
+
if pt['PatientID'] == patient_id:
|
|
239
|
+
study_idx = {}
|
|
240
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
241
|
+
study_desc = st['StudyDescription']
|
|
242
|
+
if study_desc in study_idx:
|
|
243
|
+
study_idx[study_desc] += 1
|
|
244
|
+
else:
|
|
245
|
+
study_idx[study_desc] = 0
|
|
246
|
+
studies.append((study_desc, study_idx[study_desc]))
|
|
247
|
+
|
|
248
|
+
# Apply filters
|
|
249
|
+
if desc is not None:
|
|
250
|
+
studies = [s for s in studies if s[0]==desc]
|
|
251
|
+
if contains is not None:
|
|
252
|
+
studies = [s for s in studies if contains in s[0]]
|
|
253
|
+
if isin is not None:
|
|
254
|
+
studies = [s for s in studies if s[0] in isin]
|
|
255
|
+
|
|
256
|
+
# Return result
|
|
257
|
+
return [[database, patient_id, study] for study in studies]
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def series(dbtree, stdy, desc=None, contains=None, isin=None):
|
|
262
|
+
database, patient_id, study = stdy[0], stdy[1], stdy[2]
|
|
263
|
+
study_as_str = isinstance(study, str)
|
|
264
|
+
if study_as_str:
|
|
265
|
+
study = (study, 0)
|
|
266
|
+
series = []
|
|
267
|
+
for pt in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
268
|
+
if pt['PatientID'] == patient_id:
|
|
269
|
+
study_idx = {}
|
|
270
|
+
for st in sorted(pt['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
271
|
+
study_desc = st['StudyDescription']
|
|
272
|
+
if study_desc in study_idx:
|
|
273
|
+
study_idx[study_desc] += 1
|
|
274
|
+
else:
|
|
275
|
+
study_idx[study_desc] = 0
|
|
276
|
+
if study[0] == study_desc:
|
|
277
|
+
if study_as_str:
|
|
278
|
+
if study_idx[study_desc] > 0:
|
|
279
|
+
raise AmbiguousError(
|
|
280
|
+
f"Multiple studies named {study_desc} in patient {patient_id}. Please provide an index along with the study description."
|
|
281
|
+
)
|
|
282
|
+
if study == (study_desc, study_idx[study_desc]):
|
|
283
|
+
series_idx = {}
|
|
284
|
+
for sr in sorted(st['series'], key=lambda sr: sr['SeriesNumber']):
|
|
285
|
+
series_desc = sr['SeriesDescription']
|
|
286
|
+
if series_desc in series_idx:
|
|
287
|
+
series_idx[series_desc] += 1
|
|
288
|
+
else:
|
|
289
|
+
series_idx[series_desc] = 0
|
|
290
|
+
series.append((series_desc, series_idx[series_desc]))
|
|
291
|
+
if not study_as_str:
|
|
292
|
+
break
|
|
293
|
+
|
|
294
|
+
# Apply filters (if any)
|
|
295
|
+
if desc is not None:
|
|
296
|
+
series = [s for s in series if s[0]==desc]
|
|
297
|
+
if contains is not None:
|
|
298
|
+
series = [s for s in series if contains in s[0]]
|
|
299
|
+
if isin is not None:
|
|
300
|
+
series = [s for s in series if s[0] in isin]
|
|
301
|
+
|
|
302
|
+
# Return result
|
|
303
|
+
return [[database, patient_id, study, s] for s in series]
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# def append(dbtree, parent, child_name):
|
|
308
|
+
# if len(parent) == 1:
|
|
309
|
+
# return _new_patient(dbtree, parent, child_name)
|
|
310
|
+
# elif len(parent) == 2:
|
|
311
|
+
# return _new_study(dbtree, parent, child_name)
|
|
312
|
+
# elif len(parent) == 3:
|
|
313
|
+
# return _new_series(dbtree, parent, child_name)
|
|
314
|
+
|
|
315
|
+
# def _new_patient(dbtree, database, patient_id):
|
|
316
|
+
# if patient_id in patients(dbtree, database):
|
|
317
|
+
# raise ValueError(
|
|
318
|
+
# f"Cannot create a new patient with id {patient_id}."
|
|
319
|
+
# f"The ID is already taken."
|
|
320
|
+
# )
|
|
321
|
+
# return [database, patient_id]
|
|
322
|
+
|
|
323
|
+
# def new_study(dbtree, patient, study): #len(patient)=2
|
|
324
|
+
# desc = study if isinstance(study, str) else study[0]
|
|
325
|
+
# studies_in_patient = studies(dbtree, patient, desc=desc)
|
|
326
|
+
# cnt = len(studies_in_patient)
|
|
327
|
+
# return patient + [(desc, cnt)]
|
|
328
|
+
|
|
329
|
+
# def new_series(dbtree, study, sery): #len(study)=3
|
|
330
|
+
# desc = sery if isinstance(sery, str) else sery[0]
|
|
331
|
+
# series_in_study = series(dbtree, study, desc=desc)
|
|
332
|
+
# cnt = len(series_in_study)
|
|
333
|
+
# return study + [(desc, cnt)]
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def print_tree(dbtree):
|
|
338
|
+
tree = summary(dbtree)
|
|
339
|
+
for patient, studies in tree.items():
|
|
340
|
+
print(f"Patient: ({patient[0]}, {patient[1]})")
|
|
341
|
+
for study, series in studies.items():
|
|
342
|
+
print(f" Study: ({study[0]}, {study[1]})")
|
|
343
|
+
for s in series:
|
|
344
|
+
print(f" Series: ({s[0]}, {s[1]})")
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def summary(dbtree):
|
|
348
|
+
# A human-readable summary tree
|
|
349
|
+
|
|
350
|
+
summary = {}
|
|
351
|
+
|
|
352
|
+
for patient in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
353
|
+
pat_id, pat_name = patient['PatientID'], patient['PatientName']
|
|
354
|
+
summary[pat_id, pat_name] = {}
|
|
355
|
+
|
|
356
|
+
study_idx = {}
|
|
357
|
+
for study in sorted(patient['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
358
|
+
study_desc = study['StudyDescription']
|
|
359
|
+
if study_desc in study_idx:
|
|
360
|
+
study_idx[study_desc] += 1
|
|
361
|
+
else:
|
|
362
|
+
study_idx[study_desc] = 0
|
|
363
|
+
summary[pat_id, pat_name][study_desc, study_idx[study_desc]] = []
|
|
364
|
+
|
|
365
|
+
series_idx = {}
|
|
366
|
+
for series in sorted(study['series'], key=lambda sr: sr['SeriesNumber']):
|
|
367
|
+
series_desc = series['SeriesDescription']
|
|
368
|
+
if series_desc in series_idx:
|
|
369
|
+
series_idx[series_desc] += 1
|
|
370
|
+
else:
|
|
371
|
+
series_idx[series_desc] = 0
|
|
372
|
+
summary[pat_id, pat_name][study_desc, study_idx[study_desc]].append((series_desc, series_idx[series_desc]))
|
|
373
|
+
|
|
374
|
+
return summary
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def to_csv(dbtree, csv_file):
|
|
378
|
+
|
|
379
|
+
columns = ['PatientID', 'PatientName', 'StudyDescription', 'study index', 'SeriesDescription', 'SeriesNumber', 'nr of files']
|
|
380
|
+
|
|
381
|
+
with open(csv_file, 'w', newline='') as file:
|
|
382
|
+
writer = csv.writer(file)
|
|
383
|
+
writer.writerow(columns)
|
|
384
|
+
|
|
385
|
+
for patient in sorted(dbtree, key=lambda pt: pt['PatientID']):
|
|
386
|
+
pat_id, pat_name = patient['PatientID'], patient['PatientName']
|
|
387
|
+
|
|
388
|
+
study_idx = {}
|
|
389
|
+
for study in sorted(patient['studies'], key=lambda st: st['StudyInstanceUID']):
|
|
390
|
+
study_desc = study['StudyDescription']
|
|
391
|
+
if study_desc in study_idx:
|
|
392
|
+
study_idx[study_desc] += 1
|
|
393
|
+
else:
|
|
394
|
+
study_idx[study_desc] = 0
|
|
395
|
+
|
|
396
|
+
for series in sorted(study['series'], key=lambda sr: sr['SeriesNumber']):
|
|
397
|
+
n_instances = len(series['instances'])
|
|
398
|
+
row = [pat_id, pat_name, study_desc, study_idx[study_desc], series['SeriesDescription'], series['SeriesNumber'], n_instances]
|
|
399
|
+
writer.writerow(row)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
@@ -2,25 +2,11 @@
|
|
|
2
2
|
# Produced by pydicom codify utility script
|
|
3
3
|
import numpy as np
|
|
4
4
|
import pydicom
|
|
5
|
-
from pydicom.dataset import
|
|
5
|
+
from pydicom.dataset import FileMetaDataset
|
|
6
6
|
from pydicom.sequence import Sequence
|
|
7
7
|
|
|
8
|
-
from dbdicom.ds.dataset import DbDataset
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
def __init__(self, dataset=None, template=None):
|
|
12
|
-
super().__init__()
|
|
13
|
-
|
|
14
|
-
if (dataset is None) and (template is None):
|
|
15
|
-
template = 'VPH'
|
|
16
|
-
|
|
17
|
-
if dataset is not None:
|
|
18
|
-
self.__dict__ = dataset.__dict__
|
|
19
|
-
|
|
20
|
-
if template == 'VPH':
|
|
21
|
-
vph(self)
|
|
22
|
-
|
|
23
|
-
def vph(ds):
|
|
9
|
+
def default(ds): #VPH
|
|
24
10
|
|
|
25
11
|
# File meta info data elements
|
|
26
12
|
ds.file_meta = FileMetaDataset()
|