Test 3D models using official candidates or candidates detected ourselves

#!/usr/local/bin/python
# -- coding: utf-8 --
‘’’This function calculates if the axis is a nodule using 3D model.
the output is the posibility of an axis whether it is a nodule.

Author: Kong Haiyang
‘’’
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import SimpleITK as sitk

import os
import sys
from os import walk
import numpy as np
import csv
import time
import cv2
from scipy.ndimage.interpolation import zoom

from six.moves import urllib
from six.moves import xrange
import tensorflow as tf

FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_integer(‘IMAGE_SIZE’, 40, “Size of input Image.”)
tf.app.flags.DEFINE_integer(‘PIXEL_DATA_SIZE’, 4, “Size of Image pixel.”)
tf.app.flags.DEFINE_integer(‘CHANNEL_NUMBER’, 1, “Channels of input Image.”)
tf.app.flags.DEFINE_integer(‘LABEL_NUMBER’, 2, “Label number.”)
tf.app.flags.DEFINE_integer(‘BATCH_SIZE’, 128, “Size of a Batch.”)
tf.app.flags.DEFINE_integer(‘NUM_EPOCHS’, 4, “Number of epochs.”)
tf.app.flags.DEFINE_integer(‘EVAL_BATCH_SIZE’, 128, “Size of an Evalution Batch.”)
tf.app.flags.DEFINE_integer(‘SEED’, 66478, “Seed of Shuffle.”)
tf.app.flags.DEFINE_string(‘TOWER_NAME’, ‘JP’, “Name of tower.”)
tf.app.flags.DEFINE_integer(‘NUM_GPU’, 1, “How many GPUs to use.”)
tf.app.flags.DEFINE_integer(‘NUM_PREPROCESS_THREADS’, 12,
“Number of preprocessing threads.”)
tf.app.flags.DEFINE_integer(‘NUM_LABEL’, 1, “How many Label bytes in a unit of Bin file.”)
tf.app.flags.DEFINE_integer(
‘NUM_IMAGE’, 40 ** 3, “How many Image bytes in a unit of Bin file.”)
tf.app.flags.DEFINE_integer(‘PIXEL_LENGTH’, 4, “Length of label or image pixel.”)
tf.app.flags.DEFINE_string(
‘CSV_FILE’, ‘/home/kong/4T/official3D_110W/Shuffle.csv’, “Csv file path and name.”)
tf.app.flags.DEFINE_string(
‘BIN_FILE’, ‘/home/kong/4T/official3D_110W/shuffle3D64.bin’, “Bin file path and name.”)
tf.app.flags.DEFINE_bool(‘SAVE_MODEL’, True, ‘Save model or not.’)
tf.app.flags.DEFINE_bool(‘USE_OFFICIAL’, True, ‘Use official csv or not.’)
tf.app.flags.DEFINE_integer(‘CUT_SIZE’, 64, ‘Size of block cutting out.’)
tf.app.flags.DEFINE_integer(‘SAVE_SIZE’, 40, ‘Size of block saved.’)

XAVIER_INIT = tf.contrib.layers.xavier_initializer(seed=FLAGS.SEED)

def normalizePlanes(npzarray):
maxHU = 400.
minHU = -1000.
npzarray = (npzarray - minHU) / (maxHU - minHU)
npzarray[npzarray > 1] = 1.
npzarray[npzarray < 0] = 0.
return npzarray

def saveImageSlice(image, cc, path):
if not os.path.exists(path):
os.makedirs(path)
imageTemp = image[cc[0] - 32:cc[0] + 32, …]
for index in np.arange(imageTemp.shape[0]):
sliceTemp = imageTemp[index, :, :].copy()
cv2.rectangle(sliceTemp, (cc[2] - 32, cc[1] - 32),
(cc[2] + 32, cc[1] + 32), (255, 255, 255))
cv2.imwrite(path + str(index) + ‘.png’, sliceTemp * 255)

def worldToVoxelCoord(worldCoord, origin, outputSpacing):
stretchedVoxelCoord = np.absolute(worldCoord - origin)
voxelCoord = stretchedVoxelCoord / outputSpacing
return voxelCoord

