[6673ef]: / rvseg / patient.py

Download this file

131 lines (109 with data), 4.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/usr/bin/env python
from __future__ import division, print_function
import os, glob, re
import dicom
import numpy as np
from PIL import Image, ImageDraw
def maybe_rotate(image):
# orient image in landscape
height, width = image.shape
return np.rot90(image) if width < height else image
class PatientData(object):
"""Data directory structure (for patient 01):
directory/
P01dicom.txt
P01dicom/
P01-0000.dcm
P01-0001.dcm
...
P01contours-manual/
P01-0080-icontour-manual.txt
P01-0120-ocontour-manual.txt
...
"""
def __init__(self, directory):
self.directory = os.path.normpath(directory)
# get patient index from contour listing file
glob_search = os.path.join(directory, "P*list.txt")
files = glob.glob(glob_search)
if len(files) == 0:
raise Exception("Couldn't find contour listing file in {}. "
"Wrong directory?".format(directory))
self.contour_list_file = files[0]
match = re.search("P(..)list.txt", self.contour_list_file)
self.index = int(match.group(1))
# load all data into memory
self.load_images()
# some patients do not have contour data, and that's ok
try:
self.load_masks()
except FileNotFoundError:
pass
@property
def images(self):
return [self.all_images[i] for i in self.labeled]
@property
def dicoms(self):
return [self.all_dicoms[i] for i in self.labeled]
@property
def dicom_path(self):
return os.path.join(self.directory, "P{:02d}dicom".format(self.index))
def load_images(self):
glob_search = os.path.join(self.dicom_path, "*.dcm")
dicom_files = sorted(glob.glob(glob_search))
self.all_images = []
self.all_dicoms = []
for dicom_file in dicom_files:
plan = dicom.read_file(dicom_file)
image = maybe_rotate(plan.pixel_array)
self.all_images.append(image)
self.all_dicoms.append(plan)
self.image_height, self.image_width = image.shape
self.rotated = (plan.pixel_array.shape != image.shape)
def load_contour(self, filename):
# strip out path head "patientXX/"
match = re.search("patient../(.*)", filename)
path = os.path.join(self.directory, match.group(1))
x, y = np.loadtxt(path).T
if self.rotated:
x, y = y, self.image_height - x
return x, y
def contour_to_mask(self, x, y, norm=255):
BW_8BIT = 'L'
polygon = list(zip(x, y))
image_dims = (self.image_width, self.image_height)
img = Image.new(BW_8BIT, image_dims, color=0)
ImageDraw.Draw(img).polygon(polygon, outline=1, fill=1)
return norm * np.array(img, dtype='uint8')
def load_masks(self):
with open(self.contour_list_file, 'r') as f:
files = [line.strip() for line in f.readlines()]
inner_files = [path.replace("\\", "/") for path in files[0::2]]
outer_files = [path.replace("\\", "/") for path in files[1::2]]
# get list of frames which have contours
self.labeled = []
for inner_file in inner_files:
match = re.search("P..-(....)-.contour", inner_file)
frame_number = int(match.group(1))
self.labeled.append(frame_number)
self.endocardium_contours = []
self.epicardium_contours = []
self.endocardium_masks = []
self.epicardium_masks = []
for inner_file, outer_file in zip(inner_files, outer_files):
inner_x, inner_y = self.load_contour(inner_file)
self.endocardium_contours.append((inner_x, inner_y))
outer_x, outer_y = self.load_contour(outer_file)
self.epicardium_contours.append((outer_x, outer_y))
inner_mask = self.contour_to_mask(inner_x, inner_y, norm=1)
self.endocardium_masks.append(inner_mask)
outer_mask = self.contour_to_mask(outer_x, outer_y, norm=1)
self.epicardium_masks.append(outer_mask)
def write_video(self, outfile, FPS=24):
import cv2
image_dims = (self.image_width, self.image_height)
video = cv2.VideoWriter(outfile, -1, FPS, image_dims)
for image in self.all_images:
grayscale = np.asarray(image * (255 / image.max()), dtype='uint8')
video.write(cv2.cvtColor(grayscale, cv2.COLOR_GRAY2BGR))
video.release()