Prolog
Internetom sada kruži video koji prikazuje kako Teslin autopilot vidi cestu.
Dugo sam žudio da emitiram video obogaćen detektorom i to u stvarnom vremenu.
Problem je u tome što želim emitirati video s Raspberryja, a izvedba detektora neuronske mreže na njemu ostavlja mnogo za poželjeti.
Intel Neural Computer Stick
Razmatrao sam različita rješenja.
В
Iako Intel nudi pretvarače za glavne okvire, postoji niz zamki.
Na primjer, format tražene mreže može biti nekompatibilan, a ako je kompatibilan, neki slojevi možda neće biti podržani na uređaju, a ako su podržani, može doći do grešaka tijekom procesa konverzije, uslijed čega dobivamo neke čudne stvari na izlazu.
Općenito, ako želite neku vrstu proizvoljne neuronske mreže, ona možda neće raditi s NCS-om. Stoga sam odlučio pokušati riješiti problem koristeći najrasprostranjenije i najpristupačnije alate.
Oblak
Očigledna alternativa lokalnom hardverskom rješenju je odlazak u oblak.
Gotove opcije - oči mi divljaju.
Svi voditelji:
... I deseci manje poznatih.
Odabir među ovom raznolikošću nije nimalo lak.
I odlučio sam ne birati, već umotati staru dobru radnu shemu na OpenCV-u u Docker i pokrenuti je u oblaku.
Prednost ovog pristupa je fleksibilnost i kontrola - možete promijeniti neuronsku mrežu, hosting, poslužitelj - općenito, bilo koji hir.
Server
Počnimo s lokalnim prototipom.
Tradicionalno koristim Flask za REST API, OpenCV i MobileSSD mrežu.
Nakon što sam instalirao trenutne verzije na Docker, otkrio sam da OpenCV 4.1.2 ne radi s Mobile SSD v1_coco_2018_01_28 i morao sam se vratiti na provjerenu 11/06_2017.
Na početku usluge učitavamo nazive klasa i mrežu:
def init():
tf_labels.initLabels(dnn_conf.DNN_LABELS_PATH)
return cv.dnn.readNetFromTensorflow(dnn_conf.DNN_PATH, dnn_conf.DNN_TXT_PATH)
Na lokalnom dockeru (na ne baš mladom prijenosnom računalu) potrebno je 0.3 sekunde, na Raspberryju - 3.5.
Krenimo s izračunom:
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, Raspberry - 1.7.
Pretvaranje tenzorskog ispuha u čitljiv 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
dalje
Alternativna opcija, u kojoj se više posla prebacuje na poslužitelj: on sam kruži pronađene objekte i vraća gotovu sliku.
Ova opcija je dobra kada ne želimo povući opencv na poslužitelj.
Lučki radnik
Sakupljamo sliku.
Šifra je pročešljana i objavljena
Kao platformu, uzet ćemo isti Debian Stretch kao i na Raspberryju - nećemo odstupiti od provjerene tehnologije.
Trebate instalirati flask, protobuf, requests, opencv_python, preuzeti Mobile SSD, kod poslužitelja s Githuba i pokrenuti poslužitelj.
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"]
Jednostavan
Objavljivanje na Docker Hubu
Docker registri množe se brzinom ništa manjom od detektora oblaka.
Da ne bi smetali, proći ćemo konzervativno
- Registar
- Prijaviti se:
docker prijava - Smislimo smisleno ime:
docker oznaka opencv-detect tprlab/opencv-detect-ssd - Prenesi sliku na server:
docker push tprlab/opencv-detect-ssd
Pokrećemo u oblaku
Izbor mjesta za pokretanje kontejnera također je prilično širok.
Svi veliki igrači (Google, Microsoft, Amazon) nude mikroinstancu besplatno tijekom prve godine.
Nakon eksperimentiranja s Microsoft Azureom i Google Cloudom, odlučio sam se za potonji jer se brže razvio.
Ovdje nisam pisao upute jer je ovaj dio vrlo specifičan za odabranog pružatelja usluga.
Isprobao sam različite opcije hardvera,
Niske razine (dijeljene i namjenske) - 0.4 - 0.5 sekundi.
Snažniji automobili - 0.25 - 0.3.
Pa i u najgorem slučaju dobitak je trostruki, možete pokušati.
video
Pokrećemo jednostavan OpenCV video streamer na Raspberryju, otkrivajući putem Google Clouda.
Za eksperiment je korištena video datoteka koja je jednom snimljena na nasumičnom raskrižju.
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")
S detektorom ne dobivamo više od tri slike u sekundi, sve ide jako sporo.
Ako ubacite moćnu mašinu u GCloud, možete detektirati 4-5 sličica u sekundi, ali razlika je gotovo nevidljiva oku, i dalje je spor.
Oblak i troškovi transporta nemaju veze s tim, detektor radi na običnom hardveru i radi takvom brzinom.
Neuralni računalni štap
Nisam mogao odoljeti i pokrenuo sam benchmark na NCS-u.
Brzina detektora bila je nešto sporija od 0.1 sekunde, u svakom slučaju 2-3 puta brža od oblaka na slabom stroju, tj. 8-9 sličica u sekundi.
Razlika u rezultatima objašnjava se činjenicom da je NCS pokretao Mobile SSD verziju 2018_01_28.
PS Osim toga, eksperimenti su pokazali da prilično moćan stolni stroj s I7 procesorom pokazuje nešto bolje rezultate i pokazalo se da je na njemu moguće stisnuti 10 sličica u sekundi.
Klastera
Eksperiment je otišao dalje i instalirao sam detektor na pet čvorova u Google Kubernetesu.
Sami moduli bili su slabi i svaki od njih nije mogao obraditi više od 2 okvira u sekundi.
Ali ako pokrenete klaster s N čvorova i analizirate okvire u N niti, tada s dovoljnim brojem čvorova (5) možete postići željenih 10 okvira u sekundi.
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
Evo što se dogodilo:
Malo manje brzo nego s NCS-om, ali snažnije nego u jednom streamu.
Dobitak, naravno, nije linearan - postoje slojevi za sinkronizaciju i duboko kopiranje opencv slika.
Zaključak
Sve u svemu, eksperiment nam omogućuje da zaključimo da ako pokušate, možete se izvući s jednostavnim oblakom.
Ali moćno stolno računalo ili lokalni hardver omogućuje postizanje boljih rezultata, i to bez ikakvih trikova.
reference
Izvor: www.habr.com