Βίντεο του Cloud Object Detector στο Raspberry Pi

Πρόλογος

Ένα βίντεο κυκλοφορεί τώρα στο Διαδίκτυο που δείχνει πώς ο αυτόματος πιλότος της Tesla βλέπει τον δρόμο.

Έχω φαγούρα για πολύ καιρό για να μεταδώσω βίντεο εμπλουτισμένο με ανιχνευτή και σε πραγματικό χρόνο.

Βίντεο του Cloud Object Detector στο Raspberry Pi

Το πρόβλημα είναι ότι θέλω να μεταδώσω βίντεο από το Raspberry και η απόδοση του ανιχνευτή νευρωνικών δικτύων σε αυτό αφήνει πολλά να είναι επιθυμητή.

Intel Neural Computer Stick

Σκέφτηκα διαφορετικές λύσεις.

В τελευταίο άρθρο πειραματίστηκε με το Intel Neural Computer Stick. Το υλικό είναι ισχυρό, αλλά απαιτεί τη δική του μορφή δικτύου.

Παρόλο που η Intel παρέχει μετατροπείς για μεγάλα πλαίσια, υπάρχουν πολλές παγίδες.

Για παράδειγμα, η μορφή του απαιτούμενου δικτύου μπορεί να είναι ασύμβατη και εάν είναι συμβατό, τότε ορισμένα επίπεδα ενδέχεται να μην υποστηρίζονται στη συσκευή και εάν υποστηρίζονται, ενδέχεται να προκύψουν σφάλματα κατά τη διαδικασία μετατροπής, ως αποτέλεσμα των οποίων έχουμε κάποια περίεργα πράγματα στην έξοδο.

Γενικά, αν θέλετε κάποιο είδος αυθαίρετου νευρωνικού δικτύου, τότε μπορεί να μην λειτουργεί με το NCS. Ως εκ τούτου, αποφάσισα να προσπαθήσω να λύσω το πρόβλημα χρησιμοποιώντας τα πιο διαδεδομένα και προσβάσιμα εργαλεία.

Cloud

Η προφανής εναλλακτική λύση σε μια τοπική λύση υλικού είναι να μεταβείτε στο cloud.

Έτοιμες επιλογές - τα μάτια μου τρελαίνονται.

Όλοι οι ηγέτες:

... Και δεκάδες λιγότερο γνωστοί.

Η επιλογή μεταξύ αυτής της ποικιλίας δεν είναι καθόλου εύκολη.

Και αποφάσισα να μην επιλέξω, αλλά να τυλίξω το παλιό καλό σχήμα εργασίας στο OpenCV στο Docker και να το τρέξω στο cloud.

Το πλεονέκτημα αυτής της προσέγγισης είναι η ευελιξία και ο έλεγχος - μπορείτε να αλλάξετε το νευρωνικό δίκτυο, τη φιλοξενία, τον διακομιστή - γενικά, οποιαδήποτε ιδιοτροπία.

Διακομιστή

Ας ξεκινήσουμε με ένα τοπικό πρωτότυπο.

Παραδοσιακά χρησιμοποιώ το Flask για REST API, OpenCV και δίκτυο MobileSSD.

Έχοντας εγκαταστήσει τις τρέχουσες εκδόσεις στο Docker, ανακάλυψα ότι το OpenCV 4.1.2 δεν λειτουργεί με Mobile SSD v1_coco_2018_01_28 και έπρεπε να επιστρέψω στην αποδεδειγμένη 11/06_2017.

Στην αρχή της υπηρεσίας, φορτώνουμε τα ονόματα κλάσεων και το δίκτυο:

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

Σε ένα τοπικό docker (σε έναν όχι πολύ νέο φορητό υπολογιστή) χρειάζονται 0.3 δευτερόλεπτα, στο Raspberry - 3.5.

Ας ξεκινήσουμε τον υπολογισμό:

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

Μετατροπή της εξάτμισης του τανυστήρα σε αναγνώσιμο json:

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

Περαιτέρω εξάγετε αυτήν τη λειτουργία μέσω του Flask(η είσοδος είναι μια εικόνα, η έξοδος είναι τα αποτελέσματα του ανιχνευτή σε json).

Μια εναλλακτική επιλογή, στην οποία περισσότερη εργασία μεταφέρεται στον διακομιστή: ο ίδιος κυκλώνει τα αντικείμενα που βρέθηκαν και επιστρέφει την ολοκληρωμένη εικόνα.

Αυτή η επιλογή είναι καλή όταν δεν θέλουμε να σύρουμε το opencv στον διακομιστή.

Λιμενεργάτης

Συλλέγουμε την εικόνα.

Ο κωδικός χτενίζεται και δημοσιεύεται στο Github, ο docker θα το πάρει απευθείας από εκεί.

Ως πλατφόρμα, θα χρησιμοποιήσουμε το ίδιο Debian Stretch όπως στο Raspberry - δεν θα αποκλίνουμε από την αποδεδειγμένη τεχνολογία στοίβας.

Πρέπει να εγκαταστήσετε το flask, το protobuf, τις αιτήσεις, το opencv_python, να κατεβάσετε το Mobile SSD, τον κωδικό διακομιστή από το Github και να ξεκινήσετε τον διακομιστή.

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

Απλή πελάτη ανιχνευτή βάσει αιτημάτων.

Δημοσίευση στο Docker Hub

Τα μητρώα Docker πολλαπλασιάζονται με ταχύτητα όχι μικρότερη από τους ανιχνευτές νέφους.

