prolog
Un videoclip circulă acum pe internet care arată cum vede pilotul automat Tesla drumul.
Am mâncărime de mult timp să difuzez videoclipuri îmbogățite cu un detector și în timp real.
Problema este că vreau să difuzez video de la Raspberry, iar performanța detectorului de rețea neuronală de pe el lasă de dorit.
Stick de computer Intel Neural
Am luat în calcul soluții diferite.
В
Chiar dacă Intel oferă convertoare pentru cadrele majore, există o serie de capcane.
De exemplu, formatul rețelei necesare poate fi incompatibil și, dacă este compatibil, este posibil ca unele straturi să nu fie acceptate pe dispozitiv și, dacă sunt acceptate, pot apărea erori în timpul procesului de conversie, drept urmare primim niște lucruri ciudate la ieșire.
În general, dacă doriți un fel de rețea neuronală arbitrară, atunci este posibil să nu funcționeze cu NCS. Prin urmare, am decis să încerc să rezolv problema folosind cele mai răspândite și accesibile instrumente.
nor
Alternativa evidentă la o soluție hardware locală este să mergi în cloud.
Opțiuni gata făcute - ochii îmi fug.
Toți liderii:
... Și zeci de altele mai puțin cunoscute.
Alegerea dintre această varietate nu este deloc ușoară.
Și am decis să nu aleg, ci să împachetez vechea schemă de lucru bună pe OpenCV în Docker și să o rulez în cloud.
Avantajul acestei abordări este flexibilitatea și controlul - puteți schimba rețeaua neuronală, găzduire, server - în general, orice capriciu.
Server
Să începem cu un prototip local.
În mod tradițional, folosesc Flask pentru REST API, OpenCV și rețeaua MobileSSD.
După ce am instalat versiunile actuale pe Docker, am descoperit că OpenCV 4.1.2 nu funcționează cu Mobile SSD v1_coco_2018_01_28 și a trebuit să revin la data dovedită 11/06_2017.
La începutul serviciului, încărcăm numele claselor și rețeaua:
def init():
tf_labels.initLabels(dnn_conf.DNN_LABELS_PATH)
return cv.dnn.readNetFromTensorflow(dnn_conf.DNN_PATH, dnn_conf.DNN_TXT_PATH)
Pe un docker local (pe un laptop nu foarte tânăr) durează 0.3 secunde, pe Raspberry - 3.5.
Să începem calculul:
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 secunde, Zmeura - 1.7.
Transformarea eșapamentului tensorului în json lizibil:
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
mai departe
O opțiune alternativă, în care mai multă muncă este transferată către server: el însuși încercuiește obiectele găsite și returnează imaginea finală.
Această opțiune este bună acolo unde nu vrem să tragem opencv pe server.
Docher
Colectăm imaginea.
Codul este pieptănat și postat pe
Ca platformă, vom lua același Debian Stretch ca și pe Raspberry - nu ne vom abate de la stiva tehnologică dovedită.
Trebuie să instalați flask, protobuf, requests, opencv_python, să descărcați Mobile SSD, codul de server din Github și să porniți serverul.
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"]
simplu
Publicarea în Docker Hub
Registrele Docker se înmulțesc cu o viteză nu mai mică decât detectoarele de nor.
Pentru a nu ne deranja, vom trece conservator
- Inregistreaza-te
- Log in:
conectare la docker - Să găsim un nume semnificativ:
etichetă docker opencv-detect tprlab/opencv-detect-ssd - Încărcați imaginea pe server:
docker push tprlab/opencv-detect-ssd
Lansăm în cloud
Alegerea unde să ruleze containerul este, de asemenea, destul de largă.
Toți jucătorii mari (Google, Microsoft, Amazon) oferă o microinstanță gratuită pentru primul an.
După ce am experimentat cu Microsoft Azure și Google Cloud, m-am hotărât pe acesta din urmă pentru că a decolat mai repede.
Nu am scris instrucțiuni aici, deoarece această parte este foarte specifică furnizorului selectat.
Am încercat diferite opțiuni hardware,
Niveluri scăzute (partajat și dedicat) - 0.4 - 0.5 secunde.
Mașini mai puternice - 0.25 - 0.3.
Ei bine, chiar și în cel mai rău caz, câștigurile sunt de trei ori, poți încerca.
video
Lansăm un simplu streamer video OpenCV pe Raspberry, detectând prin Google Cloud.
Pentru experiment, a fost folosit un fișier video care a fost filmat cândva la o intersecție aleatorie.
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")
Cu detectorul nu obținem mai mult de trei cadre pe secundă, totul merge foarte încet.
Dacă introduceți o mașină puternică în GCloud, puteți detecta 4-5 cadre pe secundă, dar diferența este aproape invizibilă pentru ochi, este încă lentă.
Cloudul și costurile de transport nu au nimic de-a face cu asta; detectorul funcționează pe hardware obișnuit și funcționează cu o astfel de viteză.
Stick de computer neuronal
Nu am putut rezista și am rulat benchmark-ul pe NCS.
Viteza detectorului a fost puțin mai mică de 0.1 secunde, în orice caz de 2-3 ori mai rapidă decât cea a norului pe o mașină slabă, adică 8-9 cadre pe secundă.
Diferența de rezultate se explică prin faptul că NCS rula SSD mobil versiunea 2018_01_28.
PS În plus, experimentele au arătat că o mașină desktop destul de puternică cu un procesor I7 arată rezultate puțin mai bune și s-a dovedit a fi posibil să stoarce 10 cadre pe secundă pe ea.
Cluster
Experimentul a mers mai departe și am instalat detectorul pe cinci noduri din Google Kubernetes.
Păstăile în sine erau slabe și fiecare dintre ele nu putea procesa mai mult de 2 cadre pe secundă.
Dar dacă rulați un cluster cu N noduri și analizați cadre în N fire, atunci cu un număr suficient de noduri (5) puteți obține cele 10 cadre pe secundă dorite.
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
Iată ce s-a întâmplat:
Puțin mai puțin rapid decât cu NCS, dar mai viguros decât într-un singur flux.
Câștigul, desigur, nu este liniar - există suprapuneri pentru sincronizare și copiere profundă a imaginilor opencv.
Concluzie
În general, experimentul ne permite să concluzionam că, dacă încerci, poți scăpa cu un simplu nor.
Dar un desktop puternic sau un hardware local vă permite să obțineți rezultate mai bune și fără trucuri.
referințe
Sursa: www.habr.com