Optimised way to generate summary of a video? - python

The code which I have written is able to create the summary of the video by skippig the frames with no motion. But it is taking more than 10 times the duration of the video to save the output video. So anyone can please help me with making some changes in the code. It is not the issue with system configuration. I have tried it even in a i7 GPU system.
import cv2
import imutils
vs = cv2.VideoCapture("example_01.mp4")
fgbg = cv2.createBackgroundSubtractorMOG2()
pathOut = "output.mp4"
frame_array = []
while True:
ret,frame = vs.read()
forig = frame.copy()
height,width,layers = frame.shape
size = (width,height)
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
mask = fgbg.apply(gray)
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
cnts = cv2.findContours(mask.copy(),
cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cv2.imshow('mask',mask)
for c in cnts:
area = cv2.contourArea(c)
if area > 2000:
frame_array.append(frame)
out = cv2.VideoWriter(pathOut,cv2.VideoWriter_fourcc(*'MPEG'), 20,
size)
for i in range(len(frame_array)):
out.write(frame_array[i])
out.release()
cv2.imshow("Frame", frame)
key = cv2.waitKey(25)
if key == ord("q"):
break
vs.release()
cv2.destroyAllWindows()

I took 18seconds movie and your code on my old computer worked many minutes and it was slowing down so finally I killed it and didn't get output movie.
This code needs ~57 seconds to do the same. If I don't display windows then it needs 39 seconds.
I open out only once. I don't append frame to frame_array but write this one frame directly to file.
import cv2
import imutils
import time
vs = cv2.VideoCapture("Wideo/1-sierpinski-carpet-turtle.mp4")
fgbg = cv2.createBackgroundSubtractorMOG2()
pathOut = "output.mp4"
out = None
start = time.time()
while True:
ret, frame = vs.read()
if frame is None:
break
forig = frame.copy()
height, width, layers = frame.shape
size = (width, height)
if not out:
out = cv2.VideoWriter(pathOut,cv2.VideoWriter_fourcc(*'MPEG'), 20, size)
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
mask = fgbg.apply(gray)
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cv2.imshow('mask',mask)
for c in cnts:
area = cv2.contourArea(c)
if area > 2000:
out.write(frame)
break # don't check other areas
cv2.imshow("Frame", frame)
key = cv2.waitKey(25)
if key == ord("q"):
break
end = time.time()
print("time:", end-start)
out.release()
vs.release()
cv2.destroyAllWindows()
Your code opens file again and again and write all frames again and again - so it was slowing down on my computer. You could eventually write it only once after loop.
import cv2
import imutils
import time
vs = cv2.VideoCapture("Wideo/1-sierpinski-carpet-turtle.mp4")
fgbg = cv2.createBackgroundSubtractorMOG2()
pathOut = "output.mp4"
frame_array = []
start = time.time()
while True:
ret, frame = vs.read()
if frame is None:
break
forig = frame.copy()
height, width, layers = frame.shape
size = (width, height)
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
mask = fgbg.apply(gray)
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cv2.imshow('mask',mask)
for c in cnts:
area = cv2.contourArea(c)
if area > 2000:
frame_array.append(frame)
break
cv2.imshow("Frame", frame)
key = cv2.waitKey(25)
if key == ord("q"):
break
# --- after loop ---
out = cv2.VideoWriter(pathOut,cv2.VideoWriter_fourcc(*'MPEG'), 20, size)
for frame in frame_array:
out.write(frame)
out.release()
end = time.time()
print("time:", end-start)
vs.release()
cv2.destroyAllWindows()

Related

How to continuously capture images while motion detection is running with webcam? (python)

I'm thinking if I can take pictures while the motion detection is using my laptop webcam, so I used the code below from Pyimagesearch website. What I'm trying to do is take a picture when the motion detector detected something using webcam, while the frame is different from the previous one, then take a photo and save it to a file, I tried 'ret, frame = cap.read()' but it didn't work well, I may put it the wrong way, could somebody solve it this for me, please?
The code is here:
# import the necessary packages
from imutils.video import VideoStream
import argparse
import datetime
import imutils #pip install imutils on terminal
import time
import cv2
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the video file") #video file is optional, if video file equals None, then opencv will use webcam
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimum area size") #500 pixels, no need to process large raw images through webcam
args = vars(ap.parse_args())
# if the video argument is None, then we are reading from webcam
if args.get('video', None) is None:
vs = VideoStream(src=0).start()
time.sleep(2.0)
# otherwise, we are reading from a video file
else:
vs = cv2.VideoCapture(args["video"])
# initialize the first frame in the video stream
firstFrame = None
# loop over the frames of the video
while True:
frame = vs.read()
frame = frame if args.get('video', None) is None else frame[1]
text = 'Unoccupied'
if frame is None:
break
frame = imutils.resize(frame, width=500)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
if firstFrame is None:
firstFrame = gray
continue
frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
for c in cnts:
if cv2.contourArea(c) < args["min_area"]:
continue
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
text = 'Occupied'
cv2.putText(frame, "Room Status: {}".format(text), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
(10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)
cv2.imshow('Motion Detector', frame)
cv2.imshow('Thresh', thresh)
cv2.imshow('Frame Delta', frameDelta)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
vs.stop()if args.get("video", None) is None else vs.release()
cv2.destroyAllWindows()
If I understand your question correctly, you want to take images if motion is detected. An approach I would suggest, is to do not try and extract every frame but to extract one frame every let's say 1 second. So a 1-minute video will give 60 frames(images) and you won't end up with thousands of images(Webcam frame rate * seconds).
count = 0 # initialize counter (outside while loop)
On motion, take a picture every 1 sec(I would place it inside this if statement:
if cv2.contourArea(c) < args["min_area"]:
vs.set(cv2.CAP_PROP_POS_MSEC,(count*1000)) # wait 1 sec between each capture
cv2.imwrite('Motion_det'+str(count)+'.jpg',frame) # save frame as JPEG file
count+=1
The adjust bellow will save image and video:
# import the necessary packages
from imutils.video import VideoStream
import argparse
import datetime
import imutils #pip install imutils on terminal
import time
import cv2
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the video file") #video file is optional, if video file equals None, then opencv will use webcam
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimum area size") #500 pixels, no need to process large raw images through webcam
args = vars(ap.parse_args())
# if the video argument is None, then we are reading from webcam
if args.get('video', None) is None:
vs = VideoStream(src=0).start()
time.sleep(2.0)
# otherwise, we are reading from a video file
else:
vs = cv2.VideoCapture(args["video"])
# initialize the first frame in the video stream
firstFrame = None
# loop over the frames of the video
sta = 0
while True:
frame = vs.read()
frame = frame if args.get('video', None) is None else frame[1]
text = 'Unoccupied'
if frame is None:
break
frame = imutils.resize(frame, width=500)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
if firstFrame is None:
firstFrame = gray
continue
frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
record = "No"
for c in cnts:
if cv2.contourArea(c) < args["min_area"]:
record = "No"
continue
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
text = 'Occupied'
record = "Yes"
cv2.putText(frame, "Room Status: {}".format(text), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
(10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)
cv2.imshow('Motion Detector', frame)
cv2.imshow('Thresh', thresh)
cv2.imshow('Frame Delta', frameDelta)
if record = "Yes":
try:
ts = time.time()
st = datetime.datetime.fromtimestamp(ts).strftime("%Y.%m.%d_%H.%M")
if sta != st:
filename = 'video-' + st + '.mp4'
out = cv2.VideoWriter(filename, fourcc, float(fps), (1280,720))
frame_name = filename.replace('.mp4','.png').format(frame_index)
cv2.imwrite(frame_name,frame)
sta = st
frame_record = cv2.resize(frame,(1280,720),fx=0,fy=0, interpolation = cv2.INTER_CUBIC)
out.write(frame_record)
except Exception as e:
print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
vs.stop()
vs.release()
cv2.destroyAllWindows()

Opencv, renew "first" frame

I followed a tutorial on opencv motion detection. I found the code in several sites, it's more or less the same: in a while loop, after having caught the first frame as base frame, find the difference comparing it to the next others.
I want to renew the first frame every 15 mins (thinking about light conditions during the day) but I cant understand why after taking the "new" first frame the process doesn't work anymore.
# import the necessary packages
from imutils.video import VideoStream
import argparse
import datetime
import imutils
import time
import cv2
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the video file")
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimum area size")
args = vars(ap.parse_args())
# if the video argument is None, then we are reading from webcam
if args.get("video", None) is None:
vs = VideoStream(src=0).start()
time.sleep(2.0)
# otherwise, we are reading from a video file
else:
vs = cv2.VideoCapture(args["video"])
# initialize the first frame in the video stream
firstFrame = None
interval = datetime.datetime.now()
# loop over the frames of the video
while True:
# grab the current frame and initialize the occupied/unoccupied
# text
frame = vs.read()
frame = frame if args.get("video", None) is None else frame[1]
text = "Unoccupied"
# if the frame could not be grabbed, then we have reached the end
# of the video
if frame is None:
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 first frame is None, initialize it
if firstFrame is None:
firstFrame = gray
continue
# compute the absolute difference between the current frame and
# first frame
frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
# dilate the thresholded image to fill in holes, then find contours
# on thresholded image
thresh = cv2.dilate(thresh, None, iterations=2)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# loop over the contours
for c in cnts:
# if the contour is too small, ignore it
if cv2.contourArea(c) < args["min_area"]:
continue
# compute the bounding box for the contour, draw it on the frame,
# and update the text
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
text = "Occupied"
# draw the text and timestamp on the frame
cv2.putText(frame, "Room Status: {}".format(text), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.putText(frame, datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"),
(10, frame.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 255), 1)
# show the frame and record if the user presses a key
cv2.imshow("Security Feed", frame)
cv2.imshow("Thresh", thresh)
cv2.imshow("Frame Delta", frameDelta)
key = cv2.waitKey(1) & 0xFF
# if the `q` key is pressed, break from the lop
if key == ord("q"):
break
# cleanup the camera and close any open windows
if (datetime.datetime.now() - interval) > datetime.timedelta(minutes=15):
firstFrame = None
vs.stop() if args.get("video", None) is None else vs.release()
cv2.destroyAllWindows()

Detect if a object is big enough open cv

I need some help here plz :)
I have this piece of code with open cv:
import cv2
import numpy as np
import time
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
low_red = np.array([100, 100, 100])
high_red = np.array([225, 225, 225])
mask = cv2.inRange(frame, low_red, high_red)
hasColor = np.sum(mask)
if hasColor > 1000000:
print(f'Hand opened, infos : {hasColor}')
elif hasColor > 500000 and hasColor < 1000000:
print(f'Hand closed, infos : {hasColor}')
cv2.imshow("Camera", frame)
cv2.imshow("Mask", mask)
if cv2.waitKey(1) & 0xFF == ord('e'):
break
And I would like that it detect only the objects of a certain size (like a hand) and not the smaller ones.
Thanks for help :)
EDIT: Made some good progress but still don't know how to get the size
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
low = np.array([100, 100, 100])
high = np.array([225, 225, 225])
mask = cv2.inRange(frame, low, high)
cv2.imshow("Camera", frame)
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
ret, tresh = cv2.threshold(gray, 127, 255, 0)
contours, hierarchy = cv2.findContours(tresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):
x,y,w,h = cv2.boundingRect(contours[i])
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("Mask", mask)
cv2.imshow("Hull", frame)
if cv2.waitKey(1) & 0xFF == ord('e'):
break
So I think I have to get the value of the bounding box but idk how
Problem solved thanks to a kind human,
I just had to add the following code in the for loop:
if h > 150 and w > 150:
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
I just had to check for the size, thanks for your help !

OpenCV Background Subtraction Get Color Objects (Python)

Background subtraction method (BackgroundSubtractorMOG2) will normally return the output in binary image.
Is there a solution on how I can get the original colour of the object after implementing the BackgroundSubtractorMOG2 ?
import cv2
import numpy as np
cap = cv2.VideoCapture("people.mkv")
kernel_dil = np.ones((10,10), np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
fgbg = cv2.createBackgroundSubtractorMOG2(history=0, varThreshold=444, detectShadows=False)
while True:
ret, frame1 = cap.read()
frame = cv2.resize(frame1,(1364,700),fx=0,fy=0, interpolation = cv2.INTER_CUBIC)
mask = np.zeros(frame.shape, dtype=np.uint8)
mask.fill(255)
roi_corners = np.array([[(11,652), (1353,652), (940,84), (424,84)]], dtype=np.int32)
cv2.fillPoly(mask, roi_corners, 0)
masking = cv2.bitwise_or(frame, mask)
if ret == True:
fgmask = fgbg.apply(masking,mask)
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
dilation = cv2.dilate(fgmask2, kernel_dil, iterations = 1)
(contours,hierarchy) = cv2.findContours(dilation, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for pic, contour in enumerate(contours):
area = cv2.contourArea(contour)
x,y,w,h = cv2.boundingRect(contour)
if(area>0.001):
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,0,255), 2)
cv2.putText(frame, 'People', (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,0,0), 2, cv2.LINE_AA)
cv2.imshow("FullScreen", frame)
cv2.imshow("FGMask1", fgmask)
cv2.imshow("FGMask2", dilation)
key = cv2.waitKey(12)
if key == ord("q"):
break
If you consider the output as binary mask, you could just do a bitwise and with your original image
cv.bitwise_and(input, input, mask = yourMOG_output);

Python cv2 VideoWriter File getting corrupted

I was following this tutorial and I tried to save the video to an avi file, but every time I tried the file was corrupted. I was able to save the frames individually using cv2.imwrite(), but stitching together the individual frames was a lot of work, and would lag the entire program. Here is my code:
from imutils.video import VideoStream
import imutils
import time
import cv2
MINIMUM_AREA = 500
# For Video Recording Purposes
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('vibralert_test001.avi', fourcc, 20, (640, 480))
vs = VideoStream(src=0).start()
print("Setting up feed.")
time.sleep(2)
print("Live")
firstFrame = None
while True:
frame = vs.read()
text = "No Movement Detected"
frame = imutils.resize(frame, width=500)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
if firstFrame is None:
firstFrame = gray
continue
delta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(delta, 25, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
for c in cnts:
if cv2.contourArea(c) < MINIMUM_AREA:
continue
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
text = "Movement Detected"
cv2.putText(frame, "Room Status: {}".format(text), (10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.imshow("VibrAlert v0.1", frame)
out.write(frame)
key = cv2.waitKey(1) & 0xFF
if key == 27:
break
vs.stop()
out.release()
cv2.destroyAllWindows()
print('End Feed')
The size of the frame images need to be the same as the frameSize given to the VideoWriter, (640,480). The frame is being resized to have a width of 500, while the videoWriter is expecting a width of 640.
However, changing the resize to frame = imutils.resize(frame, width=640) probably won't work either. imutils.resize will return images with the same aspect ratio as the original image, so the height won't necessarily be 480. I would suggest replacing that line with the opencv.resize:
frame = cv2.resize(frame, (640,480))

Categories

Resources