2026-03-28 16:54:11 +11:00
|
|
|
#include "ANSByteTrackEigen.h"
|
|
|
|
|
#include "boost/property_tree/ptree.hpp"
|
|
|
|
|
#include "boost/property_tree/json_parser.hpp"
|
|
|
|
|
#include "boost/foreach.hpp"
|
|
|
|
|
#include "boost/optional.hpp"
|
|
|
|
|
#include <map>
|
|
|
|
|
namespace ANSCENTER {
|
|
|
|
|
|
|
|
|
|
Eigen::MatrixXf GetByteTrackEigenInputs(const char* jsonString, std::vector<std::string>&object_ids) {
|
|
|
|
|
Eigen::MatrixXf ret(0, 7); // 0 row and 7 columns
|
|
|
|
|
try {
|
|
|
|
|
boost::property_tree::ptree pt;
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << jsonString;
|
|
|
|
|
object_ids.clear();
|
|
|
|
|
boost::property_tree::read_json(ss, pt);
|
|
|
|
|
boost::property_tree::ptree detections = pt.get_child("results");
|
|
|
|
|
if (detections.size() > 0) {
|
|
|
|
|
for (const auto& detection : detections) {
|
|
|
|
|
float xMin = detection.second.get<float>("x");
|
|
|
|
|
float yMin = detection.second.get<float>("y");
|
|
|
|
|
float width = detection.second.get<float>("width");
|
|
|
|
|
float height = detection.second.get<float>("height");
|
|
|
|
|
float conf = detection.second.get<float>("prob");
|
|
|
|
|
int classId = detection.second.get<int>("class_id");
|
|
|
|
|
std::string object_id = detection.second.get<std::string>("object_id");
|
|
|
|
|
double xMax = xMin + width;
|
|
|
|
|
double yMax = yMin + height;
|
|
|
|
|
Eigen::MatrixXf newRow(1, ret.cols());
|
|
|
|
|
newRow << xMin, yMin, xMax, yMax, conf, classId;
|
|
|
|
|
ret.conservativeResize(ret.rows() + 1, Eigen::NoChange);
|
|
|
|
|
ret.row(ret.rows() - 1) = newRow;
|
|
|
|
|
object_ids.push_back(object_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
|
Eigen::MatrixXf newRow(1, ret.cols());
|
|
|
|
|
newRow << 0, 0, 0, 0, 0, 0, 0;
|
|
|
|
|
ret.conservativeResize(ret.rows() + 1, Eigen::NoChange);
|
|
|
|
|
ret.row(ret.rows() - 1) = newRow;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ANSByteTrackEigen::ANSByteTrackEigen()
|
|
|
|
|
{
|
|
|
|
|
_licenseValid = false;
|
|
|
|
|
CheckLicense();
|
|
|
|
|
|
2026-04-05 11:55:37 +10:00
|
|
|
int frame_rate = 10;
|
2026-03-28 16:54:11 +11:00
|
|
|
int track_buffer = 30;
|
|
|
|
|
float track_thresh = 0.25;
|
|
|
|
|
float track_highthres = 0.25;
|
|
|
|
|
float match_thresh = 0.8;
|
|
|
|
|
|
|
|
|
|
tracker.update_parameters(frame_rate, track_buffer, track_thresh, track_highthres, match_thresh);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ANSByteTrackEigen::~ANSByteTrackEigen() {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
bool ANSByteTrackEigen::Destroy() {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ANSByteTrackEigen::UpdateParameters(const std::string& trackerParameters) {
|
|
|
|
|
//// Use JSON Boost to parse paramter from trackerParameters
|
|
|
|
|
try {
|
|
|
|
|
int frameRate, trackBuffer;
|
|
|
|
|
double trackThreshold, highThreshold, matchThresold;
|
|
|
|
|
bool autoFrameRate;
|
|
|
|
|
frameRate = 15;
|
|
|
|
|
trackBuffer = 300;
|
|
|
|
|
trackThreshold = 0.5;
|
|
|
|
|
highThreshold = 0.1;
|
|
|
|
|
matchThresold = 0.95;
|
|
|
|
|
autoFrameRate = true;
|
|
|
|
|
if (!trackerParameters.empty()) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << trackerParameters;
|
|
|
|
|
boost::property_tree::ptree pt;
|
|
|
|
|
boost::property_tree::read_json(ss, pt);
|
|
|
|
|
auto rootNode = pt.get_child("parameters");
|
|
|
|
|
|
|
|
|
|
auto childNode = rootNode.get_child("frame_rate");
|
|
|
|
|
frameRate = childNode.get_value<int>();
|
|
|
|
|
|
|
|
|
|
childNode = rootNode.get_child("track_buffer");
|
|
|
|
|
trackBuffer = childNode.get_value<int>();
|
|
|
|
|
|
|
|
|
|
childNode = rootNode.get_child("track_threshold");
|
|
|
|
|
trackThreshold = childNode.get_value<float>();
|
|
|
|
|
|
|
|
|
|
childNode = rootNode.get_child("high_threshold");
|
|
|
|
|
highThreshold = childNode.get_value<float>();
|
|
|
|
|
|
|
|
|
|
childNode = rootNode.get_child("match_thresold");
|
|
|
|
|
matchThresold = childNode.get_value<float>();
|
|
|
|
|
|
|
|
|
|
// Optional: auto frame rate estimation
|
|
|
|
|
if (auto optNode = rootNode.get_child_optional("auto_frame_rate")) {
|
|
|
|
|
autoFrameRate = optNode->get_value<int>() != 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tracker.update_parameters(frameRate, trackBuffer, trackThreshold, highThreshold, matchThresold, autoFrameRate);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
|
this->_logger.LogFatal("ANSByteTrackEigen::UpdateParameters", e.what(), __FILE__, __LINE__);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string ANSByteTrackEigen::Update(int modelId, const std::string& detectionData) {
|
|
|
|
|
if (!_licenseValid) {
|
|
|
|
|
this->_logger.LogFatal("ANSByteTrackEigen::Update", "Invalid license", __FILE__, __LINE__);
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
boost::property_tree::ptree root;
|
|
|
|
|
boost::property_tree::ptree trackingObjects;
|
|
|
|
|
boost::property_tree::ptree pt;
|
|
|
|
|
std::vector<std::string>obj_ids;
|
|
|
|
|
Eigen::MatrixXf objects = GetByteTrackEigenInputs(detectionData.c_str(), obj_ids);
|
|
|
|
|
////2. Do tracking
|
|
|
|
|
const auto outputs = tracker.update(objects, obj_ids);
|
|
|
|
|
if (outputs.size() > 0) {
|
|
|
|
|
for (int i = 0; i < outputs.size(); i++) {
|
|
|
|
|
double x = outputs[i]._tlwh[0]; //top left x
|
|
|
|
|
double y = outputs[i]._tlwh[1]; //top left y
|
|
|
|
|
double w = outputs[i]._tlwh[2]; //width
|
|
|
|
|
double h = outputs[i]._tlwh[3]; //height
|
|
|
|
|
double left = x;
|
|
|
|
|
double top = y;
|
|
|
|
|
double right = x + w;
|
|
|
|
|
double bottom = y + h;
|
|
|
|
|
int object_id = outputs[i].get_frame_id(); // Should be object id
|
|
|
|
|
bool is_valid = outputs[i].get_is_activated();
|
|
|
|
|
boost::property_tree::ptree trackingNode;
|
|
|
|
|
trackingNode.put("model_id", modelId);
|
|
|
|
|
trackingNode.put("track_id", outputs[i].get_track_id());
|
|
|
|
|
trackingNode.put("class_id", outputs[i].get_track_id());//It should be class id
|
|
|
|
|
trackingNode.put("prob", outputs[i].get_score());
|
|
|
|
|
trackingNode.put("x", outputs[i]._tlwh[0]);
|
|
|
|
|
trackingNode.put("y", outputs[i]._tlwh[1]);
|
|
|
|
|
trackingNode.put("width", outputs[i]._tlwh[2]);
|
|
|
|
|
trackingNode.put("height", outputs[i]._tlwh[3]);
|
|
|
|
|
trackingNode.put("left", left);
|
|
|
|
|
trackingNode.put("top", top);
|
|
|
|
|
trackingNode.put("right", right);
|
|
|
|
|
trackingNode.put("bottom", bottom);
|
|
|
|
|
trackingNode.put("valid", is_valid);
|
|
|
|
|
trackingNode.put("object_id", object_id);
|
|
|
|
|
trackingObjects.push_back(std::make_pair("", trackingNode));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//3. Convert result
|
|
|
|
|
root.add_child("results", trackingObjects);
|
|
|
|
|
std::ostringstream stream;
|
|
|
|
|
boost::property_tree::write_json(stream, root,false);
|
|
|
|
|
std::string st = stream.str();
|
|
|
|
|
return st;
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
|
this->_logger.LogFatal("ANSByteTrackEigen::Update", e.what(), __FILE__, __LINE__);
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
std::vector<TrackerObject> ANSByteTrackEigen::UpdateTracker(int modelId, const std::vector<TrackerObject>& detectionObjects) {
|
|
|
|
|
if (!_licenseValid) {
|
|
|
|
|
this->_logger.LogFatal("ANSByteTrackNCNN::UpdateTracker", "Invalid license", __FILE__, __LINE__);
|
|
|
|
|
return std::vector<TrackerObject>();
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
std::vector<TrackerObject> trackingResults;
|
|
|
|
|
Eigen::MatrixXf ret(0, 7); // 0 row and 7 columns
|
|
|
|
|
std::vector<std::string> object_ids;
|
|
|
|
|
for (const auto& detection : detectionObjects) {
|
|
|
|
|
float xMin = detection.x;
|
|
|
|
|
float yMin = detection.y;
|
|
|
|
|
float width = detection.width;
|
|
|
|
|
float height = detection.height;
|
|
|
|
|
float conf = detection.prob;
|
|
|
|
|
int classId = detection.class_id;
|
|
|
|
|
std::string object_id = detection.object_id;
|
|
|
|
|
double xMax = xMin + width;
|
|
|
|
|
double yMax = yMin + height;
|
|
|
|
|
Eigen::MatrixXf newRow(1, ret.cols());
|
|
|
|
|
newRow << xMin, yMin, xMax, yMax, conf, classId;
|
|
|
|
|
ret.conservativeResize(ret.rows() + 1, Eigen::NoChange);
|
|
|
|
|
ret.row(ret.rows() - 1) = newRow;
|
|
|
|
|
object_ids.push_back(object_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//2. Do tracking
|
|
|
|
|
const auto outputs = tracker.update(ret, object_ids);
|
|
|
|
|
if (outputs.size() > 0) {
|
|
|
|
|
for (int i = 0; i < outputs.size(); i++) {
|
|
|
|
|
TrackerObject trackObj;
|
|
|
|
|
double x = outputs[i]._tlwh[0]; //top left x
|
|
|
|
|
double y = outputs[i]._tlwh[1]; //top left y
|
|
|
|
|
double w = outputs[i]._tlwh[2]; //width
|
|
|
|
|
double h = outputs[i]._tlwh[3]; //height
|
|
|
|
|
double left = x;
|
|
|
|
|
double top = y;
|
|
|
|
|
double right = x + w;
|
|
|
|
|
double bottom = y + h;
|
|
|
|
|
std::string object_id = outputs[i].object_id; // Should be object id
|
|
|
|
|
bool is_valid = outputs[i].get_is_activated();
|
|
|
|
|
|
|
|
|
|
trackObj.track_id = outputs[i].get_track_id();
|
|
|
|
|
trackObj.class_id = outputs[i].class_id;
|
|
|
|
|
trackObj.prob = outputs[i].get_score();
|
|
|
|
|
trackObj.x = x;
|
|
|
|
|
trackObj.y = y;
|
|
|
|
|
trackObj.width = w;
|
|
|
|
|
trackObj.height = h;
|
|
|
|
|
trackObj.left =x;
|
|
|
|
|
trackObj.top = y;
|
|
|
|
|
trackObj.right = right;
|
|
|
|
|
trackObj.bottom = bottom;
|
|
|
|
|
trackObj.object_id = object_id;
|
|
|
|
|
trackingResults.push_back(trackObj);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return trackingResults;
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception& e) {
|
|
|
|
|
this->_logger.LogFatal("ANSByteTrackNCNN::UpdateTracker", e.what(), __FILE__, __LINE__);
|
|
|
|
|
return std::vector<TrackerObject>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|