openCV VideoCapture is several frames behind - python

I am making a program that uses a webcam to track objects in real time. The resulting display is always a few frames behind. For an example, when I move the camera to point at a new spot, it still shows the first position for a few frames.
Here is my program, it should find the circles in the frame and return an image with them circled:
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles() # parameters removed
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
#draw circle
cv2.circle() # parameters removed
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0XFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
As you can see it takes some time to process each frame. I expected the it to be choppy, but the result shows images from several seconds ago.

Related

Replacing 3D submatrix in Numpy and OpenCV2

Replace sub part of matrix by another small matrix in numpy generally seems to work for my purposes but I'm running into something I can't reconcile. Consider the following code, that creates two 3D matrices the shape of OpenCV2 webcam input, (in my case (480, 640, 3)), one of all 1s (frame) and one of random floats (rgb_noise_mask), replaces a specified submatrix in frame with the same submatrix of rgb_noise_mask, and displays it to the screen. This code works as intended, displaying a block of RGB-based static on a field of white.
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
frame = np.ones(frame.shape)
rgb_noise_mask = np.random.random(size=frame.shape)
while True:
boxes = [[300,300,30,30]]
for box in boxes:
x, y, width, height = box
frame[y:y2, x:x2] = rgb_noise_mask[y:y2, x:x2]
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Now take off the training wheels and use the actual webcam input instead of faking it. That same box now appears as uniform black instead of the expected colors:
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
rgb_noise_mask = np.random.random(size=frame.shape)
while True:
ret, frame = cap.read()
boxes = [[300,300,30,30]]
for box in boxes:
x, y, width, height = box
frame[y:y2, x:x2] = rgb_noise_mask[y:y2, x:x2]
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Why is this, and how can I get around it? Further adding to my confusion is that if I replace frame[y:y2, x:x2] = rgb_noise_mask[y:y2, x:x2] with frame[y:y2, x:x2] = frame[y:y2, x:x2][::-1] in the second code sample it behaves as expected and displays the live output with that square mirrored.
The issue here is that ret, frame = cap.read() returns frame as a numpy array with dtype=np.uint8, while rgb_noise_mask is float between 0,1, so all 0 when converted to uint8.
A simple fix is to generate noise as integers with randint:
rgb_noise_mask = np.random.randint(0,256, size=frame.shape, dtype=np.uint8)

Controlling Contrast and Brightness of Video Stream in OpenCV and Python

