发布于 2026-01-06 7 阅读
0

如何在一下午内搭建自己的亚马逊Ring安防系统

如何在一下午内搭建自己的亚马逊Ring安防系统

在本教程中,我们将探索机器学习人工智能领域最前沿的技术之一……计算机视觉!为了展示其功能,本文将一步步指导您使用人脸识别机器学习算法构建您自己的桌面安全系统。

只需一个简单的网络摄像头,您的程序就能识别您允许进入系统的人员的面部。如果任何未识别的面孔出现在您的网络摄像头前,程序将触发短信、电子邮件和快照图像警报系统。我们还将使用 Cloudinary 和 PubNub 构建一个 React Native 应用程序,该应用程序可以接收“入侵者”面部的快照图像。如果您需要,它还允许用户添加到系统中。

什么是计算机视觉?

计算机视觉是人工智能领域的一个特定分支,它致力于训练机器学习模型来理解和解释视觉世界。通过学习来自摄像头和视频的图像和帧,计算机视觉人工智能可以准确地对所看到的物体进行分类,并像人类一样执行相应的反应任务。

资料来源访问日期:2019年7月31日

用于人脸追踪警报系统的 Python 代码

在开始编写代码之前,请务必注册一个免费的 PubNub 帐户,以免以后出现任何问题。

要从头开始构建项目,请使用计算机的命令行应用程序创建项目目录:

mkdir faceTrackingApp
cd 面部追踪应用程序
然后创建一个名为facetracker.py的新 Python 文件。

计算机视觉的库和依赖项

OpenCV 和人脸识别

首先,我们需要导入一些机器学习库来实现应用程序的人脸追踪功能。我们将主要使用 OpenCV 和 face_recognition 这两个库。
import face_recognition # 用于人脸识别的机器学习库
import cv2 # OpenCV
导入 numpy 库并将其命名为 np # 处理数据
导入时间
导入 os、sys
OpenCV是最流行的实时计算机视觉机器学习库。该库提供了许多实用工具,例如网络摄像头控制以及用于从零开始训练人脸追踪应用程序的模型。然而,我们的项目将主要使用ageitgey 的 face_recognition Python 库,因为它已经内置了人脸识别模型,使用起来极其快速便捷。

PubNub

接下来,我们将设置 PubNub 作为数据流网络,以处理 Python 脚本和移动应用程序之间的所有数据。获取免费的PubNub API 密钥后,请安装PubNub Python SDK
使用 pip 安装 'pubnub>=4.1.4'
然后,在你的Python文件中导入该库。
from pubnub.callbacks import SubscribeCallback
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
from pubnub.enums import PNOperationType, PNStatusCategory
并使用您的 API 密钥配置 PubNub 实例。
# PubNub 配置
pnconfig = PNConfiguration()
pnconfig.subscribe_key = "您的订阅密钥"
pnconfig.publish_key = "YOUR_PUBLISH_KEY"
pnconfig.ssl = False
pubnub = PubNub(pnconfig)

云端

最后,我们将设置Cloudinary作为内容分发网络 (CDN),用于存储入侵者面部图像。这将与 PubNub 完美配合,因为我们的 Python 脚本可以将图像上传到 Cloudinary,从响应中获取 URL,然后 PubNub 会将该 URL 发送给我们的客户端应用程序进行渲染。首先,注册一个免费的 Cloudinary 帐户,然后使用以下命令安装 Cloudinary Python SDK:
pip install cloudinary
CLOUDINARY_URL 通过从 管理控制台复制来设置环境变量。使用 zsh/bash/sh:
export CLOUDINARY_URL=cloudinary://API-Key:API-Secret@Cloud-name
在你的Python脚本中导入该库,
from cloudinary.api import delete_resources_by_tag, resources_by_tag
从 cloudinary.uploader 导入 upload
from cloudinary.utils import cloudinary_url
并配置一个 Cloudinary 实例。
# Cloudinary 配置
os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '.'))
如果 os.path.exists('settings.py'):
    执行(打开('settings.py').read())
DEFAULT_TAG = "python_sample_basic"

机器学习人脸追踪算法

