پیش نویس
اکنون ویدئویی در اینترنت در حال پخش است که نشان می دهد خلبان خودکار تسلا چگونه جاده را می بیند.
مدت زیادی است که برای پخش ویدیوی غنی شده با آشکارساز و در زمان واقعی خارش دارم.
مشکل این است که من می خواهم ویدیویی را از رزبری پخش کنم و عملکرد آشکارساز شبکه عصبی روی آن چیزهای زیادی را برای شما باقی می گذارد.
استیک کامپیوتر عصبی اینتل
راه حل های مختلفی را در نظر گرفتم.
В
اگرچه اینتل مبدلهایی را برای فریمورکهای اصلی ارائه میکند، اما تعدادی از مشکلات وجود دارد.
به عنوان مثال، فرمت شبکه مورد نیاز ممکن است ناسازگار باشد و اگر سازگار باشد، ممکن است برخی از لایه ها روی دستگاه پشتیبانی نشوند و اگر پشتیبانی شوند، ممکن است در فرآیند تبدیل خطاهایی رخ دهد که در نتیجه آن ما چیزهای عجیبی در خروجی دریافت می کنیم.
به طور کلی، اگر یک نوع شبکه عصبی دلخواه را می خواهید، ممکن است با NCS کار نکند. بنابراین، تصمیم گرفتم با استفاده از گسترده ترین و در دسترس ترین ابزار، مشکل را حل کنم.
ابر
جایگزین واضح برای راه حل سخت افزاری محلی، رفتن به ابر است.
گزینه های آماده - چشمان من وحشی می شوند.
همه رهبران:
... و ده ها مورد کمتر شناخته شده.
انتخاب از بین این تنوع اصلاً آسان نیست.
و من تصمیم گرفتم انتخاب نکنم، بلکه طرح کار خوب قدیمی را روی OpenCV در Docker بپیچم و آن را در فضای ابری اجرا کنم.
مزیت این رویکرد انعطاف پذیری و کنترل است - شما می توانید شبکه عصبی، میزبانی، سرور را تغییر دهید - به طور کلی، هر هوس.
سرور
بیایید با یک نمونه اولیه محلی شروع کنیم.
من به طور سنتی از Flask برای REST API، OpenCV و شبکه MobileSSD استفاده می کنم.
با نصب نسخه های فعلی در Docker، متوجه شدم که 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 ثانیه، تمشک - 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
بعدا
یک گزینه جایگزین، که در آن کار بیشتری به سرور منتقل میشود: خودش دور اشیاء پیدا شده میچرخد و تصویر تمام شده را برمیگرداند.
این گزینه برای جایی که نمی خواهیم opencv را به سرور بکشیم خوب است.
داکر
ما تصویر را جمع آوری می کنیم.
کد شانه شده و ارسال می شود
به عنوان یک پلتفرم، ما همان Debian Stretch را در Raspberry انتخاب خواهیم کرد - از پشته فناوری اثبات شده منحرف نخواهیم شد.
شما باید flask، protobuf، requests، opencv_python، Mobile SSD، کد سرور را از Github دانلود کنید و سرور را راه اندازی کنید.
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"]
ساده
انتشار در Docker Hub
ثبت داکر با سرعتی کمتر از آشکارسازهای ابری در حال افزایش است.
برای اینکه مزاحم نشویم، محافظه کارانه گذر خواهیم کرد
- ثبت نام
- وارد شدن:
ورود به سیستم داکر - بیایید یک نام معنادار پیدا کنیم:
تگ docker opencv-detect tprlab/opencv-detect-ssd - آپلود تصویر در سرور:
docker push tprlab/opencv-detect-ssd
ما در فضای ابری راه اندازی می کنیم
انتخاب محل اجرای ظرف نیز بسیار گسترده است.
همه بازیکنان بزرگ (گوگل، مایکروسافت، آمازون) یک نمونه میکرو را به صورت رایگان برای سال اول ارائه می دهند.
پس از آزمایش با مایکروسافت Azure و Google Cloud، من به دومی اکتفا کردم زیرا سریعتر از بین رفت.
من دستورالعملی را در اینجا ننوشتم، زیرا این بخش برای ارائه دهنده انتخاب شده بسیار خاص است.
من گزینه های سخت افزاری مختلف را امتحان کردم،
سطوح پایین (به اشتراک گذاشته شده و اختصاصی) - 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 فریم در ثانیه را تشخیص دهید، اما تفاوت تقریباً برای چشم نامرئی است، هنوز هم کند است.
هزینه های ابر و حمل و نقل هیچ ربطی به آن ندارند؛ آشکارساز با سخت افزار معمولی کار می کند و با چنین سرعتی کار می کند.
استیک کامپیوتر عصبی
من نتوانستم مقاومت کنم و معیار NCS را اجرا کردم.
سرعت آشکارساز کمی کمتر از 0.1 ثانیه بود، در هر صورت 2-3 برابر سریعتر از ابر در یک ماشین ضعیف، یعنی 8-9 فریم در ثانیه.
تفاوت در نتایج با این واقعیت توضیح داده می شود که NCS نسخه SSD موبایل 2018_01_28 را اجرا می کرد.
علاوه بر این، آزمایشها نشان دادهاند که یک دستگاه رومیزی نسبتاً قدرتمند با پردازنده 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
در اینجا چیزی اتفاق افتاده است:
کمی سریعتر از NCS، اما قوی تر از یک جریان.
البته سود خطی نیست - پوشش هایی برای همگام سازی و کپی عمیق تصاویر opencv وجود دارد.
نتیجه
به طور کلی، این آزمایش به ما اجازه میدهد به این نتیجه برسیم که اگر تلاش کنید، میتوانید با یک ابر ساده خلاص شوید.
اما یک دسکتاپ قدرتمند یا سخت افزار محلی به شما اجازه می دهد تا به نتایج بهتری و بدون هیچ ترفندی برسید.
مراجع
منبع: www.habr.com