乐趣区

关于人工智能:用树莓派4b构建深度学习应用六TensorFlow-Lite篇

前言

上回咱们建设了 tensorflow 的开发环境,跑测试模型比拟不便,但对于树莓派来说,还是太宏大了。仅有 0.3fps 无奈满足实时的生产环境需要,这篇咱们部署一下专门为挪动端和嵌入式设施而生的 TensorFlow Lite,并跑几个模型,测试一下纯树莓派上的极限帧率。

TensorFlow Lite 是一组工具,可帮忙开发者在挪动设施、嵌入式设施和 IoT 设施上运行 TensorFlow 模型。它反对设施端机器学习推断,提早较低,并且二进制文件很小。

TensorFlow Lite 包含两个次要组件:

  • TensorFlow Lite 解释器,它可在手机、嵌入式 Linux 设施和微控制器等很多不同类型的硬件上运行通过专门优化的模型。
  • TensorFlow Lite 转换器,它可将 TensorFlow 模型转换为高效模式以供解释器应用,并可引入优化以减小二进制文件的大小和进步性能。

转换器个别在主电脑上实现,次要是为了动态化计算图,转换权重类型后,生成 .tflite 文件。而解释器次要在嵌入式设施上运行,咱们这里先在树莓派上装置一下 tensorflow lite。

装置 TensorFlow Lite

1. 建设虚拟环境

python3 -m venv --system-site-packages ~/my_envs/tf_lite
source ~/my_envs/tf_lite/bin/activate

2. 装置 2.1.0 版本

# 能够在官网里下载各个版本
# https://www.tensorflow.org/lite/guide/python
wget https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp37-cp37m-linux_armv7l.whl
pip install tflite_runtime-2.1.0.post1-cp37-cp37m-linux_armv7l.whl

3. 启用 opencv

cd ~/my_envs/tf_lite/lib/python3.7/site-packages
ln -s /usr/local/lib/python3.7/site-packages/cv2 cv2

4. 装置实现

import tflite_runtime.interpreter as tflite
tflite.Interpreter

配置树莓派摄像头

1. 测试摄像头

先保障摄像头排线已插入树莓派的 CSI 接口,并在 raspi-config 里 Interfacing options -> camera 选项里启用了摄像头。

# 拍摄照片
raspistill -o demo.jpg
# 录制视频
raspivid -o vid.h264

看一下目录里是否有拍摄的 demo.jpg 照片和 vid.h264 视频文件,保障硬件没有问题。

2. 应用 picamera 管制摄像头

pip install picamera

拍摄一张照片

import picamera
from 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()

Tip:

肯定要记得用完摄像头后用 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)
    # 按 q 退出
    if(cv2.waitKey(1) == ord('q')):
        cv2.destroyAllWindows()
        break

Tip:

如果在 OpenCV 中调用 CSI 摄像头会呈现无数据的景象,这是因为树莓派中的 camera module 是放在 /boot/ 目录中以固件模式加载的,不是一个规范的 V4L2 的摄像头驱动,所以加载起来之后会找不到 /dev/video0 的设施节点。咱们在 /etc/modules 外面增加一行 bcm2835-v4l2 就能解决问题。

$sudo nano /etc/modules

增加:bcm2835-v4l2,保留敞开即可。

4. 查问其余 usb 视频设施

ls -ltrh /dev/video*

应用 jupyter notebook

Jupyter Notebook 是一个开源的 Web 应用程序,容许用户创立和共享蕴含代码、方程式、可视化和文本的文档。宽泛的用于各种云平台上,为了后续代码的可移植性,咱们这里先装置一下。

1. 装置 jupyter

sudo apt install jupyter

2. 创立配置文件

创立 Jupyter notebook 的配置文件 jupyter_notebook_config.py,在终端中输出:

jupyter notebook --generate-config
sudo 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 内

pip install ipykernel

用 python -m ipykernel install –user –name 虚拟环境名 –display-name Jupyter 中要显示的名字,来绑定已有的虚拟环境。

# 配置 pytorch 环境
python -m ipykernel install --user --name pytorch --display-name pytorch

# 配置 opencv 环境
python -m ipykernel install --user --name opencv --display-name opencv

# 配置 tensorflow 环境
python -m ipykernel install --user --name tensorflow --display-name tensorflow

# 配置 tensorflow 2.x 环境
python -m ipykernel install --user --name tf2 --display-name tf2

# 配置 tf_lite 环境
python -m ipykernel install --user --name tf_lite --display-name tf_lite

4. 启动 notebook,切换不同的环境

jupyter notebook

菜单里 服务 -> 扭转服务 -> 列出了所有的虚拟环境。

Tensorflow Lite 利用

1. 克隆官网示例

git clone https://github.com/tensorflow/examples --depth 1

2. 图像分类利用

首先装置依赖包,再下载一个 MobileNet 模型文件和分类标签文件到 tmp 目录中。

cd examples/lite/examples/image_classification/raspberry_pi
bash download.sh /tmp

3. 在 notebook 中运行一下推理

jupyter 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)
        # print(camera.annotate_text)
        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 的精简模型的减速,可达到准实时的成果。

4. 指标检测利用

cd examples/lite/examples/object_detection/raspberry_pi
bash download.sh /tmp

下载一个 MobileNet ssd v2 模型文件和 coco 标签文件到 tmp 目录中。

5. 在 notebook 中运行一下推理

jupyter 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()

  # Get all output details
  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:
    # Convert the bounding box figures from relative coordinates
    # to absolute coordinates based on the original resolution
    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)

    # Overlay the box, label, and score on the camera preview
    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 扩大资源了。

安装包和源码下载

本期相干文件材料,可在公众号后盾回复:“rpi06”,获取下载链接。


下一篇预报

咱们将在 tersorflow lite 上,
做一些乏味的利用,
敬请期待 …


退出移动版