在开始构建人脸识别机器学习模型之前,我们需要声明一些全局变量:
# 设置一些全局变量
video_capture = cv2.VideoCapture(0) # 网络摄像头实例
known_face_names = [] # 人脸名称
known_face_encodings = [] # 人脸编码
count = 0 # 未知用户数量计数器
flag = 0 # 用于设置/取消设置“入侵者模式”的标志
【注:我们为未知用户创建一个计数变量,因为我们将动态地把用户的快照图像保存到指定文件路径中。】我们会将计数添加到文件名中,就像添加一个 ID 标签一样。为了稍后找到这张快照图像,我们需要获取该用户的计数变量,以便在文件路径中找到对应的图像。要开始训练我们的人脸识别模型,我们将从两张人脸样本图像开始。您的项目目录中需要两张不同人物的人脸图像。
# 加载示例图片并学习如何识别它。
sample_face_1 = face_recognition.load_image_file("sample_1.jpeg")
sample_face_1_encoding = face_recognition.face_encodings(sample_face_1)[0]

# 加载第二个示例图片,学习如何识别它。
sample_face_2 = face_recognition.load_image_file("17.png")
sample_face_2_encoding = face_recognition.face_encodings(sample_face_2)[0]

# 创建已知人脸编码及其名称的数组
known_face_encodings = [
    sample_face_1_encoding,
    sample_face_2_encoding
]

# 为样本人脸编码创建名称
known_face_names = [
    "sample_1",
    "sample_2"
]

# 初始化一些变量
face_locations = []
face_encodings = []
face_names = []
process_this_frame = True
接下来,我们将声明一个 while 循环,它将在应用程序运行期间持续运行。该循环将负责我们应用程序的主要功能:
  • 打开并显示网络摄像头画面
  • 实时追踪出现在网络摄像头前的人脸,并在人脸周围绘制红色方框
  • 在已知用户的头像下方显示姓名,在尚未添加到数据库的头像下方显示“未知”。
  • 调用一系列警报和函数来处理屏幕上出现“未知”面孔的情况
while(True):
    
    video_capture = cv2.VideoCapture(0)
    # 截取视频中的单帧画面
    ret, 帧 = video_capture.read()

    # 将视频帧调整为 1/4 大小,以加快人脸识别处理速度
    small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)

    # 将图像从 BGR 颜色(OpenCV 使用)转换为 RGB 颜色(人脸识别使用)
    rgb_small_frame = small_frame[:, :, ::-1]

    # 仅处理视频的隔帧以节省时间
    如果 process_this_frame:
        # 查找当前视频帧中的所有人脸及其编码
        face_locations = face_recognition.face_locations(rgb_small_frame)
        face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)

        face_names = []
        对于 face_encodings 中的每个 face_encoding:
            # 查看该人脸是否与已知人脸匹配
            matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
            名称 = "未知"

            # 如果在 known_face_encodings 中找到匹配项,则直接使用第一个。
            # 如果匹配结果为真:
            # first_match_index = matches.index(True)
            # 姓名 = known_face_names[first_match_index]

            或者,使用与新面距离最小的已知面。
            face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
            best_match_index = np.argmin(face_distances)
            如果 matches[best_match_index]:
                姓名 = known_face_names[最佳匹配索引]

            face_names.append(name)

            #---------------------有关此代码块的解释,请参见下一节---------------------#

            ## 设置未知用户标志并发送警报
            #全球旗帜
            #if(name=='未知' and flag==0):
            # 标志 = 1
            # 警报()
            #
            #--------------------------------------------------------------------------------------------#

    process_this_frame = 不 process_this_frame

    # 显示结果
    for (top, right, bottom, left), name in zip(face_locations, face_names):
        # 由于我们检测到的帧被缩放至 1/4 大小,因此需要重新缩放人脸位置。
        前 4 名
        右 *= 4
        底部 *= 4
        左侧 *= 4

        # 在脸部周围画一个框。
        cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)

        # 在脸部下方画一个带有名称的标签
        cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
        字体 = cv2.FONT_HERSHEY_DUPLEX
        cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)

    # 显示生成的图像
    cv2.imshow('视频', frame)

    按键盘上的“q”键退出!
    如果 cv2.waitKey(10) & 0xFF == ord('q'):
        休息

# 释放网络摄像头手柄
video_capture.release()
cv2.destroyAllWindows()

