In [1]:
import sys, os
import swat as sw
from mayavi import mlab
import numpy as np
import matplotlib
from IPython.display import display
from matplotlib import image as mpimg
import matplotlib.pylab as plt
import pandas as pd
import struct

from importlib import reload
from tabulate import tabulate
pd.set_option('display.max_colwidth', -1)
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
%matplotlib inline
from itertools import cycle, islice
import matplotlib.font_manager as fm

from dlpy.network import *
from dlpy.utils import *
from dlpy.applications import *
from dlpy.model import *
from dlpy.images import *
from dlpy.layers import *


********************************************************************************
WARNING: Imported VTK version (8.1) does not match the one used
         to build the TVTK classes (7.0). This may cause problems.
         Please rebuild TVTK.
********************************************************************************


In [2]:
cashost='localhost'
casport=12125
s = swat.CAS(cashost, casport)
s.loadactionset('image')
s.loadactionset('biomedimage')
s.loadactionset('fedsql')
s.loadactionset('deepLearn')


NOTE: Added action set 'image'.
NOTE: Added action set 'biomedimage'.
NOTE: Added action set 'fedsql'.
NOTE: Added action set 'deepLearn'.
Out[2]:
§ actionset
deepLearn

elapsed 0.00338s · user 0.000322s · sys 0.00245s · mem 0.252MB


In [3]:
demo_path = '<path to the root dir of images and annotations>'
caslib_name = 'demo'
demo_subdir = ''
s.addcaslib(name=caslib_name, path=demo_path, subdirectories=True, datasource='path')

In [5]:
slides_dir = '<relative path to slides images>'  # Slides
caslib_name = 'casuser'

def load_slides(cas_session, slides_table):
    """Load the slides."""
    casout = s.CASTable(slides_table, replace=True)
    s.image.loadimages(path=slides_dir, caslib=caslib_name, casout=casout, decode=True)
    fetched_slides = casout[['_resolution_', '_image_', '_path_']].to_frame()
    slide_binaries = fetched_slides['_image_']
    slide_resolutions = fetched_slides['_resolution_']
    slide_paths = fetched_slides['_path_']
    res = np.frombuffer (slide_resolutions[0], np.int64)
    slide_width = res[0]
    slide_height = res[1]
    return slide_binaries, slide_paths, slide_width, slide_height

slide_binaries, slide_paths, slide_width, slide_height = load_slides(s, 'slides')

bar_colors_sc = ['#007477','#a2d3d9']

def plot_volumes_with_ground_truth_sc(volumes, volumes_gt):

    stats = volumes.to_frame()
    stats._minimum_ = (stats._minimum_.astype(int))

    stats_gt = volumes_gt.to_frame()
    stats_gt._minimum_ = (stats_gt._minimum_.astype(int))+1

    joined = pd.concat([stats, stats_gt])
    joined['_content_'] = joined['_content_']/1000000
    qua = joined.pivot(index='_id_', columns='_minimum_', values='_content_')
    plot_bar_chart_with_ground_truth_sc(qua, r'Spinal Cord volume ($\mathregular{\times 10^6\ mm^3}$)')

def plot_bar_chart_with_ground_truth_sc(qua, ylabel):
    my_colors = list(islice(cycle(bar_colors_sc), None, len(qua)))
    my_edge_colors = bar_edge_colors
    ax = qua.plot(kind='bar', width=0.75, align='center', color=my_colors, edgecolor=bar_colors_sc)
    for i in range(len(qua.columns)):
        for j in range(len(qua)):
            ax.containers[i][j].set_edgecolor(bar_colors_sc[i])
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.spines['bottom'].set_color('#2b3138')
    ax.grid(axis='y', color='#2b3138', linestyle=':', alpha=0.3, linewidth=2)
    legend = ax.legend(bbox_to_anchor=(1, 1), prop=ax_ticks_fp)
    legend.get_texts()[0].set_text('Model Predicted')
    legend.get_texts()[1].set_text('Ground Truth')

    fig = matplotlib.pyplot.gcf()
    fig.set_size_inches(fig_width, fig_height)
    plt.xlabel('Patient ID', fontproperties=ax_labels_fp)
    plt.text(0, 1.05, ylabel, fontproperties=ax_labels_fp, transform=ax.transAxes, ha='center', va='center')
    plt.xticks(fontproperties=ax_ticks_fp, rotation='horizontal')
    plt.yticks(fontproperties=ax_ticks_fp)
    plt.tight_layout()
    return ax

bar_edge_colors = ['#007477', '#C96400', '#758c20']
fig_width = 20
fig_height = 10

ax_labels_fp = fm.FontProperties(size=24)
ax_ticks_fp = fm.FontProperties(size=20)

def display_slice_3d(images, dims, ress, fmts, poss, oris, scas, perm, image_index, slice_index, rf, imin=-100, imax=400, additive=0):
    """Display an image slice in 3D."""
    image = get_image_array(images, dims, ress, fmts, image_index)
    geo_perm = np.zeros(3, dtype=np.int)
    for i in range(3):
        geo_perm[mapping(i)] = mapping(perm[i])
    image = np.transpose(image, perm)
    image = image[slice_index, :, :] + additive
    nr, nc = image.shape[:2]
    dimension = int(dims[image_index])
    pos = np.array(struct.unpack('=%sd' % dimension, poss[image_index]))
    sca = np.array(struct.unpack('=%sd' % dimension, scas[image_index][0:8 * dimension]))
    ori = np.array(struct.unpack('=%sd' % (dimension*dimension), oris[image_index][0:8 * dimension * dimension]))
    xx, yy = np.meshgrid(np.linspace(0, nc, nc), np.linspace(0, nr, nr))
    zz = np.zeros((nr, nc))
    lc = np.vstack((np.reshape(xx, (1, nc*nr)), np.reshape(yy, (1, nc*nr)), np.reshape(zz, (1, nc*nr))))
    ori = np.reshape(ori, (3, 3))
    ori = ori[:, geo_perm]
    sca = sca[geo_perm]
    pos = pos + slice_index * sca[2] * ori[:, 2]
    pos = np.reshape(pos, (3, 1))
    sca = np.diag(sca)
    gc = np.matmul(ori, np.matmul(sca, lc))
    gc = gc + np.matmul(pos, np.ones((1, nc*nr)))
    mlab.mesh(np.reshape(gc[0, :], (nr, nc)), np.reshape(gc[1, :], (nr, nc)), np.reshape(gc[2, :], (nr, nc)),
              scalars = image, colormap='gray', vmin=imin, vmax=imax)
    if (rf):
        for i in range(3):
            clr=((i == 0) * 1, (i == 1) * 1, (i == 2) * 1)
            mlab.quiver3d(pos[0], pos[1], pos[2], ori[0, i], ori[1, i], ori[2, i],
                          line_width=5, scale_factor=50*sca[i, i], color=clr, mode='arrow')
            
def get_image_array_from_row(image_binary, dimension, resolution, myformat, channel_count=1):
    """Get a 3D image from CAS table row."""
    num_cells = np.prod(resolution)
    if (myformat == '32S'):
        image_array = np.array(struct.unpack('=%si' % num_cells, image_binary[0:4 * num_cells]))
        image_array = np.reshape(image_array, resolution)
    elif myformat == '32F':
        image_array = np.array(struct.unpack('=%sf' % num_cells, image_binary[0:4 * num_cells]))
        image_array = np.reshape(image_array, resolution)
    elif myformat == '64F':
        image_array = np.array(struct.unpack('=%sd' % num_cells, image_binary[0:8 * num_cells]))
        image_array = np.reshape(image_array, resolution)
    elif myformat == '64U':
        image_array = np.array(struct.unpack('=%sQ' % num_cells, image_binary[0:8 * num_cells]))
        image_array = np.reshape(image_array, resolution)
    elif myformat == '16S':
        image_array = np.array(struct.unpack('=%sh' % num_cells, image_binary[0:2 * num_cells]))
        image_array = np.reshape(image_array, resolution)
    elif myformat == '16U':
        image_array = np.array(struct.unpack('=%sH' % num_cells, image_binary[0:2 * num_cells]))
        image_array = np.reshape(image_array, resolution)
    elif myformat == '8U' and channel_count==3:
        image_array = np.array(bytearray(image_binary[0:(num_cells*3)]))
        image_array = np.reshape(image_array, (resolution[0], resolution[1], 3))[:, :, 0:3]
        image_array = reverse(image_array, 2)
    elif myformat == '8S':
        image_array = np.array(struct.unpack('=%sb' % num_cells, image_binary[0:num_cells]))
        image_array = np.reshape(image_array, resolution)
    elif myformat == '8U':
        image_array = np.array(struct.unpack('=%sB' % num_cells, image_binary[0:num_cells]))
        image_array = np.reshape(image_array, resolution)
    else:
        image_array = np.array(bytearray(image_binary))
        image_array = np.reshape(image_array, (resolution[0], resolution[1], 3))
        image_array = reverse(image_array, 2)
    return image_array

