目录
引言
激动的心,颤抖的手,轻轻拆开包装香橙派AIpro的兜!直接上图!
为AI而生
简单直接的标语
看到风扇还是很开心的,毕竟有风扇代表性能释放更彻底。
按捺不住好奇的心,直接HDMI外接显示器、电源、鼠标键盘,上电开机……
dengdengdeng……
注意:此时使用板中间位置的HDMI接口
欧耶!一次点亮。
此处密码:Mind@123
登录成功。
摸索了一会系统,稳定了一下情绪,重新来审视香橙派公司和香橙派AIpro。
香橙派介绍
公司简介(来自官网)
香橙派(Orange Pi)是深圳市迅龙软件有限公司旗下开源产品品牌,迅龙软件成立于2005年,是全球领先的开源硬件和开源软件服务商,致力于让极客、创客、电子爱好者享用到来自中国的优质、低价的科技产品,通过大规模的社会化协作去创建一个更加美好的信息化人类文明。
香橙派AIpro介绍
Orange Pi AI Pro 开发板是香橙派联合华为精心打造的高性能 AI 开发板,其搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算,可广泛用于教育、机器人、无人机等场景。
香橙派AIpro官网介绍: 地址
香橙派AIPro硬件规格参数
类别 | 参数 |
---|---|
CPU | 4核64位处理器+ AI处理器 |
GPU | 集成图形处理器 |
AI算力 | 8-12TOPS算力 |
内存 | LPDDR4X:8GB/16GB(可选),速率:3200Mbps |
存储 | • SPI FLASH:32MB• SATA/NVME SSD(M.2接口2280)• eMMC插槽:32GB/64GB/128GB/256GB(可选),eMMC5.1 HS400• TF插槽 |
WIFI+蓝牙 | Wi-Fi 5双频2.4G和5G BT4.2/BLE |
以太网收发器 | 10/100/1000Mbps以太网 |
显示 | • 2xHDMI2.0 Type-A TX 4K@60FPS• 1x2 lane MIPI DSI via FPC connector |
摄像头 | 2x2-lane MIPI CSI camera interface,兼容树莓派摄像头 |
USB | • USB 3.0 HOST x2 • USB Type-C 3.0 HOST x1 • Micro USB x1 串口打印功能 |
音频 | 3.5mm耳机孔音频输入/输出 |
按键 | 1x关机键、1xRESET键、2x启动方式拨动键、1x烧录按键 |
40PIN | 40PIN 功能扩展接口,支持以下接口类型:GPIO、UART、I2C、SPI、 I2S、PWM |
风扇 | 风扇接口x1 |
预留接口 | 2PIN电池接口 |
电源 | Type-C PD 20V IN ,标准65W |
支持的操作系统 | Ubuntu、openEuler |
产品尺寸 | 107*68mm |
重量 | 82g |
开发板接口详情
注意:图片来自用户手册
来自用户手册的提示:NVMe SSD目前测试了樊想、金士顿和三星的,只有三星的NVMe SSD能稳
定运行Linux系统。非三星品牌的NVMe SSD需要等后续软件更新才能正常使用。
再次贴上香橙派AIpro官网地址:link
按照官网提供的手册、工具、官方镜像很容易烧录系统,在此不做赘述。如有不明白的,请单独私信笔者,如有必要单独写一篇烧录文章。
系统登陆与使用
开机后显示登陆界面,见文章开头的图片,默认用户名HwHiAiUser密码Mind@123。默认没有开启root用户登陆,登陆普通用户后可自行切换到root用户,同样密码是Mind@123。
指示灯
- 电源指示灯 上电就会亮,如果没亮那就是没接好或者板子有问题(板子出问题概率很小)
- MIPI LCD 和 CAMERA0 之间的绿灯:此绿灯由 GPIO4_19 控制其亮灭,可以作为 SATA 硬盘的指示灯或者其他需要的用途。目前发布的 Linux 系统默认在 DTS中将其点亮。当看到此灯点亮后,至少可以说明 Linux 内核已经启动了。
更多开发板的使用细节在此不做赘述,各位移步用户手册进行查看
AI运行实例
既然是AI开发板,那咱们就来跑一跑AI实例。
AI CPU和control CPU的设置方法
香橙派AIpro cpu知识
在实际跑AI实例之前,先了解一下cpu相关知识。
开发板使用的昇腾 SOC 总共有 4 个 CPU,这 4 个 CPU 既可以设置为 controlCPU,也可以设置为 AI CPU。默认情况下,control CPU 和 AI CPU 的分配数量为3:1。使用 npu-smi info 命令可以查看下 control CPU 和 AI CPU 的分配数量。
当 Linux 系统跑满后,使用 htop 命令会看到有一个 CPU 的占用率始终接近 0,请注意,这是正常的。因为这个 CPU 默认用于 AI CPU。
查询AIcpu占用率与cpu类别设置
如果当前环境模型中无 AI CPU 算子,且运行业务时查询 AI CPU 占用率持续为 0,则可以将 AI CPU 的数量配置为 0。查询 AI CPU 占用率的命令如下所示:
如果不需要使用 AI CPU,使用下面的命令可以将 4 个 CPU 都设置为 control CPU。设置完后需要重启系统让配置生效。
命令为:sudo npu-smi set -t cpu-num-cfg -i 0 -c 0 -v 0:4:0
笔者在此不做4此设置,记住有这个命令即可。
Juypter lab使用
JuypterLab介绍
官方的镜像中预装了Jupyter Lab软件。Jupyter Lab 软件是一个基于 web 的交互式开发环境,集成了代码编辑器、终端、文件管理器等功能,使得开发者可以在一个界面中完成各种任务。
登录方法
JuypterLab的启动
- 执行
cd
命令,到sample目录下 - 执行start_notebook.sh脚本文件
- 打开火狐浏览器,在浏览器中输入上边命令行中输出的网址即可。一般是127.0.0.1:8888xxxxxx形式。
- 如上图,登录后左侧文件管理器中是 8 个 AI 应用样例和Jupyter Lab。
JuypterLab使用
JupyterLab的文档由一个个的Cell组成,每个Cell可以选择代码或Markdown或者其它形式。
JupyterLab最大的好处是可以在文档中写代码并运行展示结果:
JuypterLab的其他使用方法和技巧不做过多解释。
运行文字识别样例
文字识别概述
文本识别指从图像中识别出文本,将图像中的文字区域转化为字符信息,通常采用 CNN 网络从图像中提取丰富的特征信息,然后根据提取的特征信息进行识别。这里采用 ResNet 作为特征提取网络,采用 CTC 方法进行识别。此脚本用于将cnnctc模型ckpt文件转换成AIR文件,再转换成OM文件,最后进行离线推理。
1. 目录查找
在JupyterLab界面左侧双击02-CNNCTC,进入到此目录中。
2.准备执行文件
双击main_cnnctc.ipynb文件,此时在右侧窗口显示该文件内容。
3.运行代码
点击启动按钮,开始识别predict.png图片内容
点击Restart。
4. 检查执行结果
非常短时间,程序即可执行完成。在输出中可看到识别出的文字。
其中,还有官方还有其他示例,在此不做过多解释,感兴趣的朋友可自行购买来进行学习。
检测视频流中的车辆示例
原理说明:视频检测原理是逐帧进行分析,并设置跟踪器进行目标跟踪,之后进行计数与标注。
导入依赖库和模块
import argparse
import os
from typing import Dict, List, Set, Tuple
import cv2
import numpy as np
from inference.models.utils import get_roboflow_model
from tqdm import tqdm
import supervision as sv
依赖库使用pip安装在此不做过多解释
准备框选的多边形和边框颜色
COLORS = sv.ColorPalette.default()
ZONE_IN_POLYGONS = [
np.array([[592, 282], [900, 282], [900, 82], [592, 82]]),
np.array([[950, 860], [1250, 860], [1250, 1060], [950, 1060]]),
np.array([[592, 582], [592, 860], [392, 860], [392, 582]]),
np.array([[1250, 282], [1250, 530], [1450, 530], [1450, 282]]),
]
ZONE_OUT_POLYGONS = [
np.array([[950, 282], [1250, 282], [1250, 82], [950, 82]]),
np.array([[592, 860], [900, 860], [900, 1060], [592, 1060]]),
np.array([[592, 282], [592, 550], [392, 550], [392, 282]]),
np.array([[1250, 860], [1250, 560], [1450, 560], [1450, 860]]),
]
管理检测结果类
class DetectionsManager:
"""
管理检测结果的类,用于跟踪检测对象在不同区域中的移动。
"""
def __init__(self) -> None:
"""
初始化检测管理器,设置跟踪ID到区域ID的映射和计数器。
"""
# 跟踪ID到区域ID的映射,用于记录跟踪对象当前所在的区域ID
self.tracker_id_to_zone_id: Dict[int, int] = {}
# 记录检测对象从一个区域移动到另一个区域的计数,用于分析和统计
self.counts: Dict[int, Dict[int, Set[int]]] = {}
def update(
self,
detections_all: sv.Detections,
detections_in_zones: List[sv.Detections],
detections_out_zones: List[sv.Detections],
) -> sv.Detections:
"""
更新检测对象的区域信息,并记录对象在区域间的移动。
参数:
detections_all: 所有检测对象的集合。
detections_in_zones: 每个区域内的检测对象集合。
detections_out_zones: 每个区域外的检测对象集合。
返回:
更新后的所有检测对象集合,仅包含有有效区域ID的对象。
"""
# 更新跟踪ID到区域ID的映射,对于每个区域内的检测对象,记录其区域ID
for zone_in_id, detections_in_zone in enumerate(detections_in_zones):
for tracker_id in detections_in_zone.tracker_id:
self.tracker_id_to_zone_id.setdefault(tracker_id, zone_in_id)
# 记录检测对象从区域外出移到其他区域的情况
for zone_out_id, detections_out_zone in enumerate(detections_out_zones):
for tracker_id in detections_out_zone.tracker_id:
# 如果跟踪ID已在映射中,则记录其从当前区域出移的情况
if tracker_id in self.tracker_id_to_zone_id:
zone_in_id = self.tracker_id_to_zone_id[tracker_id]
# 初始化区域间的移动计数器
self.counts.setdefault(zone_out_id, {})
self.counts[zone_out_id].setdefault(zone_in_id, set())
# 记录跟踪ID的移动
self.counts[zone_out_id][zone_in_id].add(tracker_id)
# 更新所有检测对象的类ID,根据跟踪ID到区域ID的映射
detections_all.class_id = np.vectorize(
lambda x: self.tracker_id_to_zone_id.get(x, -1)
)(detections_all.tracker_id)
# 返回更新后的检测对象集合,移除没有有效区域ID的对象
return detections_all[detections_all.class_id != -1]
初始化多边形
def initiate_polygon_zones(
polygons: List[np.ndarray],
frame_resolution_wh: Tuple[int, int],
triggering_position: sv.Position = sv.Position.CENTER,
) -> List[sv.PolygonZone]:
"""
初始化多边形区域列表。
根据给定的多边形列表、帧分辨率和触发位置,创建并返回一个sv.PolygonZone对象的列表。
每个多边形区域都将在指定的帧分辨率和触发位置下被定义。
参数:
polygons: 多边形列表,每个多边形由一组顶点坐标数组表示。
frame_resolution_wh: 帧的宽度和高度,用于定义多边形区域的分辨率。
triggering_position: 触发位置,指定为多边形的中心或其他角点,默认为中心。
返回:
一个sv.PolygonZone对象的列表,每个对象代表一个在指定帧分辨率和触发位置下的多边形区域。
"""
# 通过列表推导式遍历每个多边形,创建并返回PolygonZone对象的列表
return [
sv.PolygonZone(
polygon=polygon,
frame_resolution_wh=frame_resolution_wh,
triggering_position=triggering_position,
)
for polygon in polygons
]
视频处理类
class VideoProcessor:
"""
视频处理类,用于对视频进行对象检测、跟踪和区域计数。
参数:
- roboflow_api_key: Roboflow API密钥,用于获取预训练模型。
- model_id: 模型ID,指定使用的对象检测模型。
- source_video_path: 输入视频文件的路径。
- target_video_path: 可选,输出视频文件的路径。如果未指定,则在原始视频上显示处理结果。
- confidence_threshold: 置信度阈值,用于过滤检测结果。
- iou_threshold: 交并比阈值,用于非极大值抑制。
"""
def __init__(
self,
roboflow_api_key: str,
model_id: str,
source_video_path: str,
target_video_path: str = None,
confidence_threshold: float = 0.3,
iou_threshold: float = 0.7,
) -> None:
"""
初始化视频处理器,包括加载模型、设置跟踪器和初始化视频信息。
"""
self.conf_threshold = confidence_threshold
self.iou_threshold = iou_threshold
self.source_video_path = source_video_path
self.target_video_path = target_video_path
# 加载对象检测模型
self.model = get_roboflow_model(model_id=model_id, api_key=roboflow_api_key)
# 初始化跟踪器
self.tracker = sv.ByteTrack()
# 初始化视频信息
self.video_info = sv.VideoInfo.from_video_path(source_video_path)
# 初始化区域
self.zones_in = initiate_polygon_zones(
ZONE_IN_POLYGONS, self.video_info.resolution_wh, sv.Position.CENTER
)
self.zones_out = initiate_polygon_zones(
ZONE_OUT_POLYGONS, self.video_info.resolution_wh, sv.Position.CENTER
)
# 初始化标注工具
self.box_annotator = sv.BoxAnnotator(color=COLORS)
self.trace_annotator = sv.TraceAnnotator(
color=COLORS, position=sv.Position.CENTER, trace_length=100, thickness=2
)
# 初始化检测管理器,用于区域计数
self.detections_manager = DetectionsManager()
def process_video(self):
"""
处理整个视频。如果指定了输出视频路径,则将处理结果写入该文件;
否则,在窗口中显示处理结果。
"""
# 获取视频帧生成器
frame_generator = sv.get_video_frames_generator(
source_path=self.source_video_path
)
if self.target_video_path:
# 如果指定了输出视频路径,创建视频输出流
with sv.VideoSink(self.target_video_path, self.video_info) as sink:
for frame in tqdm(frame_generator, total=self.video_info.total_frames):
annotated_frame = self.process_frame(frame)
sink.write_frame(annotated_frame)
else:
# 如果未指定输出视频路径,直接在窗口中显示处理结果
for frame in tqdm(frame_generator, total=self.video_info.total_frames):
annotated_frame = self.process_frame(frame)
cv2.imshow("Processed Video", annotated_frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
def annotate_frame(
self, frame: np.ndarray, detections: sv.Detections
) -> np.ndarray:
"""
标注视频帧。
参数:
- frame: 待标注的视频帧。
- detections: 在该帧中检测到的对象。
返回:
- annotated_frame: 标注后的视频帧。
"""
annotated_frame = frame.copy()
# 绘制区域边界
for i, (zone_in, zone_out) in enumerate(zip(self.zones_in, self.zones_out)):
annotated_frame = sv.draw_polygon(
annotated_frame, zone_in.polygon, COLORS.colors[i]
)
annotated_frame = sv.draw_polygon(
annotated_frame, zone_out.polygon, COLORS.colors[i]
)
# 标注跟踪ID和边界框
labels = [f"#{tracker_id}" for tracker_id in detections.tracker_id]
annotated_frame = self.trace_annotator.annotate(annotated_frame, detections)
annotated_frame = self.box_annotator.annotate(
annotated_frame, detections, labels
)
# 在区域外显示区域内的对象计数
for zone_out_id, zone_out in enumerate(self.zones_out):
zone_center = sv.get_polygon_center(polygon=zone_out.polygon)
if zone_out_id in self.detections_manager.counts:
counts = self.detections_manager.counts[zone_out_id]
for i, zone_in_id in enumerate(counts):
count = len(self.detections_manager.counts[zone_out_id][zone_in_id])
text_anchor = sv.Point(x=zone_center.x, y=zone_center.y + 40 * i)
annotated_frame = sv.draw_text(
scene=annotated_frame,
text=str(count),
text_anchor=text_anchor,
background_color=COLORS.colors[zone_in_id],
)
return annotated_frame
def process_frame(self, frame: np.ndarray) -> np.ndarray:
"""
处理单个视频帧,包括对象检测、跟踪和区域计数。
参数:
- frame: 待处理的视频帧。
返回:
- annotated_frame: 处理后的视频帧。
"""
# 对视频帧进行对象检测
results = self.model.infer(
frame, confidence=self.conf_threshold, iou_threshold=self.iou_threshold
)[0]
detections = sv.Detections.from_inference(results)
detections.class_id = np.zeros(len(detections))
# 对象跟踪
detections = self.tracker.update_with_detections(detections)
# 区域内和区域外的对象分离
detections_in_zones = []
detections_out_zones = []
for i, (zone_in, zone_out) in enumerate(zip(self.zones_in, self.zones_out)):
detections_in_zone = detections[zone_in.trigger(detections=detections)]
detections_in_zones.append(detections_in_zone)
detections_out_zone = detections[zone_out.trigger(detections=detections)]
detections_out_zones.append(detections_out_zone)
# 更新区域计数
detections = self.detections_manager.update(
detections, detections_in_zones, detections_out_zones
)
# 标注视频帧
return self.annotate_frame(frame, detections)
主函数
# 当模块作为主程序运行时,执行以下代码
if __name__ == "__main__":
# 初始化命令行参数解析器,用于配置流量分析工具的参数
parser = argparse.ArgumentParser(
description="Traffic Flow Analysis with Inference and ByteTrack"
)
# 添加模型ID参数,用于指定使用的模型,默认值为"vehicle-count-in-drone-video/6"
parser.add_argument(
"--model_id",
default="vehicle-count-in-drone-video/6",
help="Roboflow model ID",
type=str,
)
# 添加Roboflow API密钥参数,用于访问Roboflow服务,无默认值,必须通过命令行提供
parser.add_argument(
"--roboflow_api_key",
default=None,
help="Roboflow API KEY",
type=str,
)
# 添加源视频路径参数,必须提供,用于指定要分析的视频文件路径
parser.add_argument(
"--source_video_path",
required=True,
help="Path to the source video file",
type=str,
)
# 添加目标视频路径参数,可选,用于指定处理后的视频文件保存路径
parser.add_argument(
"--target_video_path",
default=None,
help="Path to the target video file (output)",
type=str,
)
# 添加置信度阈值参数,用于过滤低置信度的检测结果,默认值为0.3
parser.add_argument(
"--confidence_threshold",
default=0.3,
help="Confidence threshold for the model",
type=float,
)
# 添加IOU阈值参数,用于判断两个边界框是否重叠,默认值为0.7
parser.add_argument(
"--iou_threshold", default=0.7, help="IOU threshold for the model", type=float
)
# 解析命令行参数
args = parser.parse_args()
# 尝试从环境变量中获取Roboflow API密钥,如果未设置,则使用命令行参数提供的密钥
api_key = args.roboflow_api_key
api_key = os.environ.get("ROBOFLOW_API_KEY", api_key)
# 如果没有提供API密钥,则抛出异常
if api_key is None:
raise ValueError(
"Roboflow API KEY is missing. Please provide it as an argument or set the "
"ROBOFLOW_API_KEY environment variable."
)
# 更新命令行参数中的API密钥为最终获取的密钥
args.roboflow_api_key = api_key
# 初始化视频处理类,用于执行视频分析和处理
processor = VideoProcessor(
roboflow_api_key=args.roboflow_api_key,
model_id=args.model_id,
source_video_path=args.source_video_path,
target_video_path=args.target_video_path,
confidence_threshold=args.confidence_threshold,
iou_threshold=args.iou_threshold,
)
# 开始处理视频
processor.process_video()
代码说明
上述代码实现了一个视频处理类VideoProcessor
,用于对视频进行区域检测和跟踪管理。它利用Roboflow
模型进行物体检测,并使用ByteTrack
进行目标跟踪。代码中定义了两个多边形区域列表ZONE_IN_POLYGONS
和ZONE_OUT_POLYGONS
,分别表示区域内的多边形和区域外的多边形。
DetectionsManager
类用于管理检测结果,更新跟踪ID到区域ID的映射关系,并统计进入和离开不同区域的物体数量。
initiate_polygon_zones
函数根据给定的多边形列表、帧分辨率和触发位置,初始化并返回一个区域列表。
VideoProcessor
类的初始化方法__init__
设置了视频处理所需的参数,如API密钥、模型ID、源视频路径、目标视频路径、置信度阈值和IOU阈值。它还创建了检测管理器detections_manager
、跟踪器tracker
和其他辅助对象。
process_video
方法是视频处理的主要方法,它从源视频中提取帧,并对每一帧进行处理。如果指定了目标视频路径,则将处理后的帧写入目标视频文件;否则,通过OpenCV显示处理后的帧。
annotate_frame
方法用于在给定的帧上绘制多边形区域、物体跟踪轨迹和物体数量。
process_frame
方法对给定的帧进行物体检测和跟踪,并根据区域检测结果更新检测管理器。最后,它调用annotate_frame
方法在帧上添加注释,并返回处理后的帧。
运行效果
经过3分钟的等待,看效果
本来以为香橙派AIpro是跑不动检测视频流程序的,结果还是充满了惊喜。
使用心得总结
- 香橙派让人很容易联想到大名鼎鼎的树莓派。但是香橙派的的确确是本土的厂家生产。
- 本以为香橙派AIpro使用起来会有较多不便和麻烦之处。体验过才知道官方真的很贴心,各种工具 环境部署 示例运行介绍的非常详细。非常适合想学习的小白进行尝试。
- 从算力上来说,搭载了升腾AI处理器,npu速度很不错。
综合来说,香橙派AIpro性能强大、使用便捷。可以承载众多极客的丰富想法。愿香橙派越来越好。