Відэа з хмарным дэтэктарам аб'ектаў на Raspberry Pi

пралог

Па сетцы зараз гуляе відэа - як аўтапілот Теслы бачыць дарогу.

У мяне даўно свярбелі рукі трансляваць відэа, узбагачанае дэтэктарам, ды і ў рэальным часе.

Відэа з хмарным дэтэктарам аб'ектаў на Raspberry Pi

Праблема ў тым, што трансляваць відэа я хачу з Raspberry, а прадукцыйнасць нейросетевого дэтэктара на ёй пакідае жадаць лепшага.

Intel Neural Computer Stick

Я разглядаў розныя варыянты рашэння.

В мінулым артыкуле эксперыментаваў з Intel Neural Computer Stick. Жалезка магутная, але патрабуе свайго фармату сеткі.

Нягледзячы на ​​тое, што Интел падае канвертары для асноўных фрэймворкаў, тут ёсць шэраг падводных камянёў.

Напрыклад, фармат патрэбнай сеткі можа быць несумяшчальны, а калі сумяшчальны, то нейкія пласты могуць не падтрымлівацца на дэвайсе, а калі падтрымліваюцца, то ў працэсе канвертавання могуць адбывацца памылкі, у выніку якіх на выхадзе атрымліваем нейкія дзіўныя рэчы.

Увогуле, калі жадаецца нейкую адвольную нейрасетку, то з NCS можа не атрымацца. Таму я вырашыў паспрабаваць вырашыць праблему праз самыя масавыя і даступныя інструменты.

воблака

Відавочная альтэрнатыва лакальна-хардварнаму рашэнню - пайсці ў воблака.

Гатовых варыянтаў - вочы разбягаюцца.

Усе лідэры:

… І дзясяткі менш вядомых.

Выбраць сярод гэтай разнастайнасці зусім не проста.

І я вырашыў не выбіраць, а загарнуць старую добрую працоўную схему на OpenCV у докер і запусціць яго ў воблаку.

Перавага такога падыходу ў гнуткасці і кантролі – можна памяняць нейросетку, хостынг, сервер – увогуле, любы капрыз.

Сервер

Пачнём з лакальнага прататыпа.

Традыцыйна я выкарыстоўваю Flask для REST API, OpenCV і MobileSSD сетку.

Паставіўшы на докер бягучыя версіі, выявіў што 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)

На лакальным докеры (на не самым маладым лаптопе) гэта займае 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()

Докер - 0.2 сек, 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 на сервер.

Докер

Збіраны выява.

Код прычэсаны і выкладзены на Гітхаб, докер возьме яго напрамую адтуль.

У якасці платформы возьмем той жа Debian Stretch, што і на Raspberry – не будзем сыходзіць ад праверанага тэхстэка.

Трэба паставіць flask, protobuf, requests, opencv_python, спампаваць Mobile SSD, код сервера з Гітхаба і запусціць сервер.

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

просты кліент для дэтэктара на аснове requests.

Публікацыя на Docker Hub

Рэестры докера размнажаюцца з хуткасцю не меншай, чым хмарныя дэтэктары.

Каб не затлумляцца, мы кансерватыўна пойдзем праз ДокерХаб.

  1. Рэгіструемся
  2. Аўтарызуемся:
    докер лагін
  3. Прыдумаем змястоўнае імя:
    docker tag opencv-detect tprlab/opencv-detect-ssd
  4. Загружаем выяву на сервер:
    docker push tprlab/opencv-detect-ssd

Запускаем у воблаку

Выбар, дзе запусціць кантэйнер, таксама вельмі шырокі.

Усе вялікія гульцы (Гугл, Мікрасофт, Амазон) прапануюць мікраінстанс бясплатна ў першы год.
Паэкспераментаваўшы з Microsoft Azure і Google Cloud, спыніўся на апошнім - таму, што хутчэй узляцела.

Не стаў пісаць тут інструкцыю, бо гэтая частка вельмі спецыфічная для абранага правайдэра.

Паспрабаваў розныя варыянты жалеза,
Нізкія ўзроўні (shared і выдзеленыя) - 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 кадраў у секунду, але розніца вокам практычна незаўважная, усё роўна павольна.

Відэа з хмарным дэтэктарам аб'ектаў на Raspberry Pi

Воблака і транспартныя выдаткі тут не прычым, на звычайным жалезе дэтэктар і працуе з такой хуткасцю.

Neural Computer Stick

Не ўтрымаўся і прагнаў бенчмарк на NCS.

Хуткасць дэтэктара была крыху павольней 0.1 секунды, у любым выпадку ў 2-3 разы хутчэй аблокі на слабой машыне, т.е 8-9 кадраў у секунду.

Відэа з хмарным дэтэктарам аб'ектаў на Raspberry Pi

Розніца ў выніках тлумачыцца тым, што на NCS запускаўся Mobile SSD версіі 2018_01_28.

P.S. Акрамя таго, эксперыменты паказалі, што дастаткова магутная дэсктопная машына з I7 працэсарам паказвае крыху лепшыя вынікі і на ёй аказалася магчыма выціснуць 10 кадраў у секунду.

кластар

Эксперымент пайшоў далей і я паставіў дэтэктар на пяці вузлах у Google Kubernetes.
Самі па сабе поды былі слабыя і кожны з іх не мог апрацаваць больш за 2 кадры ў секунду.
Але калі запусціць кластар на N вузлоў і разбіраць кадры ў 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

Вось што атрымалася:

Відэа з хмарным дэтэктарам аб'ектаў на Raspberry Pi

Трохі не так жвава як з NCS, але бадзёрыя чым у адзін струмень.

Выйгрыш, вядома, не лінены – выстрэльваюць накладкі на сінхранізацыю і глыбокае капіраванне малюнкаў opencv.

Заключэнне

У цэлым, эксперымент дазваляе зрабіць выснову, што, калі паспрабаваць, можна выкруціцца з простым воблакам.

Але магутны дэсктоп або лакальная жалязяка дазваляюць дамагчыся лепшых вынікаў, прычым без усялякіх хітрыкаў.

Спасылкі

Крыніца: habr.com

Дадаць каментар