def get_image_array(image_binaries, dimensions, resolutions, formats, n, channel_count=1):
    """Get a 3D image from CAS table."""
    dimension = int(dimensions[n])
    resolution = np.array(struct.unpack('=%sq' % dimension, resolutions[n][0:dimension * 8]))
    resolution = resolution[::-1]
    myformat = formats[n]
    num_cells = np.prod(resolution)
    return get_image_array_from_row(image_binaries[n], dimension, resolution, myformat, channel_count)

def mapping(val):
    """A simple mapping from int to int."""
    if (val == 0):
        return 2
    elif (val == 2):
        return 0
    else:
        return val

def remove_duplicates(array): 
    final_list = [] 
    for num in array: 
        if num not in final_list: 
            final_list.append(num) 
    return final_list


NOTE: Loaded 11 images from /u/coambr/SpinalCordSeg/SpinalCordSegmentationPipeline into Cloud Analytic Services table slides.

In [6]:
s.image.fetchImages(imageTable = 'slides').Images.Image[0]


NOTE: Table SLIDES contains decompressed images.
Out[6]:

In [7]:
s.image.fetchImages(imageTable = 'slides').Images.Image[1]


NOTE: Table SLIDES contains decompressed images.
Out[7]:

In [8]:
s.image.fetchImages(imageTable = 'slides').Images.Image[2]


NOTE: Table SLIDES contains decompressed images.
Out[8]:

In [9]:
s.image.fetchImages(imageTable = 'slides').Images.Image[3]


NOTE: Table SLIDES contains decompressed images.
Out[9]:

Citation:

Yang, Jinzhong; Sharp, Greg; Veeraraghavan, Harini ; van Elmpt, Wouter ; Dekker, Andre; Lustberg, Tim; Gooding, Mark. (2017). Data from Lung CT Segmentation Challenge. The Cancer Imaging Archive. http://doi.org/10.7937/K9/TCIA.2017.3r3fvz08

Yang, J. , Veeraraghavan, H. , Armato, S. G., Farahani, K. , Kirby, J. S., Kalpathy‐Kramer, J. , van Elmpt, W. , Dekker, A. , Han, X. , Feng, X. , Aljabar, P. , Oliveira, B. , van der Heyden, B. , Zamdborg, L. , Lam, D. , Gooding, M. and Sharp, G. C. (2018), Autosegmentation for thoracic radiation treatment planning: A grand challenge at AAPM 2017. Med. Phys.. . doi:10.1002/mp.13141

Clark K, Vendt B, Smith K, Freymann J, Kirby J, Koppel P, Moore S, Phillips S, Maffitt D, Pringle M, Tarbox L, Prior F. The Cancer Imaging Archive (TCIA): Maintaining and Operating a Public Information Repository, Journal of Digital Imaging, Volume 26, Number 6, December, 2013, pp 1045-1057. (paper)


In [10]:
s.image.fetchImages(imageTable = 'slides').Images.Image[4]


NOTE: Table SLIDES contains decompressed images.
Out[10]:

Load patient images


In [11]:
imsuid = 'SeriesInstanceUID'
impn = 'PatientName'
imad = 'AcquisitionDate'
impa = 'PatientAge'
impx = 'PatientSex'
allKeysIm = [imsuid, impn, imad, impa, impx]

image = s.CASTable('image', replace=True)
s.image.loadimages(
    path=demo_subdir,
    caslib='bigdisk',
    casout=image,
    addColumns=dict(
        general=['position', 'orientation', 'spacing', 'channeltype', 'width', 'height', 'depth'],
        dicomattributes=dict(keywords=allKeysIm)),
    series=dict(dicom=True),
    recurse=True,
    labellevels=1,
    decode=True
)


NOTE: Loaded 60 images from /bigdisk/lax/coambr/ahhati/LCTSC into Cloud Analytic Services table image.
Out[11]:
§ OutputCasTables
casLib Name Label Rows Columns casTable
0 CASUSER(coambr) image 60 21 CASTable('image', caslib='CASUSER(coambr)')

elapsed 53.6s · user 22.2s · sys 18.9s · mem 2.64e+03MB

Load DICOM-RT files


In [12]:
patient='PatientID'
image_check='Modality'
rtcsq = 'ROIContourSequence'
rtsuid = 'ReferencedFrameOfReferenceSequence{1}RTReferencedStudySequence{1}RTReferencedSeriesSequence{1}SeriesInstanceUID' 
rtsdesc = 'SeriesDescription'
allKeysRt = [rtsuid, rtcsq, rtsdesc,patient,image_check]
rtdata_points=s.CASTable("rtdata_points",replace=True)
all_colors=s.CASTable("all_colors",replace=True)
fir=True
for j in range(1,6):
    roi1color=('3006_0039_'+str(j)+'_3006_002a')
    organ=('3006_0020_'+str(j)+'_3006_0026')
    allTags = [roi1color,organ]
    s.biomedimage.loaddicomdata(
        path=demo_subdir,
        caslib="bigdisk",
        casOut=rtdata_points,
        addColumns=dict(tags=allTags,keywords=allKeysRt))

    s.table.altertable(table='rtdata_points',
                       columns=[dict(name="_"+roi1color+"_", rename='color'),
                                dict(name="_"+organ+"_", rename='organ')])
    if fir:
        fir=False
        s.fedsql.execdirect("create table all_colors {options replace=true}"
                            "as (select * from rtdata_points)")
    else:
        s.fedsql.execdirect("create table all_colors {options replace=true} "
                            "as (select * from rtdata_points union select * from all_colors)")


NOTE: Loaded 9593 DICOM data row(s) from path ahhati/LCTSC into table rtdata_points.
NOTE: Table ALL_COLORS was created in caslib CASUSER(coambr) with 9593 rows returned.
NOTE: Loaded 9593 DICOM data row(s) from path ahhati/LCTSC into table rtdata_points.
NOTE: Table ALL_COLORS was created in caslib CASUSER(coambr) with 9653 rows returned.
NOTE: Loaded 9593 DICOM data row(s) from path ahhati/LCTSC into table rtdata_points.
NOTE: Table ALL_COLORS was created in caslib CASUSER(coambr) with 9713 rows returned.
NOTE: Loaded 9593 DICOM data row(s) from path ahhati/LCTSC into table rtdata_points.
NOTE: Table ALL_COLORS was created in caslib CASUSER(coambr) with 9773 rows returned.
NOTE: Loaded 9593 DICOM data row(s) from path ahhati/LCTSC into table rtdata_points.
NOTE: Table ALL_COLORS was created in caslib CASUSER(coambr) with 9833 rows returned.

Determine colors of the spinal cord for each image


In [13]:
s.fedsql.execdirect("create table imrt {options replace=true} "
                    "as (select a._id_, a._ROIContourSequence_, a._PatientId_, a.color, a.organ, b._image_, b._dimension_, b._resolution_, b._imageformat_, b._position_, b._orientation_, b._spacing_, "
                    "b._PatientName_, a._SeriesDescription_, b._id_ as rtid, b._label_ "
                    "from all_colors as a _hash_ inner join image as b "
                    "on b._SeriesInstanceUID_=a._ReferencedFrameOfReferenceSequence_1_RTReferencedStudySequence_1_RTReferencedSeriesSequence_1_SeriesInstanceUID_ "
                    "WHERE a.organ='SpinalCord')")

