dose_ocr.py 3.45 KB
Newer Older
1
# !/usr/bin/env python
2
# -*- coding: utf-8 -*-
3 4
# Extraction of dose information from dose screens created by `Ge`, `Siemens` and `Toshiba`
# CT Scanners using OCR.
5

6 7 8 9
from config import *
from pydicom.dicomio import read_file
from pydicom.misc import is_dicom
from pydicom.tag import Tag
10 11
from os.path import join
from os import listdir, remove
12
import numpy as np
13
import pytesseract as pt
14 15
from PIL.Image import open as open_image
from scipy.misc import imfilter, imresize, toimage
16 17


18 19
# Dictionary containing the limits of the pertinent text areas on the dose screen for all
# manufacturers in a tuple (top, bottom, left, right).
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
column_width = {
    'GE': {
        'Series': (160, 511, 0, 54),
        'Type': (160, 511, 55, 128),
        'Scan Range': (160, 511, 129, 270),
        'CTDIvol': (160, 511, 271, 339),
        'DLP': (160, 511, 340, 424),
        'Phantom': (160, 511, 425, 511)},
    'Siemens': {
        'Total mAs': (),
        'Total DLP': (),
        'Name': (),
        'Scan': (),
        'kV' : (),
        'mAs/ref.': (),
        'CTDIvol': (),
        'DLP': (),
        'TI': (),
        'cSL': ()},
    'Toshiba': {
        'Total mAs': (),
        'CTDIvol': (),
42
        'DLP': ()}
43 44
}

45
# Synonyms dictionary used to identify the correct manufacturer (the one used in `column_width`)
46
manufacturers_dict = {
47
    'GE MEDICAL SYSTEMS': 'GE', 'GE': 'GE',
48
    'SIEMENS': 'Siemens',
49 50 51
    'Toshiba': 'Toshiba'}


52
def get_array_from_overlay(dcm):
53 54
    """ Return a 2D numpy array of the overlay of index 1 for the given DICOM file from
        binary data. Useful to extract dose informaiton from `Siemens MedCom Object Graphics`.
55
    """
56 57 58 59 60 61 62
    size = (int(dcm[Tag(0x60000010)].value), int(dcm[Tag(0x60000011)].value))
    overlay_raw = dcm[Tag(0x60003000)].value
    area = size[0] * size[1]
    length = len(overlay_raw)
    n_bits = int(area / length)
    decoded_linear = np.zeros(area)
    for i in range(1, length):
63 64 65
        bits = tuple(int(j) for j in format(overlay_raw[i], '08b'))[::-1]
        for k in range (0, n_bits):
            decoded_linear[i * n_bits + k] = bits[k]
66
    return np.reshape(decoded_linear, size)
67 68


69 70 71 72
def read_column(array, limits, name):
    """ Use OCR to read the content of an image and return a list of strings. `limits` is
        a tuple containing the limits of the column in the format (top, bottom, left, right).
    """
73
    tmp_png_path = join(dcm_dir, 'PNG_Files', 'column_' + name + '.png')
74
    column = np.invert(array[limits[0]:limits[1], limits[2]:limits[3]])
75 76
    toimage(
        imfilter(imresize(column, tuple(i*2 for i in column.shape)), 'sharpen'),
77 78
        high=255,
        low=0).save(tmp_png_path)
79
    string = pt.image_to_string(open_image(tmp_png_path))
80 81 82 83
    remove(tmp_png_path)
    return string.split('\n')


84 85
def read_dicom(dcm_path):
    """ Return a dictionary with information extracted from the DICOM image located at a given path.
86
    """
87 88
    dcm = read_file(dcm_path)
    manufacturer = manufacturers_dict[dcm.Manufacturer]
89
    if manufacturer == 'GE':
90
        pixel_array = dcm.pixel_array
91 92
    elif manufacturer == 'Siemens':
        pixel_array = get_array_from_overlay(dcm)
93 94 95 96 97 98 99
    limits = column_width[manufacturer]
    info = {i: read_column(pixel_array, limits[i], i) for i in limits}
    series = [{
        'Series': int(i + 2),
        'Type': info['Type'][i + 1],
        'CTDIvol': float(info['CTDIvol'][i]),
        'DLP': float(info['DLP'][i])} for i in range(len(info['DLP']) - 1)]
100
    return {'Total_DLP': float(info['DLP'][-1]), 'Series': series}