Antaŭparolo
Vidbendo nun cirkulas en la Interreto montrante kiel la aŭtomata piloto de Tesla vidas la vojon.
Mi delonge jukas elsendi videon riĉigitan per detektilo, kaj en reala tempo.
La problemo estas, ke mi volas elsendi filmeton de Raspberry, kaj la agado de la neŭrala reto-detektilo lasas multe por deziri.
Intel Neŭrala Komputila Stilo
Mi pripensis malsamajn solvojn.
В
Kvankam Intel provizas konvertilojn por gravaj kadroj, ekzistas kelkaj malfacilaĵoj.
Ekzemple, la formato de la bezonata reto povas esti malkongrua, kaj se ĝi estas kongrua, tiam iuj tavoloj eble ne estas subtenataj sur la aparato, kaj se ili estas subtenataj, tiam eraroj povas okazi dum la konverta procezo, kiel rezulto de kio. ni ricevas kelkajn strangajn aferojn ĉe la eligo.
Ĝenerale, se vi volas ian arbitran neŭralan reton, tiam ĝi eble ne funkcias kun NCS. Tial mi decidis provi solvi la problemon per la plej disvastigitaj kaj alireblaj iloj.
Nubo
La evidenta alternativo al loka aparatara solvo estas iri al la nubo.
Pretaj opcioj - miaj okuloj sovaĝiĝas.
Ĉiuj gvidantoj:
... Kaj dekoj da malpli konataj.
Elekti inter ĉi tiu vario tute ne estas facila.
Kaj mi decidis ne elekti, sed envolvi la bonan malnovan laborskemon sur OpenCV en Docker kaj ruli ĝin en la nubo.
La avantaĝo de ĉi tiu aliro estas fleksebleco kaj kontrolo - vi povas ŝanĝi la neŭralan reton, gastigadon, servilon - ĝenerale, ajna kaprico.
Servilo
Ni komencu per loka prototipo.
Tradicie mi uzas Flask por REST API, OpenCV kaj MobileSSD-reto.
Instalinte la nunajn versiojn sur Docker, mi malkovris, ke OpenCV 4.1.2 ne funkcias kun Mobile SSD v1_coco_2018_01_28, kaj mi devis reveni al la pruvita 11/06_2017.
Komence de la servo, ni ŝarĝas la klasnomojn kaj reton:
def init():
tf_labels.initLabels(dnn_conf.DNN_LABELS_PATH)
return cv.dnn.readNetFromTensorflow(dnn_conf.DNN_PATH, dnn_conf.DNN_TXT_PATH)
Sur loka dokero (sur ne tre juna tekkomputilo) necesas 0.3 sekundoj, ĉe Raspberry - 3.5.
Ni komencu la kalkulon:
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, Frambo - 1.7.
Turnante tensora ellasilo en legeblan 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
Plue
Alternativa opcio, en kiu pli da laboro estas movita al la servilo: ĝi mem rondiras la trovitajn objektojn kaj resendas la finitan bildon.
Ĉi tiu opcio estas bona kie ni ne volas treni opencv al la servilo.
Docker
Ni kolektas la bildon.
La kodo estas kombita kaj afiŝita
Kiel platformo, ni prenos la saman Debian Stretch kiel sur Raspberry - ni ne devios de la provita teknika stako.
Vi devas instali flaskon, protobuf, petojn, opencv_python, elŝuti Poŝtelefonan SSD, servilan kodon de Github kaj komenci la servilon.
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"]
Simpla
Eldonado al Docker Hub
Docker-registroj multiĝas je rapideco ne malpli ol nubaj detektiloj.
Por ne ĝeni, ni konserveme trairos
- Registru
- Ensaluti:
docker ensaluto - Ni elpensu signifoplenan nomon:
docker-etikedo opencv-detect tprlab/opencv-detect-ssd - Alŝutu la bildon al la servilo:
docker push tprlab/opencv-detect-ssd
Ni lanĉas en la nubo
La elekto de kie ruli la ujon ankaŭ estas sufiĉe larĝa.
Ĉiuj grandaj ludantoj (Google, Microsoft, Amazon) ofertas mikro-instancon senpage dum la unua jaro.
Post eksperimentado kun Microsoft Azure kaj Google Cloud, mi decidis pri ĉi-lasta ĉar ĝi ekflugis pli rapide.
Mi ne skribis instrukciojn ĉi tie, ĉar ĉi tiu parto estas tre specifa por la elektita provizanto.
Mi provis malsamajn aparatajn opciojn,
Malaltaj niveloj (dividitaj kaj dediĉitaj) - 0.4 - 0.5 sekundoj.
Pli potencaj aŭtoj - 0.25 - 0.3.
Nu, eĉ en la plej malbona kazo, la gajno estas trioble, vi povas provi.
Video
Ni lanĉas simplan OpenCV-videofluilon sur Raspberry, detektante per Google Cloud.
Por la eksperimento oni uzis videodosieron, kiu iam estis filmita ĉe hazarda intersekciĝo.
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")
Kun la detektilo ni ricevas ne pli ol tri kadrojn por sekundo, ĉio iras tre malrapide.
Se vi prenas potencan maŝinon en GCloud, vi povas detekti 4-5 kadrojn sekundo, sed la diferenco estas preskaŭ nevidebla por la okulo, ĝi ankoraŭ malrapidas.
La nubo kaj transportkostoj havas nenion por fari kun ĝi; la detektilo funkcias per ordinara aparataro kaj funkcias kun tia rapideco.
Neŭrala Komputila Bastono
Mi ne povis rezisti kaj kuris la komparnormon sur NCS.
La rapido de la detektilo estis iomete pli malrapida ol 0.1 sekundoj, ĉiukaze 2-3 fojojn pli rapida ol la nubo sur malforta maŝino, t.e. 8-9 kadroj sekundo.
La diferenco en rezultoj estas klarigita per la fakto, ke NCS rulis Mobile SSD version 2018_01_28.
PS Krome, eksperimentoj montris, ke sufiĉe potenca labortabla maŝino kun I7-procesoro montras iomete pli bonajn rezultojn kaj montriĝis eble premi 10 kadrojn por sekundo sur ĝi.
Areto
La eksperimento iris plu kaj mi instalis la detektilon sur kvin nodoj en Guglo Kubernetes.
La guŝoj mem estis malfortaj kaj ĉiu el ili ne povis prilabori pli ol 2 kadrojn por sekundo.
Sed se vi kuras areton kun N nodoj kaj analizas kadrojn en N fadenoj, tiam kun sufiĉa nombro da nodoj (5) vi povas atingi la deziratajn 10 kadrojn sekundo.
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
Jen kio okazis:
Iom malpli rapide ol kun NCS, sed pli vigla ol en unu rivereto.
La gajno, kompreneble, ne estas linia - estas supermetaĵoj por sinkronigado kaj profunda kopiado de opencv-bildoj.
konkludo
Ĝenerale, la eksperimento permesas al ni konkludi, ke se vi provas, vi povas eliri per simpla nubo.
Sed potenca labortablo aŭ loka aparataro permesas vin atingi pli bonajn rezultojn, kaj sen ajnaj lertaĵoj.
referencoj
fonto: www.habr.com