imrt=s.CASTable("imrt",replace=True)
cols=imrt[["color"]].values

spinal_cord_colors=remove_duplicates(cols)

for i in spinal_cord_colors:
    roidisplaycolor=i[0]
    print(roidisplaycolor)


NOTE: Table IMRT was created in caslib CASUSER(coambr) with 60 rows returned.
255\255\0
242\202\48
255\0\0
0\0\255
34\139\34
200\180\255
0\240\0
255\99\71
165\80\65
255\0\255

Create mask images


In [14]:
first = True
for colors in spinal_cord_colors:
    imrt=s.CASTable("imrt",replace=True)
    imrt2=s.CASTable("imrt2",replace=True)
    string="color like '"+ colors[0] + "'"
    tmpTable=imrt.query(string)
    s.partition(table=tmpTable, casout=imrt2)
    s.biomedimage.processbiomedimages(images=dict(table=imrt2),
                                      steps=[dict(stepparameters=dict(steptype='roi2mask',
                                                                      roi2maskparameters=dict(roi2masktype='dicomrt_specific',
                                                                                              roicontoursequence='_ROIContourSequence_',
                                                                                              correctionsensitivity=.25,
                                                                                              pixelintensity=255,
                                                                                              outputbackground=0,
                                                                                              roidisplaycolor=colors[0]))),
                                             dict(stepParameters=dict(stepType='rescale', rescaleparameters=dict(rescaleType="channeltype_8u")))],
                                      casout=dict(name='masks_all', replace=True),
                                      copyvars=['_id_', 'color', 'RTID', '_label_'])
    if (first):
        first = False
        s.fedsql.execdirect("create table pdata {options replace=true}"
                            "as (select * from masks_all)")
    else:
        s.fedsql.execdirect("create table pdata {options replace=true}"
                            "as (select * from masks_all union select * from pdata)")
        
pdata=s.CASTable("pdata",replace=True)


NOTE: Processed 3 images from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 3 rows returned.
NOTE: Processed 19 images from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 22 rows returned.
NOTE: Processed 19 images from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 41 rows returned.
NOTE: Processed 11 images from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 52 rows returned.
NOTE: Processed 1 image from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 53 rows returned.
NOTE: Processed 1 image from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 54 rows returned.
NOTE: Processed 3 images from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 57 rows returned.
NOTE: Processed 1 image from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 58 rows returned.
NOTE: Processed 1 image from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 59 rows returned.
NOTE: Processed 1 image from Cloud Analytic Services table IMRT2.
NOTE: Table PDATA was created in caslib CASUSER(coambr) with 60 rows returned.

Export to 2D and merge


In [15]:
s.biomedimage.processbiomedimages(images=dict(table=pdata),
                                  steps=[dict(stepparameters=dict(steptype='clamp', clampParameters=dict(clampType='basic',high=1.0,low=0.0))),
                                         dict(stepparameters=dict(steptype='rescale', rescaleParameters=dict(rescaleType='channeltype_8u', min=0, max=1))),
                                         dict(stepparameters=dict(steptype='export'))],
                                  decode=False,
                                  copyvars=['rtid','_id_','_label_'],
                                  casout=dict(name='masks_2d', replace=True)
                                 )

images_working_2d=s.CASTable('image', replace=True)
images_2d=s.CASTable('images_2d', replace=True)
s.biomedimage.processbiomedimages(images=dict(table=images_working_2d),
                                                steps=[dict(stepparameters=dict(steptype='export'))],
                                                copyvars=['_id_','_label_'],
                                                casout=images_2d)

s.table.altertable(table='masks_2d', columns=[dict(name='_image_', rename='seg')])

s.fedsql.execdirect("create table merged_im {options replace=true} "
                    "as (select a._id_, a.seg, b._image_, a._bioMedId_, a._bioMedDimension_, a._sliceIndex_, b._label_, b._id_ as rtid "
                    "from masks_2d as a _hash_ inner join images_2d as b "
                    "on b._label_=a._label_ and a._sliceIndex_=b._sliceIndex_)")


NOTE: Processed 60 images from Cloud Analytic Services table PDATA.
NOTE: Processed 60 images from Cloud Analytic Services table IMAGE.
NOTE: Table MERGED_IM was created in caslib CASUSER(coambr) with 9533 rows returned.
Out[15]:

elapsed 1.1s · user 1.41s · sys 2.04s · mem 1.77e+03MB

Define subsets


In [16]:
image_subs={
"LCTSC-Test-S1-101":[1, 104],
"LCTSC-Test-S1-102":[1, 112],
"LCTSC-Test-S1-103":[30, 118],
"LCTSC-Test-S1-104":[1, 35],
"LCTSC-Test-S1-201":[1, 25],
"LCTSC-Test-S1-202":[1, 23],
"LCTSC-Test-S1-203":[18, 40],
"LCTSC-Test-S1-204":[4, 30],
"LCTSC-Test-S2-101":[1, 47],
"LCTSC-Test-S2-102":[12, 52],
"LCTSC-Test-S2-103":[2, 12],
"LCTSC-Test-S2-104":[1, 29],
"LCTSC-Test-S2-201":[2, 25],
"LCTSC-Test-S2-202":[6, 16],
"LCTSC-Test-S2-203":[2, 29],
"LCTSC-Test-S2-204":[10, 38],
"LCTSC-Test-S3-101":[59, 84],
"LCTSC-Test-S3-102":[46, 50],
"LCTSC-Test-S3-103":[41, 97],
"LCTSC-Test-S3-104":[43, 72],
"LCTSC-Test-S3-201":[66, 70],
"LCTSC-Test-S3-202":[1, 98],
"LCTSC-Test-S3-203":[44, 46],
"LCTSC-Test-S3-204":[1, 155],
"LCTSC-Train-S1-001":[1, 140],
"LCTSC-Train-S1-002":[12, 131],
"LCTSC-Train-S1-003":[1, 127],
"LCTSC-Train-S1-004":[1, 25],
"LCTSC-Train-S1-005":[1, 81],
"LCTSC-Train-S1-006":[1, 108],
"LCTSC-Train-S1-007":[3, 124],
"LCTSC-Train-S1-008":[1, 24],
"LCTSC-Train-S1-009":[1, 121],
"LCTSC-Train-S1-010":[1, 26],
"LCTSC-Train-S1-011":[1, 117],
"LCTSC-Train-S1-012":[1,29],
"LCTSC-Train-S2-001":[6, 39],
"LCTSC-Train-S2-002":[4, 45],
"LCTSC-Train-S2-004":[19, 31],
"LCTSC-Train-S2-005":[10, 58],
"LCTSC-Train-S2-006":[1,25 ],
"LCTSC-Train-S2-007":[7, 27],
"LCTSC-Train-S2-008":[2,40],
"LCTSC-Train-S2-009":[10, 45],
"LCTSC-Train-S2-010":[1, 27],
"LCTSC-Train-S2-011":[2, 41],
"LCTSC-Train-S2-012":[6, 39],
"LCTSC-Train-S3-001":[1, 53],
"LCTSC-Train-S3-002":[1, 41],
"LCTSC-Train-S3-003":[50, 88],
"LCTSC-Train-S3-004":[55, 201],
"LCTSC-Train-S3-005":[1, 52],
"LCTSC-Train-S3-006":[18, 35],
"LCTSC-Train-S3-007":[1, 47],
"LCTSC-Train-S3-008":[1, 119],
"LCTSC-Train-S3-009":[1, 55],
"LCTSC-Train-S3-010":[22, 48],
"LCTSC-Train-S3-011":[6, 97],
"LCTSC-Train-S3-012":[3, 18]
}

merged_im=s.CASTable('merged_im', replace=True)
image_subset = s.CASTable('image_subset', replace=True)
subsets = s.CASTable('subsets', replace=True)

