Video des Cloud Object Detectors auf Raspberry Pi

Prolog

Im Internet kursiert nun ein Video, das zeigt, wie Teslas Autopilot die Straße sieht.

Es juckt mich schon seit langem, mit einem Detektor angereicherte Videos in Echtzeit zu übertragen.

Video des Cloud Object Detectors auf Raspberry Pi

Das Problem ist, dass ich Videos von Raspberry übertragen möchte und die Leistung des neuronalen Netzwerkdetektors darauf zu wünschen übrig lässt.

Intel Neural Computer Stick

Ich habe über verschiedene Lösungen nachgedacht.

В letzter Artikel experimentierte mit Intel Neural Computer Stick. Die Hardware ist leistungsstark, erfordert jedoch ein eigenes Netzwerkformat.

Obwohl Intel Konverter für die wichtigsten Frameworks bereitstellt, gibt es eine Reihe von Fallstricken.

Beispielsweise kann das Format des erforderlichen Netzwerks inkompatibel sein, und wenn es kompatibel ist, werden einige Schichten möglicherweise auf dem Gerät nicht unterstützt, und wenn sie unterstützt werden, können beim Konvertierungsprozess Fehler auftreten, was zur Folge hat Am Ausgang bekommen wir einige seltsame Dinge.

Wenn Sie ein beliebiges neuronales Netzwerk wünschen, funktioniert es im Allgemeinen möglicherweise nicht mit NCS. Deshalb habe ich beschlossen, das Problem mit den am weitesten verbreiteten und zugänglichsten Tools zu lösen.

Wolke

Die offensichtliche Alternative zu einer lokalen Hardwarelösung ist der Wechsel in die Cloud.

Fertige Optionen – meine Augen laufen wild.

Alle Führungskräfte:

... Und Dutzende weniger bekannte.

Die Wahl zwischen dieser Sorte ist gar nicht so einfach.

Und ich habe beschlossen, mich nicht zu entscheiden, sondern das gute alte Arbeitsschema auf OpenCV in Docker zu packen und in der Cloud auszuführen.

Der Vorteil dieses Ansatzes ist Flexibilität und Kontrolle – Sie können das neuronale Netzwerk, das Hosting, den Server – im Allgemeinen nach Lust und Laune – ändern.

Server

Beginnen wir mit einem lokalen Prototyp.

Traditionell verwende ich Flask für REST-API-, OpenCV- und MobileSSD-Netzwerke.

Nachdem ich die aktuellen Versionen auf Docker installiert hatte, stellte ich fest, dass OpenCV 4.1.2 nicht mit Mobile SSD v1_coco_2018_01_28 funktioniert, und ich musste auf die bewährte Version 11/06_2017 zurücksetzen.

Zu Beginn des Dienstes laden wir die Klassennamen und das Netzwerk:

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

Auf einem lokalen Docker (auf einem nicht sehr jungen Laptop) dauert es 0.3 Sekunden, auf Raspberry 3.5.

Beginnen wir mit der Berechnung:

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 Sek., Raspberry – 1.7.

Tensor-Auspuff in lesbaren JSON umwandeln:

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

weiter Exportieren Sie diesen Vorgang über Flask(Eingabe ist ein Bild, Ausgabe sind die Ergebnisse des Detektors in JSON).

Eine alternative Möglichkeit, bei der mehr Arbeit auf den Server verlagert wird: Er selbst umkreist die gefundenen Objekte und gibt das fertige Bild zurück.

Diese Option ist gut, wenn wir opencv nicht auf den Server ziehen möchten.

Docker

Wir sammeln das Bild.

Der Code wird gekämmt und veröffentlicht GitHub, Docker übernimmt es direkt von dort.

Als Plattform verwenden wir das gleiche Debian Stretch wie bei Raspberry – wir werden nicht vom bewährten Tech-Stack abweichen.

Sie müssen flask, protobuf, request, opencv_python installieren, Mobile SSD und Servercode von Github herunterladen und den Server starten.

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"]

Einfach Detektor-Client basierend auf Anfragen.

Veröffentlichung im Docker Hub

Docker-Register vermehren sich mit einer Geschwindigkeit, die nicht geringer ist als die von Cloud-Detektoren.

