Video del rilevatore di oggetti cloud su Raspberry Pi

prologo

Ora circola su Internet un video che mostra come il pilota automatico di Tesla vede la strada.

È da molto tempo che desidero trasmettere video arricchiti con un rilevatore e in tempo reale.

Video del rilevatore di oggetti cloud su Raspberry Pi

Il problema è che voglio trasmettere video dal Raspberry e le prestazioni del rilevatore di rete neurale su di esso lasciano molto a desiderare.

Chiavetta per computer neurale Intel

Ho considerato diverse soluzioni.

В ultimo articolo sperimentato con Intel Neural Computer Stick. L'hardware è potente, ma richiede il proprio formato di rete.

Anche se Intel fornisce convertitori per i principali framework, esistono numerose insidie.

Ad esempio, il formato della rete richiesta potrebbe essere incompatibile e, se è compatibile, alcuni livelli potrebbero non essere supportati sul dispositivo e, se sono supportati, potrebbero verificarsi errori durante il processo di conversione, di conseguenza otteniamo alcune cose strane in uscita.

In generale, se si desidera una sorta di rete neurale arbitraria, potrebbe non funzionare con NCS. Ho quindi deciso di provare a risolvere il problema utilizzando gli strumenti più diffusi e accessibili.

nuvola

L'ovvia alternativa a una soluzione hardware locale è passare al cloud.

Opzioni già pronte: i miei occhi si scatenano.

Tutti i leader:

... E decine di quelli meno conosciuti.

Scegliere tra questa varietà non è affatto facile.

E ho deciso di non scegliere, ma di avvolgere il buon vecchio schema di lavoro su OpenCV in Docker ed eseguirlo nel cloud.

Il vantaggio di questo approccio è la flessibilità e il controllo: puoi modificare la rete neurale, l'hosting, il server - in generale, qualsiasi capriccio.

Server

Iniziamo con un prototipo locale.

Tradizionalmente utilizzo Flask per API REST, OpenCV e rete MobileSSD.

Dopo aver installato le versioni attuali su Docker, ho scoperto che OpenCV 4.1.2 non funziona con Mobile SSD v1_coco_2018_01_28 e ho dovuto tornare alla comprovata 11/06_2017.

All'avvio del servizio carichiamo i nomi delle classi e della rete:

def init():
    tf_labels.initLabels(dnn_conf.DNN_LABELS_PATH)
    return cv.dnn.readNetFromTensorflow(dnn_conf.DNN_PATH, dnn_conf.DNN_TXT_PATH)

Su una finestra mobile locale (su un laptop non molto giovane) ci vogliono 0.3 secondi, su Raspberry - 3.5.

Iniziamo il calcolo:

def inference(img):
    net.setInput(cv.dnn.blobFromImage(img, 1.0/127.5, (300, 300), (127.5, 127.5, 127.5), swapRB=True, crop=False))
    return net.forward()

Docker - 0.2 secondi, Lampone - 1.7.

Trasformare lo scarico del tensore in json leggibile:

def build_detection(data, thr, rows, cols):
    ret = []
    for detection in data[0,0,:,:]:
        score = float(detection[2])
        if score > thr:
            cls = int(detection[1])
            a = {"class" : cls, "name" : tf_labels.getLabel(cls),  "score" : score}
            a["x"] = int(detection[3] * cols)
            a["y"] = int(detection[4] * rows)
            a["w"] = int(detection[5] * cols ) - a["x"]
            a["h"] = int(detection[6] * rows) - a["y"]
            ret.append(a)
    return ret