发送警报

我们将处理未注册人脸出现在网络摄像头前的情况。我们希望程序在检测到“未知”人脸时立即触发警报系统。幸运的是,我们只需要在主 while 循环的末尾,也就是 ` for face_encoding in face_encodings: ` 循环的末尾添加几行代码即可。
# 设置未知用户标志并发送警报
全局标志
如果(name=='未知' 且 flag==0):
    flag = 1 # 在处理完未知用户之前,停止重复调用警报
    Alert() # 触发警报系统
当警报触发时,我们可以定义一个函数来拍摄未知用户的面部快照,调用一个函数将快照上传到 Cloudinary,最后调用我们的短信/电子邮件警报函数。
def Alert():
    全球计数
    video_capture = cv2.VideoCapture(0) # 创建 OpenCV 网络摄像头实例
    path = './' # 指定快照的存储位置
    name = 'Unknown_User' + str(count) # 将用户 ID 添加到文件路径

    等待 3 秒
    print('倒计时3秒拍照')
    time.sleep(1)
    print('正在拍照(2)')
    time.sleep(1)
    print('正在拍照 1')
    time.sleep(1)

    # 拍照
    ret, 帧 = video_capture.read()

    # 使用灰度图像以节省内存空间
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 将图像保存到文件路径
    status = cv2.imwrite('%s/%s.jpg' % (path, name),gray)
    print('未知用户已保存到数据库', status)

    # 将快照上传到 Cloudinary
    upload_files('%s/%s.jpg' % (path,name))
    
    # 发送电子邮件和短信提醒
    sendAlerts()
