Tensorflow无人车使用移动端的SSD(单发多框检测)来识别物体及Graph的认识

news2024/11/27 7:43:06

环境是树莓派3B+,当然这里安装tensorflow并不是一定要在树莓派环境,只需要是ARM架构就行,也就是目前市场上绝大部分的嵌入式系统都是用这套精简指令集。
在电脑端的检测,有兴趣的可以查阅SSD(Single Shot MultiBox Detector)系列:计算机视觉之SSD改进版本(平滑L1范数损失与焦点损失)《4》

操作系统属于Linux,所以我们来熟悉下自己的硬件环境,主要就是查看自己的芯片架构是属于哪种。

1、Linux查看芯片架构

前面提到了需要在ARM架构里面安装,如何通过指令来查看当前是什么芯片架构。
因为都是在Linux系统中,所以输入的命令都是一样的,下面三条命令中的任意一个都可以查看到所属架构: 

arch
uname -a
file /bin/bash

X86图(我这是安装的WSL):

这里是X86_64,就是X86的64位扩展,最开始是AMD设计的,所以也叫做“AMD64”,后来被Intel采用,它的就叫做“Intel64”
ARM图:

这里的aarch64ARMv8-A架构中引入的64位指令集 ,属于ARM架构。
还有一种Linux系统的收银机,由于占用资源少,使用的是低配的芯片即可,一般是“奔腾”,返回的是i686 

2、安装环境

安装tensorflow,当然这个版本根据自己情况选择,目前最新版本是tensorflow-2.4.0-cp35-none-linux_armv6l.whl
# https://github.com/lhelontra/tensorflow-on-arm/releases
pip install tensorflow-1.8.0-cp27-none-linux_armv7l.whl

2.1、SSD移动端模型

#http://download.tensorflow.org/models/object_detection/ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz
tar -xzvf ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz .

2.2、安装OpenCV视觉库

pip install opencv-python

2.3、运行Demo,命令行运行

python opencv_camera.py
# 或者如果是在Jupyter中加载.py文件,会将其内容拷贝过来,点击运行
%load opencv_camera.py

3、物体识别

接下来我们就来实际操作下,让摄像头检测对象并标注对象类别,这里使用的是CSI摄像头,如果你的是USB接口,代码也有注释。

3.1、导入相关库

导入相关的库,其中的object_detection等源码在最后给出了地址,有兴趣测试的,可以clone下来玩玩。

import numpy as np
import cv2
import os,time
import tensorflow as tf
from object_detection.utils import label_map_util
from object_detection.utils import visualization_utils as vis_utils
import ipywidgets.widgets as widgets
from image_fun import bgr8_to_jpeg

3.2、摄像头初始化

摄像头初始化,这里也给出了USB接口的摄像头,我这里就借用无人车上面的摄像头,为CSI接口。如果你的摄像头是USB的情况,
可以通过 ls /dev/video* 命令查看设备索引,camera = USBCamera(capture_device=1)

#from jetcam.usb_camera import USBCamera
from jetcam.csi_camera import CSICamera
from jetcam.utils import bgr8_to_jpeg

#camera = USBCamera(width=320, height=240, capture_fps=30)
camera = CSICamera(width=320, height=240, capture_fps=30)
#将相机设置为running = True,为新帧附加回调
camera.running = True

3.3、安装JetCam

当然在这里如果没有安装JetCam,可以先进行安装。JetCam是NVIDIA Jetson的一个易于使用的Python相机接口,安装jetcam

git clone https://github.com/NVIDIA-AI-IOT/jetcam
cd jetcam

sudo python3 setup.py install

3.4、Image显示组件

摄像头初始化好了之后,我们就在Jupyter中新建一个图片组件,用来更新摄像头拍摄过来的数据

image_widget = widgets.Image(format='jpg', width=320, height=240)
display(image_widget)
image_widget.value = bgr8_to_jpeg(camera.value)

运行之后,这里会出现画面,不过是静止的第一帧,想要持续的更新,就会用到后面的两种更新方法。

3.5、初始化模型

将轻量级的SSD模型与标签加载进来

