#include "objects_tracker.h" #include "track_link.h" #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) ObjectsTracker::ObjectsTracker() : parameters(), numTrackedSteps(0) { } ObjectsTracker::~ObjectsTracker() { } ObjectsTracker::Parameters::Parameters() { maxTrackLifetime=6; numLastPositionsToTrack=4; numDetectedToWaitBeforeFirstShow=0; numStepsToWaitBeforeFirstShow=6; numStepsToTrackWithoutDetectingIfObjectHasNotBeenShown=4; numStepsToShowWithoutDetecting=5; } float CalculateIOU(Rect_T r1, Rect_T r2) { int xmin0 = r1.x; int ymin0 = r1.y; int xmax0 = r1.x +r1.width; int ymax0 = r1.y +r1.height; int xmin1 = r2.x; int ymin1 = r2.y; int xmax1 = r2.x +r2.width; int ymax1 = r2.y +r2.height; float w = max(0.f, min(xmax0, xmax1) - max(xmin0, xmin1)); float h = max(0.f, min(ymax0, ymax1) - max(ymin0, ymin1)); float i = w * h; float u = (xmax0 - xmin0) * (ymax0 - ymin0) + (xmax1 - xmin1) * (ymax1 - ymin1) - i; return u <= 0.f ? 0.f : (i / u); } float CalculateArea(Rect_T r) { float i = r.width * r.height; return i; } void ObjectsTracker::predict_loctation(TrackedObject& curObject, int image_width,int image_height, float& pre_x, float& pre_y, float& v_x, float& v_y){ int numpositions = (int)curObject.lastPositions.size(); Rect_T prevRect = curObject.lastPositions[numpositions-1]; Rect_T tmpRect=curObject.lastPositions[numpositions-2]; float vx_1 = (prevRect.x -tmpRect.x)/(1.0f +curObject.preNumFramesNotDetected); float vx_2 = (prevRect.x +prevRect.width -tmpRect.x -tmpRect.width)/(1.0f +curObject.preNumFramesNotDetected); float vx_ = min(vx_1, vx_2); float vy_1 = (prevRect.y -tmpRect.y)/(1.0f +curObject.preNumFramesNotDetected); float vy_2 = (prevRect.y +prevRect.height -tmpRect.y -tmpRect.height)/(1.0f +curObject.preNumFramesNotDetected); float vy_ = min(vy_1, vy_2); v_x = 0.5f*vx_ + 0.5f*curObject.vx; v_y = 0.5f*vy_ + 0.5f*curObject.vy; int x = (int)(prevRect.x + v_x*(1 ) *0.7f +0.5f); int y = (int)(prevRect.y + v_y*(1 ) *0.7f +0.5f); x = x >= 0 ? x : 0; y = y >= 0 ? y : 0; if(x + prevRect.width >= image_width) x -= (x + prevRect.width -image_width +1); if(y + prevRect.height >= image_height) y -= (y + prevRect.height -image_height +1); x = x >= 0 ? x : 0; y = y >= 0 ? y : 0; pre_x = x; pre_y = y; } void ObjectsTracker::getObjects(std::vector& result) { result.clear(); for(size_t i=0; i < trackedObjects.size(); i++) { ObjectStatus status; Rect_T r=calcTrackedObjectPositionToShow((int)i, status); if (CalculateArea(r)==0.f || trackedObjects[i].numFramesDetected < 2){ continue; } result.push_back(ExtObject(trackedObjects[i].id, r, trackedObjects[i].predict_loc_when_miss, trackedObjects[i].smooth_Positionn, status, trackedObjects[i].numFramesDetected, trackedObjects[i].numFramesNotDetected, trackedObjects[i].obj_class, trackedObjects[i].score, trackedObjects[i].miss)); } } void ObjectsTracker::updateTrackedObjects(const std::vector& detectedObjects, const std::vector objects_class , const std::vector objects_score, int maxTrackLifetime, int image_width,int image_height) { enum { NEW_RECTANGLE=-1, INTERSECTED_RECTANGLE=-2 }; int N1=(int)trackedObjects.size(); int N2=(int)detectedObjects.size(); for(int i=0; i < N1; i++) { trackedObjects[i].numDetectedFrames++; } std::vector correspondence(detectedObjects.size(), NEW_RECTANGLE); std::vector correspondenceScore(detectedObjects.size(), 0); for(int i=0; i < N1; i++) { TrackedObject& curObject=trackedObjects[i]; int bestIndex=-1; float bestArea=-1; int numpositions=(int)curObject.lastPositions.size(); Rect_T prevRect=curObject.lastPositions[numpositions-1]; //save predict loctation if(numpositions>1) { float pre_x, pre_y, vx, vy; predict_loctation(curObject, image_width, image_height, pre_x, pre_y, vx, vy); curObject.vx = vx; curObject.vy = vy; prevRect.x = (int)pre_x; prevRect.y = (int)pre_y; curObject.predict_loc_when_miss = prevRect; } //search track loaction for(int j=0; j < N2; j++) { float percentage_IOU = CalculateIOU(prevRect, detectedObjects[j]); if ( percentage_IOU > 0.1f ) {//&& objects_class[j] == curObject.obj_class float trackScore = percentage_IOU *1.f / (curObject.numFramesNotDetected + 1); if ( percentage_IOU > bestArea && correspondenceScore[j] < trackScore) { bestIndex = j; bestArea = percentage_IOU; correspondenceScore[j] = trackScore; } } } if (bestIndex >= 0) { correspondence[bestIndex] = i; } } //select track loaction for (int i = 0; i < N1; i++) { TrackedObject& curObject = trackedObjects[i]; int bestIndex = -1; for (int j = 0; j < N2; j++) { if (correspondence[j] == i){ bestIndex = j; break; } } if (bestIndex >= 0) { correspondence[bestIndex] = i; for (int j = 0; j < N2; j++) { if (correspondence[j] >= 0) continue; float percentage_IOU = CalculateIOU(detectedObjects[j], detectedObjects[bestIndex]); if (percentage_IOU > 0.45f ){//&& objects_class[j] == curObject.obj_class correspondence[j] = INTERSECTED_RECTANGLE; } } curObject.numFramesDetected++; } else { curObject.numFramesNotDetected++; curObject.miss = 1; } } //allocate new detected location for(int j=0; j < N2; j++) { int i = correspondence[j]; if (i >= 0) {//add position trackedObjects[i].lastPositions.push_back(detectedObjects[j]); while ((int)trackedObjects[i].lastPositions.size() > (int) parameters.numLastPositionsToTrack) { trackedObjects[i].lastPositions.erase(trackedObjects[i].lastPositions.begin()); } trackedObjects[i].preNumFramesNotDetected = trackedObjects[i].numFramesNotDetected; trackedObjects[i].numFramesNotDetected = 0; trackedObjects[i].score = objects_score[j]; trackedObjects[i].obj_class = objects_class[j]; trackedObjects[i].miss = 0; //smooth rect trackedObjects[i].smooth_Positionn.width = (trackedObjects[i].smooth_Positionn.width *1 +detectedObjects[j].width )/(2); trackedObjects[i].smooth_Positionn.height = (trackedObjects[i].smooth_Positionn.height *1 +detectedObjects[j].height)/(2); Rect_T r_smooth = trackedObjects[i].smooth_Positionn; float weight_p = 0.5f, weight_n = 1.f; trackedObjects[i].smooth_Positionn.x = (int)(((r_smooth.x +r_smooth.width*0.5f) *weight_p +(detectedObjects[j].x +detectedObjects[j].width*0.5f)*weight_n)/(weight_p +weight_n) +trackedObjects[i].vx *weight_p /(weight_p +weight_n) -r_smooth.width *0.5f); trackedObjects[i].smooth_Positionn.y = (int)(((r_smooth.y +r_smooth.height*0.5f) *weight_p +(detectedObjects[j].y +detectedObjects[j].height*0.5f)*weight_n)/(weight_p +weight_n) +trackedObjects[i].vy *weight_p /(weight_p +weight_n)-r_smooth.height *0.5f); } else if (i==NEW_RECTANGLE){ //new object trackedObjects.push_back(detectedObjects[j]); int _N2 = (int)trackedObjects.size(); trackedObjects[_N2-1].obj_class = objects_class[j]; trackedObjects[_N2-1].score = objects_score[j]; correspondence[j] = _N2 -1; trackedObjects[_N2-1].smooth_Positionn.width = detectedObjects[j].width; trackedObjects[_N2-1].smooth_Positionn.height = detectedObjects[j].height; trackedObjects[_N2-1].smooth_Positionn.x = (detectedObjects[j].x); trackedObjects[_N2-1].smooth_Positionn.y = (detectedObjects[j].y); } } std::vector::iterator it=trackedObjects.begin(); while( it != trackedObjects.end() ) { if ( (it->numFramesNotDetected > maxTrackLifetime) #if 0 || ( (it->numDetectedFrames <= parameters.numStepsToWaitBeforeFirstShow) && (it->numFramesNotDetected > parameters.numStepsToTrackWithoutDetectingIfObjectHasNotBeenShown) ) #endif ) { it=trackedObjects.erase(it); } else { it++; } } } Rect_T ObjectsTracker::calcTrackedObjectPositionToShow(int i, ObjectStatus& status) const { Rect_T r; r.x = 0; r.y = 0; r.width = 0; r.height = 0; if ( (i < 0) || (i >= (int)trackedObjects.size()) ) { status = WRONG_OBJECT; return r; } #if 0 if (trackedObjects[i].numDetectedFrames <= parameters.numStepsToWaitBeforeFirstShow) { status = DETECTED_NOT_SHOWN_YET; return r; } if (trackedObjects[i].numFramesDetected <= parameters.numDetectedToWaitBeforeFirstShow) { status = DETECTED_NOT_SHOWN_YET; return r; } if (trackedObjects[i].numFramesNotDetected > parameters.numStepsToShowWithoutDetecting) { status = DETECTED_TEMPORARY_LOST; return r; } #endif const TrackedObject::PositionsVector& lastPositions=trackedObjects[i].lastPositions; int N=(int)lastPositions.size(); if (N<=0) { status = WRONG_OBJECT; return r; } return lastPositions[N-1]; }