Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix yolox no detections #991

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions boxmot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
__version__ = '10.0.15'
__version__ = '10.0.14'

from pathlib import Path

from boxmot.trackers import StrongSORT
from boxmot.trackers import OCSort as OCSORT
from boxmot.trackers import BYTETracker
from boxmot.trackers import BoTSORT
from boxmot.trackers import DeepOCSort as DeepOCSORT
from boxmot.appearance.reid_multibackend import ReIDDetectMultiBackend
from boxmot.strongsort.strong_sort import StrongSORT
from boxmot.ocsort.ocsort import OCSort as OCSORT
from boxmot.bytetrack.byte_tracker import BYTETracker
from boxmot.botsort.bot_sort import BoTSORT
from boxmot.deepocsort.ocsort import OCSort as DeepOCSORT
from boxmot.deep.reid_multibackend import ReIDDetectMultiBackend

from boxmot.tracker_zoo import create_tracker, get_tracker_config

Expand Down
13 changes: 0 additions & 13 deletions boxmot/appearance/__init__.py

This file was deleted.

File renamed without changes.
File renamed without changes.
18 changes: 10 additions & 8 deletions boxmot/trackers/botsort/bot_sort.py → boxmot/botsort/bot_sort.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import cv2
import matplotlib.pyplot as plt
import numpy as np
from collections import deque

from ...utils.matching import iou_distance, fuse_score, linear_assignment, embedding_distance, fuse_motion
from ...utils.gmc import GlobalMotionCompensation
from .matching import iou_distance, fuse_score, linear_assignment, embedding_distance, fuse_motion
from .gmc import GMC
from .basetrack import BaseTrack, TrackState
from ...motion.adapters import BotSortKalmanFilterAdapter
from .kalman_filter import KalmanFilter
import torch

from ...appearance.reid_multibackend import ReIDDetectMultiBackend
from ...utils.ops import xyxy2xywh, xywh2xyxy
from boxmot.deep.reid_multibackend import ReIDDetectMultiBackend
from boxmot.utils.ops import xyxy2xywh, xywh2xyxy


class STrack(BaseTrack):
shared_kalman = BotSortKalmanFilterAdapter()
shared_kalman = KalmanFilter()

def __init__(self, tlwh, score, cls, feat=None, feat_history=50):