def interpolatefilter(inputpath):
inputimage = sitk.ReadImage(inputpath)
origin = inputimage.GetOrigin()
spacing = inputimage.GetSpacing()
direction = inputimage.GetDirection()
outputspacing = (spacing[0], spacing[0], spacing[0])
size = inputimage.GetSize()
tmp = int(spacing[2] * size[2] / spacing[0])
if tmp % 2 != 0:
tmp = tmp - 1

outputsize = (size[0], size[1], tmp)
resamplefilter = sitk.ResampleImageFilter()
resamplefilter.SetOutputDirection(direction)
resamplefilter.SetSize(outputsize)
resamplefilter.SetOutputOrigin(origin)
resamplefilter.SetOutputSpacing(outputspacing)
outputimage = resamplefilter.Execute(inputimage)
numpyImage = sitk.GetArrayFromImage(outputimage)
numpyImage = normalizePlanes(numpyImage)
return numpyImage, list(outputsize), spacing, outputspacing, origin

def createImageBorder(numpyImage, outputsize):
BackImage = np.zeros(((800, 800, 800)))
BackImage[400 - int(outputsize[2] / 2):400 + int(outputsize[2] / 2),
400 - int(outputsize[1] / 2):400 + int(outputsize[1] / 2),
400 - int(outputsize[0] / 2):400 + int(outputsize[0] / 2)] = numpyImage
return BackImage

def readCSV(filename):
‘’’read lines from a csv file.
‘’’
lines = []
with open(filename, “rb”) as f:
csvreader = csv.reader(f)
for line in csvreader:
lines.append(line)
return lines

def readSpecialLine(csvLines, uid):
coor = []
label = []
for line in csvLines:
if line[0] == uid:
coor.append(np.array(line[-2:0:-1], np.float))
label.append(int(line[-1]))
return coor, label

def readSpecialLine_own(csvLines, uid):
coor = []
label = []
for line in csvLines:
if line[1] == uid and line[-1] == ‘0’:
coor.append(np.array(line[5:2:-1], np.float))
label.append(int(line[-2]))
return coor, label

Wb = {
‘W11’: tf.get_variable(‘W11’, [3, 3, 3, FLAGS.CHANNEL_NUMBER, 16], tf.float32, XAVIER_INIT),
‘b11’: tf.Variable(tf.zeros([16])),
‘W12’: tf.get_variable(‘W12’, [3, 3, 3, 16, 24], tf.float32, XAVIER_INIT),
‘b12’: tf.Variable(tf.zeros([24])),
‘W2’: tf.get_variable(‘W2’, [3, 3, 3, 24, 32], tf.float32, XAVIER_INIT),
‘b2’: tf.Variable(tf.zeros([32])),
‘W3’: tf.get_variable(‘W3’, [3, 3, 3, 32, 48], tf.float32, XAVIER_INIT),
‘b3’: tf.Variable(tf.zeros([48])),
‘W4’: tf.get_variable(‘W4’, [3, 3, 3, 48, 64], tf.float32, XAVIER_INIT),
‘b4’: tf.Variable(tf.zeros([64])),
‘fcw1’: tf.get_variable(‘fcw1’, [2**3 * 64, 32], tf.float32, XAVIER_INIT),
‘fcb1’: tf.Variable(tf.zeros([32])),
‘fcw2’: tf.get_variable(‘fcw2’, [32, FLAGS.LABEL_NUMBER], tf.float32, XAVIER_INIT),
‘fcb2’: tf.Variable(tf.zeros([FLAGS.LABEL_NUMBER]))
}

