首页 > 解决方案 > 并行化只需要在每 X 次迭代中运行的慢速函数,以免降低循环速度

问题描述

该项目

我正在进行一个项目,我需要检测人脸(边界框和地标)并执行人脸识别(识别人脸)。检测速度非常快(在我的笔记本电脑上甚至不需要几毫秒),但识别速度可能非常慢(在我的笔记本电脑上大约需要 0.4 秒)。我正在使用face_recognition Python 库来执行此操作。经过几次测试,我发现是图像的嵌入速度慢。

这是一个示例代码,您可以自己尝试一下:

# 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)

问题

我需要做的是处理视频(30 FPS),因此 0.4s 的计算是不可接受的。我的想法是识别只需要运行几次而不是每一帧,因为从一帧到另一帧,如果视频中没有剪辑,给定的头部将接近其先前的位置。因此,第一次出现头部时,我们运行识别非常慢,但是对于接下来的 X 帧,我们不必这样做,因为我们会检测到位置接近前一个,因此它必须是那个感动的人。当然,这种方法并不完美,但似乎是一个很好的折衷方案,我想尝试一下。

唯一的问题是,通过这样做,视频在出现头部之前是流畅的,然后视频由于识别而冻结,然后再次变得流畅。这就是我想介绍多处理的地方,我希望能够在循环遍历视频帧的同时计算识别。如果我能做到这一点,我将只需要提前处理几帧,这样当一张脸出现时,它已经在几秒钟前在几帧期间计算了它的识别,这样我们就不会看到帧速率降低了。

简单的配方

因此,这就是我所拥有的(在 python 伪代码中,以便更清晰):

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)

我想做这样的事情:

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)

目标是在循环的多次迭代中计算一个慢速函数。

我试过的

我抬头看了看multiprocessingRay但没有找到我想做的事的例子。大多数时候,我发现人们使用multiprocessing 同时​​计算不同输入的函数的结果。这不是我想要的。我希望有一个并行的循环和一个从循环(帧)接受数据的进程,进行一些计算,然后在不中断或减慢循环的情况下向循环返回一个值(或者至少传播减慢速度而不是而不是一个非常慢的迭代和 9 个快速的迭代)。

标签: pythonparallel-processingmultiprocessingface-recognitionopencv-python

解决方案


我想我几乎找到了如何做我想做的事。这是一个例子:

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")

当然,我需要添加标志,这样我就不会在循环中读取回调的同时写入值。


推荐阅读