【注:我们对图像进行灰度处理,因为人脸识别器不需要颜色来识别面部特征。我们还将快照图像存储在本地,以便客户端稍后添加用户时可以将人脸添加到识别器中。】在定义 `upload_files ()`函数时,我们传入快照的文件路径,以便 Cloudinary 知道从哪里上传文件。然后,我们获取图像在云端的 URL。我们将此 URL 以及用户 ID(未知用户的数量)通过 PubNub 发送给客户端应用程序。客户端应用程序随后可以从 Cloudinary URL 渲染快照图像。
def upload_files(msg):
    全局计数 # 对计数进行全局更改
    response = upload(msg, tags=DEFAULT_TAG) # 上传图片到 Cloudinary
    url, options = cloudinary_url(
        response['public_id'],
        format=response['format'],
        宽度=200,
        高度=150,
        裁剪“填充”
    字典 = {"url": url, "ID": count}
    pubnub.publish().channel('global').message(dictionary).pn_async(publish_callback)
    count+=1 # 未知用户计数加一
要使用 PubNub 发布内容,我们需要定义一个发布回调函数。
def publish_callback(result, status):
    经过
    # 处理 PNPublishResult 和 PNStatus
  要设置短信和电子邮件提醒,您需要注册一个免费的 ClickSend 帐户和一个免费的 SendGrid 帐户来获取 API 密钥。现在,您可以通过我们的合作伙伴模块体验PubNub Functions 的强大功能和卓越性能。请访问我们的ClickSend 模块SendGrid 模块。通过这些链接,PubNub 将自动生成一个可自定义的 PubNub Function。无服务器的开源代码将完全为您处理 API。您只需输入 API 密钥即可!设置好 PubNub Functions 后,您可以定义一个sendAlerts()函数来发布消息,从而实现短信和电子邮件提醒:
def sendAlerts():
    字典 = {
    “至”:'接收电话号码',
    "body": "您的桌面上有一位未注册用户!"
    }
    pubnub.publish().channel('clicksend-text').message(dictionary).pn_async(publish_callback)

    字典 = {
    收件人:电子邮件收件人
    "toname": "电子邮件发件人",
    “主题”:“入侵警报”,
    文本:您的桌面上有一位未注册用户
    }   
    pubnub.publish().channel('email-sendgrid-channel').message(dictionary).pn_async(publish_callback)
注意:为了正确使用 PubNub 块,您需要通过块中指定的同一通道发布(您可以在块的 Functions 控制面板中查看),并正确格式化消息有效负载(根据块的文档)。

将用户添加到我们的面部追踪器

当我们的网络摄像头检测到未注册的人脸时,我们的 Python 脚本会向客户端应用程序发送电子邮件/短信警报以及一张快照图像。 现在,我们希望添加将用户人脸添加到应用程序的“ known_faces ”数据库中的功能,这样用户就不会再触发我们的警报系统。为此,客户端应用程序必须通过 PubNub 发布一条消息。 为了在我们的 Python 应用程序中接收此消息,我们必须订阅客户端发布消息的频道,并创建一个订阅者回调来处理传入的消息。
class MySubscribeCallback(SubscribeCallback):
    def status(self, pubnub, status):
        经过
        # 返回的状态对象始终与订阅相关,但也可能包含
        # 关于订阅、心跳或错误的信息
        # 使用 operationType 来切换不同的选项
        如果 status.operation == PNoperationType.PNSubscribeOperation \
                或者 status.operation == PNOperationType.PNUnsubscribeOperation:
            如果 status.category == PNStatusCategory.PNConnectedCategory:
                经过
                # 这是订阅操作的预期结果,意味着没有任何错误或问题。
            elif status.category == PNStatusCategory.PNReconnectedCategory:
                经过
                # 这通常发生在订阅暂时失败但重新连接时。这意味着
                # 之前出现过错误,但现在问题已解决。
            elif status.category == PNStatusCategory.PNDisconnectedCategory:
                经过
                # 这是退订的预期类别。这意味着这里
                取消订阅所有内容均无误。
            elif status.category == PNStatusCategory.PNUnexpectedDisconnectCategory:
                经过
                这通常是网络连接问题,这是一个错误,请处理。
                # 系统会自动调用适当的重试机制。
            elif status.category == PNStatusCategory.PNAccessDeniedCategory:
                经过
                # 这意味着 PAM 不允许此客户端订阅此服务。
                # 通道和通道组配置。这是另一个明确的错误。
            别的:
                经过
                这通常是网络连接问题,属于错误,请妥善处理。
                # 将自动调用重试机制
        elif status.operation == PNoperationType.PNSubscribeOperation:
            # 心跳操作实际上可能会出错,因此首先检查是否存在错误非常重要。
            # 有关如何通过状态配置心跳通知的更多信息
            如果 status.is_error():
                经过
                心跳操作出现错误,请在此处处理。
            别的:
                经过
                心跳手术成功
        别的:
            经过
            # 遇到未知状态类型
 
    def presence(self, pubnub, presence):
        传递 # 处理传入的在线状态数据
    def message(self, pubnub, message):
        addUser(message.message["ID"], message.message["name"])
 
 
pubnub.add_listener(MySubscribeCallback())
pubnub.subscribe().channels('ch1').execute()
注意:以上假设客户端会发布未知用户的 ID(用于图像文件路径)以及用户名(显示在用户头像下方)。有了这些参数,我们就可以将新用户添加到数据库中。
def addUser(ID, name):
    全局 known_face_encodings、known_face_names、标志
    path = './Unknown_User' + str(ID) # 将用户 ID 添加到文件路径
    # 加载用户图片并学习如何识别它。
    user_image = face_recognition.load_image_file('% s.jpg' % (path)) # 加载图像
    user_face_encoding = face_recognition.face_encodings(user_image)[0] # 对图像进行编码
    known_face_encodings.append(user_face_encoding) # 将编码后的图像添加到“已知人脸”数组
    known_face_names.append(name) # 将新用户的姓名添加到数据库
    flag = 0 # 重置未知用户标志

我们客户端应用程序的 React Native 代码

搭建我们的实时 React Native 环境

安装Xcode以便我们构建和模拟 iOS 应用,安装Android Studio以便构建和模拟 Android 应用。然后使用Homebrew安装Node.js和 watchman
brew install node
brew install watchman
使用 NPM 安装 React Native CLI:
npm install -g react-native-cli
要创建 React Native 应用模板,请在项目目录中输入 React Native CLI 命令:
react-native 初始化客户端
CD客户端
由于我们将在客户端应用程序中使用 PubNub 来发送和接收消息,因此我们需要安装 PubNub React SDK。
npm install --save pubnub pubnub-react
然后像这样链接库:
react-native 链接 pubnub-react

设置实时发布/订阅消息传递

要开始在我们的应用程序中实时发送和接收消息,首先需要导入PubNub React SDK
import PubNubReact from 'pubnub-react';
然后从 React Native 导入TouchableOpacityImage组件,
进口 {
  样式表
  看法,
  文本,
  文本输入框
  TouchableOpacity,
  图像,
来自 'react-native';

现在我们在应用程序组件的顶部添加一个构造函数。该构造函数将负责使用我们的发布/订阅键设置一个 PubNub 实例,并初始化以下状态变量:

  • image - 来自未知用户警报的快照图像(我们会用占位符图像初始化它,直到收到快照警报)。
  • 消息- 来自面部追踪应用程序的传入警报消息。
  • text - 客户端用户输入用户名的输入框。
  • 计数- 用于跟踪我们从哪个未知用户处收到警报。
export default class App extends React.Component {

  构造函数(props) {
    超级(道具)

    this.pubnub = new PubNubReact({
      publishKey: "您的发布密钥",
      订阅密钥:"您的订阅密钥"
    })

    //基本状态
    this.state = {
      图片: require('./assets/PLACEHOLDER_IMAGE.jpg'),
      信息: '',
      文本: '',
      计数:0
    }

    this.pubnub.init(this);
  }

/// .......VVV 其余代码 VVV.......///
当我们的客户端应用首次启动时,我们会声明一个异步函数,该函数会订阅我们的人脸追踪警报通道并处理消息事件。在这种情况下,我们会收到未知用户的 ID(未知用户的数量)以及快照图像 URL(来自 Cloudinary)。
async componentDidMount() {
  this.setUpApp()    
}

async setUpApp(){
  this.pubnub.getMessage("global", msg => {
    this.setState({count: msg.message.ID})
    this.setState({image: msg.message.url})
  })

  this.pubnub.subscribe({
    通道:["global"],
    withPresence: false
  });
}
一旦移动应用接收到该图像,客户端用户就应该能够将未知用户添加到人脸追踪器的“已知人脸”数据库中。我们可以定义一个函数来设置客户端用户输入未知用户姓名的状态。
handleText = (name) => {
   this.setState({ text: name })
}

我们还可以编写一个函数,将新增用户的姓名和 ID 发布出去。

发布名称 = (文本) => {
  this.pubnub.publish({
    信息: {
      ID:this.state.count,
      名称:文本,
    },
    通道:"ch1"
  });
}

创建和渲染应用程序组件

屏幕顶部将显示来自传入的“未知用户”警报的快照图像。该图像的来源是我们从警报消息中获取并保存到状态的 URI。

` <图片
  来源={{uri: this.state.image}}
  style={{width: 250, height: 250}}/>

下面我们可以显示合适的标题。

<Text>{'你认识这个人吗?'}</Text>

然后,我们创建一个文本输入组件,用于存储要添加到人脸跟踪器中的用户姓名(如果客户端决定这样做)。

<TextInput style = {styles.input}
         underlineColorAndroid = "透明"
         占位符 = "姓名"
         占位符文本颜色 = "#9a73ef"
         autoCapitalize = "none"
         onChangeText = {this.handleText}/>

最后,我们创建一个带有 TouchableOpacity 属性的提交按钮,以便将添加的用户姓名发布到我们的人脸追踪器中,并添加到系统中:

<可触摸不透明度
    样式 = {styles.submitButton}
    onPress = {
      () => this.publishName(this.state.text)
    }>
      <Text>"提交"</Text>
</TouchableOpacity>

将所有这些组件包裹在<View> </View>中,就大功告成了!

运行程序

首先,在 Android 或 iOS 上打开客户端应用程序目录中的终端,启动 React Native 客户端应用程序。

react-native run-ios

或者

react-native 运行 Android

然后,在另一个终端窗口中运行 Python 人脸跟踪器。

python facetracker.py

如果你还意犹未尽……

如有任何疑问、疑虑或意见,请随时发送至devrel@pubnub.com

如果您仍然渴望了解更多 PubNub 机器学习内容,以下是一些您可能感兴趣的其他文章:

文章来源:https://dev.to/pubnub/how-to-build-your-own-amazon-ring-security-system-in-an-afternoon-3hm2