#include "EigenKalmanBBoxTrack.h" namespace ByteTrackEigen { // Initializing the static shared Kalman filter KalmanFilter KalmanBBoxTrack::shared_kalman; /** * @brief Default constructor for KalmanBBoxTrack. * Initializes member variables to their default state. */ KalmanBBoxTrack::KalmanBBoxTrack() : BaseTrack(), _tlwh(Eigen::Vector4d::Zero()), // Initializes the bounding box to a zero vector kalman_filter(KalmanFilter()), // Default initialization of the Kalman filter mean(Eigen::VectorXd()), // Initializes the mean state vector to default covariance(Eigen::MatrixXd()), // Initializes the covariance matrix to default tracklet_len(0) // Initializes the tracklet length to zero { } /** * @brief Constructor for KalmanBBoxTrack with initial bounding box and detection score. * Throws an exception if the tlwh vector does not contain exactly 4 values. * * @param tlwh Initial bounding box in top-left width-height format. * @param score Detection score associated with the bounding box. */ KalmanBBoxTrack::KalmanBBoxTrack(const std::vector tlwh, float score, int class_id_,std::string object_id_): BaseTrack(score), _tlwh(tlwh.size() == 4 ? Eigen::Vector4d(tlwh[0], tlwh[1], tlwh[2], tlwh[3]) : Eigen::Vector4d::Zero()), // Ensures the bounding box has 4 elements kalman_filter(KalmanFilter()), // Initializes the Kalman filter mean(Eigen::VectorXd()), // Initializes the mean state vector covariance(Eigen::MatrixXd()), // Initializes the covariance matrix tracklet_len(0), // Initializes the tracklet length to zero class_id(class_id_), object_id(object_id_), detection_count_(1), class_id_locked_(false) { // Validation of tlwh size if (tlwh.size() != 4) { throw std::invalid_argument("tlwh vector must contain exactly 4 values."); } class_id_scores_[class_id_] = score; } void KalmanBBoxTrack::voteClassId(int new_class_id, float score) { if (class_id_locked_) return; class_id_scores_[new_class_id] += score; detection_count_++; int best_id = class_id; float best_score = 0.0f; for (const auto& entry : class_id_scores_) { if (entry.second > best_score) { best_score = entry.second; best_id = entry.first; } } class_id = best_id; if (detection_count_ >= CLASS_ID_LOCK_FRAMES) { class_id_locked_ = true; } } /** * @brief Static method to perform the prediction step on multiple KalmanBBoxTrack objects. * Updates the mean and covariance of each track based on the shared Kalman filter. * * @param tracks Vector of shared pointers to KalmanBBoxTrack instances. */ void KalmanBBoxTrack::multi_predict(std::vector>& tracks) { if (tracks.empty()) { return; // Early exit if no tracks to process } // Extract mean and covariance for each track std::vector multi_means; std::vector multi_covariances; for (const auto track : tracks) { multi_means.push_back(track->mean); // Eigen performs deep copy by default multi_covariances.push_back(track->covariance); } // For each track, set velocity to 0 if it's not in Tracked state for (size_t i = 0; i < tracks.size(); ++i) { if (tracks[i]->get_state() != TrackState::Tracked) { multi_means[i](7) = 0; // Zero out the velocity if not tracked } } // Convert vectors to Eigen matrices for Kalman filter processing Eigen::MatrixXd means_matrix(multi_means.size(), multi_means[0].size()); for (size_t i = 0; i < multi_means.size(); ++i) { means_matrix.row(i) = multi_means[i]; } int n = (int)multi_covariances[0].rows(); // Assuming square matrices for covariance Eigen::MatrixXd covariances_matrix(n, n * multi_covariances.size()); for (size_t i = 0; i < multi_covariances.size(); ++i) { covariances_matrix.middleCols(i * n, n) = multi_covariances[i]; } // Use shared kalman filter for prediction Eigen::MatrixXd predicted_means, predicted_covariances; std::tie(predicted_means, predicted_covariances) = shared_kalman.multi_predict(means_matrix, covariances_matrix); // Update each track with the predicted mean and covariance for (size_t i = 0; i < tracks.size(); ++i) { tracks[i]->mean = predicted_means.col(i); } int pcr = (int)predicted_covariances.rows(); // Assuming the block matrix is composed of square matrices size_t num_matrices = predicted_covariances.cols() / pcr; for (size_t i = 0; i < num_matrices; ++i) { Eigen::MatrixXd block = predicted_covariances.middleCols(i * pcr, pcr); tracks[i]->covariance = block; } } /** * @brief Converts bounding box from tlwh format to xyah format (center x, center y, aspect ratio, height). * * @param tlwh Bounding box in tlwh format. * @return Eigen::VectorXd Bounding box in xyah format. */ Eigen::VectorXd KalmanBBoxTrack::tlwh_to_xyah(Eigen::VectorXd tlwh) { Eigen::VectorXd ret = tlwh; ret.head(2) += ret.segment(2, 2) / 2.0; // Adjust x, y to be the center of the bounding box if (ret(3) > 0) ret(2) /= ret(3); // Update aspect ratio else ret(2) = 0; return ret; } /** * @brief Activates the track with a given Kalman filter and frame ID. * Initializes the track ID and sets up the Kalman filter with the bounding box information. * * @param kalman_filter Kalman filter to be used for this track. * @param frame_id Frame ID at which this track is activated. */ void KalmanBBoxTrack::activate(KalmanFilter& kalman_filter, int frame_id) { // Link to the provided Kalman filter this->kalman_filter = kalman_filter; // Initialize track ID this->track_id = BaseTrack::next_id(); // Convert bounding box to the xyah format and initiate the Kalman filter std::tie(this->mean, this->covariance) = this->kalman_filter.initiate(this->tlwh_to_xyah(this->_tlwh)); this->tracklet_len = 0; this->state = TrackState::Tracked; // Check if track is activated in the first frame this->is_activated = (frame_id == 1); this->frame_id = frame_id; this->start_frame = frame_id; } /** * @brief Updates the track with a new detection, potentially assigning a new ID. * * @param new_track The new detection represented as a KalmanBBoxTrack object. * @param frame_id Frame ID of the new detection. * @param new_id Flag indicating whether to assign a new ID to this track. */ void KalmanBBoxTrack::update_track(const KalmanBBoxTrack& new_track, int frame_id, bool new_id) { // Track update logic this->frame_id = frame_id; this->tracklet_len++; std::tie(this->mean, this->covariance) = this->kalman_filter.update(this->mean, this->covariance, this->tlwh_to_xyah(new_track.tlwh())); this->state = TrackState::Tracked; this->is_activated = true; // Assigning a new ID if necessary if (new_id) { this->track_id = BaseTrack::next_id(); } // Update the score and vote on class_id this->score = new_track.get_score(); voteClassId(new_track.class_id, new_track.get_score()); this->object_id = new_track.object_id; } /** * @brief Re-activates the track with a new detection, possibly assigning a new ID. * Essentially a wrapper for the update_track method. * * @param new_track The new detection to reactivate the track with. * @param frame_id Frame ID of the reactivation. * @param new_id Flag indicating whether to assign a new ID. */ void KalmanBBoxTrack::re_activate(const KalmanBBoxTrack& new_track, int frame_id, bool new_id) { update_track(new_track, frame_id, new_id); } /** * @brief Updates the track with a new detection without changing its ID. * Wrapper for the update_track method without the new_id flag. * * @param new_track The new detection to update the track with. * @param frame_id Frame ID of the update. */ void KalmanBBoxTrack::update(const KalmanBBoxTrack& new_track, int frame_id) { update_track(new_track, frame_id); } /** * @brief Converts tlwh bounding box to top-left bottom-right (tlbr) format. * * @param tlwh Bounding box in tlwh format. * @return Eigen::Vector4d Bounding box in tlbr format. */ Eigen::Vector4d KalmanBBoxTrack::tlwh_to_tlbr(const Eigen::Vector4d tlwh) { Eigen::Vector4d ret = tlwh; ret.tail<2>() += ret.head<2>(); return ret; } /** * @brief Converts tlbr bounding box to tlwh format. * * @param tlbr Bounding box in tlbr format. * @return Eigen::Vector4d Bounding box in tlwh format. */ Eigen::Vector4d KalmanBBoxTrack::tlbr_to_tlwh(const Eigen::Vector4d tlbr) { Eigen::Vector4d ret = tlbr; ret.tail<2>() -= ret.head<2>(); return ret; } /** * @brief Get current bounding box in tlwh format * * @return Eigen::Vector4d Bounding box in tlwh format. */ Eigen::Vector4d KalmanBBoxTrack::tlwh() const { if (mean.isZero(0)) { // Checking if 'mean' is uninitialized or zero return _tlwh; } Eigen::Vector4d ret = mean.head(4); ret[2] *= ret[3]; ret[0] -= ret[2] / 2.0; ret[1] -= ret[3] / 2.0; return ret; } /** * @brief Returns the bounding box in tlbr format. * * @return Eigen::Vector4d Bounding box in tlbr format. */ Eigen::Vector4d KalmanBBoxTrack::tlbr() const { return this->tlwh_to_tlbr(this->tlwh()); } }