在Jetson nano使用训练模型进行目标检测

naka-kazz
1120 0 2020-12-27

第4回:训练模型转换和目标检测

这次,我们使用了SSD Mobilenet v2 coco的对象检测方法,该方法是使用COCO数据集进行训练的模型,可以检测90种目标,并且使用了Google Colab在第2部分中创建的原始学习模型。 介绍目标检测方法。

使用SSD_mobilenet_v2进行对象检测

首先,我想使用SSD Mobilenet v2 coco检测对象。


下载SSD Mobilenet v2 coco

首先,下载SSD Mobilenet v2 coco模型。 顺便说一句,训练后的模型称为“模型动物园”,可以从“ Tensor Flow 1 Detection Model Zoo”站点下载。

$ cd
$ wget http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz
・・・省略・・・
$


②解压缩档案 下载完成后,解压缩tar.gz存档。 您有一个类似于创建原始学习模型时的文件,例如Frozen_inference_graph.pb。 解压缩后,移动目录。


③创建用于uff转换的配置文件 Tensorflow pb文件在Jetson nano的TensorRT中无法按原样提供。 因此,您需要将TensorRT的pb文件转换为uff文件。 创建转换所需的配置文件“ config.py”,如下所示。 NVIDIA开发者论坛在此介绍了此内容。

import graphsurgeon as gs
import tensorflow as tf
 
Input = gs.create_node("Input",
    op="Placeholder",
    dtype=tf.float32,
    shape=[1, 3, 300, 300])
PriorBox = gs.create_plugin_node(name="GridAnchor", op="GridAnchor_TRT",
    numLayers=6,
    minSize=0.2,
    maxSize=0.95,
    aspectRatios=[1.0, 2.0, 0.5, 3.0, 0.33],
    variance=[0.1,0.1,0.2,0.2],
    featureMapShapes=[19, 10, 5, 3, 2, 1])
NMS = gs.create_plugin_node(name="NMS", op="NMS_TRT",
    shareLocation=1,
    varianceEncodedInTarget=0,
    backgroundLabelId=0,
    confidenceThreshold=1e-8,
    nmsThreshold=0.6,
    topK=100,
    keepTopK=100,
    numClasses=91,
    ###########################################
    #inputOrder=[0, 2, 1],
    inputOrder=[1, 0, 2],
    ###########################################
    confSigmoid=1,
    isNormalized=1,
    scoreConverter="SIGMOID")
concat_priorbox = gs.create_node(name="concat_priorbox", op="ConcatV2", dtype=tf.float32, axis=2)
concat_box_loc = gs.create_plugin_node("concat_box_loc", op="FlattenConcat_TRT", dtype=tf.float32, axis=1, ignoreBatch=0)
concat_box_conf = gs.create_plugin_node("concat_box_conf", op="FlattenConcat_TRT", dtype=tf.float32, axis=1, ignoreBatch=0)
 
namespace_plugin_map = {
    "MultipleGridAnchorGenerator": PriorBox,
    "Postprocessor": NMS,
    "Preprocessor": Input,
    "ToFloat": Input,
    "image_tensor": Input,
#   "MultipleGridAnchorGenerator/Concatenate": concat_priorbox,
    "Concatenate": concat_priorbox,
    "concat": concat_box_loc,
    "concat_1": concat_box_conf
}
 
def preprocess(dynamic_graph):
    all_assert_nodes = dynamic_graph.find_nodes_by_op("Assert")
    dynamic_graph.remove(all_assert_nodes, remove_exclusive_dependencies=True)
 
    all_identity_nodes = dynamic_graph.find_nodes_by_op("Identity")
    dynamic_graph.forward_inputs(all_identity_nodes)
 
    dynamic_graph.collapse_namespaces(namespace_plugin_map)
    dynamic_graph.remove(dynamic_graph.graph_outputs, remove_exclusive_dependencies=False)
    dynamic_graph.find_nodes_by_op("NMS_TRT")[0].input.remove("Input")


④转换为uff文件 创建config.py后,使用NVIDIA版本Tensorflow中包含的“ convert_to_uff.py”将pb文件转换为uff文件,如下所示。 我在这里跌跌撞撞,担心了大约两个星期