MODEL_NAME = 'ssdlite_mobilenet_v2_coco_2018_05_09'
PATH_TO_CKPT = MODEL_NAME + '/frozen_inference_graph.pb' 
PATH_TO_LABELS = os.path.join('data', 'mscoco_label_map.pbtxt')

NUM_CLASSES = 90
IMAGE_SIZE = (12, 8)
fileAlreadyExists = os.path.isfile(PATH_TO_CKPT)

if not fileAlreadyExists:
    print('Model does not exsist !')
    exit

3.6、加载Graph

这里是加载上面的计算图,是一种冻结的序列化图,意思就是说不能训练,就是说拿来做预测的,是一个预训练模型。
另外也加载标签图,将这些标签进行分类,做成一个id与name对应的字典类型。后面将详细介绍Graph的用法!

print('Loading...')
detection_graph = tf.Graph()
with detection_graph.as_default(): #语句下定义属于计算图detection_graph的张量和操作
    od_graph_def = tf.compat.v1.GraphDef()
    with tf.io.gfile.GFile(PATH_TO_CKPT, 'rb') as fid: 
        serialized_graph = fid.read()
        od_graph_def.ParseFromString(serialized_graph)
        tf.import_graph_def(od_graph_def, name='')
label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True) 
category_index = label_map_util.create_category_index(categories)
print('Finish Load Graph..')

print(len(category_index),category_index)
print(category_index[1]['name'])#person

这里的category_index是一个80个分类的字典,类似于 {1: {'id': 1, 'name': 'person'}, 2: {'id': 2, 'name': 'bicycle'},...} 如下图:

3.7、摄像头检测

调用摄像头实时检测的方法有两种,一种是一直循环的读取摄像头的值,也就是在死循环里面处理;另外一种就是通过回调函数,当摄像头的值有变化的时候就会自动更新值到图片组件里面。 