Um uns nicht die Mühe zu machen, werden wir konservativ durchgehen DockerHub.

  1. Registrieren Sie sich
  2. Anmeldung:
    Docker-Login
  3. Lassen Sie uns einen aussagekräftigen Namen finden:
    Docker-Tag opencv-detect tprlab/opencv-detect-ssd
  4. Laden Sie das Bild auf den Server hoch:
    Docker Push tprlab/opencv-detect-ssd

Wir starten in der Cloud

Auch die Auswahl, wo der Container betrieben werden soll, ist recht groß.

Alle großen Player (Google, Microsoft, Amazon) bieten im ersten Jahr eine Mikroinstanz kostenlos an.
Nachdem ich mit Microsoft Azure und Google Cloud experimentiert hatte, entschied ich mich für Letzteres, weil es sich schneller durchsetzte.

Ich habe hier keine Anleitung geschrieben, da dieser Teil sehr spezifisch für den ausgewählten Anbieter ist.

Ich habe verschiedene Hardwareoptionen ausprobiert,
Niedrige Ebenen (gemeinsam genutzt und dediziert) – 0.4–0.5 Sekunden.
Stärkere Autos - 0.25 - 0.3.
Nun, selbst im schlimmsten Fall beträgt der Gewinn das Dreifache, Sie können es versuchen.

Video

Wir starten einen einfachen OpenCV-Videostreamer auf Raspberry, der über Google Cloud erkennt.
Für das Experiment wurde eine Videodatei verwendet, die einmal an einer zufälligen Kreuzung gefilmt wurde.


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")

Mit dem Detektor bekommen wir nicht mehr als drei Bilder pro Sekunde, alles geht sehr langsam.
Wenn Sie eine leistungsstarke Maschine in GCloud integrieren, können Sie 4-5 Bilder pro Sekunde erkennen, aber der Unterschied ist für das Auge fast unsichtbar, er ist immer noch langsam.

Video des Cloud Object Detectors auf Raspberry Pi

Die Cloud- und Transportkosten haben damit nichts zu tun; der Detektor läuft auf gewöhnlicher Hardware und arbeitet mit einer solchen Geschwindigkeit.

Neuronaler Computerstick

Ich konnte nicht widerstehen und habe den Benchmark auf NCS durchgeführt.

Die Geschwindigkeit des Detektors war etwas langsamer als 0.1 Sekunden, auf jeden Fall 2-3 mal schneller als die Cloud auf einer schwachen Maschine, also 8-9 Bilder pro Sekunde.

Video des Cloud Object Detectors auf Raspberry Pi

Der Unterschied in den Ergebnissen erklärt sich aus der Tatsache, dass NCS die Mobile SSD-Version 2018_01_28 ausführte.

PS Darüber hinaus haben Experimente gezeigt, dass ein ziemlich leistungsstarker Desktop-Rechner mit einem I7-Prozessor etwas bessere Ergebnisse zeigt und es sich herausstellte, dass es möglich war, 10 Bilder pro Sekunde darauf zu komprimieren.

Cluster

Das Experiment ging weiter und ich installierte den Detektor auf fünf Knoten in Google Kubernetes.
Die Pods selbst waren schwach und jeder von ihnen konnte nicht mehr als 2 Bilder pro Sekunde verarbeiten.
Wenn Sie jedoch einen Cluster mit N Knoten ausführen und Frames in N Threads analysieren, können Sie mit einer ausreichenden Anzahl von Knoten (5) die gewünschten 10 Frames pro Sekunde erreichen.

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

Hier ist, was passiert:

Video des Cloud Object Detectors auf Raspberry Pi

Etwas weniger schnell als bei NCS, aber kräftiger als in einem Stream.

Der Gewinn ist natürlich nicht linear – es gibt Overlays für die Synchronisierung und das tiefe Kopieren von OpenCV-Bildern.

Abschluss

Insgesamt lässt das Experiment den Schluss zu, dass man mit einer einfachen Cloud davonkommt, wenn man es versucht.

Doch mit einem leistungsstarken Desktop oder lokaler Hardware lassen sich bessere Ergebnisse erzielen, und das ganz ohne Tricks.

Referenzen

Source: habr.com

Kommentar hinzufügen