function [OutCount, OutBBox] = videoobjtracker(Area, Centroid, BBox, maxNumTracks, ...
    areaChangeFraction, centroidChangeFraction, maxConsecutiveMiss, ...
    minPersistenceRatio, alarmCount)
% This function implements an abandoned object tracker.

%   Copyright 2006-2010 The MathWorks, Inc.

% turn some inputs from percentages to fractions
areaChangeFraction = areaChangeFraction/100;
centroidChangeFraction = centroidChangeFraction/100;

Count = cast(size(BBox,1),class(BBox));

% Initialize output variables
OutCount = zeros(1,class(Count));
OutBBox  = zeros([maxNumTracks size(BBox,2)],class(BBox));

% Declare integer valued, persistent elements of a Track
persistent track;
if isempty(track)
    track = repmat(empty_track,1,maxNumTracks);
end
    
%%%%%%%%%%%%%%%%%%%%%%%%%%% CORE ALGORITHM %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if Count % if blobs were found
   % process tracks
   
   for i=1:double(Count) % scan through all the incoming blobs

       % quantize the values that we'll be tracking
       inCentroid = round(Centroid(i,:)/2);
       inArea     = round(double(Area(i))/2);
       inBBox     = BBox(i,:);
       
       found = false;
       for j=1:maxNumTracks
           T = track(j);
           if T.isTrackActive
               trackArea     = double(T.area);
               trackCentroid = double(T.centroid);
               
               areaRatio = abs(trackArea - inArea)/(trackArea+1);
               centDiff  = inCentroid - trackCentroid;
               centRatio = (centDiff*centDiff')/inArea;
                           
               % does this object belong to an existing track?
               if (areaRatio < areaChangeFraction) && ...
                  (centRatio < centroidChangeFraction)
                  found = true;
                  break;
               end
           end
       end
       
       if found % update existing track
           track(j).justHit = true;
           track(j).hitCount= track(j).hitCount + 1;
       else     % create new track
           % find first unused track
           for k=1:maxNumTracks
               if ~track(k).isTrackActive
                   break;
               end
           end
           % fill track information
           track(k).area = int32(inArea);
           track(k).centroid = int32(inCentroid);
           track(k).bbox = int32(inBBox);
           track(k).age = int32(1);
           track(k).hitCount = int32(1);
           track(k).missCount = int32(0);
           
           track(k).justHit = true;
           track(k).isTrackActive = true;
       end
   end
end

% Age existing tracks.  Update hit/miss counts and see which tracks were
% just hit.
for i=1:maxNumTracks
    track(i).age = track(i).age + 1;
    % Find all tracks that were justHit and reset justHit flag
    % For the tracks that were just hit, increment hitCount and clear 
    % missCount for the tracks that were not just hit, increment missCount
    if track(i).justHit
        track(i).justHit = false;
        track(i).missCount = int32(0); % clear consecutive misses
    else
        track(i).missCount = track(i).missCount + 1;
    end
    
    % Mark track for deletion if: 
    %  * consecutive misses exceeds maxConsecutiveMiss
    %  * ratio of hitCount to age drops below minPersistenceRatio
    deleteMissFlag  = (track(i).missCount > maxConsecutiveMiss);
    deleteRatioFlag = (track(i).hitCount < track(i).age*...
        minPersistenceRatio);
    
    if (deleteMissFlag || deleteRatioFlag)
        track(i).isTrackActive = false;
    end
end

% Determine which objects are stationary
OutCount = zeros(1,class(OutCount));
for i=1:maxNumTracks
    if track(i).hitCount >= alarmCount
        OutCount = OutCount + 1;
        OutBBox(OutCount, :) = track(i).bbox;
    end
end

%------------------------------------------------------------------------%
function t = empty_track

t.area          = int32(0);
t.centroid      = int32([1 1]);
t.bbox          = int32([1 1 0 0]);
t.age           = int32(0);
t.hitCount      = int32(0);
t.missCount     = int32(0);
t.justHit       = false;
t.isTrackActive = false;
