I'm trying to write a code so that a webcam will take a picture every hour for an undetermined amount of time (2 weeks for now). Here's what I have so far (which works) :
t0=time.perf_counter()
time_max=3600*24*14
time_step= 3600
while(True):
tc=time.perf_counter()
crit = tc-t0
if (crit>time_max) :
cap.release()
break
cap.open(num_cam,api)
cap.set(cv2.CAP_PROP_FOCUS,70)
ret=cap.grab() #takes a picture
tst,frm=cap.retrieve()
cv2.imwrite('test1h_'+str(np.round(crit,2))+'_f70.png',frm)
cap.release()
time.sleep(time_step)
pass
I would like for this loop to stop if I press 'q' for example, and for the webcam to take a picture if I press 'p', how could I implement that ? I read you can you can use cv2.waitKey but I don't know where to put that in the code. I also read that you can use the nodule "keyboard" but that it requires a root in linux and I work on windows.
You can just use this:
if cv.waitKey(20) % 0xFF == ord("d"):
break
So for example if you want to display a video while not pressing the "d" key, this should work:
while True:
isTrue, frame = capture.read()
cv.imshow("Video", frame)
if cv.waitKey(20) % 0xFF == ord("d"):
break
capture.realease()
cv.destroyAllWindows()
Edit: Had mixed up 'char' and 'key', fixed it now.
If you're okay with not using cv2, I would suggest this using pynput:
from pynput import keyboard
def on_press(key):
print(key)
listener = keyboard.Listener(on_press=on_press)
listener.start()
In your case, you could implement it like this:
import time
from pynput import keyboard
t0=time.perf_counter()
time_max=3600*24*14
time_step= 3600
def on_press(key):
if key.char=='p':
#Take picture
cap.open(num_cam,api)
cap.set(cv2.CAP_PROP_FOCUS,70)
ret=cap.grab()
tst,frm=cap.retrieve()
cv2.imwrite('test1h_'+str(np.round(crit,2))+'_f70.png',frm)
cap.release()
elif key.char=='q':
#Interrupt
exit()
listener = keyboard.Listener(on_press=on_press)
listener.start()
while(True):
tc=time.perf_counter()
crit = tc-t0
if (crit>time_max) :
cap.release()
break
cap.open(num_cam,api)
cap.set(cv2.CAP_PROP_FOCUS,70)
ret=cap.grab() #takes a picture
tst,frm=cap.retrieve()
cv2.imwrite('test1h_'+str(np.round(crit,2))+'_f70.png',frm)
cap.release()
time.sleep(time_step)
pass
Related
I made a simple code to take snapshots from my webcam when prompted by pressing 's', and quit if I pressed 'q'. Code runs fine but the elif statement for the quitting part alone is ignored.
import cv2
cap = cv2.VideoCapture(0)
counter = 0
while(True):
ret, frame = cap.read()
cv2.imshow('Webcam', frame)
if (cv2.waitKey(1) & 0xFF) == ord('s'):
print("s working")
counter = counter + 1
cv2.imwrite("Snapshot_" + str(counter) + ".png", frame)
elif (cv2.waitKey(1) & 0xFF) == ord('q'):
print("q working")
break
cap.release()
cv2.destroyAllWindows()
I also tried swapping their order, the if statement being the 'q' condition and the elif being 's', the second statement is always the one skipped.
Update: it only works if I hold q for a while
Why isn't it working normally?
As I learned from the comments, the problem was caused by the waitKey delay. So to fix it I put the pressed button in a variable and compared the condition statements to that variable instead.
import cv2
cap = cv2.VideoCapture(0)
counter = 0
while(True):
ret, frame = cap.read()
cv2.imshow('Webcam', frame)
k = cv2.waitKey(1) & 0xFF
if k == ord('s'):
print("s working")
counter = counter + 1
cv2.imwrite("Screenshot_" + str(counter) + ".png", frame)
elif k == ord('q'):
print("q working")
break
cap.release()
cv2.destroyAllWindows()
The issue is not caused by any delays, nor by any blocking.
After cap.read() returns a new frame, there may be key events waiting because you pressed some keys.
Each waitKey() consumes one key press event and returns it, if there was a key event.
If you pressed a key, the first waitKey() receives that event, and if it was the wrong key, the if doesn't run its consequent code.
Then, the second waitKey() has no key event to handle, so it returns -1.
If you held the key down, you might have had a chance to create enough key press events to get one of them into the second waitKey().
The proper solution is to call waitKey() once per loop, and store its returned value in a variable.
Then you test that variable against as many key codes as you like.
while True:
...
keycode = cv.waitKey()
if keycode == ord('s'):
...
elif keycode == ord('q'):
...
That &0xFF stuff is superfluous and outdated since 2016. waitKey() does that already. It is superfluous.
I am using Jupyter Notebook for practicing coding (if it's relevant) and trying to use cv2 in order to take pictures using my webcam. However, whenever I take a picture and have that picture saved, the previous picture gets overwritten because of having the same file name. How do I keep this from not happening?
import cv2
key = cv2.waitKey(1)
webcam = cv2.VideoCapture(0)
while True:
try:
check, frame = webcam.read()
print(check)
print(frame)
cv2.imshow("Capturing", frame)
key = cv2.waitKey(1)
if key == ord('s'):
cv2.imwrite(filename='saved_img.jpg', img=frame)
webcam.release()
cv2.waitKey(1650)
cv2.destroyAllWindows()
break
elif key == ord('q'):
print("Turning off camera.")
webcam.release()
print("Camera off.")
print("Program ended.")
cv2.destroyAllWindows()
break
except(KeyboardInterrupt):
print("Turning off camera.")
webcam.release()
print("Camera off.")
print("Program ended.")
cv2.destroyAllWindows()
break
'saved_img' image file always gets overwritten whenever I capture...
You can use the isfile function from the os module:
import os
if os.isfile("saved_img.jpg"):
if input("do you want to overwrite saved_img.jpg? ") == "yes":
cv2.imwrite(filename='saved_img.jpg', img=frame)
else:
cv2.imwrite(filename='saved_img.jpg', img=frame)
If you need to restart-proof, you can check how many files have been saved before, so that code allows (as you said in comment) one image per run of th program
nb_files = len(list(Path(".").glob("saved_img_*.jpg")))
filename = f"saved_img_{nb_files}.jpg"
import cv2
key = cv2.waitKey(1)
webcam = cv2.VideoCapture(0)
while True:
try:
check, frame = webcam.read()
print(check)
print(frame)
cv2.imshow("Capturing", frame)
key = cv2.waitKey(1)
if key == ord('s'):
cv2.imwrite(filename=filename, img=frame)
webcam.release()
cv2.waitKey(1650)
cv2.destroyAllWindows()
break
...
Here's a variation if one run of the program may create multiple images
nb_files = len(list(Path(".").glob("saved_img_*.jpg")))
i = 0
import cv2
key = cv2.waitKey(1)
webcam = cv2.VideoCapture(0)
while True:
try:
check, frame = webcam.read()
print(check)
print(frame)
cv2.imshow("Capturing", frame)
key = cv2.waitKey(1)
if key == ord('s'):
cv2.imwrite(filename=f"saved_img_{nb_files + i}.jpg", img=frame)
i += 1
I am trying to put the camera footage into a different thread as the cv2.wait() does not work correctly anymore for access keys even when using an '& and hexadecimal'.
Does anyone know what I have done wrong with the daemon thread I have created for displaying camera footage in the main as when placed in the main while loop it fails while loop?
How do I use a thread to continuously display camera footage in my while loop in the main program?
quote My thread for the ViewCamera function isn't working right - can anyone help?
from djitellopy import Tello
# Access the telopy libray.
import cv2, math, time
from time import sleep, perf_counter
from threading import Thread
stop = True
def ViewCamera():
global stop
while stop == True:
try:
#camera window
img = frame_read.frame
cv2.imshow("drone", img)
print ('thread test')
except Exception as e:
print ("Cerror is : ,", e)
drone = Tello()
'''
create an instance of the Tellopy libray
drone.connect() #connect to drone using wifi
drone.streamon()
#make use of a data stream to drone to pass on keyboard commands
#frame_read = drone.get_frame_read() #read cammera data using frame per frame rate
'''
key = 0
# drone.takeoff()
if __name__ == "__main__":
print ('main program')
# Create a camera thread - runs in background.
t1 = Thread(target=ViewCamera, args=())
t1.daemon = True
print('starting thread')
t1.start()
while True:
key = ord(input('please enter letter'))
if key == ord('j'):
drone.land()
print('stopping thread')
stop = False
break
elif key == ord('w'): # Forward
drone.move_forward(30)
print('moving forwards')
elif key == ord('s'): # Move backwards
drone.move_back(30)
elif key == ord('a'): # Move left
drone.move_left(30)
elif key == ord('d'): # Move right
drone.move_right(30)
elif key == ord('e'): # Rotate right
drone.rotate_clockwise(30)
elif key == ord('q'): # Rotate left
drone.rotate_counter_clockwise(30)
elif key == ord('r'): # Move up
drone.move_up(30)
elif key == ord('f'): # Move down
drone.move_down(30)
print('stopping thread')
t1.join()
drone.land()
The threading daemon isn't seeming to work it should be running in background and continually allowing the camera images in a window to be viewed - any ideas why it is not?
I have the following code:
total_frames = 50
cv2.cv.NamedWindow("Dragonfly Simulation")
cv2.cv.StartWindowThread()
for i in range(total_frames):
# do stuff
img_name = # something
img = cv2.cv.LoadImage(img_name)
cv2.cv.ShowImage("Dragonfly Simulation", img)
cv2.cv.WaitKey(2)
cv2.cv.DestroyWindow("Dragonfly Simulation")
cv2.cv.WaitKey(1)
# rest of code
So what does it do:
Opens a window
In a loop, shows an image on the window
Once finished, closes the window
Runs the rest of the code
However in this case I have the total_frame given before. I don't want that.
Instead, I want a code that does the following:
Opens a window
In a loop, shows an image on the window
Waits for the user to close that window
When the user closes that window, exit loop, goes on with the rest of the code.
However, I cannot find a function in OpenCV that can detect when user closes a window. Can anyone suggest a workaround please?
I was just looking for a way to detect when the window has been closed using the X button of the window in addition to waiting for a key press, but I couldn't find an answer anywhere (IsWindowVisible and cvGetWindowHandle are not available in the Python cv2 module).
So I played around and this is how it works:
while cv2.getWindowProperty('window-name', 0) >= 0:
keyCode = cv2.waitKey(50)
# ...
cv2.getWindowProperty() returns -1 as soon as the window is closed.
For explanation, see the documentation for the enumeration of cv::WindowPropertyFlags: getting the flag with index 0 is the fullscreen property, but actually it doesn't matter which flag to use, they all become -1 as soon as the window is closed.
Note: This might only work for certain GUI backends. Notably, it will not work with the GTK backend used in Debian/Ubuntu packages. To use the Qt backend instead, you have to install opencv-python via pip.
As of version 2.2 there is a simple solution (this is modified from the loop in hist.py):
cv2.imshow('image',im)
while True:
k = cv2.waitKey(100) # change the value from the original 0 (wait forever) to something appropriate
...
elif k == 27:
print('ESC')
cv2.destroyAllWindows()
break
if cv2.getWindowProperty('image',cv2.WND_PROP_VISIBLE) < 1:
break
cv2.destroyAllWindows()
I tested on C++ using the getWindowProperty('image', WND_PROP_VISIBLE), but it does not work. So I used the WND_PROP_AUTOSIZE and it works.
I did like this:
cv::namedWindow("myTitle", WINDOW_AUTOSIZE);
while(1)
{
cv::imshow("myTitle", myImage);
if (cv::getWindowProperty("myTitle", WND_PROP_AUTOSIZE) == -1)
break;
}
if cv2.getWindowProperty('windowname',1) == -1 :
break
cv2.imshow('windowname', image)
import cv2
import numpy as np
# total_frames = 50
cv2.namedWindow("Dragonfly Simulation")
cv2.startWindowThread()
# for i in range(total_frames):
while True:
# do stuff
img = np.random.randint(0,255,(200,300)).astype(np.uint8)
cv2.imshow("Dragonfly Simulation", img)
key = cv2.waitKey(200)
print key
if key in [ord('a'), 1048673]:
print 'a pressed!'
elif key in [27, 1048603]: # ESC key to abort, close window
cv2.destroyAllWindows()
break
# do the rest of processing after break
print 'results:'
Sure, you can check user inputs using waitKey and here is a small example based on your code. I changed old cv interface to cv2. I think cv is obsolete.
(Edit) I moved cv2.destroyAllWindows() to inside the while loop to make it clear that the window closes when the user pressed ESC key (which you can assign a key of your choice). I do not think opencv has a proper event handler to catch the window close event like in other GUI toolkit (wxPython etc). So you will need to define how your users should close the window and watch out for that.
I used the following code t check if a key is pressed or the window is closed at the same time.
while cv2.getWindowProperty(camera.get_name(), cv2.WND_PROP_VISIBLE) > 0:
if cv2.waitKey(100) > 0:
break
cv2.destroyAllWindows()
I made a simple function based on this post. It works in opencv 4.5.2
def wait_with_check_closing(win_name):
"""
https://stackoverflow.com/questions/35003476/"
"opencv-python-how-to-detect-if-a-window-is-closed/37881722
"""
while True:
keyCode = cv2.waitKey(50)
if keyCode != -1:
break
win_prop = cv2.getWindowProperty(win_name, cv2.WND_PROP_VISIBLE)
if win_prop <= 0:
break
It can be used instead of cv2.waitKey(0) like following example.
# Usage
cv2.imshow(title, img_resized)
# cv2.waitKey(0)
wait_with_check_closing(title)
cv2.destroyAllWindows()
https://www.programcreek.com/python/example/110676/cv2.WND_PROP_VISIBLE
For my scenario, it had to be the following. I had to call k = cv2.waitKey(1) & 0xFF before checking status of the window for it to work
def show_webcam(mirror=False):
cam = cv2.VideoCapture(0)
# cv2.startWindowThread()
window_name="A_star webcam"
while True:
ret_val, img = cam.read()
if mirror:
img = cv2.flip(img, 1)
cv2.imshow(window_name, img)
k = cv2.waitKey(1) & 0xFF
if not (cv2.getWindowProperty(window_name,cv2.WND_PROP_VISIBLE)):
break
if cv2.waitKey(1) == 27:
break # esc to quit
cv2.destroyAllWindows()
The libraries installed are
autopep8==1.6.0
numpy==1.22.1
opencv-python==4.5.5.62
pycodestyle==2.8.0
toml==0.10.2
#David Kohen's answer is working great for me. But if you have many images to show its better to convert it to a function
def waitUntilX(window):
while True:
k = cv2.waitKey(100) # change the value from the original 0 (wait forever) to something appropriate
if k == 27:
print('ESC')
cv2.destroyAllWindows()
break
if cv2.getWindowProperty(window,cv2.WND_PROP_VISIBLE) < 1:
break
cv2.destroyAllWindows()
Usage
invertedmask = cv2.bitwise_not(mask)
cv2.imshow('inverted mask', invertedmask)
waitUntilX('inverted mask')
When I run the following sample code and press just "q", it'll ends properly,
but if I pressed any other characters "for instance many breaks and a lot of other characters" and then press "q" it'll not exit, how can I solve this?
import curses, time
def main(sc):
sc.nodelay(1)
while True:
sc.addstr(1, 1, time.strftime("%H:%M:%S"))
sc.refresh()
if sc.getch() == ord('q'):
break
time.sleep(1)
if __name__=='__main__': curses.wrapper(main)
Pressing other keys cause time.sleep(1) call, you should wait n seconds (n = number of other key strokes).
Removing time.sleep call will solve your problem.
def main(sc):
sc.nodelay(1)
while True:
sc.addstr(1, 1, time.strftime("%H:%M:%S"))
sc.refresh()
if sc.getch() == ord('q'):
break
#time.sleep(1) <------
Alternative: call time.sleep conditionally (only when no key was pressed, getch returns -1 if no key was pressed in non-blocking mode):
while True:
sc.addstr(1, 1, time.strftime("%H:%M:%S"))
sc.refresh()
key = sc.getch()
if key == ord('q'):
break
elif key < 0:
time.sleep(1)
The function window.timeout(delay) is most likely what you are looking for. Once a timeout is set, getch will wait delay milliseconds before returning -1.
Although using time.sleep(seconds) will work, timeout is much cleaner and will give a smoother user experience due to sleep delaying the processing of user input by as much as seconds.