Expand Down Expand Up @@ -254,7 +256,7 @@ def __init__(self,

self.buffer_size = int(frame_rate / 30.0 * track_buffer)
self.max_time_lost = self.buffer_size
self.kalman_filter = BotSortKalmanFilterAdapter()
self.kalman_filter = KalmanFilter()

# ReID module
self.proximity_thresh = proximity_thresh
Expand All @@ -263,7 +265,7 @@ def __init__(self,

self.model = ReIDDetectMultiBackend(weights=model_weights, device=device, fp16=fp16)

self.gmc = GlobalMotionCompensation(method=cmc_method, verbose=[None,False])
self.gmc = GMC(method=cmc_method, verbose=[None,False])

def update(self, dets, img):

Expand Down
File renamed without changes.
6 changes: 4 additions & 2 deletions boxmot/utils/gmc.py → boxmot/botsort/gmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import time


class GlobalMotionCompensation:
class GMC:
def __init__(self, method='sparseOptFlow', downscale=2, verbose=None):
super(GMC, self).__init__()

self.method = method
self.downscale = max(1, int(downscale))

Expand Down Expand Up @@ -53,7 +55,7 @@ def __init__(self, method='sparseOptFlow', downscale=2, verbose=None):
elif self.method == 'none' or self.method == 'None':
self.method = 'none'
else:
raise ValueError("Error: Unknown GMC method:" + method)
raise ValueError("Error: Unknown CMC method:" + method)

self.prevFrame = None
self.prevKeyPoints = None
Expand Down
269 changes: 269 additions & 0 deletions boxmot/botsort/kalman_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
# vim: expandtab:ts=4:sw=4
import numpy as np
import scipy.linalg


"""
Table for the 0.95 quantile of the chi-square distribution with N degrees of
freedom (contains values for N=1, ..., 9). Taken from MATLAB/Octave's chi2inv
function and used as Mahalanobis gating threshold.
"""
chi2inv95 = {
1: 3.8415,
2: 5.9915,
3: 7.8147,
4: 9.4877,
5: 11.070,
6: 12.592,
7: 14.067,
8: 15.507,
9: 16.919}


class KalmanFilter(object):
"""
A simple Kalman filter for tracking bounding boxes in image space.

The 8-dimensional state space

x, y, w, h, vx, vy, vw, vh

contains the bounding box center position (x, y), width w, height h,
and their respective velocities.

Object motion follows a constant velocity model. The bounding box location
(x, y, w, h) is taken as direct observation of the state space (linear
observation model).

"""

def __init__(self):
ndim, dt = 4, 1.

# Create Kalman filter model matrices.
self._motion_mat = np.eye(2 * ndim, 2 * ndim)
for i in range(ndim):
self._motion_mat[i, ndim + i] = dt
self._update_mat = np.eye(ndim, 2 * ndim)

# Motion and observation uncertainty are chosen relative to the current
# state estimate. These weights control the amount of uncertainty in
# the model. This is a bit hacky.
self._std_weight_position = 1. / 20
self._std_weight_velocity = 1. / 160

def initiate(self, measurement):
"""Create track from unassociated measurement.

Parameters
----------
measurement : ndarray
Bounding box coordinates (x, y, w, h) with center position (x, y),
width w, and height h.

Returns
-------
(ndarray, ndarray)
Returns the mean vector (8 dimensional) and covariance matrix (8x8
dimensional) of the new track. Unobserved velocities are initialized
to 0 mean.

"""
mean_pos = measurement
mean_vel = np.zeros_like(mean_pos)
mean = np.r_[mean_pos, mean_vel]

std = [
2 * self._std_weight_position * measurement[2],
2 * self._std_weight_position * measurement[3],
2 * self._std_weight_position * measurement[2],
2 * self._std_weight_position * measurement[3],
10 * self._std_weight_velocity * measurement[2],
10 * self._std_weight_velocity * measurement[3],
10 * self._std_weight_velocity * measurement[2],
10 * self._std_weight_velocity * measurement[3]]
covariance = np.diag(np.square(std))
return mean, covariance

def predict(self, mean, covariance):
"""Run Kalman filter prediction step.

Parameters
----------
mean : ndarray
The 8 dimensional mean vector of the object state at the previous
time step.
covariance : ndarray
The 8x8 dimensional covariance matrix of the object state at the
previous time step.

Returns
-------
(ndarray, ndarray)
Returns the mean vector and covariance matrix of the predicted
state. Unobserved velocities are initialized to 0 mean.

"""
std_pos = [
self._std_weight_position * mean[2],
self._std_weight_position * mean[3],
self._std_weight_position * mean[2],
self._std_weight_position * mean[3]]
std_vel = [
self._std_weight_velocity * mean[2],
self._std_weight_velocity * mean[3],
self._std_weight_velocity * mean[2],
self._std_weight_velocity * mean[3]]
motion_cov = np.diag(np.square(np.r_[std_pos, std_vel]))

mean = np.dot(mean, self._motion_mat.T)
covariance = np.linalg.multi_dot((
self._motion_mat, covariance, self._motion_mat.T)) + motion_cov

return mean, covariance

def project(self, mean, covariance):
"""Project state distribution to measurement space.

Parameters
----------
mean : ndarray
The state's mean vector (8 dimensional array).
covariance : ndarray
The state's covariance matrix (8x8 dimensional).

Returns
-------
(ndarray, ndarray)
Returns the projected mean and covariance matrix of the given state
estimate.

"""
std = [
self._std_weight_position * mean[2],
self._std_weight_position * mean[3],
self._std_weight_position * mean[2],
self._std_weight_position * mean[3]]
innovation_cov = np.diag(np.square(std))

mean = np.dot(self._update_mat, mean)
covariance = np.linalg.multi_dot((
self._update_mat, covariance, self._update_mat.T))
return mean, covariance + innovation_cov

def multi_predict(self, mean, covariance):
"""Run Kalman filter prediction step (Vectorized version).
Parameters
----------
mean : ndarray
The Nx8 dimensional mean matrix of the object states at the previous
time step.
covariance : ndarray
The Nx8x8 dimensional covariance matrics of the object states at the
previous time step.
Returns
-------
(ndarray, ndarray)
Returns the mean vector and covariance matrix of the predicted
state. Unobserved velocities are initialized to 0 mean.
"""
std_pos = [
self._std_weight_position * mean[:, 2],
self._std_weight_position * mean[:, 3],
self._std_weight_position * mean[:, 2],
self._std_weight_position * mean[:, 3]]
std_vel = [
self._std_weight_velocity * mean[:, 2],
self._std_weight_velocity * mean[:, 3],
self._std_weight_velocity * mean[:, 2],
self._std_weight_velocity * mean[:, 3]]
sqr = np.square(np.r_[std_pos, std_vel]).T

motion_cov = []
for i in range(len(mean)):
motion_cov.append(np.diag(sqr[i]))
motion_cov = np.asarray(motion_cov)

mean = np.dot(mean, self._motion_mat.T)
left = np.dot(self._motion_mat, covariance).transpose((1, 0, 2))
covariance = np.dot(left, self._motion_mat.T) + motion_cov

return mean, covariance

def update(self, mean, covariance, measurement):
"""Run Kalman filter correction step.

Parameters
----------
mean : ndarray
The predicted state's mean vector (8 dimensional).
covariance : ndarray
The state's covariance matrix (8x8 dimensional).
measurement : ndarray
The 4 dimensional measurement vector (x, y, w, h), where (x, y)
is the center position, w the width, and h the height of the
bounding box.

Returns
-------
(ndarray, ndarray)
Returns the measurement-corrected state distribution.

"""
projected_mean, projected_cov = self.project(mean, covariance)

chol_factor, lower = scipy.linalg.cho_factor(
projected_cov, lower=True, check_finite=False)
kalman_gain = scipy.linalg.cho_solve(
(chol_factor, lower), np.dot(covariance, self._update_mat.T).T,
check_finite=False).T
innovation = measurement - projected_mean

new_mean = mean + np.dot(innovation, kalman_gain.T)
new_covariance = covariance - np.linalg.multi_dot((
kalman_gain, projected_cov, kalman_gain.T))
return new_mean, new_covariance

def gating_distance(self, mean, covariance, measurements,
only_position=False, metric='maha'):
"""Compute gating distance between state distribution and measurements.
A suitable distance threshold can be obtained from `chi2inv95`. If
`only_position` is False, the chi-square distribution has 4 degrees of
freedom, otherwise 2.
Parameters
----------
mean : ndarray
Mean vector over the state distribution (8 dimensional).
covariance : ndarray
Covariance of the state distribution (8x8 dimensional).
measurements : ndarray
An Nx4 dimensional matrix of N measurements, each in
format (x, y, a, h) where (x, y) is the bounding box center
position, a the aspect ratio, and h the height.
only_position : Optional[bool]
If True, distance computation is done with respect to the bounding
box center position only.
Returns
-------
ndarray
Returns an array of length N, where the i-th element contains the
squared Mahalanobis distance between (mean, covariance) and
`measurements[i]`.
"""
mean, covariance = self.project(mean, covariance)
if only_position:
mean, covariance = mean[:2], covariance[:2, :2]
measurements = measurements[:, :2]

d = measurements - mean
if metric == 'gaussian':
return np.sum(d * d, axis=1)
elif metric == 'maha':
cholesky_factor = np.linalg.cholesky(covariance)
z = scipy.linalg.solve_triangular(
cholesky_factor, d.T, lower=True, check_finite=False,
overwrite_b=True)
squared_maha = np.sum(z * z, axis=0)
return squared_maha
else:
raise ValueError('invalid distance metric')
Loading