-->
-->
智能設備

使用樹莓派進行簡易人臉識別

字號+作者: 來源: 2017-01-09 16:02 我要評論() 收藏成功收藏本文

使用樹莓派2和OpenCV制作一個簡易的人臉識別和追蹤系統。所需硬件需要:樹莓派2Pi Camera非必須(如果需要追蹤人臉運動,需要一個有兩個馬達的小云臺):云臺...

使用樹莓派2和OpenCV制作一個簡易的人臉識別和追蹤系統。

識別效果

所需硬件

需要:

  • 樹莓派2

  • Pi Camera

非必須(如果需要追蹤人臉運動,需要一個有兩個馬達的小云臺):

  • 云臺

安裝OpenCV

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install python-opencv123123

安裝PiCamera

由于我沒有使用USB攝像頭,而是用了特殊的Pi Camera,樣子如下圖, 所以需要安裝PiCamera來控制攝像頭。


安裝PiCamera.jpg

安裝PiCamera:

sudo apt-get install python-pip
sudo apt-get install python-dev
sudo pip install picamera123123

至此人臉識別所需要的準備工作已經完成,可以使用下面的演示代碼進行測試。

示例代碼

Demo.1

第一個演示只使用單核,由于樹莓派的性能有限,在只使用一個CPU核心的情況下視頻的幀數非常之低,只有5幀左右,效果不太理想, 另外代碼中通過Servo Blaster 控制云臺的電機,來實現追蹤人臉的功能,不過考慮到這個功能不是必須,所以不在此進行介紹。

### Imports ###################################################################from picamera.array import PiRGBArrayfrom picamera import PiCameraimport timeimport cv2import os### Setup ###################################################################### Center coordinatescx = 160cy = 120os.system( "echo 0=150 > /dev/servoblaster" )
os.system( "echo 1=150 > /dev/servoblaster" )

xdeg = 150ydeg = 150# Setup the cameracamera = PiCamera()
camera.resolution = ( 320, 240 )
camera.framerate = 60rawCapture = PiRGBArray( camera, size=( 320, 240 ) )# Load a cascade file for detecting facesface_cascade = cv2.CascadeClassifier( '/home/pi/opencv-2.4.9/data/lbpcascades/lbpcascade_frontalface.xml' ) 