3.7.1、死循环 

    # Main
    t_start = time.time()
    fps = 0

    with detection_graph.as_default():
        with tf.compat.v1.Session(graph=detection_graph) as sess:
            while True:
                frame = camera.value
               # ret, frame = cap.read()
    #            frame = cv2.flip(frame, -1) # Flip camera vertically
    #             frame = cv2.resize(frame,(320,240))
                ##############
                image_np_expanded = np.expand_dims(frame, axis=0)
                image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
                detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
                detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
                detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
                num_detections = detection_graph.get_tensor_by_name('num_detections:0')
    #             print('Running detection..')
                (boxes, scores, classes, num) = sess.run( 
                    [detection_boxes, detection_scores, detection_classes, num_detections], 
                    feed_dict={image_tensor: image_np_expanded})
    #             print('Done.  Visualizing..')
                vis_utils.visualize_boxes_and_labels_on_image_array(
                        frame,
                        np.squeeze(boxes),
                        np.squeeze(classes).astype(np.int32),
                        np.squeeze(scores),
                        category_index,
                        use_normalized_coordinates=True,
                        line_thickness=8)
        
                for i in range(0, 10):
                    if scores[0][i] >= 0.5:
                        print(category_index[int(classes[0][i])]['name'])
                ##############
                fps = fps + 1
                mfps = fps / (time.time() - t_start)
                cv2.putText(frame, "FPS " + str(int(mfps)), (10,10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 2)
                image_widget.value = bgr8_to_jpeg(frame)

3.7.2、回调函数

创建一个更新函数,当摄像头启动时就调用它,这样就可以做到摄像头的值变化时,更新函数就会做出处理。一般推荐使用这种方法!

detection_graph.as_default()
sess = tf.compat.v1.Session(graph=detection_graph)
t_start = time.time()
fps = 0

def update_image(change):
    global fps
    global sess
    frame = change['new']
    image_np_expanded = np.expand_dims(frame, axis=0)
    image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
    detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
    detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
    detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
    num_detections = detection_graph.get_tensor_by_name('num_detections:0')

    (boxes, scores, classes, num) = sess.run(
        [detection_boxes, detection_scores, detection_classes, num_detections],
        feed_dict={image_tensor: image_np_expanded})

    # 锚框与标签
    vis_utils.visualize_boxes_and_labels_on_image_array(
        frame,
        np.squeeze(boxes),
        np.squeeze(classes).astype(np.int32),
        np.squeeze(scores),
        category_index,
        use_normalized_coordinates=True,
        line_thickness=8)
    '''
    for i in range(0, 10):
        if scores[0][i] >= 0.5:
            print(category_index[int(classes[0][i])]['name'])
    '''

    fps = fps + 1
    mfps = fps / (time.time() - t_start)
    cv2.putText(frame, "FPS " + str(int(mfps)), (10,10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 2)
    image_widget.value = bgr8_to_jpeg(frame)

#摄像头更新函数
camera.observe(update_image, names='value')

#停止更新,单纯的unobserve不能停止摄像头的继续运行,我加了一个stop停止
#camera.unobserve(update_image, names='value')
#camera.stop()

效果图如下:

当然也存在识别有误的情况,比如下面这张图,将后面的烟,识别成了书,这个也正常,跟数据集有关,毕竟看起来像书的陈列形状。

4、tf.Graph()

这里对上述代码中出现的tf.Graph函数做一个相关介绍,它是Tensorflow中的计算图,对里面张量有专门的计算模块,还可以新建多张计算图,通常表示为G(V,E),其中G表示一个图,V是图G中顶点的集合,E是图G中边的集合。这样的构建就可以并行化处理,对于提升性能很有帮助。
Graph实质是一种集合数组(Tensor张量)与Operation操作的数据结构,可以运行在非Python环境中,只需要对这个数据结构进行解释即可。所以说通过构建Graph,我们可以方便地表示和优化计算过程,从而实现深度学习模型的训练和推断。
一旦Graph被构建完成,我们就可以使用TensorFlow的Session对象来执行这个Graph。在执行过程中,Session会按照Graph中定义的节点和边的关系,将数据从一个节点传递到另一个节点,并执行每个节点的操作,最终得到我们需要的计算结果。
我们来看一个例子:

g1 = tf.Graph()
g2 = tf.Graph()

with g1.as_default():
    a = tf.constant([1,2])
    b = tf.constant([3,4])
    c = tf.constant([5,6])
    result1 = a + b + c

with g2.as_default():
    d = tf.constant([11,22,33])
    e = tf.constant([33,44,55])
    result2 = d * e

定义好两个计算图之后,我们就可以分别在其下面计算,然后通过Sessionrun来执行计算的结果

with tf.compat.v1.Session(graph=g1) as sess:
    print(a)#Tensor("Const_12:0", shape=(2,), dtype=int32)
    print(result1)#Tensor("add_8:0", shape=(2,), dtype=int32)
    print(sess.run(result1))#[ 9 12]

可以看到在g1计算图下面,我们可以查看计算节点与计算节点,这里是做相加的操作,接着看下g2做相乘的操作:

with tf.compat.v1.Session(graph=g2) as sess:
    print(d)#Tensor("Const_12:0", shape=(3,), dtype=int32)
    print(result2)#Tensor("mul:0", shape=(3,), dtype=int32)
    print(sess.run(result2))#[ 363  968 1815]

我们也可以对这些张量的计算图属于哪一个做一个验证:

print(a.graph is g1)#True
print(a.graph is g2)#False
print(d.graph is g1)#False
print(d.graph is g2)#True

4.1、run()中fetches参数

当然这个run函数里面的fetches参数值除了上面求和求积操作,还可以是单元素,列表,元组

import tensorflow as tf

sess = tf.compat.v1.Session()
a = tf.constant([10, 20])
b = tf.constant([1.0, 2.0])

v = sess.run(a)
print(type(v),v)#<class 'numpy.ndarray'> [10 20]

v = sess.run([a, b])
print(v)#[array([10, 20], dtype=int32), array([1., 2.], dtype=float32)]

当然也可以是字典,里面的a,b值将转成numpy数组

import collections
MyData = collections.namedtuple('MyData', ['a', 'b'])
v = sess.run({'k1': MyData(a, b), 'k2': [b, a]})
print(v)
#{'k1': MyData(a=array([10, 20], dtype=int32), b=array([1., 2.], dtype=float32)), 'k2': [array([1., 2.], dtype=float32), array([10, 20], dtype=int32)]}

4.2、run()中feed_dict参数

这个feed_dict参数的输入值需要是字典类型:

v = sess.run([a, b],feed_dict={b:[33,44]})
print(v)
#[array([10, 20], dtype=int32), array([33., 44.], dtype=float32)]

更详细源码:https://github.com/yihangzhao/SSDMobile

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/780301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringCloudAlibaba微服务实战系列(四)Sentinel熔断降级、异常fallback、block细致处理

SpringCloudAlibaba Sentinel降级和熔断 接着上篇文章的内容&#xff0c;在Sentinel中如何进行降级和熔断呢&#xff1f; 熔断降级规则 降级规则 在Sentinel中降级主要有三个策略&#xff1a;RT、异常比例、异常数&#xff0c;也是针对某个资源的设置。而在1.8.0版本后RT改为…

python post请求编码问题

在日常的python使用中&#xff0c;必不可少的就是爬虫相关的知识。 爬虫也常会遇到一个问题&#xff0c;即就是编码的问题。 如下&#xff1a; 通过抓包可以看到额&#xff0c;实际python提交的参数为&#xff0c;如下格式&#xff1a; 那这种的签名必不可能通过&#xff0…

FPGA实现串口回环

文章目录 前言一、串行通信1、分类1、同步串行通信2、异步串行通信 2、UART串口通信1、UART通信原理2、串口通信时序图 二、系统设计1、系统框图2.RTL视图 三、源码1、串口发送模块2、接收模块3、串口回环模块4、顶层模块 四、测试效果五、总结六、参考资料 前言 环境&#xff…

【框架篇】Spring Boot核心介绍及项目创建(详细教程)

Spring Boot介绍及项目创建 一&#xff0c;Spring Boot 核心介绍 Spring Boot 是基于 Spring 开发的一种轻量级的全新框架&#xff0c;不仅继承了 Spring 框架原有的优秀特性&#xff0c;而且还通过简化配置来进一步简化了 Spring 应用的整个搭建和开发过程。通过Spring Boot&…

NO.453 最小操作次数使数组元素相等

题目 给你一个长度为 n 的整数数组&#xff0c;每次操作将会使 n - 1 个元素增加 1 。返回让数组所有元素相等的最小操作次数。 思路 本题要求&#xff0c;获取最小操作次数&#xff0c;即在满足所有元素均相等的情况下&#xff0c;操作次数最少。 由于本题无法确定最终元素…

火车头采集器伪原创插件【php源码】

火车头采集是一款基于Python语言开发的网络爬虫工具&#xff0c;用于快速高效地从互联网上采集数据并存储到本地或远程数据库。它简单易用且功能强大&#xff0c;在各行各业广泛应用。 火车头采集器AI伪原创PHP源码&#xff1a; <?php header("Content-type: text/h…

(四)springboot实战——springboot的事件与监听器原理

前言 本节内容是关于springboot的一些核心原理的总结&#xff0c;包括springboot的事件原理、生命周期流程、事件触发流程等核心内容的介绍&#xff0c;从而帮助我们更好的理解与使用springboot&#xff0c;这里只做概念性的内容总结&#xff0c;实战的部分请关注作者后续博客…

数学建模-分类模型 Fisher线性判别分析

论文中1. 判别分析系数 2. 分类结果 多分类问题 勾选内容和上面一样

06微分方程模型练习

用Matlab求解微分方程 y ′ − 2 y 2 x 2 2 x , y ( 0 ) 1 y-2y2x^{2}2x,y\left( 0\right) 1 y′−2y2x22x,y(0)1 y1 dsolve(Dy-2*y2*x.^22*x) y2 dsolve(Dy-2*y2*x.^22*x,y(0)1,x)用Matlab分别求解微分方程 y ′ − 2 y 2 x 2 2 x , y ( 0 ) 1 y-2y2x^{2}2x,y\left…

C语言动态内存管理(二)经典笔试题

第二篇内容为大家详细剖析关于动态内存管理的几个经典笔试题 目录 四、笔试题1.请问运行Test函数会有什么样的结果&#xff1f;结果&#xff1a;结果运行出错的原因&#xff1a;本题目注意点&#xff1a;改正该题目的错误&#xff1a;正确修改1&#xff1a;&#xff08;利用传值…

Electron入门学习_使用预加载脚本

学习网址&#xff1a; https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-preload 1.什么是预加载脚本 Electorn 的主进程是一个拥有完全操作系统访问权限的Node.js ,除了Electron 模组&#xff0c;之外&#xff0c;您也可以访问Node.js 内置模块和所有通过npm安装…

质效两全:媒体服务的创新“顶设”

做媒体服务&#xff0c;一定要有刻入骨髓的抽象思维。 视频化浪潮汹涌、生成式人工智能AIGC极速迭代、体验需求和应用场景愈发多样......面对“视频生产力”的变革&#xff0c;我们能否透过纷繁复杂的表象&#xff0c;洞察音视频行业的“真正需求”&#xff1f; 是否存在一套…

【mysql数据库】MySQL7在Centos7的环境安装

说明&#xff1a; 安装与卸载中&#xff0c;用户全部切换成为root&#xff0c;⼀旦安装&#xff0c;普通用户就能使用。初期练习&#xff0c;mysql不进行用户管理&#xff0c;全部使⽤root进⾏&#xff0c;尽快适应mysql语句&#xff0c;后⾯学了用户管理&#xff0c;在考虑新…

matlab dot()函数求矩阵内积,三维 ,多维 详解

matlab dot()函数求矩阵内积&#xff0c;三维 &#xff0c;多维 详解 Cdot(A,b,X)&#xff0c;这个参数X 只能取1,或者2。1 表示按列&#xff0c;2表示按行&#xff0c;如果没有参数。默认按列。 1&#xff09;按列优先计算 Cdot(A,B)dot(A,B,1)[a1*b1a4*b4 ,a2*b2a5*b5 ,a…

html,css初学

安装VSCODE ,插件&#xff1a;live server &#xff0c;html support html 然后为了更好地理解&#xff0c;请逐步输入&#xff0c;并及时查看效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>D…

A--自动收小麦机--2023河南萌新联赛第(二)场:河南工业大学

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 示例1 输入 4 1 2 1 1 4 5 2 2 2 3 4 输出 10 说明 在第4格放出水流后&#xff0c;水流会流向第3格&#xff0c;由于第3格高度比第4格低&#xff0c;所以水流继续向左流向第2格&#xff0…

爬虫的编解码方式

get请求的quote方法 我们在对爬取一个网页的时候&#xff0c;我们复制了这个网页的地址&#xff0c;但我们发现在将他粘贴下来以后不会是汉字&#xff0c;而是一串字符&#xff0c;这时候&#xff0c;我们需要去对字符进行编码&#xff0c;以便于我们能够继续去爬取网页。 例…

markdown编写数学公式

在计算机这一块&#xff0c;我们肯定会接触到数学&#xff0c;数学中又包含很多公式&#xff0c;但是到现在&#xff0c;手写这些公式应该不陌生&#xff0c;但是如果让你电脑敲出来&#xff0c;你绝对很懵逼&#xff0c;这也造成了我们有时候写笔记时一些公式没办法在电脑上像…

ffmpeg离线安装ffmpeg-4.1.4-amd64-static.tar.xz

1.下载离线包 John Van Sickle - FFmpeg Static Builds 找历史版本&#xff1a;Index of /ffmpeg/old-releases 我选择是的4.1.4版本 2.解压 tar -xvJf ffmpeg-4.1.4-amd64-static.tar.xz 3.移动文件到opt目录下 4.添加全局链接 ln -s /opt/ffmpeg-4.1.4-amd64-static/ffm…

【C++基础(五)】类和对象(上)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C初阶之路⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 类和对象-上 1. 前言2. 类的引入3. 类的定义4. 类的…