Performing Queries
The main purpose of fort-myrmidon is to allow user to perform queries [1], i.e. computations on the whole set of tracking data. These queries can only be performed once Ant are properly defined and in some case (such as for collisions and interactions) their wanted virtual shape are defined.
Note
Here the python and C++ API quite diverges from the R API. While
the two first reports list
or std::vector
of object,
the latter only reports data.frame
or slist
of
data.frame
with only native R types.
Instantaneous Queries
Identifying Frames
The simplest query that could be performed on a tracking data set is identifying frames [2]. It is a simple translation of the raw tracking data from the Identification. It simply returns for each acquired video frame the position and orientation of each ant, taking into account the geometry relation between a tag position and orientation and the virtual ant desired position and orientation.
This query return a list of IdentifiedFrame
[6].
# Python
frames = fm.Query.IdentifyFrames(e)
for f in frames:
print("Got Frame at %s in space %d" % (f.FrameTime,f.Space))
print("AntID, X, Y, Angle, Zone")
print(f.Positions)
# R
d = fmQueryIdentifyFrames(e);
d$frames # a data frame summarizing for each frame its dimension, space and acquisition time
d$positions # a list of data frame with each ant position.
// C++
fort::myrmidon::IdentifyFramesQueryArgs args;
// modify here default args;
std::vector<fort::myrmidon::IdentifiedFrame::Ptr> frames;
fort::myrmidon::Query::IdentifyFrame(e,frames,args);
for ( const auto & f : frames ) {
std::cerr << "Got frame at " << f->FrameTime
<< " in space " << f->Space << std::endl
<< "AntID, X, Y, Angle, Zone" << std::endl
<< f->Positions << std::endl;
}
The main parameters for this query are:
the starting and ending time of the query: in order to select only a range of the acquired tracking data. Since dataset tends to grow larger than the available RAM, it is important to only work on part of the dataset at a time to keep the memory usage low enough.
Zone reporting: Choose if the actual zone the ant lies should be reported, see Segmenting Spaces in Zones
Collision Detection
Instead of just reporting the ant position, fort-myrmidon can also report any collision detection between ant shapes by colliding frames [3].
This query returns a collection of IdentifiedFrame
and
CollisionFrame
[7].
#python
frames = fm.Query.CollideFrame(e)
for identified,collided in frames:
print("Collisions at $s in Space %d." % ( collided.FrameTime, collided.Space) )
for c in collided.Collisions:
print("Ants %s and %s collides." % ( fm.FormatAntID(c.IDs[0]), fm.FormatAntID(c.IDs[1])))
print(c.Types)
#R
d = fmQueryCollideFrames(e)
d$frames # a data.frame that summarizes frame time, dimensions and space
d$positions # a list of data.frame with the ant position for each frame
d$collisions # a data.frame of all collisions in the selected time range. the field frame_row_index refers to the row in frames or the index in positions.
//C++
auto args = fort::myrmidon::Query::CollideFramesArgs();
// modify here default arguments;
std::vector<std::pair<fort::myrmidon::IdentifiedFrame::Ptr,
fort::myrmidon::CollisionFrame::Ptr>> frames;
fort::myrmidon::CollideFrames(e,frames,args);
for ( const auto & [identified,collided] : frames ) {
std::cerr << "Collisions at " << collided->FrameTime
<< " in space " << collided->Space
<< ": " << std::endl;
for ( const auto & c : collided->Collisions ) {
std::cerr << "Ants " << fort::myrmidon::FormatAntID(c.IDs.first)
<< " and " << fort::myrmidon::FormatAntID(c.IDs.second)
<< " collides." << std::endl
<< c.Types << std::endl;
}
}
The parameters are the same than for Identifying Frames, excepted than zone are always reported. Indeed zoning is used to prune undesired collisions as described in Segmenting Spaces in Zones.
Note
By convention, the order of the AntID
in the IDs
field
is always sorted from the smallest to the highest to ensure uniqueness.
Interaction Types
As capsules are typed, for each collision the InteractionTypes
[12] are reported. In Python and C++ it is a Nx2
matrix with each column representing the type of each ant which are
colliding with the other one.
For example the matrix:
Means body type 1 for the second Ant touches both body type 1 and 2 of the first ant, but the body type 2 of the second ant collide with no other parts.
In R, this matrix will be returned as a character: '1-1,2-1'
.
Time-spanning Queries
The next two main queries, instead of report data for a single time, reports data over a time range. This time range is not predetermined and is found as the tracking data is parsed: Indeed as it is not possible to avoid detection losses, each of these queries accepts a maximumGap parameter. If tracking is lost for a duration larger than this parameter, the considered result (i.e. the trajectory or interaction) is considered done, and any latter detection or collision found later will become part of a new result.
Note
If this behavior of segmenting results according to the maximumGap parameter wants to be avoided, one can set it to a large value, e.g. one year.
Ant Trajectories
Instead of reporting instantaneous detection, computing ant trajectories [4] will report for each individual the time ranges where it was continuously detected, accordingly to the maximumGap parameter.
Results are a list of AntTrajectory
[8].
# Python
trajectories = fm.Query.ComputeAntTrajectories(e,maximumGap = 500 * fm.Duration.Millisecond)
for t in trajectories:
print("Ant %s Trajectory from %s to %s in space %d." % (fm.FormatAntID(t.Ant),t.Start,t.End(),t.Space))
print(t.Positions)
# R
d = fmQueryComputeAntTrajectories(e, maximumGap = fmMillisecond(500))
d$trajectories_summary # a data.frame with the ant, space and starting time of each trajectories
d$trajectories # a list of data.frame with each ant position for each trajectories
// C++
auto args = fort::myrmidon::Query::ComputeAntTrajectoryArgs();
// modify here default arguments
std::vector<fort::myrmidon::AntTrajectory::Ptr> trajectories;
fort::myrmidon::Query::ComputeAntTrajectories(e,trajectories,args);
As I the case of Identifying Frames, the user can choose if the ant zone should be reported with the computeZone argument.
Ant Interactions
Instead of just looking at iNstantaneous collision, one can compute ant interactions [5], i.e. time ranges where ant collides.
Both AntTrajectory
and AntInteraction
[9]
may be reported.
# Python
trajectories,interactions = fm.Query.ComputeAntInteractions(e)
for i in interactions:
print("Ant %s and %s interacts from %s to %s" % (fm.FormatAntID(i.IDs[0]),fm.FormatAntID(i.IDs[1]),i.Start,i.End))
# R
d = fmQueryComputeAntInteractions(e)
d$trajectory_summaries # the summary of trajectory like for fmQueryComputeAntTrajectories
d$trajectories # the trajectory positions like for fmQueryComputeAntTrajectories
d$interactions # a data.frame with all interactions.
// C++
auto args = fort::myrmidon::Query::ComputeAntInteractionArgs();
// modify here default arguments;
std::vector<fort::myrmidon::AntTrajectory::Ptr> trajectories;
std::vector<fort::myrmidon::AntInteraction::Ptr> interactions;
fort::myrmidon::Query::ComputeAntInteractions(e,trajectories,interactions,args);
for ( const auto & i : interactions ) {
std::cerr << "Ant " << fort::myrmidon::FormatAntID(i->IDs.first)
<< " and " << fort::myrmidon::FormatAntID(i->IDs.second)
<< " interacts from " << i->Start
<< " to " << i->End
<< std::endl;
}
Full vs Summarized Trajectory Report
As shown by the API, by default, the ComputeAntInteractions
query returns both interactions and trajectories. In that case each
AntInteraction
object contains a pair of
AntTrajectorySegment
[10] that points to a
part of the AntTrajectory
corresponding to the current
interaction.
Alternatively to simplify the results and to save memory, one can
disable the report of the full trajectory with the
reportFullTrajectories parameter. In that case the list of returned
trajectory will be empty, and instead AntTrajectorySummary
[11] will be reported. These object contains the
mean position of the ant, and the list of zone traversed during the
interaction.
The case of R is a bit more complex. If full trajectories are
reported, the $interactions
data.frame
will report the
$trajectories
index and first and last rows corresponding to the
trajectory segment. Otherwise, only a single data.frame
is
returned, with the mean position and zone for each ant.
Building Complex Queries with Matchers
Time-spanning queries accepts a matcher [13] parameter that could be used to customize the behavior of these queries.
For example one can select only 1-1 interactions type:
# Python
trajectories,interactions = fm.Query.ComputeAntInteractions(e,matcher = fm.Matcher.InteractionType(1,1))
# R
d = fmQueryComputeAntInteractions(e,matcher = fmMatcherInteractionType(1,1))
// C++
auto args = fort::myrmidon::ComputeAntInteractionsArgs();
args.Matcher = fort::myrmidon::Matcher::InteractionType(1,1);
std::vector<fort::myrmidon::AntTrajectory::Ptr> trajectories;
std::vector<fort::myrmidon::AntInteraction::Ptr> interactions;
fort::myrmidon::Query::ComputeAntInteractions(e,trajectories,interactions,args);
Or one can prune out interactions were one of the ant have a large displacement over a given duration:
# Python
trajectories,interactions = fm.Query.ComputeAntInteractions(e,matcher = fm.Matcher.AntDisplacement(under = 100, minimumGap = 500 * fm.Duration.Millisecond))
# R
d = fmQueryComputeAntInteractions(e,matcher = fmMatcherAntDisplacement(under = 100, minimumGap = fmMillisecond(500)))
// C++
auto args = fort::myrmidon::ComputeAntInteractionsArgs();
args.Matcher = fort::myrmidon::Matcher::AntDisplacement(100,500 * fort::Duration::Millisecond);
std::vector<fort::myrmidon::AntTrajectory::Ptr> trajectories;
std::vector<fort::myrmidon::AntInteraction::Ptr> interactions;
fort::myrmidon::Query::ComputeAntInteractions(e,trajectories,interactions,args);
More complex matcher can be combined together. Here we only look at
interactions with ant 001
and the two above matcher combined:
# Python
m = fm.Matcher.And(fm.Matcher.AntID(1),fm.Matcher.InteractionType(1,1),fm.Matcher.AntDisplacement(100,500 * fm.Duration.Millisecond))
trajectories,interactions = fm.Query.ComputeAntInteractions(e,matcher = m)
# R
m <- fmMatcherAnd(list(fmMatcherAntID(1),fmMatcherInteractionType(1,1),fmMatcherAntDisplacement(100,fmMillisecond(500))))
d <- fmQueryComputeAntInteractions(e,matcher = m)
// C++
auto args = fort::myrmidon::ComputeAntInteractionsArgs();
args.Matcher = fort::myrmidon::Matcher::And({fort::myrmidon::Matcher::AntID(1),
fort::myrmidon::Matcher::InteractionType(1,1),
fort::myrmidon::Matcher::AntDisplacement(100,fmMillisecond(500))});
std::vector<fort::myrmidon::AntTrajectory::Ptr> trajectories;
std::vector<fort::myrmidon::AntInteraction::Ptr> interactions;
fort::myrmidon::Query::ComputeAntInteractions(e,trajectories,interactions,args);
More matchers can be found in the Matcher
class definition
[13].
Miscellaneous Queries
TODO