The Scan class¶
The pylidc.Scan
class holds some (but not all!) of the DICOM
attributes associated with the CT scans in the LIDC dataset. These attributes
can be used to query the data:
import pylidc as pl
# Query for all CT scans with desired traits.
scans = pl.query(pl.Scan).filter(pl.Scan.slice_thickness <= 1,
pl.Scan.pixel_spacing <= 0.6)
print(scans.count())
# => 31
pid = 'LIDC-IDRI-0078'
scan = pl.query(pl.Scan).filter(pl.Scan.patient_id == pid).first()
Note
Not all of the class attributes are queryable! Some attributes are actually computed properties. You can determine this by, for example, examining the object type for the class:
print(type(pl.Scan.slice_thickness))
# => <class 'sqlalchemy.orm.attributes.InstrumentedAttribute'>
# => ^^^ queryable because it's an sqlalchemy attribute.
# Whereas ...
print(type(pl.Scan.slice_spacing))
# => <type 'property'>
# ^^^ not queryable because it's a computed property
The slice_thickness attribute corresponds to the DICOM attribute
(0018,0050), whereas the slice_spacing attribute is computed from
the median of spacing between the slices
(see pylidc.Scan.slice_zvals()
).
A pylidc.Scan
object has zero or more pylidc.Annotation
objects, which are radiologist annotations of lung nodules found in the scan:
print(len(scan.annotations))
# => 13
Annotation clustering¶
The scan has 13 annotations, but which refer to the same nodule? This can
be determined using the pylidc.Scan.cluster_annotations()
method, which
uses a distance function to create an adjancency graph to determine which
annotations refer to the same nodule in a scan:
nods = scan.cluster_annotations()
print("%s has %d nodules." % (scan, len(nods)))
# => Scan(id=1,patient_id=LIDC-IDRI-0078) has 4 nodules.
for i,nod in enumerate(nods):
print("Nodule %d has %d annotations." % (i+1, len(nods[i])))
# => Nodule 1 has 4 annotations.
# => Nodule 2 has 4 annotations.
# => Nodule 3 has 1 annotations.
# => Nodule 4 has 4 annotations.
Converting scan image values to NumPy array¶
The scan can be converted to a NumPy array for image-processing:
vol = scan.to_volume()
print(vol.shape)
# => (512, 512, 87)
print("%.2f, %.2f" % (vol.mean(), vol.std()))
# => -702.15, 812.52
Scan visualization¶
An interactive GUI can be engaged, with clustered annotations optionally indicated with arrows:
scan.visualize(annotation_groups=nods)
which appears like