ITPub博客

首页 > 人工智能 > 人工智能 > 【玩转香橙派Orange Pi】探索AI开发板香橙派aipro: 智能安防报警系统案例分享

【玩转香橙派Orange Pi】探索AI开发板香橙派aipro: 智能安防报警系统案例分享

投稿 人工智能 编辑:ITPUB 时间:2024-05-08 18:27:23 0 删除 编辑

项目背景

老家有老人在,安装了监控。一般情况都是想起来看监控就看一下,就会错过很多瞬间。因此想用算法替代人工,记录家里有人的瞬间。

项目介绍

使用香橙派aipro开发板进行一个基于视觉的智能监控系统项目。通过香橙派aipro开发板读取家里已有的网络摄像头的画面,通过给画面添加多边形区域限制检测范围。通过对检测范围内的图像进行人形识别,由于香橙派aipro是一个ai开发板,通过搭载的昇腾310 SOC很适合用AI算法解决此类问题。检测人形的算法选择了yolov5,aipro对yolov5有着非常完美的兼容,可以完全利用NPU的8T算力。识别到人形后计算报警延时并触发报警。在云服务器或者aipro本地搭建web服务,识别端识别到人形后通过post将报警信息发送给web后端,web后端接收报警信息,用于数据库记录和报警显示。

硬件配置

aipro开发板是一款功能强大的人工智能开发板,搭载了一系列先进的硬件配置,包括:

  • 处理器:搭载了高性能的昇腾310 ai芯片,为项目提供强大的计算能力。

  • 存储:aipro开发板支持emmc、m2固态和TF卡。前两个没买,用家里剩下的64G tf卡,本项目本地可以不存储报警图片,对存储需求不大。

  • 连接性:开发板主持Wi-Fi、蓝牙和千兆网口。为了稳定直接选择了网口,网口直接接入家庭路由器,可以在调摄像头的同时发送报警信息。

软件使用

aipro开发板拥有丰富的软件生态,编程语言支持Python、C++等,由于python编程速度更快我选择python,固件默认是python3.10,并提供了python3.9的conda环境,官方提供的pytorch报是3.9的,直接使用python3.9了。aipro开发板支持pytorch调用npu进行ai推理也支持模型量化om模型进行推理,om效率高,无脑冲了。

硬件准备

  • 香橙派aipro开发板 1块

  • 海康监控摄像头 1台

  • 路由器或交换机 1台

  • 电源 (pd65 瓦)

  • 云服务器 (也可以本地搭建)

软件准备

  • Python编程环境 (python3.9 conda)

  • OpenCV库 (opencv-python 4.9)

  • Pytorch模型库 (官方npu加速版本)

  • Django后端

算法思路和实现过程

  • 摄像头的选择:选择海康网络摄像头作为监控系统的视频输入设备,通过OpenCV库对RTSP协议的视频流进行读取和处理。

  • 硬件的连接:将网络摄像头和香橙派都接入路由器LAN口,配置好静态IP。

  • 模型的搭建:使用om量化后模型进行目标检测,用于识别监控区域内的人体。

  • 区域的限制:通过图像划线确定监控区域,将监控区域限制检测范围内。

  • 报警的上传:当检测到有行人时,触发报警机制,将报警信息上传至云端服务器。

  • 平台显示:在云端服务器上搭建一个Web平台,实时显示监控画面和报警信息,方便用户查看。

流程测试和debug过程

我手上的aipro是16G的高配,没有带散热器和外壳。这颗华为的昇腾310 soc发热量巨大,只加铜鳍片的散热片压不住,翻箱倒柜找到一个5V的风扇才压住了。所以购买时一定要考虑散热。

找张64G内存卡去官网下载镜像烧进去就OK,注意启动是选择合适的拨码启动系统,不同的搭配方式启动的存储介质不一样,具体的查阅官方资料。

烧录完系统,插卡,插电,自动开机,输入密码,进入桌面。电源使用65w pd协议