Για να μην ενοχλούμε, θα περάσουμε συντηρητικά DockerHub.

  1. Κανω ΕΓΓΡΑΦΗ
  2. Σύνδεση:
    σύνδεση στο docker
  3. Ας βρούμε ένα όνομα με νόημα:
    ετικέτα docker opencv-detect tprlab/opencv-detect-ssd
  4. Ανεβάστε την εικόνα στον διακομιστή:
    docker push tprlab/opencv-detect-ssd

Εκκινούμε στο σύννεφο

Η επιλογή για το πού θα τρέξει το δοχείο είναι επίσης αρκετά μεγάλη.

Όλοι οι μεγάλοι παίκτες (Google, Microsoft, Amazon) προσφέρουν μια micro-instance δωρεάν για τον πρώτο χρόνο.
Μετά από πειραματισμούς με το Microsoft Azure και το Google Cloud, συμβιβάστηκα με το τελευταίο γιατί απογειώθηκε πιο γρήγορα.

Δεν έγραψα οδηγίες εδώ, καθώς αυτό το μέρος είναι πολύ συγκεκριμένο για τον επιλεγμένο πάροχο.

Δοκίμασα διαφορετικές επιλογές υλικού,
Χαμηλά επίπεδα (κοινόχρηστα και αποκλειστικά) - 0.4 - 0.5 δευτερόλεπτα.
Πιο ισχυρά αυτοκίνητα - 0.25 - 0.3.
Λοιπόν, ακόμη και στο χειρότερο σενάριο, τα κέρδη είναι τριπλάσια, μπορείτε να δοκιμάσετε.

βίντεο

Ξεκινάμε μια απλή ροή βίντεο OpenCV στο Raspberry, που εντοπίζεται μέσω του Google Cloud.
Για το πείραμα, χρησιμοποιήθηκε ένα αρχείο βίντεο που κάποτε γυρίστηκε σε μια τυχαία διασταύρωση.


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

Με τον ανιχνευτή δεν έχουμε περισσότερα από τρία καρέ ανά δευτερόλεπτο, όλα πάνε πολύ αργά.
Εάν βάλετε ένα ισχυρό μηχάνημα στο GCloud, μπορείτε να ανιχνεύσετε 4-5 καρέ ανά δευτερόλεπτο, αλλά η διαφορά είναι σχεδόν αόρατη στο μάτι, εξακολουθεί να είναι αργή.

Βίντεο του Cloud Object Detector στο Raspberry Pi

Το cloud και το κόστος μεταφοράς δεν έχουν καμία σχέση με αυτό· ο ανιχνευτής λειτουργεί με συνηθισμένο υλικό και λειτουργεί με τέτοια ταχύτητα.

Neural Computer Stick

Δεν μπόρεσα να αντισταθώ και έτρεξα το σημείο αναφοράς στο NCS.

Η ταχύτητα του ανιχνευτή ήταν ελαφρώς μικρότερη από 0.1 δευτερόλεπτα, σε κάθε περίπτωση 2-3 φορές μεγαλύτερη από το σύννεφο σε ένα αδύναμο μηχάνημα, δηλαδή 8-9 καρέ ανά δευτερόλεπτο.

Βίντεο του Cloud Object Detector στο Raspberry Pi

Η διαφορά στα αποτελέσματα εξηγείται από το γεγονός ότι το NCS εκτελούσε Mobile SSD έκδοση 2018_01_28.

ΥΣΤΕΡΟΓΡΑΦΟ. Επιπλέον, τα πειράματα έδειξαν ότι μια αρκετά ισχυρή επιτραπέζια μηχανή με επεξεργαστή I7 παρουσιάζει ελαφρώς καλύτερα αποτελέσματα και αποδείχθηκε ότι ήταν δυνατή η συμπίεση 10 καρέ ανά δευτερόλεπτο.

Σύμπλεγμα

Το πείραμα προχώρησε περισσότερο και εγκατέστησα τον ανιχνευτή σε πέντε κόμβους στο Google Kubernetes.
Τα ίδια τα pods ήταν αδύναμα και καθένα από αυτά δεν μπορούσε να επεξεργαστεί περισσότερα από 2 καρέ ανά δευτερόλεπτο.
Αλλά εάν εκτελέσετε ένα σύμπλεγμα με N κόμβους και αναλύετε πλαίσια σε Ν νήματα, τότε με επαρκή αριθμό κόμβων (5) μπορείτε να επιτύχετε τα επιθυμητά 10 καρέ ανά δευτερόλεπτο.

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

Εδώ τι συνέβη:

Βίντεο του Cloud Object Detector στο Raspberry Pi

Λίγο λιγότερο γρήγορο από ό,τι με το NCS, αλλά πιο δυναμικό από ό,τι σε ένα ρεύμα.

Το κέρδος, φυσικά, δεν είναι γραμμικό - υπάρχουν επικαλύψεις για συγχρονισμό και βαθιά αντιγραφή εικόνων opencv.

Συμπέρασμα

Συνολικά, το πείραμα μας επιτρέπει να συμπεράνουμε ότι αν προσπαθήσετε, μπορείτε να ξεφύγετε με ένα απλό σύννεφο.

Αλλά ένα ισχυρό επιτραπέζιο ή τοπικό υλικό σάς επιτρέπει να επιτύχετε καλύτερα αποτελέσματα και χωρίς κανένα κόλπο.

παραπομπές

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο