Created
August 13, 2018 05:51
-
-
Save adioshun/779738c3e28151ffbb9dc7d2b13c2c0a to your computer and use it in GitHub Desktop.
Simple object tracking with OpenCV
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://www.pyimagesearch.com/2018/07/23/simple-object-tracking-with-opencv/ | |
# import the necessary packages | |
from scipy.spatial import distance as dist | |
from collections import OrderedDict | |
import numpy as np | |
class CentroidTracker(): | |
def __init__(self, maxDisappeared=50): | |
# initialize the next unique object ID along with two ordered | |
# dictionaries used to keep track of mapping a given object | |
# ID to its centroid and number of consecutive frames it has | |
# been marked as "disappeared", respectively | |
self.nextObjectID = 0 | |
self.objects = OrderedDict() | |
self.disappeared = OrderedDict() | |
# store the number of maximum consecutive frames a given | |
# object is allowed to be marked as "disappeared" until we | |
# need to deregister the object from tracking | |
self.maxDisappeared = maxDisappeared | |
def register(self, centroid): | |
# when registering an object we use the next available object | |
# ID to store the centroid | |
self.objects[self.nextObjectID] = centroid | |
self.disappeared[self.nextObjectID] = 0 | |
self.nextObjectID += 1 | |
def deregister(self, objectID): | |
# to deregister an object ID we delete the object ID from | |
# both of our respective dictionaries | |
del self.objects[objectID] | |
del self.disappeared[objectID] | |
def update(self, rects): | |
# check to see if the list of input bounding box rectangles | |
# is empty | |
if len(rects) == 0: | |
# loop over any existing tracked objects and mark them | |
# as disappeared | |
for objectID in self.disappeared.keys(): | |
self.disappeared[objectID] += 1 | |
# if we have reached a maximum number of consecutive | |
# frames where a given object has been marked as | |
# missing, deregister it | |
if self.disappeared[objectID] > self.maxDisappeared: | |
self.deregister(objectID) | |
# return early as there are no centroids or tracking info | |
# to update | |
return self.objects | |
# initialize an array of input centroids for the current frame | |
inputCentroids = np.zeros((len(rects), 2), dtype="int") | |
# loop over the bounding box rectangles | |
for (i, (startX, startY, endX, endY)) in enumerate(rects): | |
# use the bounding box coordinates to derive the centroid | |
cX = int((startX + endX) / 2.0) | |
cY = int((startY + endY) / 2.0) | |
inputCentroids[i] = (cX, cY) | |
# if we are currently not tracking any objects take the input | |
# centroids and register each of them | |
if len(self.objects) == 0: | |
for i in range(0, len(inputCentroids)): | |
self.register(inputCentroids[i]) | |
# otherwise, are are currently tracking objects so we need to | |
# try to match the input centroids to existing object | |
# centroids | |
else: | |
# grab the set of object IDs and corresponding centroids | |
objectIDs = list(self.objects.keys()) | |
objectCentroids = list(self.objects.values()) | |
# compute the distance between each pair of object | |
# centroids and input centroids, respectively -- our | |
# goal will be to match an input centroid to an existing | |
# object centroid | |
D = dist.cdist(np.array(objectCentroids), inputCentroids) | |
# in order to perform this matching we must (1) find the | |
# smallest value in each row and then (2) sort the row | |
# indexes based on their minimum values so that the row | |
# with the smallest value as at the *front* of the index | |
# list | |
rows = D.min(axis=1).argsort() | |
# next, we perform a similar process on the columns by | |
# finding the smallest value in each column and then | |
# sorting using the previously computed row index list | |
cols = D.argmin(axis=1)[rows] | |
# in order to determine if we need to update, register, | |
# or deregister an object we need to keep track of which | |
# of the rows and column indexes we have already examined | |
usedRows = set() | |
usedCols = set() | |
# loop over the combination of the (row, column) index | |
# tuples | |
for (row, col) in zip(rows, cols): | |
# if we have already examined either the row or | |
# column value before, ignore it | |
# val | |
if row in usedRows or col in usedCols: | |
continue | |
# otherwise, grab the object ID for the current row, | |
# set its new centroid, and reset the disappeared | |
# counter | |
objectID = objectIDs[row] | |
self.objects[objectID] = inputCentroids[col] | |
self.disappeared[objectID] = 0 | |
# indicate that we have examined each of the row and | |
# column indexes, respectively | |
usedRows.add(row) | |
usedCols.add(col) | |
# compute both the row and column index we have NOT yet | |
# examined | |
unusedRows = set(range(0, D.shape[0])).difference(usedRows) | |
unusedCols = set(range(0, D.shape[1])).difference(usedCols) | |
# in the event that the number of object centroids is | |
# equal or greater than the number of input centroids | |
# we need to check and see if some of these objects have | |
# potentially disappeared | |
if D.shape[0] >= D.shape[1]: | |
# loop over the unused row indexes | |
for row in unusedRows: | |
# grab the object ID for the corresponding row | |
# index and increment the disappeared counter | |
objectID = objectIDs[row] | |
self.disappeared[objectID] += 1 | |
# check to see if the number of consecutive | |
# frames the object has been marked "disappeared" | |
# for warrants deregistering the object | |
if self.disappeared[objectID] > self.maxDisappeared: | |
self.deregister(objectID) | |
# otherwise, if the number of input centroids is greater | |
# than the number of existing object centroids we need to | |
# register each new input centroid as a trackable object | |
else: | |
for col in unusedCols: | |
self.register(inputCentroids[col]) | |
# return the set of trackable objects | |
return self.objects |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://www.pyimagesearch.com/2018/07/23/simple-object-tracking-with-opencv/ | |
# USAGE | |
# python object_tracker.py --prototxt deploy.prototxt --model res10_300x300_ssd_iter_140000.caffemodel | |
# import the necessary packages | |
from pyimagesearch.centroidtracker import CentroidTracker | |
from imutils.video import VideoStream | |
import numpy as np | |
import argparse | |
import imutils | |
import time | |
import cv2 | |
# construct the argument parse and parse the arguments | |
ap = argparse.ArgumentParser() | |
ap.add_argument("-p", "--prototxt", required=True, | |
help="path to Caffe 'deploy' prototxt file") | |
ap.add_argument("-m", "--model", required=True, | |
help="path to Caffe pre-trained model") | |
ap.add_argument("-c", "--confidence", type=float, default=0.5, | |
help="minimum probability to filter weak detections") | |
args = vars(ap.parse_args()) | |
# initialize our centroid tracker and frame dimensions | |
ct = CentroidTracker() | |
(H, W) = (None, None) | |
# load our serialized model from disk | |
print("[INFO] loading model...") | |
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"]) | |
# initialize the video stream and allow the camera sensor to warmup | |
print("[INFO] starting video stream...") | |
vs = VideoStream(src=0).start() | |
time.sleep(2.0) | |
# loop over the frames from the video stream | |
while True: | |
# read the next frame from the video stream and resize it | |
frame = vs.read() | |
frame = imutils.resize(frame, width=400) | |
# if the frame dimensions are None, grab them | |
if W is None or H is None: | |
(H, W) = frame.shape[:2] | |
# construct a blob from the frame, pass it through the network, | |
# obtain our output predictions, and initialize the list of | |
# bounding box rectangles | |
blob = cv2.dnn.blobFromImage(frame, 1.0, (W, H), | |
(104.0, 177.0, 123.0)) | |
net.setInput(blob) | |
detections = net.forward() | |
rects = [] | |
# loop over the detections | |
for i in range(0, detections.shape[2]): | |
# filter out weak detections by ensuring the predicted | |
# probability is greater than a minimum threshold | |
if detections[0, 0, i, 2] > args["confidence"]: | |
# compute the (x, y)-coordinates of the bounding box for | |
# the object, then update the bounding box rectangles list | |
box = detections[0, 0, i, 3:7] * np.array([W, H, W, H]) | |
rects.append(box.astype("int")) | |
# draw a bounding box surrounding the object so we can | |
# visualize it | |
(startX, startY, endX, endY) = box.astype("int") | |
cv2.rectangle(frame, (startX, startY), (endX, endY), | |
(0, 255, 0), 2) | |
# update our centroid tracker using the computed set of bounding | |
# box rectangles | |
objects = ct.update(rects) | |
# loop over the tracked objects | |
for (objectID, centroid) in objects.items(): | |
# draw both the ID of the object and the centroid of the | |
# object on the output frame | |
text = "ID {}".format(objectID) | |
cv2.putText(frame, text, (centroid[0] - 10, centroid[1] - 10), | |
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) | |
cv2.circle(frame, (centroid[0], centroid[1]), 4, (0, 255, 0), -1) | |
# show the output frame | |
cv2.imshow("Frame", frame) | |
key = cv2.waitKey(1) & 0xFF | |
# if the `q` key was pressed, break from the loop | |
if key == ord("q"): | |
break | |
# do a bit of cleanup | |
cv2.destroyAllWindows() | |
vs.stop() |
To detect the object from the image from scratch using python; Click here I found the best article https://debuggingsolution.blogspot.com/2022/02/object-detection-from-scratch-in-python.html
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for providing this. @adioshun
For those looking for 2 files needed to run the following command:
Run the commands below to download the files: