From 985e7346c5b7fa977dd71b03112b8e050b5705dc Mon Sep 17 00:00:00 2001 From: mwinter <mwinter@drexel.edu> Date: Thu, 21 Feb 2013 15:35:01 -0500 Subject: [PATCH] Basic cell MAT tracker. --- baseCellTracker.sln | 26 +++ baseCellTracker.vcproj | 375 +++++++++++++++++++++++++++++++++++++++++ src/cost.cpp | 168 ++++++++++++++++++ src/cost.h | 35 ++++ src/detection.cpp | 232 +++++++++++++++++++++++++ src/detection.h | 62 +++++++ src/paths.cpp | 144 ++++++++++++++++ src/paths.h | 65 +++++++ src/tracker.cpp | 218 ++++++++++++++++++++++++ src/tracker.h | 77 +++++++++ 10 files changed, 1402 insertions(+) create mode 100644 baseCellTracker.sln create mode 100644 baseCellTracker.vcproj create mode 100644 src/cost.cpp create mode 100644 src/cost.h create mode 100644 src/detection.cpp create mode 100644 src/detection.h create mode 100644 src/paths.cpp create mode 100644 src/paths.h create mode 100644 src/tracker.cpp create mode 100644 src/tracker.h diff --git a/baseCellTracker.sln b/baseCellTracker.sln new file mode 100644 index 0000000..b5b3e91 --- /dev/null +++ b/baseCellTracker.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "baseCellTracker", "baseCellTracker.vcproj", "{88FA6F51-2685-4A1D-8C95-BCD88E04B63D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Debug|Win32.ActiveCfg = Debug|Win32 + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Debug|Win32.Build.0 = Debug|Win32 + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Debug|x64.ActiveCfg = Debug|x64 + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Debug|x64.Build.0 = Debug|x64 + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Release|Win32.ActiveCfg = Release|Win32 + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Release|Win32.Build.0 = Release|Win32 + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Release|x64.ActiveCfg = Release|x64 + {88FA6F51-2685-4A1D-8C95-BCD88E04B63D}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/baseCellTracker.vcproj b/baseCellTracker.vcproj new file mode 100644 index 0000000..53344f5 --- /dev/null +++ b/baseCellTracker.vcproj @@ -0,0 +1,375 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="baseCellTracker" + ProjectGUID="{88FA6F51-2685-4A1D-8C95-BCD88E04B63D}" + RootNamespace="baseCellTracker" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + <Platform + Name="x64" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)\Output\$(ConfigurationName)_$(PlatformName)" + IntermediateDirectory="$(SolutionDir)\Intermediate\$(ConfigurationName)_$(PlatformName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories=".\src" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Debug|x64" + OutputDirectory="$(SolutionDir)\Output\$(ConfigurationName)_$(PlatformName)" + IntermediateDirectory="$(SolutionDir)\Intermediate\$(ConfigurationName)_$(PlatformName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories=".\src" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)\Output\$(ConfigurationName)_$(PlatformName)" + IntermediateDirectory="$(SolutionDir)\Intermediate\$(ConfigurationName)_$(PlatformName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories=".\src" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="2" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|x64" + OutputDirectory="$(SolutionDir)\Output\$(ConfigurationName)_$(PlatformName)" + IntermediateDirectory="$(SolutionDir)\Intermediate\$(ConfigurationName)_$(PlatformName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + TargetEnvironment="3" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories=".\src" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="2" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="17" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\src\cost.cpp" + > + </File> + <File + RelativePath=".\src\detection.cpp" + > + </File> + <File + RelativePath=".\src\paths.cpp" + > + </File> + <File + RelativePath=".\src\tracker.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath=".\src\cost.h" + > + </File> + <File + RelativePath=".\src\detection.h" + > + </File> + <File + RelativePath=".\src\paths.h" + > + </File> + <File + RelativePath=".\src\tracker.h" + > + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/src/cost.cpp b/src/cost.cpp new file mode 100644 index 0000000..864c0a2 --- /dev/null +++ b/src/cost.cpp @@ -0,0 +1,168 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + +#include "tracker.h" + +#undef max +#undef min + +const double costEpsilon = 1e-3; + +double GetCCDistance(int node0, int node1) +{ + if ( gDetect[node0].connCompDist.count(node1) == 0 ) + return (gCCMax + 1.0); + + return (gDetect[node0].connCompDist[node1]); +} // GetCCDistance + +double GetCOMDistance(int node0, int node1) +{ + double distSq = (SQR(gDetect[node0].X-gDetect[node1].X) + SQR(gDetect[node0].Y-gDetect[node1].Y)); + + return sqrt(distSq); +} // GetCOMDistance + +double GetWeightedDistance(int node0, int node1, double vmax, double ccmax) +{ + double hd,sd; + double nmax,nmin,cd; + + hd = GetCOMDistance(node0, node1); + if ( hd > vmax ) + return dbltype::infinity(); + + nmax = std::max<int>(gDetect[node0].pixelCoords.size(), gDetect[node1].pixelCoords.size()); + nmin = std::min<int>(gDetect[node0].pixelCoords.size(), gDetect[node1].pixelCoords.size()); + + cd = GetCCDistance(node0, node1); + if ( cd > ccmax ) + return dbltype::infinity(); + + sd = (nmax - nmin) / nmax; + + return (10.0*hd + 100.0*sd + 1000.0*cd); + +} // GetWeightedDistances + +double GetCost(const std::vector<int>& path, int srcFrameIdx, int bCheck) +{ + int k; + double dlcd; + int startIdx; + double dlocnX,dlocnY; + + double LocalCost = 0.0; + double LocationCost = 0.0; + double TotalCost = 0.0; + double OcclusionCost=1.0; + + + if ( (path.size() - srcFrameIdx) < 2 ) + return dbltype::infinity(); + + if (bCheck) + startIdx = path.size() - 2; + else + { + int tStart; + + tStart = GetTime(path[srcFrameIdx]) - gWindowSize+1; + tStart = std::max<int>(0, tStart); + startIdx = srcFrameIdx; + while ( (GetTime(path[startIdx]) > tStart) && (startIdx > 0) ) + startIdx--; + } + + for ( k=startIdx; k < path.size()-1; ++k ) + { + dlcd = GetCOMDistance(path[k], path[k+1]); + if ( dlcd > gVMax ) + return dbltype::infinity(); + } + + OcclusionCost = 1.0; + for ( k=startIdx; k < path.size()-1; ++k ) + OcclusionCost += GetTime(path[k+1]) - GetTime(path[k]) - 1; + + if (bCheck) + return 1.0; + + LocalCost = 3 * GetWeightedDistance(path[srcFrameIdx], path[srcFrameIdx+1], gVMax, gCCMax); + + if ( LocalCost == dbltype::infinity() ) + return dbltype::infinity(); + + if ( srcFrameIdx > 0 ) + LocalCost += GetWeightedDistance(path[srcFrameIdx-1], path[srcFrameIdx+1], 2*gVMax, 2*gCCMax); + else + LocalCost *= 2; + + if ( LocalCost == dbltype::infinity() ) + return dbltype::infinity(); + + if ( (srcFrameIdx < path.size()-2) ) + LocalCost += GetWeightedDistance(path[srcFrameIdx], path[srcFrameIdx+2], 2*gVMax, 2*gCCMax); + else + LocalCost *= 2; + + if ( LocalCost == dbltype::infinity() ) + return dbltype::infinity(); + + dlocnX = gDetect[path[srcFrameIdx]].X; + dlocnY = gDetect[path[srcFrameIdx]].Y; + for ( k=startIdx; k < srcFrameIdx; ++k ) + { + dlocnX += gDetect[path[k]].X; + dlocnY += gDetect[path[k]].Y; + } + dlocnX /= (srcFrameIdx-startIdx+1); + dlocnY /= (srcFrameIdx-startIdx+1); + + for ( k=srcFrameIdx; k < path.size(); ++k ) + { + LocationCost += SQR(gDetect[path[k]].X - dlocnX) + SQR(gDetect[path[k]].Y - dlocnY); + } + LocationCost /= (path.size()-srcFrameIdx); + LocationCost = sqrt(LocationCost); + + TotalCost = LocalCost + LocationCost; + if ( path.size() < 2*gWindowSize + 1 ) + { + double LengthPenalty; + LengthPenalty = (2*gWindowSize + 1) - path.size(); + TotalCost = 2*TotalCost*LengthPenalty; + } + + if (OcclusionCost > 1.0) + OcclusionCost *= 2; + + TotalCost *= OcclusionCost; + + if ( TotalCost < costEpsilon ) + TotalCost = costEpsilon; + + return TotalCost; +} + diff --git a/src/cost.h b/src/cost.h new file mode 100644 index 0000000..88e3fb0 --- /dev/null +++ b/src/cost.h @@ -0,0 +1,35 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + + + +// Get cost from a path list. +// path - vector of indices into gDetect, indicates current path being explored. +// +// srcFrameIdx - the index into path index vector of the source point (start of new path). +// srcFrameIdx is trivially 0 if there is no history being used. +// +// bCheck - If bCheck is true then only do a check to verify the path doesn't violate constraints. +double GetCost(const std::vector<int>& path, int srcFrameIdx,int bCheck); + diff --git a/src/detection.cpp b/src/detection.cpp new file mode 100644 index 0000000..60eca02 --- /dev/null +++ b/src/detection.cpp @@ -0,0 +1,232 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + +#include "tracker.h" + +int gImageWidth; +int gImageHeight; + +int GetGlobalIdx(int t, int idx) +{ + if ( t >= gFrameHash.size() ) + return -1; + + if ( idx >= gFrameHash[t].size() ) + return -1; + + return (gFrameHash[t][idx]); +} + +int GetTime(int globalIdx) +{ + if ( globalIdx >= gDetect.size() ) + return -1; + + return (gDetect[globalIdx].time); +} + +unsigned int GetLinearCoordIdx(int X, int Y) +{ + return X + gImageWidth * Y; +} + +void MakeCoordSet(std::set<int>& coordSet, int detIdx) +{ + coordSet.clear(); + + int numCoord = gDetect[detIdx].pixelCoords.size(); + for ( int i=0; i < numCoord; ++i ) + { + unsigned int uniqueLinIdx = GetLinearCoordIdx(gDetect[detIdx].pixelCoords[i].X, gDetect[detIdx].pixelCoords[i].Y); + coordSet.insert(uniqueLinIdx); + } +} + +double CalcConnectedDist(const std::set<int>& coordSet, int detIdx, int nextDetIdx) +{ + SDetection& curDet = gDetect[detIdx]; + SDetection& nextDet = gDetect[nextDetIdx]; + + // Don't bother to calculate CC distance when past center of mass velocity threshold + double comDistSq = SQR(curDet.X - nextDet.X) + SQR(curDet.Y - nextDet.Y); + if ( comDistSq > SQR(gVMax) ) + return dbltype::infinity(); + + // Overlap distance |A intersect B| / min(|A|,|B|); + int overlapCount = 0; + for ( int i=0; i < nextDet.pixelCoords.size(); ++i ) + overlapCount += coordSet.count(GetLinearCoordIdx(nextDet.pixelCoords[i].X, nextDet.pixelCoords[i].Y)); + + if ( overlapCount > 0 ) + { + int compSize = std::min(curDet.pixelCoords.size(), nextDet.pixelCoords.size()); + return (1.0 - ((double) overlapCount) / compSize); + } + + // If no overlap then find the minimum pixel distance + double minDistSq = dbltype::infinity(); + for ( int i=0; i < curDet.pixelCoords.size(); ++i ) + { + for ( int j=0; j < nextDet.pixelCoords.size(); ++j ) + { + double chkDistSq = SQR(curDet.pixelCoords[i].X - nextDet.pixelCoords[j].X) + SQR(curDet.pixelCoords[i].Y - nextDet.pixelCoords[j].Y); + if ( chkDistSq > minDistSq ) + continue; + + minDistSq = chkDistSq; + } + } + + return sqrt(minDistSq); +} + +void CalcConnectedDistToFrame(const std::set<int>& coordSet, int detIdx, int time) +{ + SDetection& curDet = gDetect[detIdx]; + + if ( time >= gFrameHash.size() ) + return; + + for ( int i=0; i < gFrameHash[time].size(); ++i ) + { + int nextDetIdx = GetGlobalIdx(time, i); + + double connDist = CalcConnectedDist(coordSet, detIdx, nextDetIdx); + if ( connDist == dbltype::infinity() ) + continue; + + curDet.connCompDist.insert(std::pair<int,double>(nextDetIdx, connDist)); + } +} + +// Precalculates connected component distances for each detection +// up to two frames into the future +int CalcConnectedDistances() +{ + std::set<int> coordSet; + + for ( int t=0; t < gNumFrames-1; ++t ) + { + for ( int i=0; i < gFrameHash[t].size(); ++i ) + { + int detIdx = GetGlobalIdx(t,i); + + MakeCoordSet(coordSet, detIdx); + CalcConnectedDistToFrame(coordSet, detIdx, t+1); + CalcConnectedDistToFrame(coordSet, detIdx, t+2); + } + } + + return 0; +} + +// Read text segmentation data from a file +int ReadSegmentationData(char* filename) +{ + FILE* fp; + + fp = fopen(filename, "r"); + if ( !fp ) + return -1; + + fscanf(fp, "%d %d\n", &gImageWidth, &gImageHeight); + fscanf(fp, "%d %d\n\n", &gNumFrames, &gNumDetections); + + gDetect.resize(gNumDetections); + gFrameHash.resize(gNumFrames); + + int totalRead = 0; + + for ( int t=0; t < gNumFrames; ++t ) + { + int frameDetections; + fscanf(fp, "%d\n", &frameDetections); + + gFrameHash[t].resize(frameDetections); + + for ( int ptItr = 0; ptItr < frameDetections; ++ptItr ) + { + gFrameHash[t][ptItr] = totalRead + ptItr; + + int numPixels; + SDetection* curPt = &gDetect[totalRead + ptItr]; + fscanf(fp, "%lf %lf %d:", &(curPt->X), &(curPt->Y), &numPixels); + + curPt->time = t; + + // Read in pixel coordinates for each pixel in the detection + // This is used to calculate connected component distances + curPt->pixelCoords.resize(numPixels); + int xPix, yPix; + for ( int pixItr = 0; pixItr < numPixels; ++pixItr ) + { + fscanf(fp, " (%d,%d)", &xPix,&yPix); + curPt->pixelCoords[pixItr].X = xPix; + curPt->pixelCoords[pixItr].Y = yPix; + } + + fscanf(fp,"\n"); + } + + totalRead += frameDetections; + } + + fclose(fp); + + return gNumFrames; +} + +int ReadDetectionData(int argc, char* argv[]) +{ + int checkResult; + + if ( argc < 1) + return -1; + + checkResult = ReadSegmentationData(argv[0]); + if ( checkResult < 0 ) + return -1; + + checkResult = CalcConnectedDistances(); + if ( checkResult < 0 ) + return -1; + + gConnectOut.resize(gDetect.size()); + gConnectIn.resize(gDetect.size()); + + gAssignedConnectIn.resize(gDetect.size()); + gAssignedConnectOut.resize(gDetect.size()); + + gAssignedTrackID.resize(gDetect.size()); + + for ( int i=0; i < gDetect.size(); ++i ) + { + gAssignedConnectIn[i] = -1; + gAssignedConnectOut[i] = -1; + gAssignedTrackID[i] = -1; + } + + return 1; +} + diff --git a/src/detection.h b/src/detection.h new file mode 100644 index 0000000..58c82a8 --- /dev/null +++ b/src/detection.h @@ -0,0 +1,62 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + +// Detection related types and variables +struct SPixelCoord +{ + int X; + int Y; +}; + +struct SDetection +{ + // Frame the detection is in + int time; + + // centroid coordinates + double X; + double Y; + + // Pixel coordinates + std::vector<SPixelCoord> pixelCoords; + + // Connected component distances + std::map<int,double> connCompDist; +}; + +// Read and initialize detection related data. All globals listed below must be filled or initialized by the end of this routine. + +// Globals: +// gDetect - filled with detection data for all frames +// gFrameHash - lists indices (into gDetect) for each frame of movie + +// gConnectOut,gConnectIn - initialized to numPts(total detections) empty std::maps each +// gAssignedConnectIn - same size as gConnectIn, initialized to -1 +// gAssignedConnectOut - same as gAssignedConnectIn, these are for quick lookup of assigned paths +int ReadDetectionData(int argc, char* argv[]); + +int GetGlobalIdx(int t, int idx); +int GetTime(int globalIdx); +int GetLocalIdx(int globalIdx); + diff --git a/src/paths.cpp b/src/paths.cpp new file mode 100644 index 0000000..0bdec61 --- /dev/null +++ b/src/paths.cpp @@ -0,0 +1,144 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + +#include "tracker.h" + +int BuildHistoryPath(CSourcePath* historyPath, CSourcePath* path, int occlLookback) +{ + int pathStartGIdx = path->index[0]; + + // Find the assigned in-edge + int histTrackID = -1; + int histIdx = gAssignedConnectIn[pathStartGIdx]; + if ( histIdx >=0 ) + { + CSourcePath* histpath = gConnectIn[pathStartGIdx][histIdx]; + // Single segment agreement requirement + if ( histpath->index.size() > 2 ) + histTrackID = histpath->trackletID; + } + + if ( histTrackID >= 0 ) + { + tPathList::iterator histIter = gAssignedTracklets[histTrackID].begin(); + while ( histIter != gAssignedTracklets[histTrackID].end() ) + { + CSourcePath* curPath = *histIter; + + historyPath->PushPoint(curPath->index[0]); + ++histIter; + } + } + + for ( int i=0; i < path->index.size(); ++i ) + historyPath->PushPoint(path->index[i]); + + return histTrackID; +} + +int DepthFirstBestPathSearch(CSourcePath path, int bestGIdx, int t, int tEnd, int occlLookback) +{ + bool bFinishedSearch = true; + if ( t < tEnd ) + { + for ( int nextPt=0; nextPt < gFrameHash[t].size(); ++nextPt ) + { + int nextGIdx = GetGlobalIdx(t, nextPt); + path.PushPoint(nextGIdx); + double chkCost = GetCost(path.index, 0, 1); + path.PopPoint(); + + if ( chkCost == dbltype::infinity() ) + continue; + + bFinishedSearch = false; + + path.PushPoint(nextGIdx); + bestGIdx = DepthFirstBestPathSearch(path, bestGIdx, t+1, tEnd, occlLookback); + path.PopPoint(); + } + } + + if ( bFinishedSearch && (path.index.size() > 1) ) + { + CSourcePath historyPath; + + int startGIdx = path.index[0]; + int nextGIdx = path.index[1]; + + int historyTrackID = BuildHistoryPath(&historyPath, &path, occlLookback); + int srcPathIdx = historyPath.index.size() - path.index.size(); + + double newPathCost = GetCost(historyPath.index, srcPathIdx, 0); + if ( newPathCost == dbltype::infinity() ) + return bestGIdx; + + path.trackletID = historyTrackID; + path.cost = newPathCost; + + if ( gConnectOut[startGIdx].count(nextGIdx) == 0 ) + { + CSourcePath* newPath = new CSourcePath(path); + gConnectOut[startGIdx].insert(std::pair<int,CSourcePath*>(nextGIdx, newPath)); + gConnectIn[nextGIdx].insert(std::pair<int,CSourcePath*>(startGIdx, newPath)); + } + else if ( newPathCost < gConnectOut[startGIdx][nextGIdx]->cost ) + { + *(gConnectOut[startGIdx][nextGIdx]) = path; + } + + if ( bestGIdx < 0 || newPathCost < gConnectOut[startGIdx][bestGIdx]->cost ) + { + bestGIdx = nextGIdx; + } + } + + return bestGIdx; +} + +void BuildBestPaths(std::map<int,int>& bestOutEdges, int t, int occlLookback) +{ + if ( t-occlLookback < 0 ) + return; + + int numDetections = gFrameHash[t-occlLookback].size(); + + int tEnd = std::min<int>(t+gWindowSize, gNumFrames); + + for ( int srcIdx=0; srcIdx < numDetections; ++srcIdx ) + { + int startGIdx = GetGlobalIdx(t-occlLookback, srcIdx); + if ( occlLookback > 0 && gAssignedConnectOut[startGIdx] > 0 ) + continue; + + CSourcePath srcPath; + srcPath.PushPoint(startGIdx); + int bestGIdx = DepthFirstBestPathSearch(srcPath, -1, t+1, tEnd, occlLookback); + if ( bestGIdx >= 0 ) + { + bestOutEdges[startGIdx] = bestGIdx; + } + } +} + diff --git a/src/paths.h b/src/paths.h new file mode 100644 index 0000000..6e6b853 --- /dev/null +++ b/src/paths.h @@ -0,0 +1,65 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + +#include <vector> +#include <list> +#include <map> + +typedef std::numeric_limits<double> dbltype; + +class CSourcePath +{ +public: + CSourcePath() + { + trackletID = -1; + cost = dbltype::infinity(); + } + + void PushPoint(int globalIdx) + { + index.push_back(globalIdx); + } + + void PopPoint() + { + if ( index.size() <= 1 ) + return; + + index.pop_back(); + } + +public: + + int trackletID; + double cost; + + std::vector<int> index; +}; + +int GetGlobalIdx(int t, int idx); +int GetTime(int globalIdx); +int GetLocalIdx(int globalIdx); +void BuildBestPaths(std::map<int,int>& bestOutEdges, int t, int occlLookcback = 0); + diff --git a/src/tracker.cpp b/src/tracker.cpp new file mode 100644 index 0000000..1858d32 --- /dev/null +++ b/src/tracker.cpp @@ -0,0 +1,218 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + +#include "tracker.h" + +// Global variables +int gNumFrames; +int gNumDetections; + +std::vector<SDetection> gDetect; +std::vector<std::vector<int>> gFrameHash; + +// Path-graph global variables +std::vector<std::map<int,CSourcePath*>> gConnectOut; +std::vector<std::map<int,CSourcePath*>> gConnectIn; + +// For quick edge lookup from point (like inID/outID) +std::vector<int> gAssignedConnectOut; +std::vector<int> gAssignedConnectIn; +std::vector<int> gAssignedTrackID; + +// Final tracklet assignments +std::vector<tPathList> gAssignedTracklets; + +// Global tracking parameters +int gWindowSize; +double gVMax; +double gCCMax; + + +void WriteTracklets(int argc, char* argv[], int argIdx) +{ + std::map<int,CSourcePath*>::iterator cIter; + CSourcePath* inPath; + double dinCost; + + if ( argIdx >= argc ) + return; + + FILE* fPathFile = fopen(argv[argIdx], "w"); + + if ( !fPathFile ) + return; + + for ( int i=0; i < gAssignedTracklets.size(); ++i ) + { + tPathList::iterator trackIter; + tPathList::iterator lastPathIter = (--gAssignedTracklets[i].end()); + for ( trackIter=gAssignedTracklets[i].begin(); trackIter != gAssignedTracklets[i].end(); ++trackIter ) + { + fprintf(fPathFile, "%d,%d,%d\n", i+1,(*trackIter)->index[0] + 1,(*trackIter)->index[1] + 1); + } + } + + fprintf(fPathFile, "-1,-1,-1\n"); + for ( int i=0; i < gDetect.size(); ++i ) + { + cIter = gConnectIn[i].begin(); + for ( int j=0; j < gConnectIn[i].size(); ++j ) + { + inPath=cIter->second; + dinCost=inPath->cost; + if (dinCost!=dinCost) // test for -1.#IND! + continue; + fprintf(fPathFile, "%d,%d,%lf\n",i+1,cIter->first+1,dinCost); + cIter++; + + } + } + + fclose(fPathFile); +} + +int FindMinInEdgeIdx(int nextGIdx) +{ + double cmin = dbltype::infinity(); + int bestIdx = -1; + + std::map<int,CSourcePath*>::iterator cIter = gConnectIn[nextGIdx].begin(); + while ( cIter != gConnectIn[nextGIdx].end() ) + { + CSourcePath* inPath = cIter->second; + if ( inPath->cost < cmin ) + { + cmin = inPath->cost; + bestIdx = cIter->first; + } + + ++cIter; + } + + return bestIdx; +} + +int FindMinCostIdx(std::vector<CSourcePath*>& edges) +{ + int minidx = -1; + double mincost = dbltype::infinity(); + for ( int i=0; i < edges.size(); ++i ) + { + if ( edges[i]->cost < mincost ) + { + minidx = i; + mincost = edges[i]->cost; + } + } + + return minidx; +} + +int main(int argc, char* argv[]) +{ + system("echo %TIME% > ttt.txt"); + + // Set default gate values and window size. + gWindowSize = 4; + gVMax = 40.0; + gCCMax = 20.0; + + int nxtArg = 1; + // Specify all the parameters (or none) + if ( argc > 4 ) + { + nxtArg = 3; + + sscanf(argv[1], "%d", &gWindowSize); + sscanf(argv[2], "%lf", &gVMax); + sscanf(argv[3], "%lf", &gCCMax); + } + + argc += nxtArg; + argv += nxtArg; + + int outputArgIdx = ReadDetectionData(argc, argv); + + if ( outputArgIdx < 0 ) + return 0; + + system("echo %TIME% >> ttt.txt"); + + argc += outputArgIdx; + argv += outputArgIdx; + + std::map<int,int> bestOutEdges; + for ( int t=0; t < gFrameHash.size()-1; ++t ) + { + bestOutEdges.clear(); + BuildBestPaths(bestOutEdges, t); + + //Occlusions + for ( int iLookback=1; iLookback < 2; ++iLookback ) + { + BuildBestPaths(bestOutEdges, t, iLookback); + } + + printf("t = %d, %d detections\n", t, gFrameHash[t].size()); + + for ( int destPtIdx=0; destPtIdx < gFrameHash[t+1].size(); ++destPtIdx) + { + int nextGIdx = GetGlobalIdx(t+1, destPtIdx); + int bestTrackletIdx = FindMinInEdgeIdx(nextGIdx); + if ( bestTrackletIdx < 0 ) + continue; + + if ( (bestOutEdges.count(bestTrackletIdx) == 0) || bestOutEdges[bestTrackletIdx] != nextGIdx ) + continue; + + int newTrackletID = gConnectOut[bestTrackletIdx][nextGIdx]->trackletID; + + if ( newTrackletID < 0 ) + { + //Add new tracklet to list etc. and set id + newTrackletID = gAssignedTracklets.size(); + gConnectOut[bestTrackletIdx][nextGIdx]->trackletID = newTrackletID; + + tPathList newList; + gAssignedTracklets.push_back(newList); + + gAssignedTrackID[bestTrackletIdx] = newTrackletID; + } + + //Add path to tracklet list + gAssignedTracklets[newTrackletID].push_back(gConnectOut[bestTrackletIdx][nextGIdx]); + + //Keep track of assignment for fast lookup + gAssignedConnectIn[nextGIdx] = bestTrackletIdx; + gAssignedConnectOut[bestTrackletIdx] = nextGIdx; + gAssignedTrackID[nextGIdx] = newTrackletID; + } + } + + WriteTracklets(argc, argv, 0); + + system("echo %TIME% >> ttt.txt"); + +} + diff --git a/src/tracker.h b/src/tracker.h new file mode 100644 index 0000000..a14d4eb --- /dev/null +++ b/src/tracker.h @@ -0,0 +1,77 @@ +//*********************************************************************** +// +// Copyright 2013 Andrew Cohen and Mark Winter +// +// This file is part of the Multitemporal Association Tracker. See +// http://bioimage.coe.drexel.edu for details +// +// LEVer is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// LEVer is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LEVer in file "gnu gpl v3.txt". If not, see +// <http://www.gnu.org/licenses/>. +// +// +//*********************************************************************** + +#include <stdio.h> + +#include <list> +#include <vector> +#include <set> +#include <map> +#include <limits> + +#include "detection.h" +#include "cost.h" +#include "paths.h" + +// Convenience defines +#define SQR(x) ((x)*(x)) +#define DOT(x1,y1,x2,y2) ((x1)*(x2) + (y1)*(y2)) +#define LENGTH(x,y) (sqrt((SQR(x))+(SQR(y)))) +#define SIGN(x) (((x) >= 0.0) ? (1.0) : (-1.0) ) + +typedef char int8; +typedef short int16; +typedef long int32; +//typedef long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned long uint32; +//typedef unsigned long long uint64; + +typedef std::list<CSourcePath*> tPathList; + +// Detection related global variables +extern int gNumFrames; +extern int gNumDetections; + +extern std::vector<SDetection> gDetect; +extern std::vector<std::vector<int>> gFrameHash; + +// Path-graph global variables +extern std::vector<std::map<int,CSourcePath*>> gConnectOut; +extern std::vector<std::map<int,CSourcePath*>> gConnectIn; + +// For quick edge lookup from point (like inID/outID) +extern std::vector<int> gAssignedConnectOut; +extern std::vector<int> gAssignedConnectIn; +extern std::vector<int> gAssignedTrackID; + +// Final tracklet assignments +extern std::vector<tPathList> gAssignedTracklets; + +// Global tracking parameters +extern int gWindowSize; +extern double gVMax; +extern double gCCMax; + -- GitLab