How to count objects in video using openCV and python? - python-3.x

I want to count vehicles when the rectangle touches(intersects) the line. I don't know more about counting objects in video frame. Based on my pre-search, I have found out some useful information such as centroid, euclidean distance and etc. But, i don't have much knowledge about the geometry math behind it. Here is an example video. https://www.youtube.com/watch?v=z1Cvn3_4yGo. I think they used the same method.
I have got the center of the rectangle and drawn a red line across the road. Now I want to count vehicles when it touches the line. Bellow, I have uploaded my current output of the system in a image format as well as my current code.
# import the necessary packages
from imutils.video import VideoStream
from imutils.video import FPS
import argparse
import imutils
import time
import cv2
tracker = cv2.TrackerCSRT_create()
vs = cv2.VideoCapture("Video.mp4")
initBB = None
# loop over frames from the video stream
while vs.isOpened():
ret,frame = vs.read()
cv2.line(frame, (933 , 462), (1170 , 462), (0,0,255), 3)
# check to see if we are currently tracking an object
if initBB is not None:
# grab the new bounding box coordinates of the object
(success, box) = tracker.update(frame)
# check to see if the tracking was a success
if success:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h),
(0, 255, 0), 2)
cX = int((x + x+w) / 2.0)
cY = int((y + y+h) / 2.0)
cv2.circle(frame, (cX, cY), 4, (255, 0, 0), -1)
# show the output frame
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("s"):
# select the bounding box of the object we want to track (make
# sure you press ENTER or SPACE after selecting the ROI)
initBB = cv2.selectROI("Frame", frame, fromCenter=False,
showCrosshair=True)
# start OpenCV object tracker using the supplied bounding box
# coordinates, then start the FPS throughput estimator as well
tracker.init(frame, initBB)
fps = FPS().start()
# if the `q` key was pressed, break from the loop
elif key == ord("q"):
break
else:
vs.release()
cv2.destroyAllWindows()

You should just check whether the lower two x axis points of your bonding box are greater than or equal to the x axis points of the line

Related

Opencv3 - How to give ID to the people I count based on contours and rectrangles?