t_start = time.time()
fps = 0### Main ####################################################################### Capture frames from the camerafor frame in camera.capture_continuous( rawCapture, format="bgr", use_video_port=True ):

    image = frame.array    # Use the cascade file we loaded to detect faces
    gray = cv2.cvtColor( image, cv2.COLOR_BGR2GRAY )
    faces = face_cascade.detectMultiScale( gray )    print "Found " + str( len( faces ) ) + " face(s)"

    # Draw a rectangle around every face and move the motor towards the face
    for ( x, y, w, h ) in faces:

        cv2.rectangle( image, ( x, y ), ( x + w, y + h ), ( 100, 255, 100 ), 2 )
        cv2.putText( image, "Face No." + str( len( faces ) ), ( x, y ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )

        tx = x + w/2
        ty = y + h/2

        if   ( cx - tx >  10 and xdeg <= 190 ):
            xdeg += 3
            os.system( "echo 0=" + str( xdeg ) + " > /dev/servoblaster" )        elif ( cx - tx < -10 and xdeg >= 110 ):
            xdeg -= 3
            os.system( "echo 0=" + str( xdeg ) + " > /dev/servoblaster" )        if   ( cy - ty >  10 and ydeg >= 110 ):
            ydeg -= 3
            os.system( "echo 1=" + str( ydeg ) + " > /dev/servoblaster" )        elif ( cy - ty < -10 and ydeg <= 190 ):
            ydeg += 3
            os.system( "echo 1=" + str( ydeg ) + " > /dev/servoblaster" )    # Calculate and show the FPS
    fps = fps + 1
    sfps = fps / ( time.time() - t_start )
    cv2.putText( image, "FPS : " + str( int( sfps ) ), ( 10, 10 ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )    

    # Show the frame
    cv2.imshow( "Frame", image )
    cv2.waitKey( 1 )    # Clear the stream in preparation for the next frame
    rawCapture.truncate( 0 )


Demo1

另外請注意由于我使用HaarCascade來進行人臉檢測, 需要使用到識別人臉的XML,這些人臉識別的XML文件是隨著OpenCV一起安裝的,不需要額外的安裝, 不過當你在自己樹莓派上運行時,請注意調整XML文件的路徑, 就是調整這一行:

# Load a cascade file for detecting faces
face_cascade = cv2.CascadeClassifier( '你的XML文件路徑' )

Demo.2

通過同時使用不同的XML文件,可以實現同時識別不同物體的功能,比如下面這段代碼可以同時識別人臉和黑色手機,識別手機所需要的XML文件是由Radamés Ajna和Thiago Hersan制作的, 來源在這里。 更進一步的,我們可以根據自己的需要訓練自己的Cascade文件,Naotoshi Seo在此處 給出了詳細的教程, 比較簡易的還有Thorsten Ball的香蕉識別教程

### Imports ###################################################################from picamera.array import PiRGBArrayfrom picamera import PiCameraimport timeimport cv2import osimport pygame### Setup #####################################################################os.putenv('SDL_FBDEV', '/dev/fb1')# Setup the cameracamera = PiCamera()
camera.resolution = ( 320, 240 )
camera.framerate = 40rawCapture = PiRGBArray( camera, size=( 320, 240 ) )# Load the cascade files for detecting faces and phonesface_cascade = cv2.CascadeClassifier( '/home/pi/opencv-2.4.9/data/lbpcascades/lbpcascade_frontalface.xml' )
phone_cascade = cv2.CascadeClassifier( 'cascade.xml' )

t_start = time.time()
fps = 0### Main ####################################################################### Capture frames from the camerafor frame in camera.capture_continuous( rawCapture, format="bgr", use_video_port=True ):

    image = frame.array    # Look for faces and phones in the image using the loaded cascade file
    gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    faces  = face_cascade.detectMultiScale(gray)
    phones = phone_cascade.detectMultiScale(gray)    # Draw a rectangle around every face
    for (x,y,w,h) in faces:
        cv2.rectangle( image, ( x, y ), ( x + w, y + h ), ( 255, 255, 0 ), 2 )
        cv2.putText( image, "Face No." + str( len( faces ) ), ( x, y ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )    # Draw a rectangle around every phone
    for (x,y,w,h) in phones:
        cv2.rectangle( image, ( x, y ), ( x + w, y + h ), ( 255, 0, 0 ), 2 )
        cv2.putText( image, "iPhone", ( x, y ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 255, 255 ), 2 )    # Calculate and show the FPS
    fps = fps + 1
    sfps = fps / ( time.time() - t_start )
    cv2.putText( image, "FPS : " + str( int( sfps ) ), ( 10, 10 ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )

    cv2.imshow( "Frame", image )
    cv2.waitKey( 1 )    # Clear the stream in preparation for the next frame
    rawCapture.truncate( 0 )
Demo2

由于使用了更多的XML文件進行識別,幀數降低到了2~3幀。

Demo.3

為了解決幀數較低的問題,有一個比較簡單的方法就是跳幀,可以不對每一幀圖像都進行識別,而是隔幾幀識別一次(因為最初因為懶不想將程序寫成多線程,但是為了提高幀數,所以有了這個蛋疼的方法…)。

### Imports ###################################################################from picamera.array import PiRGBArrayfrom picamera import PiCameraimport timeimport cv2import osimport pygame### Setup #####################################################################os.putenv( 'SDL_FBDEV', '/dev/fb1' )# Setup the cameracamera = PiCamera()
camera.resolution = ( 320, 240 )
camera.framerate = 30rawCapture = PiRGBArray( camera, size=( 320, 240 ) )

fcounter = 0facefind = 0# Load a cascade file for detecting facesface_cascade = cv2.CascadeClassifier( '/home/pi/opencv-2.4.9/data/lbpcascades/lbpcascade_frontalface.xml' )

t_start = time.time()
fps = 0### Main ####################################################################### Capture frames from the camerafor frame in camera.capture_continuous( rawCapture, format="bgr", use_video_port=True ):

    image = frame.array    # Run the face detection algorithm every four frames
    if fcounter == 3:

        fcounter = 0

        # Look for faces in the image using the loaded cascade file
        gray = cv2.cvtColor( image, cv2.COLOR_BGR2GRAY )
        faces = face_cascade.detectMultiScale( gray )        print "Found " + str( len( faces ) ) + " face(s)"

        if str( len( faces ) ) != 0:
            facefind = 1
            facess = faces        else:
            facefind = 0

        # Draw a rectangle around every face
        for ( x, y, w, h ) in faces:
            cv2.rectangle( image, ( x, y ), ( x + w, y + h ), ( 200, 255, 0 ), 2 )
            cv2.putText( image, "Face No." + str( len( facess ) ), ( x, y ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )
            facess = faces    else:        if facefind == 1 and str( len( facess ) ) != 0:            # Continue to draw the rectangle around every face
            for ( x, y, w, h ) in facess:
                cv2.rectangle( image, ( x, y ), ( x + w, y + h ), ( 200, 255, 0 ), 2 )
                cv2.putText( image, "Face No." + str( len( facess ) ), ( x, y ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )

    fcounter += 1


    # Calculate and show the FPS
    fps = fps + 1
    sfps = fps / ( time.time() - t_start )
    cv2.putText( image, "FPS : " + str( int( sfps ) ), ( 10, 10 ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )

    cv2.imshow( "Frame", image )
    cv2.waitKey( 1 )    # Clear the stream in preparation for the next frame
    rawCapture.truncate( 0 )
Demo3

這樣子幀數會提高到10幀左右,已經不像原來那么卡頓,但是當你移動速度很快的時候,識別框會出現滯后。

Demo.4

畢竟跳幀只是權宜之計,這個版本使用了全部的CPU核心,幀數穩定在了15幀左右。

### Imports ###################################################################from picamera.array import PiRGBArrayfrom picamera import PiCamerafrom functools import partialimport multiprocessing as mpimport cv2import osimport time### Setup #####################################################################os.putenv( 'SDL_FBDEV', '/dev/fb0' )

resX = 320resY = 240cx = resX / 2cy = resY / 2os.system( "echo 0=150 > /dev/servoblaster" )
os.system( "echo 1=150 > /dev/servoblaster" )

xdeg = 150ydeg = 150# Setup the cameracamera = PiCamera()
camera.resolution = ( resX, resY )
camera.framerate = 60# Use this as our outputrawCapture = PiRGBArray( camera, size=( resX, resY ) )# The face cascade file to be usedface_cascade = cv2.CascadeClassifier('/home/pi/opencv-2.4.9/data/lbpcascades/lbpcascade_frontalface.xml')

t_start = time.time()
fps = 0### Helper Functions ##########################################################def get_faces( img ):

    gray = cv2.cvtColor( img, cv2.COLOR_BGR2GRAY )
    faces = face_cascade.detectMultiScale( gray )    return faces, imgdef draw_frame( img, faces ):

    global xdeg    global ydeg    global fps    global time_t    # Draw a rectangle around every face
    for ( x, y, w, h ) in faces:

        cv2.rectangle( img, ( x, y ),( x + w, y + h ), ( 200, 255, 0 ), 2 )
        cv2.putText(img, "Face No." + str( len( faces ) ), ( x, y ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )

        tx = x + w/2
        ty = y + h/2

        if   ( cx - tx > 15 and xdeg <= 190 ):
            xdeg += 1
            os.system( "echo 0=" + str( xdeg ) + " > /dev/servoblaster" )        elif ( cx - tx < -15 and xdeg >= 110 ):
            xdeg -= 1
            os.system( "echo 0=" + str( xdeg ) + " > /dev/servoblaster" )        if   ( cy - ty > 15 and ydeg >= 110 ):
            ydeg -= 1
            os.system( "echo 1=" + str( ydeg ) + " > /dev/servoblaster" )        elif ( cy - ty < -15 and ydeg <= 190 ):
            ydeg += 1
            os.system( "echo 1=" + str( ydeg ) + " > /dev/servoblaster" )    # Calculate and show the FPS
    fps = fps + 1
    sfps = fps / (time.time() - t_start)
    cv2.putText(img, "FPS : " + str( int( sfps ) ), ( 10, 10 ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 ) 

    cv2.imshow( "Frame", img )
    cv2.waitKey( 1 )### Main ######################################################################if __name__ == '__main__':

    pool = mp.Pool( processes=4 )
    fcount = 0

    camera.capture( rawCapture, format="bgr" )  

    r1 = pool.apply_async( get_faces, [ rawCapture.array ] )    
    r2 = pool.apply_async( get_faces, [ rawCapture.array ] )    
    r3 = pool.apply_async( get_faces, [ rawCapture.array ] )    
    r4 = pool.apply_async( get_faces, [ rawCapture.array ] )    

    f1, i1 = r1.get()
    f2, i2 = r2.get()
    f3, i3 = r3.get()
    f4, i4 = r4.get()

    rawCapture.truncate( 0 )    

    for frame in camera.capture_continuous( rawCapture, format="bgr", use_video_port=True ):
        image = frame.array        if   fcount == 1:
            r1 = pool.apply_async( get_faces, [ image ] )
            f2, i2 = r2.get()
            draw_frame( i2, f2 )        elif fcount == 2:
            r2 = pool.apply_async( get_faces, [ image ] )
            f3, i3 = r3.get()
            draw_frame( i3, f3 )        elif fcount == 3:
            r3 = pool.apply_async( get_faces, [ image ] )
            f4, i4 = r4.get()
            draw_frame( i4, f4 )        elif fcount == 4:
            r4 = pool.apply_async( get_faces, [ image ] )
            f1, i1 = r1.get()
            draw_frame( i1, f1 )

            fcount = 0

        fcount += 1

        rawCapture.truncate( 0 )

幀數上升到了13左右,而且識別框沒有延遲。


Demo4

Demo.5

搞定了低幀數問題,我又試了試多核加跳幀…幀數可到28幀左右。

### Imports ###################################################################from picamera.array import PiRGBArrayfrom picamera import PiCamerafrom functools import partialimport multiprocessing as mpimport cv2import os### Setup #####################################################################os.putenv( 'SDL_FBDEV', '/dev/fb0' )

resX = 320resY = 240# Setup the cameracamera = PiCamera()
camera.resolution = ( resX, resY )
camera.framerate = 90t_start = time.time()
fps = 0# Use this as our outputrawCapture = PiRGBArray( camera, size=( resX, resY ) )# The face cascade file to be usedface_cascade = cv2.CascadeClassifier( '/home/pi/opencv-2.4.9/data/lbpcascades/lbpcascade_frontalface.xml' )### Helper Functions ##########################################################def get_faces( img ):

    gray = cv2.cvtColor( img, cv2.COLOR_BGR2GRAY )    return face_cascade.detectMultiScale( gray ), imgdef draw_frame( img, faces ):

    global fps    global time_t    # Draw a rectangle around every face
    for ( x, y, w, h ) in faces:
        cv2.rectangle( img, ( x, y ),( x + w, y + h ), ( 200, 255, 0 ), 2 )    # Calculate and show the FPS
    fps = fps + 1
    sfps = fps / (time.time() - t_start)
    cv2.putText(img, "FPS : " + str( int( sfps ) ), ( 10, 10 ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 ) 

    cv2.imshow( "Frame", img )
    cv2.waitKey( 1 )### Main ######################################################################if __name__ == '__main__':

    pool = mp.Pool( processes=4 )

    i = 0
    rList = [None] * 17
    fList = [None] * 17
    iList = [None] * 17 

    camera.capture( rawCapture, format="bgr" )  

    for x in range ( 17 ):
        rList[x] = pool.apply_async( get_faces, [ rawCapture.array ] )
        fList[x], iList[x] = rList[x].get()
        fList[x] = []

    rawCapture.truncate( 0 )    

    for frame in camera.capture_continuous( rawCapture, format="bgr", use_video_port=True ):
        image = frame.array        if   i == 1:
            rList[1] = pool.apply_async( get_faces, [ image ] )
            draw_frame( iList[2], fList[1] )        elif i == 2:
            iList[2] = image
            draw_frame( iList[3], fList[1] )        elif i == 3:
            iList[3] = image
            draw_frame( iList[4], fList[1] )        elif i == 4:
            iList[4] = image
            fList[5], iList[5] = rList[5].get()
            draw_frame( iList[5], fList[5] )        elif i == 5:
            rList[5] = pool.apply_async( get_faces, [ image ] )
            draw_frame( iList[6], fList[5] )        elif i == 6:
            iList[6] = image
            draw_frame( iList[7], fList[5] )        elif i == 7:
            iList[7] = image
            draw_frame( iList[8], fList[5] )        elif i == 8:
            iList[8] = image
            fList[9], iList[9] = rList[9].get()
            draw_frame( iList[9], fList[9] )        elif i == 9:
            rList[9] = pool.apply_async( get_faces, [ image ] )
            draw_frame( iList[10], fList[9] )        elif i == 10:
            iList[10] = image
            draw_frame( iList[11], fList[9] )        elif i == 11:
            iList[11] = image
            draw_frame( iList[12], fList[9] )        elif i == 12:
            iList[12] = image
            fList[13], iList[13] = rList[13].get()
            draw_frame( iList[13], fList[13] )        elif i == 13:
            rList[13] = pool.apply_async( get_faces, [ image ] )
            draw_frame( iList[14], fList[13] )        elif i == 14:
            iList[14] = image
            draw_frame( iList[15], fList[13] )        elif i == 15:
            iList[15] = image
            draw_frame( iList[16], fList[13] )        elif i == 16:
            iList[16] = image
            fList[1], iList[1] = rList[1].get()
            draw_frame( iList[1], fList[1] )

            i = 0

        i += 1

        rawCapture.truncate( 0 )
Demo5

跳幀加多核,強行30幀哈哈,不過還是建議最終使用Demo4。


09/23/2016 Update:

我使用的云臺是這個,馬達型號是sg90。

sg90.jpg

ServoBlaster的下載地址是:鏈接


本文原文來源于:使用樹莓派進行簡易人臉識別

1.樹莓派吧遵循行業規范,任何轉載的稿件都會明確標注作者和來源,如有版權問題,請聯系QQ613789238刪除。; 2.樹莓派吧的原創文章,請轉載時務必注明文章作者和"來源:樹莓派吧",不尊重原創的行為樹莓派吧或將追究責任; 3.作者投稿可能會經樹莓派吧編輯修改或補充。

網友點評
17500乐彩网