作者|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卡上的办法:

  1. 从Nvidia下载Jetson Nano Developer Kit SD卡映像。

    • https://developer.nvidia.com/...
  2. 下载Etcher,该程序将Jetson软件映像写入SD卡。
  • https://www.balena.io/etcher/
  1. 运行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 updatesudo 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_recognitionimport cv2from datetime import datetime, timedeltaimport numpy as npimport platformimport 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

咱们在这里做一些重要的事件:

  1. 应用face_recogntion库,咱们查看未知脸孔与所有以前的访问者的类似水平。所述face_distance()函数为咱们提供了未知脸部和所有已知的面之间的相似性的数值测量——数字越小,面部越相似。
  2. 如果脸孔与咱们的一位已知访客十分类似,则咱们假如他们是反复访客。在这种状况下,咱们将更新它们的“上次观看”工夫,并减少在视频帧中看到它们的次数。
  3. 最初,如果在最近的五分钟内有人在镜头前看到这个人,那么咱们假如他们依然在这里作为同一次访问的一部分。否则,咱们假如这是对咱们屋宇的新拜访,因而咱们将重置跟踪他们最近拜访的工夫戳。

程序的其余部分是主循环——一个有限循环,在该循环中,咱们获取视频帧,在图像中查找人脸并解决咱们看到的每个人脸。这是该程序的次要外围。让咱们来看看:

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 = 0for metadata in known_face_metadata:    # 如果咱们在最初一分钟见过此人,    if datetime.now() - metadata["last_seen"]                          < timedelta(seconds=10):# 绘制已知的面部图像        x_position = number_of_recent_visitors * 150frame[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 = 0else:    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/