first=True
for i in image_subs.keys():
    tmpTable=merged_im.query("_label_ = '"+i+"' and _sliceIndex_ >= " 
                      + str(image_subs[i][0]-1) + " and _sliceIndex_ <= " 
                       + str(image_subs[i][1]-1),inplace =False)
    s.partition(table=tmpTable, casout=subsets)
    if (first):
        first = False
        s.fedsql.execdirect("create table image_subset {options replace=true}"
                            "as (select * from subsets)")
    else:
        s.fedsql.execdirect("create table image_subset {options replace=true}"
                            "as (select * from subsets union select * from image_subset)")


NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 122 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 169 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 196 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 308 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 337 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 348 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 361 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 403 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 430 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 470 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 491 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 517 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 558 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 713 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 729 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 849 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 941 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 982 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1080 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1188 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1212 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1239 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1278 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1303 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1443 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1562 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1585 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1610 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1731 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1783 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1807 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1825 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1942 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 1976 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2080 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2109 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2137 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2160 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2195 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2220 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2231 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2286 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2375 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2380 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2461 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2490 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2617 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2651 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2708 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2713 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2716 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2863 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2912 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 2951 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 3004 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 3034 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 3060 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 3107 rows returned.
NOTE: Table IMAGE_SUBSET was created in caslib CASUSER(coambr) with 3143 rows returned.

Split into training, validation, and testing data

  • training 36 images
  • validation 7 images
  • test 17 images

In [17]:
s.fedsql.execdirect("create table merged_im {options replace=true}"
                    "as (select * from image_subset)")

merged_im = s.CASTable('merged_im', replace=True)

train = s.CASTable('train', replace=True)
test = s.CASTable('test', replace=True)
validation = s.CASTable('validation', replace=True)

tmpTable=merged_im.query("_label_ like '%Train%'")
s.partition(table=tmpTable, casout=train)

cvdata=("'LCTSC-Test-S1-101'"+","+
        "'LCTSC-Test-S1-102'"+","+
        "'LCTSC-Test-S1-202'"+","+
        "'LCTSC-Test-S2-103'"+","+
        "'LCTSC-Test-S2-204'"+","+
        "'LCTSC-Test-S3-101'"+","+
        "'LCTSC-Test-S3-202'"
       )

tmpTable=merged_im.query("_label_ like '%Test%' and _label_ not in ("+cvdata+") ")
s.partition(table=tmpTable, casout=test)

tmpTable=merged_im.query("_label_ in ("+cvdata+")")
s.partition(table=tmpTable, casout=validation)


NOTE: Table MERGED_IM was created in caslib CASUSER(coambr) with 3143 rows returned.
Out[17]:
§ caslib
CASUSER(coambr)

§ tableName
VALIDATION

§ rowsTransferred
0

§ shuffleWaitTime
0.0

§ minShuffleWaitTime
1e+300

§ maxShuffleWaitTime
0.0

§ averageShuffleWaitTime
0.0

§ casTable
CASTable('VALIDATION', caslib='CASUSER(coambr)')

elapsed 0.0402s · user 0.0309s · sys 0.0785s · mem 263MB


In [18]:
s.image.fetchImages(imageTable = 'slides').Images.Image[5]


NOTE: Table SLIDES contains decompressed images.
Out[18]:

Create UNet Model


In [19]:
model = UNet(s, 
             n_classes=2,
             width=512,
             height=512,
             n_channels=1, 
             bn_after_convolutions=False)


NOTE: Model compiled successfully.

In [20]:
model.plot_network()


Out[20]:
UNet data 512x512x1 data(input) Conv2d_1 3x3 Conv2d_1(convo) data->Conv2d_1 512 x 512 x 1 Conv2d_2 3x3 Conv2d_2(convo) Conv2d_1->Conv2d_2 512 x 512 x 64 Pooling_1 2x2 Pooling_1(pool) Conv2d_2->Pooling_1 512 x 512 x 64 Concat_4 512x512x128 Concat_4(concat) Conv2d_2->Concat_4 512 x 512 x 64 Conv2d_3 3x3 Conv2d_3(convo) Pooling_1->Conv2d_3 256 x 256 x 64 Conv2d_4 3x3 Conv2d_4(convo) Conv2d_3->Conv2d_4 256 x 256 x 128 Pooling_2 2x2 Pooling_2(pool) Conv2d_4->Pooling_2 256 x 256 x 128 Concat_3 256x256x256 Concat_3(concat) Conv2d_4->Concat_3 256 x 256 x 128 Conv2d_5 3x3 Conv2d_5(convo) Pooling_2->Conv2d_5 128 x 128 x 128 Conv2d_6 3x3 Conv2d_6(convo) Conv2d_5->Conv2d_6 128 x 128 x 256 Pooling_3 2x2 Pooling_3(pool) Conv2d_6->Pooling_3 128 x 128 x 256 Concat_2 128x128x512 Concat_2(concat) Conv2d_6->Concat_2 128 x 128 x 256 Conv2d_7 3x3 Conv2d_7(convo) Pooling_3->Conv2d_7 64 x 64 x 256 Conv2d_8 3x3 Conv2d_8(convo) Conv2d_7->Conv2d_8 64 x 64 x 512 Pooling_4 2x2 Pooling_4(pool) Conv2d_8->Pooling_4 64 x 64 x 512 Concat_1 64x64x1024 Concat_1(concat) Conv2d_8->Concat_1 64 x 64 x 512 Conv2d_9 3x3 Conv2d_9(convo) Pooling_4->Conv2d_9 32 x 32 x 512 Conv2d_10 3x3 Conv2d_10(convo) Conv2d_9->Conv2d_10 32 x 32 x 1024 Conv2DTranspose_1 3x3 Conv2DTranspose_1(transconvo) Conv2d_10->Conv2DTranspose_1 32 x 32 x 1024 Conv2DTranspose_1->Concat_1 64 x 64 x 512 Conv2d_11 3x3 Conv2d_11(convo) Concat_1->Conv2d_11 64 x 64 x 1024 Conv2d_12 3x3 Conv2d_12(convo) Conv2d_11->Conv2d_12 64 x 64 x 512 Conv2DTranspose_2 3x3 Conv2DTranspose_2(transconvo) Conv2d_12->Conv2DTranspose_2 64 x 64 x 512 Conv2DTranspose_2->Concat_2 128 x 128 x 256 Conv2d_13 3x3 Conv2d_13(convo) Concat_2->Conv2d_13 128 x 128 x 512 Conv2d_14 3x3 Conv2d_14(convo) Conv2d_13->Conv2d_14 128 x 128 x 256 Conv2DTranspose_3 3x3 Conv2DTranspose_3(transconvo) Conv2d_14->Conv2DTranspose_3 128 x 128 x 256 Conv2DTranspose_3->Concat_3 256 x 256 x 128 Conv2d_15 3x3 Conv2d_15(convo) Concat_3->Conv2d_15 256 x 256 x 256 Conv2d_16 3x3 Conv2d_16(convo) Conv2d_15->Conv2d_16 256 x 256 x 128 Conv2DTranspose_4 3x3 Conv2DTranspose_4(transconvo) Conv2d_16->Conv2DTranspose_4 256 x 256 x 128 Conv2DTranspose_4->Concat_4 512 x 512 x 64 Conv2d_17 3x3 Conv2d_17(convo) Concat_4->Conv2d_17 512 x 512 x 128 Conv2d_18 3x3 Conv2d_18(convo) Conv2d_17->Conv2d_18 512 x 512 x 64 Conv2d_19 3x3 Conv2d_19(convo) Conv2d_18->Conv2d_19 512 x 512 x 64 Segmentation_1 512x512x2 Segmentation_1(segmentation) Conv2d_19->Segmentation_1 512 x 512 x 2

Define training parameters


In [21]:
solver = AdamSolver(lr_scheduler=StepLR(learning_rate=0.0001, step_size=30, gamma=0.9), 
                    clip_grad_max = 100, 
                    clip_grad_min = -100)

optimizer = Optimizer(algorithm=solver, 
                      mini_batch_size=1, 
                      log_level=2, 
                      max_epochs=50, 
                      reg_l2=0.0001, 
                      seed=13309)

