In [1]:
import sys
sys.path.insert(0, '../../../ndreg/')
sys.path.insert(0,'../code/functions/')
import ndreg
import math
import cv2
import pickle
import numpy as np
import matplotlib.pyplot as plt
import plosLib as pLib
import connectLib as cLib
import hyperReg as hype
import igraph as ig
from cluster import Cluster
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
from random import randrange
from random import uniform as floatRand
from PIL import Image
from scipy.ndimage.measurements import center_of_mass
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import plotly.graph_objs as go
init_notebook_mode(connected=True)
In [2]:
def toDiff(imgA, imgB):
ret = np.empty((imgA.shape[0], imgA.shape[1], 3), dtype=np.uint8)
for y in range(imgA.shape[0]):
for x in range(imgA.shape[1]):
if imgA[y][x] and not imgB[y][x]:
ret[y][x][0] = 255
ret[y][x][1] = 0
ret[y][x][2] = 0
elif not imgA[y][x] and imgB[y][x]:
ret[y][x][0] = 0
ret[y][x][1] = 255
ret[y][x][2] = 0
elif imgA[y][x] and imgB[y][x]:
ret[y][x][0] = 255
ret[y][x][1] = 0
ret[y][x][2] = 255
else:
ret[y][x][0] = 255
ret[y][x][1] = 255
ret[y][x][2] = 255
return ret
def visDiff(sliceA, sliceB):
disp = toDiff(sliceA, sliceB)
return disp
def visVolDiff(volumeA, volumeB):
for i in range(volumeA.shape[0]):
plt.figure()
plt.title('Disperity at z=' + str(i))
plt.imshow(visDiff(volumeA[i], volumeB[i]))
plt.show()
def visReg(clustersBase, clustersReg, pairing, title, origVolume):
data = []
subPairing = hype.randomSubset(pairing, 100)
for baseIdx, regIdx in subPairing:
this = [1, 2]
trace = go.Scatter3d(
x = [clustersBase[baseIdx].centroid[2], clustersReg[regIdx].centroid[2]],
y = [clustersBase[baseIdx].centroid[1], clustersReg[regIdx].centroid[1]],
z = [clustersBase[baseIdx].centroid[0], clustersReg[regIdx].centroid[0]],
marker = dict(size=4, color=this, colorscale='Viridis'),
line = dict(color='#1f77b4', width=1)
)
data.append(trace)
layout = dict(
width=800,
height=700,
autosize=False,
title=title,
scene=dict(
xaxis=dict(
gridcolor='rgb(255, 255, 255)',
zerolinecolor='rgb(255, 255, 255)',
showbackground=True,
backgroundcolor='rgb(230, 230,230)'
),
yaxis=dict(
gridcolor='rgb(255, 255, 255)',
zerolinecolor='rgb(255, 255, 255)',
showbackground=True,
backgroundcolor='rgb(230, 230,230)'
),
zaxis=dict(
gridcolor='rgb(255, 255, 255)',
zerolinecolor='rgb(255, 255, 255)',
showbackground=True,
backgroundcolor='rgb(230, 230,230)'
),
camera=dict(
up=dict(
x=0,
y=0,
z=1
),
eye=dict(
x=-1.7428,
y=1.0707,
z=0.7100,
)
),
aspectratio = dict( x=1, y=1, z=0.7 ),
aspectmode = 'manual'
),
)
fig = dict(data=data, layout=layout)
iplot(fig)
volCentroid = [origVolume.shape[0], origVolume.shape[1], origVolume.shape[2]]
plt.figure()
plt.title('Error as a function of Radius')
xList = []
yList = []
for elem in subPairing:
centroidDev = math.sqrt(np.sum(np.subtract(volCentroid, clustersBase[elem[0]].centroid)**2))
error = math.sqrt(np.sum(np.subtract(clustersBase[elem[0]].centroid, clustersReg[elem[1]].centroid)**2))
xList.append(centroidDev)
yList.append(error)
plt.scatter(xList, yList)
plt.show()
In [2]:
#A very simple registration problem where the data is the same, and is not offset at all
funcDat1A = [Cluster([[1, 1, 1], [1, 1, 2]]), Cluster([[2, 2, 2], [2, 2, 3]])]
funcDat1B = [Cluster([[1, 1, 1], [1, 1, 2]]), Cluster([[2, 2, 2], [2, 2, 3]])]
funcDat1Sol = [(0, 0), (1, 1)]
#A simple registration test set and its corresponding solution. Data has a linar shift of <1, 1, 1>.
funcDat2A = [Cluster([[1, 1, 1], [1, 1, 2]]), Cluster([[2, 2, 2], [2, 2, 3]])]
funcDat2B = [Cluster([[2, 2, 2], [2, 2, 3]]), Cluster([[3, 3, 3], [3, 3, 4]])]
funcDat2Sol = [(0, 0), (1, 1)]
There will be 3 sets of synthetic validation data:
In addition to this, there will be the actual tp1 and tp2 data sets, though they will not have as straightforward error metrics
In [3]:
#Prepare validation data by first running real data through the pipeline
#import the pickled versions of the real data
tp1 = pickle.load(open('../code/tests/synthDat/realDataRaw_t0.io', 'r'))
tp2 = pickle.load(open('../code/tests/synthDat/realDataRaw_t1.io', 'r'))
In [4]:
#cut the data to a reasonable size for testing
tp1TestData = tp1[:7]
tp2TestData = tp2[:7]
#run the data through the pipeline
tp1PostPipe = cLib.otsuVox(pLib.pipeline(tp1TestData))
tp2PostPipe = cLib.otsuVox(pLib.pipeline(tp2TestData))
#cut out the ill defined sections
tp1PostPipe = tp1PostPipe[1:6]
tp2PostPipe = tp2PostPipe[1:6]
In [5]:
#Display the data to be used for testing
for i in range(tp1PostPipe.shape[0]):
fig = plt.figure()
plt.title('Time Point 1 Pipe Output at z='+str(i))
plt.imshow(tp1PostPipe[i], cmap='gray')
plt.show()
In [6]:
#Display the data to be used for testing
for i in range(tp2PostPipe.shape[0]):
fig = plt.figure()
plt.title('Time Point 2 Pipe Output at z='+str(i))
plt.imshow(tp2PostPipe[i], cmap='gray')
plt.show()
In [7]:
transform1 = hype.get3DRigid(pitch=0., yaw=0., roll=0., xT=5., yT=0., zT=0.)
transformVolume1 = hype.apply3DRigid(tp1PostPipe, transform1, True)
transform2 = hype.get3DRigid(pitch=0., yaw=.15, roll=0., xT=0., yT=0., zT=0.)
transformVolume2 = hype.apply3DRigid(tp1PostPipe, transform2, True)
transform3 = hype.get3DRigid(pitch=0., yaw=.15, roll=0., xT=5., yT=0., zT=0.)
transformVolume3 = hype.apply3DRigid(tp1PostPipe, transform3, True)
In [8]:
#get the cluster lists from both the base and the transformations
valDatA = cLib.thresholdByVolumeNaive(cLib.connectedComponents(tp1PostPipe), lowerLimit = 0, upperLimit = 50)
valDat1B = cLib.thresholdByVolumeNaive(cLib.connectedComponents(transformVolume1), lowerLimit = 0, upperLimit = 50)
valDat2B = cLib.thresholdByVolumeNaive(cLib.connectedComponents(transformVolume2), lowerLimit = 0, upperLimit = 50)
valDat3B = cLib.thresholdByVolumeNaive(cLib.connectedComponents(transformVolume3), lowerLimit = 0, upperLimit = 50)
In [10]:
#Display the base data
print 'Perfect Matching:'
visVolDiff(tp1PostPipe, tp1PostPipe)
print '\n\nSlight Translation:'
visVolDiff(tp1PostPipe, transformVolume1)
print '\n\nSlight Rotation:'
visVolDiff(tp1PostPipe, transformVolume2)
print '\n\nSlight Rigid:'
visVolDiff(tp1PostPipe, transformVolume3)
In [ ]:
####################################
###THIS IS PSEUDOCODE; DO NOT RUN###
####################################
def simpleRegister(clusters1, clusters2):
for cluster in clusters1:
find pair in clusters2 minimizing l2 norm of centroids
pair the current cluster in its pair
remove the pair from cluster 2
In [11]:
def simpleRegister(clusters1, clusters2, verbose=False):
pairings = []
this = len(clusters1)
for clusterIdx in range(len(clusters1)):
if verbose:
print 'Progress: ', clusterIdx/float(this)
lossList = [np.sum(abs(np.array(clusters1[clusterIdx].centroid) - np.array(elem.centroid))) for elem in clusters2]
pairings.append((clusterIdx, np.argmin(lossList)))
return pairings
In [12]:
ftest1 = simpleRegister(funcDat1A, funcDat1B)
ftest2 = simpleRegister(funcDat2A, funcDat2B)
In [13]:
print 'Test1:', ftest1 == funcDat1Sol
print '\tExpected: ', funcDat1Sol
print '\tReturned:', ftest1
print '\n'
print 'Test2:', ftest2 == funcDat2Sol
print '\tExpected: ', funcDat2Sol
print '\tReturned:', ftest2
print '\n'
In [16]:
#comparison test
test0 = simpleRegister(valDatA, valDatA)
In [17]:
test1 = simpleRegister(valDatA, valDat1B)
test2 = simpleRegister(valDatA, valDat2B)
test3 = simpleRegister(valDatA, valDat3B)
In [60]:
visReg(valDatA, valDat1B, test1, 'Test 1 Registration Visualization', tp1PostPipe)
visReg(valDatA, valDat2B, test2, 'Test 2 Registration Visualization', tp1PostPipe)
visReg(valDatA, valDat3B, test3, 'Test 3 Registration Visualization', tp1PostPipe)
In [9]:
truDatA = valDatA
truDatB = cLib.thresholdByVolumeNaive(cLib.connectedComponents(tp2PostPipe), lowerLimit = 0, upperLimit = 50)
In [11]:
pickle.dump(truDatA, open('tp1_z1-5_clusters.io', 'w'))
pickle.dump(truDatB, open('tp2_z1-5_clusters.io', 'w'))
In [19]:
print '\n\nTrue Disp:'
visVolDiff(tp1PostPipe, tp2PostPipe)
In [20]:
truTest = simpleRegister(truDatA, truDatB)
In [61]:
visReg(truDatA, truDatB, truTest, 'True Data Registration', tp1PostPipe)
Based on the real data, and the known propensity of the l2 matching algorithm to misclassify based on points that happen to be near together, I propose the following iterations:
-Basic center of mass alignment before registration
The point of this exploration is to determine the effects of preprocessing volumes with a center of mass alignment
INPUT SPACE: One base volume, and one volume to be registered to the base
OUTPUT SPACE: A transformed registrtaion volume whose center of mass has been translated to match that of the base volume
In [ ]:
####################################
###THIS IS PSEUDOCODE; DO NOT RUN###
####################################
#get the centroids of both volumes
vol1CentList = []
for elem in volume1.nonzero():
vol1CentList.append(elem.centroid)
vol2CentList = []
for elem in vol2.nonzero():
vol2CentList.append(elem.centroid)
vol1Cent = average(vol1CentList)
vol2Cent = average(vol2CentList)
#get the shift required to align them
diff = vol1Cent - vol2Cent
#apply the translation
vol2 = vol2.nonzero() + diff
return vol2
In [48]:
def align3DCOM(npVolA, npVolB, verbose=False):
#get the centers of mass
comA = center_of_mass(npVolA)
comB = center_of_mass(npVolB)
if verbose:
print 'comA', comA, '\t', 'comB: ', comB
#find the disperity
translationVector = np.subtract(comB, comA)
#prep the vectorfor apply3DRigid
translationVector = np.flipud(translationVector)
translationVector = np.append(translationVector, 1.)
transform = np.identity(4)
transform[:,3]=translationVector
if verbose:
print 'transform: ', transform
return hype.apply3DRigid(npVolA, transform, verbose=verbose)
In [51]:
#Generate Synthetic Volume
comFTestVolA = np.zeros((3, 10, 10))
for i in range(4, 7):
for j in range(4, 7):
comFTestVolA[1][i][j] = 1.
#Generate volume that is offset by 1 in every dim
comFTestVolB = np.zeros((3, 10, 10))
for i in range(3, 6):
for j in range(3, 6):
comFTestVolB[2][i][j] = 1.
#Display the initial disperity
print 'Pre Alignment:'
visVolDiff(comFTestVolA, comFTestVolB)
#Perform the alignment, and display results
resVol = align3DCOM(comFTestVolB, comFTestVolA)
print 'Post Alignment:'
visVolDiff(comFTestVolA, resVol)
In [53]:
#get the cluster lists from both the base and the transformations
comValDatA = valDatA
#apply the preprocessing step
comPreVol1B = align3DCOM(transformVolume1, tp1PostPipe)
comPreVol2B = align3DCOM(transformVolume2, tp1PostPipe)
comPreVol3B = align3DCOM(transformVolume3, tp1PostPipe)
#get the cluster data from the volumes for registration
comValDat1B = cLib.thresholdByVolumeNaive(cLib.connectedComponents(comPreVol1B), lowerLimit = 0, upperLimit = 50)
comValDat2B = cLib.thresholdByVolumeNaive(cLib.connectedComponents(comPreVol2B), lowerLimit = 0, upperLimit = 50)
comValDat3B = cLib.thresholdByVolumeNaive(cLib.connectedComponents(comPreVol3B), lowerLimit = 0, upperLimit = 50)
In [54]:
comTest1 = simpleRegister(comValDatA, comValDat1B)
comTest2 = simpleRegister(comValDatA, comValDat2B)
comTest3 = simpleRegister(comValDatA, comValDat3B)
In [62]:
visReg(comValDatA, comValDat1B, comTest1, 'Test 1 Registration Visualization', tp1PostPipe)
visReg(comValDatA, comValDat2B, comTest2, 'Test 2 Registration Visualization', tp1PostPipe)
visReg(comValDatA, comValDat3B, comTest3, 'Test 3 Registration Visualization', tp1PostPipe)
In [56]:
comTruDatA = valDatA
comPreTruVolB = align3DCOM(tp2PostPipe, tp1PostPipe)
comTruDatB = cLib.thresholdByVolumeNaive(cLib.connectedComponents(comPreTruVolB), lowerLimit = 0, upperLimit = 50)
In [58]:
print '\n\nTrue Disp:'
visVolDiff(tp1PostPipe, comPreTruVolB)
In [59]:
comTruTest = simpleRegister(comTruDatA, comTruDatB)
In [63]:
visReg(comTruDatA, comTruDatB, comTruTest, 'True Data Registration', tp1PostPipe)
In [ ]: