这个网络摄像头人脸检测有啥问题?

Posted

技术标签:

【中文标题】这个网络摄像头人脸检测有啥问题?【英文标题】:What's wrong with this webcam face detection?这个网络摄像头人脸检测有什么问题? 【发布时间】:2016-12-11 10:53:02 【问题描述】:

Dlib 有一个非常方便、快速和高效的对象检测例程,我想制作一个酷炫的面部跟踪示例,类似于示例 here。

得到广泛支持的 OpenCV 具有相当快的 VideoCapture 模块(快照需要五分之一秒,而调用某些程序来唤醒网络摄像头并获取图片需要 1 秒或更长时间)。我将此添加到 Dlib 中的人脸检测 Python 示例中。

如果您直接显示和处理 OpenCV VideoCapture 输出,它看起来很奇怪,因为显然 OpenCV 存储 BGR 而不是 RGB 顺序。调整后,它可以工作,但速度很慢:

from __future__ import division
import sys

import dlib
from skimage import io


detector = dlib.get_frontal_face_detector()
win = dlib.image_window()

if len( sys.argv[1:] ) == 0:
    from cv2 import VideoCapture
    from time import time

    cam = VideoCapture(0)  #set the port of the camera as before

    while True:
        start = time()
        retval, image = cam.read() #return a True bolean and and the image if all go right

        for row in image:
            for px in row:
                #rgb expected... but the array is bgr?
                r = px[2]
                px[2] = px[0]
                px[0] = r
        #import matplotlib.pyplot as plt
        #plt.imshow(image)
        #plt.show()

        print( "readimage: " + str( time() - start ) )

        start = time()
        dets = detector(image, 1)
        print "your faces: %f" % len(dets)
        for i, d in enumerate( dets ):
            print("Detection : Left:  Top:  Right:  Bottom: ".format(
                i, d.left(), d.top(), d.right(), d.bottom()))
            print("from left: ".format( ( (d.left() + d.right()) / 2 ) / len(image[0]) ))
            print("from top: ".format( ( (d.top() + d.bottom()) / 2 ) /len(image)) )
        print( "process: " + str( time() - start ) )

        start = time()
        win.clear_overlay()
        win.set_image(image)
        win.add_overlay(dets)

        print( "show: " + str( time() - start ) )
        #dlib.hit_enter_to_continue()



for f in sys.argv[1:]:
    print("Processing file: ".format(f))
    img = io.imread(f)
    # The 1 in the second argument indicates that we should upsample the image
    # 1 time.  This will make everything bigger and allow us to detect more
    # faces.
    dets = detector(img, 1)
    print("Number of faces detected: ".format(len(dets)))
    for i, d in enumerate(dets):
        print("Detection : Left:  Top:  Right:  Bottom: ".format(
            i, d.left(), d.top(), d.right(), d.bottom()))

    win.clear_overlay()
    win.set_image(img)
    win.add_overlay(dets)
    dlib.hit_enter_to_continue()


# Finally, if you really want to you can ask the detector to tell you the score
# for each detection.  The score is bigger for more confident detections.
# Also, the idx tells you which of the face sub-detectors matched.  This can be
# used to broadly identify faces in different orientations.
if (len(sys.argv[1:]) > 0):
    img = io.imread(sys.argv[1])
    dets, scores, idx = detector.run(img, 1)
    for i, d in enumerate(dets):
        print("Detection , score: , face_type:".format(
            d, scores[i], idx[i]))

从这个程序的时间输出来看,处理和抓取图片似乎都需要五分之一秒,所以你会认为它应该每秒显示一到两次更新 - 但是,如果你举手它会在 5 秒左右后显示在网络摄像头视图中!

是否有某种内部缓存阻止它获取最新的网络摄像头图像?我可以调整或多线程网络摄像头输入过程来修复延迟吗?这是在配备 16GB RAM 的 Intel i5 上。

更新

根据此处,它建议读取逐帧抓取视频。这将解释它抓取下一帧和下一帧,直到它最终赶上在处理时已抓取的所有帧。我想知道是否有一个选项来设置帧率或将其设置为丢帧,然后只需单击网络摄像头中的面部图片现在阅读? http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_gui/py_video_display/py_video_display.html#capture-video-from-camera

【问题讨论】:

dlib 检测图像所花费的时间,您可以尝试将图像调整为更小的尺寸以获得更好的性能。 @ZdaR 感谢您的建议。请运行该示例,您会发现只需要几分之一秒。为什么从移动到在网络摄像头窗口中显示移动需要将近 5 秒(在它更新之前显示了许多中间帧?)这就是问题所在。 相机分辨率和人脸尺寸是多少? 1280x720,面部的位置似乎并不重要。 【参考方案1】:

我感觉到你的痛苦。实际上,我最近使用了那个网络摄像头脚本(多次迭代;大量编辑)。我想我让它工作得很好。为了让您可以看到我所做的,我创建了一个包含详细信息(代码;HTML 自述文件;示例输出)的 GitHub Gist:

https://gist.github.com/victoriastuart/8092a3dd7e97ab57ede7614251bf5cbd

【讨论】:

Python3.5 也是。不错!【参考方案2】:

也许问题是设置了一个阈值。 如上所述here

dots = detector(frame, 1)

应该改为

dots = detector(frame)

为了避免阈值。 这对我有用,但同时存在帧处理速度过快的问题。

【讨论】:

