基於STM32F407的人臉追蹤

整體概述

本項目採用兩個舵機構成的二自由度的電動雲臺作爲執行機構,控制攝像頭在水平和垂直方向的運動。舵機帶動攝像頭進行二維平面的運動的同時,攝像頭進行實時人臉檢測,一旦檢測到人臉,則進行人臉跟蹤。

攝像頭採用星瞳openMV H7,主控採用的是正點原子探索者F407開發板。

本文通過openMV和STM32兩方面來講解。

openMV部分

OpenMV攝像頭是一款小巧,低功耗,低成本的電路板,它幫助你很輕鬆的完成機器視覺(machine vision)應用。其使用的是STM32F765VI ARM Cortex M7 處理器,所以其實我們編寫openMV的代碼從某種程度上來說其實還是往STM32裏寫代碼,唯一不同的是,openMV這部分代碼我們要用microPython來寫。

在openMV上我們要做的很簡單,如果攝像頭識別到人臉,用矩形框將人臉框起來,通過串口把矩形框的中心座標返回到開發板,開發讀取座標後控制舵機執行相應的運動。

所以首先,人臉追蹤的基礎是攝像頭能夠進行人臉檢測。而openMV已經集成封裝好了很多能夠調用的庫,其中正好就包含人臉檢測相關的庫和方法,只管調用就是了。(同時,openMV內置了n多個example,我們可以在example的基礎上對代碼進行修改。本例就是在face_detection的基礎上修改實現的。)

模板例程這裏就不贅述了,註釋都寫得很清楚,不瞭解的可以去看看官方的教程。這裏主要講一下人臉追蹤的部分。

其中,我們要知道find_features方法返回一個關於這些特徵的邊界框矩形元組(x,y,w,h)的列表,若未發現任何特徵,則返回一個空白列表。x,y是矩形框左上角的座標值,w,h分別爲矩形框的寬度和高度。那麼很容易得到中心座標(cx = x + w / 2, cy = y + h / 2)

接下來的任務就是將這兩個數據通過串口傳輸到開發板。我們在串口上寫了個簡單的協議,規定發送數據的格式爲:‘X’ + cx + ‘Y’ + cy + ‘OK’(後面講STM32部分的時候會提到),於是我們這裏向串口發送的數據爲(‘X%dY%dOK\r\n’ % (cx,cy)),到了STM32部分我們會對接收到的數據進行分析然後把有效數據cx和cy從中提取出來。

具體代碼如下:

import sensor, time, image
from pyb import UART

# Reset sensor
sensor.reset()

# Sensor settings
sensor.set_contrast(1)
sensor.set_gainceiling(16)
# HQVGA and GRAYSCALE are the best for face tracking.
sensor.set_framesize(sensor.HQVGA)
sensor.set_pixformat(sensor.GRAYSCALE)

# Load Haar Cascade
face_cascade = image.HaarCascade("frontalface", stages=25)

# OpenMV上P4,P5對應的串口3
uart = UART(3, 115200, timeout_char=1000)

# FPS clock
clock = time.clock()

while (True):
    clock.tick()

    # Capture snapshot
    img = sensor.snapshot()

    # Find objects.
    objects = img.find_features(face_cascade, threshold=0.75, scale_factor=1.25)
    #find_features函數返回一個關於要找的這些feature的邊界框矩形元組(x,y,w,h)的列表。
    #若未發現任何要找的feature,則返回一個空白列表,即objects是一個元組列表,包含(x,y,w,h)這個元組作爲唯一一個元素

    # Draw objects
    for r in objects:
        if(r):
            img.draw_rectangle(r,color = (255,0,0))
            cx = r[0]+r[2]/2                                            #計算得到人臉矩形框的中心x座標
            cy = r[1]+r[3]/2                                            #計算得到人臉矩形框的中心y座標
            print('x:%d, y:%d, w:%d, h:%d' % (r[0],r[1],r[2],r[3]) )    #打印矩形框的x,y,w,h
            print('cx = %d, cy = %d' % (cx,cy))
            uart.write('X%dY%dOK\r\n' % (cx,cy))
            break
    else:
            #print('cannot find any face!')
            uart.write('X-1Y-1OK\r\n')

STM32部分

舵機部分

掃描函數:用兩層循環分別控制x方向和y方向的舵機運動,即可實現攝像頭的二維運動。一旦識別到人臉,退出掃描函數,進入跟隨函數,讓攝像頭跟隨人臉運動,使人臉一直處於畫面正中心。

跟隨函數:如果識別到人臉,獲取人臉在屏幕中的位置,當其距離屏幕正中心超過一定的閾值時,調節相應的舵機,使之向屏幕中心靠攏。如果未識別到人臉,則繼續從當前位置開始執行掃描函數。

串口部分

上文提到,openMV捕獲到人臉後,不斷通過串口向STM32發送數據,所以我們串口函數的目的就是把接收到的數據中的cx和cy提取出來(其餘數據丟棄不用),然後把cx和cy和舵機的運動函數聯繫起來,進而控制攝像頭雲臺執行相應運動實現人臉追蹤。

串口的配置過於簡單,這裏就略過不講了,主要還是講解一下如何解析從openMV接收到的格式爲’X’ + cx + ‘Y’ + cy + 'OK’的數據並從中提取有效信息cx和xy。

我們提取數據的代碼將在正點原子的串口中斷函數的基礎上進行更改,可以先在我的上一篇博文了解到正點原子串口中斷函數的思路:基於STM32F407的串口通信

正點原子串口中斷函數改編版
通過正點原子的例子,我們知道如果我們的數據如果是以‘OK’結尾,會被串口認爲有效數據接收,且‘OK’不會作爲數據內容存入buffer數組中。

於是,‘X’ + cx + ‘Y’ + cy + 'OK’格式的數據(e.g. X17Y41OK)被STM32的串口有效接收後存放到buffer數組裏的數據是X17Y41,那麼我們只要通過遍歷把XY之間的cx提取出來,把Y之後的cy提取出來,分別存放在兩個數組裏,就實現了cx和cy的提取。

(如果STM32部分的代碼(如舵機控制)也乾脆在openMV上寫,那麼提取數據用Python的正則表達式之類的應該很輕鬆能做到。不過當然,如果舵機控制部分在openMV上寫,那也不會有串口通信和這些帶格式的數據了,畢竟數據不需要通過串口從兩個設備之間傳輸了。當然這是後話了,以後嘗試只用openMV實現)

顯然,我們是在數據接收完成後(即USART_RX_STA&0x8000==1)進行的進一步操作,於是我們在if((USART_RX_STA&0x8000)==0)後緊跟else if語句,條件爲(USART_RX_STA&0x8000),表明接收完成,開始分析數據。

思路前面已經大致提到了,我們先建立兩個buffer數組分別用於存放cx和cy的數據。然後按字節遍歷整個接收數據,當識別到當前字節的數據爲‘X’時,將之後出現的數據存放到cx數組中,當識別到當前字節的數據爲‘Y’時,將之後出現的數據存放到cy數組中。最後,由於串口接收到的是char型數據,我們再通過atoi()函數將char型轉化成int型的數據即可。

以上就是人臉追蹤的大致思路了,寫得有點複雜,其實代碼還可以較大程度的優化,以後有時間再改了。

具體代碼見:完整代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章