dataspecs=[dict(type='image', layer='data', data=['_image_']),
           dict(type='image', layer='Segmentation_1', data=['seg'])]


The following argument(s) learning_rate, learning_rate_policy, gamma, step_size, power are overwritten by the according arguments specified in lr_scheduler.

Fit the model


In [22]:
model.fit(data=train,
          valid_table=validation,
          optimizer=optimizer, 
          data_specs=dataspecs, 
          n_threads=32, 
          record_seed=54321,
          force_equal_padding=True,
          save_best_weights=True, 
          gpu=dict(devices=[0,1])
          )


NOTE: Training from scratch.
WARNING: Using dataSpecs settings. Additional input or target option settings will be ignored.
NOTE: Using sysgpu01.unx.sas.com: 2 out of 4 available GPU devices.
NOTE:  Synchronous mode is enabled.
NOTE:  The total number of parameters is 34513282.
NOTE:  The approximate memory cost is 52318.00 MB.
NOTE:  Loading weights cost       0.00 (s).
NOTE:  Initializing each layer cost      23.80 (s).
NOTE:  The total number of threads on each worker is 32.
NOTE:  The total mini-batch size per thread on each worker is 1.
NOTE:  The maximum mini-batch size across all workers for the synchronous mode is 32.
NOTE:  Number of input variables:     1
NOTE:  Number of numeric input variables:      1
NOTE:  Epoch Learning Rate        Loss  Fit Error  Validation Loss Validation Error   Time(s)
NOTE:  0        0.0001       3.004e+04    0.01139             1411         0.000698    75.00
NOTE:  1        0.0001            1406   0.000751             1147         0.000698    53.85
NOTE:  2        0.0001            1237   0.000763              994         0.000698    53.84
NOTE:  3        0.0001            1168  0.0007453            878.9         0.000698    53.74
NOTE:  4        0.0001           940.3  0.0007614            616.9         0.000698    53.68
NOTE:  5        0.0001           633.5  0.0007425            453.5         0.000698    53.66
NOTE:  6        0.0001           457.5  0.0007566            333.4         0.000698    53.63
NOTE:  7        0.0001           445.4  0.0007647              315         0.000698    53.62
NOTE:  8        0.0001           411.4  0.0007553            333.8         0.000698    53.67
NOTE:  9        0.0001           364.8  0.0007546            264.8        0.0006981    53.63
NOTE:  10       0.0001           344.5  0.0007578            303.4        0.0006979    53.65
NOTE:  11       0.0001           320.3  0.0007518            259.2        0.0006977    53.64
NOTE:  12       0.0001             274  0.0007622            262.7        0.0006979    53.61
NOTE:  13       0.0001           258.8  0.0007492            241.1        0.0006835    53.59
NOTE:  14       0.0001             241  0.0004668            242.8        0.0003893    53.59
NOTE:  15       0.0001           226.4  0.0003328            188.8        0.0002938    53.63
NOTE:  16       0.0001             206  0.0003143            180.5        0.0002835    53.58
NOTE:  17       0.0001           153.8  0.0002473            189.2        0.0002933    53.61
NOTE:  18       0.0001           158.5  0.0002408            160.6        0.0002403    53.61
NOTE:  19       0.0001           134.1  0.0002126            143.1        0.0002225    53.57
NOTE:  20       0.0001           120.1   0.000192            156.4        0.0002368    53.52
NOTE:  21       0.0001           112.3  0.0001783            129.4         0.000198    53.54
NOTE:  22       0.0001           114.3  0.0001792            154.5        0.0002301    53.54
NOTE:  23       0.0001           111.4  0.0001757              138        0.0002083    53.53
NOTE:  24       0.0001           103.2  0.0001663            116.9        0.0001798    53.54
NOTE:  25       0.0001           104.2  0.0001651            125.5        0.0001975    53.54
NOTE:  26       0.0001           95.02   0.000153            117.2         0.000185    53.49
NOTE:  27       0.0001           89.85  0.0001445            118.2        0.0001861    53.51
NOTE:  28       0.0001           85.44  0.0001384            118.5        0.0001813    53.50
NOTE:  29       0.0001           82.22  0.0001333            107.9        0.0001682    53.48
NOTE:  30       0.0001           76.66  0.0001237            103.6         0.000157    53.48
NOTE:  31       0.0001           77.31  0.0001258            106.9        0.0001598    53.49
NOTE:  32       0.0001           75.18  0.0001222            113.7         0.000172    53.46
NOTE:  33       0.0001           72.23  0.0001168              112        0.0001664    53.47
NOTE:  34       0.0001            68.7  0.0001116            99.11        0.0001509    53.48
NOTE:  35       0.0001           69.27  0.0001121            112.6        0.0001718    53.46
NOTE:  36       0.0001           67.61  0.0001092            117.9        0.0001737    53.45
NOTE:  37       0.0001           65.41  0.0001062            98.76        0.0001476    53.46
NOTE:  38       0.0001           63.91  0.0001029            104.2        0.0001522    53.45
NOTE:  39       0.0001           62.42  0.0001012            129.4        0.0001806    53.46
NOTE:  40       0.0001            65.7  0.0001065              102        0.0001535    53.46
NOTE:  41       0.0001           68.65  0.0001099            140.5        0.0001972    53.46
NOTE:  42       0.0001           89.59  0.0001369            101.8         0.000151    53.51
NOTE:  43       0.0001           83.53  0.0001253            106.8        0.0001611    53.54
NOTE:  44       0.0001           66.39  0.0001065            115.3        0.0001608    53.48
NOTE:  45       0.0001           60.89    9.8e-05            107.4        0.0001504    53.48
NOTE:  46       0.0001           56.24  9.086e-05            114.4        0.0001565    53.47
NOTE:  47       0.0001           58.86  9.461e-05            101.8        0.0001452    53.47
NOTE:  48       0.0001           55.31  8.962e-05              108        0.0001501    53.48
NOTE:  49       0.0001           53.65  8.646e-05            110.2         0.000153    53.46
NOTE:  The optimization reached the maximum number of epochs.
NOTE:  The total time is    2701.61 (s).
Out[22]:
§ ModelInfo
Descr Value
0 Model Name unet
1 Model Type Convolutional Neural Network
2 Number of Layers 33
3 Number of Input Layers 1
4 Number of Output Layers 0
5 Number of Convolutional Layers 19
6 Number of Pooling Layers 4
7 Number of Fully Connected Layers 0
8 Number of Concatenation Layers 4
9 Number of Transpose Convolution Layers 4
10 Number of Segmentation Layers 1
11 Number of Weight Parameters 34506432
12 Number of Bias Parameters 6850
13 Total Number of Model Parameters 34513282
14 Approximate Memory Cost for Training (MB) 52383

§ OptIterHistory
Epoch LearningRate Loss FitError L2Norm ValidLoss ValidError
0 1 0.00010 30043.423679 0.011388 0.673892 1410.516592 0.000698
1 2 0.00010 1405.655800 0.000751 0.659619 1147.467990 0.000698
2 3 0.00010 1237.400571 0.000763 0.651844 993.963451 0.000698
3 4 0.00010 1167.739603 0.000745 0.648050 878.914096 0.000698
4 5 0.00010 940.270184 0.000761 0.646480 616.885535 0.000698
5 6 0.00010 633.527666 0.000742 0.646287 453.525847 0.000698
6 7 0.00010 457.549162 0.000757 0.646689 333.419496 0.000698
7 8 0.00010 445.369965 0.000765 0.647080 315.035738 0.000698
8 9 0.00010 411.410260 0.000755 0.647682 333.807903 0.000698
9 10 0.00010 364.841303 0.000755 0.648492 264.790327 0.000698
10 11 0.00010 344.517356 0.000758 0.649261 303.428245 0.000698
11 12 0.00010 320.300198 0.000752 0.649981 259.231197 0.000698
12 13 0.00010 273.979162 0.000762 0.650622 262.685916 0.000698
13 14 0.00010 258.815844 0.000749 0.651114 241.064830 0.000684
14 15 0.00010 241.017937 0.000467 0.651789 242.798061 0.000389
15 16 0.00010 226.439632 0.000333 0.653148 188.800921 0.000294
16 17 0.00010 205.952714 0.000314 0.654688 180.516394 0.000283
17 18 0.00010 153.837159 0.000247 0.656058 189.213932 0.000293
18 19 0.00010 158.489311 0.000241 0.657098 160.646386 0.000240
19 20 0.00010 134.102727 0.000213 0.658161 143.147675 0.000222
20 21 0.00010 120.114039 0.000192 0.658931 156.446126 0.000237
21 22 0.00010 112.301341 0.000178 0.659536 129.415042 0.000198
22 23 0.00010 114.272860 0.000179 0.660085 154.545107 0.000230
23 24 0.00010 111.432736 0.000176 0.660733 138.023570 0.000208
24 25 0.00010 103.210250 0.000166 0.661304 116.893545 0.000180
25 26 0.00010 104.158239 0.000165 0.661757 125.504614 0.000198
26 27 0.00010 95.020817 0.000153 0.662258 117.161270 0.000185
27 28 0.00010 89.854172 0.000144 0.662636 118.161634 0.000186
28 29 0.00010 85.444479 0.000138 0.662922 118.498866 0.000181
29 30 0.00010 82.222410 0.000133 0.663222 107.894397 0.000168
30 31 0.00009 76.658425 0.000124 0.663490 103.568373 0.000157
31 32 0.00009 77.308087 0.000126 0.663717 106.918386 0.000160
32 33 0.00009 75.176644 0.000122 0.663996 113.718948 0.000172
33 34 0.00009 72.234499 0.000117 0.664190 112.015312 0.000166
34 35 0.00009 68.702986 0.000112 0.664397 99.110842 0.000151
35 36 0.00009 69.273405 0.000112 0.664601 112.570816 0.000172
36 37 0.00009 67.608597 0.000109 0.664820 117.908497 0.000174
37 38 0.00009 65.406973 0.000106 0.665041 98.759359 0.000148
38 39 0.00009 63.906603 0.000103 0.665226 104.152332 0.000152
39 40 0.00009 62.422037 0.000101 0.665399 129.435026 0.000181
40 41 0.00009 65.697101 0.000107 0.665587 102.017735 0.000153
41 42 0.00009 68.650815 0.000110 0.665974 140.482969 0.000197
42 43 0.00009 89.591280 0.000137 0.666611 101.809492 0.000151
43 44 0.00009 83.526463 0.000125 0.667565 106.835182 0.000161
44 45 0.00009 66.387030 0.000106 0.668062 115.343590 0.000161
45 46 0.00009 60.887296 0.000098 0.668290 107.417245 0.000150
46 47 0.00009 56.236933 0.000091 0.668424 114.387935 0.000156
47 48 0.00009 58.861295 0.000095 0.668579 101.840984 0.000145
48 49 0.00009 55.308654 0.000090 0.668728 108.032877 0.000150
49 50 0.00009 53.648653 0.000086 0.668871 110.220632 0.000153

§ OutputCasTables
casLib Name Rows Columns casTable
0 CASUSER(coambr) model_best_weights_obhiNa 34513282 3 CASTable('model_best_weights_obhiNa', caslib='CASUSER(coambr)')
1 CASUSER(coambr) UNet_weights 34513282 3 CASTable('UNet_weights', caslib='CASUSER(coambr)')

elapsed 2.73e+03s · user 2.58e+03s · sys 1.1e+03s · mem 5.25e+04MB

Load weights


In [23]:
weights_path = 'path to weights and attributes'
model.load_weights(weights_path+'/UNet_weights.sashdat')


NOTE: Model weights attached successfully!
NOTE: UNet_weights_attr.sashdat is used as weigths attribute.
NOTE: Model attributes attached successfully!

Predict segmentation for test set


In [24]:
model.predict(test, gpu=dict(devices=[0,1]))


NOTE: Using sysgpu01.unx.sas.com: 2 out of 4 available GPU devices.
Out[24]:
§ ScoreInfo
Descr Value
0 Number of Observations Read 634
1 Number of Observations Used 634
2 Misclassification Error (%) 0.017236
3 Pixel Accuracy (%) 99.97787
4 Mean Accuracy (%) 98.29181
5 Mean IoU (%) 87.84488
6 Frequency Weighted IoU (%) 99.78626
7 Loss Error 128.4363

§ OutputCasTables
casLib Name Rows Columns casTable
0 CASUSER(coambr) Valid_Res_iPJz13 634 524300 CASTable('Valid_Res_iPJz13', caslib='CASUSER(coambr)')

elapsed 67.4s · user 117s · sys 25.1s · mem 1.09e+05MB

Condense images


In [25]:
img_size=512

def compile_segmentation_predictions():
    cvars = ['origimage', 'c0']
    for i in range(1, img_size*img_size):
        list.append(cvars, 'c' + str(i))

    cvarspgm = "length origimage varchar(*); origimage=_image_; c0=input(_DL_PredName0_, 12.); "
    for i in range(1, img_size*img_size):
        cvarspgm += 'c' + str(i) + '=input(_DL_PredName' + str(i) + '_, 12.); '

    s.table.view(
        name='oview',
        tables=[dict(name=model.valid_res_tbl.name,
                     computedvars=cvars, 
                     computedvarsprogram=cvarspgm,
                     varlist=['_DL_PredName0_','_bioMedId_','_label_','_sliceIndex_','_id_','_bioMedDimension_', 'seg'])],
        replace=True)
    s.image.condenseimages(table='oview',
                           width=img_size, height=img_size, 
                           casout=dict(name='final_images_predicted', replace=True),
                           numberofchannels=1,
                           depth='bit8',
                           copyvars=['_DL_PredName0_','_bioMedId_','_bioMedDimension_','_label_','_sliceIndex_', 'origimage','_id_','seg']),
    
compile_segmentation_predictions()


NOTE: 634 out of 634 images were processed successfully and saved to the Cloud Analytic Services table final_images_predicted.

Annotate Images


In [26]:
s.fedsql.execdirect("create table annotated {options replace=true}"
                    "as (select * from final_images_predicted)")

s.table.altertable(table='annotated',
                   columns=[dict(name='_dimension_', rename='dim'),
                            dict(name='_resolution_', rename='res'),
                            dict(name='_imageformat_', rename='form')])

s.image.annotateimages(images=dict(table='annotated', image='origimage'),
                       annotations=[
                            dict(annotationparameters=
                                dict(annotationtype='segmentation',
                                    image='seg',
                                    colorMap='hsv',
                                    transparency=50,
                                    inputbackground=0
                                    )),
                           dict(annotationparameters=
                                dict(annotationtype='segmentation',
                                    image='_image_',
                                    dimension='dim',
                                    resolution='res',
                                    imageformat='form',
                                    colorMap='cool',
                                    transparency=50,
                                    inputbackground=0
                                    ))
                       ],
                       copyVars=['_sliceIndex_', '_label_'],
                       casout=dict(name='overlay', replace=True))


NOTE: Table ANNOTATED was created in caslib CASUSER(coambr) with 634 rows returned.
NOTE: 634 of 634 images were processed successfully and saved as encoded images to the Cloud Analytic Services table overlay.
Out[26]:
§ OutputCasTables
casLib Name Label Rows Columns casTable
0 CASUSER(coambr) overlay 634 4 CASTable('overlay', caslib='CASUSER(coambr)')

elapsed 0.327s · user 12.8s · sys 2.18s · mem 288MB


In [27]:
display(s.image.fetchimages(table='overlay', to=200).Images['Image'][15])
display(s.image.fetchimages(table='overlay', to=200).Images['Image'][20])
display(s.image.fetchimages(table='overlay', to=200).Images['Image'][25])


NOTE: Table OVERLAY contains compressed images.
NOTE: Table OVERLAY contains compressed images.
NOTE: Table OVERLAY contains compressed images.

Import images back into 3D


In [28]:
# Get decoded geometry info from CT scans
expname = 'LCTSC-Test-S3-204'
single_image_query = "_label_='"+expname+"'"
image_rows = s.CASTable('image').query(single_image_query).to_frame()

image_dims = image_rows["_dimension_"]
image_fmts = image_rows["_channelType_"]
image_images = image_rows["_image_"]
image_ress = image_rows["_resolution_"]
image_scas = image_rows["_spacing_"]
image_poss = image_rows["_position_"]
image_oris = image_rows["_orientation_"]
image_id = image_rows["_id_"]

# Import ground truth test set back into 3D
tmpTable=test.query("_label_ like 'LCTSC-Test-S3-204'")
ground_truth_seg = s.CASTable('ground_truth_seg', replace=True)
s.partition(table=tmpTable, casout=dict(name='ground_truth_seg', replace=True))

final_segs = s.CASTable('final_segs', replace=True)
s.biomedimage.processbiomedimages(
    images=dict(table=dict(name="ground_truth_seg"), image='seg'),
    steps=[dict(stepparameters=dict(steptype='import',targetDimension=3))],
    decode=True,
    copyVars=['_id_', '_label_'],
    casout=final_segs
)

final_testim = s.CASTable('final_testim', replace=True)
s.biomedimage.processbiomedimages(
    images=dict(table=dict(name="test")),
    steps=[dict(stepparameters=dict(steptype='import',targetDimension=3))],
    decode=True,
    copyVars=['_id_', '_label_'],
    casout=final_testim
)

# Import predicted images into 3D
import_images = s.CASTable('import_images', replace=True)
s.biomedimage.processbiomedimages(
    images=dict(table=dict(name="final_images_predicted")),
    steps=[dict(stepparameters=dict(steptype='import',targetDimension=3))],
    decode=True,
    copyVars=['_id_', '_label_'],
    casout=import_images
)

import_images_geo = s.CASTable('import_images_geo')
s.fedsql.execdirect("create table import_images_geo {options replace=true} as "
                        "select a.*, b._position_, b._orientation_, b._spacing_ "
                        "from import_images as a inner join image as b "
                        "on a._label_=b._label_")

tmpTable=import_images_geo.query("_label_ like 'LCTSC-Test-S3-204'")
single_import_image = s.CASTable('single_import_image', replace=True)
s.partition(table=tmpTable, casout=dict(name='single_import_image', replace=True))


NOTE: Processed 155 images from Cloud Analytic Services table GROUND_TRUTH_SEG.
NOTE: Processed 634 images from Cloud Analytic Services table TEST.
NOTE: Processed 634 images from Cloud Analytic Services table FINAL_IMAGES_PREDICTED.
NOTE: Table IMPORT_IMAGES_GEO was created in caslib CASUSER(coambr) with 17 rows returned.
Out[28]:
§ caslib
CASUSER(coambr)

§ tableName
SINGLE_IMPORT_IMAGE

§ rowsTransferred
0

§ shuffleWaitTime
0.0

§ minShuffleWaitTime
1e+300

§ maxShuffleWaitTime
0.0

§ averageShuffleWaitTime
0.0

§ casTable
CASTable('SINGLE_IMPORT_IMAGE', caslib='CASUSER(coambr)')

elapsed 0.102s · user 0.0311s · sys 0.0853s · mem 325MB

Build surfaces for ground truth and predicted segmentations


In [29]:
# Build predicted image surface
verticesMasked = s.CASTable('verticesMasked', replace=True)
facesMasked = s.CASTable('facesMasked', replace=True)
s.biomedimage.buildsurface(images=dict(table=single_import_image),
                           thresholds=[dict(low=1, high=1000)],
                           smoothing=dict(iterations=3),
                           outputvertices=verticesMasked,
                           outputfaces=facesMasked
                          )

fetchedVertices=verticesMasked.to_frame()
fetchedFaces=facesMasked.to_frame()

x = fetchedVertices.loc[:, ["_x_"]]
y = fetchedVertices.loc[:, ["_y_"]]
z = fetchedVertices.loc[:, ["_z_"]]
flist = fetchedFaces.loc[:, ["_v1_", "_v2_", "_v3_"]]

rgb = (255, 0, 0)
op = 0.75

# Build ground truth image surface
final_segs_geo = s.CASTable('final_segs_geo')

s.fedsql.execdirect("create table final_segs_geo {options replace=true} as "
                        "select a.*, b._position_, b._orientation_, b._spacing_ "
                        "from final_segs as a inner join image as b "
                        "on a._label_=b._label_")

vertices_seg = s.CASTable('vertices_seg', replace=True)
faces_seg = s.CASTable('faces_seg', replace=True)
s.biomedimage.buildsurface(images=dict(table=final_segs_geo),
                           thresholds=[dict(low=1, high=1000)],
                           smoothing=dict(iterations=3),
                           outputvertices=vertices_seg,
                           outputfaces=faces_seg)

fetchedVertices=vertices_seg.to_frame()
fetchedFaces=faces_seg.to_frame()

xs = fetchedVertices.loc[:, ["_x_"]]
ys= fetchedVertices.loc[:, ["_y_"]]
zs = fetchedVertices.loc[:, ["_z_"]]
flists = fetchedFaces.loc[:, ["_v1_", "_v2_", "_v3_"]]

rgbs = (0, 255, 0)
ops = 0.75


NOTE: Processed 1 image from Cloud Analytic Services table SINGLE_IMPORT_IMAGE.
NOTE: Table FINAL_SEGS_GEO was created in caslib CASUSER(coambr) with 1 rows returned.
NOTE: Processed 1 image from Cloud Analytic Services table FINAL_SEGS_GEO.

In [30]:
s.image.fetchImages(imageTable = 'slides').Images.Image[6]


NOTE: Table SLIDES contains decompressed images.
Out[30]:

Display spinal cord segmentation results in 3D


In [31]:
mlab.triangular_mesh(xs, ys, zs, flists, color=tuple([c/255 for c in rgbs]),opacity=ops)
mlab.triangular_mesh(x, y, z, flist, color=tuple([c/255 for c in rgb]),opacity=op)
display_slice_3d(image_images, image_dims, image_ress, image_fmts, image_poss,image_oris,image_scas,(0, 1, 2),0,55,0)
display_slice_3d(image_images, image_dims, image_ress, image_fmts, image_poss,image_oris,image_scas,(2, 1, 0),0,120,0)
display_slice_3d(image_images, image_dims, image_ress, image_fmts, image_poss,image_oris,image_scas,(1, 0, 2),0,250,0)
mlab.show()

In [32]:
s.image.fetchImages(imageTable = 'slides').Images.Image[7]


NOTE: Table SLIDES contains decompressed images.
Out[32]:

Compute DICE score


In [33]:
num_test_slice=test.shape[0]

images=s.image.fetchimages(table='test',
                           to = num_test_slice,
                           image='seg',
                           sortBy=[dict(name='_label_', order='ascending'),
                                   dict(name='_sliceIndex_', order='ascending')],
                           fetchVars=['_label_','pid','_sliceIndex_','seg'])

images2=s.image.fetchimages(table='final_images_predicted',
                            to = num_test_slice,
                            image="_image_",
                           sortBy=[dict(name='_label_', order='ascending'),
                                   dict(name='_sliceIndex_', order='ascending')],
                            fetchVars=['_label_','pid','_sliceIndex_','seg'])

num=0
den=0

for n in range(0, num_test_slice):
    
    # Compute Dice coefficient
    im1=images['Images']['Image'][n]
    im2=images2['Images']['Image'][n]
            
    im1 = np.asarray(im1).astype(np.bool)
    im2 = np.asarray(im2).astype(np.bool)

    intersection = np.logical_and(im1, im2)
    
    num = num + 2. * intersection.sum()
    den = den + im1.sum() + im2.sum()

num/den


NOTE: Table TEST contains compressed images.
NOTE: Table FINAL_IMAGES_PREDICTED contains decompressed images.
Out[33]:
0.7128660886488454

Use segmentation image to mask CT scan


In [34]:
s.image.fetchImages(imageTable = 'slides').Images.Image[8]


NOTE: Table SLIDES contains decompressed images.
Out[34]:

