Im trying to develop a simple code using opencv and python. My idea is the following:
I have a video with a moving object(free falling or parabolic) and I've managed to separate the video in frames. What I need (and I'm a total newby in this aaaand have little time) is to extract coordinates of the object frame by frame. So the idea is to click on the moving object, and get the (x, y) coordinate and the number of frame and open the next frame so I can do the same. So basically something with clicks over the foto and extracting the data in a csv and showing the next frame. Thus I could study its movement through space with its velocity and acelerations and stuff.
Haven't written any code yet.
Thanks in advance.
Look at docs example with using mouse input in opencv w python:
mouse example - opencv
You can define callback reading the click coordinates:
def get_clic_point(event,x,y,flags,param):
if event == cv.EVENT_LBUTTONDBLCLK: # on left double click
print(x,y) # here goes Your sepcific code, using x, y as click coordinates
In main loop, You need to create window and supply it with callback method
cv.namedWindow('main_win')
cv.setMouseCallback('main_win',get_clic_point)
Then using window name (in this case 'main_win') as Your window handle, You can
show image, calling cv.imshow('main_win',img), where img is loaded image.
You can write simple loop like this:
cv.namedWindow('main_win')
cv.setMouseCallback('main_win',get_clic_point)
images = []
# read images to this list
# ...
i = 0
while(1):
cv.imshow('main_win',images[i])
k = cv.waitKey(1) & 0xFF
if k == ord('n'): # n for next
# change image
i = i + 1
i = i % len(images)
elif k == 27:
break
cv.destroyAllWindows()
then just substitiute call back with desired logic
Related
I am using python 3.9, and PyCharm as my editor, but run the python files through the regular windows cmd.
So I am wondering is there any way that I can set the cursor position (specify where I want to put text in to CMD console) in python. I have already tried to use:
print("\033[6,3;HHello")
but it doesn't seem to work, and only prints "<[6,3HHello" directly, instead of printing at the coordinates of 6, 3, Any ideas or examples that I could use?
One way is to use opencv
import numpy as np
import cv2
dim_y = 500 #pixels
dim_x = int(dim_y*1.618+0.5)
ground = np.ones((dim_y,dim_x,3))*255
x_text = 50 #in horizontal axis
y_text = 50 #in vertical axis
while True:
cv2.putText(ground,'Hello World',(x_text, y_text),cv2.FONT_HERSHEY_SIMPLEX, 2.0, (220, 120, 120),2)
cv2.imshow('Window Name',ground)
key = cv2.waitKey(20) & 0xFF
if key == 27: # press ESC to break
break
cv2.waitKey(0) # press any key to pass
cv2.destroyAllWindows()
You need to press ESC to close the new window.
You can pass text at any coordinate whitin dim_x and dim_y.
With modifying the "putText" function, you can print whatever you type in realtime.
More information in this Github Repo Snake
I'm a noobie at programming and this question may sound easy and dumb but I really can not do it! The goal here is to change the color between green, blue and red everytime I click. I'm sorry if that's obvious, but I'm for hours stuck at this. That's the code I have, that prints everytime the same color. Now, I want to add something to change colors everytime I click.
import cv2
import numpy as np
def draw_circle(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),100,color=(0,255,0),thickness=10)
cv2.namedWindow(winname='my_drawing')
cv2.setMouseCallback('my_drawing', draw_circle)
img = np.zeros((512,512,3),np.int8)
while True:
cv2.imshow('my_drawing', img)
if cv2.waitKey(20) &0xFF == 27:
break
cv2.destroyAllWindows
I've tried a lot of stuff that I don't even know where to start, but I've tried creating a variable inside the function that each time it enters on the function, it sums, and depending on the value(using if) it goes to a different color, but the variable doesn't seem to change if it enters the loop again, then I've tried returning the variable as well. No success. Adding a Paramater. No Success as well. I believe it's such a simple thing that my head cannot think at this point.
Here's one way... make a list of the colours you want to cycle through. Have a global variable that you use as the index into the list to get the next colour. Each time you use it, add 1 to it and reduce it modulo the length of the list of colours. Colour the circle with value from the colour list as indexed by the index.
#!/usr/bin/env python3
import cv2
import numpy as np
index = 0
colours = [[255,0,0], [0,255,0], [0,0,255]]
def draw_circle(event,x,y,flags,param):
global index
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),100,color=colours[index],thickness=10)
index = (index+1) % len(colours)
cv2.namedWindow(winname='my_drawing')
cv2.setMouseCallback('my_drawing', draw_circle)
img = np.zeros((512,512,3),np.int8)
while True:
cv2.imshow('my_drawing', img)
if cv2.waitKey(20) &0xFF == 27:
break
cv2.destroyAllWindows
My problem here is that when I extracting a video into a frame using opencv, sometimes the frame that I get will flip up which happened to me for both my machine(window) and VM(ubuntu) But some of the video I tested, frames are not flip. So, I wonder what factor or what should be changed/added in my code to make the extract fixed without a flip
def extract_frame(video,folder):
global fps
os.mkdir('./green_frame/{folder}/'.format(folder=folder))
vidcap = cv2.VideoCapture(video)
success,image = vidcap.read()
fps = vidcap.get(cv2.CAP_PROP_FPS)
count = 0
success = True
while success: #os.path.join(pathOut,(name+'.png'))
cv2.imwrite(os.path.join('./green_frame/{folder}/'.format(folder=folder),"frame%d.png" % count), image)
success,image = vidcap.read()
print('Read a new frame: ', success)
count += 1
This is the example of frame I get from this code.
Which my orginal video that I used is upside down like this:
So, in my case, what I have to changed to make it not flip like my first picture. Is it relate to the resolution or framerate of the video? I tested with a 1280x720 resolution video and all of the frame extracted are flipped upside down but a frame from video with a 568x320 is normal
Thank you
Edit:
So, I look at the information of the video and I found out that in the metadata, it has rotate 180 for the video that extract to an upside down frame
But when I check with a normal video that produced a non upside-down frame, it does not have rotate:180
So from this, how can I deal with a video that has a rotation?
Update
This problem can now be solved by simply updating to OpenCV v4.5 and above.
If upgrading is a problem follow the old answer, below.
For anyone still looking into this, I was just stuck on the same problem. Turns out some Android phones and iPhones take images/frames in landscape and convert them on the fly according to the exif 'rotate' tag to display the images/frames.
Weird design choice in OpenCV is that cv2.imread(img_file) already reads the image in correct orientation by reading the image's rotate tag, but the cv2.VideoStream's read() method does not do this.
So, to fix this I used ffmpeg to read the 'rotate' tag and rotate the video frame to its correct orientation.(Big thanks to the comments above, for pointing me in the right direction 👍)
Following is the code:
Make sure you have ffmpeg for python. (pip install ffmpeg-python)
Create a method to check if rotation is required by the video_file:
import ffmpeg
def check_rotation(path_video_file):
# this returns meta-data of the video file in form of a dictionary
meta_dict = ffmpeg.probe(path_video_file)
# from the dictionary, meta_dict['streams'][0]['tags']['rotate'] is the key
# we are looking for
rotateCode = None
if int(meta_dict['streams'][0]['tags']['rotate']) == 90:
rotateCode = cv2.ROTATE_90_CLOCKWISE
elif int(meta_dict['streams'][0]['tags']['rotate']) == 180:
rotateCode = cv2.ROTATE_180
elif int(meta_dict['streams'][0]['tags']['rotate']) == 270:
rotateCode = cv2.ROTATE_90_COUNTERCLOCKWISE
return rotateCode
Create a method to correct the rotation of the frame in video file:
def correct_rotation(frame, rotateCode):
return cv2.rotate(frame, rotateCode)
Finally, do this in your main loop:
# open a pointer to the video file stream
vs = cv2.VideoCapture(video_path)
# check if video requires rotation
rotateCode = check_rotation(video_path)
# loop over frames from the video file stream
while True:
# grab the frame from the file
grabbed, frame = vs.read()
# if frame not grabbed -> end of the video
if not grabbed:
break
# check if the frame needs to be rotated
if rotateCode is not None:
frame = correct_rotation(frame, rotateCode)
# now your logic can start from here
Hope this helps 🍻
Sometimes the following will solve the problem of opening some videos upside-down.
cap = cv2.VideoCapture(path, apiPreference=cv2.CAP_MSMF)
When I recently ran into this issue, I found that all that was required was to update to OpenCV v4.5:
This is the related Github issue: https://github.com/opencv/opencv/issues/15499
Here is the commit: https://github.com/opencv/opencv/commit/f0271e54d90b3af62301f531f5f00995b00d7cd6
The rotate tag is optional so the check_rotation will fail,
This code fix it:
def check_rotation(path_video_file):
# this returns meta-data of the video file in form of a dictionary
meta_dict = ffmpeg.probe(path_video_file)
# from the dictionary, meta_dict['streams'][0]['tags']['rotate'] is the key
# we are looking for
rotate_code = None
rotate = meta_dict.get('streams', [dict(tags=dict())])[0].get('tags', dict()).get('rotate', 0)
return round(int(rotate) / 90.0) * 90
I would just do this in your frame processing loop:
frame = cv2.flip(frame,0)
The 0 flips vertically, see Open CV documentation for more info.
Here is the code for the same, have a look at it. In this, below code I am creating a Motion Detector and with this I will be recording the timings of when the various objects appeared and disappeared for which I am using dataframe.
The issue with this is that the program executes but when the output is displayed in the form of a Window, it freezes when I try to terminate.
import cv2, pandas
from datetime import datetime
first_frame = None
status_list = [None,None]
times = []
df = pandas.DataFrame(columns=["Start", "End"]) #Dataframe to store the time values during which object detection and movement appears.
video = cv2.VideoCapture(0)#VideoCapture object is used to record video using web cam
while True:
#check is a bool data type, returns true if VideoCapture object is read
check,frame = video.read()
status = 0
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) # For converting the frame color to gray scale
gray = cv2.GaussianBlur(gray,(21,21),0) # For converting the gray scale frame to GaussianBlur
if first_frame is None:
first_frame = gray # used to store the first image/frame of the video
continue
delta_frame = cv2.absdiff(first_frame,gray)#calculates the difference between first and other frames
thresh_delta = cv2.threshold(delta_frame,30,255,cv2.THRESH_BINARY)[1]
thresh_delta = cv2.dilate(thresh_delta,None,iterations=0) #Provides threshold value, so if the difference is <30 it will turn to black otherwise if >30 pixels will turn to white
(_,cnts,_) = cv2.findContours(thresh_delta.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) #Define the contour area,i.e. adding borders
#Removing noises and shadows, any part which is greater than 1000 pixels will be converted to white
for contour in cnts:
if cv2.contourArea(contour) < 1000:
continue
status = 1 #change in status when the object is detected
#Creating a rectangular box around the object in frame
(x,y,w,h) = cv2.boundingRect(contour)
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),3)
#list of status for every frame
status_list.append(status)
status_list=status_list[-2:]
#Record datetime in a list when change occurs
if status_list[-1]==1 and status_list[-2]==0:
times.append(datetime.now())
if status_list[-1]==0 and status_list[-2]==1:
times.append(datetime.now())
cv2.imshow('frame',frame)
#cv2.imshow('Capturing',gray)
#cv2.imshow('delta',delta_frame)
#cv2.imshow('thresh',thresh_delta)
key = cv2.waitKey(1) #changing the frame after 1 millisecond
#Used for terminating the loop once 'q' is pressed
if key == ord('q'):
break
print(status_list)
print(times)
for i in range(0,len(times),2):
df = df.append({"Start":times[i],"End":times[i+1]},ignore_index=True)
df.to_csv("Times.csv")
video.release()
cv2.destroyAllWindows #will be closing all the windows
Try this:
if cv2.waitKey(1) & 0xFF == ord('q'):
break
For a brief explanation about what "& 0xFF" is, see: What's 0xFF for in cv2.waitKey(1)?
It is basically a bit mask that takes the portion of 'waitKey' value(32 bit) that can be compared to ASCII (8 bit).
You can try this.
k = cv2.waitKey(1) & 0xFF
if k == 27 :
break
where k can be any ASCII code on the keyboard ASCII code help
In this case 27 is 'Esc' button.
However, the problem you're having where it is freezing, have you tried continuously pressing any button besides q? I had a similar problem when I was testing out OpenCV's tutorial code here
but i figured it out by playing around and then realizing i needed to hold down any button besides the 'exit' button.
I had faced the same issue bcz of this piece of code from geeksforgeeks
Please chck the last line of the code:
cv2.destroyAllWindows #will be closing all the windows
Add parenthesis, it shud b:
cv2.destroyAllWindows()
Please try this, rest looks fine
Well you can also pass a particular frame that you want to close like:
cv2.destroyAllWindows("frame")
I am having a problem related to performance using OpenCV3.2 with Python. I have just integrated another subsystem to my main program and it slowed down a lot.
This is my initial code without integrating the new subsystem, I am using cv2.getTickCount to measure time, as suggested by OpenCv3.2 Official Website.
# Infinite loop
while True:
# getting tick count
e1 = cv2.getTickCount()
# storing frame
_, img = cap.read()
# define red colours in the screen
findRedColours(img, board)
# getting tick count after the functions
e2 = cv2.getTickCount()
# calculating time
t = (e2 - e1) / cv2.getTickFrequency()
# print time
print(t)
# check if img is none
if img is not None:
# omitted code
k = cv2.waitKey(20) & 0xFF
# start the game, hide info
if (k == ord('s') or k == ord('S')) and start is False:
# create new thread to play game
t = Thread(target=playGame)
t.start()
Basically, I am calling a function in the infinite loop to find red colours and by pressing start I create the Thread and the game starts.
This is the time needed before I press 'S' to create the Thread:
0.019336862
0.016924178
0.022487864
This is the time needed after I press 'S' to create the Thread:
0.091731532
0.125760734
0.098221829
Here everything works fine, there is a light change in the time, but nothing too important. I start to have problems when I add my new subsystem. Here the following code with the integration of the new system, it is the same of the previous one, it is just a function call that changes:
# Infinite loop
while True:
# getting tick count
e1 = cv2.getTickCount()
# storing frame
_, img = cap.read()
# extract grid
gridExtractor.extractGrid(img)
# define red colours in the screen
findRedColours(img, board)
# getting tick count after the functions
e2 = cv2.getTickCount()
# calculating time
t = (e2 - e1) / cv2.getTickFrequency()
# print time
print(t)
# check if img is none
if img is not None:
# omitted code
k = cv2.waitKey(20) & 0xFF
# start the game, hide info
if (k == ord('s') or k == ord('S')) and start is False:
# create new thread to play game
t = Thread(target=playGame)
t.start()
and this is the time before I create the Thread:
0.045629524
0.023788123
0.10517206
It is slightly higher than the one without the integration, but still ok. Here is the time after I create Thread:
1.061517957
0.568310864
0.691701059
There is an enormous difference between this one and the previous one, it reaches even a whole second. It is noticeable even from the camera output, really slow.
My questions are, am I creating my Thread in the wrong way? There is a better and more efficient way to use rather the Threads? Or is there actually a way to optimise performance in this case without having to modify these functions findRedColours(img, board), t = Thread(target=playGame), gridExtractor.extractGrid(img)?
I am new using OpenCV and Python and still having troubles around. Hope someone can address me to the right way. Thanks.
Thanks to user 'deets' who help commenting above it has been possible to optimise performance.
In this case is enough to substitute Thread with Process from multiprocessing module in Python.
from multiprocessing import Process
#omitted code
while True:
# getting tick count
e1 = cv2.getTickCount()
# storing frame
_, img = cap.read()
# extract grid - first subsystem
gridExtractor.extractGrid(img)
# define red colours in the screen - second subsystem
findRedColours(img, board)
# getting tick count after the functions
e2 = cv2.getTickCount()
# calculating time
t = (e2 - e1) / cv2.getTickFrequency()
# print time
print(t)
# check if img is none
if img is not None:
# omitted code
k = cv2.waitKey(20) & 0xFF
# start the game, hide info
if (k == ord('s') or k == ord('S')) and start is False:
# create new thread to play game
p = Process(target=playGame)
p.start()
And the relative time needed is:
0.022570883
0.11354852
0.119643379
Compared to Thread the use of Process is way more efficient in terms of performance.