อารัมภบท
ขณะนี้วิดีโอกำลังเผยแพร่บนอินเทอร์เน็ตเพื่อแสดงให้เห็นว่าระบบขับเคลื่อนอัตโนมัติของ Tesla มองเห็นถนนได้อย่างไร
ฉันอยากออกอากาศวิดีโอที่มีเครื่องตรวจจับและแบบเรียลไทม์มาเป็นเวลานาน
ปัญหาคือฉันต้องการออกอากาศวิดีโอจาก Raspberry และประสิทธิภาพของเครื่องตรวจจับโครงข่ายประสาทเทียมบนนั้นยังเป็นที่ต้องการอย่างมาก
Intel Neural คอมพิวเตอร์สติ๊ก
ฉันพิจารณาวิธีแก้ปัญหาที่แตกต่างกัน
В
แม้ว่า Intel จะจัดเตรียมคอนเวอร์เตอร์สำหรับเฟรมเวิร์กหลักๆ แต่ก็มีข้อผิดพลาดอยู่หลายประการ
ตัวอย่างเช่น รูปแบบของเครือข่ายที่ต้องการอาจเข้ากันไม่ได้ และหากเข้ากันได้ อุปกรณ์บางเลเยอร์อาจไม่รองรับ และหากได้รับการรองรับ ข้อผิดพลาดอาจเกิดขึ้นในระหว่างกระบวนการแปลง ซึ่งเป็นผลให้ เราได้รับสิ่งแปลก ๆ ที่เอาต์พุต
โดยทั่วไป หากคุณต้องการโครงข่ายประสาทเทียมตามอำเภอใจ เครือข่ายนั้นอาจไม่ทำงานกับ 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, คำขอ, 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
การลงทะเบียนนักเทียบท่ากำลังทวีคูณด้วยความเร็วไม่น้อยไปกว่าตัวตรวจจับคลาวด์
เพื่อไม่ให้เป็นการรบกวนเราจะดำเนินการอย่างระมัดระวัง
- ลงทะเบียน
- เข้าสู่ระบบ:
นักเทียบท่าเข้าสู่ระบบ - เรามาตั้งชื่อที่มีความหมายกันดีกว่า:
แท็กนักเทียบท่า opencv-detect tprlab/opencv-detect-ssd - อัปโหลดภาพไปยังเซิร์ฟเวอร์:
นักเทียบท่ากด tprlab/opencv-detect-ssd
เราเปิดตัวในระบบคลาวด์
ทางเลือกของตำแหน่งที่จะเรียกใช้คอนเทนเนอร์ก็ค่อนข้างกว้างเช่นกัน
ผู้เล่นรายใหญ่ทั้งหมด (Google, Microsoft, Amazon) เสนออินสแตนซ์ขนาดเล็กฟรีในปีแรก
หลังจากทดลองใช้ Microsoft 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 ใช้ Mobile 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 แบบลึก
ข้อสรุป
โดยรวมแล้ว การทดลองช่วยให้เราสรุปได้ว่าหากคุณลอง คุณจะรอดจากระบบคลาวด์ธรรมดาๆ ได้
แต่เดสก์ท็อปหรือฮาร์ดแวร์ภายในเครื่องที่มีประสิทธิภาพช่วยให้คุณได้ผลลัพธ์ที่ดีขึ้น และไม่มีลูกเล่นใดๆ
การอ้างอิง
ที่มา: will.com