AI初学者的利器——香橙派AIpro

引言

激动的心,颤抖的手,轻轻拆开包装香橙派AIpro的兜!直接上图!
在这里插入图片描述
为AI而生
简单直接的标语在这里插入图片描述
看到风扇还是很开心的,毕竟有风扇代表性能释放更彻底。
按捺不住好奇的心,直接HDMI外接显示器、电源、鼠标键盘,上电开机……
dengdengdeng……
注意:此时使用板中间位置的HDMI接口
在这里插入图片描述
欧耶!一次点亮。
此处密码:Mind@123
在这里插入图片描述
登录成功。
摸索了一会系统,稳定了一下情绪,重新来审视香橙派公司香橙派AIpro

香橙派介绍

公司简介(来自官网)

香橙派(Orange Pi)是深圳市迅龙软件有限公司旗下开源产品品牌,迅龙软件成立于2005年,是全球领先的开源硬件和开源软件服务商,致力于让极客、创客、电子爱好者享用到来自中国的优质、低价的科技产品,通过大规模的社会化协作去创建一个更加美好的信息化人类文明。

香橙派AIpro介绍

Orange Pi AI Pro 开发板是香橙派联合华为精心打造的高性能 AI 开发板,其搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算,可广泛用于教育、机器人、无人机等场景。
香橙派AIpro官网介绍: 地址

香橙派AIPro硬件规格参数

类别参数
CPU4核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烧录按键
40PIN40PIN 功能扩展接口,支持以下接口类型: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的启动

  1. 执行cd命令,到sample目录下
  2. 执行start_notebook.sh脚本文件
    在这里插入图片描述
  3. 打开火狐浏览器,在浏览器中输入上边命令行中输出的网址即可。一般是127.0.0.1:8888xxxxxx形式。
    在这里插入图片描述
  4. 如上图,登录后左侧文件管理器中是 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_POLYGONSZONE_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是跑不动检测视频流程序的,结果还是充满了惊喜。

使用心得总结

  1. 香橙派让人很容易联想到大名鼎鼎的树莓派。但是香橙派的的确确是本土的厂家生产。
  2. 本以为香橙派AIpro使用起来会有较多不便和麻烦之处。体验过才知道官方真的很贴心,各种工具 环境部署 示例运行介绍的非常详细。非常适合想学习的小白进行尝试。
  3. 从算力上来说,搭载了升腾AI处理器,npu速度很不错。

综合来说,香橙派AIpro性能强大、使用便捷。可以承载众多极客的丰富想法。愿香橙派越来越好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值