2017-02-16 3 views
2

Я использую OpenCV и Dlib для выполнения распознавания лица ж/ориентиров, живу от потока веба. Язык Python. Он отлично работает на моем ноутбуке macbook, но мне нужно, чтобы он запускался с настольного компьютера 24 часа в сутки. Компьютер представляет собой компьютер Intel® Core ™ 2 Quad CPU Q6600 @ 2.40GHz 32bit, работающий с Debian Jessie. Резкое падение производительности: есть задержка в 10 секунд из-за обработки!OpenCV/Python: многопоточность для живого распознавания лица

поэтому я посмотрел в многопоточной, чтобы получить производительность:

  1. Я первый попробовал образец кода с помощью OpenCV, и результат отличный! Все четыре ядра достигли 100%, а производительность намного лучше.
  2. Затем я заменил код обработки кадра кодом, и он не улучшает производительность вообще! Только одно ядро ​​попадает на 100%, остальные остаются очень низкими. Я даже думаю, что это хуже с многопоточным включением.

Я получил код ориентира лица из кода примера dlib. Я знаю, что он, вероятно, может быть оптимизирован, но я хочу понять , почему я не могу использовать полную мощность моего старого компьютера с многопоточными?

зайду мой код ниже, спасибо много для чтения :)

from __future__ import print_function 
 

 
import numpy as np 
 
import cv2 
 
import dlib 
 

 
from multiprocessing.pool import ThreadPool 
 
from collections import deque 
 

 
from common import clock, draw_str, StatValue 
 
import video 
 

 
class DummyTask: 
 
    def __init__(self, data): 
 
     self.data = data 
 
    def ready(self): 
 
     return True 
 
    def get(self): 
 
     return self.data 
 

 
if __name__ == '__main__': 
 
    import sys 
 

 
    print(__doc__) 
 

 
    try: 
 
     fn = sys.argv[1] 
 
    except: 
 
     fn = 0 
 
    cap = video.create_capture(fn) 
 
    
 
    #Face detector 
 
    detector = dlib.get_frontal_face_detector() 
 

 
    #Landmarks shape predictor 
 
    predictor = dlib.shape_predictor("landmarks/shape_predictor_68_face_landmarks.dat") 
 

 
    # This is where the facial detection takes place 
 
    def process_frame(frame, t0, detector, predictor): 
 
     # some intensive computation... 
 
     gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 
 
     clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) 
 
     clahe_image = clahe.apply(gray) 
 
     detections = detector(clahe_image, 1) 
 
     for k,d in enumerate(detections): 
 
      shape = predictor(clahe_image, d) 
 
      for i in range(1,68): #There are 68 landmark points on each face 
 
       cv2.circle(frame, (shape.part(i).x, shape.part(i).y), 1, (0,0,255), thickness=2) 
 
     return frame, t0 
 

 
    threadn = cv2.getNumberOfCPUs() 
 
    pool = ThreadPool(processes = threadn) 
 
    pending = deque() 
 

 
    threaded_mode = True 
 

 
    latency = StatValue() 
 
    frame_interval = StatValue() 
 
    last_frame_time = clock() 
 
    while True: 
 
     while len(pending) > 0 and pending[0].ready(): 
 
      res, t0 = pending.popleft().get() 
 
      latency.update(clock() - t0) 
 
      draw_str(res, (20, 20), "threaded  : " + str(threaded_mode)) 
 
      draw_str(res, (20, 40), "latency  : %.1f ms" % (latency.value*1000)) 
 
      draw_str(res, (20, 60), "frame interval : %.1f ms" % (frame_interval.value*1000)) 
 
      cv2.imshow('threaded video', res) 
 
     if len(pending) < threadn: 
 
      ret, frame = cap.read() 
 
      t = clock() 
 
      frame_interval.update(t - last_frame_time) 
 
      last_frame_time = t 
 
      if threaded_mode: 
 
       task = pool.apply_async(process_frame, (frame.copy(), t, detector, predictor)) 
 
      else: 
 
       task = DummyTask(process_frame(frame, t, detector, predictor)) 
 
      pending.append(task) 
 
     ch = cv2.waitKey(1) 
 
     if ch == ord(' '): 
 
      threaded_mode = not threaded_mode 
 
     if ch == 27: 
 
      break 
 
cv2.destroyAllWindows()

ответ

0

Не имеют большой опыт работы с использованием ThreadPool, но я всегда просто использовать процесс, как показано ниже. Вы должны иметь возможность легко редактировать этот код в соответствии с вашими потребностями. Я написал это с учетом вашей реализации.

Этот код получит количество ядер и начнет работать с несколькими рабочими процессами, которые будут выполнять параллельную реализацию желаемой функции. Все они разделяют очередь кадров для ввода, и все они помещаются в одну и ту же выходную Очередь для основного, чтобы получить и показать. Каждая очередь имеет максимальный размер, в этом случае 5. Это гарантирует, что, несмотря на процессорное время, которое требуется для обработки, оно всегда будет относительно живым.

import numpy as np 
 
import cv2 
 

 
from multiprocessing import Process, Queue 
 
import time 
 

 
#from common import clock, draw_str, StatValue 
 
#import video 
 

 
class Canny_Process(Process): 
 
    
 
    def __init__(self,frame_queue,output_queue): 
 
     Process.__init__(self) 
 
     self.frame_queue = frame_queue 
 
     self.output_queue = output_queue 
 
     self.stop = False 
 
     #Initialize your face detectors here 
 
     
 

 
    def get_frame(self): 
 
     if not self.frame_queue.empty(): 
 
      return True, self.frame_queue.get() 
 
     else: 
 
      return False, None 
 

 
    def stopProcess(self): 
 
     self.stop = True 
 
      
 
    def canny_frame(self,frame): 
 
     # some intensive computation... 
 
     gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 
 
     edges = cv2.Canny(gray, 50, 100) 
 
     
 
     #To simulate CPU Time 
 
     ############################# 
 
     for i in range(1000000): 
 
      x = 546*546 
 
      res = x/(i+1) 
 
     ############################# 
 
     'REPLACE WITH FACE DETECT CODE HERE' 
 

 
     if self.output_queue.full(): 
 
      self.output_queue.get_nowait() 
 
     self.output_queue.put(edges) 
 

 
    def run(self): 
 
     while not self.stop: 
 
      ret, frame = self.get_frame() 
 
      if ret: 
 
       self.canny_frame(frame) 
 

 

 
if __name__ == '__main__': 
 

 
    frame_sum = 0 
 
    init_time = time.time() 
 

 
    def put_frame(frame): 
 
     if Input_Queue.full(): 
 
      Input_Queue.get_nowait() 
 
     Input_Queue.put(frame) 
 

 
    def cap_read(cv2_cap): 
 
     ret, frame = cv2_cap.read() 
 
     if ret: 
 
      put_frame(frame) 
 
     
 
    cap = cv2.VideoCapture(0) 
 

 
    threadn = cv2.getNumberOfCPUs() 
 

 
    threaded_mode = True 
 

 
    process_list = [] 
 
    Input_Queue = Queue(maxsize = 5) 
 
    Output_Queue = Queue(maxsize = 5) 
 

 
    for x in range((threadn -1)):  
 
     canny_process = Canny_Process(frame_queue = Input_Queue,output_queue = Output_Queue) 
 
     canny_process.daemon = True 
 
     canny_process.start() 
 
     process_list.append(canny_process) 
 

 
    ch = cv2.waitKey(1) 
 
    cv2.namedWindow('Threaded Video', cv2.WINDOW_NORMAL) 
 
    while True:   
 
     cap_read(cap) 
 
     
 
     if not Output_Queue.empty(): 
 
      result = Output_Queue.get() 
 
      cv2.imshow('Threaded Video', result) 
 
      ch = cv2.waitKey(5) 
 

 
     if ch == ord(' '): 
 
      threaded_mode = not threaded_mode 
 
     if ch == 27: 
 
      break 
 
    cv2.destroyAllWindows()

Это должно сделать трюк просто изменить Осторожные функцию, чтобы сделать ваше обнаружение лица. Я написал это из вашего кода и сравнил их. Это значительно быстрее. Я использую multiprocessing.Process здесь. В python процессы действительно параллельны, и потоки не совсем из-за GIL. Я использую 2 очереди для отправки данных назад и вперед между основным и процессом. Очереди - это потоки и процессы.

+0

Большое спасибо за то, что нашли время, чтобы написать весь этот код :) Это не работает по какой-то причине, я собираюсь погрузиться в него на этой неделе. Я просто сменил компьютер на i7-2600K @ @ 3.40Ghz, и у меня все еще проблемы с производительностью! Наверное, узкое место где-то еще. ** Где-то в архитектуре OSX и Debian возможно? ** потому что все отлично работает в OSX! Еще раз спасибо, я буду информировать вас о моих результатах на этой неделе :) – Simon

+0

Что не совсем работает, код или производительность? Является ли код запущенным, но все еще недостаточно быстрым или код не работает вообще? Если вы дадите мне ошибку, я могу сказать вам, что это такое. Спасибо –

+0

Код работает, но не так, как ожидалось. Я поместил журнал в этот файл: https://docs.google.com/document/d/1cs9Jpf03EjTPHommMLmJ_84PxoAT3ATtmFlvBEg6l6I/edit?usp=sharing Опять же, у меня не было времени еще докопаться до него, я сделаю так что завтра. Еще раз спасибо :) – Simon

2

Проблема с производительностью была вызвана плохой компиляцией dlib. Не используйтеpip install dlib, который работает очень медленно по какой-то причине по сравнению с соответствующей компиляцией. Я пошел с почти 10 секунд отставание примерно до 2 секунд таким образом. Поэтому, наконец, мне не нужна многопоточность/обработка, но я работаю над этим, чтобы еще больше повысить скорость. Спасибо за помощь :)

0

Я пробовал упрощенный подход, такой как P.Ро упомянул в своем ответе, что процессы записываются в очередь вывода, но почему-то очередь блокировалась большую часть времени, потому что все процессы писали ей в одно и то же время. (просто моя догадка), я, вероятно, сделал что-то неправильно.

В конце концов я закончил использование труб.

этот код является отвратительным. но если бы я был мной несколько часов назад. я все равно был бы рад найти пример, который действительно работает без усилий.

from multiprocessing import Process, Queue, Manager,Pipe 
import multiprocessing 
import face_recognition as fik 
import cv2 
import time 


video_input = 0 

obama_image = fik.load_image_file("obama.png") 
obama_face_encoding = fik.face_encodings(obama_image)[0] 



quality = 0.7 


def f(id,fi,fl): 
    import face_recognition as fok 

    while True: 
     small_frame = fi.get() 
     print("running thread"+str(id)) 
     face_locations = fok.face_locations(small_frame) 

     if(len(face_locations)>0): 
      print(face_locations) 
      for (top7, right7, bottom7, left7) in face_locations: 

       small_frame_c = small_frame[top7:bottom7, left7:right7] 
       fl.send(small_frame_c) 

fps_var =0 
if __name__ == '__main__': 
     multiprocessing.set_start_method('spawn') 


     # global megaman 
     with Manager() as manager: 

      video_capture = cv2.VideoCapture(video_input) 

      fi = Queue(maxsize=14) 

      threads = 8 
      proc = [] 

      parent_p = [] 
      thread_p = [] 
      # procids = range(0,threads) 
      for t in range(0,threads): 
       p_t,c_t = Pipe() 
       parent_p.append(p_t) 
       thread_p.append(c_t) 
       print(t) 
       proc.append(Process(target=f, args=(t,fi,thread_p[t]))) 
       proc[t].start() 


      useframe = False 

      frame_id = 0 
      while True: 
       # Grab a single frame of video 
       ret, frame = video_capture.read() 
       effheight, effwidth = frame.shape[:2] 
       if effwidth < 20: 
        break 
       # Resize frame of video to 1/4 size for faster face recognition processing 
       xxx = 930 
       yyy = 10/16 #0.4234375 
       small_frame = cv2.resize(frame, (xxx, int(xxx*yyy))) 
       if frame_id%2 == 0: 
        if not fi.full(): 


         fi.put(small_frame) 

         print(frame_id) 

         cv2.imshow('Video', small_frame) 


         print("FPS: ", int(1.0/(time.time() - fps_var))) 
         fps_var = time.time() 


       #GET ALL DETECTIONS 
       for t in range(0,threads): 
        if parent_p[t].poll(): 
         small_frame_c = parent_p[t].recv() 
         cv2.imshow('recc', small_frame_c) 
         height34, width34 = small_frame_c.shape[:2] 
         # print fsizeee 
         if(width34<20): 
          print("face 2 small") 
          print(width34) 
          break 
         face_encodings_cam = fik.face_encodings(small_frame_c,[(0, width34, height34, 0)]) 

         match = fik.compare_faces([obama_face_encoding], face_encodings_cam[0]) 
         name = "Unknown" 

         if match[0]: 
          name = "Barack" 

         print(name) 
         break 

       frame_id += 1 

       # Hit 'q' on the keyboard to quit! 
       if cv2.waitKey(1) & 0xFF == ord('q'): 
        break 
Смежные вопросы