DesignSpark Electrical Logolinkedin
菜单 搜寻
提问问题

簡易的樹莓派識別器 (繁體)

使用Pi 3 Model B +,Intel Movidius NCS,Pi-Top CEED Pro和網絡攝像頭構建基於樹莓派的20級識別小工具。

深度學習並不是一門新學問,但要構建深度神經網絡(DNN)並不是一件易如反掌的事。感謝英特爾發明了Movidius Intelligence,我們可以利用Movidus花更少時間和精力來構築DNN。從我們的電腦上訓練Movidius NCS再將Movidius放上上單板機設備上,包括Raspberry Pi,Arduino等。

今次的分享用到了以下產品:運行Raspbian Stretch的Raspberry Pi 3 Model B plus、Intel Movidius NCS和USB網絡攝像頭連接Raspberry Pi。最後,在Pi-Top CEED Pro上顯示我們的識別結果。

 

 

 

硬件

名稱

RS庫存號碼

製造商零件號碼

製造商

Raspberry Pi Model 3B+

(137-3331)

Raspberry PI 3B+

Raspberry Pi

Movidius Neural Network Compute Stick

(139-3655)

NCSM2450.DK

Intel

Pi-Top CEED Pro, Green

(122-6547)

PT-CEED01-GR-PRO

Pi-Top

在此分享中,我建議使用Ubuntu16.04安裝在任何VM上進行。我重複它必須是16.04,我測試了18.X版本,相信我,18.X版本只會不斷出錯。


那麼,讓我們開始吧。
首先由Raspberry Pi 開始。
把你的pi3+放入 pi top。

以下是Raspberry Pi 的代碼:

pi@raspberryPi: sudo raspi-config

Option 5- Interfacing->P1- Camera->Enable->Yes

Option 5- Interfacing->P4- SPI->Enable->Yes

Option 7-Advanced Options->A7-GL driver->G2 GL (Fake KMS)-> Activate

要在Raspberry Pi上設置movidius,可以參考由Andrew Back寫的AI Powered Identification Pidentifier。讀者可以按照他的步驟用Movidius Stick設置樹莓派。或者我們可以按照Movidius啟動指南進行操作。 NCSDK v1是唯一選擇。

 

軟件的使用

由https://movidius.github.io/ncsdk/提供。

我們可以通過不同的方式,圖片,圖形甚至語音來調適movidius。通過反覆的調適,我們可以獲得一個合適的caffe模型和一個更好的圖卷積。這樣可以進一步用於Raspberry Pi這樣的EDGE設備。

一開始,我用root機的方法在window筆電上安裝ubuntu桌面並CLONE了最新版本的NCSDK。最新版本的NCSDK並不支援YOLONCS,我花了更多時間來重設整個ubuntu因為最新版本的NCSDK不能被解除安裝。為了解決這個問題,我建議使用安裝了VM的筆電來繼續之後的工作。

 

上圖顯示了簡單的深度學習示例。

圖卷積在圖中是類似於黑色橢圓形,它的作用是選擇權重交出最後的結果。一般的電腦很難區分貓與狗。然而,筆電可以利用NCS來加速計算單個圖卷積。

在筆電上安裝Movidius的方法類似於在Raspberry Pi上安裝。

LAPTOP@WHOYOUARE:  sudo apt-get update
LAPTOP@WHOYOUARE:  sudo apt-get upgrade
LAPTOP@WHOYOUARE:  sudo apt-get install python3-pip python3-numpy git cmake

 

為什麼我們要做這個工序?

這是因為有許多make的文件還沒有下載,運行示例正正在構建每個示例。每個示例都帶有自己的Make文件,做這個工序我們的NCSDK會自行安裝該特定示例及其任何所需的文件。

 

硬件檢驗

sudo apt-get install fswebcam

fswebcam image.jpg

如果以下代碼在你的屏幕上顯示。恭喜!你的硬件運作正常。

--- Processing captured image...

Writing JPEG image to 'image.jpg'

 

訓練提示

樹莓派上的程序

樹莓派沒有像英特爾的CPU那麼快,很難運行多層的神經網絡。同樣,YoloV2NCS和YoloNCS由於復雜的指令而不適合樹莓派令PI 滯後。

為了減少這個問題,我們可以修改Yolo到Tiny Yolo,感謝PINTO; 雖然準確性只是Yolo的10分1,但我們仍然可以使用Tiny Yolo來分析20類物體。

