作者 |Adam Geitgey
编译 |Flin
起源 |medium
新的 Nvidia Jetson Nano 2GB 开发板(明天发表!)是一款单板机,售价 59 美元,运行带有 GPU 减速的人工智能软件。
到 2020 年,你能够从一台售价 59 美元的单板计算机中取得令人惊叹的性能。让咱们用它来创立一个门铃摄像头的简略版本,该摄像头能够跟踪走到屋宇前门的每个人。通过面部辨认,即便这些人衣着不同,它也能够立刻晓得你家门口的人是否已经来访问过你。
什么是 Nvidia Jetson Nano 2GB?
Jets o n Nano 2GB 是一款单板计算机,具备四核 1.4GHz ARM CPU 和内置的 Nvidia Maxwell GPU。它是最便宜的 Nvidia Jetson 机型,针对的是购买树莓派的业余爱好者。
如果你曾经相熟树莓派产品系列,则除了 Jetson Nano 装备 Nvidia GPU 外,这是和其余产品简直完全相同的。它能够运行 GPU 减速的应用程序(如深度学习模型),其速度远比树莓派这样的板(不反对大多数深度学习框架的 GPU)快得多。
那里有很多 AI 开发板和加速器模块,但 Nvidia 领有一大劣势——它与桌面 AI 库间接兼容,不须要你将深度学习模型转换为任何非凡格局即可运行他们。
它应用简直所有每个基于 Python 的深度学习框架都已应用的雷同的 CUDA 库进行 GPU 减速。这意味着你能够采纳现有的基于 Python 的深度学习程序,简直无需批改就能够在 Jetson Nano 2GB 上运行它,并且依然能够取得良好的性能(只有你的应用程序能够在 2GB 的 RAM 上运行)。
它将为弱小的服务器编写的 Python 代码部署在价格为 59 美元的独立设施上的能力十分杰出。
这款新的 Jetson Nano 2GB 主板也比 Nvidia 以前的硬件版本更加光鲜亮丽。
第一个 Jetson Nano 机型莫名其妙地短少 WiFi,但该机型随附一个可插入的 WiFi 模块,因而你不用再加上芜杂的以太网电缆了。他们还将电源输出降级到了更古代的 USB- C 端口,并且在软件方面,一些毛糙的边缘已被磨掉。例如,你无需执行诸如启用交换文件之类的基本操作。
Nvidia 踊跃地推出了一款价格低于 60 美元的带有实在 GPU 的简略易用的硬件设施。仿佛他们正以此为指标瞄准树莓派,并试图霸占教育 / 爱好者市场。看看市场如何反馈将是很乏味的。
让咱们组装零碎
对于任何硬件我的项目,第一步都是收集咱们须要的所有整机:
1. Nvidia Jetson Nano 2GB 主板(59 美元)
这些板目前可预订(截至 2020 年 10 月 5 日),预计将于 10 月底公布。
- 在此处预订:https://nvda.ws/30v5w3M
我不晓得发行后的初始可用性会如何,然而先前的 Jetson Nano 机型在发行后的几个月中供不应求。
全面披露:我从英伟达取得了收费的 Jetson Nano 2GB 开发板作为评估单元,但我与英伟达没有财务或编辑关系。这就是我可能提前编写本指南的形式。
2. USB- C 电源适配器(你可能曾经有一个?)
新型 Jetson Nano 2GB 应用 USB- C 供电。不包含电源适配器,然而你可能曾经有一个电源适配器了。
3. 摄像头—— USB 网络摄像头(你可能有一个?)或树莓派摄像头模块 v2.x(约 30 美元)
如果你心愿将小型相机装置在机壳中,那么树莓派相机模块 v2.x 是一个不错的抉择(留神:v1.x 相机模块将无奈应用)。你能够在 Amazon 或各种经销商处取得它们。
一些 USB 网络摄像头(如 Logitech 的 C270 或 C920)也能够在 Jetson Nano 2GB 上失常工作,因而如果你曾经领有一个 USB 摄像头,也能够拿来应用。这里有一个摄像头的不残缺清单。
- https://elinux.org/Jetson_Nan…
在购买新产品之前,请不要胆怯尝试摆放任何 USB 设施。并非所有性能都反对 Linux 驱动程序,但有些性能会反对。我插入了在亚马逊上买的价值 20 美元的通用 HDMI 到 USB 适配器,它工作得很好。因而,我无需任何额定配置就能够将我的高端数码相机用作通过 HDMI 的视频源。
- 购买链接:https://www.amazon.com/Eterma…
你还须要其余一些货色,然而你可能曾经筹备好了:
至多具备 32GB 空间的 microSD 卡。咱们将在此装置 Linux。你能够重复使用现有的任何 microSD 卡。
一个 microSD 卡阅读器:以便你能够装置 Jetson 软件。
一个有线 USB 键盘和一个有线 USB 鼠标 管制 Jetson Nano。
任何间接承受 HDMI(而不是通过 HDMI-DVI 转换器)的 监视器 或电视,你都能够看到本人在做什么。即便当前不应用显示器运行 Jetson Nano 初始设置,也须要一个监视器。
加载 Jetson Nano 2GB 软件
在开始将货色插入 Jetson Nano 之前,你须要下载 Jetson Nano 的软件映像。
Nvidia 的默认软件映像包含预装了 Python 3.6 和 OpenCV 的 Ubuntu Linux 18.04。
以下是将 Jetson Nano 软件装置到 SD 卡上的办法:
-
从 Nvidia 下载 Jetson Nano Developer Kit SD 卡映像。
- https://developer.nvidia.com/…
- 下载 Etcher,该程序将 Jetson 软件映像写入 SD 卡。
- https://www.balena.io/etcher/
- 运行 Etcher 并应用它来编写下载到 SD 卡的 Jetson Nano Developer Kit SD 卡映像。这大概须要 20 分钟。
是时候将其余的硬件拆箱了!
插入所有整机
首先,请拿出你的 Jetson Nano 2GB:
第一步是插入 microSD 卡。microSD 卡插槽已齐全暗藏,但你能够在散热器底部底部的反面找到它:
你还应该持续将随附的 USB WiFi 适配器插入以下 USB 端口之一:
接下来,你须要插入相机。
如果你应用的是树莓派 v2.x 相机模块,则它会通过带状电缆连贯。在 Jetson 上找到带状电缆插槽,弹出连接器,插入电缆,而后将其弹出敞开。确保带状电缆上的金属触点向内朝向散热器:
如果你应用 USB 网络摄像头,只需将其插入 USB 端口之一,而疏忽带状电缆端口。
当初,插入其余所有整机:
将鼠标和键盘插入 USB 端口。
应用 HDMI 电缆插入显示器。
最初,插入 USB- C 电源线以启动它。
如果你应用的是树莓派相机模块,则最终会失去如下所示的内容:
或者,如果你应用的是 USB 视频输出设施,它将看起来像这样:
插入电源线后,Jetson Nano 会主动启动。几秒钟后,你应该会看到 Linux 设置屏幕呈现在监视器上。请依照以下步骤创立你的帐户并连贯到 WiFi。非常简单。
装置 Linux 和 Python 库以进行人脸识别
一旦实现了 Linux 的初始设置,就须要装置几个咱们将在人脸识别零碎中应用的库。
在 Jetson Nano 桌面上,关上一个 LXTerminal 窗口并运行以下命令。每次要求输出明码时,请输出创立用户帐户时输出的明码:
sudo apt-get update
sudo apt-get install python3-pip cmake libopenblas-dev liblapack-dev libjpeg-dev
首先,咱们要更新 apt,这是规范的 Linux 软件装置工具,咱们将应用它来装置所需的其余零碎库。
而后,咱们将装置一些尚未事后装置咱们软件须要的 linux 库。
最初,咱们须要装置 face_recognition Python 库及其依赖项,包含机器学习库 dlib。你能够应用以下单个命令主动执行此操作:
sudo pip3 -v install Cython face_recognition
因为没有可用于 Jetson 平台的 dlib 和 numpy 的预构建正本,所以此命令将从源代码编译这些库。因而,趁此机会吃个午餐,因为这可能须要一个小时的工夫!
当最终实现时,你的 Jetson Nano 2GB 就能够通过残缺的 CUDA GPU 减速进行人脸识别。持续下一个乏味的局部!
运行面部辨认门铃摄像头演示应用程序
face_recognition 库是我编写的一个 Python 库,它使得应用 DLIB 做人脸识别超级简略。它使你可能检测到面部,将每个检测到的面部转换为惟一的面部编码,而后比拟这些面部编码以查看它们是否可能是同一个人——只需几行代码即可。
- face_recognition 库:https://github.com/ageitgey/f…
- dlib:http://dlib.net/
应用该库,我构建了一个门铃摄像头应用程序,该应用程序能够辨认走到你的前门并在人每次回来时对其进行跟踪的人。运行时看起来像这样:
首先,请下载代码。我曾经在此处增加了残缺的代码和正文。
- https://gist.github.com/ageit…
然而这里有一个更简略的办法能够从命令行下载到你的 Jetson Nano 上:
wget -O doorcam.py tiny.cc/doorcam2gb
在程序的顶部,你须要编辑一行代码以通知你是应用 USB 相机还是树莓派相机模块。你能够像这样编辑文件:
gedit doorcam.py
依照阐明进行操作,而后保留它,退出 GEdit 并运行代码:
python3 doorcam.py
你会在桌面上看到一个视频窗口。每当有新人走到摄像机前时,它都会记录他们的脸并开始跟踪他们在你家门口的工夫。如果同一个人来到并在 5 分钟后回来,它将从新注册并再次跟踪他们。你能够随时按键盘上的“q”退出。
该应用程序会主动将看到的每个人的信息保留到一个名为 known_faces.dat 的文件中。当你再次运行该程序时,它将应用该数据记住以前的访问者。如果要革除已知脸孔的列表,只需退出程序并删除该文件。
将其变成独立的硬件设施
至此,咱们有一个运行人脸识别模型的开发板,但它仍被解放在桌面上,以实现弱小的性能和显示成果。让咱们看看无需插入如何运行它。
古代单板计算机的一件很酷的事件是,它们简直都反对雷同的硬件规范,例如 USB。这意味着你能够在亚马逊上买到很多便宜的附件,例如触摸屏显示器和电池。你有很多输出,输入和电源选项。这是我订购的货色(但相似的货色都能够):
一个 7 英寸触摸屏 HDMI 显示屏,应用 USB 电源:
以及一个通用的 USB- C 电池组来供电:
让咱们将其连接起来,看看作为独立设施运行时的外观。只需插入 USB 电池而不是壁式充电器,而后将 HDMI 显示器插入 HDMI 端口和 USB 端口,即可充当屏幕和鼠标输出。
成果很好。触摸屏能够像一般的 USB 鼠标一样操作,无需任何其余配置。惟一的毛病是,如果 Jetson Nano 2GB 耗费的电量超过 USB 电池组可提供的电量,则会升高 GPU 的速度。然而它依然运行良好。
有了一点点创意,你就能够将所有这些打包到一个我的项目案例中,用作原型硬件设施来测试你本人的想法。而且,如果你想批量生产某些产品,则能够购买 Jetson 主板的生产版本,并将其用于构建真正的硬件产品。
门铃摄像头 Python 代码演练
想晓得代码的工作原理吗?让咱们逐渐解决。
代码从导入咱们将要应用的库开始。最重要的是 OpenCV(Python 中称为 cv2),咱们将应用 OpenCV 从相机读取图像,以及用于检测和比拟人脸的人脸识别。
import face_recognition
import cv2
from datetime import datetime, timedelta
import numpy as np
import platform
import pickle
而后,咱们须要晓得如何拜访相机——从树莓派相机模块获取图像的办法与应用 USB 相机的办法不同。因而,只需依据你的硬件将此变量更改为 True 或 False 即可:
# 这里的设置取决于你的摄像机设施类型:
# - True = 树莓派 2.x camera module
# - False = USB webcam or other USB video input (like an HDMI capture device)
USING_RPI_CAMERA_MODULE = False
接下来,咱们将创立一些变量来存储无关在摄像机前行走的人的数据。这些变量将充当已知访客的简略数据库。
known_face_encodings = []
known_face_metadata = []
该应用程序只是一个演示,因而咱们将已知的脸孔存储在 Python 列表中。在解决更多脸孔的实在应用程序中,你可能想应用实在的数据库,然而我想使此演示放弃简略。
接下来,咱们具备保留和加载已知面部数据的性能。这是保留性能:
def save_known_faces():
with open("known_faces.dat", "wb") as face_data_file:
face_data = [known_face_encodings, known_face_metadata]
pickle.dump(face_data, face_data_file)
print("Known faces backed up to disk.")
这将应用 Python 的内置 pickle 性能将已知的脸孔写入磁盘。数据以雷同的形式加载回去,然而我在这里没有显示。
每当咱们的程序检测到新面孔时,咱们都会调用一个函数将其增加到已知的脸孔数据库中:
def register_new_face(face_encoding, face_image):
known_face_encodings.append(face_encoding)
known_face_metadata.append({"first_seen": datetime.now(),
"first_seen_this_interaction": datetime.now(),
"last_seen": datetime.now(),
"seen_count": 1,
"seen_frames": 1,
"face_image": face_image,
})
首先,咱们将代表面部的面部编码存储在列表中。而后,咱们将无关面部的匹配数据字典存储在第二个列表中。咱们将应用它来跟踪咱们第一次见到该人的工夫,他们最近在摄像头四周晃荡了多长时间,他们拜访过咱们屋宇的次数以及他们的脸部图像。
咱们还须要一个辅助函数来查看面部数据库中是否曾经存在未知面部:
def lookup_known_face(face_encoding):
metadata = None
if len(known_face_encodings) == 0:
return metadata
face_distances = face_recognition.face_distance(
known_face_encodings,
face_encoding
)
best_match_index = np.argmin(face_distances)
if face_distances[best_match_index] < 0.65:
metadata = known_face_metadata[best_match_index]
metadata["last_seen"] = datetime.now()
metadata["seen_frames"] += 1
if datetime.now() - metadata["first_seen_this_interaction"]
> timedelta(minutes=5):
metadata["first_seen_this_interaction"] = datetime.now()
metadata["seen_count"] += 1
return metadata
咱们在这里做一些重要的事件:
- 应用 face_recogntion 库,咱们查看未知脸孔与所有以前的访问者的类似水平。所述 face_distance()函数为咱们提供了未知脸部和所有已知的面之间的相似性的数值测量——数字越小,面部越相似。
- 如果脸孔与咱们的一位已知访客十分类似,则咱们假如他们是反复访客。在这种状况下,咱们将更新它们的“上次观看”工夫,并减少在视频帧中看到它们的次数。
- 最初,如果在最近的五分钟内有人在镜头前看到这个人,那么咱们假如他们依然在这里作为同一次访问的一部分。否则,咱们假如这是对咱们屋宇的新拜访,因而咱们将重置跟踪他们最近拜访的工夫戳。
程序的其余部分是主循环——一个有限循环,在该循环中,咱们获取视频帧,在图像中查找人脸并解决咱们看到的每个人脸。这是该程序的次要外围。让咱们来看看:
def main_loop():
if USING_RPI_CAMERA_MODULE:
video_capture =
cv2.VideoCapture(get_jetson_gstreamer_source(),
cv2.CAP_GSTREAMER
)
else:
video_capture = cv2.VideoCapture(0)
第一步是应用适宜咱们计算机硬件的任何一种办法来拜访相机。
当初让咱们开始获取视频帧:
while True:
# Grab a single frame of video
ret, frame = video_capture.read()
# Resize frame of video to 1/4 size
small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)
# Convert the image from BGR color
rgb_small_frame = small_frame[:, :, ::-1]
每次抓取一帧视频时,咱们都会将其放大到 1 / 4 尺寸。这将使人脸识别过程运行得更快,但代价是仅检测图像中较大的人脸。然而因为咱们正在构建一个门铃摄像头,该摄像头只能辨认摄像头左近的人,所以这不是问题。
咱们还必须解决这样一个事实,OpenCV 从摄像机中提取图像,每个像素存储为蓝绿色 - 红色值,而不是规范的红色 - 绿色 - 蓝色值。在图像上进行人脸识别之前,咱们须要转换图像格式。
当初,咱们能够检测图像中的所有面部,并将每个面部转换为面部编码。只需两行代码:
face_locations = face_recognition.face_locations(rgb_small_frame)
face_encodings = face_recognition.face_encodings(
rgb_small_frame,
face_locations
)
接下来,咱们将遍历每一个检测到的脸孔,并确定该脸孔是咱们过来见过的人还是新的访客:
for face_location, face_encoding in zip(
face_locations,
face_encodings):
metadata = lookup_known_face(face_encoding)
if metadata is not None:
time_at_door = datetime.now() -
metadata['first_seen_this_interaction']
face_label = f"At door {int(time_at_door.total_seconds())}s"
else:
face_label = "New visitor!"
# Grab the image of the face
top, right, bottom, left = face_location
face_image = small_frame[top:bottom, left:right]
face_image = cv2.resize(face_image, (150, 150))
# Add the new face to our known face data
register_new_face(face_encoding, face_image)
如果咱们以前见过此人,咱们将检索咱们存储的无关他们先前拜访的元数据。
如果没有,咱们将它们增加到咱们的脸部数据库中,并从视频图像中获取他们的脸部图片以增加到咱们的数据库中。
当初咱们找到了所有的人并弄清了他们的身份,咱们能够再次遍历检测到的人脸,只是在每个人脸四周绘制框并为每个人脸增加标签:
for (top, right, bottom, left), face_label in
zip(face_locations, face_labels):
# Scale back up face location
# since the frame we detected in was 1/4 size
top *= 4
right *= 4
bottom *= 4
left *= 4
# Draw a box around the face
cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2
)
# Draw a label with a description below the face
cv2.rectangle(frame, (left, bottom - 35), (right, bottom),
(0, 0, 255), cv2.FILLED
)
cv2.putText(
frame, face_label,
(left + 6, bottom - 6),
cv2.FONT_HERSHEY_DUPLEX, 0.8,
(255, 255, 255), 1
)
我还心愿在屏幕上方绘制一份最近访问者的运行列表,其中蕴含他们拜访过你屋宇的次数:
要绘制该图像,咱们须要遍历所有已知的脸孔,并查看最近在镜头前的脸孔。对于每个最近的访客,咱们将在屏幕上绘制他们的脸部图像并绘制拜访次数:
number_of_recent_visitors = 0
for metadata in known_face_metadata:
# 如果咱们在最初一分钟见过此人,if datetime.now() - metadata["last_seen"]
< timedelta(seconds=10):
# 绘制已知的面部图像
x_position = number_of_recent_visitors * 150
frame[30:180, x_position:x_position + 150] =
metadata["face_image"]
number_of_recent_visitors += 1
# Label the image with how many times they have visited
visits = metadata['seen_count']
visit_label = f"{visits} visits"
if visits == 1:
visit_label = "First visit"
cv2.putText(
frame, visit_label,
(x_position + 10, 170),
cv2.FONT_HERSHEY_DUPLEX, 0.6,
(255, 255, 255), 1
)
最初,咱们能够在屏幕上显示以后视频帧,并在其顶部绘制所有正文:
cv2.imshow('Video', frame)
为了确保程序不会解体,咱们将每 100 帧将已知脸孔列表保留到磁盘上:
if len(face_locations) > 0 and number_of_frames_since_save > 100:
save_known_faces()
number_of_faces_since_save = 0
else:
number_of_faces_since_save += 1
程序退出时,仅需一行或两行清理代码即可敞开相机。
该程序的启动代码位于该程序的最底部:
if __name__ == "__main__":
load_known_faces()
main_loop()
咱们要做的就是加载已知的脸孔(如果有的话),而后启动主循环,该循环永远从相机读取并在屏幕上显示后果。
整个程序只有大概 200 行,然而它能够检测到访客,对其进行辨认并每当他们来到你的家门时进行跟踪。
一个乏味的事实:这种人脸跟踪代码在许多街道和汽车站的广告中运行,以跟踪谁在看广告以及继续多长时间。以前,这听起来仿佛很遥不可及,但当初你花 60 美元就能够买到同样的货色!
扩大程序
该程序是一个示例,阐明了如何应用在便宜的 Jetson Nano 2GB 板上运行的大量 Python 3 代码来构建功能强大的零碎。
如果你想把它变成一个真正的门铃摄像头零碎,你能够增加这样一个性能:当零碎检测到门口有新的人时,它就会用 Twilio 向你发送短信,而不是仅仅在你的显示器上显示。或者你能够尝试用实在的数据库替换简略的内存中的人脸数据库。
你也能够尝试将这个程序转换成齐全不同的程序。浏览一帧视频,在图像中寻找内容,而后采取行动的模式是各种计算机视觉零碎的根底。尝试更改代码,看看你能想出什么!当你回家走到本人家门口时,让它播放你本人定制的主题音乐怎么样?你能够查看其余一些面部辨认示例,以理解如何进行相似的操作。
- 面部辨认示例:https://github.com/ageitgey/f…
理解无关 Nvidia Jetson 平台的更多信息
如果你想理解无关应用 Nvidia Jetson 硬件平台进行构建的更多信息,Nvidia 会提供新的收费 Jetson 培训课程。查看他们的网站以获取更多信息。
- https://courses.nvidia.com/co…:DLI+C-RX-02+V1/about
他们也有很棒的社区资源,例如 JetsonHacks 网站。
- https://www.jetsonhacks.com/
如果你想进一步理解无关应用 Python 构建 ML 和 AI 零碎的更多信息,请在我的网站上查看我的其余文章和书。
- https://www.machinelearningis…
原文链接:https://medium.com/@ageitgey/…
欢送关注磐创 AI 博客站:
http://panchuang.net/
sklearn 机器学习中文官网文档:
http://sklearn123.com/
欢送关注磐创博客资源汇总站:
http://docs.panchuang.net/