how to speed up video capture from webcam in opencv? - python

I need real time video capture i've tried reducing resolution, setting a static fps and none worked why am i getting a slow video feed although it says my fps is 30 i don't really know where exactly is the problem it's really diving me mad.
Code:
import cv2
import os
import face_recognition
import pickle
from cv2.cv2 import CAP_DSHOW
known_faces_dir = "known_faces"
video = cv2.VideoCapture(0)
accuracy = 0.6
frame_thikness = 3
font_size = 2
MODEL = "cnn"
print("loading known faces")
known_faces = []
known_names = []
unknown_faces = []
for name in os.listdir(known_faces_dir):
for filename in os.listdir(f"{known_faces_dir}/{name}"):
image = face_recognition.load_image_file(f"{known_faces_dir}/{name}/{filename}")
encodings = face_recognition.face_encodings(image)[0]
# encodings = pickle.load(open(f"{name}/{filename}","rb"))
known_faces.append(encodings)
known_names.append(name)
print("treating unknow faces")
while True :
# print(filename)
# image = face_recognition.load_image_file(f"{unknown_faces_dir}/{filename}")
ret, image = video.read()
print(video.get(cv2.CAP_PROP_FPS))
locations = face_recognition.face_locations(image, model=MODEL)
encodings = face_recognition.face_encodings(image, locations)
# image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
for face_location, face_encodings in zip(locations, encodings):
results = face_recognition.compare_faces(known_faces, face_encodings, tolerance=0.54)
if True in results:
match = known_names[results.index(True)]
print("Face Found" f"{match}")
top_l = (face_location[3], face_location[0])
bottom_r = (face_location[1], face_location[2])
color = [0, 255, 0]
cv2.rectangle(image, top_l, bottom_r, color, frame_thikness)
top_l = (face_location[3], face_location[2])
bottom_r = (face_location[1], face_location[2] + 22)
cv2.rectangle(image, top_l, bottom_r, color, cv2.FILLED)
cv2.putText(image, str(match), (face_location[3]+10, face_location[2]+15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 200), 2)
cv2.imshow("", image)
if cv2.waitKey(1)&0xFF == ord("e"):
break
# cv2.waitKey(10200)
video.release()
cv2.destroyWindow(filename)

Try this and look at elapsed time to compute fps.
Then add other process.
Work with gray images from the beginning is a good idea.
if faces images are in color save it in gray and use only gray images.
Avoid the process repeats same things if not necessary.
import numpy as np
import cv2
import time
cap = cv2.VideoCapture(0)
start_time = time.time()
end_time = start_time
elapsed_time = 0
font = cv2.FONT_HERSHEY_SIMPLEX
org = (50, 50)
fontScale = 1
color = (255, 0, 0)
thickness = 2
while(True):
start_time = time.time()
# Capture frame-by-frame
ret, frame = cap.read()
# Our operations on the frame come here
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# Display the resulting frame
cv2.putText(gray, str(1 / elapsed_time) + "fps", org, font,
fontScale, color, thickness, cv2.LINE_AA)
cv2.imshow('frame',gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
end_time = time.time()
elapsed_time = end_time - start_time
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

It is highly likely that the inferencing of your model is taking more than 33ms (1000ms / 30 FPS) and thus limiting your FPS. Try to remove your face recognition model from the loop and see if it still is slow.
If that solves your problem, your CPU or GPU is the limiting factor depending on how you run the model.

Related

How to change the color of HandConnections in Media pipe?

I was learning MediaPipe from some Tutorials and notices that the color of the HandConnection was White.
How do I change the color of the HandConnections from white ---> green??
Here is my code:
import cv2
import mediapipe as mp
import time
cap = cv2.VideoCapture(0)
mpHands = mp.solutions.hands
hands = mpHands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence = 0.4, min_tracking_confidence=0.5)
# Parameters,
"""
static_image_mode, takes a boolean value. if True, It will track at all times (Not Recommended)
max_num_hands, Takes a Ineteger. Max Hands in a Capture
min_detection_confidence, Takes a float (0 --> 1). Min Confidence for static_image_mode to operate if False (Detection)
min_tracking_confidence, Takes a float (0 --> 1). Min Confidence for static_image_mode to operate if False (Tracking)
"""
mpDraw = mp.solutions.drawing_utils
while True:
success, img = cap.read() # Succes representing if image captured
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # Changing format
results = hands.process(imgRGB)
# print(results)
# print(results.multi_hand_landmarks)
if results.multi_hand_landmarks:
for handLms in results.multi_hand_landmarks:
mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS)
# print(success)
cv2.imshow('Image', img)
if cv2.waitKey(65) == ord('e'):
break
cv2.destroyAllWindows() # Must
You can just edit the colour in the draw_landmarks() function in drawing_utils.py
In this line
cv2.line(image, idx_to_coordinates[start_idx],
idx_to_coordinates[end_idx], drawing_spec.color,
drawing_spec.thickness)
just change drawing_spec.color to a tuple that represents your RGB colour of choice - e.g. (255,255,255) would be white:
cv2.line(image, idx_to_coordinates[start_idx],
idx_to_coordinates[end_idx], (255, 255, 255),
drawing_spec.thickness)