I want to give ID to the contourareas that I draw rectangle on them. Now my code tracks moving object on the screen and put a rectangle around them. I want to give an ID to the each of rectangles. I know how to count how many rectangles on the screen but I don't know how to give rectangles an exact ID that doesn't change when another rectangle joins the screen.
The code I use to draw rectangles:
video_path = 'C:\\Users\\MONSTER\\Desktop\\video.avi'
cv2.ocl.setUseOpenCL(False)
version = cv2.__version__.split('.')[0]
print(version)
#read video file
cap = cv2.VideoCapture(video_path)
#check opencv version
if version == '2' :
fgbg = cv2.BackgroundSubtractorMOG2()
if version == '3':
fgbg = cv2.createBackgroundSubtractorMOG2()
while (cap.isOpened):
#if ret is true than no error with cap.isOpened
ret, frame = cap.read()
if ret==True:
#apply background substraction
fgmask = fgbg.apply(frame)
ret1,th1 = cv2.threshold(fgmask,150,200,cv2.THRESH_BINARY)
#check opencv version
if version == '2' :
(contours, hierarchy) = cv2.findContours(th1.copy(),
cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
if version == '3' :
(im2, contours, hierarchy) = cv2.findContours(th1.copy(),
cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
if cv2.contourArea(c) < 200:
continue
#get bounding box from countour
(x, y, w, h) = cv2.boundingRect(c)
#draw bounding box
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('foreground and background',th1)
cv2.imshow('rgb',frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
And I changed the code above to the code below to put text on rectangles but the text changes when another rectangle joins.
i = 1
for c in contours:
if cv2.contourArea(c) < 200:
continue
#get bounding box from countour
(x, y, w, h) = cv2.boundingRect(c)
#draw bounding box
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
i = i + 1
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(frame,str(i),(x,y), font, 2,(255,255,255),2,cv2.LINE_AA)
Do you have any idea to give rectangles exact ID's.
Your code does not do the tracking. only the detection from the background only. To make your code natively do the tracking, I am afraid it's not that simple. tracking will only happen if you have a perfect connected object and there is only 1 object. It usually rarely happens as fail detection happens all the time. Thus multiple RECT are created.
The ID will keep changes for different frames when you enter the scene and there are multiple detections like the image below. I tried it before. Each time the bounding rect will change from object to object. Especially when you use a simple method like bgfg, this lost track or lost id happens almost every frame.
The proper way is to use real tracking algorithm to constantly update the object. e.g
https://www.youtube.com/watch?v=qvcyK4ZMKbM
The input to TLD tracker is from the Rect obtained by
(x, y, w, h) = cv2.boundingRect(c)
The source code is in the github. feel free to test it
https://github.com/gnebehay/TLD
Follow the installation to get it up and integrate into your current detection routing.
https://github.com/zk00006/OpenTLD/wiki/Installation
You need to track multiple objects.
You need to check their intersections and stop tracking when they collide.
https://www.pyimagesearch.com/2018/08/06/tracking-multiple-objects-with-opencv/

Using this motion detector code with a single webcam in a raspberry pi

No matter what I try I can't convert this code from Adrian Rosebrock to work with a single webcam, I have tested it as working with 2 webcams.
As far as I can tell the problem comes in attempting to convert this line;
for (stream, motion) in zip((webcam1, webcam2), (camMotion, piMotion)):
from __future__ import print_function
from pyimagesearch.basicmotiondetector import BasicMotionDetector
from imutils.video import VideoStream
import numpy as np
import datetime
import imutils
import time
import cv2
# initialize the video streams and allow them to warmup
print("[INFO] starting cameras...")
#webcam = VideoStream(src=0).start()
#picam = VideoStream(usePiCamera=True).start()
webcam1 = VideoStream(src=0).start()
webcam2 = VideoStream(src=1).start()
time.sleep(2.0)
# initialize the two motion detectors, along with the total
# number of frames read
camMotion = BasicMotionDetector()
piMotion = BasicMotionDetector()
total = 0
# loop over frames from the video streams
while True:
# initialize the list of frames that have been processed
frames = []
# loop over the frames and their respective motion detectors
for (stream, motion) in zip((webcam1, webcam2), (camMotion, piMotion)):
# read the next frame from the video stream and resize
# it to have a maximum width of 400 pixels
frame = stream.read()
frame = imutils.resize(frame, width=400)
# convert the frame to grayscale, blur it slightly, update
# the motion detector
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
locs = motion.update(gray)
# we should allow the motion detector to "run" for a bit
# and accumulate a set of frames to form a nice average
if total < 32:
frames.append(frame)
continue
# otherwise, check to see if motion was detected
if len(locs) > 0:
# initialize the minimum and maximum (x, y)-coordinates,
# respectively
(minX, minY) = (np.inf, np.inf)
(maxX, maxY) = (-np.inf, -np.inf)
# loop over the locations of motion and accumulate the
# minimum and maximum locations of the bounding boxes
for l in locs:
(x, y, w, h) = cv2.boundingRect(l)
(minX, maxX) = (min(minX, x), max(maxX, x + w))
(minY, maxY) = (min(minY, y), max(maxY, y + h))
# draw the bounding box
cv2.rectangle(frame, (minX, minY), (maxX, maxY),
(0, 0, 255), 3)
# update the frames list
frames.append(frame)
# increment the total number of frames read and grab the
# current timestamp
total += 1
timestamp = datetime.datetime.now()
ts = timestamp.strftime("%A %d %B %Y %I:%M:%S%p")
# loop over the frames a second time
for (frame, name) in zip(frames, ("Webcam", "Picamera")):
# draw the timestamp on the frame and display it
cv2.putText(frame, ts, (10, frame.shape[0] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)
cv2.imshow(name, frame)
# check to see if a key was pressed
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
print("[INFO] cleaning up...")
cv2.destroyAllWindows()
webcam.stop()
picam.stop()
Thanks for the help

Multi-object tracking initialization in Opencv using multiTracker object

I am using multiTracker in cv2 to track multiple objects. My code is built based on this link and this one. I want to initialize all the bounding boxes at once at any point during the video. However, I have trouble doing this. Here is the code:
import imutils
import cv2
from random import randint
trackerName = 'csrt'
videoPath = "C:/Users/Art/testVideo.mp4"
OPENCV_OBJECT_TRACKERS = {
"csrt": cv2.TrackerCSRT_create,
"kcf": cv2.TrackerKCF_create,
"boosting": cv2.TrackerBoosting_create,
"mil": cv2.TrackerMIL_create,
"tld": cv2.TrackerTLD_create,
"medianflow": cv2.TrackerMedianFlow_create,
"mosse": cv2.TrackerMOSSE_create
}
trackers = cv2.MultiTracker_create()
cap = cv2.VideoCapture(videoPath)
while cap.isOpened():
ret, frame = cap.read()
if frame is None:
break
# for fast processing resize the frame
frame = imutils.resize(frame, width=600)
(success, boxes) = trackers.update(frame)
for box in boxes:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# if the 'p' key is selected, we select a bounding box to track
if key == ord("p"):
boxes = []
colors = []
boxFlag = True
while boxFlag:
box = cv2.selectROI('MultiTracker', frame, fromCenter=False,
showCrosshair=True)
boxes.append(box)
colors.append((randint(64, 255), randint(64, 255), randint(64,
255)))
print("Press q to quit selecting boxes and start tracking")
print("Press any other key to select next object")
if key == ord("q"): # q is pressed
boxFlag = False
# Initialize MultiTracker
for box in boxes:
tracker = OPENCV_OBJECT_TRACKERS[trackerName]()
trackers.add(tracker, frame, box)
cap.release()
cv2.destroyAllWindows()
However, there are some problems. First, when I click on 'p' key to select bounding boxes, the video pauses and another window opens that shows the frame at which video was paused and I can select bounding boxes on the new window only. Also, when I press 'q' key, nothing will happen and basically it stays there forever. My question is how I can fix this problem, and be able to initialize tracking after I select all of the bounding boxes.
I figured it out and thought this might be useful for someone else. I made some changes to the previous code. I also realized that cv2 has selectROIs method which can be useful if someone wants to select multiple bounding boxes at once. Here is the updated code:
import imutils
import cv2
from random import randint
trackerName = 'csrt'
videoPath = "C:/Users/Art/testVideo.mp4"
OPENCV_OBJECT_TRACKERS = {
"csrt": cv2.TrackerCSRT_create,
"kcf": cv2.TrackerKCF_create,
"boosting": cv2.TrackerBoosting_create,
"mil": cv2.TrackerMIL_create,
"tld": cv2.TrackerTLD_create,
"medianflow": cv2.TrackerMedianFlow_create,
"mosse": cv2.TrackerMOSSE_create
}
# initialize OpenCV's special multi-object tracker
trackers = cv2.MultiTracker_create()
cap = cv2.VideoCapture(videoPath)
while cap.isOpened():
ret, frame = cap.read()
if frame is None:
break
frame = imutils.resize(frame, width=600)
(success, boxes) = trackers.update(frame)
# loop over the bounding boxes and draw them on the frame
for box in boxes:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
# if the 's' key is selected, we are going to "select" a bounding
# box to track
if key == ord("s"):
colors = []
# select the bounding box of the object we want to track (make
# sure you press ENTER or SPACE after selecting the ROI)
box = cv2.selectROIs("Frame", frame, fromCenter=False,
showCrosshair=True)
box = tuple(map(tuple, box))
for bb in box:
tracker = OPENCV_OBJECT_TRACKERS[trackerName]()
trackers.add(tracker, frame, bb)
# if you want to reset bounding box, select the 'r' key
elif key == ord("r"):
trackers.clear()
trackers = cv2.MultiTracker_create()
box = cv2.selectROIs("Frame", frame, fromCenter=False,
showCrosshair=True)
box = tuple(map(tuple, box))
for bb in box:
tracker = OPENCV_OBJECT_TRACKERS[trackerName]()
trackers.add(tracker, frame, bb)
elif key == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
After selecting each bounding box, you need to click on "Enter" or "Space" button to finalize it and start selecting a new bounding box. Once you are done with bounding box selection, press "Esc" key to end ROI selection and start tracking. Also, if you need to reset bounding boxes for any reason, press "r" button.
Maybe You should try the "cv2.selectROIs" function before the "while cap.isOpened():" loop, without loop, because it looks like "cv2.selectROIs" function works as loop itself, while you are selecting ROIs.

Pixel Co-Ordinate Logging for an imported Image (Python)

Okay so, I want to be able to record the co-ordinates of pixels within a given image. The image is used to calibrate and determine the lens distortion and iFoV (Field of View) of a camera at varying distances.
I am trying to code (in Python) a program that will allow me to open up an initial image within a directory and allow me to pinpoint the pixel co-ordinates of the following areas;
TOP LEFT, TOP RIGHT, TOP MIDDLE, MID RIGHT, MID LEFT, BOT LEFT, BOT MID and BOT RIGHT
So basically, i need to open the image click those 8 points (recording the points in a csv file) then press a key e.g. ''SPACEBAR'' or ''ESC'' etc. which brings up the next image in the directory (Tool at a new length). Being able to repeat for many images.
Example Calibration Tool - Red Dots are the Wanted Co-Ordinates:
I have written a mock tool that addresses your purpose.
Pass the appropriate path conatining image in filepath.
Press and release left mouse button to see the marking.
Hit spacebar to clear markings on current image (in case you make a mistake).
Press lower case 'q' to move to next image.
Code:
import cv2
import numpy as np
import glob
ix, iy = -1, -1
# mouse callback function
def draw_circle(event, x, y, flags, param):
global ix, iy, mode
if event == cv2.EVENT_LBUTTONDOWN:
ix, iy = x, y
elif event == cv2.EVENT_LBUTTONUP:
cv2.circle(img, (x, y), 5, (0, 0, 255), -1)
print(x, y)
cv2.namedWindow('image')
cv2.setMouseCallback('image', draw_circle)
filepath = r'C:\Users\Jackson\Desktop\*.png'
for file in glob.glob(filepath):
img = cv2.imread(file)
img2 = img.copy()
while(1):
cv2.imshow('image', img)
k = cv2.waitKey(1) & 0xFF
if k == 32: #--- Press spacebar to clear markings on current image ---
img = img2.copy()
elif k == ord('q'): #--- Press lowercase q to move to next image ---
break
cv2.destroyAllWindows()

how to measure distance between center of objects - openCV?

I have the following code that extracts and "tracks" black objects within a video file or webcam stream. I am new to Python and CV and am struggling to understand how to extract the center points of the tracked objects for comparison.
Code:
#for this demo the only colour we are interested in is black or near black
#remember open cv uses BGR not RGB
groupColorLower = np.array([0,0,0], dtype = "uint8")
groupColorUpper = np.array([179,179,179], dtype ="uint8")
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", required=True,
help="path to the input video file")
args = vars(ap.parse_args())
######### Reading input video or opening webcam#########
# if a video path was not supplied, grab the reference
# to the webcam
if not args.get("video", False):
camera = cv2.VideoCapture(0)
# otherwise, load the video
else:
camera = cv2.VideoCapture(args["video"])
######### Finished Reading input video or opening webcam#########
# keep looping
while True:
# grab the current frame
(grabbed, frame) = camera.read()
# if we are viewing a video and we did not grab a
# frame, then we have reached the end of the video
if args.get("video") and not grabbed:
break
# determine which pixels fall within the black boundaries
# and then blur the binary image
blue = cv2.inRange(frame, groupColorLower, groupColorUpper)
blue = cv2.GaussianBlur(blue, (3, 3), 0)
# find contours in the image
(_, contours1, _) = cv2.findContours(blue.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
#lets try to sort contours1 left to right
(contours1, _) = contours.sort_contours(contours1)
if len(contours1) > 0:
#now lets extract the center point of the left most contour
fixedPoint = cv2.moments(contours1[0])
fixedPointX = int (fixedPoint["m10"] / fixedPoint["m00"])
fixedPointY = int (fixedPoint["m01"] / fixedPoint["m00"])
#lets draw a white dot in left most object
cv2.circle(frame, (fixedPointX, fixedPointY), 7, (255,255,255), -1)
#lets use the nearest to fixedPoint as anchor (left most [1] where [0] is fixedPoint
if len(contours1) > 1:
# sort the contours so we can iterate over them
cnt2 = sorted([(c, cv2.boundingRect(c)[0]) for c in contours1], key = lambda x: x[1], reverse = True)
#draw boxes around all objects we are tracking
for (c, _) in cnt2:
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y +h), (255, 255, 0), 4)
M = cv2.moments(c)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# draw a dot in center of the black object on the image
cv2.circle(frame, (cX, cY), 7, (100, 100, 100), -1)
centre.append((int(M["m10"]/M["m00"]), int(M["m01"]/M["m00"])))
What I would like to be able to do is print the distance (in pixels) between the objects as they are found but I am not sure how to access this data.
The fixed point calculation works as I assume the left most object can be hard coded, but I need to get the coords for any other objects located.
The code currently loops over each black object and places a dot in the center of it.
Thanks for any help offered:
andy

Resources