开发者学堂课程【计算机视觉入门及案例实战:运动传感器的实现】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/744/detail/13174
运动传感器的实现
内容介绍
一、需求分析
二、实现
一、需求分析
以下是运动传感器的演示:
运行代码之后,打开了两个窗口,窗口也就是摄像头拍摄到的画面。有人闯入到摄像头拍摄到的画面范围之内后,手机上可以收到通知。以上就是对运动传感器的案例效果演示。对需求进一步分析:
(1)
摄像头拍摄到的画面中,Motion 是运动的意思,可以理解为运动传感器,状态是undetected。左下角是英文的写法,是时间2019年10月29日,星期日,由此明确第一个需求:在画面中,标识状态和当前的时间。
(2)
以上是展示有人闯入到摄像头所拍摄的画面区域,是运动传感器程序所展示出来的状态。绿色的字变成了红色,undetected 变成了 detected,意思就是运动传感器探测到了有人闯入,下方的时间变成了红色的字样,运动的部分用绿色的框框住了,在黑白的展示窗口中展示了运动的部分。得出第2个需求,当运动传感器探测到了有人闯入时,状态变化,颜色变化,并且能用绿色的框将动的部分框住,在黑白区域中,展示出有变化的部分。
(3)
以上图片展示了运动传感器探测到有人闯入之后,手机所接收到的通知,通知由微信公众号平台发出。由此明确第三个需求,当运动传感器探测到有运动的物体之后,给手机发送通知,通知使用微信公众平台的第三方接口。
二、实现
实现探测主要有两种方式,一种方式是光流法,一种方式是差分法,该课题中主要讲解通过差分法实现对物体的检测。
1.实现原理
当在进行找不同游戏时,也是在使用差分法。游戏提供方会给出两张图片,要做的就是找出两张图片上方不同的地方。
运动传感器也会截取第一帧作为第一幅图像,往后的每一帧都和第一帧做比较把不同的地方找出来,并框出。飞度图是单通道的,以下是单通道飞度图的实例:
每个像素点都只有一个数值,每一行有两个像素点,由于是飞度图,所以描述一个像素点用0-255之间的数值。
如图:
用户关心两张图片有哪些地方不同,更确切地说,也就是关心每个特定位置上的像素是否相同,可以直接比较数值,如何从数学上实现两者之间的差别计算,用图一减去图二求绝对值即可。通过计算出的结果能找到图一与图二之间的不同,差分法的运用类似。
实现信息通知会借助第三方的微信公众号平台,只需要按照开发文档调用接口即可。
2.实现
将运动传感器的实现分成两步,第一步实现发送通知的功能,第二步实现运动传感器的功能。实现通知的具体步骤分为以下三步:
(1) 准备微信公众平台测试号;
(2) 安装HTTP 库requests;
(3) 编码实现
准备微信公众平台测试号:
(1)访问微信公众平台网站https://mp.weixin.qq.com
(2)查看服务号开发文档
(3)开始开发>接口测试号申请>进入微信公众帐号测试号申请系统
(4)扫码登录即可
首先访问微信公众平台的官方网址之后。查看开发文档,通过导航栏的菜单进入测试号申请系统,点击登录按钮之后,扫码登录即可。测试号信息包括ID和密码。 APP ID是微信公众号在公众平台的标识。公众号可能会被许多人关注,关注的人会出现在用户列表当中。微信号就是关注的人在该公众号上的标识,称为open ID。要给该用户发送信息时,就要使用open ID。安装requests库,这个库能够帮助用户调用微信公众平台提供的HTTP接口。安装代码如下:
pip install requests
如果希望速度更快,可以使用镜像。完成requests库的安装之后,准备工作已经完成。接下来通过编码实现通知的发送。
新建新的文件,命名之后,将后缀更改为PY。通过以下代码导入需要用到的库:
import json
import requests
微信通知的发送具体实现步骤分为两步:
第一步,获取access token;
第二步,利用access token发送微信通知。
Access token就是一把调用微信公众平台或其他接口的钥匙。通过调用微信公众平台的接口,实现获取access token。在官方文档中提到需要发起请求,请求方式是get。请求地址如下:
https请求方式:
GEThttps://api.weixin.qq.com/cgi-bin/token?dranttype=client credential&appid=APPID&secret=APPSECRET
请求地址中参数说明如下:
grant type是必需参数,获取access_token填写client_credential
appid是必需参数,第三方用户唯一凭证
secret是必需参数,第三方用户唯一凭证密钥,即appsecret
接口文档中需要APP ID以及Secret。 APP ID和Secret如下:
app_id = 'wx7f23641379450a28'
app_secret = '2bfecd48e13964d00b4a1b0bf26b0acb'
url=https://api.weixin.qq.com/cgibin/token?dranttype=clientcredential&appid=APPID&secret=APPSECRE
通过以下方式将网站中的APP ID和Secret换成自己的APP ID和Secret:
url=f’https://api.weixin.qq.com/cgibin/token?dranttype=clientcredential&appid={ wx7f23641379450a28}&secret={2bfecd48e13964d00b4a1b0bf26b0acb}’
执行三行代码之后,变量管理器效果如下:
向URL地址发起请求:用 requests could get方法发起请求,Get方法需要传入URL,表示向该 URL 发起 get 请求。使用变量承接响应结果,此时得到的数据是JSON 形式,所以能够直接通过调用 JSON 得到数据:
resp = requests.get(url).json()
执行代码之后,得到如下结果:
之后,通过如下代码取出 Access token 的值:
access_token = resp.get( ' access_token ' )
结果如下:
利用该 access token 调用微信其他接口。此时需要调用的是。发送通知的接口,该接口名称叫客服信息。查看官方文档的消息管理中,点击客服消息,客服接口发消息,此时会出现接口说明,拷贝如下地址到代码当中就能够实现发送文本消息:
http 请求方式:POST
https://api.weixin.qq.com/cgibin/message/custom/send?access_token=ACCESs_TOKEN
地址中需要用到 access token,请求方式是 post。
其中需要用到之前获取到的 access token,代码如下:
url=”https://api.weixin.qq.com/cgibin/message/custom/send?access_token={access_token}”
执行代码之后向该 URL 发送 post 请求,post 数据如下:
Req_data{
"touser" : "OPtNL,"msgtype" : "text","text" :
{
"content" : "Hello world"
}
}
数据中op
enid指的就是要数据中,open ID 指的就是要发送的对象。如果要发送给自己,就将自己的 open ID 拷贝到当中。定义变量如下:
opend_id = "oqtB6wxelAcohf9rasCA7VLHNk9c"
更改发送内容为,有人闯入了你的家。更改结果如下:
req_data = {
"touser" : opend_id,"msgtype" : "text","text" :
{
" content" :"有人闯入了你的家"
)
}
使用 requests 库发送 post 请求:
req_str = json.dumps(req_data)
传入 URL,如果是 post 请求,可以覆盖请求体,通过data指定请求数据此时需要将data 数据进行转换,通过 JSON 包,将其转化为字符串,传入 req_data:
requests.post(ur1, data= )
此时执行代码,req 是特殊编码。通过如下代码,阻止默认行为:
req_str = json.dumps(req_data,ensure_ascii=False)
在发送数据时需要将数据转化为二进制的数据。由于requests库使用的编码不是UTf-8,会导致发送出现问题,所以需要将请求的数据编码为二进制数据。通过如下代码实现通过utf-8的编码将字符串,转化为二进制数据:
req_data = req_str.encode( ' utf-8' )
requests.post(ur1, data=req_data)
执行如下三行代码,调用出手机实时桌面:
req_str = json.dumps(req_data,ensure_ascii=False)
req_data = req_str.encode( ' utf-8' )
requests.post(ur1, data=req_data)
结果如下:
代表程序能够正常执行。对程序进行优化:代码将字典的数据转化成了字符串,将字符串编码为二进制数据,再调用requests库将数据发送。优化如下,用一行代码替换之前的三行代码:requests.post(url,data=json.dumps(req_data,ensure_ascii=False).encode( ' utf-8' ))
替换之后执行程序仍然可以正常运行。至此通知功能已经完全实现。实现过程通过两步实现:
第一步,获取access token;
第二步,发送客服文本消息。
代码从上写到下,没有任何封装,叫做面向过程编程。这种编程方式的缺点:如果需要在其他地方获取a ccess token,就不能使用此处的代码。如果将该过程分装成一个函数,就能够被其他模块所调用。将代码改装为面向函数:
首先定义函数,目的是获取 access token,函数名就写为 get_access _token,传入两个参数:APPID 和 APPSecret。方法的目的是获取 access token,所以通过Return关键字返回 access token。以上方法通过def定义了一个函数,函数目的是获取access token。在使用该函数时,需要传入2个参数:APP ID 和 APP Secret。函数内部在执行的过程中,使用 APP ID 和 APP Secret 构造 URL, 得到 access token的值,最后将 access 的值返回。代码如下:
def get_access_token(app_id,app_secret) :
url=f'
https://api.weixin.qq.com/cgibin/token
?dranttype=clientcredential&appid={ wx7f23641379450a28}&secret={2bfecd48e13964d00b4a1b0bf26b0acb}’
resp = requests.get(url).json()
access_token = resp.get( 'access_token ')
return access_token
通过以下代码使用该函数:
if __name_ == "_main_":
app_id = 'wx7f23641379450a28'
app_secret = '2bfecd48e13964d00b4a1b0bf26b0acb'
access_token = get_access_token(app_id,app_secret)
首先做判断,如果该函数是直接运行的,那么Name就等于引号中的值,如果是导入,那么 Name就不会等于引号中的值。相当于是入口判断。做完判断之后,开始使用函数,需要传入两个参数,也就定义的APP ID和APP Secret。此时调用该方法就能够得到access token的值,结果如下:
能够从微信公众平台获取到值。首先导入了包和函数,函数中的代码运行到此并不会执行。之后进入到If语句中,定义APP ID和APP Secret,调用之前定义的get access token函数,获得了access token。由此可以仿照获取access token的值的方法,将发送通知的值也封装在函数中:首先定义函数,函数需要3个参数Access token,open ID,msg,代码如下:
def send_wx_customer_msg(access_token,opend_id,msg="有人闯入了你的家"):
url=f'https://api.weixin.qq.com/cgibin/message/custom/send?access_token={access_token}’
req_data =
{
"touser" : opend_id,
"msgtype" : "text",
"text" :
{
"content" : msg
}
}
requests.post(url,data=json.dumps(req_data,ensure_ascii=False).encode( ' utf-8' ))
if name== "__main_":
app_id = 'wx7f23641379450a28'
app_secret = '2bfecd48e13964d00b4a1b0bf26b0acb'
access_token = get_access_token(app_id,app_secret)
send_wx_customer_msg(access_token,"oqtB6wXelAcohf9rasCA7VLHNk9c")
在主函数中调用该函数,在括号中传入需要的参数即可。第一个参数是access token,第二个参数是open ID,open ID从参考文档中获, 第3个参数是MSG,MSG可以不用传入参数。执行代码之后,结果仍然接收到了通知,证明改造之后的程序也是可以正常运行的。以上改造,将代码从面向过程变成了面向函数。最大的好处就是函数可以被复用,意味着别人如果要获取access token,就不需要再实现一遍代码,只需要将函数导入并且调用即可,没有必要再重新实现一遍,大大减少了开发时间,提高了开发效率。至此完成了从面向过程到面向函数的优化。为何要使用面向对象,在编程领域有一句话“可以接受定义的复杂,但是不能接受调用的复杂”,当实现函数的时候,过程可以很复杂,但是调用函数的时候不能很复杂。因为定义函数的时候,只会定义一次,调用函数的时候,会调用很多次。在获取access token的时候,要传入APP ID和APP Secret。以上过程没有多余的地方,在发送消息的时候,用户关心的是给谁发送消息,此时不需要关心access token是什么。此时的access token对于函数调用来说就会多余,所以可以通过面向对象将access token省略。
面向对象的编码,新建模块文件,对于从面向函数改造为面向对象的过程,如果没有编程基础或Python基础,对于理解内容会有些困难,但可以将这部分内容理解为扩展内容。类的名字叫Wxtools
,定义方法:init
class WxTools():
def_ init_ _(self, app_ id, app_ secret):
Self.app id=app id
self.app_ secret = app_ secret
传入参数Self,一个类需要2个属性:APP ID和APP Secret, open ID是变化的,不同的人拥有不同的open ID。但是一个微信公众号平台只有一个APP ID和一个APP Secret。所以将该方法中的Self添加属性,Self可以理解为类的实例。实例化类的对象之后。传入APP ID 和APP Secret的值:
wx_tools=WxTools('wx7f23641379450a28','2bfecd48e13964d00b4a1b8bf26b0acb')
执行以上代码之后,得到变量WX tools的值:
属性如下:
将get_access_token从函数变为对象的方法,init就是一个方法。第一个参数是对象本身,保持好缩进的关系,放在类中。在类中第一个参数就是实例本身。由于WX tools由APP ID和APP Secret组成,所以在调用get_access_token时,没有必要再继续传入APP ID和APP Secret ,可以直接从Self身上取到。方法改造如下:
class WxTools():
def_ init__ (self, app_ _id, app_ _secret):
self.app_ id = app_ id
self.app_ secret = app_ secret
def get_ _access_ token(self):
url = f'https://api。weixin. gq. com/cgi-bin/token?grant type=client credential&appid=(self.app id}&secretefapp secretl'
resp = requests. get(ur1). json()
I access_ token = resp.get( 'access_ token')
return access_ token
WX_tools=WxTools('wx7f23641379450a28','2bfecd48e13964d00b4a1b0bf26b0acb' )
执行代码结果如下:
以上结果证明代码改造完成。
通过工具类获取access token的时候,不需要传入APP ID和APP Secret。此时,从某种意义上来说,就是调用的简化。将方法拷贝粘贴到类当中时,需要注意缩进。
改用第3个方法发送函数时,需要注意参数。第1个参数是Self本身,第2个参数是access token,但此时access token可以不用传入。因为可以通过get access token方法得到access token的值。第3个参数是。open ID,open ID必须要传入。第4个参数是MSG,也需要传入。直接通过Self调用get access token方法之后,可以获取到access token,调用方法需要注意带上括号。方法改造完成如下:
def send_ wx. customer, msg(self, opend_ 1d, msg="有人闯入了你的家"):
url=f'https://api,solxin,90.com/cei :bin/nessare/custom/send?access_token={access_token}’
req_ data =
{
"touser": opend_ id,
"msgtype":"text" ,
"text" :
{
"content": msg
}
}
requests . post(ur1, data=json. dumps(req. data, ensure_ ascii=False) .ensure_ascii=False).encode( ' utf-8' ))
改造完成之后使用该方法发送信息。此时,该方法只需要传入2个参数。由于Message有默认值,所以只需要传入一个参数open ID即可。使用如下代码调用:
wx_tools.send.wx_customer_msg("oqtB6wXelAcohf9rasCA7VLHNk9c”)
执行代码结果如下:
以上结果证明改造程序能够正常运行。
作为使用者的角度,进行比较两种方法的不同。第一种方法需要调用 get access token 方法获取到 access token,在发送消息时,需要传入 access token,使用open ID 发送。该方法不符合人的使用习惯。如果使用第二种方法,在使用时,需要传入APP ID和APP Secret,完成了类的实例化,继续调用类的方法发送通知。调用该方法时,只传入了一个参数,就是 Open ID。从使用者的角度而言。使用第二种方法更容易,这也就是定义的“可以接受定义的复杂,但不能接受调用的复杂”。至此完成了如何进行代码的封装和优化,即从面向过程到面向函数到面向对象的转换。
对于实现运动传感器和通知的步骤分为了以下几个步骤:
◆展示拍摄画而及文字等;
◆得到灰度图像并展示;
◆得到差分图像;
◆应用差分法,动的地方,画绿色框框;
◆如果探测到动的地方则修改文字内容为已探测到,并把文字颜色改为红色;
◆在探测到东西的时候发送微信通知,为防止过多骚扰,只发送一次。
第一步实现代码如下:
import cv2
import datet ime
camera = cv2.VideoCapture(0)
while True:
ret, frame = camera . read( )
cv2. putText(frame, "Motion: Undetected", (10, 20),
cv2. FONT_ _HERSHEY_ SIMPLEX, 0.5,(0, 255, 0), 2)
cv2.putText(frame,datetime.datetime.now().strftime("%A %d %B %Y %I :%M:%S%p"),
(10,frame.shape[0] - 10), cv2. FONT_ HERSHEY_ SIMPLEX,
0.35, (0, 255, 0), 1)
cv2. imshow( 'video', frame)
key = cv2. waitKey(1) & OxFFf
if key == ord('q'):
break
camera. release( )
cv2.destroyAllWindows( )
首先导入需要用到的库,使用open CV打开了摄像头,使用循环一直识别摄像头中的内容,退出循环之后释放摄像头资源及关闭窗口。在循环中,不停地读取摄像头所拍摄到的画面,往画面中添加运动传感器的状态,启动程序之后,状态展示在左上角。对应的位置也就是沿着画面左上角右侧画X轴,下侧画Y轴。下方定义了字体的颜色大小及宽度。接着在画面中写字,所写内容是对应的日期时间。获取日获取日期时间的方式就是通过dateTime模块的datetime子模块获取到当前子时间,获取到时间对象将时间对象转化为所需要的格式。转换之后,定义了所放字体的位置。字体位置是高度减去10,实际上是获取到画面的形状,然后取高度维度-10,之后是字体大小及字体颜色和厚度:
while True:
ret, frame = camera . read( )
cv2. putText(frame, "Motion: Undetected", (10, 20),
cv2. FONT_ _HERSHEY_ SIMPLEX, 0.5,(0, 255, 0), 2)
cv2.putText(frame,datetime.datetime.now().strftime("%A %d %B %Y %I :%M:%S%p"),
(10,frame.shape[0] - 10), cv2. FONT_ HERSHEY_ SIMPLEX,
0.35, (0, 255, 0), 1)
以下代码为窗口命名:
cv2. imshow( 'video', frame)
窗口命名完之后设置了退出机制:
key = cv2. waitKey(1) & OxFFf
if key == ord('q'):
break
以下代码释放资源:
camera. release( )
cv2.destroyAllWindows( )
以上代码实现与人脸识别相似。接下来需要得到灰度图片并将其展示。因为灰度图像能够比RGB图像更简单,计算机使用灰度图像进行运算会因为它的通道只有一个而运算速度更快。得到灰度图像并展示的代码如下:
import cv2
import datet ime
camera = cv2. VideoCapture(0)
while True:
grabbed, frame = camera.read( )
gray_ frame = cv2. cvtColor(frame, cv2. COLOR_ BGR2GRAY)
gray_ _frame = cv2 . GaussianBlur(gray_ _frame, (25, 25), 3)
cv2. putText(frame, "Motion: Undetected", (10, 20),
cv2. FONT_ HERSHEY_ SIMPLEX,0.5,(0,255, 0),2)
cv2. putText(frame,
datetime.datetime. now() .strftime("%A %d %B %Y %I :%M:%S%p"),
(10,frame. shape[0] - 10), cv2. FONT_ HERSHEY_ _SIMPLEX,
0.35,(0, 255, 0), 1)
cv2. imshow( 'video', frame )
cv2. imshow('diff', gray _frame)
key = cv2. waitKey(1) & 0xFFf
if key == ord('q'):
break
camera. release( )
cv2. destroyAllWindows ( )
灰度图像是单通道,RGB图像是三通道,运算起来会比RGB图像更快:
gray_ frame = cv2. cvtColor(frame, cv2. COLOR_ BGR2GRAY)
用高斯滤波消除噪点,可以理解为消除噪音,消除了图像中的高频部分。在听声音的时候需要消除噪声,在看图像的时候需要消除噪点,高斯滤波就能够帮助消除噪点。
gray_ _frame = cv2 . GaussianBlur(gray_ _frame, (25, 25), 3)
以下代码将灰度图像展示。该灰度图像是经过高斯滤波之后的灰度图像:
cv2. imshow('diff', gray _frame)
运行以上程序之后,程序打开了两个窗口,右边是正常拍摄的画面,灰度图像经过了高斯滤波处理之后的效果,变得有些模糊,但其实是消除噪点。
通过如下代码得到差分图像:
import cv2
import datetime
camera = cv2.VideoCapture(0)
background = None
es = cv2. getStructuringElement(cv2.MORPH_ ELLIPSE, (5, 4))
while True:
grabbed, frame = camera.read()
gray_ frame = cv2 . cvtColor(frame, cv2 . COLOR_ BGR2GRAY)
gray_ frame = cv2.GaussianBlur(gray_ frame, (25, 25), 3)
if background is None:
background = gray_ _frame
continue
diff = cv2. absdiff(background, gray_ _frame)
diff = cv2. threshold(diff, 50,255, cv2. THRESH_ BINARY)[1]
diff = cv2.dilate(diff, es, iterations=3)
cv2. putText(frame, "Motion: Undetected", (10, 20),
cv2. FONT_ HERSHEY_ _SIMPLEX,0.5, (日, 255, 0),2)
cv2. putText(frame ,
datetime.datetime. now(). strftime("%A %d %B %Y %I :%M:%S%p"),
(10,frame.shape[0] - 10), cv2. FONT_ HERSHEY_ _SIMPLEX,
0.35,(0, 255, 0), 1)
cv2. imshow('video', frame)
cv2. imshow('diff', diff)
key = cv2.waitKey(1) & 0xFFf
if key == ord('q'):
break
camera.release()
cv2. destroyAllWindows()
以上代码实现效果如下:
此时使用Background存储的一幅画面。以下代码取结构化元素,用来做形态学膨胀:
es = cv2. getStructuringElement(cv2.MORPH_ ELLIPSE, (5, 4))
形态学膨胀将物体变得更连贯。
以下代码就是求背景图与其他灰度图的差,使用了差分法:
diff = cv2. absdiff(background, gray_ _frame)
diff = cv2. threshold(diff, 50,255, cv2. THRESH_ BINARY)[1]
diff = cv2.dilate(diff, es, iterations=3)
以下代码设置预值,此时定义如果绝对值超过50就算不一样,如果小于50就算一样:
iff = cv2. threshold(diff, 50,255, cv2. THRESH_ BINARY)[1]
255代表白色不同的地方用白色处理。
以下代码就是形态学膨胀:
diff = cv2.dilate(diff, es, iterations=3)
在差异较小的情况下就可以使用形态学膨胀优化这种差值,使之更连贯。以下代码是展示differ图像:
cv2. imshow('diff', diff)
该图像经过了绝对值运算优化。绝对值优算。绝对值优化过后,进行了阈值运算。阈值运算的意思就是差别小于50就算0,如果是大于50就算做255,展示出了如下黑白图像:
用差分法动的地方画上绿色的框,增加代码如下:
contours, hierarchy = cv2. findContours(diff . copy(), Cv2.RETR_ EXTERNAL,
Cv2.CHAIN_ APPROX_ _SIMPLE)
is_ detected = False
for C in contours :
if cv2. contourArea(c) < 2000:
continue
(x, y, W, h) = cv2. boundingRect(c)
cv2. rectangle(frame, (x, y), (x + W, y + h), (0,255, 0),2)
is_ detected = True
以下代码使用了OpenCV中较为重要的一个方法,就是发现图像中有多少个连续的物体:
contours, hierarchy = cv2. findContours(diff . copy(), Cv2.RETR_ EXTERNAL,
Cv2.CHAIN_ APPROX_ _SIMPLE)
为了防止方法修改了diva导致展示出现问题,所以将代码拷贝一份,而不是使用原图像。接下来定义变量指是否探测到动的物体,然后遍历该物体。用如下方法判断连续不同的物体大小是否大于2000,如果物体太小,直接跳到下一次循环中:
for C in contours :
if cv2. contourArea(c) < 2000:
continue
(x, y, W, h) = cv2. boundingRect(c)
cv2. rectangle(frame, (x, y), (x + W, y + h), (0,255, 0),2)
is_ detected = True
如果连续不同的物体,它的大小大于2,000,就会去获取该物体的坐标以及宽度和高度,获取到之后就会开始展示绿色的框。以下代码用于展示绿色的框:
cv2. rectangle(frame, (x, y), (x + W, y + h), (0,255, 0),2)
如果探测到有运动的地方就修改文字内容及颜色和框的颜色,改为红色。在第四步当中能够得到变量值,标识当前运动传感器是否探测到有移动的物体。如果探测到运动的物体,就把运动传感器的状态改为“探测到物体”,颜色改为红色。如果没有探测到移动的物体,展示的文本就是运动传感器,没有检测到移动的物体,展示的颜色是绿色。
第六步,在探测到有移动物体的时候发送微信,为防止过多造成骚扰,只发送一次,定义了新的变量叫is_Send_Msg,默认是false。此时该变量的作用是标识有没有发送过通知。如果已经发送过通知,就算持续检测到有移动物体,都不会再继续发送通知。在探测到有移动的物体之后,并且该物体大小超过2000,就判断如果之前没有发送过消息,那么此时使用微信公众平台发送。发送一次消息之后将is_Send_Message改为true。从之前所定义的代码中导入APP ID和APP Secret,Settings就是配置文件的意思。在配置文件中定义了APP ID和APP Secret的内容。该配置文件的作用就是可以直接修改配置文件信息,不需要在代码中进行修改。
启动整个程序,并展示实时桌面:
从实验结果中可以看出,当有人闯入摄像头范围之内时,微信中可以收到通知“有人闯入了你的家”。此时,运动传感器的案例已经完成。