def model(data):
with tf.variable_scope(‘conv1’):
conv = tf.nn.conv3d(data, Wb[‘W11’], strides=[1, 1, 1, 1, 1], padding=’SAME’)
relu = tf.nn.relu(tf.nn.bias_add(conv, Wb[‘b11’]))
conv = tf.nn.conv3d(relu, Wb[‘W12’], strides=[1, 1, 1, 1, 1], padding=’SAME’)
relu = tf.nn.relu(tf.nn.bias_add(conv, Wb[‘b12’]))
pool = tf.nn.max_pool3d(relu, ksize=[1, 2, 2, 2, 1],
strides=[1, 2, 2, 2, 1], padding=’VALID’)
with tf.variable_scope(‘conv2’):
conv = tf.nn.conv3d(pool, Wb[‘W2’], strides=[1, 1, 1, 1, 1], padding=’SAME’)
relu = tf.nn.relu(tf.nn.bias_add(conv, Wb[‘b2’]))
pool = tf.nn.max_pool3d(relu, ksize=[1, 2, 2, 2, 1],
strides=[1, 2, 2, 2, 1], padding=’VALID’)
with tf.variable_scope(‘conv3’):
conv = tf.nn.conv3d(pool, Wb[‘W3’], strides=[1, 1, 1, 1, 1], padding=’SAME’)
relu = tf.nn.relu(tf.nn.bias_add(conv, Wb[‘b3’]))
pool = tf.nn.max_pool3d(relu, ksize=[1, 2, 2, 2, 1],
strides=[1, 2, 2, 2, 1], padding=’VALID’)
with tf.variable_scope(‘conv4’):
conv = tf.nn.conv3d(pool, Wb[‘W4’], strides=[1, 1, 1, 1, 1], padding=’SAME’)
relu = tf.nn.relu(tf.nn.bias_add(conv, Wb[‘b4’]))
pool = tf.nn.max_pool3d(relu, ksize=[1, 2, 2, 2, 1],
strides=[1, 2, 2, 2, 1], padding=’VALID’)
with tf.variable_scope(‘reshape’):
ps = pool.get_shape().as_list()
reshape = tf.reshape(pool, [-1, ps[1] ps[2] ps[3] * ps[4]])
with tf.variable_scope(‘fc1’):
hidden = tf.nn.relu(tf.matmul(reshape, Wb[‘fcw1’]) + Wb[‘fcb1’])
with tf.variable_scope(‘fc2’):
out = tf.matmul(hidden, Wb[‘fcw2’]) + Wb[‘fcb2’]
return out

def LUNAtest(ssNo, image):
modelPath = ‘/home/kong/4T/official3D_110W/Cross{}’.format(ssNo)
test_data_node = tf.placeholder(
tf.float32, (None, FLAGS.IMAGE_SIZE, FLAGS.IMAGE_SIZE, FLAGS.IMAGE_SIZE, FLAGS.CHANNEL_NUMBER))
test_prediction = tf.nn.softmax(model(test_data_node))

def test_in_batches(data, sess):
size = data.shape[0]
predictions = np.ndarray(shape=(size, FLAGS.LABEL_NUMBER), dtype=np.float32)
for begin in xrange(0, size, FLAGS.EVAL_BATCH_SIZE):
end = begin + FLAGS.EVAL_BATCH_SIZE
if end <= size:
predictions[begin:end, :] = sess.run(
test_prediction,
feed_dict={test_data_node: data[begin:end, …]})
else:
batch_predictions = sess.run(
test_prediction,
feed_dict={test_data_node: data[-FLAGS.EVAL_BATCH_SIZE:, …]})
predictions[begin:, :] = batch_predictions[begin - size:, :]
return predictions

saver = tf.train.Saver()
with tf.Session() as sess:
ckpt = tf.train.get_checkpoint_state(modelPath)
ckpt.model_checkpoint_path = os.path.join(modelPath, ckpt.model_checkpoint_path)
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, ckpt.model_checkpoint_path)
predictions = test_in_batches(image, sess)
return predictions

def error_rate(preds, labels):
“””Calculate the error rate based on predictions and labels.”””
return 100.0 - (100.0 * np.sum(np.argmax(preds, 1) == labels) / len(preds))

def test_official(mhdOriginalPath, uid, ssNo, csvLines, count):
print(‘Processing No. {}…’.format(count))
axis, label = readSpecialLine(csvLines, uid)
interpolatedImage, outputsize, _, outputspacing, origin = interpolatefilter(
mhdOriginalPath)
BackImage = createImageBorder(interpolatedImage, outputsize)
ccList = []
for a in axis:
cutCenter = worldToVoxelCoord(a, origin[::-1], outputspacing)
cutCenter += (400 - np.array(outputsize[::-1]) / 2)
ccList.append(cutCenter)
ccList = np.round(ccList).astype(np.int)
image = np.empty([len(ccList), FLAGS.SAVE_SIZE, FLAGS.SAVE_SIZE, FLAGS.SAVE_SIZE, 1])
for index, cc in enumerate(ccList):
cutTemp = BackImage[cc[0] - 32:cc[0] + 32,
cc[1] - 32:cc[1] + 32,
cc[2] - 32:cc[2] + 32]

