上回我們建立了 tensorflow 的開發環境,跑測試模型比較方便,但對於樹莓派來說,還是太龐大了。僅有 0.3fps 無法滿足實時的生產環境需求,這篇我們部署一下專門為移動端和嵌入式設備而生的 TensorFlow Lite,並跑幾個模型,測試一下純樹莓派上的極限幀率。
TensorFlow Lite 是一組工具,可幫助開發者在行動裝置、嵌入式設備和 IoT 設備上運行 TensorFlow 模型。它支持設備端機器學習推斷,延遲較低,並且二進位文件很小。TensorFlow Lite 包括兩個主要組件:
轉換器一般在主電腦上完成,主要是為了靜態化計算圖,轉換權重類型後,生成 .tflite 文件。而解釋器主要在嵌入式設備上運行,我們這裡先在樹莓派上安裝一下 tensorflow lite。
1 建立虛擬環境
python3 -m venv --system-site-packages ~/my_envs/tf_litesource ~/my_envs/tf_lite/bin/activate2 安裝 2.1.0 版本
wget https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp37-cp37m-linux_armv7l.whlpip install tflite_runtime-2.1.0.post1-cp37-cp37m-linux_armv7l.whl3 啟用 opencv
cd ~/my_envs/tf_lite/lib/python3.7/site-packagesln -s /usr/local/lib/python3.7/site-packages/cv2 cv24 安裝完成
import tflite_runtime.interpreter as tflitetflite.Interpreter1 測試攝像頭
先保證攝像頭排線已插入樹莓派的 CSI 接口,並在 raspi-config 裡 Interfacing options -> camera 選項裡啟用了攝像頭。
# 拍攝照片raspistill -o demo.jpg# 錄製視頻raspivid -o vid.h264看一下目錄裡是否有拍攝的 demo.jpg 照片和 vid.h264 視頻文件,保證硬體沒有問題。
2 使用 picamera 控制攝像頭
拍攝一張照片
import picamerafrom time import sleep
camera = picamera.PiCamera()try: camera.resolution = (1024, 768) camera.brightness = 60 camera.start_preview() camera.annotate_text = 'Raspberry Pi' sleep(3) camera.capture('image1.jpeg')finally: camera.stop_preview() camera.close()一定要記得用完攝像頭後用 camera.close() 關閉,或是用 With 子句自動釋放資源。否則會收到 picamera.exc.PiCameraMMALError: Failed to enable connection: Out of resources 的錯誤信息,只能 Kill python 進程強制釋放了。
錄製一段視頻
with picamera.PiCamera() as camera: camera.resolution = (640, 480) camera.start_preview() camera.start_recording('video.h264') camera.wait_recording(10) camera.stop_recording()3 使用 opencv 控制攝像頭
import cv2
cap = cv2.VideoCapture(0)cap.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, 640)cap.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, 480)
while True: ret, frame = cap.read() cv2.imshow("capture", frame) if(cv2.waitKey(1) == ord('q')): cv2.destroyAllWindows() break如果在OpenCV中調用CSI攝像頭會出現無數據的現象,這是因為樹莓派中的camera module是放在/boot/目錄中以固件形式加載的,不是一個標準的V4L2的攝像頭驅動,所以加載起來之後會找不到/dev/video0的設備節點。我們在/etc/modules裡面添加一行bcm2835-v4l2 就能解決問題。
添加:bcm2835-v4l2,保存關閉即可。
4 查詢其他 usb 視頻設備
Jupyter Notebook是一個開源的Web應用程式,允許用戶創建和共享包含代碼、方程式、可視化和文本的文檔。廣泛的用於各種雲平臺上,為了後續代碼的可移植性,我們這裡先安裝一下。
1 安裝 jupyter
2 創建配置文件
創建Jupyter notebook的配置文件jupyter_notebook_config.py,在終端中輸入:
jupyter notebook --generate-configsudo nano ~/.jupyter/jupyter_notebook_config.py編輯配置文件 jupyter_notebook_config.py
c.NotebookApp.ip = '127.0.0.1'
c.NotebookApp.open_browser = True
這兩個去除注釋,監聽本機埠地址 127.0.0.1 即可。
3 配置虛擬環境到 jupyter 內
用 python -m ipykernel install --user --name 虛擬環境名 --display-name Jupyter中要顯示的名字,來綁定已有的虛擬環境。
python -m ipykernel install
python -m ipykernel install
python -m ipykernel install
python -m ipykernel install
python -m ipykernel install4 啟動 notebook,切換不同的環境
菜單裡 服務 -> 改變服務 -> 列出了所有的虛擬環境。
1 克隆官方示例
git clone https://github.com/tensorflow/examples --depth 12 圖像分類應用
首先安裝依賴包,再下載一個 MobileNet 模型文件和分類標籤文件到 tmp 目錄中。
cd examples/lite/examples/image_classification/raspberry_pibash download.sh /tmp3 在 notebook 中運行一下推理
我們新建一個 classify_picamera.ipynb 文件,讀入tflite模型,labels = load_labels('/tmp/labels_mobilenet_quant_v1_224.txt')interpreter = Interpreter('/tmp/mobilenet_v1_1.0_224_quant.tflite')interpreter.allocate_tensors()_, height, width, _ = interpreter.get_input_details()[0]['shape']調用 interpreter.invoke() 來推理,def classify_image(interpreter, image, top_k=1): """Returns a sorted array of classification results.""" set_input_tensor(interpreter, image) interpreter.invoke() output_details = interpreter.get_output_details()[0] output = np.squeeze(interpreter.get_tensor(output_details['index']))
# If the model is quantized (uint8 data), then dequantize the results if output_details['dtype'] == np.uint8: scale, zero_point = output_details['quantization'] output = scale * (output - zero_point)
ordered = np.argpartition(-output, top_k) return [(i, output[i]) for i in ordered[:top_k]]最後在 opencv 的窗口來展示一下分類結果。
with picamera.PiCamera(resolution=(640, 480), framerate=30) as camera: camera.start_preview() try: stream = io.BytesIO() for _ in camera.capture_continuous( stream, format='jpeg', use_video_port=True): stream.seek(0) image = Image.open(stream).convert('RGB').resize((width, height), Image.ANTIALIAS) start_time = time.time() results = classify_image(interpreter, image) elapsed_ms = (time.time() - start_time) * 1000 label_id, prob = results[0] camera.annotate_text = '%s %.2f\n%.1fms' % (labels[label_id], prob, elapsed_ms) data = np.frombuffer(stream.getvalue(), dtype=np.uint8) dst = cv2.imdecode(data, cv2.IMREAD_UNCHANGED) cv2.imshow("img", dst) stream.seek(0) stream.truncate(0) if(cv2.waitKey(1) == ord('q')): cv2.destroyAllWindows() break finally: camera.stop_preview() print('end')速度還是比較快的,130ms一幀,能達到大約是7-8fps,準確率也很高,可見提升還是很明顯的。這既得益於 MobileNet 的小巧,也得益於 tflite 的精簡模型的加速,可達到準實時的效果。
2 目標檢測應用
cd examples/lite/examples/object_detection/raspberry_pibash download.sh /tmp下載一個 MobileNet ssd v2 模型文件和 coco 標籤文件到 tmp 目錄中。
3 在 notebook 中運行一下推理
我們新建一個 detect_picamera.ipynb 文件,讀入tflite模型,labels = load_labels('/tmp/coco_labels.txt')interpreter = Interpreter('/tmp/detect.tflite')interpreter.allocate_tensors()_, input_height, input_width, _ = interpreter.get_input_details()[0]['shape']調用 interpreter.invoke() 來推理,def detect_objects(interpreter, image, threshold): """Returns a list of detection results, each a dictionary of object info.""" set_input_tensor(interpreter, image) interpreter.invoke()
boxes = get_output_tensor(interpreter, 0) classes = get_output_tensor(interpreter, 1) scores = get_output_tensor(interpreter, 2) count = int(get_output_tensor(interpreter, 3))
results = [] for i in range(count): if scores[i] >= threshold: result = { 'bounding_box': boxes[i], 'class_id': classes[i], 'score': scores[i] } results.append(result) return results獲得標註目標,轉換標註框和類別標籤
def annotate_objects(annotator, results, labels): """Draws the bounding box and label for each object in the results.""" for obj in results: ymin, xmin, ymax, xmax = obj['bounding_box'] xmin = int(xmin * CAMERA_WIDTH) xmax = int(xmax * CAMERA_WIDTH) ymin = int(ymin * CAMERA_HEIGHT) ymax = int(ymax * CAMERA_HEIGHT)
annotator.bounding_box([xmin, ymin, xmax, ymax]) annotator.text([xmin, ymin], '%s\n%.2f' % (labels[obj['class_id']], obj['score']))最後用 opencv 合併一下視頻流和標註層,在 cv2 窗口展示。
with picamera.PiCamera( resolution=(CAMERA_WIDTH, CAMERA_HEIGHT), framerate=30) as camera: camera.start_preview() try: stream = io.BytesIO() annotator = Annotator(camera) for _ in camera.capture_continuous( stream, format='jpeg', use_video_port=True): stream.seek(0) image = Image.open(stream).convert('RGB').resize( (input_width, input_height), Image.ANTIALIAS) start_time = time.monotonic() results = detect_objects(interpreter, image, 0.4) elapsed_ms = (time.monotonic() - start_time) * 1000
annotator.clear() annotate_objects(annotator, results, labels) annotator.text([5, 0], '%.1fms' % (elapsed_ms)) annotator.update() data = np.frombuffer(stream.getvalue(), dtype=np.uint8) dst = cv2.imdecode(data, cv2.IMREAD_UNCHANGED) dst2 = cv2.cvtColor(np.asarray(annotator._buffer),cv2.COLOR_RGB2BGR) dst = cv2.add(dst, dst2) cv2.imshow("img", dst) stream.seek(0) stream.truncate(0) if(cv2.waitKey(1) == ord('q')): cv2.destroyAllWindows() break
finally: camera.stop_preview()180-200ms一幀,5 fps左右,工作量上升了些,所以比分類任務稍慢了一些,這應該已達到樹莓派 4代的極限。若要再提升性能,就要用上 intel 神經榜或 google coral 這類 usb 擴展資源了。
下一篇
我們將在 tersorflow lite 上,
做一些有趣的應用,
敬請期待...