$ python3 /usr/lib/python3.6/dist-packages/uff/bin/convert_to_uff.py frozen_inference_graph.pb -o frozen_inference_graph.uff -O NMS -p config.py
・・・省略・・・
NOTE: UFF has been tested with TensorFlow 1.15.0.
WARNING: The version of TensorFlow installed on this system is not guaranteed to work with UFF.
UFF Version 0.6.9
=== Automatically deduced input nodes ===
[name: "Input"
op: "Placeholder"
attr {
  key: "dtype"
  value {
    type: DT_FLOAT
  }
}
attr {
  key: "shape"
  value {
    shape {
      dim {
        size: 1
      }
      dim {
        size: 3
      }
      dim {
        size: 300
      }
      dim {
        size: 300
      }


⑤nvinfer_custom_impl的副本 接下来,为SSD模型创建一个nvinfer自定义插件。 即使它是一个自定义插件,也按原样使用NVIDIA示例。 首先,复制NVIDIA源。 复制的源中的“ objectDetector_SSD”目录中存在nvinfer_custom_impl。

⑥建立nvinfer_custom_impl 然后构建nvinfer_custom_impl。 构建需要“ CUDA_VER”环境变量,因此请在构建之前进行设置。 如果构建成功,将创建“ libnvdsinfer_custom_impl_ssd.so”。 将创建的“ libnvdsinfer_custom_impl_ssd.so”复制到uff文件所在的目录。

$ export CUDA_VER=10.2
$ echo $CUDA_VER
10.2
 
$ make -C nvdsinfer_custom_impl_ssd
make: Entering directory '/home/test/ssd_mobilenet_v2_coco_2018_03_29/sources/objectDetector_SSD/nvdsinfer_custom_impl_ssd'
g++ -o libnvdsinfer_custom_impl_ssd.so nvdsparsebbox_ssd.cpp nvdsiplugin_ssd.cpp -Wall -Werror -std=c++11 -shared -fPIC -Wno-error=deprecated-declarations -I../../includes -I/usr/local/cuda-10.2/include -Wl,--start-group -lnvinfer -lnvparsers -L/usr/local/cuda-10.2/lib64 -lcudart -lcublas -Wl,--end-group
make: Leaving directory '/home/test/ssd_mobilenet_v2_coco_2018_03_29/sources/objectDetector_SSD/nvdsinfer_custom_impl_ssd'
 
$ ls -l nvdsinfer_custom_impl_ssd/
total 120
-rw-r--r-- 1 test test  1865 12月 12 19:18 Makefile
-rwxrwxr-x 1 test test 97888 12月 12 19:22 libnvdsinfer_custom_impl_ssd.so
-rw-r--r-- 1 test test 11515 12月 12 19:18 nvdsiplugin_ssd.cpp
-rw-r--r-- 1 test test  4670 12月 12 19:18 nvdsparsebbox_ssd.cpp
 
$ cp nvdsinfer_custom_impl_ssd/libnvdsinfer_custom_impl_ssd.so ../../
 


⑦创建标签文件 接下来,使用检测到的对象的名称创建一个标签文件。 您可以在此处下载COCO型号标签列表。 但是,似乎必须将第一行设置为“未定义”,因此请将“未定义”添加到第一行。

$ cd ../../
$ wget https://raw.githubusercontent.com/amikelive/coco-labels/master/coco-labels-paper.txt
$ ls -l coco-labels-paper.txt 
-rw-rw-r-- 1 test test 702 12月 12 21:42 coco-labels-paper.txt
$sed -i '1s/^/Undefined\n/' coco-labels-paper.txt
$

⑧创建nvinfer设置文件 如下创建nvinfer配置文件“ config_infer.txt”。 在此文件中,描述到目前为止准备的三个文件(分别是uff,label)的路径。 顺便说一下,模型引擎文件是自动生成的,因此目前没有文件。


⑨创建应用程序设置文件 最后,为Deep Stream应用程序创建一个配置文件“ app_config.txt”。 关键是配置文件部分。 请通过参考上一篇文章来自定义[source0]和[sink0]。


⑩确认所需文件 完成到目前为止的步骤后,您将拥有以下5个文件,因此让我们检查一下! !!


11.运行DeepStream应用 现在,让我们运行DeepStream应用程序并测试是否可以检测到对象!


以下是物体检测结果的视频。 可以检测到汽车,人和公共汽车!


这样就可以使用SSD Mobilenet v2 coco完成对象检测。

使用原始模型进行物体检测

从这里开始,我们将使用IBM Cloud Annotations进行注释,并使用通过Google Colab学习的原始模型执行对象检测。 该过程与SSD Mobilenet v2的过程相同,但是原始模型具有可检测到的不同数量的对象,因此需要更改此部分。 ①上传模型文件 首先,创建一个工作目录“ myModel”,上传在Google Colab中创建的模型,然后将其解压缩。 另外,上传测试视频文件“ input.mp4”。


②所需文件的副本 复制为SSD Mobilenet v2 coco创建的“ config.py”,“ app_config.txt”,“ config_infer.txt”文件和“ sources”目录。


③编辑配置文件以进行uff转换 首先,将“ config.py”的第23行上的“ numClasses = 91”描述更改为“ numClasses = 3”。 数字“ 3”是标签数+1。 在原始模型中,定义了两个标签“ Untitled Label”和“ flower”,因此为“ 3”。 另外,由于原始模型包含“ Cost”操作,并且会发生错误,因此请在“ config.py”的最后一行添加两行以删除“ Cast”操作。 

import graphsurgeon as gs
import tensorflow as tf
 
Input = gs.create_node("Input",
    op="Placeholder",
    dtype=tf.float32,
    shape=[1, 3, 300, 300])
PriorBox = gs.create_plugin_node(name="GridAnchor", op="GridAnchor_TRT",
    numLayers=6,
    minSize=0.2,
    maxSize=0.95,
    aspectRatios=[1.0, 2.0, 0.5, 3.0, 0.33],
    variance=[0.1,0.1,0.2,0.2],
    featureMapShapes=[19, 10, 5, 3, 2, 1])
NMS = gs.create_plugin_node(name="NMS", op="NMS_TRT",
    shareLocation=1,
    varianceEncodedInTarget=0,
    backgroundLabelId=0,
    confidenceThreshold=1e-8,
    nmsThreshold=0.6,
    topK=100,
    keepTopK=100,
    numClasses=3,
    ###########################################                                                                                      
    #inputOrder=[0, 2, 1],                                                                                                           
    inputOrder=[1, 0, 2],
    ###########################################                                                                                      
    confSigmoid=1,
    isNormalized=1,
    scoreConverter="SIGMOID")
concat_priorbox = gs.create_node(name="concat_priorbox", op="ConcatV2", dtype=tf.float32, axis=2)
concat_box_loc = gs.create_plugin_node("concat_box_loc", op="FlattenConcat_TRT", dtype=tf.float32, axis=1, ignoreBatch=0)
concat_box_conf = gs.create_plugin_node("concat_box_conf", op="FlattenConcat_TRT", dtype=tf.float32, axis=1, ignoreBatch=0)
 
namespace_plugin_map = {
    "MultipleGridAnchorGenerator": PriorBox,
    "Postprocessor": NMS,
    "Preprocessor": Input,
    "ToFloat": Input,
    "image_tensor": Input,
#   "MultipleGridAnchorGenerator/Concatenate": concat_priorbox, 
    "Concatenate": concat_priorbox,
    "concat": concat_box_loc,
    "concat_1": concat_box_conf
}
 
def preprocess(dynamic_graph):
    all_assert_nodes = dynamic_graph.find_nodes_by_op("Assert")
    dynamic_graph.remove(all_assert_nodes, remove_exclusive_dependencies=True)
 
    all_identity_nodes = dynamic_graph.find_nodes_by_op("Identity")
    dynamic_graph.forward_inputs(all_identity_nodes)
 
 
    dynamic_graph.collapse_namespaces(namespace_plugin_map)
    dynamic_graph.remove(dynamic_graph.graph_outputs, remove_exclusive_dependencies=False)
    dynamic_graph.find_nodes_by_op("NMS_TRT")[0].input.remove("Input")
    
    cast_nodes = dynamic_graph.find_nodes_by_op("Cast")
    dynamic_graph.remove(cast_nodes, remove_exclusive_dependencies=False)
 

④转换为uff文件

编辑“ config.py”后,将pb文件转换为uff文件。

$python3 /usr/lib/python3.6/dist-packages/uff/bin/convert_to_uff.py frozen_inference_graph.pb -o frozen_inference_graph.uff -O NMS -p config.py
・・・省略・・・
UFF Version 0.6.9
=== Automatically deduced input nodes ===
[name: "Input"
op: "Placeholder"
input: "Cast"
attr {
  key: "dtype"
  value {
    type: DT_FLOAT
  }
}
attr {
  key: "shape"
  value {
    shape {
      dim {
        size: 1
      }
      dim {
        size: 3
      }
      dim {
        size: 300
      }
      dim {
        size: 300
      }
    }
  }
}
]
=========================================
 
Using output node NMS
Converting to UFF graph
・・・省略・・・
No. nodes: 644
UFF Output written to frozen_inference_graph.uff
$ ls -l *.uff
-rw-rw-r-- 1 test test 18839555 12月 13 10:08 frozen_inference_graph.uff
$


⑤编辑nvinfer_custom_impl 然后编辑nvinfer_custom_impl。 将“ sources / objectDetector_SSD / nvdsinfer_custom_impl_ssd / nvdsparsebbox_ssd.cpp”的第52行上的“ NUM_CLASSES_SSD = 91;”重写为“ NUM_CLASSES_SSD = 3;”。


⑥建立nvinfer_custom_impl 重写后,构建并重新创建“ libnvdsinfer_custom_impl_ssd.so”,并将其复制到uff文件所在的目录中。

$ cd sources/objectDetector_SSD/
$ export CUDA_VER=10.2
$ make -C nvdsinfer_custom_impl_ssd
make: Entering directory '/home/test/myModel/sources/objectDetector_SSD/nvdsinfer_custom_impl_ssd'
g++ -o libnvdsinfer_custom_impl_ssd.so nvdsparsebbox_ssd.cpp nvdsiplugin_ssd.cpp -Wall -Werror -std=c++11 -shared -fPIC -Wno-error=deprecated-declarations -I../../includes -I/usr/local/cuda-10.2/include -Wl,--start-group -lnvinfer -lnvparsers -L/usr/local/cuda-10.2/lib64 -lcudart -lcublas -Wl,--end-group
make: Leaving directory '/home/test/myModel/sources/objectDetector_SSD/nvdsinfer_custom_impl_ssd'
 
$ ls -l nvdsinfer_custom_impl_ssd/
total 128
-rw-r--r-- 1 test test  1865 12月 13 10:02 Makefile
-rwxrwxr-x 1 test test 97888 12月 13 10:15 libnvdsinfer_custom_impl_ssd.so
-rw-r--r-- 1 test test 11515 12月 13 10:02 nvdsiplugin_ssd.cpp
-rw-r--r-- 1 test test  4669 12月 13 10:14 nvdsparsebbox_ssd.cpp
 
$ cp nvdsinfer_custom_impl_ssd/libnvdsinfer_custom_impl_ssd.so ../../
 
$ cd ../../


⑦创建标签文件 接下来,在与uff文件相同的目录中创建标签文件“ mylabels.txt”。 看起来如下。

⑧编辑nvinfer设置文件 由于标签文件的名称已更改,因此请在“ config_infer.txt”中更改标签文件的名称。 另外,更改输入视频的文件名。


⑨编辑应用程序设置文件 要更改为应用设置文件“ app_config.txt”,您只需更改要输入的视频的文件名。

#uri=file:///opt/nvidia/deepstream/deepstream-5.0/samples/streams/sample_1080p_h264.mp4
uri=file://./input.mp4


⑩确认所需文件 通过以上工作,确认准备了以下6个文件。


11.运行DeepStream应用 现在,让我们使用原始模型执行对象检测,这是本系列的最终目的! 让我们如下启动DeepStream应用程序!


以下是目标检测的视频。 已正确检测到“向日葵”! !! 我印象深刻