frame rate not correct in videos - opencv

I have a 12 sec video in 25 fps. When I play the video in 25 fps in opencv the video becomes 16 sec. I get the fps by using
fps = get(cv2.CAP_PROP_FPS) and then I set waitKey(1000/fps) but the video plays to slow...
import numpy as np
import cv2
import time
start = time.time()
cap = cv2.VideoCapture("hackerman.mp4")
fps = cap.get(cv2.CAP_PROP_FPS)
print(fps)
while True:
# Capture frame-by-frame
ret, frame = cap.read()
if ret == True:
frame_new = frame
else:
end = time.time()
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # turn video gray
# Display the resulting frame
cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
cv2.imshow('frame', frame_new)
k = cv2.waitKey(int(round(1000/fps))) & 0xFF
if k == 27: # wait for ESC key to exit
break
elif cv2.getWindowProperty("frame", 0) == -1:
break
print(end-start)
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

It appears that not unreasonably cap.read() is reading faster than the frame rate, which would be highly desirable if you were processing the frames rather than displaying them - so in your application you need to add a delay using e.g. time.sleep() or in your case waitKey() and this must be calculated to hit the frame rate 25fps.
For most precise 25fps, base the time for the end of the next frame on the overall starting time, like this (untested):
frameref_ms = int(time.time()*1000)
frametime_ms = int(1000/fps)
while True:
# update frameref to end frame period from now (regardless of how long reading and displaying the frame takes)
frameref_ms += frametime_ms
# Capture frame-by-frame
ret, frame = cap.read()
if ret == True:
frame_new = frame
else:
end = time.time()
# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # turn video gray
# Display the resulting frame
cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
cv2.imshow('frame', frame_new)
# wait for a keypress or the time needed to finish the frame duration
k = cv2.waitKey(frameref_ms-int(time.time()*1000)) & 0xFF
if k == 27: # wait for ESC key to exit
break
elif cv2.getWindowProperty("frame", 0) == -1:
break
This technique of having an absolute time to finish the display/read the next frame means that the frame rate will be exact, automatically compensating for multi-tasking other OS tasks as long as those other tasks don't take so much CPU that your code hardly runs, if you hit that problem I guess you'll have to increase the priority of Python which will slow the other programs down. I've used this method in timing sampling for temperature/vibration measurements and it works very well. See the same technique in one of my answers Inaccurate while loop timing in Python
If you were being really careful/pessimistic you'd also check that waitKey() isn't being given a negative delay in the case where the frame read+display took longer than the frame period.

Related

Print frame size above frame