I’m using OpenCV3 and Python 3.7 to capture a live video stream from my webcam and I want to control the brightness and contrast. I cannot control the camera settings using OpenCV's cap.set(cv2.CAP_PROP_BRIGHTNESS, float) and cap.set(cv2.CAP_PROP_BRIGHTNESS, int) commands so I want to apply the contrast and brightness after each frame is read. The Numpy array of each captured image is (480, 640, 3). The following code properly displays the video stream without any attempt to change the brightness or contrast.
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
I get a washed-out video stream when I use Numpy’s clip() method to control the contrast and brightness, even when I set contrast = 1.0 (no change to contrast) and brightness = 0 (no change to brightness). Here is my attempt to control contrast and brightness.
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
contrast = 1.0
brightness = 0
frame = np.clip(contrast * frame + brightness, 0, 255)
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
How can I control the contrast and brightness of a video stream using OpenCV?
I found the solution using the numpy.clip() method and #fmw42 provided a solution using the cv2.normalize() method. I like the cv2.normalize() solution slightly better because it normalizes the pixel values to 0-255 rather than clip them at 0 or 255. Both solutions are provided here.
The cv2.normalize() solution:
Brightness - shift the alpha and beta values the same amount. Alpha
can be negative and beta can be higher than 255. (If alpha >= 255,
then the picture is white and if beta <= 0, then the picure is black.
Contrast - Widen or shorten the gap between alpha and beta.
Here is the code:
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
cv2.normalize(frame, frame, 0, 255, cv2.NORM_MINMAX)
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
The numpy.clip() solution:
This helped me solve the problem: How to fast change image brightness with python + OpenCV?. I need to:
Convert Red-Green Blue (RGB) to Hue-Saturation-Value (HSV) first
(“Value” is the same as “Brightness”)
“Slice” the Numpy array to the Value portion of the Numpy array and adjust brightness and contrast on that slice
Convert back from HSV to RGB.
Here is the working solution. Vary the contrast and brightness values. numpy.clip() ensures that all the pixel values remain between 0 and 255 in each on the channels (R, G, and B).
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
# Capture frame-by-frame
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
contrast = 1.25
brightness = 50
frame[:,:,2] = np.clip(contrast * frame[:,:,2] + brightness, 0, 255)
frame = cv2.cvtColor(frame, cv2.COLOR_HSV2BGR)
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
import cv2 as cv
cap = cv.VideoCapture(0)
while True:
# Capture frame-by-frame
ret, frame = cap.read()
# normalize the frame
frame = cv.normalize(
frame, None, alpha=0, beta=255, norm_type=cv.NORM_MINMAX, dtype=cv.CV_8UC1
)
# Display the resulting frame
cv.imshow("frame", frame)
# press q to quit
if cv.waitKey(1) & 0xFF == ord("q"):
break

Human Detection output is faster and the feeds is Slow

This is my first time coding in image processing using python and OpenCV. My first project is using webcam to creates a Human Motion detection and display their walking speed in real-time monitoring feed and now the first step I have created a program to detect human motion but the feed is super slow and the results of the output video are very fast. For example, I have running the program for about 1 minutes but the output video duration is only 7-10 seconds. I don't know how to fix this because I have try some adjustments like changing the waitKey() and the fps. I'm using HOG methods to detect human motion.
note : I'm using Logitech C270 with 30fps
this is my program:
import cv2
import numpy as np
import imutils
import time
#initialize the HOG descriptor/person detector
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
cv2.startWindowThread()
#open webcam video stream
cap = cv2.VideoCapture(1)
#output will be written to output.avi
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output_4.avi', fourcc, 18.0, (640,480))
frameCount = 0
start_time = time.time()
while(True):
#capture frame-by-frame
ret, frame = cap.read()
cv2.waitKey(30)
#resizing for faster detection
frame = cv2.resize(frame, (640,480))
#using a greyscale picture, also for faster detection
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
#detect people in the image
boxes, weights = hog.detectMultiScale(frame, winStride=(8,8))
#returns the bounding boxes for the detected objects
boxes = np.array([[x, y, x+w, y+h] for (x,y,w,h) in boxes])
#final bounding boxes
for (xA, yA, xB, yB) in boxes:
cv2.rectangle(frame, (xA, yA), (xB, yB),
(0,255,0),2)
#write the output video
out.write(frame)
#display the resulting frame
cv2.imshow('Human Detection',frame)
if cv2.waitKey(30) & 0xFF == ord('q'):
break
#release the capture
cap.release()
#release the output
out.release()
#close the window
cv2.destroyAllWindows()
cv2.waitKey(30)
Thank You.I hope you can help me.

How to get previous frame of a video in opencv python

I want to detect obstacles from a video based on their increasing size.To do that first I applied SIFT on gray image to get feature points of current frame. Next to compare the feature points of current frame with the previous frame I want to apply Brute-Force algorithm. For that I want to get feature points in previous frame. How can I access previous frame in opencv python ? and how to avoid accessing previous frame when the current frame is the first frame of the video?
below is the code written in python to get feature points of current frame.
import cv2
import numpy as np
cap = cv2.VideoCapture('video3.mov')
while(cap.isOpened()):
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#detect key feature points
sift = cv2.xfeatures2d.SIFT_create()
kp, des = sift.detectAndCompute(gray, None)
#draw key points detected
img=cv2.drawKeypoints(gray,kp,gray,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow("grayframe",img)
if cv2.waitKey(100) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
You could also get/set the zero-based frame index (CAP_PROP_POS_FRAMES), which might be useful if you wanted flexibility to step back through more than one frame, compare to a specific frame, etc. Note though that this would reset the position for the next read(), so if you really only ever want the previous frame, storing it in a variable per the other answers is probably better.
next_frame = cap.get(cv2.CAP_PROP_POS_FRAMES)
current_frame = next_frame - 1
previous_frame = current_frame - 1
if previous_frame >= 0:
cap.set(cv2.CAP_PROP_POS_FRAMES, previous_frame)
ret, frame = cap.read()
There is no specific function in OpenCV to access the previous frame. Your problem can be solved by calling cap.read() once before entering the while loop. Use a variable prev_frame to store the previous frame just before reading the new frame. Finally, as a good practice, you should verify that the frame was properly read, before doing computations on it. Your code could look something like:
import cv2
import numpy as np
cap = cv2.VideoCapture('video3.mov')
ret, frame = cap.read()
while(cap.isOpened()):
prev_frame=frame[:]
ret, frame = cap.read()
if ret:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#detect key feature points
sift = cv2.xfeatures2d.SIFT_create()
kp, des = sift.detectAndCompute(gray, None)
#some magic with prev_frame
#draw key points detected
img=cv2.drawKeypoints(gray,kp,gray, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow("grayframe",img)
else:
print('Could not read frame')
if cv2.waitKey(100) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Simply save the current frame to be the previous frame in the next iteration. Use a list, if you need more than 1.
import cv2
import numpy as np
cap = cv2.VideoCapture('video3.mov')
previousFrame=None
while(cap.isOpened()):
ret, frame = cap.read()
if previousFrame is not None:
#use previous frame here
pass
#save current frame
previousFrame=frame
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#detect key feature points
sift = cv2.xfeatures2d.SIFT_create()
kp, des = sift.detectAndCompute(gray, None)
#draw key points detected
img=cv2.drawKeypoints(gray,kp,gray,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow("grayframe",img)
if cv2.waitKey(100) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()

get frame from video with CV_CAP_PROP_POS_FRAMES in opencv python

I am trying to detect a (photography) flash in a video using OpenCV.
I detected the frame in which the flash occurs (average brightness above a threshold) and now I'd like to get the frame number.
I tried using CV_CAP_PROP_POS_FRAMES from the OpenCV docs without any success.
import numpy as np
import cv2
cap = cv2.VideoCapture('file.MOV')
while(cap.isOpened()):
ret, frame = cap.read()
BW = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
average = np.average(v) #computes the average brightness
if average > 200: #flash is detected
cv2.imshow('frame',BW)
frameid = cap.get(CV_CAP_PROP_POS_FRAMES) # <--- this line does not work
print(frameid)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Any tips ?
You can either:
Use cap.get(cv2.CAP_PROP_POS_FRAMES) (see here, also), or
increment a variable at each iteration: its current value is the current frame number
From opencv-doc:
When querying a property that is not supported by the backend used by the VideoCapture class, value 0 is returned
Probably it is not supported. In that case you have to count the frame number yourself.

Categories

Resources