diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..24482e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,116 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + diff --git a/centroid-tracker/__init__.py b/centroid-tracker/__init__.py new file mode 100644 index 0000000..8fb813d --- /dev/null +++ b/centroid-tracker/__init__.py @@ -0,0 +1 @@ +from .centroidtracker import CentroidTracker diff --git a/centroid-tracker/centroidtracker.py b/centroid-tracker/centroidtracker.py new file mode 100644 index 0000000..97baa5e --- /dev/null +++ b/centroid-tracker/centroidtracker.py @@ -0,0 +1,84 @@ +from scipy.spatial import distance as dist +from collections import OrderedDict +import numpy as np + +class CentroidTracker(): + def __init__(self, maxDisappeared=50): + self.nextObjectID = 0 + self.objects = OrderedDict() + self.disappeared = OrderedDict() + + self.maxDisappeared = maxDisappeared + + def register(self, centroid): + self.objects[self.nextObjectID] = centroid + self.disappeared[self.nextObjectID] = 0 + self.nextObjectID += 1 + + def deregister(self, objectID): + del self.objects[objectID] + del self.disappeared[objectID] + + def update(self, rects): + + if(len(rects) == 0): + for objectID in self.disappeared.keys(): + self.disappeared[objectID] += 1 + + if(self.disappeared[objectID] > self.maxDisappeared): + self.deregister(objectID) + return self.objects + + inputCentroids = np.zeros((len(rects), 2), dtype="int") + + for(i, (startX, startY, endX, endY)) in enumerate(rects): + cX = int((startX + endX) / 2.0) + cY = int((startY + endY) / 2.0) + inputCentroids[i] = (cX, cY) + + if(len(self.objects) == 0): + for i in range(0, len(inputCentroids)): + self.register(inputCentroids[i]) + + else: + objectIDs = list(self.objects.keys()) + objectCentroids = list(self.objects.values()) + + D = dist.cdist(np.array(objectCentroids), inputCentroids) + + rows = D.min(axis=1).argsort() + cols = D.argmin(axis=1)[rows] + + usedRows = set() + usedCols = set() + + for (row, col) in zip(rows, cols): + + if row in usedRows or col in usedCols: + continue + + objectID = objectIDs[row] + self.objects[objectID] = inputCentroids[col] + self.disappeared[objectID] = 0 + + usedRows.add(row) + usedCols.add(col) + + unusedRows = set(range(0, D.shape[0])).difference(usedRows) + unusedCols = set(range(0, D.shape[1])).difference(usedCols) + + if D.shape[0] >= D.shape[1]: + + for row in unusedRows: + objectID = objectIDs[row] + self.disappeared[objectID] += 1 + + if self.disappeared[objectID] > self.maxDisappeared: + self.deregister(objectID) + + else: + + for col in unusedCols: + self.register(inputCentroids[col]) + + return self.objects diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ea1adf2 --- /dev/null +++ b/setup.py @@ -0,0 +1,20 @@ +import setuptools + +setuptools.setup( + name="centroid_tracker", + version="0.0.1", + author="Ross Leitch", + author_email="ross@end-game.com", + description="A small centroid tracker library based off https://www.pyimagesearch.com/2018/07/23/simple-object-tracking-with-opencv/", + url="https://github.com/balbatross/centroid-tracker", + packages=['centroid-tracker'], + install_requires=[ + 'scipy', + 'numpy' + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent" + ], +)