wget https://github.com/PINTO0309/OpenCVonARMv7/blob/master/libopencv3_3.4.1-20180304.1_armhf.deb

sudo apt install -y ./libopencv3_3.4.1-20180304.1_armhf.deb

sudo ldconfig

同樣,我們必須在pi上安裝YoloV2NCS

sudo apt install -y ./libopencv3_3.4.1-20180304.1_armhf.deb

sudo ldconfig

 

首先,我們從YoloV2和python3導入一些必需的次資料庫。

import sys
graph_folder="./" # Graph

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

from mvnc import mvncapi as mvnc
import numpy as np
import cv2
from os import system
import io, time
from os.path import isfile, join
from queue import Queue
from threading import Thread, Event, Lock
import re
from time import sleep
from Visualize import *
from libpydetector import YoloDetector
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

#設置全局日誌級別為verbose

mvnc.SetGlobalOption(mvnc.GlobalOption.LOG_LEVEL, 2)
# check the Movidius NCS is linked or not.
# show the numbers of movidius NCS implunged. 
devices = mvnc.EnumerateDevices()
if len(devices) == 0:
    print("No devices found")
    quit()
print(len(devices))
#Create arrays for Name and graphs’ weights
devHandle   = []
graphHandle = []

with open(join(graph_folder, "graph"), mode="rb") as f:
    graph = f.read()

for devnum in range(len(devices)):
    devHandle.append(mvnc.Device(devices[devnum]))
    devHandle[devnum].OpenDevice()

    graphHandle.append(devHandle[devnum].AllocateGraph(graph))
    graphHandle[devnum].SetGraphOption(mvnc.GraphOption.ITERATIONS, 1)
    iterations = graphHandle[devnum].GetGraphOption(mvnc.GraphOption.ITERATIONS)

    dim = (320,320)
    blockwd = 9
    targetBlockwd = 9
    wh = blockwd*blockwd
    classes = 20 
    threshold = 0.3
    nms = 0.4

print("\nLoaded Graphs!!!")

#此時已加載圖表。我們要構建Web Camera信息

#以及顯示信息。

 

widowWidth = 320
windowHeight = 240
cam.set(cv2.CAP_PROP_FRAME_WIDTH, widowWidth)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, windowHeight)

lock = Lock()
frameBuffer = []
results = Queue()
lastRR = None
detector = YoloDetector(1)

#以下是opengl和Webcam的一些定義glClearColor前三個參數是背景的RGB顏色。

# 我們只為最後一個值加上0.7。

 

def init():
    glClearColor(0.7, 0.7, 0.7, 0.7)

def idle():
    glutPostRedisplay()

def resizeview(w, h):
    glViewport(0, 0, w, h)
    glLoadIdentity()
    glOrtho(-w / 1920, w / 1920, -h / 1080, h / 1080, -1.0, 1.0)

def keyboard(key, x, y):
    key = key.decode('utf-8')
    if key == 'q':
        lock.acquire()
        while len(frameBuffer) > 0:
            frameBuffer.pop()
        lock.release()
        for devnum in range(len(devices)):
            graphHandle[devnum].DeallocateGraph()
            devHandle[devnum].CloseDevice()
        print("\n\nFinished\n\n")
        sys.exit()

#我們將在打開網絡攝像頭時清除圖像。 因此它比YoloV2NCS和YoloNCS更少滯後。

 

def camThread():   
    global lastresults

    s, img = cam.read()

    if not s:
        print("Could not get frame")
        return 0

    lock.acquire()
    if len(frameBuffer)>10:
        for i in range(10):
            del frameBuffer[0]
    frameBuffer.append(img)
    lock.release()
    res = None

    if not results.empty():
        res = results.get(False)
        if res == None:
            if lastRR == None:
                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                h, w = img.shape[:2]
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, img)
            else:
                imdraw = Visualize(img, lastresults)
                imdraw = cv2.cvtColor(imdraw, cv2.COLOR_BGR2RGB)
                h, w = imdraw.shape[:2]
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, imdraw)
        else:
            img = Visualize(img, res)
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            h, w = img.shape[:2]
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, img)
            lastRR = res
    else:
        if lastRR == None:
            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
            h, w = img.shape[:2]
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, img)
        else:
            imdraw = Visualize(img, lastresults)
            imdraw = cv2.cvtColor(imdraw, cv2.COLOR_BGR2RGB)
            h, w = imdraw.shape[:2]
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, imdraw)

#這裡用於定位NCS區域的Rectangle openGL level。為了減少顏色重疊,我們為每個設置了20種顏色graph類

 

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glColor3f(1.0, 1.0, 1.0)
    glEnable(GL_TEXTURE_2D)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glBegin(GL_QUADS) 
    glTexCoord2d(0.0, 1.0)
    glVertex3d(-1.0, -1.0,  0.0)
    glTexCoord2d(1.0, 1.0)
    glVertex3d( 1.0, -1.0,  0.0)
    glTexCoord2d(1.0, 0.0)
    glVertex3d( 1.0,  1.0,  0.0)
    glTexCoord2d(0.0, 0.0)
    glVertex3d(-1.0,  1.0,  0.0)
    glEnd()
    glFlush()
    glutSwapBuffers()

def infer(results, lock, frameBuffer, handle):
    failure = 0
    sleep(1)
    while failure < 100:

        lock.acquire()
        if len(frameBuffer) == 0:
            lock.release()
            failure += 1
            continue

        img = frameBuffer[-1].copy()
        del frameBuffer[-1]
        failure = 0
        lock.release()

        start = time.time()
        imgw = img.shape[1]
        imgh = img.shape[0]

        now = time.time()
        im,offx,offy = PI (img, dim)        
        handle.LoadTensor(im.astype(np.float16), 'user object')
        out, userobj = handle.GetResult()
        out = RS(out, dim)
        internalresults = detector.Detect(out.astype(np.float32), int(out.shape[0]/wh), blockwd, blockwd, classes, imgw, imgh, threshold, nms, targetBlockwd)
        pyresults = [BBox(x) for x in internalresults]
        results.put(pyresults)
        print("elapsedtime = ", time.time() - now)
# we took picture to classify the object. Although Delay exists, it is still acceptable. 
def PI (img, dim):
    imgw = img.shape[1]
    imgh = img.shape[0]
    imgb = np.empty((dim[0], dim[1], 3))
    imgb.fill(0.5)

    newh = dim[1]
    neww = dim[0]
    offx = int((dim[0] - neww)/2)
    offy = int((dim[1] - newh)/2)

    imgb[offy:offy+newh,offx:offx+neww,:] = cv2.resize(img.copy()/255.0,(newh,neww))
    im = imgb[:,:,(2,1,0)]

    return im,offx,offy

def RS(out, dim):
    shape = out.shape
    out = np.transpose(out.reshape(wh, int(shape[0]/wh)))
    out = out.reshape(shape)
    return out

class BBox(object):
    def __init__(self, bbox):
        self.left = bbox.left
        self.top = bbox.top
        self.right = bbox.right
        self.bottom = bbox.bottom
        self.confidence = bbox.confidence
        self.objType = bbox.objType
        self.name = bbox.name

glutInitWindowPosition(0, 0)
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE )
glutCreateWindow("DEMO")
glutFullScreen()
glutDisplayFunc(camThread)
glutReshapeFunc(resizeview)
glutKeyboardFunc(keyboard)
init()
glutIdleFunc(idle) 

print("press 'q' to quit!\n")

threads = []

for devnum in range(len(devices)):
  t = Thread(target=infer, args=(results, lock, frameBuffer, graphHandle[devnum]))
  t.start()
  threads.append(t)

glutMainLoop()

看一下結果~

 

cd workspace/YoloV2NCS/

python3 ./detectionExample/rasp-identifier.py

Caffe 問題!

如果有Caffe錯誤,您可以直接輸入以下代碼來更改.py運行的caffe 路徑

$export PYTHONPATH=$env:"/opt/movidius/caffe/python":$PYTHONPATH

 

感謝YoloV2NCS,為我們提供一套簡單的指南,感謝GitHub大神們的解答。 謝Andrew Back演示了一個簡單的方法來緩解Caffe問題。

 

可能性

未來的深度學習和數據分析是市場的第一線。在這分享中,只有20類權重和模型可供NCS應對。但是,有多大的可能性在樹莓派提高識別更多種類而速度不會滯後?

是否可以使用樹莓派實時處理TensorFlow?有人說可能性高但要使用更多的NCS。

ncsdk v1即將刪除並將被ncsdk v2取代,以上代碼是否可以在新環境使用?

我們不可能控制自己的未來,但我們可以明智地利用深入學習,讓它融入社會,共創美好未來。

 

Brian0925 还没写个人简介...

10 Oct 2018, 7:00