Proloog
Nüüd ringleb Internetis video, mis näitab, kuidas Tesla autopiloot teed näeb.
Olen juba pikka aega soovinud detektoriga rikastatud videot edastada ja seda reaalajas.
Probleem on selles, et ma tahan Raspberryst videot edastada ja sellel oleva närvivõrgu detektori jõudlus jätab soovida.
Intel Neural Computer Stick
Kaalusin erinevaid lahendusi.
В
Kuigi Intel pakub suuremate raamistike jaoks muundureid, on sellel mitmeid lõkse.
Näiteks võib vajaliku võrgu formaat olla mitteühilduv ja kui see on ühilduv, siis ei pruugi seade mõnda kihti toetada ja kui need on toetatud, siis võib teisendusprotsessi käigus tekkida tõrkeid, mille tulemusena saame väljundis kummalisi asju.
Üldiselt, kui soovite mingit suvalist närvivõrku, siis see ei pruugi NCS-iga töötada. Seetõttu otsustasin proovida probleemi lahendada kõige levinumate ja ligipääsetavamate tööriistade abil.
Pilv
Ilmselge alternatiiv kohalikule riistvaralahendusele on minna pilve.
Valmis variandid – mu silmad jooksevad metsikult.
Kõik juhid:
... Ja kümneid vähemtuntud.
Selle sordi hulgast valimine pole sugugi lihtne.
Ja otsustasin mitte valida, vaid pakkida vana hea OpenCV tööskeemi Dockerisse ja käivitada see pilves.
Selle lähenemise eeliseks on paindlikkus ja juhtimine - saate muuta närvivõrku, hostimist, serverit - üldiselt mis tahes kapriisi.
Server
Alustame kohaliku prototüübiga.
Traditsiooniliselt kasutan Flask REST API, OpenCV ja MobileSSD võrgu jaoks.
Olles installinud Dockerisse praegused versioonid, avastasin, et OpenCV 4.1.2 ei tööta koos Mobile SSD-ga v1_coco_2018_01_28 ja pidin tagasi pöörduma tõestatud versioonile 11/06_2017.
Teenuse käivitamisel laadime klassinimed ja võrgu:
def init():
tf_labels.initLabels(dnn_conf.DNN_LABELS_PATH)
return cv.dnn.readNetFromTensorflow(dnn_conf.DNN_PATH, dnn_conf.DNN_TXT_PATH)
Kohalikus dokis (mitte väga noorel sülearvutil) kulub 0.3 sekundit, Raspberryl - 3.5.
Alustame arvutamist:
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, Vaarikas - 1.7.
Tensori väljalaske muutmine loetavaks jsoniks:
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
Edasi
Alternatiivne variant, mille puhul nihutatakse rohkem tööd serverile: ta teeb ise leitud objektid ringi ja tagastab valmis pildi.
See valik on hea, kui me ei soovi opencv-d serverisse lohistada.
Docker
Kogume pildi.
Kood kammitakse ja postitatakse
Platvormina kasutame sama Debian Stretchi, mis Raspberry puhul – me ei kaldu kõrvale tõestatud tehnoloogiast.
Peate installima flask, protobuf, taotlused, opencv_python, alla laadima Githubi mobiilse SSD, serveri koodi ja käivitama serveri.
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"]
Lihtne
Docker Hubis avaldamine
Dockeri registrid paljunevad kiirusega, mis pole väiksem kui pilvedetektorid.
Et mitte tülitada, läheme konservatiivselt läbi
- Registreeri
- Logi sisse:
dokkeri sisselogimine - Mõtleme välja tähendusrikka nime:
dokkeri silt opencv-detect tprlab/opencv-detect-ssd - Laadi pilt serverisse üles:
Docker push tprlab/opencv-detect-ssd
Käivitame pilves
Valik, kuhu konteiner jooksutada, on samuti üsna lai.
Kõik suured tegijad (Google, Microsoft, Amazon) pakuvad esimesel aastal mikroeksemplari tasuta.
Pärast Microsoft Azure'i ja Google Cloudi katsetamist otsustasin viimase kasuks, kuna see läks kiiremini käima.
Ma ei kirjutanud siia juhiseid, kuna see osa on valitud pakkuja jaoks väga spetsiifiline.
Proovisin erinevaid riistvaravalikuid,
Madalad tasemed (jagatud ja pühendatud) - 0.4 - 0.5 sekundit.
Võimsamad autod - 0.25 - 0.3.
Noh, isegi halvimal juhul on võidud kolmekordsed, võite proovida.
video
Käivitame Raspberrys lihtsa OpenCV-videostriimi, mis tuvastab Google Cloudi kaudu.
Katse jaoks kasutati videofaili, mis filmiti kunagi juhuslikul ristmikul.
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")
Detektoriga saame mitte rohkem kui kolm kaadrit sekundis, kõik läheb väga aeglaselt.
Kui võtate võimsa masina GCloudi, saate tuvastada 4-5 kaadrit sekundis, kuid erinevus on silmale peaaegu nähtamatu, see on siiski aeglane.
Pilvel ja transpordikuludel pole sellega midagi pistmist, detektor töötab tavalisel riistvaral ja töötab sellise kiirusega.
Neuraalne arvutipulk
Ma ei suutnud vastu panna ja tegin NCS-i võrdlusaluse.
Detektori kiirus oli veidi aeglasem kui 0.1 sekundit, igal juhul 2-3 korda kiirem kui pilv nõrgal masinal ehk 8-9 kaadrit sekundis.
Tulemuste erinevus on seletatav asjaoluga, et NCS kasutas Mobile SSD versiooni 2018_01_28.
PS Lisaks on katsed näidanud, et üsna võimas I7 protsessoriga lauamasin näitab veidi paremaid tulemusi ja osutus võimalikuks pigistada 10 kaadrit sekundis.
Kobar
Katse läks kaugemale ja paigaldasin detektori Google Kubernetes viiele sõlmele.
Kaunad ise olid nõrgad ja igaüks neist ei suutnud töödelda rohkem kui 2 kaadrit sekundis.
Kuid kui käivitate N sõlmega klastri ja sõelute kaadrit N lõimes, siis piisava arvu sõlmedega (5) saate soovitud 10 kaadrit sekundis saavutada.
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
See on juhtunud:
Natuke vähem kiire kui NCS-iga, kuid jõulisem kui ühes voos.
Kasum ei ole muidugi lineaarne – opencv-piltide sünkroonimiseks ja sügavaks kopeerimiseks on olemas ülekatted.
Järeldus
Üldiselt võimaldab katse järeldada, et kui proovite, pääsete lihtsa pilvega.
Kuid võimas töölaud või kohalik riistvara võimaldab teil saavutada paremaid tulemusi ja ilma igasuguste trikkideta.
Viited
Allikas: www.habr.com