# saveImageSlice(BackImage, cc, 'test1/')
cutTemp = zoom(cutTemp, float(FLAGS.SAVE\_SIZE) / FLAGS.CUT\_SIZE)
image\[index, :, :, :, 0\] = cutTemp

results = LUNAtest(ssNo, image)
return results, np.array(axis), label

def test_own(mhdOriginalPath, uid, ssNo, csvLines, count):
print(‘Processing No. {}…’.format(count))
axis, label = readSpecialLine_own(csvLines, uid)
interpolatedImage, outputsize, spacing, _, _ = interpolatefilter(mhdOriginalPath)
BackImage = createImageBorder(interpolatedImage, outputsize)
ccList = []
for a in axis:
cutCenter = np.array(a, np.float) * spacing[::-1] / spacing[0]
cutCenter += (400 - np.array(outputsize[::-1]) / 2)
ccList.append(cutCenter)
ccList = np.round(ccList).astype(np.int)
image = np.empty([len(ccList), FLAGS.SAVE_SIZE, FLAGS.SAVE_SIZE, FLAGS.SAVE_SIZE, 1])
for index, cc in enumerate(ccList):
cutTemp = BackImage[cc[0] - 32:cc[0] + 32,
cc[1] - 32:cc[1] + 32,
cc[2] - 32:cc[2] + 32]

# saveImageSlice(BackImage, cc, 'test1/')
cutTemp = zoom(cutTemp, float(FLAGS.SAVE\_SIZE) / FLAGS.CUT\_SIZE)
image\[index, :, :, :, 0\] = cutTemp

results = LUNAtest(ssNo, image)
return results, np.array(axis), label

def genPath(originalPath):
‘’’generate a dict with the uid as keys
and the subset number as values.
‘’’
pathDict = {}
for (dirpath, dirnames, filenames) in walk(originalPath):
dirnames.sort()
filenames.sort()
if(len(filenames) < 20):
continue
else:
for filename in filenames:
if filename.endswith(‘.mhd’):
pathDict.setdefault(filename[:-4], dirpath[-1])
return pathDict

def main(_):
filePath = ‘/home/kong/4T/nodule_project’
pathDict = genPath(filePath)
if FLAGS.USE_OFFICIAL:
csvName = ‘/home/kong/4T/nodule_project/CSVFILES/candidates.csv’
csvLines = readCSV(csvName)[1:]
count = 0
with open(‘results_official.csv’, ‘wb’) as f:
csvwriter = csv.writer(f)
writeContent = [‘seriesuid’, ‘coordX’, ‘coordY’, ‘coordZ’, ‘probability’]
csvwriter.writerow(writeContent)
for uid in pathDict:
ssNo = pathDict[uid]
mhdFileName = (filePath + ‘/subset{}/‘ + ‘{}.mhd’).format(ssNo, uid)
result, axis, label = test_official(mhdFileName, uid, ssNo, csvLines, count)
for index in range(result.shape[0]):
writeContent = [uid, axis[index, 2], axis[
index, 1], axis[index, 0], result[index, 1]]
csvwriter.writerow(writeContent)
count += 1
else:
csvName = ‘/home/kong/4T/new_detect_first/LUNASTable.csv’
csvLines = readCSV(csvName)[1:]
with open(‘results_own.csv’, ‘wb’) as f:
csvwriter = csv.writer(f)
writeContent = [‘seriesuid’, ‘coordX’, ‘coordY’, ‘coordZ’, ‘probability’]
csvwriter.writerow(writeContent)
for uid in pathDict:
ssNo = pathDict[uid]
mhdFileName = (filePath + ‘/subset{}/‘ + ‘{}.mhd’).format(ssNo, uid)
result, axis, label = test_own(mhdFileName, uid, ssNo, csvLines)
for index in range(result.shape[0]):
writeContent = [uid, axis[index, 2], axis[
index, 1], axis[index, 0], result[index, 1]]
csvwriter.writerow(writeContent)

if __name__ == ‘__main__‘:
tf.app.run()