Using hand pose coordinates (landmarks) from python in Blender for visualisation

I am doing a project to visualise hand movement from a wearable device (gloves) to a screen. I am using Mediapipe to get hand landmarks. This is to get synthetic data for my project. Now I am planning to use Blender to visualise and animate the hand movement. My questions are as follows:
Is there any alternatives for this method?
How to I import these points (as a csv file)and map them on to my blender image?
Note: I am a novice in both Python and Blender.
Any and all help would be appreciated . Thanks in advance.
I used Mediapipe to get the coordinates.
my code for Mediapipe:
'''
import cv2
import mediapipe as mp
import time
import uuid
import os
import numpy as np
cap = cv2.VideoCapture(0)
mpHands = mp.solutions.hands
hands = mpHands.Hands(max_num_hands=1)
mpDraw = mp.solutions.drawing_utils
pTime = 0
cTime = 0
while True:
success, img = cap.read()
#img = cv2.resize(img, (680,420))
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
imgRGB.flags.writeable = False
results = hands.process(imgRGB)
imgRGB.flags.writeable = True
imgRGB = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
print(results.multi_hand_landmarks)
if results.multi_hand_landmarks:
for handLms in results.multi_hand_landmarks:
for id, lm in enumerate(handLms.landmark):
#to get width and height of the image
h, w, c = img.shape
#to get center points
cx,cy = int(lm.x*w), int(lm.y*h)
print(id, cx, cy)
#lms= lm.append([id, cx, cy])
if id == 0:
cv2.circle(img, (cx,cy), 15, (255,0,255),cv2.FILLED)
mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS,
mpDraw.DrawingSpec(color=(201,122,76), thickness=2, circle_radius=2),
mpDraw.DrawingSpec(color=(121,44,250), thickness=4, circle_radius=4),)
cTime = time.time()
fps = 1/(cTime-pTime)
pTime = cTime
cv2.putText(img, str(int(fps)), (10,70), cv2.FONT_HERSHEY_PLAIN, 3, (255 ,125,0),2)
#cv2.imwrite(os.path.join('Output Images', '{}.jpg'.format(uuid.uuid1())),img)
cv2.imshow('Image', img)
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
''''

Converting an OpenCV program into a Module reduces frame rate drastically