In [35]:
s.fedsql.execdirect("create table q_test {options replace=true} as "
                        "select * from import_images_geo")

s.table.altertable(table='q_test',
                   columns=[dict(name='_dimension_', rename='dim'),
                            dict(name='_resolution_', rename='res'),
                            dict(name='_imageformat_', rename='form'),
                            dict(name='_orientation_', rename='ori'),
                            dict(name='_position_', rename='pos'),
                            dict(name='_spacing_', rename='spa'),
                            dict(name='_image_', rename='seg')])

jdata = s.CASTable('jdata')

s.fedsql.execdirect("create table jdata {options replace=true} as "
                        "select a.*, b._image_ "
                        "from q_test as a inner join final_testim as b "
                        "on a._label_=b._label_ ")

bdata = s.CASTable('bdata', replace=True)

s.biomedimage.processbiomedimages(
    images=dict(
        table=jdata, dimension='dim', resolution='res', imageformat='form'),
    steps=[dict(stepparameters=dict(
                steptype='binary_operation',
                binaryoperation=dict(
                    binaryoperationtype='mask_specific',
                    image='seg',
                    dimension='dim', 
                    resolution='res', 
                    imageformat='form',
                    outputBackground=-1000
                )))],
    casout=bdata,
    decode=True,
    addcolumns=['channeltype', 'position', 'orientation', 'spacing', 'width', 'height', 'depth'],
    copyVars=['_id_', '_label_'],
)


NOTE: Table Q_TEST was created in caslib CASUSER(coambr) with 17 rows returned.
NOTE: Table JDATA was created in caslib CASUSER(coambr) with 17 rows returned.
NOTE: Processed 17 images from Cloud Analytic Services table JDATA.
Out[35]:
§ OutputCasTables
casLib Name Label Rows Columns casTable
0 CASUSER(coambr) bdata 17 14 CASTable('bdata', caslib='CASUSER(coambr)')

elapsed 0.886s · user 3s · sys 0.459s · mem 409MB

Quantify data


In [36]:
vol = s.CASTable(name='vol', replace=True)

s.biomedimage.quantifybiomedimages(images=dict(table='bdata'),
                                   quantities=[dict(quantityparameters=dict(quantitytype='content',usespacing=True)),
                                               dict(quantityparameters=dict(quantitytype='mean')),
                                               dict(quantityparameters=dict(quantitytype='maximum')),
                                               dict(quantityparameters=dict(quantitytype='minimum'))],
                                   inputbackground=0,
                                   labelParameters=dict(labelType='basic', connectivity='vertex'),
                                   region='image',
                                   copyvars=['_id_', '_label_'],
                                   casout=vol)
vol.to_frame()


NOTE: Processed 17 images from Cloud Analytic Services table BDATA.
Out[36]:
Selected Rows from Table VOL
_imageId_ _content_ _mean_ _maximum_ _minimum_ _id_ _label_
0 1116 11749.0 0.183264 231.0 0.0 1116 LCTSC-Test-S2-102
1 683 6436.0 0.065477 138.0 0.0 683 LCTSC-Test-S1-201
2 7834 4911.0 0.024017 176.0 0.0 7834 LCTSC-Test-S3-103
3 954 7638.0 0.071217 176.0 0.0 954 LCTSC-Test-S1-204
4 7835 6133.0 0.050966 121.0 0.0 7835 LCTSC-Test-S3-104
5 2370 520.0 0.045151 109.0 0.0 2370 LCTSC-Test-S3-203
6 1575 2730.0 0.021362 34.0 0.0 1575 LCTSC-Test-S2-202
7 1718 6784.0 0.150630 237.0 0.0 1718 LCTSC-Test-S2-203
8 2576 33439.0 0.055379 248.0 0.0 2576 LCTSC-Test-S3-204
9 8185 584.0 0.031531 111.0 0.0 8185 LCTSC-Test-S3-201
10 955 12027.0 0.126630 175.0 0.0 955 LCTSC-Test-S2-101
11 433 19749.0 0.059247 156.0 0.0 433 LCTSC-Test-S1-103
12 7399 7419.0 0.081494 146.0 0.0 7399 LCTSC-Test-S1-203
13 2055 963.0 0.044730 103.0 0.0 2055 LCTSC-Test-S3-102
14 1438 4576.0 0.104053 197.0 0.0 1438 LCTSC-Test-S2-201
15 1293 6906.0 0.134973 203.0 0.0 1293 LCTSC-Test-S2-104
16 565 8973.0 0.064918 134.0 0.0 565 LCTSC-Test-S1-104

Import ground truth, mask and quantify


In [37]:
test_import = s.CASTable('test_import', replace=True)
s.biomedimage.processbiomedimages(
    images=dict(table=dict(name="test"), image='seg'),
    steps=[dict(stepparameters=dict(steptype='import',targetDimension=3))],
    decode=True,
    copyVars=['_id_', '_label_'],
    addColumns=['width', 'height', 'depth', 'channeltype', 'spacing', 'position', 'orientation'],
    casout=test_import
)

s.fedsql.execdirect("create table q_test_gt {options replace=true} as "
                        "select * from test_import")

s.table.altertable(table='q_test_gt',
                   columns=[dict(name='_dimension_', rename='dim'),
                            dict(name='_resolution_', rename='res'),
                            dict(name='_imageformat_', rename='form'),
                            dict(name='_orientation_', rename='ori'),
                            dict(name='_position_', rename='pos'),
                            dict(name='_spacing_', rename='spa'),
                            dict(name='_image_', rename='seg')])

jdata_gt = s.CASTable('jdata_gt')

s.fedsql.execdirect("create table jdata_gt {options replace=true} as "
                        "select a.*, b._image_ "
                        "from q_test_gt as a inner join final_testim as b "
                        "on a._label_=b._label_ ")

bdata_gt = s.CASTable('bdata_gt', replace=True)

s.biomedimage.processbiomedimages(
    images=dict(
        table=jdata_gt, dimension='dim', resolution='res', imageformat='form'),
    steps=[dict(stepparameters=dict(
                steptype='binary_operation',
                binaryoperation=dict(
                    binaryoperationtype='mask_specific',
                    image='seg',
                    dimension='dim', 
                    resolution='res', 
                    imageformat='form',
                    outputBackground=-1000
                )))],
    casout=bdata_gt,
    decode=True,
    addcolumns=['position', 'orientation', 'spacing', 'channeltype', 'width', 'height', 'depth'],
    copyVars=['_id_', '_label_'],
)

vol_orig = s.CASTable(name='vol_orig', replace=True)

s.biomedimage.quantifyBioMedImages(
    images=dict(table='bdata_gt'),
    region='image',
    quantities=[dict(quantityparameters=dict(quantitytype='CONTENT',usespacing=True)),
                dict(quantityparameters=dict(quantitytype='MEAN')),
                dict(quantityparameters=dict(quantitytype='MAXIMUM')),
                dict(quantityparameters=dict(quantitytype='MINIMUM')),
    ],
    inputbackground=0,
    labelParameters=dict(labelType='basic', connectivity='vertex'),
    copyvars=['_label_', '_id_'],
    casout=vol_orig)


NOTE: Processed 634 images from Cloud Analytic Services table TEST.
NOTE: Table Q_TEST_GT was created in caslib CASUSER(coambr) with 17 rows returned.
NOTE: Table JDATA_GT was created in caslib CASUSER(coambr) with 17 rows returned.
NOTE: Processed 17 images from Cloud Analytic Services table JDATA_GT.
NOTE: Processed 17 images from Cloud Analytic Services table BDATA_GT.
Out[37]:
§ OutputCasTables
casLib Name Label Rows Columns casTable
0 CASUSER(coambr) vol_orig 17 7 CASTable('vol_orig', caslib='CASUSER(coambr)')

elapsed 0.239s · user 1.08s · sys 0.00312s · mem 168MB


In [38]:
plot_volumes_with_ground_truth_sc(vol, vol_orig)



In [39]:
s.image.fetchImages(imageTable = 'slides').Images.Image[10]


NOTE: Table SLIDES contains decompressed images.
Out[39]: