I am writing a program for real time pitch detection. Here's the outline of the code :-
def pitch_detection :
result = []
while True :
// apply pitch detection algorithm
if pitch_energy > threshold :
result.append(pitch)
return result
I want to brrak from the loop if pitch_energy > threshold is False for sometime
What I am doing right now is timeout the loop after sometime.
Like this :-
How would I stop a while loop after n amount of time?
Save the time whenever you make an append to the list. Then compare it to the current time when you don't make a correction. If the time since you last made an append is greater than some threshold, you break:
def pitch_detection :
result = []
start = time.time()
MAX_TIME_ALLOWED = 5 # seconds
while True :
// apply pitch detection algorithm
if pitch_energy > threshold :
result.append(pitch)
start = time.time() # reset the time when we append.
else:
if (time.time() - start) > MAX_TIME_ALLOWED:
break
return result
This assumes you actually want to use elapsed time to decide when to break. If you want to use number of iterations of the loop, just use a counter that starts at 0, and increment it in the else block, rather than time.time().
def pitch_detection :
result = []
no_append = 0
MAX_TIME_ALLOWED = 5 # seconds
while True :
// apply pitch detection algorithm
if pitch_energy > threshold :
result.append(pitch)
no_append = 0
else:
no_append += 1
if no_append > MAX_TIMES_ALLOWED:
break
return result
You can use an alarm to time yourself out after four seconds. It'll require some setup to get going, since you need a function and an exception class to use.
import signal
# Define a couple things.
class TimeoutException(Exception):
pass
def timeout_handler(signum, frame):
raise TimeoutException()
# Set the alarm handler.
signal.signal(signal.SIGALRM,timeout_handler)
alarmOn = False
try:
while True:
# Pitch detection algorithm
if pitch_energy <= threshold:
# False condition - start your alarm.
if not alarmOn:
signal.alarm(4)
alarmOn = True
else:
# True condition - turn off the alarm.
alarmOn = False
signal.alarm(0)
result.append(pitch)
except TimeoutException: # This happens if four seconds pass without signal.alarm(0) being called.
print "We're done."
signal.alarm(0) # Turn the alarm off
The signal.alarm() function starts a timer with whatever value you give it, in seconds. If that many seconds pass without the alarm being reset, then a SIGALRM signal is sent. The way the code above works, it will catch that SIGALRM signal and throw our custom TimeoutException. This allows us to break out of the while loop, since we catch that exception.
EDIT: looking at dano's answer, it's entirely possible that this one is overly complicated. The main difference between ours is that this solution will immediately interrupt what you're doing if four seconds pass - that could stop your pitch detection algorithm right in the middle. dano's solution will always wait until that certain point in the code to check the time.
Related
The project
I am conducting a project where I need to both detect faces (bounding boxes and landmarks) and perform face recognition (identify a face). The detection is really fast (it takes not even a few milliseconds on my laptop) but the recognition can be really slow (about 0.4 seconds on my laptop). I am using the face_recognition Python library to do so. After a few tests, I discovered that it is the embedding of the image that is slow.
Here is an example code to try it out for yourself :
# Source : https://pypi.org/project/face-recognition/
import face_recognition
known_image = face_recognition.load_image_file("biden.jpg")
biden_encoding = face_recognition.face_encodings(known_image)[0]
image = face_recognition.load_image_file("your_file.jpg")
face_locations = face_recognition.face_locations(image)
face_landmarks_list = face_recognition.face_landmarks(image)
unknown_encoding = face_recognition.face_encodings(image)[0]
results = face_recognition.compare_faces([biden_encoding], unknown_encoding)
The problem
What I need to do is to process a video (30 FPS), therefore 0.4s of computation is unacceptable. The idea that I have is that the recognition will only need to be run a few times and not every frame since from one frame to another, if there are no cuts in the video, a given head will be close to its previous position. Therefore, the first time the head appears, we run the recognition which is very slow but then for the next X frames, we won't have to since we'll detect that the position is close to the previous one, therefore it must be the same person that moved. Of course, this approach is not perfect but seems to be a good compromise and I would like to try it.
The only problem is that by doing so the video is smooth until a head appears, then the video freezes because of the recognition and then becomes smooth again. This is where I would like to introduce multiprocessing, I would like to be able to compute the recognition in parallel of looping through the frame of the video. If I manage to do so, I will then only have to process a few frames in advance so that when a face shows up it already computed its recognition a few seconds ago during several frames so that we did not see a reduced frame rate.
Simple formulation
Therefore here is what I have (in python pseudo code so that it is clearer):
def slow_function(image):
# This function takes a lot of time to compute and would normally slow down the loop
return Recognize(image)
# Loop that we need to maintain at a given speed
person_name = "unknown"
frame_index = -1
while True:
frame_index += 1
frame = new_frame() # this is not important and therefore not detailes
# Every ten frames, we run a heavy function
if frame_index % 10 == 0:
person_name = slow_function(image)
# each frame we use the person_name even if we only compute it every so often
frame.drawText(person_name)
And I would like to do something like this :
def slow_function(image):
# This function takes a lot of time to compute and would normally slow down the loop
return Recognize(image)
# Loop that we need to maintain at a given speed
person_name = "unknown"
frame_index = -1
while True:
frame_index += 1
frame = new_frame() # this is not important and therefore not detailes
# Every ten frames, we run a heavy function
if frame_index % 10 == 0:
DO slow_function(image) IN parallel WITH CALLBACK(person_name = result)
# each frame we use the person_name even if we only compute it every so often
frame.drawText(person_name)
The goal is to compute a slow function over several iterations of a loop.
What I have tried
I looked up multiprocessing and Ray but I did not find examples of what I wanted to do. Most of the time I found people using multiprocessing to compute at the same time the result of a function for different inputs. This is not what I want. I want to have in parallel a loop and a process that accepts data from the loop (a frame), do some computation, and returns a value to the loop without interrupting or slowing down the loop (or at least, spreading the slow down rather than having one really slow iteration and 9 fast ones).
I think I found pretty much how to do what I want. Here is an example:
from multiprocessing import Pool
import time
# This seems to me more precise than time.sleep()
def sleep(duration, get_now=time.perf_counter):
now = get_now()
end = now + duration
while now < end:
now = get_now()
def myfunc(x):
time.sleep(1)
return x
def mycallback(x):
print('Callback for i = {}'.format(x))
if __name__ == '__main__':
pool=Pool()
# Approx of 5s in total
# Without parallelization, this should take 15s
t0 = time.time()
titer = time.time()
for i in range(100):
if i% 10 == 0: pool.apply_async(myfunc, (i,), callback=mycallback)
sleep(0.05) # 50ms
print("- i =", i, "/ Time iteration:", 1000*(time.time()-titer), "ms")
titer = time.time()
print("\n\nTotal time:", (time.time()-t0), "s")
t0 = time.time()
for i in range(100):
sleep(0.05)
print("\n\nBenchmark sleep time time:", 10*(time.time()-t0), "ms")
Of course, I will need to add flags so that I do not write a value with the callback at the same time that I read it in the loop.
In my app I want to allow the user to scroll through images by holding down an arrow key. Not surprisingly with larger images the pc can't keep up, and builds up a potentially large buffer that carries on being processed after the key is released.
None of this is unexpected, and my normal answer is just to check the timestamp in the event against the current time and discard any events that are more than (say) .2 seconds old. This way the backlog can never get too large.
But tkinter uses some random timebase of events so that comparing with time.time() is meaningless, and I can't find a function to get hold of tkinter's own clock. I'm sure its in there, it's just most of the pythonised tkinter documentation is a bit naff, and searching for time or clock isn't helping either.
def plotprev(self,p):
if time.time() - p.time > .2:
return
Sadly this test always returns true, where is tkinter's pseudo clock to be found?
Any other method will be complex in comparison.
well it's nor very nice, but it isn't too tedious and seems to work quite well: (with a little bit of monitoring as well)
def checklag(self,p):
if self.lasteventtime is None: #assume first event arrives with no significant delay
self.lasteventtime = p.time
self.lasteventrealtime = time.time()
self.lagok=0
self.lagfail=0
return True
ptdiff = (p.time-self.lasteventtime) / 1000
rtdiff = time.time() - self.lasteventrealtime
lag = rtdiff-ptdiff
if lag < .3:
self.lagok += 1
if self.lagok %20 == 0:
print("lagy? OK: %d, fail: %d" %(self.lagok, self.lagfail))
return True
else:
self.lagfail += 1
return False
I am working on a small proof of concept and using python to illustrate the idea. The idea is the program will run in a loop and will check for input. Now if the input falls under a threshold then it sends a notification. But I am trying to restrict the notification at an interval of 4 sec. And thats where I am loosing either with the logic or with some syntax. Either way It is doing some unexpected things
1: keep on entering 0 and it will display the below threshold message until it reaches a 4 sec mark and then it just prints out the message 4 times in a single line. I want them to show after every 4 seconds. The idea is (A)the input might change in that 4 sec and the notification switches. (B)I want the notification to play out as a reminder with a recurrence of 4 sec every time the script hits the condition if weightIn < 0.5..if it is true then the notification goes out after 4 sec from the first time it was sent
Sorry if I tried over explaining it. I am pretty new to python
import threading
def main():
while True:
weightIn = float(input("Get value: "))
threshold = .5
def operation():
if weightIn < 0.5:
#send notification at an interval of 4 sec
threading.Timer(4.0, operation).start()
print("Below weight threshhold...send notification")
else:
print("You are good")
if threshold is not None:
operation()
main()
First avoid declaring functions in a loop. Then ask yourself, if an object would not be appropriate, because it properly encloses state attributes.
But for the algorithmic part, it is simple (if I have correctly understood the problem ...). Store the timestamp of last notification and send a new one if more the 4 seconds have elapsed. In pseudo-code :
last_notification_time = 0
threshold = 0.5
loop:
weighIn = get_new_value()
if weightIn < threshold:
time = get_time_in_seconds()
if (time > last_notification_time + 4):
last_notification_time = time
send_notification()
# actual processing
In Python, it could look like :
#import time
def main():
last_notification_time = 0
threshold = 0.5
while True:
weighIn = float(input("Get value: "))
if weightIn < threshold:
cur_time = time.time()
if (cur_time > last_notification_time + 4):
last_notification_time = time
print("Below weight threshhold...send notification")
# actual processing
main()
I've got this program:
import multiprocessing
import time
def timer(sleepTime):
time.sleep(sleepTime)
fooProcess.terminate()
fooProcess.join() #line said to "cleanup", not sure if it is required, refer to goo.gl/Qes6KX
def foo():
i=0
while 1
print i
time.sleep(1)
i
if i==4:
#pause timerProcess for X seconds
fooProcess = multiprocessing.Process(target=foo, name="Foo", args=())
timer()
fooProcess.start()
And as you can see in the comment, under certain conditions (in this example i has to be 4) the timer has to stop for a certain X time, while foo() keeps working.
Now, how do I implement this?
N.B.: this code is just an example, the point is that I want to pause a process under certain conditions for a certain amount of time.
I am think you're going about this wrong for game design. Games always (no exceptions come to mind) use a primary event loop controlled in software.
Each time through the loop you check the time and fire off all the necessary events based on how much time has elapsed. At the end of the loop you sleep only as long as necessary before you got the next timer or event or refresh or ai check or other state change.
This gives you the best performance regarding lag, consistency, predictability, and other timing features that matter in games.
roughly:
get the current timestamp at the time start time (time.time(), I presume)
sleep with Event.wait(timeout=...)
wake up on an Event or timeout.
if on Event: get timestamp, subtract initial on, subtract result from timer; wait until foo() stops; repeat Event.wait(timeout=[result from 4.])
if on timeout: exit.
Here is an example, how I understand, what your Programm should do:
import threading, time, datetime
ACTIVE = True
def main():
while ACTIVE:
print "im working"
time.sleep(.3)
def run(thread, timeout):
global ACTIVE
thread.start()
time.sleep(timeout)
ACTIVE = False
thread.join()
proc = threading.Thread(target = main)
print datetime.datetime.now()
run(proc, 2) # run for 2 seconds
print datetime.datetime.now()
In main() it does a periodic task, here printing something. In the run() method you can say, how long main should do the task.
This code producess following output:
2014-05-25 17:10:54.390000
im working
im working
im working
im working
im working
im working
im working
2014-05-25 17:10:56.495000
please correct me, if I've understood you wrong.
I would use multiprocessing.Pipe for signaling, combined with select for timing:
#!/usr/bin/env python
import multiprocessing
import select
import time
def timer(sleeptime,pipe):
start = time.time()
while time.time() < start + sleeptime:
n = select.select([pipe],[],[],1) # sleep in 1s intervals
for conn in n[0]:
val = conn.recv()
print 'got',val
start += float(val)
def foo(pipe):
i = 0
while True:
print i
i += 1
time.sleep(1)
if i%7 == 0:
pipe.send(5)
if __name__ == '__main__':
mainpipe,foopipe = multiprocessing.Pipe()
fooProcess = multiprocessing.Process(target=foo,name="Foo",args=(foopipe,))
fooProcess.start()
timer(10,mainpipe)
fooProcess.terminate()
# since we terminated, mainpipe and foopipe are corrupt
del mainpipe, foopipe
# ...
print 'Done'
I'm assuming that you want some condition in the foo process to extend the timer. In the sample I have set up, every time foo hits a multiple of 7 it extends the timer by 5 seconds while the timer initially counts down 10 seconds. At the end of the timer we terminate the process - foo won't finish nicely at all, and the pipes will get corrupted, but you can be certain that it'll die. Otherwise you can send a signal back along mainpipe that foo can listen for and exit nicely while you join.
I'm reading serial data with a while loop. However, I have no control over the sample rate.
The code itself seems to take 0.2s to run, so I know I won't be able to go any faster than that. But I would like to be able to control precisely how much slower I sample.
I feel like I could do it using 'sleep', but the problem is that there is potential that at different points the loop itself will take longer to read(depending on precisely what is being transmitted over serial data), so the code would have to make up the balance.
For example, let's say I want to sample every 1s, and the loop takes anywhere from 0.2s to 0.3s to run. My code needs to be smart enough to sleep for 0.8s (if the loop takes 0.2s) or 0.7s (if the loop takes 0.3s).
import serial
import csv
import time
#open serial stream
while True:
#read and print a line
sample_value=ser.readline()
sample_time=time.time()-zero
sample_line=str(sample_time)+','+str(sample_value)
outfile.write(sample_line)
print 'time: ',sample_time,', value: ',sample_value
Just measure the time running your code takes every iteration of the loop, and sleep accordingly:
import time
while True:
now = time.time() # get the time
do_something() # do your stuff
elapsed = time.time() - now # how long was it running?
time.sleep(1.-elapsed) # sleep accordingly so the full iteration takes 1 second
Of course not 100% perfect (maybe off one millisecond or another from time to time), but I guess it's good enough.
Another nice approach is using twisted's LoopingCall:
from twisted.internet import task
from twisted.internet import reactor
def do_something():
pass # do your work here
task.LoopingCall(do_something).start(1.0)
reactor.run()
An rather elegant method is you're working on UNIX : use the signal library
The code :
import signal
def _handle_timeout():
print "timeout hit" # Do nothing here
def second(count):
signal.signal(signal.SIGALRM, _handle_timeout)
signal.alarm(1)
try:
count += 1 # put your function here
signal.pause()
finally:
signal.alarm(0)
return count
if __name__ == '__main__':
count = 0
count = second(count)
count = second(count)
count = second(count)
count = second(count)
count = second(count)
print count
And the timing :
georgesl#cleese:~/Bureau$ time python timer.py
5
real 0m5.081s
user 0m0.068s
sys 0m0.004s
Two caveats though : it only works on *nix, and it is not multithread-safe.
At the beginning of the loop check if the appropriate amount of time has passed. If it has not, sleep.
# Set up initial conditions for sample_time outside the loop
sample_period = ???
next_min_time = 0
while True:
sample_time = time.time() - zero
if sample_time < next_min_time:
time.sleep(next_min_time - sample_time)
continue
# read and print a line
sample_value = ser.readline()
sample_line = str(sample_time)+','+str(sample_value)
outfile.write(sample_line)
print 'time: {}, value: {}'.format(sample_time, sample_value)
next_min_time = sample_time + sample_period