默认系统里面相关的AI算法库都是齐全的,可以跑个官方demo看看。官方demo是Python实现的,IDE用的是jupyter。打开用户目录下samples对应的文件路径并运行jupyter服务。在脚本目录下打开终端,使用 ./start_notebook.sh指令启动服务。运行完效果如下图,终端里会打印浏览器地址。

启动jupyter服务后打开浏览器进入项目页面,进入地址在运行jupyter脚本时有显示。

打开左边01-yolov5文件夹,运行main.ipynb程序即可看到npu推理效果

详见视频:


测试完npu功能后就需要将ipynb文件改成我们项目中的py文件,并根据自己的需求封装成调用接口。我只留下了必要的接口。

图片预处理接口和预处理方法:

def preprocess_image(image, cfg, bgr2rgb=True):
"""图片预处理"""
img, scale_ratio, pad_size = letterbox(image, new_shape=cfg['input_shape'])
if bgr2rgb:
img = img[:, :, ::-1]
img = img.transpose(2, 0, 1) # HWC2CHW
img = np.ascontiguousarray(img, dtype=np.float32)
return img, scale_ratio, pad_size
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=False, scaleFill=False, scaleup=True):
# Resize image to a 32-pixel-multiple rectangle https://github.com/ultralytics/yolov3/issues/232
shape = img.shape[:2] # current shape [height, width]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
# Scale ratio (new / old)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
if not scaleup: # only scale down, do not scale up (for better test mAP)
r = min(r, 1.0)
# Compute padding
ratio = r, r # width, height ratios
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
if auto: # minimum rectangle
dw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh padding
elif scaleFill: # stretch
dw, dh = 0.0, 0.0
new_unpad = (new_shape[1], new_shape[0])
ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios
dw /= 2 # divide padding into 2 sides
dh /= 2
if shape[::-1] != new_unpad: # resize
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
return img, ratio, (dw, dh)识别结果显示,画框


def draw_bbox(bbox, img0, color, wt):

"""在图片上画预测框"""