I found the size of the frame, the information is displayed in the console and now I need the size to be displayed above the frame, it doesn’t work through putText.
OpenCV 4.7.0.68
please describe the full function of the code
import numpy as np
import cv2
cap=cv2.VideoCapture(1)
kernel=np.ones((2,2),np.uint8)
while (True):
# Capture frame-by-frame
ret,frame=cap.read()
# Our operations on the frame come here
gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
gray=cv2.GaussianBlur(gray,(7,7),0)
gray=cv2.medianBlur(gray,5) # to remove salt and paper noise
# to binary
ret,thresh=cv2.threshold(gray,128,128,128) # to detect white objects
# to get outer boundery only
thresh=cv2.morphologyEx(thresh,cv2.MORPH_GRADIENT,kernel)
# to strength week pixels
thresh=cv2.dilate(thresh,kernel,iterations=1)
im2=contours,hierarchy=cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
if len(contours)>0:
cv2.drawContours(frame,contours,-5,(0,255,0),3)
# find the biggest countour (c) by the area
c=max(contours,key=cv2.contourArea)
x,y,w,h=cv2.boundingRect(c)
# draw the biggest contour (c) in green
cv2.rectangle(frame,(x,y),(x+w,y+h),(255,255,0),2)
cv2.rectangle (frame,(x,y),(x+w*1,y+h//2),(255,255,0),2)
frameWidth=cap.get (cv2.CAP_PROP_FRAME_WIDTH)
frameHeight=cap.get (cv2.CAP_PROP_FRAME_HEIGHT)
print (frameWidth / 43 / 3.8)
print (frameHeight / 43 / 3.8)
# Display the resulting frame
cv2.imshow('frame',frame,)
if cv2.waitKey(27)&0xFF==ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

What is the purpose of decimation when calibrating a camera with Charuco?

I have been working on performing camera calibration using ChAruCo boards.
Following the code here (my commented version is shown below), it appears that only every other image is used when performing the camera calibration - due to the decimator.
What could be a reason for this? Other than to save processing power, which seems unnecessary since this step is only performed once.
def read_chessboards(chessboard_images):
# Charuco base pose estimation.
print("POSE ESTIMATION STARTS:")
# Declare lists to store corner locations and IDs
allCorners = []
allIds = []
decimator = 0
# SUB PIXEL CORNER DETECTION CRITERION
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.00001)
# for each of the chessboard images
for im in chessboard_images:
print("=> Processing image {0}".format(im))
frame = cv2.imread(im) # read current image into frame variable
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # convert to grayscale
corners, ids, rejectedImgPoints = cv2.aruco.detectMarkers(gray, ARUCO_DICT) # detect markers present in image
# if there are any markers detected
if len(corners) > 0:
# SUB PIXEL DETECTION
for corner in corners:
# refine corner locations
# TODO: check if this works
cv2.cornerSubPix(gray, corner,
winSize=(3, 3),
zeroZone=(-1, -1),
criteria=criteria)
# interpolate position of ChArUco board corners.
res2 = cv2.aruco.interpolateCornersCharuco(corners, ids, gray, board)
print(f'Charuco corners at: {res2}')
# if 3+ corners are detected, add to allCorners list for every other image
if res2[1] is not None and res2[2] is not None and len(res2[1]) > 3 and decimator % 1 == 0:
allCorners.append(res2[1])
allIds.append(res2[2])
# why only every other chessboard image?
decimator += 1
imsize = gray.shape
return allCorners, allIds, imsize
Just realized that x % 1 always evaluates to 0, so it doesn't actually do anything. I guess it was included as an optional feature - if you change 1 to some other number.

Mouse.position from pynput not working [python2, opencv, mac, jupyter]

I'm new to this so it may just be a syntax problem, but can anyone figure out why line 77 mouse.position = (x,y) isn't moving my mouse? It should be mapped to a dot drawn by holding a green object up to my webcam.
Furthermore, when I introduce the while mouse.position!=(x,y): pass the camera freezes when a green object is introduced.
Code here (no errors shown when freezing):
import cv2
import numpy as np
from pynput.mouse import Button, Controller
import wx
mouse=Controller()
#get monitor size
app=wx.App(False)
(sx,sy)=wx.GetDisplaySize()
print sx, sy
#output window size
(camx,camy)=(320,240)
#set filter limits
lowerBound=np.array([33,80,40])
upperBound=np.array([102,255,255])
#initialise cam
cam= cv2.VideoCapture(0)
cam.set(3,camx)
cam.set(4,camy)
#mask parameters
kernelOpen=np.ones((5,5))
kernelClose=np.ones((20,20))
while True:
ret, img=cam.read()
img=cv2.resize(img,(340,220))
#convert BGR to HSV
imgHSV= cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
#create the Mask
mask=cv2.inRange(imgHSV,lowerBound,upperBound)
#morphology
maskOpen=cv2.morphologyEx(mask,cv2.MORPH_OPEN,kernelOpen)
maskClose=cv2.morphologyEx(maskOpen,cv2.MORPH_CLOSE,kernelClose)
maskFinal=maskClose
#Find contours
conts,h=cv2.findContours(maskFinal.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
#For two contours (open):
if(len(conts)==2):
x1,y1,w1,h1=cv2.boundingRect(conts[0])
x2,y2,w2,h2=cv2.boundingRect(conts[1])
cv2.rectangle(img,(x1,y1),(x1+w1,y1+h1),(255,0,0),2)
cv2.rectangle(img,(x2,y2),(x2+w2,y2+h2),(255,0,0),2)
#finding points in middle of rectangles
cx1=x1+w1/2
cy1=y1+h1/2
cx2=x2+w2/2
cy2=y2+h2/2
#find centre of line
cx=(cx1+cx2)/2
cy=(cy1+cy2)/2
#create line between centres of contours
cv2.line(img,(cx1,cy1),(cx2,cy2),(255,0,0),2)
#create dot in middle of line
cv2.circle(img, (cx,cy),2,(0,0,255),2)
#to check values
print (cx*sx/camx)
print (cy*sy/camy)
#move mouse to dot cx,cy (scaled for monitor)
mouse.position = (cx*sx/camx,cy*sy/camy)
while mouse.position != (cx*sx/camx,cy*sy/camy):
pass
#For one contour (closed):
elif (len(conts)==1):
x,y,w,h= cv2.boundingRect(conts[0])
#draw single rectangle around 'clicked' object
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
#show when clicked with large circle
cx=x+w/2
cy=y+h/2
cv2.circle(img,(cx,cy),(w+h)/4,(0,0,255),2)
#set mouse to dot cx,cy
mouse.position = (cx*sx/camx,cy*sy/camy)
while mouse.position != (cx*sx/camx,cy*sy/camy):
pass
#cv2.imshow("maskClose",maskClose)
#cv2.imshow("mask",mask)
cv2.imshow("cam",img)
cv2.waitKey(5)
I appreciate any help!
My problem was that I needed to allow permission for terminal and jupyter to access mouse control (Mac Mojave 10.14.2).
The reason for the freeze is that it was stuck in the while loop - but with the mouse.position now working its all good!

Get Depth image in grayscale in ROS with imgmsg_to_cv2 [python]

I am using Kinect v1 and I want to get the depth image in greyscale mode from the channel "/camera/depth_registered/image" in ROS. As I found here, I can do it by using the function imgmsg_to_cv2. The default desired_encoding for my depth messages is "32FC1", which I keep. The problem is that when I use the cv2.imshow() function to show it, I get the image in binary... When I do the same for the RGB image everything is being shown just fine...
Any help appreciated!
So after all, I found a solution, which you can see here:
def Depthcallback(self,msg_depth): # TODO still too noisy!
try:
# The depth image is a single-channel float32 image
# the values is the distance in mm in z axis
cv_image = self.bridge.imgmsg_to_cv2(msg_depth, "32FC1")
# Convert the depth image to a Numpy array since most cv2 functions
# require Numpy arrays.
cv_image_array = np.array(cv_image, dtype = np.dtype('f8'))
# Normalize the depth image to fall between 0 (black) and 1 (white)
# http://docs.ros.org/electric/api/rosbag_video/html/bag__to__video_8cpp_source.html lines 95-125
cv_image_norm = cv2.normalize(cv_image_array, cv_image_array, 0, 1, cv2.NORM_MINMAX)
# Resize to the desired size
cv_image_resized = cv2.resize(cv_image_norm, self.desired_shape, interpolation = cv2.INTER_CUBIC)
self.depthimg = cv_image_resized
cv2.imshow("Image from my node", self.depthimg)
cv2.waitKey(1)
except CvBridgeError as e:
print(e)
However, the result is not that perfect as the one I get from the image_view node of ROS, but it is still pretty acceptable!

How to free memory space of an image after capturing and processing in python and opencv?

In the following code,after capturing and processing some images, this code occupy around 80% of main memory and then terminate automatically by OS.
import cv2
import numpy as np
capture = cv2.VideoCapture(0)
num = 1
RED_MIN=np.array([170,160,60],np.uint8)
RED_MAX=np.array([180,255,255],np.uint8)
while True:
flag, img = capture.read()
flag=capture.set(3,640)
flag=capture.set(4,480)
cv2.imwrite('pic.jpg', img)
img1=cv2.imread('pic.jpg')
img=cv2.blur(img1,(3,3))
hsv_img=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
frame_thresh=cv2.inRange(hsv_img,RED_MIN,RED_MAX)
contours,hierarchy=cv2.findContours(frame_thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
x_area=0
print num
num=num+1
if num == 500:
del(capture)
I used capture.release() in each iteration but unable to free space. When the value of num reaches to 500, it only releases the camera not main memory space that can be seen on terminal using top command.How can I free memory space after processing each image?

Resources