I wrote a code for pose estimation using OpenCV and mediapipe library. The program was working well and I was getting around 30-35 fps. When I tried to convert the same program to a module so that I can use it easily in future for different projects, the fps of the new code(module) reduced drastically to 3-4 fps.
My original Program:
import cv2
import mediapipe as mp
import time
cap = cv2.VideoCapture(1)
pTime = 0
cTime = 0
mpDraw = mp.solutions.drawing_utils
mpPose = mp.solutions.pose
pose = mpPose.Pose()
while True:
success, img1 = cap.read()
img = cv2.flip(img1, 1)
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
results = pose.process(imgRGB)
if results.pose_landmarks:
mpDraw.draw_landmarks(img, results.pose_landmarks, mpPose.POSE_CONNECTIONS)
for id, lm in enumerate(results.pose_landmarks.landmark):
h, w, c = img.shape
cx, cy = int(lm.x*w), int(lm.y*h)
cv2.circle(img, (cx, cy), 5, (255, 0, 0), cv2.FILLED)
cTime = time.time()
fps = 1/(cTime - pTime)
pTime = cTime
cv2.putText(img, "FPS : " + str(int(fps)), (10, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 8), 2)
cv2.imshow("Live Feed", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
My attempt at converting it into a module :
import cv2
import mediapipe as mp
import time
class poseDetector():
def __init__(self, mode=False, upBody=False, smooth=True, detectionCon = 0.5, trackingCon=0.5):
self.mode = mode
self.upBody = upBody
self.smooth = smooth
self.detectionCon = detectionCon
self.trackingCon = trackingCon
self.mpDraw = mp.solutions.drawing_utils
self.mpPose = mp.solutions.pose
self.pose =self.mpPose.Pose(self.mode, self.upBody, self.smooth, self.detectionCon, self.trackingCon)
def findPose(self, img, draw=True):
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.results = self.pose.process(imgRGB)
if self.results.pose_landmarks:
if draw:
self.mpDraw.draw_landmarks(img, self.results.pose_landmarks, self.mpPose.POSE_CONNECTIONS)
return img
def findPosition(self, img, draw=True):
lmList = []
if self.results.pose_landmarks:
for id, lm in enumerate(self.results.pose_landmarks.landmark):
h, w, c = img.shape
cx, cy = int(lm.x*w), int(lm.y*h)
lmList.append([id, cx, cy])
if draw:
cv2.circle(img, (cx, cy), 5, (255, 0, 0), cv2.FILLED)
return lmList
def main():
cap = cv2.VideoCapture(1)
pTime = 0
cTime = 0
while True:
success, img1 = cap.read()
img = cv2.flip(img1, 1)
detector = poseDetector()
img = detector.findPose(img)
lmList = detector.findPosition(img)
cTime = time.time()
fps = 1/(cTime - pTime)
pTime = cTime
cv2.putText(img, "FPS : " + str(int(fps)), (10, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 8), 2)
cv2.imshow("Live Feed", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
if __name__ == '__main__':
main()
According to me , both the code should have been working in the same manner, but they are not. Can anyone tell where am I making mistake ?
You need to place detector = poseDetector() to be before the while True::
detector = poseDetector()
while True:
success, img1 = cap.read()
...
Your "module" implementation creates a new poseDetector object every iteration of the main loop.
Each execution of detector = poseDetector() includes a call to poseDetector.__init__ that calls self.pose =self.mpPose.Pose...
There is a lot of overhead...
while True:
success, img1 = cap.read()
img = cv2.flip(img1, 1)
detector = poseDetector()
...
In your original ("non-module") implementation, you are executing pose = mpPose.Pose() only once (before the loop).
pose = mpPose.Pose()
while True:
success, img1 = cap.read()
...
I have tested your code before and after moving detector = poseDetector() outside the loop.
After moving the line above the loop, the frame rate is the same as the "non-module" implementation.

How to solve Opencv VideoWriter issues with global variables

I'mm writing this piece of python to display a stream of video from my webcam while at the same time record the video - which I've got working, however I've grayscaled the video streaming to my screen and time stamped it - but my recorded video is in colour! I've included the code below - I've tried using some global variables but nothing worked - any help, greatly appreciated
import cv2
import numpy as np
import time, datetime
import os
genericfilename = "recording"
filetime = str(time.time())
extension = '.avi'
filename = genericfilename + filetime +extension
frames_per_second = 100
res = '720p'
print("NEW FILE NAME: " + filename)
# Set resolution for the video capture
def change_res(cap, width, height):
cap.set(3, width)
cap.set(4, height)
# Standard Video Dimensions Sizes
STD_DIMENSIONS = {
"480p": (640, 480),
"720p": (1280, 720),
"1080p": (1920, 1080),
"4k": (3840, 2160),
}
# grab resolution dimensions and set video capture to it.
def get_dims(cap, res='1080p'):
width, height = STD_DIMENSIONS["480p"]
if res in STD_DIMENSIONS:
width,height = STD_DIMENSIONS[res]
## change the current caputre device
## to the resulting resolution
change_res(cap, width, height)
return width, height
# Video Encoding, might require additional installs
VIDEO_TYPE = {
'avi': cv2.VideoWriter_fourcc(*'XVID'),
#'mp4': cv2.VideoWriter_fourcc(*'H264'),
'mp4': cv2.VideoWriter_fourcc(*'XVID'),
}
def get_video_type(filename):
filename, ext = os.path.splitext(filename)
if ext in VIDEO_TYPE:
return VIDEO_TYPE[ext]
return VIDEO_TYPE['avi']
capture = cv2.VideoCapture(0)
out = cv2.VideoWriter(filename, get_video_type(filename), 60,
get_dims(capture, res))
while(True):
ret, frame = capture.read()
out.write(frame)
grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
font = cv2.FONT_ITALIC = 1
cv2.putText(grayFrame, str(datetime.datetime.now()), (-330, 460), font, 3,
(200, 200, 200), 2, cv2.LINE_AA)
cv2.imshow('combilift output', grayFrame)
# Press Q on keyboard to exit
if cv2.waitKey(1) & 0xFF == ord('q'):
break
if cv2.waitKey(1) & 0xFF == ord('r'):
print(datetime.datetime.now())
capture.release()
out.release()
cv2.destroyAllWindows()
You save the frame to video, then convert frame to gray.
out.write(frame)
grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
If you want your recorded video to be gray, maybe reverse the order of operations and save grayFrame?
grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
out.write(grayFrame)
If you want to also save the texts, put the text before writing frame to output.
Lets take a look at ur code
out = cv2.VideoWriter(filename, get_video_type(filename), 60,
.....
while(True):
ret, frame = capture.read()
out.write(frame)
grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
You first save out then convert color
The correct sequence should be
out = cv2.VideoWriter(filename, get_video_type(filename), 60,
.....
while(True):
ret, frame = capture.read()
grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
out.write(grayFrame)
I don't have data to test. Just in case you experience some issue with channels. You can use opencv merge(grayFrame,grayFrame,grayFrame) to create a normal 3 channel grey scale image and save to video

Python: record video on motion, but release VideoWriter when motion not present

My goal of this script is to start recording to a video file when motion is detected. When motion is no longer detected, the writer will be released and the script will create a new video the next time that motion is detected...and so on.
With the below script, I'm able to start writing to a video file once motion is detected, but I have to press the q button to release the writer and make the video playable. If I don't press q, the recording will stop, but the next time there's motion it will just be added to the existing video. I've tried writer.release() in a few places without success.
# import the necessary packages
from pyimagesearch.tempimage import TempImage
import argparse
import warnings
import datetime
import imutils
import json
import numpy as np
import time
import cv2
print("[INFO] Kicking off script - " +
datetime.datetime.now().strftime("%Y-%m-%d_%H_%M_%S"))
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-c", "--conf", required=True,
help="path to the JSON configuration file")
args = vars(ap.parse_args())
# filter warnings, load the configuration and initialize the Dropbox
# client
warnings.filterwarnings("ignore")
conf = json.load(open(args["conf"]))
client = None
# initialize the camera and grab a reference to the raw camera capture
# if the video argument is None, then we are reading from webcam
if not conf["use_ip_cam"]:
camera = cv2.VideoCapture(0)
time.sleep(0.25)
# otherwise, we are reading from a video input
else:
camera = cv2.VideoCapture(conf["ip_cam_addr"])
# allow the camera to warmup, then initialize the average frame, last
# uploaded timestamp, and frame motion counter
print("[INFO] warming up...")
time.sleep(conf["camera_warmup_time"])
avg = None
lastUploaded = datetime.datetime.now()
motionCounter = 0
fourcc = 0x00000020 # a little hacky, but works for now
writer = None
(h, w) = (None, None)
zeros = None
output = None
# capture frames from the camera
# for f in camera.capture_continuous(rawCapture, format="bgr",
# use_video_port=True):
while True:
# grab the raw NumPy array representing the image and initialize
# the timestamp and occupied/unoccupied text
(grabbed, frame) = camera.read()
# frame = f.array
timestamp = datetime.datetime.now()
motion_detected = False
# if the frame could not be grabbed, then we have reached the end
# of the video
if not grabbed:
break
# resize the frame, convert it to grayscale, and blur it
frame = imutils.resize(frame, width=500)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# if the average frame is None, initialize it
if avg is None:
print("[INFO] starting background model...")
avg = gray.copy().astype("float")
# frame.truncate(0)
continue
# accumulate the weighted average between the current frame and
# previous frames, then compute the difference between the current
# frame and running average
cv2.accumulateWeighted(gray, avg, 0.5)
frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
# threshold the delta image, dilate the thresholded image to fill
# in holes, then find contours on thresholded image
thresh = cv2.threshold(frameDelta, conf["delta_thresh"], 255,
cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
(_, cnts, _) = cv2.findContours(thresh.copy(),
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# loop over the contours
for c in cnts:
# if the contour is too small, ignore it
if cv2.contourArea(c) < conf["min_area"]:
continue
# compute the bounding box for the contour, draw it on the frame,
# and update the text
(x, y, w1, h1) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w1, y + h1), (0, 255, 0), 2)
motion_detected = True
fps = camera.get(cv2.CAP_PROP_FPS)
ts = timestamp.strftime("%Y-%m-%d_%H_%M_%S")
time_and_fps = ts + " - fps: " + str(fps)
# draw the text and timestamp on the frame
cv2.putText(frame, "Motion Detected: {}".format(motion_detected), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, time_and_fps, (10, frame.shape[0] - 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.35, (0, 0, 255), 1)
# check to see if the room is occupied
if motion_detected:
motionCounter += 1
# check to see if the number of frames with consistent motion is
# high enough
if motionCounter >= conf["min_motion_frames"]:
# check if the writer is None
if writer is None:
print("hitting writer is none")
# store the image dimensions, initialzie the video
# writer, and construct the zeros array
(h2, w2) = frame.shape[:2]
writer = cv2.VideoWriter("/Users/user/Library/Mobile Documents/com~apple~CloudDocs/testMotionDetection/" +
datetime.datetime.now().strftime("%Y-%m-%d_%H_%M_%S") + ".mp4",
fourcc, fps,
(w2, h2), True)
zeros = np.zeros((h2, w2), dtype="uint8")
# construct the final output frame, storing the
# original frame
output = np.zeros((h2, w2, 3), dtype="uint8")
output[0:h2, 0:w2] = frame
# write the output frame to file
writer.write(output)
# otherwise, there is no motion
else:
writer.release()
# Traceback (most recent call last):
# File "pi_surveillance.py", line 178, in <module>
# writer.release()
# AttributeError: 'NoneType' object has no attribute 'release'
motionCounter = 0
# check to see if the frames should be displayed to screen
if conf["show_video"]:
# display the security feed
cv2.imshow("Security Feed", frame)
key = cv2.waitKey(1) & 0xFF
# if the `q` key is pressed, break from the loop
if key == ord("q"):
break
# cleanup the camera and close any open windows
print("[INFO] cleaning up...")
camera.release()
cv2.destroyAllWindows()
# writer.release() - only releases writer when q is pressed
I was able to get this working correctly. This script creates a VideoWriter object for every frame with a temporary file. If motion is no detected within that frame, the writer is released and the file that was created with VideoWriter gets deleted.
If motion is detected, the file is kept and written to. Once motion is no longer detected, a countdown timer is started so it continues recording as long as you set. If motion is detected before the timer hits 0, then it continues to record and resets the timer and so on.
This is still being tuned, but works pretty well.
#!/usr/local/bin/python3
import argparse
import warnings
import datetime
import imutils
import json
import numpy as np
import os
import time
import cv2
print("[INFO] Kicking off script - " +
datetime.datetime.now().strftime("%Y-%m-%d_%H_%M_%S"))
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-c", "--conf", required=True,
help="path to the JSON configuration file")
args = vars(ap.parse_args())
# filter warnings, load the configuration
warnings.filterwarnings("ignore")
conf = json.load(open(args["conf"]))
# initialize the camera and grab a reference to the raw camera capture
# if the video argument is None, then we are reading from webcam
if not conf["use_ip_cam"]:
camera = cv2.VideoCapture(0)
time.sleep(0.25)
# otherwise, we are reading from a video input
else:
camera = cv2.VideoCapture(conf["ip_cam_addr"])
# allow the camera to warmup, then initialize the average frame, last
# uploaded timestamp, and frame motion counter
print("[INFO] warming up...")
time.sleep(conf["camera_warmup_time"])
avg = None
lastUploaded = datetime.datetime.now()
motion_counter = 0
non_motion_timer = conf["nonMotionTimer"]
fourcc = 0x00000020 # a little hacky, but works for now
writer = None
(h, w) = (None, None)
zeros = None
output = None
made_recording = False
# capture frames from the camera
while True:
# grab the raw NumPy array representing the image and initialize
# the timestamp and occupied/unoccupied text
(grabbed, frame) = camera.read()
timestamp = datetime.datetime.now()
motion_detected = False
# if the frame could not be grabbed, then we have reached the end
# of the video
if not grabbed:
print("[INFO] Frame couldn't be grabbed. Breaking - " +
datetime.datetime.now().strftime("%Y-%m-%d_%H_%M_%S"))
break
# resize the frame, convert it to grayscale, and blur it
frame = imutils.resize(frame, width=conf["resizeWidth"])
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# if the average frame is None, initialize it
if avg is None:
print("[INFO] starting background model...")
avg = gray.copy().astype("float")
# frame.truncate(0)
continue
# accumulate the weighted average between the current frame and
# previous frames, then compute the difference between the current
# frame and running average
cv2.accumulateWeighted(gray, avg, 0.5)
frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
# threshold the delta image, dilate the thresholded image to fill
# in holes, then find contours on thresholded image
thresh = cv2.threshold(frameDelta, conf["delta_thresh"], 255,
cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
(_, cnts, _) = cv2.findContours(thresh.copy(),
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# loop over the contours
for c in cnts:
# if the contour is too small, ignore it
if cv2.contourArea(c) < conf["min_area"]:
continue
# compute the bounding box for the contour, draw it on the frame,
# and update the text
(x, y, w1, h1) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w1, y + h1), (0, 255, 0), 2)
motion_detected = True
fps = int(round(camera.get(cv2.CAP_PROP_FPS)))
record_fps = 10
ts = timestamp.strftime("%Y-%m-%d_%H_%M_%S")
time_and_fps = ts + " - fps: " + str(fps)
# draw the text and timestamp on the frame
cv2.putText(frame, "Motion Detected: {}".format(motion_detected), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, time_and_fps, (10, frame.shape[0] - 10),
cv2.FONT_HERSHEY_SIMPLEX,
0.35, (0, 0, 255), 1)
# Check if writer is None TODO: make path configurable
if writer is None:
filename = datetime.datetime.now().strftime("%Y-%m-%d_%H_%M_%S")
file_path = (conf["userDir"] + "/Library/Mobile Documents/"
"com~apple~CloudDocs/testMotionDetection/testing/"
"{filename}.mp4")
file_path = file_path.format(filename=filename)
(h2, w2) = frame.shape[:2]
writer = cv2.VideoWriter(file_path, fourcc, record_fps, (w2, h2), True)
zeros = np.zeros((h2, w2), dtype="uint8")
def record_video():
# construct the final output frame, storing the original frame
output = np.zeros((h2, w2, 3), dtype="uint8")
output[0:h2, 0:w2] = frame
# write the output frame to file
writer.write(output)
# print("[DEBUG] Recording....")
if motion_detected:
# increment the motion counter
motion_counter += 1
# check to see if the number of frames with motion is high enough
if motion_counter >= conf["min_motion_frames"]:
if conf["create_image"]:
# create image TODO: make path configurable
image_path = (conf["userDir"] + "/Library/Mobile Documents/"
"com~apple~CloudDocs/testMotionDetection/testing"
"/{filename}.jpg").format(filename=filename)
cv2.imwrite(image_path, frame)
record_video()
made_recording = True
non_motion_timer = conf["nonMotionTimer"]
# If there is no motion, continue recording until timer reaches 0
# Else clean everything up
else: # TODO: implement a max recording time
# print("[DEBUG] no motion")
if made_recording is True and non_motion_timer > 0:
non_motion_timer -= 1
# print("[DEBUG] first else and timer: " + str(non_motion_timer))
record_video()
else:
# print("[DEBUG] hit else")
motion_counter = 0
if writer is not None:
# print("[DEBUG] hit if 1")
writer.release()
writer = None
if made_recording is False:
# print("[DEBUG] hit if 2")
os.remove(file_path)
made_recording = False
non_motion_timer = conf["nonMotionTimer"]
# check to see if the frames should be displayed to screen
if conf["show_video"]:
cv2.imshow("Security Feed", frame)
key = cv2.waitKey(1) & 0xFF
# if the `q` key is pressed, break from the loop
if key == ord("q"):
break
# cleanup the camera and close any open windows
print("[INFO] cleaning up...")
camera.release()
cv2.destroyAllWindows()
Example config file:
{
"show_video": true,
"use_ip_cam": false,
"ip_cam_addr": "rtsp://<ip>/live0.264",
"create_image": true,
"min_upload_seconds": 5,
"min_motion_frames": 12,
"camera_warmup_time": 2.5,
"delta_thresh": 5,
"resolution": [640, 480],
"fps": 16,
"min_area": 500,
"userDir": "/Path/to/user",
"resizeWidth": 500,
"nonMotionTimer": 36
}

Categories