ulteriormente esportare questa operazione tramite Flask(l'input è un'immagine, l'output sono i risultati del rilevatore in json).

Un'opzione alternativa, in cui la maggior parte del lavoro viene spostata sul server: esso stesso circonda gli oggetti trovati e restituisce l'immagine finita.

Questa opzione è utile quando non vogliamo trascinare opencv sul server.

Docker

Raccogliamo l'immagine.

Il codice viene pettinato e pubblicato Github, la finestra mobile lo prenderà direttamente da lì.

Come piattaforma, prenderemo la stessa Debian Stretch di Raspberry: non ci allontaneremo dallo stack tecnologico collaudato.

È necessario installare flask, protobuf, request, opencv_python, scaricare Mobile SSD, codice server da Github e avviare il server.

FROM python:3.7-stretch

RUN pip3 install flask
RUN pip3 install protobuf
RUN pip3 install requests
RUN pip3 install opencv_python

ADD http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_11_06_2017.tar.gz /
RUN tar -xvf /ssd_mobilenet_v1_coco_11_06_2017.tar.gz

ADD https://github.com/tprlab/docker-detect/archive/master.zip /
RUN unzip /master.zip

EXPOSE 80

CMD ["python3", "/docker-detect-master/detect-app/app.py"]

Semplice client del rilevatore in base alle richieste.

Pubblicazione su Docker Hub

I registri Docker si stanno moltiplicando a una velocità non inferiore a quella dei rilevatori di cloud.

Per non disturbarci, procederemo in modo conservativo DockerHub.

  1. Registrati
  2. Login:
    login docker
  3. Troviamo un nome significativo:
    tag docker opencv-detect tprlab/opencv-detect-ssd
  4. Carica l'immagine sul server:
    docker push tprlab/opencv-detect-ssd

Lanciamo nel cloud

Anche la scelta su dove posizionare il contenitore è piuttosto ampia.

Tutti i big player (Google, Microsoft, Amazon) offrono una microistanza gratuita per il primo anno.
Dopo aver sperimentato Microsoft Azure e Google Cloud, ho optato per quest'ultimo perché è decollato più velocemente.

Non ho scritto istruzioni qui, poiché questa parte è molto specifica per il provider selezionato.

Ho provato diverse opzioni hardware,
Livelli bassi (condivisi e dedicati) - 0.4 - 0.5 secondi.
Auto più potenti - 0.25 - 0.3.
Bene, anche nel peggiore dei casi, le vincite sono tre volte, puoi provarci.

Video

Lanciamo un semplice streamer video OpenCV su Raspberry, rilevando tramite Google Cloud.
Per l'esperimento è stato utilizzato un file video filmato una volta in un incrocio casuale.


def handle_frame(frame):
    return detect.detect_draw_img(frame)
       
def generate():
    while True:
        rc, frame = vs.read()
        outFrame = handle_frame(frame)
        if outFrame is None:
            (rc, outFrame) = cv.imencode(".jpg", frame)
        yield(b'--framern' b'Content-Type: image/jpegrnrn' + bytearray(outFrame) + b'rn')

@app.route("/stream")
def video_feed():
    return Response(generate(), mimetype = "multipart/x-mixed-replace; boundary=frame")

Con il rilevatore non otteniamo più di tre fotogrammi al secondo, tutto procede molto lentamente.
Se porti una macchina potente in GCloud, puoi rilevare 4-5 fotogrammi al secondo, ma la differenza è quasi invisibile alla vista, è comunque lento.

Video del rilevatore di oggetti cloud su Raspberry Pi

I costi del cloud e del trasporto non hanno nulla a che fare con questo; il rilevatore funziona su hardware normale e funziona ad una tale velocità.

Stick per computer neurale

Non ho potuto resistere e ho eseguito il benchmark su NCS.

La velocità del rilevatore era leggermente inferiore a 0.1 secondi, in ogni caso 2-3 volte più veloce della nuvola su una macchina debole, cioè 8-9 fotogrammi al secondo.

Video del rilevatore di oggetti cloud su Raspberry Pi

La differenza nei risultati è spiegata dal fatto che NCS utilizzava la versione Mobile SSD 2018_01_28.

PS Inoltre, gli esperimenti hanno dimostrato che una macchina desktop abbastanza potente con un processore I7 mostra risultati leggermente migliori e si è scoperto che è possibile spremere 10 fotogrammi al secondo.

grappolo

L'esperimento è andato oltre e ho installato il rilevatore su cinque nodi in Google Kubernetes.
I pod stessi erano deboli e ciascuno di essi non riusciva a elaborare più di 2 fotogrammi al secondo.
Ma se esegui un cluster con N nodi e analizzi i frame in N thread, con un numero sufficiente di nodi (5) puoi ottenere i 10 frame al secondo desiderati.

def generate():
    while True:
        rc, frame = vs.read()
        if frame is not None:
            future = executor.submit(handle_frame, (frame.copy()))
            Q.append(future)

        keep_polling = len(Q) > 0
        while(keep_polling):            
            top = Q[0]
            if top.done():
                outFrame = top.result()
                Q.popleft()
                if outFrame:
                    yield(b'--framern' b'Content-Type: image/jpegrnrn' + bytearray(outFrame) + b'rn')
                keep_polling = len(Q) > 0
            else:
                keep_polling = len(Q) >= M

Ecco cosa è successo:

Video del rilevatore di oggetti cloud su Raspberry Pi

Un po' meno veloce che con NCS, ma più vigoroso che in un flusso.

Il guadagno, ovviamente, non è lineare: ci sono sovrapposizioni per la sincronizzazione e la copia profonda delle immagini opencv.

conclusione

Nel complesso, l'esperimento ci permette di concludere che se ci provi, puoi farla franca con una semplice nuvola.

Ma un desktop potente o un hardware locale ti consentono di ottenere risultati migliori e senza trucchi.

riferimenti

Fonte: habr.com

Aggiungi un commento