preson = False
for idx, class_id in enumerate(bbox[:, 5]):
if float(bbox[idx][4] < float(0.05)):
continue
img0 = cv2.rectangle(img0, (int(bbox[idx][0]), int(bbox[idx][1])), (int(bbox[idx][2]), int(bbox[idx][3])), color, wt)
img0 = cv2.putText(img0, config.class_names[int(class_id)] + ' {:.4f}'.format(bbox[idx][4]),
(int(bbox[idx][0]), int(bbox[idx][1] - 10)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
if int(class_id) == 0:
preson = True
return img0, preson

结果的后处理

def def_infer_frame_with_vis(image_o, image):
global model, cfg

# 数据预处理

img, scale_ratio, pad_size = preprocess_image(image, cfg, True)

# 模型推理

output = model.infer([img])[0]
output = torch.tensor(output)
# 非极大值抑制后处理
boxout = nms(output, conf_thres=cfg["conf_thres"], iou_thres=cfg["iou_thres"])
pred_all = boxout[0].numpy()
# 预测坐标转换
scale_coords(cfg['input_shape'], pred_all[:, :4], image.shape, ratio_pad=(scale_ratio, pad_size))
# 图片预测结果可视化
img_vis, preson = draw_bbox(pred_all, image_o, (0, 255, 0), 2)
_ = 0
if len(pred_all) > 0 and preson:
_ = 1
return img_vis, _

接下来就是上监控相机了,我使用的是海康的相机。只要能调用rtsp都ok

将相机和aipro插在同一个路由器上,OpenCV写个rtsp读取的测试代码,测试是否正常采集到视频。

import cv2
rtsp_url = "rtsp://username:password@ip_address:port/stream"
cap = cv2.VideoCapture(rtsp_url)
while True:
ret, frame = cap.read()
if not ret:
print("Error: Unable to read frame.")
break
cv2.imshow('RTSP Stream', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()

视频读取到后需要加入npu的推理功能,实时查看在rtsp上的推理效果,通过下面视频可以看到npu识别到人和瓶子,并且实时性非常高。(视频2是远程录屏的,视频3是手机录像的,录像速度是真实识别速度,所以为了展示aipro真实的速度,接下来的视频都采用手机录像)

详见视频2和3:

查看CPU占用情况,cpu总体占用在20%以下。理论上npu工作时cpu不会满载,因为主要推理计算都在npu上,这也就是aipro作为AI开发板的意义所在。当推理任务进行时,CPU也会有较高的占用是因为像视频解码,图片预处理,输出结果的后处理只能在CPU上运算,npu只是负担了推理这一个环节。

详见视频4:

解决视频读取和实时推理后,接下来增加视频划线功能,也可以叫电子围栏。首先给出我划线方案的代码。代码很简单,创建一个类,将鼠标事件和opencv串口绑定。点击鼠标记录一个点,存在self.marks里,一直点一直存。定义一个按键事件,按下1保存所有点位并应用。

class mark:
def __init__(self, marks, dpoint):
self.marks = []
self.dpoint = False
def draw_circle(self, event, x, y, flags, pam):
if event == cv2.EVENT_LBUTTONDBLCLK:
self.marks.append([int(x), int(y)])
self.dpoint = True
def keyn(self, camcfg):
with open ('points_cfg/cfg%s.pkl'%camcfg,'wb') as f:
pickle.dump(self.marks, f)
self.points_config = np.array(self.marks, dtype=np.int32)
self.marks = []
self.dpoint = False
MARK = mark([], False)

使用鼠标点击多个点,按1闭合并显示。

详见视频5:

画框完成后,过滤绘制窗口外的图形,不进行识别。

if len(MARK.points_config) > 0:
Polygon = np.zeros(frame.shape, np.uint8)
Polygon = cv2.polylines(Polygon, [MARK.points_config], True, (0, 0, 0))
Polygon = cv2.fillPoly(Polygon, [MARK.points_config], (255, 255, 255))
result_img = cv2.bitwise_and(Polygon, frame)
#cv2.imshow("showre", result_img)
else:
result_img = frame

显示效果就是绘制区域外目标不识别

aipro识别到结果进行post推送

def sent(pic, ID, url, label):
pic=base64.b64encode(cv2.imencode('.jpg',pic)[1]).decode()
data = {"alarmDev":ID,"alarmImg":pic,"label":label}
json_mod = json.dumps(data)
header = {"Content-Type":"application/json;charset=UTF-8"}
res = requests.post(url=url,data=json_mod,headers=header)
print("post msg to http server: ", res.content.decode("utf-8"))

服务器搭建个Django后端,建个数据库,数据库创建两张表,记录报警时间,报警类型和报警图片路径

写一个简单的页面,显示最近的报警信息

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Just the latest 5 photos</title>
</head>
<body>
<a href="http://192.168.50.100:8003/show_pic"><input type="button" value='手动刷新'></a>
<strong> &nbsp&nbsp&nbsp&nbsp 数据库图片总数量:{{sum_len}} 张 </strong></br>
</br>
<img src={{pic1}} width='355px' height='200px'>
</br>
<strong>{{name1}} </strong></br>
</br>
<img src={{pic2}} width='355px' height='200px'>
</br>
<strong>{{name2}} </strong></br>
</br>
<img src={{pic3}} width='355px' height='200px'>
</br>
<strong>{{name3}} </strong></br>
</br>
<img src={{pic4}} width='355px' height='200px'>
</br>
<strong>{{name4}} </strong></br>
</br>
<img src={{pic5}} width='355px' height='200px'>
</br>
<strong>{{name5}} </strong></br>
</br>
</br>
</br>
Power By <a href="http://192.168.50.100:8003/admin"> Django </a>
</br>
</br>
</body>
</html>

aipro部署在老家局域网中,局域网里的aipro读取监控将报警画面推送至云服务器,使用浏览器访问云服务器地址即可查看报警画面


云平台实时查看地址:

http://118.89.106.119:8001/show_pic

原文作者: ;链接:https://blog.itpub.net/31545819/viewspace-3014786/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-09-19

  • 博文量
    181
  • 访问量
    398472