diff --git a/matlab/GoPrintTracks.m b/matlab/GoPrintTracks.m
new file mode 100644
index 0000000000000000000000000000000000000000..2860f224e5ef392a17bee1cdc3326ae174ef4a50
--- /dev/null
+++ b/matlab/GoPrintTracks.m
@@ -0,0 +1,90 @@
+%inputroot = 'C:\Users\mwinter\Documents\Data\OHSU\BDNF';
+%outputroot = fullfile(inputroot, 'OutputTrackingResults');
+%inputroot = 'C:\Users\mwinter\Documents\Data\OHSU\ssNPY';
+%outputroot = fullfile(inputroot, 'OutputTrackingResults');
+inputroot = 'C:\Users\mwinter\Documents\Data\OHSU\BDNF';
+outputroot = fullfile(inputroot, 'OutputTrackingResults');
+
+if ( ~exist(outputroot, 'dir') )
+    mkdir(outputroot);
+end
+
+if ( ~exist('movielist', 'var') )
+    if ( exist(fullfile(inputroot, 'movielist.mat'), 'file') )
+        load(fullfile(inputroot, 'movielist.mat'));
+    else
+        movielist = MakeBDNFMovieList(inputroot);
+        save(fullfile(inputroot, 'movielist.mat'), 'movielist');
+    end
+end
+
+if ( ~exist('startidx', 'var') )
+    startidx = 1;
+end
+
+if ( ~exist('endidx', 'var') )
+    endidx = length(movielist);
+end
+
+disp(['Start time: ' datestr(clock())]);
+
+goodsegs = [];
+for i=startidx:endidx
+    for tryalpha = 0.01:0.01:2.0
+%         if ( exist(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '.mat']), 'file') )
+%             system(['copy "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '.mat']) '" "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' mkalphastr(tryalpha) '.mat']) '"']);
+%             delete(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '.mat']));
+%         end
+%         if ( exist(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_seg.dat']), 'file') )
+%             system(['copy "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_seg.dat']) '" "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' mkalphastr(tryalpha) '_seg.dat']) '"']);
+%             delete(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_seg.dat']));
+%         end
+%         if ( exist(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_mb.dat']), 'file') )
+%             system(['copy "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_mb.dat']) '" "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' mkalphastr(tryalpha) '_mb.dat']) '"']);
+%             delete(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_mb.dat']));
+%         end
+%         if ( exist(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_tracks.txt']), 'file') )
+%             system(['copy "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_tracks.txt']) '" "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' mkalphastr(tryalpha) '_tracks.txt']) '"']);
+%             delete(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_tracks.txt']));
+%         end
+%         if ( exist(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_dbginfo.txt']), 'file') )
+%             system(['copy "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_dbginfo.txt']) '" "' fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' mkalphastr(tryalpha) '_dbginfo.txt']) '"']);
+%             delete(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' num2str(10*tryalpha,'%02d') '_dbginfo.txt']));
+%         end
+        
+        if ( ~exist(fullfile(outputroot, [movielist(i).root movielist(i).cellname '_a' mkalphastr(tryalpha) '.mat']), 'file') )
+            continue;
+        end
+        
+        goodsegs(end+1,:) = [i tryalpha];
+    end
+end
+
+if ( ~exist(fullfile(outputroot,'Prints'), 'dir') )
+    mkdir(fullfile(outputroot,'Prints'));
+end
+
+for i=1:size(goodsegs,1)
+    mvidx = goodsegs(i,1);
+    alpha = goodsegs(i,2);
+    
+    if ( ~exist(fullfile(outputroot, [movielist(mvidx).root movielist(mvidx).cellname '_a' mkalphastr(alpha) '_tracks.txt']), 'file')...
+        || ~exist(fullfile(outputroot,[movielist(mvidx).root movielist(mvidx).cellname '_a' mkalphastr(alpha) '_dbginfo.txt']), 'file') )
+        continue;
+    end
+    
+    outdatname = [movielist(mvidx).root movielist(mvidx).cellname '_a' mkalphastr(alpha) '_tracks.txt'];
+    dbgdatname = [movielist(mvidx).root movielist(mvidx).cellname '_a' mkalphastr(alpha) '_dbginfo.txt'];
+    load(fullfile(outputroot, [movielist(mvidx).root movielist(mvidx).cellname '_a' mkalphastr(alpha) '.mat']));
+    trackingStruct = readConnectData(trackingStruct, fullfile(outputroot, outdatname), fullfile(outputroot, dbgdatname));
+    save(fullfile(outputroot, [trackingStruct.fullname '_a' mkalphastr(alpha) '.mat']), 'trackingStruct');
+    
+    try
+        plotConnect(trackingStruct,5);
+        print('-r300', '-dtiffn', [fullfile(outputroot,'Prints') filesep trackingStruct.fullname '_a' mkalphastr(alpha) '_tracks.tiff']);
+    catch ME
+        disp(['Problem plotting: ' fullfile(outputroot,'Prints') filesep trackingStruct.fullname '_a' mkalphastr(alpha) '_tracks.tiff']);
+        disp(['   :' ME.message]);
+    end
+    close all;
+end
\ No newline at end of file
diff --git a/matlab/GoTrackFakes.m b/matlab/GoTrackFakes.m
new file mode 100644
index 0000000000000000000000000000000000000000..df8bbc6f6000b45b4ed415e972aeeca64e6be30d
--- /dev/null
+++ b/matlab/GoTrackFakes.m
@@ -0,0 +1,143 @@
+outdir = 'C:\Users\mwinter\Documents\MATLAB\faketest_singleaxon_three';
+mkdir(outdir);
+% numsdetlist = [10 25 50 75 100 125 150 200 250 300 350];
+numsdetlist = [10 20 40 60 80 100];
+falsedet = [0 10 20 40 60];
+numtrys = 20;
+
+trackertypes = {'_mta';'_sfa';'_Jaqaman'};
+% trackertypes = ['_sfa'];
+
+% % axons = [];
+% load(fullfile(outdir,'axons.mat'));
+% for idx=1:length(numsdetlist)
+%     numdet = numsdetlist(idx);
+%     
+%     for idxfalse = 1:length(falsedet)
+%         pctfalse = falsedet(idxfalse);
+%         numfalse = round(numdet*(pctfalse/100));
+%         
+%         for i=1:numtrys
+%             [ds, axons] = makePhantom(512, 512, 'numAxons',1, 'axons',axons, 'numDetections',numdet, 'speedVarProb',0, 'paramVarProb',0, 'falseDetections', numfalse);
+%     %         load(fullfile(outdir,['fakedata_' num2str(numdet,'%04d') 'det_' num2str(i,'%04d') 'try.mat']));
+% 
+%             for k=1:length(ds.rgDetect)
+%                 for j=1:size(ds.rgDetect{i},1)
+%                     ds.rgDetect{k}(j,:) = [ds.loc{k}(1,j) ds.loc{k}(2,j) ones(1,25)];
+%                 end
+%             end
+%             
+%             fname_prefix = ['fakedata_' num2str(numdet,'%04d') 'det_' num2str(pctfalse,'%04d') 'false_' num2str(i,'%04d') 'try'];
+% 
+%             save(fullfile(outdir,[fname_prefix '.mat']), 'ds');
+% 
+%             for tkidx=1:size(trackertypes,1)
+%                 outdatname = [fname_prefix trackertypes(tkidx,:) '_tracks.txt'];
+%                 dbgdatname = [fname_prefix trackertypes(tkidx,:) '_dbginfo.txt'];
+%                 segdatname = [fname_prefix '_seg.dat'];
+%                 mbdatname = [fname_prefix '_mb.dat'];
+% 
+%                 if ( ~exist(fullfile(outdir,outdatname), 'file') )
+%                     writeSegData(fullfile(outdir,segdatname), ds.rgDetect, ds.rgCC, [1 512; 1 512]);
+%                     writeMotionBlurData(fullfile(outdir,mbdatname), ds.rgMotionBlur);
+% 
+%                     system(['KymoTracker_v2' trackertypes(tkidx) '.exe "' fullfile(outdir,segdatname) '" "' fullfile(outdir,mbdatname) '" "' fullfile(outdir,outdatname) '" "' fullfile(outdir,dbgdatname) '" &']);
+%                 end
+%             end
+%             clear ds;
+%         end
+%     end
+% end
+% save(fullfile(outdir,'axons.mat'), 'axons');
+
+%errstruct = [];
+for tkidx=1:length(trackertypes)
+%     if ( ~exist('pctAdds','var') || ~all(size(pctAdds) == [length(numsdetlist) length(falsedet) numtrys]) )
+        pctAdds = nan*ones(length(numsdetlist),length(falsedet),numtrys);
+        pctMisses = nan*ones(length(numsdetlist),length(falsedet),numtrys);
+        pctErrs = nan*ones(length(numsdetlist),length(falsedet),numtrys);
+        avgPixelErrs = nan*ones(length(numsdetlist),length(falsedet),numtrys);
+%     else
+%         pctAdds = errstruct(tkidx,1).pctAdds;
+%         pctMisses = errstruct(tkidx,1).pctMisses;
+%         pctErrs = errstruct(tkidx,1).pctErrs;
+%         avgPixelErrs = errstruct(tkidx,1).avgPixelErrs;
+%     end
+
+    for idx=1:length(numsdetlist)
+        numdet = numsdetlist(idx);
+
+        for idxfalse = 1:length(falsedet)
+            pctfalse = falsedet(idxfalse);
+            numfalse = round(numdet*(pctfalse/100));
+            %numfalse = pctfalse;
+
+            for i=1:numtrys
+
+                fname_prefix = ['fakedata_' num2str(numdet,'%04d') 'det_' num2str(pctfalse,'%04d') 'false_' num2str(i,'%04d') 'try'];
+                %fname_prefix = ['fakedata_' num2str(numdet,'%04d') 'det_' num2str(i,'%04d') '_false' num2str(pctfalse,'%04d') '_try'];
+
+                disp(['Dataset: ' num2str(numdet) ' detections, ' num2str(pctfalse) ' false, ' num2str(i) 'th try'])
+
+                if ( isnan(pctAdds(idx,idxfalse,i)) )
+                    if ( strcmpi(trackertypes{tkidx},'_Jaqaman') )
+                        outdatname = [fname_prefix trackertypes{tkidx} '_tracks.mat'];
+                    else
+                        outdatname = [fname_prefix trackertypes{tkidx} '_tracks.txt'];
+                        dbgdatname = [fname_prefix trackertypes{tkidx} '_dbginfo.txt'];
+                    end
+                    
+                    if ( ~exist(fullfile(outdir,outdatname), 'file') )
+                        continue;
+                    end
+                    
+                    disp('Loading true phantom data...');
+                    tic
+                    load(fullfile(outdir,[fname_prefix '.mat']));
+                    toc
+
+                    if ( exist('dataset','var') )
+                        ds = dataset;
+                        clear dataset
+                        save(fullfile(outdir,[fname_prefix '.mat']), 'ds');
+                    end
+
+                    intracking.gAssigned = [];
+                    intracking.gConnect = [];
+                    intracking.gCellData = [];
+                    intracking.gCellDbgData = [];
+                    intracking.trackList = [];
+
+                    disp('Loading phantom tracked data...');
+                    tic
+                    try
+                        if ( strcmpi(trackertypes{tkidx},'_Jaqaman') )
+                            load(fullfile(outdir,outdatname));
+                            intracking = trackingStruct;
+                        else
+                            intracking = readConnectData(intracking, fullfile(outdir,outdatname), fullfile(outdir,dbgdatname));
+                        end
+                    catch me
+                        continue;
+                    end
+                    toc
+
+                    disp('Calculating edge errors...');
+                    tic
+                    [pctAdds(idx,idxfalse,i), pctMisses(idx,idxfalse,i), pctErrs(idx,idxfalse,i), avgPixelErrs(idx,idxfalse,i)] = calcErrs(ds, intracking, numdet, numfalse);
+                    toc
+                end
+
+                disp('---------------------');
+            end
+        end
+    end
+    errstruct(tkidx,1).name = trackertypes{tkidx};
+    errstruct(tkidx,1).pctAdds = pctAdds;
+    errstruct(tkidx,1).pctMisses = pctMisses;
+    errstruct(tkidx,1).pctErrs = pctErrs;
+    errstruct(tkidx,1).avgPixelErrs = avgPixelErrs;
+end
+
+save(['trackComparisonErrs_' num2str(max(numsdetlist),'%04d') 'detmax_' num2str(max(falsedet),'%04d') 'falsemax.mat'], 'errstruct', 'numsdetlist', 'falsedet', 'numtrys', 'trackertypes');
+%errSurfaces(100*pctErrs, falsedet, numsdetlist, 30)
diff --git a/matlab/buildMovieInfo.m b/matlab/buildMovieInfo.m
new file mode 100644
index 0000000000000000000000000000000000000000..f4a7dc454f1f6e601b0ec6280f6a25d04196d700
--- /dev/null
+++ b/matlab/buildMovieInfo.m
@@ -0,0 +1,8 @@
+function movieInfo = makeMovieInfo(ds)
+    for i=1:length(ds.rgDetect)
+        movieInfo(i).xCoord = [ds.rgDetect{i}(:,1) zeros(size(ds.rgDetect{i}(:,1)))];
+        movieInfo(i).yCoord = [ds.rgDetect{i}(:,2) zeros(size(ds.rgDetect{i}(:,2)))];
+        movieInfo(i).zCoord = [zeros(size(ds.rgDetect{i}(:,1))) zeros(size(ds.rgDetect{i}(:,1)))];
+        movieInfo(i).amp = [ds.rgDetect{i}(:,15) zeros(size(ds.rgDetect{i}(:,1)))];
+    end
+end
\ No newline at end of file
diff --git a/matlab/buildTrackingStruct.m b/matlab/buildTrackingStruct.m
new file mode 100644
index 0000000000000000000000000000000000000000..d9d94b0a6562ebe7ead8f41790779a0a5af23ab0
--- /dev/null
+++ b/matlab/buildTrackingStruct.m
@@ -0,0 +1,45 @@
+function trackingStruct = buildTrackingStruct(ds, tracksFinal, compWindowSize)
+    l2g = zeros(length(ds.rgDetect)+1,1);
+    g2l = zeros(size(ds.gConnectActual,1),2);
+    for j=1:length(ds.rgDetect)
+        l2g(j+1) = l2g(j) + size(ds.rgDetect{j},1);
+        g2l((l2g(j)+1):l2g(j+1),:) = [j*ones(size(ds.rgDetect{j},1),1) cumsum(ones(size(ds.rgDetect{j},1),1))];
+    end
+    
+    tracksList = [];
+    numEdges = length([tracksFinal(:).tracksFeatIndxCG]);
+    gAssignedTracking = sparse([],[],[],size(ds.gConnectActual,1),size(ds.gConnectActual,2),numEdges);
+    gConnectTracking = sparse([],[],[],size(ds.gConnectActual,1),size(ds.gConnectActual,2),numEdges);
+    gCellDatatTracking = [];
+    for i=1:length(tracksFinal)
+        bNonMissedFrames = ~(tracksFinal(i).tracksFeatIndxCG == 0);
+        trackFrames = tracksFinal(i).seqOfEvents(1,1):tracksFinal(i).seqOfEvents(end,1);
+        tracksList{i} = tracksFinal(i).tracksFeatIndxCG + l2g(trackFrames)';
+        tracksList{i} = tracksList{i}(bNonMissedFrames);
+        
+        r = tracksList{i}(1:end-1);
+        c = tracksList{i}(2:end);
+        linIdx = sub2ind(size(gAssignedTracking),r,c);
+        
+        bAlreadyAssigned = any(gAssignedTracking(r,:),2);
+        if ( any(bAlreadyAssigned) )
+            error('We do not handle shared detections for the Jaqaman tracker yet');
+        end
+        
+        gAssignedTracking(linIdx) = 1;
+        
+        for j=1:length(r)
+            newCellIdx = length(gCellDatatTracking)+1;
+            
+            gConnectTracking(r(j),c(j)) = newCellIdx;
+            
+            gCellDatatTracking(newCellIdx).trackID = i;
+            gCellDatatTracking(newCellIdx).path = tracksList{i}(j:min(j+compWindowSize,length(tracksList{i})));
+        end
+    end
+    
+    trackingStruct.gConnect = gConnectTracking;
+    trackingStruct.gCellData = gCellDatatTracking;
+    trackingStruct.gAssigned = gAssignedTracking;
+    trackingStruct.trackList = tracksList;
+end
\ No newline at end of file
diff --git a/matlab/calcErrs.m b/matlab/calcErrs.m
new file mode 100644
index 0000000000000000000000000000000000000000..0f4818ccf6ec4da3a4834776fdd14a052804c1b3
--- /dev/null
+++ b/matlab/calcErrs.m
@@ -0,0 +1,126 @@
+function [pctAdds, pctMisses, pctErrs, avgPixelErr, errs] = calcErrs(ds, intracking, numdet, numfalse)
+    l2g = zeros(length(ds.rgDetect)+1,1);
+    g2l = zeros(size(ds.gConnectActual,1),2);
+    for j=1:length(ds.rgDetect)
+        l2g(j+1) = l2g(j) + size(ds.rgDetect{j},1);
+        g2l((l2g(j)+1):l2g(j+1),:) = [j*ones(size(ds.rgDetect{j},1),1) cumsum(ones(size(ds.rgDetect{j},1),1))];
+    end
+    
+    gAssignedActual = ds.gConnectActual > 0;
+    gAssignedTracking = intracking.gAssigned;
+    gAssignedExpandedShared = gAssignedTracking;
+    
+    mintracklength = 6;
+
+    % Expand shared edges
+    %  Note: Could also expand missed detections by interpolation
+    [r,c] = find(gAssignedTracking);
+    sharedidx = find(c > l2g(g2l(r,1)+2));
+    for i=1:length(sharedidx)
+        ridx = r(sharedidx(i));
+        cidx = c(sharedidx(i));
+        cellidx = intracking.gConnect(ridx,find(intracking.gAssigned(ridx,:)));
+        if ( length(cellidx) > 1 )
+            error(['Multiple edge assignments in gConnect row: ' num2str(i)]);
+        end
+%         if ( isempty(cellidx) )
+%             continue;
+%         end
+        %  Note: Could also expand missed detections by interpolation
+        %  if path has no internal points (>ridx,<cidx) then interp
+        
+        path = intracking.gCellData(cellidx).path;
+        path = path(path >= ridx & path <= cidx);
+        linidx = sub2ind(size(gAssignedExpandedShared), path(1:end-1),path(2:end));
+        
+        gAssignedExpandedShared(linidx) = 1;
+        gAssignedExpandedShared(ridx,cidx) = 0;
+    end
+    
+    edgecount = nnz(gAssignedActual);
+    
+    misscount = 0;
+    errs = [];
+    swaperr = [];
+    swaperrcount = 1;
+    addcount = 0;
+    falseaddcount = 0;
+    for i=1:size(gAssignedActual,1)
+        t = g2l(i,1);
+        locidx = g2l(i,2);
+        
+        % No error if assigned edges match perfectly
+        if ( all((gAssignedActual(i,:) - gAssignedTracking(i,:)) == 0) )
+            continue;
+        end
+        
+        cellidx = intracking.gConnect(i,find(intracking.gAssigned(i,:)));
+        tracklen = mintracklength;
+        if ( ~isempty(cellidx) )
+            tracklen = length(intracking.trackList{intracking.gCellData(cellidx).trackID});
+        end
+        
+        bIsFalse = (locidx > numdet);
+        
+        % Ignore short track edge errors in false
+        if ( bIsFalse && tracklen < mintracklength )
+            continue;
+        end
+        
+        comprow = any(gAssignedActual(i,:)) - any(gAssignedExpandedShared(i,:));
+        
+        trueinidx = find(gAssignedActual(i,:));
+        trackedinidx = find(gAssignedExpandedShared(i,:));
+        if ( comprow > 0 && ~any(gAssignedActual(:,trueinidx)) )
+            misscount = misscount + 1;
+        elseif (comprow < 0 && ~any(any(gAssignedActual(:,trackedinidx))) )
+            addcount = addcount + 1;
+            if ( bIsFalse )
+                falseaddcount = falseaddcount + 1;
+            end
+        elseif ( any(gAssignedExpandedShared(i,:)) )
+            % Errors are associated with the row they occur on not column?
+            trackedinidx = find(gAssignedExpandedShared(i,:));
+            [trueoutidx,c] = find(gAssignedActual(:,trackedinidx));
+            
+            trueoutidx = trueoutidx';
+            
+            trueedges = [i*ones(size(trueinidx)) trueoutidx; trueinidx trackedinidx(c)];
+            trackedges = [i*ones(size(trackedinidx)); trackedinidx];
+            
+            trackerr = 0;
+            disterr = Inf*ones(size(trueedges,2),1);
+            for j=1:size(trackedges,2)
+                for k=1:size(trueedges,2)
+                    if ( any(trueedges(:,k) == trackedges(:,j)) )
+                        truepts(:,1) = ds.loc{g2l(trueedges(1,k),1)}(:,g2l(trueedges(1,k),2));
+                        truepts(:,2) = ds.loc{g2l(trueedges(2,k),1)}(:,g2l(trueedges(2,k),2));
+                        trackpts(:,1) = ds.loc{g2l(trackedges(1,j),1)}(:,g2l(trackedges(1,j),2));
+                        trackpts(:,2) = ds.loc{g2l(trackedges(2,j),1)}(:,g2l(trackedges(2,j),2));
+                        
+                        disterr(k) = sum(sqrt((truepts(1,:)-trackpts(1,:)).^2 + (truepts(2,:)-trackpts(2,:)).^2));
+                    end
+                end
+                trackerr = trackerr + min(disterr);
+            end
+            
+            if ( ~isempty(trueinidx) )
+                errs{swaperrcount,1} = [trackerr i trueinidx trackedinidx];
+            else
+                errs{swaperrcount,1} = [trackerr i 0 trackedinidx];
+            end
+            
+            swaperr(swaperrcount,1) = trackerr;
+            swaperrcount = swaperrcount + 1;
+        end
+    end
+    
+    pctAdds = addcount / edgecount;
+    pctMisses = misscount / edgecount;
+    pctErrs = swaperrcount / edgecount;
+    
+    avgPixelErr = 0;
+    if ( ~isempty(swaperr) )
+        avgPixelErr = mean(swaperr);
+    end
+end
\ No newline at end of file
diff --git a/matlab/compareErrSurf.m b/matlab/compareErrSurf.m
new file mode 100644
index 0000000000000000000000000000000000000000..4ede69e41c2de2a8260b5b3bbde42dbcc5c583be
--- /dev/null
+++ b/matlab/compareErrSurf.m
@@ -0,0 +1,46 @@
+function compareErrSurf(erra, errb, errc, gridx, gridy, interppts, interpmethod)    
+
+    cmap = [summer(512);autumn(512);winter(512)];
+
+    [X,Y] = meshgrid(gridx, gridy);
+    [XI,YI] = meshgrid(linspace(gridx(1), gridx(end), interppts), linspace(gridy(1), gridy(end), interppts));
+    
+    mederra = nanmedian(erra,3);
+    interpmederra = interp2(X,Y,mederra,XI,YI, interpmethod);
+    mederrb = nanmedian(errb,3);
+    interpmederrb = interp2(X,Y,mederrb,XI,YI, interpmethod);
+    mederrc = nanmedian(errc,3);
+    interpmederrc = interp2(X,Y,mederrc,XI,YI, interpmethod);
+    
+    ca = 511*((mederra-min(mederra(:))) / (max(mederra(:))-min(mederra(:)))) + 1;
+    cb = 511*((mederrb-min(mederrb(:))) / (max(mederrb(:))-min(mederrb(:)))) + 513;
+    cc = 511*((mederrc-min(mederrc(:))) / (max(mederrc(:))-min(mederrc(:)))) + 1025;
+    
+    colora = 511*((interpmederra-min(interpmederra(:))) / (max(interpmederra(:))-min(interpmederra(:)))) + 1;
+    colorb = 511*((interpmederrb-min(interpmederrb(:))) / (max(interpmederrb(:))-min(interpmederrb(:)))) + 513;
+    colorc = 511*((interpmederrc-min(interpmederrb(:))) / (max(interpmederrc(:))-min(interpmederrb(:)))) + 1025;
+    
+    
+    
+    figure;colormap(cmap);
+    surf(X,Y,mederra,ca);
+    hold on;
+    surf(X,Y,mederrb,cb);
+    surf(X,Y,mederrc,cc);
+    xlabel('False Detections(% of Detections)');
+    ylabel('Detections per Frame');
+    
+    
+    figure;colormap(cmap);
+    h = surf(XI,YI,interpmederra,colora);
+    hold on;
+    h = surf(XI,YI,interpmederrb,colorb);
+    alpha(h, 0.85);
+    h = surf(XI,YI,interpmederrc,colorc);
+    alpha(h, 0.75);
+    
+    xlabel('False Detections(% of Detections)');
+    ylabel('Detections per Frame');
+    %legend('Multitemporal Assignment','Single Frame Assignment');
+    
+end
\ No newline at end of file
diff --git a/matlab/compareKymo.m b/matlab/compareKymo.m
new file mode 100644
index 0000000000000000000000000000000000000000..8d2d269e8a5d2e3086bc7443c9567ae7331e363e
--- /dev/null
+++ b/matlab/compareKymo.m
@@ -0,0 +1,269 @@
+function outColors = compareKymo(root, fname, trackingStruct, tracks, times)
+
+    rmkdir(root);
+
+    cmap = hsv(length(tracks)+1);
+    ctweak = [zeros(size(cmap,1),1) zeros(size(cmap,1),1) zeros(size(cmap,1),1)];
+    ctweak(4:6,3) = [0 0.3 0.4];
+    cmap = hsv2rgb(rgb2hsv(cmap)-ctweak);
+    trackColors = zeros(length(tracks),3);
+    for i=1:length(tracks)
+    	trackColors(i,:) = cmap(i,:);
+    end
+    outColors = round(255*trackColors);
+    
+    fullTrackList = BuildFullTrackList(trackingStruct);
+
+    kymosize = size(trackingStruct.imKymo);
+    
+    tracelen = norm([trackingStruct.jPath(end) trackingStruct.iPath(end)] - [trackingStruct.jPath(1) trackingStruct.iPath(1)]);
+    trcppix = tracelen / kymosize(1);
+    
+    umppix = 0.18;
+    spfrm = 0.650;
+    
+    invfig = figure('Color','w');[rgbKymo rgbmap] = gray2ind(1-mat2gray(trackingStruct.imKymo), 255);
+    imagesc([0 spfrm*(kymosize(2)-1)], [0 umppix*(kymosize(1)-1)*trcppix],ind2rgb(rgbKymo, rgbmap));axis xy; truesize(invfig, round(2*(kymosize./[trcppix 1])))
+    frm = getframe(invfig);
+    [frmimg, map] = frame2im(frm);
+    if ( ~isempty(map) )
+        imwrite(frmimg, map, fullfile(root, [fname '_kymo.tiff']), 'tiff');
+    else
+        imwrite(frmimg, fullfile(root, [fname '_kymo.tiff']), 'tiff');
+    end
+    
+    trackfig = figure('Color','w');[rgbKymo rgbmap] = gray2ind(trackingStruct.imKymo, 255);
+    imagesc([0 spfrm*(kymosize(2)-1)], [0 umppix*(kymosize(1)-1)*trcppix],ind2rgb(rgbKymo, rgbmap));axis xy;
+
+    vdata = [];
+    kvdata = [];
+    
+    figure(trackfig);hold on;
+    for i=1:length(tracks)
+        trkt = trackingStruct.globNodeLUT(fullTrackList{tracks(i)},1)';
+        trkidx = trackingStruct.globNodeLUT(fullTrackList{tracks(i)},2)';
+        
+        bValidId = trkt >= times(i,1) & trkt <= times(i,2);
+        trkt = trkt(bValidId);
+        trkidx = trkidx(bValidId);
+        trkpts = GetKymoPt(trackingStruct.rgDetect, trkidx', trkt', trackingStruct.LKymo, [trackingStruct.jPath';trackingStruct.iPath']);
+        
+        plot(spfrm*(trkt-1), umppix*(trkpts-1)*trcppix, 'Color', trackColors(i,:), 'LineWidth',2, 'LineStyle','-');
+        text(spfrm*(trkt(round(end/2))-1), umppix*(trkpts(round(end/2))-1)*trcppix, ['\leftarrow ' num2str(i)], 'Color', trackColors(i,:), 'BackgroundColor','w');
+        
+        lclSpeed = [];
+        vtan = [];
+        xldata = {i '';'Velocity' 'Kymograph Velocity';'' ''};
+        headeroffset = size(xldata,1);
+        arclen = 0;
+        for k=1:length(trkt)-1
+            lclVel = ((trackingStruct.rgDetect{trkt(k+1)}(trkidx(k+1),1:2) - trackingStruct.rgDetect{trkt(k)}(trkidx(k),1:2)) / (trkt(k+1)-trkt(k)) * (umppix / spfrm));
+            lclVel = [lclVel(2),lclVel(1)];
+            
+            lclDelta = (trackingStruct.rgDetect{trkt(k+1)}(trkidx(k+1),1:2) - trackingStruct.rgDetect{trkt(k)}(trkidx(k),1:2));
+            lclDelta = [lclDelta(2),lclDelta(1)];
+            
+            lclKymoVel = trkpts(k+1) - trkpts(k);
+            kymovec = [trackingStruct.jPath(trkpts(k+1)) trackingStruct.iPath(trkpts(k+1))] - [trackingStruct.jPath(trkpts(k)) trackingStruct.iPath(trkpts(k))];
+            
+            lclSpeed(k,1) = sign(lclKymoVel)*norm(lclVel);
+            
+            lclArcLen = dot(lclDelta, kymovec) / norm(kymovec);
+            vtan(k,1) = sign(lclKymoVel)*dot(lclVel, kymovec) / norm(kymovec);
+            if ( norm(kymovec) < 1e-7 )
+                vtan(k,1) = 0;
+                lclArcLen = 0;
+            end
+            xldata{k+headeroffset,1} = lclSpeed(k,1);
+            xldata{k+headeroffset,2} = vtan(k,1);
+            
+            arclen = arclen + lclArcLen;
+        end
+        %xlswrite(fullfile(root, [fname '_stats.xlsx']), xldata, [char((2*(i-1)+1)+65) num2str(1) ':' char((2*(i-1)+2)+65) num2str(length(lclSpeed)+headeroffset)]);
+        
+        avgSpeed = (norm((trackingStruct.rgDetect{trkt(end)}(trkidx(end),1:2) - trackingStruct.rgDetect{trkt(1)}(trkidx(1),1:2))) / (trkt(end)-trkt(1))) * (umppix / spfrm);
+        avgKymoSpeed = (arclen / (trkt(end)-trkt(1))) * (umppix / spfrm);
+        
+        vdata = [vdata;lclSpeed];
+        kvdata = [kvdata;vtan];
+    end
+    hold off;
+    hleg = legend(arrayfun(@num2str,(1:length(tracks)), 'UniformOutput',0), 'Location','NorthEastOutside');
+    %legend(hleg, 'boxoff');
+    truesize(trackfig, round(3*(kymosize./[trcppix 1])));
+    %legpos = get(hleg, 'Position');
+    %set(hleg, 'Position',legpos - [0.04,0,0,0]);
+	frm = getframe(trackfig);
+    [frmimg, map] = frame2im(frm);
+    if ( ~isempty(map) )
+        imwrite(frmimg, map, fullfile(root, [fname '_tracks.tiff']), 'tiff');
+    else
+        imwrite(frmimg, fullfile(root, [fname '_tracks.tiff']), 'tiff');
+    end
+    
+    xbins = -5.5:0.25:4.5;
+    figure();hist(vdata(abs(vdata)>0.2),xbins);
+    title(['ssNPY velocities']);
+    xlabel('Velocity [um/s] (+/-: anterograde/retrograde)');
+    h = findobj(gca,'Type','patch');
+    set(h, 'FaceColor','r');
+    set(h, 'FaceAlpha',0.5);
+    set(gca,'xminortick','on')
+    set(gca,'xtick',[-6:6])
+    print('-dtiff', fullfile(root, [fname '_velhistogram.tiff']));
+    
+    xbins = -5.5:0.25:4.5;
+    figure();hist(kvdata(abs(kvdata)>0.2),xbins);
+    title(['ssNPY kymograph velocities']);
+    xlabel('Velocity [um/s] (+/-: anterograde/retrograde)');
+    h = findobj(gca,'Type','patch');
+    set(h, 'FaceColor','r');
+    set(h, 'FaceAlpha',0.5);
+    set(gca,'xminortick','on')
+    set(gca,'xtick',[-6:6])
+    print('-dtiff', fullfile(root, [fname '_kymovelhistogram.tiff']));
+    
+    close all;
+    
+    %movie
+    iminfo = imfinfo(fullfile(trackingStruct.moviepath, trackingStruct.moviename));
+    kymoinfo = imfinfo(fullfile(trackingStruct.moviepath, trackingStruct.moviename));
+    N = length(iminfo);
+    
+    [tracemin, tracemax] = getTrackExtents(trackingStruct, N);
+    
+    imwght = ones(iminfo(1).Height, iminfo(1).Width);
+    
+    imheight = tracemax(2) - tracemin(2);
+    kymoheight = size(trackingStruct.imKymo,1);
+    
+    bLeft = boolean(ones(length(tracks),1));
+    for i=1:length(tracks)
+        trkt = trackingStruct.globNodeLUT(fullTrackList{tracks(i)},1)';
+        trkidx = trackingStruct.globNodeLUT(fullTrackList{tracks(i)},2)';
+        
+        bValidId = trkt >= times(i,1) & trkt <= times(i,2);
+        trkt = trkt(bValidId);
+        trkidx = trkidx(bValidId);
+        
+        trkpts = GetKymoPt(trackingStruct.rgDetect, trkidx', trkt', trackingStruct.LKymo, [trackingStruct.jPath';trackingStruct.iPath']);
+        n2 = round(length(trkt)/2);
+        if ( trackingStruct.jPath(round(trkpts(n2))) < trackingStruct.rgDetect{trkt(n2)}(trkidx(n2),2) )
+            bLeft(i) = 0;
+        end
+    end
+    
+    imfig = figure();
+    for t=1:N
+        %skymoFrame = drawKymo();
+        imFrame = drawTracking();
+
+        %fullFrame = kymoFrame;
+        %fullFrame.cdata = [kymoFrame.cdata imFrame.cdata];
+
+        %movfile = addframe(movfile,fullFrame);
+        [frmimg, map] = frame2im(imFrame);
+        if ( ~isempty(map) )
+            imwrite(frmimg, map, fullfile(root, [fname '_frm_' num2str(t,'%03d') '.tiff']), 'tiff');
+        else
+            imwrite(frmimg, fullfile(root, [fname '_frm_' num2str(t,'%03d') '.tiff']), 'tiff');
+        end
+    end
+    
+    close all;
+    
+    function frm = drawTracking()
+        figure(imfig);
+        im = imread(fullfile(trackingStruct.moviepath, trackingStruct.moviename), t);
+        im = mat2gray(im);
+        imagesc(im.*imwght);colormap(gray);hold on;
+        brighten(0.2);
+        
+        plot(trackingStruct.jPath(1), trackingStruct.iPath(1), 'og');
+        plot(trackingStruct.jPath(end), trackingStruct.iPath(end), 'xg');
+        
+        for i=1:length(tracks)
+            trkt = trackingStruct.globNodeLUT(fullTrackList{tracks(i)},1)';
+            trkidx = trackingStruct.globNodeLUT(fullTrackList{tracks(i)},2)';
+            
+            bValidId = trkt >= times(i,1) & trkt <= times(i,2);
+            trkt = trkt(bValidId);
+            trkidx = trkidx(bValidId);
+            
+            idx = find(trkt == t,1);
+            if ( isempty(idx) )
+                continue;
+            end
+            
+            trkpts = GetKymoPt(trackingStruct.rgDetect, trkidx', trkt', trackingStruct.LKymo, [trackingStruct.jPath';trackingStruct.iPath']);
+            
+            x=trackingStruct.rgDetect{t}(trkidx(idx),2);
+            y=trackingStruct.rgDetect{t}(trkidx(idx),1);
+            
+            bwim = zeros(size(im));
+            r = trackingStruct.rgCC{t}{trkidx(idx)}(:,1);
+            c = trackingStruct.rgCC{t}{trkidx(idx)}(:,2);
+            linidx = sub2ind(size(bwim), r,c);
+            bwim(linidx) = 1;
+            bwbound = bwboundaries(bwim);
+            plot(bwbound{1}(:,2),bwbound{1}(:,1), 'Color',trackColors(i,:), 'LineStyle','-');
+            
+            if ( bLeft(i) )
+                text(x+5,y,['\leftarrow ' num2str(i)], 'FontSize',10, 'HorizontalAlignment','Left', 'Color',trackColors(i,:));
+            else
+                text(x-5,y,[num2str(i) ' \rightarrow'], 'FontSize',10, 'HorizontalAlignment','Right', 'Color',trackColors(i,:));
+            end
+        end
+        
+        %text(tracemin(1),tracemin(2), ['t=' num2str(t)], 'HorizontalAlign','Left', 'VerticalAlign','Top', 'FontSize',8, 'BackgroundColor',[0 0.6 0.5]);
+        text(2,2, ['t=' num2str(t)], 'HorizontalAlign','Left', 'VerticalAlign','Top', 'FontSize',8, 'BackgroundColor',[0 0.6 0.5]);
+        
+        %xlim([tracemin(1) tracemax(1)]);
+        %ylim([tracemin(2) tracemax(2)]);
+        
+        hold off;
+        drawnow;
+        
+        %truesize(imfig, imsize);
+        imsize = size(im);
+        truesize(imfig, 2*imsize);
+        frm = getframe(gca);
+    end
+end
+
+function fullTrackList = BuildFullTrackList(trackingStruct)
+    fullTrackList = trackingStruct.trackList;
+    
+    for i=1:length(trackingStruct.trackList)
+        trkt = trackingStruct.globNodeLUT(trackingStruct.trackList{i},1)';
+        
+        deltat = diff(trkt);
+        sharedidx = find(deltat > 1);
+        tshift = 0;
+        for k =1 :length(sharedidx)
+            srcNode = trackingStruct.trackList{i}(sharedidx(k));
+            destNode = trackingStruct.trackList{i}(sharedidx(k)+1);
+
+            srcPath = trackingStruct.gCellData(trackingStruct.gConnect(srcNode,destNode)).path;
+
+            trkt = [trkt(1:sharedidx(k)+tshift) trackingStruct.globNodeLUT(srcPath(2:deltat(sharedidx(k))),1)' trkt(sharedidx(k)+tshift+1:end)];
+            fullTrackList{i} = [fullTrackList{i}(1:sharedidx(k)+tshift) srcPath(2:deltat(sharedidx(k))) fullTrackList{i}(sharedidx(k)+tshift+1:end)];
+
+            tshift = tshift + deltat(sharedidx(k))-1;
+        end
+    end
+end
+
+function [tracemin, tracemax] = getTrackExtents(trackingStruct, N)
+    tracemin = [Inf Inf];
+    tracemax = [0 0];
+    for t=1:N
+        xmin = min(trackingStruct.rgDetect{t}(:,2));
+        xmax = max(trackingStruct.rgDetect{t}(:,2));
+        ymin = min(trackingStruct.rgDetect{t}(:,1));
+        ymax = max(trackingStruct.rgDetect{t}(:,1));
+        
+        tracemin = [min(tracemin(1), xmin) min(tracemin(2), ymin)];
+        tracemax = [max(tracemax(1), xmax) max(tracemax(2), ymax)];
+    end
+end
\ No newline at end of file
diff --git a/matlab/compareTracking.m b/matlab/compareTracking.m
new file mode 100644
index 0000000000000000000000000000000000000000..3fd17e6766f3f8cc4f7752ec776c0918f79dbcbd
--- /dev/null
+++ b/matlab/compareTracking.m
@@ -0,0 +1,162 @@
+function compareTracking(ds, trackingStruct, extents, selectTrack)
+    gAssignedActual = (ds.gConnectActual > 0);
+    
+    if ( ~exist('selectTrack','var') )
+        selectTrack = [];
+    end
+    
+    cmap = [cool(256); spring(256); summer(256); winter(256)];
+    trackColorsActual = cmap(round(1023*rand(length(ds.tracksActual),1)+1),:);
+    trackColorsTest = cmap(round(1023*rand(length(trackingStruct.trackList),1)+1),:);
+    
+    fig = figure;
+    set(fig, 'WindowScrollWheelFcn', @scrollWheelCallback);
+    set(fig, 'KeyPressFcn', @keyReleaseCallback);
+    
+    t = 1;
+    NFRAMES = length(ds.rgDetect);
+    
+    testlims = extents;
+    actuallims = extents;
+    
+    l2g = zeros(length(ds.rgDetect)+1,1);
+    g2l = zeros(size(ds.gConnectActual,1),2);
+    for i=1:length(ds.rgDetect)
+        l2g(i+1) = l2g(i) + size(ds.rgDetect{i},1);
+        g2l((l2g(i)+1):l2g(i+1),:) = [i*ones(size(ds.rgDetect{i},1),1) cumsum(ones(size(ds.rgDetect{i},1),1))];
+    end
+    
+    %Error types
+    bCompRows = (any(gAssignedActual,2) - any(trackingStruct.gAssigned,2));
+    
+    bMisses = (bCompRows > 0);
+    bAdds = (bCompRows < 0);
+    bGood = (bCompRows == 0);
+    
+    bMistakes = bGood;
+    bMistakes(bGood) = any(gAssignedActual(bGood,:) - trackingStruct.gAssigned(bGood,:),2);
+    
+    dispFrame(testlims, actuallims);
+    
+    
+    function scrollWheelCallback(src, evt)
+        if ( evt.VerticalScrollCount < 0 )
+            t = t - 1;
+        else
+            t = t + 1;
+        end
+        
+        if ( t > NFRAMES )
+            t = NFRAMES;
+        elseif ( t < 1 )
+            t = 1;
+        end
+        
+        h = subplot(1,2,1);
+        actuallims = [xlim(h);ylim(h)];
+        h = subplot(1,2,2);
+        testlims = [xlim(h);ylim(h)];
+        
+        dispFrame(actuallims, testlims);
+    end
+
+    function dispFrame(leftlims, rightlims)
+        i = [];
+        dotradius = 4;
+        traillength = 3;
+        
+        pdfmax = normpdf(0,0,dotradius/3)^2;
+        
+        fakeim = zeros(extents(2,2),extents(1,2));
+        for j=1:size(ds.loc{t},2)
+            fakeim = drawGaussianPt(fakeim, ds.loc{t}(1,j), ds.loc{t}(2,j), 1, dotradius, dotradius);
+        end
+        
+        h = subplot(1,2,1);
+        cla;
+        imshow(0.7*mat2gray(fakeim));colormap(gray);hold on;
+        %imshow(fakeim);colormap(gray);hold on;
+        for j=1:length(ds.axons)
+            axnpts = ppval(ds.axons(j).pp, 0:ds.axons(j).len+2);
+            plot(axnpts(1,:), axnpts(2,:), '-b');
+        end
+        plot(ds.loc{t}(1,:),ds.loc{t}(2,:), '.g');
+        for j=1:length(ds.tracksActual)
+        %for j = 7:7
+            trkt = g2l(ds.tracksActual{j},1);
+            trkidx = g2l(ds.tracksActual{j},2);
+            idx = find((trkt >= t-traillength) & (trkt <= t));
+            if ( ~isempty(idx) )
+                loc = [];
+                for k=1:length(idx)
+                    loc(:,k) = ds.loc{trkt(idx(k))}(:,trkidx(idx(k)));
+                end
+                plot(loc(1,:), loc(2,:), '-', 'Color',trackColorsActual(j,:));
+                if ( trkt(idx(end)) == t )
+%                     plot(loc(1,end), loc(2,end), 'o', 'Color',trackColorsActual(j,:));
+                    plot(loc(1,end), loc(2,end), '.r');
+%                     text(loc(1,end)+4, loc(2,end)+4, num2str(j), 'Color',trackColorsActual(j,:));
+                end
+                
+%                 for k=1:length(idx)
+%                     if ( bMisses(ds.tracksActual{j}(idx(k))) || bMistakes(ds.tracksActual{j}(idx(k))) )
+%                         if ( k < length(idx) )
+%                             errloc = [ds.loc{trkt(idx(k))}(:,trkidx(idx(k))) ds.loc{trkt(idx(k+1))}(:,trkidx(idx(k+1)))];
+%                             plot(errloc(1,:), errloc(2,:), '-r', 'LineWidth',2);
+%                         else
+%                             errloc = ds.loc{trkt(idx(k))}(:,trkidx(idx(k)));
+%                             plot(errloc(1,:), errloc(2,:), 'xr', 'LineWidth',2);
+%                         end
+%                     end
+%                 end
+            end
+        end
+        hold off;
+        xlim(h,leftlims(1,:));
+        ylim(h,leftlims(2,:));
+        
+        h = subplot(1,2,2);
+        cla;
+        imshow(mat2gray(fakeim));colormap(gray);hold on;
+        %imshow(fakeim);colormap(gray);hold on;
+%         for j=1:length(ds.axons)
+%             axnpts = ppval(ds.axons(j).pp, 0:ds.axons(j).len+2);
+%             plot(axnpts(1,:), axnpts(2,:), '-b');
+%         end
+%         for j=1:length(trackingStruct.trackList)
+%             trkt = g2l(trackingStruct.trackList{j},1);
+%             trkidx = g2l(trackingStruct.trackList{j},2);
+%             idx = find((trkt >= t-traillength) & (trkt <= t));
+%             if ( ~isempty(idx) )
+%                 
+%                 loc = [];
+%                 for k=1:length(idx)
+%                     loc(:,k) = ds.loc{trkt(idx(k))}(:,trkidx(idx(k)));
+%                 end
+%                 plot(loc(1,:), loc(2,:), '-', 'Color',trackColorsTest(j,:));
+%                 if ( trkt(idx(end)) == t )
+%                     plot(loc(1,end), loc(2,end), 'o', 'Color',trackColorsTest(j,:));
+%                     text(loc(1,end)+4, loc(2,end)+4, num2str(j), 'Color',trackColorsTest(j,:));
+%                 end
+%                 
+%                 for k=1:length(idx)
+%                     if ( bAdds(trackingStruct.trackList{j}(idx(k))) || bMistakes(trackingStruct.trackList{j}(idx(k))) )
+%                         if ( k < length(idx) )
+%                             errloc = [ds.loc{trkt(idx(k))}(:,trkidx(idx(k))) ds.loc{trkt(idx(k+1))}(:,trkidx(idx(k+1)))];
+%                             plot(errloc(1,:), errloc(2,:), '-r', 'LineWidth',2);
+%                         else
+%                             errloc = ds.loc{trkt(idx(k))}(:,trkidx(idx(k)));
+%                             plot(errloc(1,:), errloc(2,:), 'xr', 'LineWidth',2);
+%                         end
+%                     end
+%                 end
+%             end
+%         end
+        hold off;
+        xlim(h,rightlims(1,:));
+        ylim(h,rightlims(2,:));
+        
+        title(num2str(t));
+        
+    end
+end
\ No newline at end of file
diff --git a/matlab/drawGaussianPt.m b/matlab/drawGaussianPt.m
new file mode 100644
index 0000000000000000000000000000000000000000..8cbf67aae69a8149d7fd8a7da3fa7fcd36b66617
--- /dev/null
+++ b/matlab/drawGaussianPt.m
@@ -0,0 +1,16 @@
+function im = drawGaussianPt(im, x, y, intensity, xRadius, yRadius)
+    grdx = round(x-xRadius-1):round(x+xRadius+1);
+    validx = (grdx >= 1 & grdx <= size(im,2));
+    grdx = grdx(validx);
+
+    grdy = round(y-yRadius-1):round(y+yRadius+1);
+    validy = (grdy >= 1 & grdy <= size(im,1));
+    grdy = grdy(validy);
+    
+    pdfmax = normpdf(0,0,xRadius/3)*normpdf(0,0,yRadius/3);
+
+    [X,Y] = meshgrid(grdx, grdy);
+    alpha = normpdf(X,x,xRadius/3).*normpdf(Y,y,yRadius/3) / pdfmax;
+    
+    im(grdy,grdx) = ((1-alpha).*im(grdy,grdx) + alpha*intensity);
+end
\ No newline at end of file
diff --git a/matlab/errSurfaces.m b/matlab/errSurfaces.m
new file mode 100644
index 0000000000000000000000000000000000000000..7fdd9c327254e17976af1c831c1df6c3de1f70ce
--- /dev/null
+++ b/matlab/errSurfaces.m
@@ -0,0 +1,16 @@
+function errSurfaces(err, gridx, gridy, interppts, cmap)
+    if ( ~exist('cmap','var') )
+        cmap = jet(1024);
+    end
+    
+    [X,Y] = meshgrid(gridx, gridy);
+    [XI,YI] = meshgrid(linspace(gridx(1), gridx(end), interppts), linspace(gridy(1), gridy(end), interppts));
+    mederr = nanmedian(err,3);
+    interpmederr = interp2(X,Y,mederr,XI,YI, 'bicubic');
+
+    hc = round((length(cmap)-1)*(mederr - min(mederr(:)))/(max(mederr(:))-min(mederr(:)))) + 1;
+    hci = round((length(cmap)-1)*(interpmederr - min(mederr(:)))/(max(mederr(:))-min(mederr(:)))) + 1;
+    
+    figure;surf(X,Y,mederr,hc);
+    figure;surf(XI,YI,interpmederr,hci);
+end
\ No newline at end of file
diff --git a/matlab/getTrackStats.m b/matlab/getTrackStats.m
new file mode 100644
index 0000000000000000000000000000000000000000..4c41f15d8a1627e7f2166c93dac658f1f1628dea
--- /dev/null
+++ b/matlab/getTrackStats.m
@@ -0,0 +1,8 @@
+function vs = getTrackStats(trackingStruct, trackIdx, names, stats)
+    dataidx = find(strcmp(names,trackingStruct.fullname));
+    if ( isempty(dataidx) )
+        return;
+    end
+    
+    vs = stats{dataidx}.vs{trackIdx};
+end
\ No newline at end of file
diff --git a/matlab/makePhantom.m b/matlab/makePhantom.m
new file mode 100644
index 0000000000000000000000000000000000000000..9ed569619fe813703d115b916fb9386cbfd39d18
--- /dev/null
+++ b/matlab/makePhantom.m
@@ -0,0 +1,524 @@
+function [ds, axons, images] = makePhantom(xlen, ylen, varargin)
+
+    p = inputParser;
+    p.StructExpand = false;
+    
+    p.addRequired('xlen');
+    p.addRequired('ylen');
+    
+    p.addParamValue('numAxons',0);
+    p.addParamValue('axons',[]);
+    p.addParamValue('numDetections',100);
+    p.addParamValue('numFrames',120);
+    
+    p.addParamValue('speedRange',[0 10]);
+	p.addParamValue('speedVarTime',[3 5]);
+    p.addParamValue('speedVarProb',0.1);
+    
+    p.addParamValue('axonWidth',8);
+    p.addParamValue('paramVarTime',[7 10]);
+    p.addParamValue('paramVarProb',0.05);
+    
+    p.addParamValue('falseDetections',0);
+    
+    p.addParamValue('frameTime',0.650);
+    p.addParamValue('exposureTime',0.150);
+    
+    p.addParamValue('maxCurve',20);
+    
+    p.addParamValue('dotRadius',6);
+    
+    p.parse(xlen, ylen, varargin{:});
+    
+    args = p.Results;
+    
+    tic;
+    
+    expratio = args.exposureTime / args.frameTime;
+
+    %dot size?
+
+    extents = [1 xlen;1 ylen];
+
+    numframes = args.numFrames;
+    numdetections = args.numDetections;
+    numaxons = args.numAxons;
+    
+    axonwidth = args.axonWidth;
+
+    falsedetpad = 3*args.falseDetections;
+    gConnectActual = sparse([], [], [], numframes*(numdetections+falsedetpad), numframes*(numdetections+falsedetpad), numframes*numdetections);
+    
+    % Generate initial detections and associated parameters
+    detLoc{1} = [xlen;ylen]*ones(1,numdetections).*rand(2,numdetections);
+    if ( numaxons > 0 )
+        if ( ~isempty(args.axons) )
+            axons = args.axons;
+        else
+            axons = MakeAxons(extents, args.maxCurve);
+        end
+        detParam{1} = [ceil((numaxons-eps)*rand(1,numdetections) + eps); 2*axonwidth*rand(1,numdetections)-axonwidth; rand(1,numdetections); 2*round(rand(1,numdetections))-1];
+        detParam{1}(3,:) = detParam{1}(3,:).*[axons(detParam{1}(1,:)).len];
+        
+        for j=1:numdetections
+            detLoc{1}(:,j) = GetAxonLoc(detParam{1}(:,j));
+            bValid = ((detLoc{1}(1,j) >= extents(1,1) & detLoc{1}(1,j) <= extents(1,2)) & (detLoc{1}(2,j) >= extents(2,1) & detLoc{1}(2,j) <= extents(2,2)));
+            checkiter = 0;
+            while ( ~bValid )
+                if ( checkiter > 200 )
+                    error('Iteration limit exceeded for valid axon start point');
+                end
+                detParam{1}(3,j) = rand()*axons(detParam{1}(1,j)).len;
+                detLoc{1}(:,j) = GetAxonLoc(detParam{1}(:,j));
+                bValid = ((detLoc{1}(1,j) >= extents(1,1) & detLoc{1}(1,j) <= extents(1,2)) & (detLoc{1}(2,j) >= extents(2,1) & detLoc{1}(2,j) <= extents(2,2)));
+                
+                checkiter = checkiter + 1;
+            end
+        end
+    else
+        detParam{1} = 2*pi*rand(1,numdetections);
+    end
+    
+    detSpeed{1} = (args.speedRange(2)-args.speedRange(1))*rand(1,numdetections) + args.speedRange(1);
+    %detSpeed{1} = 5*ones(1,numdetections);
+    
+    speedTarget = detSpeed{1};
+    if ( numaxons > 0 )
+        paramTarget = detParam{1}(2,:);
+    else
+        paramTarget = detParam{1};
+    end
+    
+    speedTime = zeros(1,numdetections);
+    paramTime = zeros(1,numdetections);
+    
+    [detFalse{1}, numfalsedet] = AddFalseDetections();
+    
+    totaldetections(1,1) = 0;
+    totaldetections(2,1) = numdetections + numfalsedet;
+    
+    g2l((totaldetections(1,1)+1):totaldetections(2,1),:) = [ones((numdetections+numfalsedet),1) cumsum(ones((numdetections+numfalsedet),1))];
+    
+    bNew = boolean(zeros(1,numdetections));
+    for i=2:numframes
+        %Vary speed
+        speedTime(bNew) = 0;
+        speedTarget(bNew) = detSpeed{i-1}(bNew);
+        [speedTarget,speedTime,deltaSpeed] = UpdateVariation(speedTarget, speedTime, detSpeed{i-1}, args.speedVarProb, [-3 3], args.speedVarTime, 0);
+        speedTarget(speedTarget < args.speedRange(1)) = args.speedRange(1);
+        speedTarget(speedTarget > args.speedRange(2)) = args.speedRange(2);
+        
+        %Vary lane or angle (parameters)
+        paramTime(bNew) = 0;
+        if ( numaxons > 0 )
+            paramTarget(bNew) = detParam{i-1}(2,bNew);
+            [paramTarget,paramTime,deltaParam] = UpdateVariation(paramTarget, paramTime, detParam{i-1}(2,:), args.paramVarProb, [-axonwidth axonwidth], args.paramVarTime, 1);
+        else
+            paramTarget(bNew) = detParam{i-1}(bNew);
+            [paramTarget,paramTime,deltaParam] = UpdateVariation(paramTarget, paramTime, detParam{i-1}, args.paramVarProb, [-(2*pi/8) (2*pi/8)], args.paramVarTime, 0);
+        end
+        
+        [detLoc{i} detParam{i} detSpeed{i}] = MoveDetections(detLoc{i-1}, detParam{i-1}, detSpeed{i-1}+deltaSpeed, deltaParam, extents);
+        [detLoc{i} detParam{i} detSpeed{i} bNew] = AddDetections(detLoc{i}, detParam{i}, detSpeed{i}, extents);
+        
+        [detFalse{i}, numfalsedet] = AddFalseDetections();
+        
+        totaldetections(i+1,1) = totaldetections(i,1) + size(detLoc{i},2) + numfalsedet;
+        g2l((totaldetections(i,1)+1):totaldetections(i+1,1),:) = [i*ones((numdetections+numfalsedet),1) cumsum(ones((numdetections+numfalsedet),1))];
+    end
+    
+    gConnectActual = gConnectActual(1:totaldetections(end),1:totaldetections(end));
+    
+    for i=1:numframes
+        detLoc{i} = [detLoc{i} detFalse{i}];
+    end
+    
+    dotradius = args.dotRadius;
+    tracksActual = {};
+    for i=1:numframes-1
+        for j=1:size(detLoc{i},2)
+            inidx = j + totaldetections(i);
+            outidx = find(gConnectActual(inidx,:),1);
+            if ( ~isempty(outidx) )
+                if ( gConnectActual(inidx,outidx) < 0 )
+                    gConnectActual(inidx,outidx) = length(tracksActual)+1;
+                    tracksActual{gConnectActual(inidx,outidx),1} = inidx;
+                end
+                trackidx = gConnectActual(inidx,outidx);
+                tracksActual{trackidx,1} = [tracksActual{trackidx} outidx];
+                
+                nextidx = find(gConnectActual(outidx,:),1);
+                if ( ~isempty(nextidx) )
+                    gConnectActual(outidx,nextidx) = trackidx;
+                end
+            end
+        end
+    end
+    
+%     detections = [];
+%     for i=1:numframes
+%         ddx = (ones(size(detLoc{i},2),1)*detLoc{i}(1,:) - ((ones(size(detLoc{i},2),1)*detLoc{i}(1,:))')).^2;
+%         ddy = (ones(size(detLoc{i},2),1)*detLoc{i}(2,:) - ((ones(size(detLoc{i},2),1)*detLoc{i}(2,:))')).^2;
+%         dd = sqrt(ddx+ddy);
+%         dd(boolean(tril(ones(size(dd))))) = Inf;
+%         [r,c] = find(dd < dotradius);
+%         detections(i).actualmap = num2cell(setdiff([1:size(detLoc{i},2)]',union(r,c)));
+%         detections(i).loc = detLoc{i}(:,[detections(i).actualmap{:}]);
+%         while( ~isempty(r) )
+%             mergepts = [r(1) c(1)];
+%             r = r(2:end);
+%             c = c(2:end);
+%             bchkmerge = ismember([r c], mergepts);
+%             if ( ~isempty(bchkmerge) )
+%                 bchkmerge = bchkmerge(:,1) | bchkmerge(:,2);
+%             end
+%             while ( any(bchkmerge) )
+%                 mergepts = unique([mergepts r(bchkmerge)' c(bchkmerge)']);
+%                 r = r(~bchkmerge);
+%                 c = c(~bchkmerge);
+%                 bchkmerge = ismember([r c], mergepts);
+%                 if ( ~isempty(bchkmerge) )
+%                     bchkmerge = bchkmerge(:,1) | bchkmerge(:,2);
+%                 end
+%             end
+%             centroid = mean(detLoc{i}(:,mergepts),2);
+%             detections(i).actualmap{end+1} = mergepts;
+%             detections(i).loc(:,end+1) = centroid;
+%         end
+%     end
+
+	toc;
+    
+    
+    cmap = hsv(1024);
+    trackColors = zeros(length(tracksActual),3);
+    for i=1:length(tracksActual)
+    	trackColors(i,:) = cmap(round(rand()*1023)+1,:);
+    end
+    
+    images = [];
+%     for i=1:numframes
+%         fakeim = zeros(ylen,xlen);
+%         for j=1:size(detLoc{i},2)
+%             grdx = round(detLoc{i}(1,j)-dotradius-1):round(detLoc{i}(1,j)+dotradius+1);
+%             validx = (grdx >= 1 & grdx <= xlen);
+%             grdx = grdx(validx);
+%             
+%             grdy = round(detLoc{i}(2,j)-dotradius-1):round(detLoc{i}(2,j)+dotradius+1);
+%             validy = (grdy >= 1 & grdy <= ylen);
+%             grdy = grdy(validy);
+%             
+%             [X,Y] = meshgrid(grdx, grdy);
+%             %fakeim(grdy,grdx) = max(fakeim(grdy,grdx), normpdf(X,detLoc{i}(1,j),dotradius/3).*normpdf(Y,detLoc{i}(2,j),dotradius/3));
+%             fakeim(grdy,grdx) = fakeim(grdy,grdx) + normpdf(X,detLoc{i}(1,j),dotradius/3).*normpdf(Y,detLoc{i}(2,j),dotradius/3);
+%         end
+%         cla;
+%         images(:,:,i) = mat2gray(fakeim);
+%         imshow(fakeim);colormap(gray);hold on;
+%         for j=1:numaxons
+%             axnpts = ppval(axons(j).pp, 0:axons(j).len+2);
+%             plot(axnpts(1,:), axnpts(2,:), '-b');
+%         end
+%         
+% %         for j=1:length(tracksActual)
+% %             trkt = g2l(tracksActual{j},1);
+% %             trkidx = g2l(tracksActual{j},2);
+% %             idx = find(trkt == i,1);
+% %             if ( ~isempty(idx) )
+% %                 loc = detLoc{i}(:,trkidx(idx));
+% %                 plot(loc(1), loc(2), 'o', 'Color',trackColors(j,:));
+% %                 text(loc(1)+4, loc(2)+4, num2str(j), 'Color',trackColors(j,:));
+% %             end
+% %         end
+%     end
+    
+    ds.axons = axons;
+    ds.loc = detLoc;
+    ds.param = detParam;
+    ds.speed = detSpeed;
+    
+	ds.gConnectActual = gConnectActual;
+    ds.tracksActual = tracksActual;
+    
+    ds.rgCC = {};
+    ds.rgDetect = {};
+    ds.rgMotionBlur = {};
+    
+    tic;
+    
+    for i=1:numframes
+        for j=1:size(detLoc{i},2)
+            ds.rgDetect{i}(j,:) = [round(detLoc{i}(1,j)) round(detLoc{i}(2,j)) ones(1,25)];
+            
+            grdx = round(detLoc{i}(1,j)-dotradius-1):round(detLoc{i}(1,j)+dotradius+1);
+            validx = (grdx >= 1 & grdx <= xlen);
+            grdx = grdx(validx);
+
+            grdy = round(detLoc{i}(2,j)-dotradius-1):round(detLoc{i}(2,j)+dotradius+1);
+            validy = (grdy >= 1 & grdy <= ylen);
+            grdy = grdy(validy);
+
+            [X,Y] = meshgrid(grdx, grdy);
+            bFG = (((X-detLoc{i}(1,j)).^2 + (Y-detLoc{i}(2,j)).^2) <= dotradius.^2);
+            r = X(bFG);
+            c = Y(bFG);
+            
+            ds.rgCC{i}{j} = [r,c];
+            if ( i < numframes )
+                ds.rgMotionBlur{i}{j} = [];
+                for nxtidx=1:size(detLoc{i+1},2)
+                    if ( sqrt(sum((detLoc{i+1}(:,nxtidx)-detLoc{i}(:,j)).^2)) <= 20 )
+                        ds.rgMotionBlur{i}{j}(1:2,end+1) = [totaldetections(i+1)+nxtidx;0];
+                    end
+                end
+            end
+        end
+    end
+    
+    toc;
+    
+    
+    
+    % Helper functions
+    function [loc param speed bValid] = MoveDetections(inLoc, inParam, inSpeed, deltaParam, extents)
+        param = inParam;
+        speed = inSpeed;
+        
+        if ( numaxons > 0 )
+            bRailParam = (deltaParam > abs(inSpeed));
+            deltaParam(bRailParam) = sign(deltaParam(bRailParam)).*inSpeed(bRailParam);
+            
+            param(2,:) = inParam(2,:) + deltaParam;
+            arclen = sqrt(deltaParam.^2);
+            testlen = inSpeed;
+            
+            inc = param(4,:) .* (inSpeed / 20);
+            
+            loc = inLoc;
+            for k=1:size(param,2)
+                loc(:,k) = GetAxonLoc(param(:,k));
+            end
+            
+            bIncArc = arclen < testlen;
+            oldLoc = loc;
+            while ( any(bIncArc) )
+                for k=1:length(bIncArc)
+                    if ( ~bIncArc(k) )
+                        continue;
+                    end
+                    param(3,k) = param(3,k) + inc(k);
+                    
+                    loc(:,k) = GetAxonLoc(param(:,k));
+                    newlen = sqrt((loc(1,k) - oldLoc(1,k)).^2 + (loc(2,k) - oldLoc(2,k)).^2);
+                    arclen(k) = arclen(k) + newlen;
+                end
+                
+                bIncArc = (arclen < testlen);
+                oldLoc = loc;
+            end
+        else
+            param = param + deltaParam;
+            loc = inLoc + (ones(2,1)*inSpeed).*[cos(param);sin(param)];
+        end
+        
+        bValid = ((loc(1,:) >= extents(1,1) & loc(1,:) <= extents(1,2)) & (loc(2,:) >= extents(2,1) & loc(2,:) <= extents(2,2)));
+
+        oldidx = find(bValid);
+        newidx = 1:nnz(bValid);
+        linidx = sub2ind(size(gConnectActual), (oldidx + totaldetections(i-1)), (newidx + totaldetections(i)));
+        gConnectActual(linidx) = -1;
+
+        if ( numaxons > 0 )
+            param = param(:,bValid);
+        else
+            param = param(bValid);
+        end
+        speed = speed(bValid);
+        loc = loc(:,bValid);
+    end
+
+    function [loc param speed bNew] = AddDetections(inLoc, inParam, inSpeed, extents)
+        numnew = numdetections - size(inLoc,2);
+        
+        loc = inLoc;
+        param = inParam;
+        speed = inSpeed;
+        
+        edge = [2:(xlen-1) xlen*ones(1,ylen) (xlen-1):-1:2 ones(1,ylen); ones(1,xlen-2) 1:ylen ylen*ones(1,xlen-2) ylen:-1:1];
+        
+        if ( numaxons > 0 )
+            newparams = [ceil((numaxons-eps)*rand(1,numnew) + eps); 2*axonwidth*rand(1,numnew)-axonwidth; zeros(1,numnew); 2*round(rand(1,numnew))-1];
+            for k=1:numnew
+                if ( newparams(4,k) < 0 )
+                    newparams(3,k) = axons(newparams(1,k)).len;
+                end
+            end
+            param = [param newparams];
+            speed = [speed 10*rand(1,numnew)];
+            oldpidx = size(inLoc,2);
+            for k=1:numnew
+                param(3,oldpidx+k) = FindStart(param(:,oldpidx+k), extents);
+                loc = [loc GetAxonLoc(param(:,oldpidx+k))];
+            end
+        else
+            idxs = round((size(edge,2)-1)*rand(1,numnew)) + 1;
+            loc = [loc edge(:,idxs)];
+            param = [param 2*pi*rand(1,numnew)];
+            speed = [speed 10*rand(1,numnew)];
+        end
+        
+        bNew = boolean([zeros(1,size(inLoc,2)) ones(1,numnew)]);
+    end
+
+    function [loc numfalse] = AddFalseDetections()
+        loc = [];
+        stdvfalse = args.falseDetections / 4;
+        numfalse = round(stdvfalse*randn(1,1) + args.falseDetections);
+        
+        if ( args.falseDetections > 0 && numfalse )
+            if ( numaxons > 0 )
+                parms = [ceil((numaxons-eps)*rand(1,numfalse) + eps); 2*axonwidth*rand(1,numfalse)-axonwidth; rand(1,numfalse); 2*round(rand(1,numfalse))-1];
+                parms(3,:) = parms(3,:).*[axons(parms(1,:)).len];
+
+                for k=1:numfalse
+                    loc(:,k) = GetAxonLoc(parms(:,k));
+                    bOnScreen = ((loc(1,k) >= extents(1,1) & loc(1,k) <= extents(1,2)) & (loc(2,k) >= extents(2,1) & loc(2,k) <= extents(2,2)));
+                    checkiter = 0;
+                    while ( ~bOnScreen )
+                        if ( checkiter > 200 )
+                            error('Iteration limit exceeded for valid axon start point');
+                        end
+                        parms(3,k) = rand()*axons(parms(1,k)).len;
+                        loc(:,k) = GetAxonLoc(parms(:,k));
+                        bOnScreen = ((loc(1,k) >= extents(1,1) & loc(1,k) <= extents(1,2)) & (loc(2,k) >= extents(2,1) & loc(2,k) <= extents(2,2)));
+
+                        checkiter = checkiter + 1;
+                    end
+                end
+            else
+                loc = [extents(1,2);extents(2,2)]*ones(1,numfalse).*rand(2,numfalse);
+            end
+        end
+    end
+
+    function [target time delta] = UpdateVariation(inTarget, inTime, inCurrent, pVariation, varRange, timeRange, bStaticVariation)
+        delta = zeros(1,length(inCurrent));
+        
+        target = inTarget;
+        time = max(inTime - 1, zeros(size(inTime)));
+        
+        bUpdateVaried = ((inTarget ~= inCurrent) & (inTime > 0));
+        bStopVaried = ((inTarget ~= inCurrent) & (inTime == 0));
+        
+        delta(bUpdateVaried) = (inTarget(bUpdateVaried) - inCurrent(bUpdateVaried)) ./ inTime(bUpdateVaried);
+        target(bStopVaried) = inCurrent(bStopVaried);
+        
+        bNewVaried = (rand(1,length(inCurrent)) < pVariation);
+        
+        time(bNewVaried) = floor((timeRange(2) - timeRange(1)+1-eps)*rand(1,nnz(bNewVaried))) + timeRange(1);
+        if ( bStaticVariation )
+            target(bNewVaried) = (varRange(2)-varRange(1))*rand(1,nnz(bNewVaried)) + varRange(1);
+        else
+            target(bNewVaried) = inCurrent(bNewVaried) + ((varRange(2)-varRange(1))*rand(1,nnz(bNewVaried)) + varRange(1));
+        end
+        
+        delta(bNewVaried) = (target(bNewVaried) - inCurrent(bNewVaried)) ./ time(bNewVaried);
+    end
+
+    function arclen = FindStart(param, extents)
+        widthtol = 50;
+        arcrange = [-widthtol axons(param(1)).len+widthtol];
+        arclen = param(3);
+        tstloc = GetAxonLoc(param, arclen);
+        edgedist = [tstloc(1)-extents(1,1) extents(1,2)-tstloc(1) tstloc(2)-extents(2,1) extents(2,2)-tstloc(2)];
+        [mindist sideidx] = min(abs(edgedist));
+        
+        if ( any(edgedist < 0) )
+            stepinc = param(4)*0.1;
+        else
+            stepinc = -param(4)*0.1;
+        end
+        
+        oldstate = any(edgedist < 0);
+        while ( (mindist > 0.5) || any(edgedist < 0) )
+            arclen = arclen + stepinc;
+            tstloc = GetAxonLoc(param, arclen);
+            edgedist = [tstloc(1)-extents(1,1) extents(1,2)-tstloc(1) tstloc(2)-extents(2,1) extents(2,2)-tstloc(2)];
+            [mindist sideidx] = min(abs(edgedist(:)));
+            
+            if ( (mindist > 0.5) && oldstate ~= any(edgedist < 0) )
+                stepinc = -(stepinc / 2);
+            end
+            
+            oldstate = any(edgedist < 0);
+            
+            if ( arclen < arcrange(1) || arclen > arcrange(2) )
+                break;
+            end
+        end
+        
+    end
+
+    function [loc, tan, nrm] = GetAxonLoc(param, arclen)
+        if ( ~exist('arclen','var') )
+            arclen = param(3);
+        end
+        sloc = ppval(axons(param(1)).pp, arclen);
+        tan = ppval(axons(param(1)).dpp, arclen);
+        nrm = [tan(2); -tan(1)] / norm(tan);
+        loc = sloc+param(2)*nrm;
+    end
+
+    function outAxons = MakeAxons(extents, maxcurve)
+        outAxons = struct();
+        extrapts = floor((3-eps)*rand(1,numaxons));
+        
+        edge = [2:(xlen-1) xlen*ones(1,ylen) (xlen-1):-1:2 ones(1,ylen); ones(1,xlen-2) 1:ylen ylen*ones(1,xlen-2) ylen:-1:1];
+        sides = [1 xlen xlen+ylen+1 2*xlen+ylen+1 length(edge)+1];
+        
+        axsides = zeros(2,numaxons);
+        axsides(1,:) = floor((4-eps)*rand(1,numaxons));
+        axsides(2,:) = mod(axsides(1,:) + floor((3-eps)*rand(1,numaxons))+1, 4);
+        axsides = axsides + 1;
+        
+        tstextents = extents;
+        tstextents(:,1) = tstextents(:,1) + axonwidth;
+        tstextents(:,2) = tstextents(:,2) - axonwidth;
+        
+        for k=1:numaxons
+            edgeidx = [round((sides(axsides(1,k)+1)-sides(axsides(1,k)))*rand())+sides(axsides(1,k)) round((sides(axsides(2,k)+1)-sides(axsides(2,k)))*rand())+sides(axsides(2,k))];
+            pts = edge(:,edgeidx);
+            
+            while ( norm(pts(:,2) - pts(:,1)) < 20.0 )
+                edgeidx = [round((sides(axsides(1,k)+1)-sides(axsides(1,k)))*rand())+sides(axsides(1,k)) round((sides(axsides(2,k)+1)-sides(axsides(2,k)))*rand())+sides(axsides(2,k))];
+                pts = edge(:,edgeidx);
+            end
+            
+            if ( extrapts(k) > 0)
+                curveoffset = cumsum(ones(1,extrapts(k)))+0.2;
+                curverat = 0.6*rand(1,extrapts(k)) + curveoffset;
+                curverat = ((1/extrapts(k))*curverat + (1-(1/extrapts(k)))*curverat(1)) * (1/3);
+                
+                curvyness = 2*maxcurve*rand(1,extrapts(k))-maxcurve;
+                
+                curvetan = (pts(:,2)-pts(:,1));
+                curvenrm = [curvetan(2); -curvetan(1)] / norm(curvetan);
+                curvepts = curvetan*curverat + pts(:,1)*ones(1,extrapts(k)) + curvenrm*curvyness;
+                
+                curvepts(1,:) = max(curvepts(1,:), tstextents(1,1)*ones(1,extrapts(k)));
+                curvepts(1,:) = min(curvepts(1,:), tstextents(1,2)*ones(1,extrapts(k)));
+                curvepts(2,:) = max(curvepts(2,:), tstextents(2,1)*ones(1,extrapts(k)));
+                curvepts(2,:) = min(curvepts(2,:), tstextents(2,2)*ones(1,extrapts(k)));
+                
+                pts = [pts(:,1) curvepts pts(:,2)];
+            end
+            
+            apprxlen = sqrt(sum(sum(((pts(:,2:end)-pts(:,1:end-1)).^2))));
+            
+            outAxons(k).pp = pchip(0:(apprxlen/(length(pts)-1)):apprxlen, pts);
+            [breaks,coefs,l,kp,d] = unmkpp(outAxons(k).pp);
+            outAxons(k).dpp = mkpp(breaks,repmat(kp-1:-1:1,d*l,1).*coefs(:,1:kp-1),d);
+            outAxons(k).len = apprxlen;
+        end
+    end
+end
\ No newline at end of file
diff --git a/matlab/makePhantomKymo.m b/matlab/makePhantomKymo.m
new file mode 100644
index 0000000000000000000000000000000000000000..9848abbe30e732699945d80c03d3a316adb642e9
--- /dev/null
+++ b/matlab/makePhantomKymo.m
@@ -0,0 +1,147 @@
+function kymos = makePhantomKymo(ds, trackingStruct)
+    kymos = [];
+    
+    cmap = [cool(512); winter(512)];
+    trackColorsTest = cmap(round(1023*rand(length(trackingStruct.trackList),1)+1),:);
+    
+    l2g = zeros(length(ds.rgDetect)+1,1);
+    g2l = zeros(size(ds.gConnectActual,1),2);
+    for j=1:length(ds.rgDetect)
+        l2g(j+1) = l2g(j) + size(ds.rgDetect{j},1);
+        g2l((l2g(j)+1):l2g(j+1),:) = [j*ones(size(ds.rgDetect{j},1),1) cumsum(ones(size(ds.rgDetect{j},1),1))];
+    end
+    
+    numFrames = length(ds.rgDetect);
+    
+    lineThickness = 2.5;
+    ptThickness = 14;
+    
+    for axIdx = 1:length(ds.axons)
+        axonLen = floor(ds.axons(axIdx).len);
+        
+        actfig = figure();
+        dotfig = figure();
+        
+        [allKymoPts, noiseKymoPts] = getAllKymoPts(ds, ds.axons(axIdx));
+        
+        figure(actfig);hold on;
+        plot(noiseKymoPts(:,1), noiseKymoPts(:,2), '.', 'MarkerSize',ptThickness, 'Color',[0.25,0.25,0.25]);
+
+        figure(dotfig);hold on;
+        plot(noiseKymoPts(:,1), noiseKymoPts(:,2), '.', 'MarkerSize',ptThickness, 'Color',[0.25,0.25,0.25]);
+        
+        plotActualTracks(actfig, ds, g2l, axIdx, '.k', lineThickness, ptThickness);
+        plotActualTracks(dotfig, ds, g2l, axIdx, '-k', lineThickness, ptThickness);
+        
+        plotTrackingResults(actfig, ds, g2l, allKymoPts, trackingStruct, trackColorsTest, 5);
+        plotTrackingResults(dotfig, ds, g2l, allKymoPts, trackingStruct, trackColorsTest, 5);
+        
+        figure(dotfig);hold on;
+        xlim([1 numFrames]);
+        ylim([0 axonLen]);
+
+        figure(actfig);hold on;
+        xlim([1 numFrames]);
+        ylim([0 axonLen]);
+    end
+end
+
+function [allKymoPts noiseKymoPts] = getAllKymoPts(ds, axon)
+    numFrames = length(ds.rgDetect);
+    
+    axonLen = floor(axon.len);
+
+    axonPts = zeros(2,axonLen+1);
+    for i=1:(axonLen+1)
+        axonPts(:,i) = ppval(axon.pp, axon.len*((i-1)/axonLen));
+    end
+    
+    allKymoPts = [];
+    noiseKymoPts = [];
+    for t=1:numFrames
+        numTotal = length(ds.loc{t}(1,:));
+        numActual = length(ds.param{t}(1,:));
+        
+        % True Detections
+        allKymoPts{t} = ds.param{t}(3,:)';
+        
+        % Axon location of false detections
+        noisePts = ds.loc{t}(:,(numActual+1):end)';
+        [dist, segparam, segidx] = distToPath(noisePts(:,1), noisePts(:,2), axonPts);
+        
+        allArcLen = axon.len*((segidx-1)/axonLen) + segparam;
+        
+        bOnAxon = (dist <= 8);
+        allArcLen(~bOnAxon) = nan;
+        noiseArcLen = allArcLen(bOnAxon);
+        
+        allKymoPts{t} = [allKymoPts{t}; allArcLen];
+        
+        noiseKymoPts = [noiseKymoPts; t*ones(length(noiseArcLen),1) noiseArcLen];
+    end
+end
+
+function plotActualTracks(fig, ds, g2l, axIdx, linespec, linewidth, ptwidth)
+    figure(fig);hold on;
+    
+    numTracks = length(ds.tracksActual);
+    
+    for i=1:numTracks
+        trkidx = g2l(ds.tracksActual{i},2)';
+        trkt = g2l(ds.tracksActual{i},1)';
+
+        if ( ds.param{trkt(1)}(1,trkidx(1)) ~= axIdx )
+            continue;
+        end
+
+        trkVal = zeros(1,length(trkt));
+        for j=1:length(trkt)
+            trkVal(j) = ds.param{trkt(j)}(3,trkidx(j));
+        end
+
+        plot(trkt, trkVal, linespec, 'LineWidth',linewidth, 'MarkerSize',ptwidth, 'LineSmoothing','on');
+    end
+    hold off;
+end
+
+function plotTrackingResults(fig, ds, g2l, allKymoPts, trackingStruct, trackColors, minlength)
+    figure(fig);hold on;
+    
+    gAssignedActual = ds.gConnectActual > 0;
+    gAssignedTracking = trackingStruct.gAssigned;
+    
+    numTracks = length(trackingStruct.trackList);
+    
+    for i=1:numTracks
+        trkidx = g2l(trackingStruct.trackList{i},2)';
+        trkt = g2l(trackingStruct.trackList{i},1)';
+        
+        if ( length(trkt) < minlength )
+            continue;
+        end
+        
+        trkPts = zeros(size(trkt));
+        for j=1:length(trkt)
+            trkPts(j) = allKymoPts{trkt(j)}(trkidx(j));
+        end
+
+        plot(trkt, trkPts, '-', 'Color',trackColors(i,:), 'LineWidth',1);
+    end
+    
+    bErr =  full(any((gAssignedActual - gAssignedTracking),2));
+    comprows = Inf*ones(size(bErr));
+    
+    comprows(bErr) = full(any(gAssignedActual(bErr,:),2) - any(gAssignedTracking(bErr,:),2));
+    edgeErrs = find(comprows == 0);
+    
+    errt = g2l(edgeErrs,1)';
+    erridx = g2l(edgeErrs,2)';
+    
+    errPts = zeros(size(erridx));
+    for j=1:length(errt)
+        errPts(j) = allKymoPts{errt(j)}(erridx(j));
+    end
+    plot(errt,errPts,'or', 'MarkerSize',6);
+    
+    hold off;
+end
diff --git a/matlab/makePhantomMovie.m b/matlab/makePhantomMovie.m
new file mode 100644
index 0000000000000000000000000000000000000000..ce95a4dda908d56644339b16725c27f74961db56
--- /dev/null
+++ b/matlab/makePhantomMovie.m
@@ -0,0 +1,152 @@
+function makePhantomMovie(fname, ds, trackingStruct, extents, zoom, fps)
+    gAssignedActual = (ds.gConnectActual > 0);
+    
+    l2g = zeros(length(ds.rgDetect)+1,1);
+    g2l = zeros(size(ds.gConnectActual,1),2);
+    for i=1:length(ds.rgDetect)
+        l2g(i+1) = l2g(i) + size(ds.rgDetect{i},1);
+        g2l((l2g(i)+1):l2g(i+1),:) = [i*ones(size(ds.rgDetect{i},1),1) cumsum(ones(size(ds.rgDetect{i},1),1))];
+    end
+    
+    cmap = [cool(256); spring(256); summer(256); winter(256)];
+    trackColorsActual = cmap(round(1023*rand(length(ds.tracksActual),1)+1),:);
+    trackColorsTest = cmap(round(1023*rand(length(trackingStruct.trackList),1)+1),:);
+    
+    zoomsize = [zoom(1,2)-zoom(1,1)+1 zoom(2,2)-zoom(2,1)+1];
+    
+    axExtents = getAxonExtents(ds, extents, 20);
+    
+    imsize = [axExtents(1,2)-axExtents(1,1) axExtents(2,2)-axExtents(2,1)];
+    if ( imsize(2) < 500 )
+        imsize = imsize * 2;
+    elseif ( imsize(2) < 600 )
+        imsize = round(imsize * 1.5);
+        imsize = imsize + mod(imsize(2),2);
+    end
+    
+    zoomratio = imsize(2) / zoomsize(2);
+    
+    imfig = figure();
+    zoomfig = figure();
+    
+    numFrames = length(ds.rgDetect);
+    
+    movfile = [];
+    
+    try
+        movfile = avifile(fname, 'Compression','None', 'fps',fps);
+        
+        for t=1:numFrames
+            drawImage(imfig, t, ds, extents, 1, 1);
+            overlayBox(imfig, zoom, 1.5);
+            xlim(axExtents(1,:));
+            ylim(axExtents(2,:));
+
+            drawImage(zoomfig, t, ds, extents, 0.7, 0);
+            overlayBox(zoomfig, [zoom(1,1)+0.25 zoom(1,2)-0.3; zoom(2,1)+0.375  zoom(2,2)-0.125], 3);
+            overlayTracks(zoomfig, t, ds, g2l, ds.tracksActual, trackColorsActual, 3);
+            xlim(zoom(1,:));
+            ylim(zoom(2,:));
+
+            figure(imfig);
+            truesize(imfig, [imsize(2) imsize(1)]);
+            imfrm = getframe(gca);
+
+            figure(zoomfig);
+            truesize(zoomfig, round(zoomratio*[zoomsize(2) zoomsize(1)]));
+            zoomfrm = getframe(gca);
+
+            if ( size(imfrm.cdata,2) ~= size(zoomfrm.cdata,2) )
+                newsz = min(size(imfrm.cdata,1), size(zoomfrm.cdata,1));
+                imfrm.cdata = imfrm.cdata(1:newsz,:,:);
+                zoomfrm.cdata = zoomfrm.cdata(1:newsz,:,:);
+            end
+            
+            fullframe = imfrm;
+            fullframe.cdata = [fullframe.cdata zoomfrm.cdata]; 
+            
+            movfile = addframe(movfile,fullframe);
+            
+            clf(imfig);
+            clf(zoomfig);
+        end
+    catch excp
+        if ( ~isempty(movfile) )
+            movfile = close(movfile);
+        end
+        rethrow (excp);
+        
+    end
+    
+    movfile = close(movfile);
+end
+
+function drawImage(fig, t, ds, extents, maxalpha, bDrawCenterline)
+    dotradius = 4;
+    
+    fakeim = zeros(extents(2,2),extents(1,2));
+    for j=1:size(ds.loc{t},2)
+        fakeim = drawGaussianPt(fakeim, ds.loc{t}(1,j), ds.loc{t}(2,j), 1, dotradius, dotradius);
+    end
+    
+    figure(fig);
+    imshow(maxalpha*mat2gray(fakeim));colormap(gray);hold on;
+    
+    if ( bDrawCenterline )
+        for j=1:length(ds.axons)
+            axnpts = ppval(ds.axons(j).pp, 0:ds.axons(j).len+2);
+            plot(axnpts(1,:), axnpts(2,:), '-b');
+        end
+    end
+end
+
+function overlayBox(fig, boxedges, thickness)
+    figure(fig);
+    rectangle('Position',[boxedges(:,1)' (boxedges(:,2)-boxedges(:,1))'], 'Curvature',[0 0], 'EdgeColor','r', 'LineWidth',thickness);
+%     plot(boxedges(1,:), ones(1,2)*boxedges(2,1), '-r', 'LineWidth',thickness);
+%     plot(boxedges(1,:), ones(1,2)*boxedges(2,2), '-r', 'LineWidth',thickness);
+%     plot(ones(1,2)*boxedges(1,1), boxedges(2,:), '-r', 'LineWidth',thickness);
+%     plot(ones(1,2)*boxedges(1,2), boxedges(2,:), '-r', 'LineWidth',thickness);
+end
+
+function overlayTracks(fig, t, ds, g2l, trackList, trackColors, traillength)
+    figure(fig);hold on;
+    
+    numDets = length(ds.param{t});
+    noisePts = ds.loc{t}(:,numDets+1:end);
+    plot(noisePts(1,:), noisePts(2,:), 'xm', 'MarkerSize',12);
+    
+    for j=1:length(trackList)
+        trkt = g2l(trackList{j},1);
+        trkidx = g2l(trackList{j},2);
+        
+        idx = find((trkt >= t-traillength) & (trkt <= t));
+        
+        if ( ~isempty(idx) )
+            loc = [];
+            for k=1:length(idx)
+                loc(:,k) = ds.loc{trkt(idx(k))}(:,trkidx(idx(k)));
+            end
+            plot(loc(1,:), loc(2,:), '-', 'Color',trackColors(j,:), 'LineWidth',1.5);
+            if ( trkt(idx(end)) == t )
+                plot(loc(1,end), loc(2,end), '.r', 'MarkerSize',12);
+            end
+        end
+    end
+end
+
+
+function extents = getAxonExtents(ds, inextents, backoff)
+
+    extents = [0 0; 0 0];
+
+    for axIdx=1:length(ds.axons)
+        axlens = [0:ds.axons(axIdx).len-1 ds.axons(axIdx).len];
+        axPts = ppval(ds.axons(axIdx).pp, axlens);
+        
+        extents(1,1) = max(min(axPts(1,:))-backoff, inextents(1,1));
+        extents(1,2) = min(max(axPts(1,:))+backoff, inextents(1,2));
+        extents(2,1) = max(min(axPts(2,:))-backoff, inextents(2,1));
+        extents(2,2) = min(max(axPts(2,:))+backoff, inextents(2,2));
+    end
+end
\ No newline at end of file
diff --git a/matlab/runfakejaqaman.m b/matlab/runfakejaqaman.m
new file mode 100644
index 0000000000000000000000000000000000000000..acc1a903167a0ec9afb1c7cbc73dfdc81262e807
--- /dev/null
+++ b/matlab/runfakejaqaman.m
@@ -0,0 +1,58 @@
+outdir = 'C:\Users\mwinter\Documents\MATLAB\faketest_singleaxon_three';
+mkdir(outdir);
+numsdetlist = [10 20 40 60 80 100];
+falsedet = [0 10 20 40 60];
+numtrys = 20;
+
+trackertypes = {'_Jaqaman'};
+
+% axons = [];
+load(fullfile(outdir,'axons.mat'));
+for idx=1:length(numsdetlist)
+    numdet = numsdetlist(idx);
+    
+    for idxfalse = 1:length(falsedet)
+        pctfalse = falsedet(idxfalse);
+        numfalse = round(numdet*(pctfalse/100));
+        
+        for i=1:numtrys
+            fname_prefix = ['fakedata_' num2str(numdet,'%04d') 'det_' num2str(pctfalse,'%04d') 'false_' num2str(i,'%04d') 'try'];
+            segdatname = [fname_prefix '_seg.dat'];
+            mbdatname = [fname_prefix '_mb.dat'];
+            
+            if ( ~exist(fullfile(outdir,[fname_prefix '.mat']),'file') )
+%                 [ds, axons] = makePhantom(512, 512, 'numAxons',1, 'axons',axons, 'numDetections',numdet, 'speedVarProb',0, 'paramVarProb',0, 'falseDetections', numfalse);
+                for k=1:length(ds.rgDetect)
+                    for j=1:size(ds.rgDetect{k},1)
+                        ds.rgDetect{k}(j,:) = [ds.loc{k}(1,j) ds.loc{k}(2,j) ones(1,25)];
+                    end
+                end
+%                 save(fullfile(outdir,[fname_prefix '.mat']), 'ds');
+%                 writeSegData(fullfile(outdir,segdatname), ds.rgDetect, ds.rgCC, [1 512; 1 512]);
+%                 writeMotionBlurData(fullfile(outdir,mbdatname),
+%                 ds.rgMotionBlur);
+            else
+                load(fullfile(outdir,[fname_prefix '.mat']));
+                
+                for k=1:length(ds.rgDetect)
+                    for j=1:size(ds.rgDetect{k},1)
+                        ds.rgDetect{k}(j,:) = [ds.loc{k}(1,j) ds.loc{k}(2,j) ones(1,25)];
+                    end
+                end
+            end
+
+            for tkidx=1:length(trackertypes)
+                outdatname = [fname_prefix trackertypes{tkidx} '_tracks.mat'];
+
+                if ( ~exist(fullfile(outdir,outdatname), 'file') )
+                    movieInfo = buildMovieInfo(ds);
+                    scriptTrackGeneral
+                    trackingStruct = buildTrackingStruct(ds, tracksFinal, 6);
+                    save(fullfile(outdir,outdatname), 'trackingStruct', 'tracksFinal', 'movieInfo');
+                end
+            end
+            clear ds;
+        end
+    end
+end
+save(fullfile(outdir,'axons.mat'), 'axons');
\ No newline at end of file
diff --git a/matlab/runfaketrack.m b/matlab/runfaketrack.m
new file mode 100644
index 0000000000000000000000000000000000000000..fd483fef7dbf1db64a5f4de8ec7ea2af36f721bf
--- /dev/null
+++ b/matlab/runfaketrack.m
@@ -0,0 +1,100 @@
+outdir = 'faketest_singleaxon_largesets';
+mkdir(outdir);
+%numsdetlist = [10 25 50 75 100 125 150 200 250 300 350];
+%numsdetlist = [10 20 40 60 80 100];
+%falsedet = [0 10 20 40 60];
+numsdetlist = [10 20 40 60 80 100 120 140];
+falsedet = [80 100];
+numtrys = 20;
+
+trackertypes = {'_mta';'_sfa'};
+%trackertypes = ['_sfa'];
+
+numwindows = 10;
+windowprocessing = [];
+windidx = 1;
+prctime = [];
+giveuptime = 54000;
+
+% axons = [];
+pause on;
+load(fullfile(outdir,'axons.mat'));
+for idx=1:length(numsdetlist)
+    numdet = numsdetlist(idx);
+    
+    for idxfalse = 1:length(falsedet)
+        pctfalse = falsedet(idxfalse);
+        numfalse = round(numdet*(pctfalse/100));
+        
+        for i=1:numtrys
+            fname_prefix = ['fakedata_' num2str(numdet,'%04d') 'det_' num2str(pctfalse,'%04d') 'false_' num2str(i,'%04d') 'try'];
+            segdatname = [fname_prefix '_seg.dat'];
+            mbdatname = [fname_prefix '_mb.dat'];
+            
+            if ( ~exist(fullfile(outdir,[fname_prefix '.mat']),'file') )
+                [ds, axons] = makePhantom(512, 512, 'numAxons',1, 'axons',axons, 'numDetections',numdet, 'speedVarProb',0, 'paramVarProb',0, 'falseDetections', numfalse);
+                for k=1:length(ds.rgDetect)
+                    for j=1:size(ds.rgDetect{k},1)
+                        ds.rgDetect{k}(j,:) = [ds.loc{k}(1,j) ds.loc{k}(2,j) ones(1,25)];
+                    end
+                end
+                save(fullfile(outdir,[fname_prefix '.mat']), 'ds');
+                writeSegData(fullfile(outdir,segdatname), ds.rgDetect, ds.rgCC, [1 512; 1 512]);
+                writeMotionBlurData(fullfile(outdir,mbdatname), ds.rgMotionBlur);
+            else
+                load(fullfile(outdir,[fname_prefix '.mat']));
+                
+                for k=1:length(ds.rgDetect)
+                    for j=1:size(ds.rgDetect{k},1)
+                        ds.rgDetect{k}(j,:) = [ds.loc{k}(1,j) ds.loc{k}(2,j) ones(1,25)];
+                    end
+                end
+            end
+
+            for tkidx=1:length(trackertypes)
+                outdatname = [fname_prefix trackertypes{tkidx} '_tracks.txt'];
+                dbgdatname = [fname_prefix trackertypes{tkidx} '_dbginfo.txt'];
+                segdatname = [fname_prefix '_seg.dat'];
+                mbdatname = [fname_prefix '_mb.dat'];
+
+                if ( ~exist(fullfile(outdir,outdatname), 'file') )
+                    while ( 1 )
+                        if ( isempty(prctime) )
+                            prctime = tic();
+                        end
+                        deltat = toc(prctime);
+
+                        keepidx = boolean(ones(size(windowprocessing)));
+                        for chkwidx=1:length(windowprocessing)
+                            windowprocessing(chkwidx).time = windowprocessing(chkwidx).time + deltat;
+
+                            if ( ~exist(windowprocessing(chkwidx).name,'file') && windowprocessing(chkwidx).time < giveuptime )
+                                continue;
+                            end
+
+                            keepidx(chkwidx) = 0;
+                        end
+                        
+                        windowprocessing = windowprocessing(keepidx);
+                        windidx = nnz(keepidx)+1;
+                        
+                        prctime = tic();
+                        if ( windidx > numwindows )
+                            pause(30);
+                        else
+                            break;
+                        end
+                    end
+
+                    system(['KymoTracker_v2' trackertypes{tkidx} '.exe "' fullfile(outdir,segdatname) '" "' fullfile(outdir,mbdatname) '" "' fullfile(outdir,outdatname) '" "' fullfile(outdir,dbgdatname) '" &']);
+                    
+                    windowprocessing(windidx).name = fullfile(outdir,outdatname);
+                    windowprocessing(windidx).time = 0;
+                    windidx = windidx + 1;
+                end
+            end
+            clear ds;
+        end
+    end
+end
+save(fullfile(outdir,'axons.mat'), 'axons');
\ No newline at end of file
diff --git a/matlab/sfaTrack.m b/matlab/sfaTrack.m
new file mode 100644
index 0000000000000000000000000000000000000000..e14e6b40848235c28c9ee1a4214602b02ba0b4e1
--- /dev/null
+++ b/matlab/sfaTrack.m
@@ -0,0 +1,53 @@
+%
+% This function takes a cell array of detections and outputs a matrix of
+% tracks.
+%
+function tracks = sfaTrack(rgDetect)
+    numFrames = length(rgDetect);
+    tracks=zeros(size(rgDetect{1},1), numFrames-1);
+    for t=1:numFrames-1
+        N = size(rgDetect{t},1);
+        M = size(rgDetect{t+1},1);
+        C = zeros(N, M);
+        trackAssgn = [];
+        for i=1:N
+            if ( t > 1 )
+                lastAssgn = find(tracks(:,t-1) == i);
+                if ( isempty(lastAssgn) )
+                    trackAssgn(i) = size(tracks,1)+1;
+                    tracks(end+1,:) = zeros(1, numFrames-1);
+                else
+                    trackAssgn(i) = lastAssgn;
+                end
+            else
+                trackAssgn = 1:N;
+            end
+            
+            for j=1:M
+                C(i,j) = GetCost(rgDetect{t}(i,:), rgDetect{t+1}(j,:));
+            end
+        end
+        
+        [assign, cost] = assignmentoptimal(C);
+        
+        for i=1:length(assign)
+            if ( assign(i) > 0 )
+                tracks(trackAssgn(i),t) = assign(i);
+            end
+        end
+    end
+end
+
+function cost = GetCost(p1, p2)
+    costEpsilon = 1e-7;
+    maxDistanceDelta = 100;
+    
+    cost = inf;
+    
+    distErr = (p1(1)-p2(1))^2 + (p1(2)-p2(2))^2;
+    if ( distErr > maxDistanceDelta )
+        return;
+    end
+    
+    cost = distErr;
+end
\ No newline at end of file
diff --git a/matlab/testpathdist.m b/matlab/testpathdist.m
new file mode 100644
index 0000000000000000000000000000000000000000..57dc648af8faa4bcc2c745e1d25087a5b12e7104
--- /dev/null
+++ b/matlab/testpathdist.m
@@ -0,0 +1,24 @@
+path = [-3, 0, 2, 6, 9;0, 0, 0, 0, 0];
+seglen = 5;
+for i=1:length(path)-1
+    path(2,i+1) = sqrt(seglen^2 - (path(1,i+1)-path(1,i))^2) + path(2,i);
+end
+
+gridx = -30:0.05:30;
+gridy = -10:0.05:45;
+[X,Y] = meshgrid(gridx, gridy);
+dtst = zeros(size(X));
+dist = zeros(size(X));
+
+% for i=1:size(X,1)
+%     for j=1:size(X,2)
+%         [dist(i,j) dtst(i,j)] = distToPath([X(i,j);Y(i,j)], path);
+%     end
+% end
+
+[dist dtst] = distToPath(X,Y,path);
+
+ndist = dist ./ max(max(dist));
+
+figure;imagesc(gridx,gridy,dtst + (0.75*ndist));hold on;
+plot(path(1,:)', path(2,:)', '-k', 'LineWidth',2);
\ No newline at end of file