这不是阈值。在 python API 中,它是在运行检测器之前进行的一些图像放大。在那种特殊情况下,“1”表示“升级一次图像”。这当然会增加处理时间(但允许检测较小的人脸)。 非常感谢您的澄清,Alexey!【参考方案3】:

如果您想在 OpenCV 中显示读取的帧,您可以在 cv2.imshow() 函数的帮助下完成,而无需更改颜色顺序。另一方面,如果你还想在matplotlib中显示图片,那么你不能避免使用这样的方法:

b,g,r = cv2.split(img)
img = cv2.merge((b,g,r))

这是我目前唯一可以帮助您的事情=)

【讨论】:

我认为 Dlib 可能需要按照它期望的顺序排列数组......或者它可能无法检测到正确的? HOGS 算法可能没问题,因为我改变了对象的颜色,它总是通过形状检测到它。 好吧,只要所有这些算法都在矩阵上运行,颜色的顺序就没有区别了。而且,检测算法通常拍摄黑白照片,这对于那些不想在颜色方面遇到麻烦的人来说也是一个加分项。 另外,OpenCV 有自己的检测算法,同样使用黑白图像进行检测 不幸的是,根据我的测试,OpenCV 有一些奇怪的故障,它会说墙壁的一部分是一张脸。 Dlib 似乎更可靠,速度更快,并且很容易在其中训练不同的形状。 blog.dlib.net/2014/02/… OpenCV 与 Dlib 人脸检测默认值对比:youtube.com/watch?v=LsK0hzcEyHI【参考方案4】:

我尝试了多线程,它同样慢,然后我只在线程中使用 .read() 进行多线程,没有处理,没有线程锁定,而且它工作得非常快 - 可能延迟 1 秒左右,而不是 3或 5. 见http://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/

from __future__ import division
import sys
from time import time, sleep
import threading

import dlib
from skimage import io


detector = dlib.get_frontal_face_detector()
win = dlib.image_window()

class webCamGrabber( threading.Thread ):
    def __init__( self ):
        threading.Thread.__init__( self )
        #Lock for when you can read/write self.image:
        #self.imageLock = threading.Lock()
        self.image = False

        from cv2 import VideoCapture, cv
        from time import time

        self.cam = VideoCapture(0)  #set the port of the camera as before
        #self.cam.set(cv.CV_CAP_PROP_FPS, 1)


    def run( self ):
        while True:
            start = time()
            #self.imageLock.acquire()
            retval, self.image = self.cam.read() #return a True bolean and and the image if all go right

            print( type( self.image) )
            #import matplotlib.pyplot as plt
            #plt.imshow(image)
            #plt.show()

            #print( "readimage: " + str( time() - start ) )
            #sleep(0.1)

if len( sys.argv[1:] ) == 0:

    #Start webcam reader thread:
    camThread = webCamGrabber()
    camThread.start()

    #Setup window for results
    detector = dlib.get_frontal_face_detector()
    win = dlib.image_window()

    while True:
        #camThread.imageLock.acquire()
        if camThread.image is not False:
            print( "enter")
            start = time()

            myimage = camThread.image
            for row in myimage:
                for px in row:
                    #rgb expected... but the array is bgr?
                    r = px[2]
                    px[2] = px[0]
                    px[0] = r


            dets = detector( myimage, 0)
            #camThread.imageLock.release()
            print "your faces: %f" % len(dets)
            for i, d in enumerate( dets ):
                print("Detection : Left:  Top:  Right:  Bottom: ".format(
                    i, d.left(), d.top(), d.right(), d.bottom()))
                print("from left: ".format( ( (d.left() + d.right()) / 2 ) / len(camThread.image[0]) ))
                print("from top: ".format( ( (d.top() + d.bottom()) / 2 ) /len(camThread.image)) )
            print( "process: " + str( time() - start ) )

            start = time()
            win.clear_overlay()
            win.set_image(myimage)
            win.add_overlay(dets)

            print( "show: " + str( time() - start ) )
            #dlib.hit_enter_to_continue()



for f in sys.argv[1:]:
    print("Processing file: ".format(f))
    img = io.imread(f)
    # The 1 in the second argument indicates that we should upsample the image
    # 1 time.  This will make everything bigger and allow us to detect more
    # faces.
    dets = detector(img, 1)
    print("Number of faces detected: ".format(len(dets)))
    for i, d in enumerate(dets):
        print("Detection : Left:  Top:  Right:  Bottom: ".format(
            i, d.left(), d.top(), d.right(), d.bottom()))

    win.clear_overlay()
    win.set_image(img)
    win.add_overlay(dets)
    dlib.hit_enter_to_continue()


# Finally, if you really want to you can ask the detector to tell you the score
# for each detection.  The score is bigger for more confident detections.
# Also, the idx tells you which of the face sub-detectors matched.  This can be
# used to broadly identify faces in different orientations.
if (len(sys.argv[1:]) > 0):
    img = io.imread(sys.argv[1])
    dets, scores, idx = detector.run(img, 1)
    for i, d in enumerate(dets):
        print("Detection , score: , face_type:".format(
            d, scores[i], idx[i]))

【讨论】:

以上是关于这个网络摄像头人脸检测有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

Emgu CV Capture IP 摄像头人脸检测问题

如何将网络摄像头选择添加到官方 mediapipe 人脸检测解决方案?

带有面部检测和形状预测的 Dlib 网络摄像头捕获速度很慢

如何使用 javascript 访问网络摄像头中的图像帧

ActionScript 人脸检测

EMGU CV 2.4.9 人脸识别准确率问题