• [技术干货] 边缘上跑 LLM,被内核 OOM kill 过几次之后……
    最近在基于昇腾NPU(CANN)的边缘设备上折腾本地LLM推理部署,踩到了一个极其尴尬的坑:服务表现为「偶发性挂掉」,应用层日志里干干净净。去翻 dmesg 才发现是触发了 oom-kill。进程直接被内核 SIGKILL,根本来不及 dump 现场;下次换个并发数或者重启一下,服务又「莫名其妙好了」——问题不可复现,自然也就无从修起。在昇腾平台上,这个问题尤为值得关注。因为NPU的显存和主机DDR是物理隔离的,但实际上我们在做RAG或视觉多模态时,Host侧的内存(预处理、Tokenization、上下文缓存)消耗可能比NPU显存更早到达瓶颈。当CANN的aclrtMalloc申请不到内存时,有时进程并不会立刻报错退出,反而会在后续Host侧的std::bad_alloc或Python内存暴涨中触发内核OOM-Killer,把整个推理进程直接端掉。这让我重新思考了一个问题:在边缘 / 嵌入式设备(尤其是国产化算力平台)的严苛预算里,「不 OOM」到底在证明什么?🔥 吞吐优先 vs 🛡️ 确定性优先很多主流推理框架(无论是基于CUDA还是CANN)的第一性目标,是把吞吐量打满。当内存不够时,常见的结局是交给操作系统来裁决:谁占得多、谁最近活跃,OOM killer 就挑一个杀掉。对开发者来说,这等于把故障现场直接撕掉。另一条路则显得更「保守」:预算不够就主动拒绝。退出码明确、账目打全、现场完全可复现。听起来似乎有些「怂」,但在 12~16 GiB 这种存在硬上限的边缘盒子上,这往往比「再挤一点内存」更靠谱——因为当你真的挤不过去时,你根本来不及抢救。🧪 正向通过只是及格,反向拒绝才算数经历了这几次 OOM 后,我养成了一个习惯:在验证内存治理时,绝不只跑「够用」的正向 Case。正向验证:给足预算(比如 12 / 16 GiB),看内存峰值、看 oom=0、看服务能正常处理对话。反向验证:故意把预算压到绝对装不下(比如限制在 768 MiB 或 4 GiB),观察系统是不是受控拒绝,而不是直接抛出 exit 137(被内核强杀)。💡 正向通过只说明「这次没撞墙」;反向通过才说明「撞墙时是谁在做决定」。内核维护的 memory.events / memory.peak 这类 cgroup 数据没法被用户态随便伪造。因此在我看来,一个坚固的「反向拒绝门」,比单纯漂亮的 tok/s 跑分更有说服力。🛠️ 一套可复现的内存治理“验收口径”如果你也在做边缘推理(无论是昇腾、鲲鹏还是Jetson)的资源治理,建议把验收标准写成「可复跑的命令」,而不是含糊的形容词。以下是一套经过实战检验的 SOP:环境仿真:固定 cgroup 内存上限、禁用 swap、挂载只读根文件系统(尽可能仿真边缘盒子的严苛约束)。正向归档:跑通正向用例,并归档 peak / events 数据。反向拦截:触发反向用例,必须看到明确的 admission reject 日志,且确保 docker_oom_killed=false。构建绑定:只要更换了镜像 digest 或模型 SHA,就必须重跑资格验证。记住:数字不跟人走,跟构建走。这套东西并不玄学,核心就是把「内存账」当成产品级的设计约束,而不是运维事后的补丁。🔗 证据与开源复现最近,我在一个纯 CPU 体验仓库里,严格按照上述口径复跑了一遍(无 GPU/NPU 依赖,提供 OpenAI 兼容接口)。如果你也对这套“内存门”和可复现性证据感兴趣,可以直接 clone 下来查看 evidence/ 目录和反向门测试命令:bashgit clone https://github.com/loopforge-labs/inferloop
  • [技术干货] 电商大促AI客服高并发架构:自动回复、防漏消息与多平台接待实践
    摘要每逢618、双11等大促节点,电商客服系统都会面临咨询量激增、平台消息并发、外部接口限流和人工接待压力上升等问题如果仍然采用同步调用模型、同步查询商品信息、同步发送回复的串行架构,很容易出现请求堆积、消息延迟甚至漏回本文从架构设计角度,介绍一种适用于电商大促场景的AI客服处理链路,包括统一消息接入、异步队列削峰、知识库检索、人机协同、失败重试和多平台聚合,并结合代码示例给出可复用的实现思路1 背景与定位电商AI客服在日常低并发场景下,最简单的实现方式通常是:平台消息 ↓服务端接收 ↓查询商品知识 ↓调用大模型 ↓生成回答 ↓发送平台消息这套流程在咨询量稳定时可以正常运行但进入大促阶段以后,系统往往会同时面临几个问题:多个平台同时进入大量用户消息商品、订单和物流接口请求快速增加大模型调用延迟累积平台回复接口出现频控服务重启或网络波动造成消息处理中断因此,大促场景下的AI客服,核心已经不只是“模型能不能回答问题”,而是整条消息处理链路能否在高并发情况下保持稳定1.1 传统同步架构的主要瓶颈传统同步架构最大的问题,是每条用户消息都需要等待完整业务流程结束例如一条“什么时候发货”的咨询,可能需要依次执行:消息解析 ↓意图识别 ↓物流规则查询 ↓模型生成 ↓风险校验 ↓发送回复如果其中任意一个环节耗时增加,整个请求都会被阻塞大促期间,这种问题会进一步被放大模型调用耗时大模型生成本身存在一定延迟,如果几百甚至几千条咨询同时触发模型请求,连接和线程资源会快速被占满外部接口限流商品、订单、物流以及平台消息接口通常都存在QPS或调用频率限制如果没有统一调度,很容易出现接口限流多平台逻辑重复淘宝、抖店、拼多多、京东等平台的消息字段和回调机制存在差异,如果每个平台单独维护业务逻辑,随着店铺数量增加,系统维护成本会明显提高1.2 架构目标:先接住消息,再异步处理更适合大促场景的思路,是将消息接收和业务处理解耦整体可以采用如下结构:淘宝 / 抖店 / 拼多多 / 京东等平台 ↓ 统一消息接入层 ↓ 消息持久化 ↓ 异步消息队列 ↓ 意图识别层 ↓ 知识检索层 ↓ AI回复生成 ↓ 风险判断 ↙ ↘ AI回复 转人工这种设计的核心是把“高峰流量”转换成“可控队列”消息先进入系统,再按照服务能力逐步消费2 核心架构一:统一消息接入多平台AI客服首先要解决的是消息格式统一如果不同平台的原始字段直接进入业务层,后续逻辑会非常复杂可以先定义统一消息模型:from pydantic import BaseModelclass UnifiedMessage(BaseModel): message_id: str platform: str shop_id: str customer_id: str message_type: str content: str create_time: int淘宝、抖店、拼多多等消息进入系统后,先转换成UnifiedMessage后续的意图识别、知识检索和AI生成就不需要感知具体平台这种统一消息中间层,也是CallFay在多平台电商客服场景中比较重要的应用思路之一,将多个店铺的咨询统一进入接待流程,再进行后续AI处理和人工分流3 核心架构二:异步队列进行削峰填谷大促期间,消息处理不能依赖所有请求同时执行更稳妥的方式是使用消息队列例如:KafkaRabbitMQRocketMQRedis Stream接入层只负责接收并入队例如:from fastapi import FastAPIimport asyncioapp = FastAPI()queue = asyncio.Queue()@app.post("/callback")async def callback(message: UnifiedMessage): await queue.put(message.dict()) return { "success": True, "message_id": message.message_id }随后由消费者异步处理:async def worker(): while True: message = await queue.get() try: await process_message(message) finally: queue.task_done()这样,即使一分钟内进入大量咨询,系统也不会让全部请求同时调用模型和外部接口4 核心架构三:意图路由与知识检索客服消息并不应该全部使用同一套处理逻辑例如:“什么时候发货”属于物流或发货规则“这两个型号有什么区别”属于商品知识“为什么还不给退款”则可能属于售后和人工升级场景因此,模型调用之前最好增加意图路由简单示例:async def route_intent(text: str): if "物流" in text or "发货" in text: return "logistics" if "退款" in text or "售后" in text: return "after_sales" return "product"生产环境可以使用语义分类模型或大模型进行意图识别之后再进入对应知识库用户消息 ↓意图识别 ↓商品 / 物流 / 售后知识 ↓模型组织答案母语AI在这一类电商场景中的价值,更偏向于把商品知识、店铺规则和自然语言回复结合起来,而不是只依赖固定关键词进行回答5 核心架构四:防重复、防漏与失败重试高并发客服系统必须默认网络和外部服务会失败常见异常包括:平台重复推送服务重启模型调用超时平台发送接口失败队列消费异常网络短暂中断因此需要至少设计两层保护5.1 幂等机制可以使用message_id作为唯一键示例:async def process_message(message): message_id = message["message_id"] if await redis.exists(f"msg:{message_id}"): return await redis.set( f"msg:{message_id}", "1", ex=86400 ) await handle_business(message)这样即使平台重复推送,也不会重复回复5.2 失败重试可以根据失败次数做阶梯式重试第一次失败:30秒后第二次失败:2分钟后第三次失败:5分钟后连续失败:进入异常队列异常队列可以由人工或监控系统进一步处理这套机制比“调用失败就结束”更适合大促场景6 人机协同:自动回复并不是全部AI客服系统真正稳定的关键,不是自动回复比例越高越好更合理的方式是分层处理场景处理方式商品参数AI自动回复活动规则AI自动回复发货时间AI自动回复常规物流AI自动回复基础售后AI先判断强烈投诉转人工异常订单转人工敏感金额转人工高价值咨询AI识别后人工跟进这也是母语智能客服更适合的使用方式AI先处理高频、标准和重复问题,人工则负责需要判断和协商的复杂情况7 大促前的监控指标大促架构不能只看“系统有没有挂”还应该监控业务处理链路建议至少关注:消息进入量队列积压量平均处理时间模型调用成功率平台发送成功率转人工率异常队列数量未处理消息数量例如:if queue_size > 1000: send_alert("消息队列积压")if model_error_rate > 0.05: send_alert("模型调用失败率过高")if platform_send_error_rate > 0.03: send_alert("平台回复接口异常")这些指标可以帮助团队提前发现系统瓶颈8 大促前建议的实践流程从实施角度,可以把大促准备分成几个阶段时间主要任务大促前2周整理商品知识与活动规则大促前10天完成AI自动回复配置大促前1周模拟高并发咨询大促前3天检查重试、转人工和异常队列大促当天监控队列、接口和人工负载如果团队不准备从零自研完整客服系统,也可以使用已经具备多平台接入和知识学习能力的SaaS方案进行验证 建议优先使用真实店铺、真实SKU和真实大促规则进行测试9 架构演进方向随着AI客服从自动回复走向业务协同,后续架构可以继续向几个方向演进多Agent拆分将商品咨询、物流、售后、投诉等场景拆成不同Agent知识库实时更新活动规则变化后自动同步知识,减少人工维护智能路由根据用户意图、订单状态和情绪决定走AI还是人工弹性扩容根据队列长度动态增加消费者和模型实例统一数据分析把不同平台的咨询数据统一沉淀,用于分析高频问题和商品需求10 总结电商大促高并发下,AI客服真正需要解决的并不是单点模型能力,而是完整的消息处理架构可以将稳定架构概括为:统一消息接入+消息持久化+异步队列+意图路由+知识检索+AI回复+人工接管+失败重试+监控告警对于多平台、多店铺经营团队来说,这类架构可以有效降低消息积压、重复处理和漏回的风险无论采用自研还是第三方服务,都建议把系统稳定性、知识准确性和人机协同能力放在同等重要的位置AI客服真正的价值,不只是大促期间多回复几条消息,而是在咨询高峰下仍然保持消息可追踪、回复可控制、异常可兜底参考资料Kafka 官方文档RabbitMQ 官方文档FastAPI 官方文档Redis Streams 文档电商平台开放接口与消息推送规范
  • [其他] 模型训练前准备订阅算法失败参数错误(content_id can't be null or empty)怎么办?
    模型训练前准备订阅算法失败参数错误(content_id can't be null or empty)怎么办?
  • [技术交流] OpenClaw(小龙虾)Windows 11 一键部署教程 2026 最新版
    OpenClaw(小龙虾)Windows 11 一键部署教程 2026 最新版 零代码免配置解压即用适用系统:Windows 11 专业版 / 家庭版 / 正式版(全版本兼容) 项目介绍:OpenClaw 是 GitHub 星标 28W + 的开源本地 AI 智能体,支持电脑自动操控、文件整理、浏览器自动化、办公自动化等功能,被国内用户称作小龙虾,部署操作也被形象称为养虾。该工具支持本地运行,数据全程保存在本地电脑,隐私性拉满!本教程特色:专为 Windows 11 系统优化,针对性解决 Win11 权限、Defender、中文路径、SmartScreen 等部署常见问题,双击即可一键安装,10 分钟就能上手使用!一键部署包 v2.6.0 下载地址:https://openclaw.ikidi.top/api/download/package/14?promoCode=IV9D9D5198DC一、前言:Windows 11 安装 OpenClaw 必看说明OpenClaw(小龙虾)是 2026 年热门的本地 AI 自动化智能体,无需联网、无需云端账号、无需付费,就能让 AI 自动完成各类电脑操作,大幅提升办公与操作效率。本教程所使用的是 Windows 11 专属一键部署包,包内内置运行环境、依赖库、系统适配文件,无需额外安装 Python、Node.js,也不用手动操作命令行,新手也能一次部署成功! 二、安装前重要提醒(99% 部署失败均源于此)⚠️ 部署前必须关闭以下软件,否则部署包会被误报、拦截甚至删除文件、360 安全卫士 / 360 杀毒、腾讯电脑管家、火绒安全、Windows 11 自带 Defender 实时防护(必须关闭)OpenClaw 运行时需要实现键鼠模拟、文件读写、浏览器控制等操作,这些行为会被安全软件判定为 “风险操作”,属于正常现象,该工具为开源项目,安全无毒,可放心使用。 三、第一步:下载 Windows 11 专属一键部署包本次使用的 OpenClaw Windows 11 一键部署包为 v2.6.0 版本,文件大小约 361MB,下载完成后将得到一个.zip 格式的压缩包。   四、第二步:正确解压文件(Win11 必看操作)Win11 自带的解压工具偶尔会出现文件丢失、权限不足的问题,建议使用 WinRAR / 7-Zip(基础免费版本即可)进行解压,具体步骤:一键部署包 v2.6.0 下载地址:https://openclaw.ikidi.top/api/download/package/14?promoCode=IV9D9D5198DC1. 右键点击下载好的压缩包2. 选择【解压到当前文件夹】3. 解压完成后将得到名为 Openclaw-win 的文件夹✅ 解压完成后,在文件夹内可看到带有红色龙虾图标的可执行文件:Openclaw Windows 一键启动.exe   五、第三步:运行一键启动程序(Win11 拦截问题解决)Windows 11 会自动拦截未签名的程序,遇到拦截时按以下步骤放行即可正常运行:1. 双击 Openclaw Windows 一键启动.exe2. 系统弹出 “Windows 已保护你的电脑” 提示框3. 点击提示框中的【更多信息】4. 继续点击【仍要运行】完成以上步骤后,即可正常启动安装程序。    六、第四步:自动安装与初始化(全程无需手动操作)程序启动后进入欢迎界面,点击【开始使用】即可进入下一步设置安装路径(Win11 部署关键步骤)核心要求:必须使用纯英文安装路径,路径中不能包含中文、空格、特殊符号!✅ 推荐安装路径:D:\OpenClawE:\AI\OpenClaw❌ 禁止使用安装路径:D:\ 软件 \OpenClawD:\ 小龙虾C:\Program Files\OpenClaw路径设置完成后,点击【开始安装】,安装程序将自动完成以下操作: · 检测 Win11 系统运行环境· 安装工具运行所需依赖· 部署 OpenClaw 核心服务· 自动配置系统对应权限· 创建桌面快捷方式此过程需等待 3~5 分钟,期间请勿关闭安装窗口!   七、第五步:启动成功 & 快速使用指南安装完成后,程序将自动打开 OpenClaw 主界面,当看到界面右上角显示「Gateway 在线」,即代表部署成功!部署成功后可直接向 AI 发送各类操作指令,示例如下:· 帮我整理 D 盘下载文件夹的图片· 打开浏览器搜索 2026 AI 智能体趋势并将结果保存为表格· 帮我批量归类桌面文件· 帮我检查电脑垃圾文件并进行清理发送指令后,AI 将自动执行对应操作,全程无需人工干预!   八、Windows 11 专属常见问题(收藏备用)1. 安装过程中提示 “权限不足”解决方法:右键点击一键启动程序,选择【以管理员身份运行】2. 主界面 Gateway 一直显示离线解决方法:· 检查 Defender 是否完全关闭· 核对安装路径是否为纯英文格式· 关闭程序后重新启动一键启动程序1. 程序启动速度特别慢说明:Win11 系统下第一次启动 OpenClaw 需要完成初始化操作,等待 1~3 分钟均属于正常现象2. AI 无法实现鼠标控制 / 文件读写操作解决方法:开启 Win11 对应系统权限,以管理员身份运行 OpenClaw3. 部署包文件被杀毒软件删除解决方法:关闭杀毒软件 → 重新解压压缩包 → 重新运行安装程序 九、写在最后OpenClaw 是一款真正能实现自动化办公、自动完成电脑操作的本地 AI 工具,在 Windows 11 系统中运行流畅、稳定,且全程本地运行无隐私泄露风险。本次教程所用的一键部署包为 Windows 11 专属优化版,无广告、无捆绑,适配个人办公、日常操作自动化、工作效率提升等各类使用场景。OpenClaw Windows 11 一键部署包 v2.6.0 下载地址:https://openclaw.ikidi.top/api/download/package/14?promoCode=IV9D9D5198DC
  • [技术交流] OpenClaw(小龙虾)Windows 11 一键部署教程 2026 最新版
    OpenClaw(小龙虾)Windows 11 一键部署教程 2026 最新版 零代码免配置解压即用适用系统:Windows 11 专业版 / 家庭版 / 正式版(全版本兼容) 项目介绍:OpenClaw 是 GitHub 星标 28W + 的开源本地 AI 智能体,支持电脑自动操控、文件整理、浏览器自动化、办公自动化等功能,被国内用户称作小龙虾,部署操作也被形象称为养虾。该工具支持本地运行,数据全程保存在本地电脑,隐私性拉满!本教程特色:专为 Windows 11 系统优化,针对性解决 Win11 权限、Defender、中文路径、SmartScreen 等部署常见问题,双击即可一键安装,10 分钟就能上手使用!一键部署包 v2.6.0 下载地址:https://openclaw.ikidi.top/api/download/package/14?promoCode=IV9D9D5198DC一、前言:Windows 11 安装 OpenClaw 必看说明OpenClaw(小龙虾)是 2026 年热门的本地 AI 自动化智能体,无需联网、无需云端账号、无需付费,就能让 AI 自动完成各类电脑操作,大幅提升办公与操作效率。本教程所使用的是 Windows 11 专属一键部署包,包内内置运行环境、依赖库、系统适配文件,无需额外安装 Python、Node.js,也不用手动操作命令行,新手也能一次部署成功! 二、安装前重要提醒(99% 部署失败均源于此)⚠️ 部署前必须关闭以下软件,否则部署包会被误报、拦截甚至删除文件、360 安全卫士 / 360 杀毒、腾讯电脑管家、火绒安全、Windows 11 自带 Defender 实时防护(必须关闭)OpenClaw 运行时需要实现键鼠模拟、文件读写、浏览器控制等操作,这些行为会被安全软件判定为 “风险操作”,属于正常现象,该工具为开源项目,安全无毒,可放心使用。 三、第一步:下载 Windows 11 专属一键部署包本次使用的 OpenClaw Windows 11 一键部署包为 v2.6.0 版本,文件大小约 361MB,下载完成后将得到一个.zip 格式的压缩包。   四、第二步:正确解压文件(Win11 必看操作)Win11 自带的解压工具偶尔会出现文件丢失、权限不足的问题,建议使用 WinRAR / 7-Zip(基础免费版本即可)进行解压,具体步骤:一键部署包 v2.6.0 下载地址:https://openclaw.ikidi.top/api/download/package/14?promoCode=IV9D9D5198DC1. 右键点击下载好的压缩包2. 选择【解压到当前文件夹】3. 解压完成后将得到名为 Openclaw-win 的文件夹✅ 解压完成后,在文件夹内可看到带有红色龙虾图标的可执行文件:Openclaw Windows 一键启动.exe   五、第三步:运行一键启动程序(Win11 拦截问题解决)Windows 11 会自动拦截未签名的程序,遇到拦截时按以下步骤放行即可正常运行:1. 双击 Openclaw Windows 一键启动.exe2. 系统弹出 “Windows 已保护你的电脑” 提示框3. 点击提示框中的【更多信息】4. 继续点击【仍要运行】完成以上步骤后,即可正常启动安装程序。    六、第四步:自动安装与初始化(全程无需手动操作)程序启动后进入欢迎界面,点击【开始使用】即可进入下一步设置安装路径(Win11 部署关键步骤)核心要求:必须使用纯英文安装路径,路径中不能包含中文、空格、特殊符号!✅ 推荐安装路径:D:\OpenClawE:\AI\OpenClaw❌ 禁止使用安装路径:D:\ 软件 \OpenClawD:\ 小龙虾C:\Program Files\OpenClaw路径设置完成后,点击【开始安装】,安装程序将自动完成以下操作: · 检测 Win11 系统运行环境· 安装工具运行所需依赖· 部署 OpenClaw 核心服务· 自动配置系统对应权限· 创建桌面快捷方式此过程需等待 3~5 分钟,期间请勿关闭安装窗口!   七、第五步:启动成功 & 快速使用指南安装完成后,程序将自动打开 OpenClaw 主界面,当看到界面右上角显示「Gateway 在线」,即代表部署成功!部署成功后可直接向 AI 发送各类操作指令,示例如下:· 帮我整理 D 盘下载文件夹的图片· 打开浏览器搜索 2026 AI 智能体趋势并将结果保存为表格· 帮我批量归类桌面文件· 帮我检查电脑垃圾文件并进行清理发送指令后,AI 将自动执行对应操作,全程无需人工干预!   八、Windows 11 专属常见问题(收藏备用)1. 安装过程中提示 “权限不足”解决方法:右键点击一键启动程序,选择【以管理员身份运行】2. 主界面 Gateway 一直显示离线解决方法:· 检查 Defender 是否完全关闭· 核对安装路径是否为纯英文格式· 关闭程序后重新启动一键启动程序1. 程序启动速度特别慢说明:Win11 系统下第一次启动 OpenClaw 需要完成初始化操作,等待 1~3 分钟均属于正常现象2. AI 无法实现鼠标控制 / 文件读写操作解决方法:开启 Win11 对应系统权限,以管理员身份运行 OpenClaw3. 部署包文件被杀毒软件删除解决方法:关闭杀毒软件 → 重新解压压缩包 → 重新运行安装程序 九、写在最后OpenClaw 是一款真正能实现自动化办公、自动完成电脑操作的本地 AI 工具,在 Windows 11 系统中运行流畅、稳定,且全程本地运行无隐私泄露风险。本次教程所用的一键部署包为 Windows 11 专属优化版,无广告、无捆绑,适配个人办公、日常操作自动化、工作效率提升等各类使用场景。OpenClaw Windows 11 一键部署包 v2.6.0 下载地址:https://openclaw.ikidi.top/api/download/package/14?promoCode=IV9D9D5198DC
  • [技术干货] Web 前端开发趋势:元框架、WebGPU 与 AI 协作
    数字化转型进入深水区,企业业务正朝着云化、全链路交互、实时渲染、智能化快速迭代 —— 中后台系统走向一体化、面向用户的产品追求极致体验与 3D 可视化、AI 能力从后台逐步走向前端实时交互,传统前端架构(组件堆砌、配置繁琐、渲染靠 CPU、协作靠人工)早已力不从心,开发效率低、首屏速度慢、渲染性能差、AI 能力接入成本高等痛点日益突出。 在此背景下,元框架、WebGPU 与 AI 协作的深度融合,成为前端架构演进的必然趋势。三者并非孤立存在,而是形成工程化 — 渲染能力 —AI 赋能三位一体的现代化前端体系,彻底打破传统前端的边界限制,为企业数字化产品提供更快、更强、更智能的前端支撑。 本文从核心定位、融合逻辑、实战价值、落地路径四个维度,拆解这场前端技术变革,适合前端开发者、技术负责人、架构师参考。  一、先理清:三者核心定位,为何必须融合? 在探讨融合之前,先明确三者的核心价值和定位 —— 它们并非替代关系,而是互补协同,共同解决传统前端的核心痛点,形成研发 — 运行 — 智能一体化闭环。 1. 三者核心定位拆解 元框架(Meta Framework) 基于 React / Vue / Svelte 等基础框架再封装,内置路由、构建、SSR/SSG、数据获取、缓存策略,解决传统前端工程化复杂、配置碎片化、性能优化难、SEO / 首屏慢问题,让开发从 “搭环境” 转向 “写业务”,是现代前端的统一研发底座。 WebGPU 下一代浏览器图形计算标准,替代 WebGL,直接调用 GPU 并行计算能力,解决传统前端复杂图表卡顿、3D 场景带不动、视频处理 / 图像处理性能不足问题,让网页具备原生级渲染与计算能力,是前端走向高性能、重视觉、重计算的硬件基石。 AI 协作(前端 + AI) 将大模型、本地推理、智能提示、智能交互能力下沉到前端,实现智能代码补全、智能表单、智能搜索、实时润色、本地 AI 助手,解决传统前端交互生硬、逻辑死板、重复性工作多、用户体验不够智能问题,让前端从 “展示层” 升级为智能交互入口。  2. 融合的核心动因:解决传统前端四大痛点 传统前端架构在面对云化、高性能、智能化业务时,存在无法逾越的瓶颈,而三者融合恰好能精准破解: 痛点 1:研发效率低 重复配置构建工具、路由、权限、请求封装,项目越大越难维护; 融合后:元框架提供标准化架构,AI 协作自动生成代码、优化逻辑,效率翻倍。 痛点 2:体验与性能差 首屏慢、白屏长、复杂页面卡顿、3D / 可视化跑不起来; 融合后:元框架提供 SSR/SSG/ 流式渲染,WebGPU 提供 GPU 加速,体验质变。 痛点 3:AI 接入成本高 前端想做智能交互,必须依赖后端接口,无法离线、延迟高、不灵活; 融合后:前端直接运行轻量模型,结合 WebGPU 加速推理,实现实时低延迟 AI 能力。 痛点 4:维护与扩展难 多项目技术栈不统一、性能优化靠经验、智能功能重复开发; 融合后:统一元框架架构 + WebGPU 标准化能力 + AI 通用协作层,低成本扩展。  二、核心融合逻辑:工程化 — 渲染 —AI 一体化闭环元框架、WebGPU 与 AI 协作的融合,本质是构建统一研发、高性能运行、智能交互的现代化前端体系,三者各司其职、协同联动,形成完整闭环,具体可分为三个层面:1. 基础设施层融合:统一研发 + 高性能运行 元框架作为项目骨架,提供路由、构建、数据获取、服务端渲染能力; WebGPU 作为底层加速能力,为3D、可视化、图像处理、AI 推理提供硬件支持; AI 模型(如前端小型 LLM、CV 模型)跑在 WebGPU 之上,实现前端本地加速推理。 例如:企业级大屏 / 数字孪生项目元框架负责工程化、权限、路由、数据请求;WebGPU 负责 3D 场景、实时曲线、海量点云渲染;AI 负责异常识别、语音交互、智能标注,且借助 WebGPU 加速。 2. 研发层融合:AI 协作 + 元框架,提升开发效率AI 协作深度嵌入研发流程:基于元框架目录结构,AI 自动生成页面、接口、表单、权限代码;AI 自动优化 WebGPU 渲染逻辑、着色器、性能参数;AI 自动检查性能问题、内存泄漏、渲染卡顿。最终实现: 需求 → AI 生成 → 元框架运行 → WebGPU 加速 人只负责业务逻辑与体验微调,大幅降低重复劳动。  3. 应用层融合:适配多样化业务场景融合架构并非 “一刀切”,而是根据场景灵活组合: 中后台系统场景 元框架做主架构 + AI 协作做智能表单 / 智能搜索 / 智能表格 追求:开发快、体验统一、维护成本低 可视化 / 数字孪生场景 元框架 + WebGPU + AI 异常检测 追求:海量数据渲染不卡、实时性高、智能预警 AIGC 交互场景 元框架 + WebGPU + 前端本地 AI 追求:低延迟、离线可用、交互自然、隐私安全  三、实战价值:企业与开发者能获得什么?1. 研发效率显著提升项目初始化从 1 天 → 5 分钟页面 / 表单 / 权限代码由 AI 自动生成统一架构,新人上手更快2. 前端性能质的飞跃首屏速度提升 50%–200%(SSR/SSG/ 流式渲染)复杂图表 / 3D 场景帧率提升 3–10 倍AI 推理延迟从秒级 → 毫秒级3. 产品体验全面智能化智能搜索、智能补全、智能建议实时语言润色、OCR、物体识别离线可用、数据不上云,更安全4. 技术架构长期稳定元框架统一规范,减少 “技术债务”WebGPU 是未来 5–10 年标准,一次投入长期受益AI 能力模块化,可快速接入新模型 四、落地路径:循序渐进,低成本落地1. 起步阶段:统一元框架先把新项目统一到 Next.js/ Nuxt / SvelteKit 等元框架,解决工程化、性能、SEO问题。2. 进阶阶段:引入 AI 协作接入 AI 代码助手、智能表单、智能搜索,先从中后台、工具型产品落地。3. 高阶阶段:接入 WebGPU + 前端 AI在可视化、3D、图像处理、实时交互场景中,使用 WebGPU 加速,并尝试前端本地 AI 推理。4. 融合阶段:一体化平台形成: 元框架底座 + WebGPU 能力中心 + AI 协作层 全公司产品统一使用,实现规模化收益。  五、避坑指南(实战总结)不要一上来全量重构:先试点、再推广不要盲目上 WebGPU:只在真正需要高性能渲染时使用AI 协作不要完全依赖:人依然负责逻辑正确性与安全注意浏览器兼容性:老项目做降级方案重视用户隐私:前端 AI 尽量本地运行,减少数据上传 六、总结元框架、WebGPU 与 AI 协作的融合,不是技术噱头,而是前端现代化的必然方向—— 它从根本上解决了传统前端效率低、性能弱、不智能的核心痛点,构建了工程化统一、渲染极致、交互智能的新一代前端架构。 对企业而言,这是提升产品竞争力、降低研发成本的关键; 对开发者而言,这是未来 3–5 年必须掌握的核心能力。 前端早已不是 “写页面”,而是全栈化、高性能、智能化的用户体验中枢。
  • [技术干货] 识别你的手势,它很有一手
    CNN-RNN 视频动态手势识别人工智能的发展日新月异,也深刻的影响到人机交互领域的发展。手势动作作为一种自然、快捷的交互方式,在智能驾驶、虚拟现实等领域有着广泛的应用。手势识别的任务是,当操作者做出某个手势动作后,计算机能够快速准确的判断出该手势的类型。本文将使用ModelArts开发训练一个视频动态手势识别的算法模型,对上滑、下滑、左滑、右滑、打开、关闭等动态手势类别进行检测,实现类似华为手机隔空手势的功能。算法简介CNN-RNN视频动态手势识别算法首先使用预训练网络InceptionResNetV2逐帧提取视频动作片段特征,然后输入LSTM进行分类。我们使用全栈AI黑客松决赛样例数据对算法进行测试,总共包含108段视频,数据集包含无效手势、上滑、下滑、左滑、右滑、打开、关闭、放大、缩小等9种手势的视频,数据集下载链接如下:https://developer.huaweicloud.com/develop/aigallery/dataset/detail?id=7e9c0d90-461f-4af2-93b3-67a8df76c109代码实现首先我们将采集的视频文件解码抽取关键帧,每隔4帧保存一次,然后对图像进行中心裁剪和预处理,代码如下:def load_video(file_name): cap = cv2.VideoCapture(file_name) # 每隔多少帧抽取一次 frame_interval = 4 frames = [] count = 0 while True: ret, frame = cap.read() if not ret: break # 每隔frame_interval帧保存一次 if count % frame_interval == 0: # 中心裁剪 frame = crop_center_square(frame) # 缩放 frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE)) # BGR -> RGB [0,1,2] -> [2,1,0] frame = frame[:, :, [2, 1, 0]] frames.append(frame) count += 1 return np.array(frames) 然后我们创建图像特征提取器,使用预训练模型InceptionResNetV2提取图像特征,代码如下:def get_feature_extractor(): feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2( weights = 'imagenet', include_top = False, pooling = 'avg', input_shape = (IMG_SIZE, IMG_SIZE, 3) ) preprocess_input = keras.applications.inception_resnet_v2.preprocess_input inputs = keras.Input((IMG_SIZE, IMG_SIZE, 3)) preprocessed = preprocess_input(inputs) outputs = feature_extractor(preprocessed) model = keras.Model(inputs, outputs, name = 'feature_extractor') return model接着提取视频特征向量,如果视频不足40帧就创建全0数组进行补白:def load_data(videos, labels): video_features = [] for video in tqdm(videos): frames = load_video(video) counts = len(frames) # 如果帧数小于MAX_SEQUENCE_LENGTH if counts < MAX_SEQUENCE_LENGTH: # 补白 diff = MAX_SEQUENCE_LENGTH - counts # 创建全0的numpy数组 padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # 数组拼接 frames = np.concatenate((frames, padding)) # 获取前MAX_SEQUENCE_LENGTH帧画面 frames = frames[:MAX_SEQUENCE_LENGTH, :] # 批量提取特征 video_feature = feature_extractor.predict(frames) video_features.append(video_feature) return np.array(video_features), np.array(labels) 最后创建LSTM Model,代码如下:def video_cls_model(class_vocab): # 类别数量 classes_num = len(class_vocab) # 定义模型 model = keras.Sequential([ layers.Input(shape=(MAX_SEQUENCE_LENGTH, NUM_FEATURES)), layers.LSTM(64, return_sequences=True), layers.Flatten(), layers.Dense(classes_num, activation='softmax') ]) # 编译模型 model.compile(optimizer = keras.optimizers.Adam(1e-5), loss = keras.losses.SparseCategoricalCrossentropy(from_logits=False), metrics = ['accuracy'] ) return model模型训练体验完整的训练流程可以点击Run in ModelArts运行我发布的Notebook使用云上的免费算力训练,代码链接如下:https://developer.huaweicloud.com/develop/aigallery/notebook/detail?id=e8914ecc-953e-48b1-8e5d-90b37b3bc8e9视频推理首先加载LSTM Model,获取视频类别索引标签:import random # 加载模型 model = tf.keras.models.load_model('saved_model') # 类别标签 label_to_name = {0:'无效手势', 1:'上滑', 2:'下滑', 3:'左滑', 4:'右滑', 5:'打开', 6:'关闭', 7:'放大', 8:'缩小'} 然后使用图像特征提取器InceptionResNetV2提取视频特征:# 获取视频特征 def getVideoFeat(frames): frames_count = len(frames) # 如果帧数小于MAX_SEQUENCE_LENGTH if frames_count < MAX_SEQUENCE_LENGTH: # 补白 diff = MAX_SEQUENCE_LENGTH - frames_count # 创建全0的numpy数组 padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3)) # 数组拼接 frames = np.concatenate((frames, padding)) # 取前MAX_SEQ_LENGTH帧 frames = frames[:MAX_SEQUENCE_LENGTH,:] # 计算视频特征 N, 1536 video_feat = feature_extractor.predict(frames) return video_feat最后将视频序列的特征向量输入LSTM进行预测:# 视频预测 def testVideo(): test_file = random.sample(videos, 1)[0] label = test_file.split('_')[-2] print('文件名:{}'.format(test_file) ) print('真实类别:{}'.format(label_to_name.get(int(label))) ) # 读取视频每一帧 frames = load_video(test_file) # 挑选前帧MAX_SEQUENCE_LENGTH显示 frames = frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8) # 保存为GIF imageio.mimsave('animation.gif', frames, duration=10) # 获取特征 feat = getVideoFeat(frames) # 模型推理 prob = model.predict(tf.expand_dims(feat, axis=0))[0] print('预测类别:') for i in np.argsort(prob)[::-1][:5]: print('{}: {}%'.format(label_to_name[i], round(prob[i]*100, 2))) return display(Image(open('animation.gif', 'rb').read())) 运行testVideo()函数,会随机选择一个视频进行预测,并显示模型的预测结果:文件名:hand_gesture/man_005_3_1.mp4 真实类别:左滑 预测类别: 左滑: 78.32% 右滑: 8.54% 下滑: 5.51% 无效手势: 4.33% 上滑: 1.67%文章小结本文介绍了基于CNN-RNN架构的视频动态手势识别算法,通过结合InceptionResNetV2卷积神经网络和LSTM循环神经网络,实现了对多种手势动作(如上滑、下滑、左滑、右滑、打开、关闭等)的高效识别。
  • 图像增加小知识点
    图像增强的核心目标是提升图像质量(如清晰度、对比度)、突出关键信息,或为后续任务(如目标检测、医学影像分析、遥感解译)优化数据。根据技术原理,可分为空域增强、频域增强、深度学习增强三大类。一、空域增强:直接操作像素域(最基础、应用最广)空域增强通过修改图像像素的灰度值或空间位置实现,无需转换到其他域,计算效率高,适合实时场景(如监控、手机拍照)。1. 灰度变换:调整像素灰度分布(提升对比度 / 亮度)通过函数映射改变每个像素的灰度值,核心是优化灰度动态范围,解决 “过暗 / 过曝”“对比度低” 问题。(1)线性灰度变换(对比度拉伸)原理:用线性函数 g(x,y) = a*f(x,y) + b 调整灰度,其中 a 控制对比度(a>1 增强,0<a<1 降低),b 控制亮度(b>0 提亮,b<0 变暗)。适用场景:图像整体灰度偏暗 / 偏亮(如逆光照片、监控夜间图像)。示例:将灰度范围 [50,200] 拉伸到 [0,255],突出暗部细节。(2)非线性灰度变换(伽马校正)原理:用幂函数 g(x,y) = c*f(x,y)^γ 调整,γ 是关键参数:γ<1:增强暗部灰度(适合过暗图像,如室内低光照片);γ>1:增强亮部灰度(适合过曝图像,如晴天雪地照片)。特点:比线性变换更贴合人眼对亮度的非线性感知,手机相机 “夜景模式” 常用此技术。(3)直方图处理(均衡化 / 匹配)全局直方图均衡化(GHE):原理:将图像灰度直方图从 “集中分布” 变为 “均匀分布”,最大化灰度动态范围。适用场景:对比度低且灰度分布均匀的图像(如雾天照片、医学 X 光片)。缺点:可能导致局部过曝(如大面积暗部区域被过度拉伸)。自适应直方图均衡化(CLAHE):改进:将图像分割为多个子块(如 8×8),对每个子块单独均衡化,避免全局过曝。核心场景:医学影像(如 CT、眼底照片)—— 需保留局部细节(如肿瘤边缘),不允许整体过曝。直方图匹配(规定化):原理:将图像直方图调整为 “目标直方图”(如参考清晰图像的直方图),用于颜色一致性校正(如批量处理监控摄像头图像)。2. 空间滤波:基于邻域像素的局部增强(去噪 / 锐化)通过 “滤波核(卷积核)” 与图像卷积,改变像素的局部灰度分布,实现去噪或锐化。(1)平滑滤波(去噪为主)核心是 “平均邻域像素”,抑制高频噪声(如椒盐噪声、高斯噪声),但会轻微模糊边缘。均值滤波:用邻域像素平均值替换中心像素,去高斯噪声效果一般,易模糊细节。高斯滤波:用高斯函数加权平均邻域像素(中心权重高、边缘低),去高斯噪声效果好,模糊程度可控( sigma 越大越模糊)。中值滤波:用邻域像素的中值替换中心像素,去椒盐噪声(黑白斑点)效果最优,且能保留边缘(非线性滤波,不平均边缘像素)。适用场景:监控视频去噪(高斯滤波)、老照片修复去斑点(中值滤波)。(2)锐化滤波(突出边缘)核心是 “增强邻域像素的灰度差异”,突出高频细节(如边缘、纹理),弥补平滑滤波的模糊。拉普拉斯滤波:通过计算邻域像素与中心像素的灰度差,强化边缘(如文字边缘、物体轮廓),但会放大噪声(需先去噪再锐化)。Sobel 滤波:分别计算水平和垂直方向的边缘梯度,可单独增强水平 / 垂直边缘(如遥感影像中的道路边缘、建筑轮廓)。USM 锐化(非锐化掩模):先对图像模糊(生成 “掩模”),再用原图减去掩模,增强细节对比度,是 Photoshop “锐化” 功能的核心算法。3. 几何变换:调整图像空间位置(对齐 / 适配)不改变像素灰度,仅调整像素的空间坐标,属于 “预处理型增强”,为后续任务(如目标检测、图像拼接)做准备。常见类型:平移(图像裁剪后对齐)、旋转(校正倾斜照片)、缩放(插值缩放,如双线性插值 —— 平滑缩放,双三次插值 —— 保留细节)、仿射变换(校正透视畸变,如手机拍文档的倾斜校正)。核心场景:OCR 文字识别(需先旋转校正倾斜文档)、无人机遥感拼接(需平移 / 缩放对齐多幅图像)。 二、频域增强:基于频率成分的全局增强(去周期性噪声 / 全局锐化)将图像通过傅里叶变换转换到 “频域”(分解为低频和高频成分):低频成分:图像的整体轮廓、大面积灰度(如天空、墙面);高频成分:图像的细节、边缘、噪声(如物体边缘、椒盐噪声)。 通过修改频域成分(保留 / 抑制高低频),再逆傅里叶变换回空域,实现增强。1. 低通滤波(保留低频,平滑去噪)抑制高频噪声,保留低频轮廓,效果类似空域的平滑滤波,但全局平滑更均匀。示例:高斯低通滤波 —— 在频域中抑制高频区域,去全局高斯噪声效果优于空域均值滤波,适合天文影像(如星空照片去宇宙射线噪声)。2. 高通滤波(保留高频,全局锐化)抑制低频模糊,保留高频细节,效果类似空域的锐化滤波,但全局锐化更自然。示例:理想高通滤波 —— 在频域中保留高频区域,增强遥感影像中的细小目标(如农田边界、电力线),但会产生 “振铃效应”(边缘出现明暗条纹),需用高斯高通滤波优化。3. 带通 / 带阻滤波(针对性处理)带通滤波:保留特定频率范围(如介于高低频之间的纹理信息),用于增强医学影像中的血管纹理(如眼底照片的血管)。带阻滤波:抑制特定频率范围,去周期性噪声效果最优(如监控摄像头因电源干扰产生的横纹 / 竖纹,其噪声频率固定,可精准抑制)。三、深度学习增强:现代技术(复杂场景下效果远超传统方法)传统方法依赖人工设计规则(如滤波核、灰度函数),对复杂场景(如超分辨率、去雾、医学影像细节增强)效果有限;深度学习通过数据驱动学习增强规则,能处理更复杂的图像退化问题。1. 超分辨率重建(SR):提升图像分辨率核心是 “从低分辨率(LR)图像生成高分辨率(HR)图像”,解决 “图像模糊、细节缺失” 问题(如老照片放大、监控图像清晰度提升)。经典模型:SRCNN(首个基于 CNN 的超分模型):用 3 层 CNN 学习 LR 到 HR 的映射,比传统插值(双三次)效果好,但细节不够精细。ESRGAN(基于 GAN 的超分):引入生成对抗网络,生成的 HR 图像细节更真实(如毛发、纹理),是当前 “图像放大” 工具(如 Topaz Gigapixel AI)的核心算法。Real-ESRGAN:针对真实世界模糊图像(如老照片、压缩失真)优化,去模糊 + 超分一体,修复效果远超传统方法。2. 图像去退化(去雾 / 去噪 / 去模糊)去雾:传统方法(如暗通道先验)依赖大气散射模型,对浓雾效果有限;深度学习方法(如 DehazeNet、GCA-Net)通过学习大量雾天 / 无雾图像对,直接生成去雾图像,可处理浓雾、不均匀雾(如城市雾霾照片)。去噪:传统方法(如 BM3D)对复杂噪声(混合高斯 + 椒盐噪声)效果一般;深度学习方法(如 DnCNN、RIDNet)通过残差学习直接学习 “噪声模式”,去噪的同时保留更多细节(如医学影像去噪 —— 不模糊肿瘤边缘)。去模糊:针对运动模糊(如手抖拍的照片)、失焦模糊,深度学习模型(如 DeblurGAN)可学习模糊核,反向恢复清晰图像,比传统盲去模糊效果好。3. 医学 / 遥感影像专用增强医学影像:如 UNet++ 增强 CT 图像中的肺结节边缘,或用注意力机制(如 ResUNet)突出 MRI 图像中的神经纤维束,辅助医生诊断。遥感影像:如用 SegSRNet 同时实现超分和地物分类增强(如区分农田与建筑),提升遥感解译精度。4. 数据增强(为模型训练服务)属于 “人工生成多样性数据”,目的是提升深度学习模型的泛化能力(而非提升单张图像质量),常用方法:基础操作:随机翻转、旋转、裁剪、缩放(扩充训练集);进阶操作:颜色抖动(随机调整亮度 / 对比度 / 饱和度,模拟不同光照)、MixUp(两张图像加权混合,增强模型鲁棒性)、CutMix(裁剪部分区域替换为其他图像,保留局部结构);核心场景:目标检测、图像分类训练(如自动驾驶数据集增强,模拟不同天气、光照下的道路场景)。 四、一些常见的场景和方法应用场景推荐方法核心原因医学影像(CT / 眼底)CLAHE、ResUNet、医学专用去噪模型需保留局部细节,避免过曝 / 模糊关键结构监控视频 / 老照片修复中值滤波(去斑点)、Real-ESRGAN(超分)处理椒盐噪声 + 低分辨率,提升清晰度雾天 / 低光照片伽马校正、DehazeNet(去雾)、USM 锐化提亮暗部 + 去雾 + 突出细节,还原真实场景遥感影像解译高斯高通滤波(边缘增强)、SegSRNet(超分)突出地物边缘,提升小目标(如电力线)识别率深度学习模型训练随机翻转、CutMix、颜色抖动扩充数据集多样性,提升模型泛化能力总结一下下图像增强方法的选择需结合场景需求(去噪 / 锐化 / 超分)、图像退化类型(噪声 / 模糊 / 雾) 及实时性要求:实时场景(如监控、手机拍照)优先用传统空域方法(高斯滤波、CLAHE);复杂场景(如医学影像、老照片修复)优先用深度学习方法(ESRGAN、医学专用模型);周期性噪声(如横纹干扰)优先用频域带阻滤波。 随着 AI 技术发展,深度学习增强正逐步取代传统方法,成为复杂场景下的首选。
  • [技术干货] 猫脸关键点检测(ModelBox)
    猫脸关键点检测(ModelBox)一、模型训练与转换ResNet50V2是改进版的深度卷积神经网络,基于 ResNet 架构发展而来。它采用前置激活(将 BN 和 ReLU 移至卷积前)与身份映射,优化了信息传播和模型训练性能。作为 50 层深度的网络,ResNet50V2 广泛应用于图像分类、目标检测等任务,支持迁移学习,适合快速适配新数据集,具有良好的泛化能力和较高准确率。模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本。在ModelArts的Notebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。二、应用开发1. 创建工程在ModelBox sdk目录下使用create.bat创建ResNet50V2工程:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t server -n ResNet50V2 ... success: create ResNet50V2 in D:\modelbox-win10-x64-1.5.3\workspacecreate.bat工具的参数中,-t参数,表示所创建实例的类型,包括server(ModelBox工程)、python(Python功能单元)、c++(C++功能单元)、infer(推理功能单元)等;-n参数,表示所创建实例的名称;-s参数,表示将使用后面参数值代表的模板创建工程,而不是创建空的工程。2. 创建推理功能单元在ModelBox sdk目录下使用create.bat创建resnet50v2_infer推理功能单元:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t infer -n resnet50_infer -p ResNet50V2 ... success: create infer resnet50_infer in D:\modelbox-win10-x64-1.5.3\workspace\ResNet50V2/model/resnet50_infercreate.bat工具使用时,-t infer即表示创建的是推理功能单元;-n xxx_infer表示创建的功能单元名称为xxx_infer;-p表示所创建的功能单元属于ResNet50V2应用。下载转换好的ResNet50V2.onnx模型到ResNet50V2\model目录下,修改推理功能单元resnet50v2_infer.toml模型的配置文件:# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. [base] name = "resnet50_infer" device = "cpu" version = "1.0.0" description = "your description" entry = "./ResNet50V2.onnx" # model file path, use relative path type = "inference" virtual_type = "onnx" # inference engine type: win10 now only support onnx group_type = "Inference" # flowunit group attribution, do not change # Input ports description [input] [input.input1] # input port number, Format is input.input[N] name = "Input" # input port name type = "float" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type: cpu, win10 now copy input from cpu # Output ports description [output] [output.output1] # output port number, Format is output.output[N] name = "Output" # output port name type = "float" # output port data type ,e.g. float or uint83. 创建后处理功能单元在ModelBox sdk目录下使用create.bat创建resnet50v2_post后处理功能单元:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t python -n resnet50v2_post -p ResNet50V2 ... success: create python resnet50v2_post in D:\modelbox-win10-x64-1.5.3\workspace\ResNet50V2/etc/flowunit/resnet50v2_postcreate.bat工具使用时,-t python即表示创建的是通用功能单元;-n xxx_post表示创建的功能单元名称为xxx_post;-p表示所创建的功能单元属于ResNet50V2应用。a. 修改配置文件我们的模型有一个输入和输出,总共包含猫脸的9个关键点:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. # Basic config [base] name = "resnet50v2_post" # The FlowUnit name device = "cpu" # The flowunit runs on cpu version = "1.0.0" # The version of the flowunit type = "python" # Fixed value, do not change description = "description" # The description of the flowunit entry = "resnet50v2_post@resnet50v2_postFlowUnit" # Python flowunit entry function group_type = "Generic" # flowunit group attribution, change as Input/Output/Image/Generic ... # Flowunit Type stream = false # Whether the flowunit is a stream flowunit condition = false # Whether the flowunit is a condition flowunit collapse = false # Whether the flowunit is a collapse flowunit collapse_all = false # Whether the flowunit will collapse all the data expand = false # Whether the flowunit is a expand flowunit # The default Flowunit config [config] keypoints = 9 # Input ports description [input] [input.input1] # Input port number, the format is input.input[N] name = "in_feat" # Input port name type = "float" # Input port type # Output ports description [output] [output.output1] # Output port number, the format is output.output[N] name = "out_data" # Output port name type = "string" # Output port typeb. 修改逻辑代码# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. #!/usr/bin/env python # -*- coding: utf-8 -*- import _flowunit as modelbox import numpy as np import json class resnet50v2_postFlowUnit(modelbox.FlowUnit): # Derived from modelbox.FlowUnit def __init__(self): super().__init__() def open(self, config): # Open the flowunit to obtain configuration information self.params = {} self.params['keypoints'] = config.get_int('keypoints') return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): # Process the data in_data = data_context.input("in_feat") out_data = data_context.output("out_data") # resnet50v2_post process code. # Remove the following code and add your own code here. for buffer_feat in in_data: feat_data = np.array(buffer_feat.as_object(), copy=False) keypoints = feat_data.reshape(-1, 2).tolist() result = {"keypoints": keypoints} result_str = json.dumps(result) out_buffer = modelbox.Buffer(self.get_bind_device(), result_str) out_data.push_back(out_buffer) return modelbox.Status.StatusCode.STATUS_SUCCESS def close(self): # Close the flowunit return modelbox.Status() def data_pre(self, data_context): # Before streaming data starts return modelbox.Status() def data_post(self, data_context): # After streaming data ends return modelbox.Status() def data_group_pre(self, data_context): # Before all streaming data starts return modelbox.Status() def data_group_post(self, data_context): # After all streaming data ends return modelbox.Status() 4. 修改应用的流程图ResNet50V2工程graph目录下存放流程图,默认的流程图ResNet50V2.toml与工程同名:# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. [driver] dir = ["${HILENS_APP_ROOT}/etc/flowunit", "${HILENS_APP_ROOT}/etc/flowunit/cpp", "${HILENS_APP_ROOT}/model", "${HILENS_MB_SDK_PATH}/flowunit"] skip-default = true [profile] profile=false trace=false dir="${HILENS_DATA_DIR}/mb_profile" [graph] format = "graphviz" graphconf = """digraph ResNet50V2 { node [shape=Mrecord] queue_size = 4 batch_size = 1 input1[type=input,flowunit=input,device=cpu,deviceid=0] httpserver_sync_receive[type=flowunit, flowunit=httpserver_sync_receive_v2, device=cpu, deviceid=0, time_out_ms=5000, endpoint="http://0.0.0.0:1234/v1/ResNet50V2", max_requests=100] image_decoder[type=flowunit, flowunit=image_decoder, device=cpu, key="image_base64", queue_size=4] image_resize[type=flowunit, flowunit=resize, device=cpu, deviceid=0, image_width=224, image_height=224] normalize[type=flowunit, flowunit=normalize, device=cpu, deviceid=0, standard_deviation_inverse="0.003921568627450,0.003921568627450,0.003921568627450"] resnet50v2_infer[type=flowunit, flowunit=resnet50v2_infer, device=cpu, deviceid=0, batch_size=1] resnet50v2_post[type=flowunit, flowunit=resnet50v2_post, device=cpu, deviceid=0] httpserver_sync_reply[type=flowunit, flowunit=httpserver_sync_reply_v2, device=cpu, deviceid=0] input1:input -> httpserver_sync_receive:in_url httpserver_sync_receive:out_request_info -> image_decoder:in_encoded_image image_decoder:out_image -> image_resize:in_image image_resize:out_image -> normalize:in_data normalize:out_data -> resnet50v2_infer:Input resnet50v2_infer:Output -> resnet50v2_post:in_feat resnet50v2_post:out_data -> httpserver_sync_reply:in_reply_info }""" [flow] desc = "ResNet50V2 run in modelbox-win10-x64" 在命令行中运行.\create.bat -t editor即可打开ModelBox图编排界面,可以实时修改并查看项目的流程图:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t editor5. 运行应用在ResNet50V2工程目录下执行.\bin\main.bat运行应用:PS D:\modelbox-win10-x64-1.5.3> cd D:\modelbox-win10-x64-1.5.3\workspace\ResNet50V2 PS D:\modelbox-win10-x64-1.5.3\workspace\ResNet50V2> .\bin\main.bat在ResNet50V2工程data目录下新建test_http.py测试脚本:#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. import os import cv2 import json import base64 import http.client class HttpConfig: '''http调用的参数配置''' def __init__(self, host_ip, port, url, img_base64_str): self.hostIP = host_ip self.Port = port self.httpMethod = "POST" self.requstURL = url self.headerdata = { "Content-Type": "application/json" } self.test_data = { "image_base64": img_base64_str } self.body = json.dumps(self.test_data) def read_image(img_path): '''读取图片数据并转为base64编码的字符串''' img_data = cv2.imread(img_path) img_data = cv2.cvtColor(img_data, cv2.COLOR_BGR2RGB) img_str = cv2.imencode('.jpg', img_data)[1].tobytes() img_bin = base64.b64encode(img_str) img_base64_str = str(img_bin, encoding='utf8') return img_data, img_base64_str def test_image(img_path, ip, port, url): '''单张图片测试''' img_data, img_base64_str = read_image(img_path) http_config = HttpConfig(ip, port, url, img_base64_str) conn = http.client.HTTPConnection(host=http_config.hostIP, port=http_config.Port) conn.request(method=http_config.httpMethod, url=http_config.requstURL, body=http_config.body, headers=http_config.headerdata) response = conn.getresponse().read().decode() print('response: ', response) result = json.loads(response) w, h = img_data.shape[1], img_data.shape[0] for x, y in result["keypoints"]: if x > 0 and y > 0: cv2.circle(img_data, (int(x * w), int(y * h)), 5, (0, 255, 0), -1) cv2.imwrite('./result-' + os.path.basename(img_path), img_data[..., ::-1]) if __name__ == "__main__": port = 1234 ip = "127.0.0.1" url = "/v1/ResNet50V2" img_folder = './test_imgs' file_list = os.listdir(img_folder) for img_file in file_list: print("\n================ {} ================".format(img_file)) img_path = os.path.join(img_folder, img_file) test_image(img_path, ip, port, url) 在ResNet50V2工程data目录下新建test_imgs文件夹存放测试图片:在另一个终端中进入ResNet50V2工程目录data文件夹下运行test_http.py脚本发起HTTP请求测试:PS D:\modelbox-win10-x64-1.5.3> cd D:\modelbox-win10-x64-1.5.3\workspace\ResNet50V2\data PS D:\modelbox-win10-x64-1.5.3\workspace\ResNet50V2\data> D:\modelbox-win10-x64-1.5.3\python-embed\python.exe .\test_http.py ================ 2256.jpg ================ response: {"keypoints": [[0.19147011637687683, 0.26770520210266113], [0.29639703035354614, 0.26533427834510803], [0.24554343521595, 0.35762542486190796], [0.11009970307350159, 0.2090619057416916], [0.08408773690462112, 0.09547536075115204], [0.17451311647891998, 0.169035404920578], [0.2880205512046814, 0.168979212641716], [0.3739408254623413, 0.0717596635222435], [0.34669068455696106, 0.20229394733905792]]} ================ 6899.jpg ================ response: {"keypoints": [[0.3829421401023865, 0.41393953561782837], [0.47102952003479004, 0.42683106660842896], [0.4321300983428955, 0.5082458853721619], [0.3185971677303314, 0.36286458373069763], [0.33502572774887085, 0.2243150770664215], [0.3852037489414215, 0.29658034443855286], [0.4819968640804291, 0.30954840779304504], [0.5504774451255798, 0.2711380124092102], [0.5290539264678955, 0.3962092399597168]]} 在ResNet50V2工程data目录下即可查看测试图片的推理结果:三、小结本节介绍了如何使用ModelArts和ModelBox训练开发一个ResNet50V2猫脸关键点检测的AI应用,我们只需要准备模型文件以及简单的配置即可创建一个HTTP服务。同时我们可以了解到ResNet50V2网络的基本结构、数据处理和模型训练方法,以及对应推理应用的逻辑。----转自博客:https://bbs.huaweicloud.com/blogs/451999
  • [技术干货] 果蔬病虫害分割(ModelBox)
    果蔬病虫害分割(ModelBox)一、模型训练与转换FCN(全卷积网络,Fully Convolutional Networks)是用于语义分割任务的一种深度学习模型架构,引入了跳跃结构(Skip Architecture),通过融合浅层和深层的特征图,保留更多的细节信息,提升分割精度。此外,FCN还利用多尺度上下文聚合,捕捉不同层级的特征,增强了对不同大小目标的识别能力。FCN的成功推动了语义分割领域的发展,成为后续许多先进模型的基础。模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本。在ModelArts的Notebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。二、应用开发1. 创建工程在ModelBox sdk目录下使用create.bat创建FCN工程:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t server -n FCN ... success: create FCN in D:\modelbox-win10-x64-1.5.3\workspacecreate.bat工具的参数中,-t参数,表示所创建实例的类型,包括server(ModelBox工程)、python(Python功能单元)、c++(C++功能单元)、infer(推理功能单元)等;-n参数,表示所创建实例的名称;-s参数,表示将使用后面参数值代表的模板创建工程,而不是创建空的工程。2. 创建推理功能单元在ModelBox sdk目录下使用create.bat创建fcn_infer推理功能单元:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t infer -n fcn_infer -p FCN ... success: create infer fcn_infer in D:\modelbox-win10-x64-1.5.3\workspace\FCN/model/fcn_infercreate.bat工具使用时,-t infer即表示创建的是推理功能单元;-n xxx_infer表示创建的功能单元名称为xxx_infer;-p表示所创建的功能单元属于FCN应用。下载转换好的FCN.onnx模型到FCN\model目录下,修改推理功能单元fcn_infer.toml模型的配置文件:# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. [base] name = "fcn_infer" device = "cpu" version = "1.0.0" description = "your description" entry = "./FCN.onnx" # model file path, use relative path type = "inference" virtual_type = "onnx" # inference engine type: win10 now only support onnx group_type = "Inference" # flowunit group attribution, do not change # Input ports description [input] [input.input1] # input port number, Format is input.input[N] name = "Input" # input port name type = "float" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type: cpu, win10 now copy input from cpu # Output ports description [output] [output.output1] # output port number, Format is output.output[N] name = "Output" # output port name type = "float" # output port data type ,e.g. float or uint83. 创建后处理功能单元在ModelBox sdk目录下使用create.bat创建fcn_post后处理功能单元:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t python -n fcn_post -p FCN ... success: create python fcn_post in D:\modelbox-win10-x64-1.5.3\workspace\FCN/etc/flowunit/fcn_postcreate.bat工具使用时,-t python即表示创建的是通用功能单元;-n xxx_post表示创建的功能单元名称为xxx_post;-p表示所创建的功能单元属于FCN应用。a. 修改配置文件我们的模型有一个输入和输出,对116种果蔬病虫害进行分割,加上背景总共是117类:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. # Basic config [base] name = "fcn_post" # The FlowUnit name device = "cpu" # The flowunit runs on cpu version = "1.0.0" # The version of the flowunit type = "python" # Fixed value, do not change description = "description" # The description of the flowunit entry = "fcn_post@fcn_postFlowUnit" # Python flowunit entry function group_type = "Generic" # flowunit group attribution, change as Input/Output/Image/Generic ... # Flowunit Type stream = false # Whether the flowunit is a stream flowunit condition = false # Whether the flowunit is a condition flowunit collapse = false # Whether the flowunit is a collapse flowunit collapse_all = false # Whether the flowunit will collapse all the data expand = false # Whether the flowunit is a expand flowunit # The default Flowunit config [config] num_classes = 117 net_w = 224 net_h = 224 # Input ports description [input] [input.input1] # Input port number, the format is input.input[N] name = "in_image" # Input port name type = "uint8" # Input port type [input.input2] # Input port number, the format is input.input[N] name = "in_feat" # Input port name type = "float" # Input port type # Output ports description [output] [output.output1] # Output port number, the format is output.output[N] name = "out_image" # Output port name type = "uint8" # Output port typeb. 修改逻辑代码# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. #!/usr/bin/env python # -*- coding: utf-8 -*- import _flowunit as modelbox import numpy as np import cv2 class fcn_postFlowUnit(modelbox.FlowUnit): # Derived from modelbox.FlowUnit def __init__(self): super().__init__() def open(self, config): # Open the flowunit to obtain configuration information self.params = {} self.params['num_classes'] = config.get_int('num_classes') self.params['net_w'] = config.get_int('net_w') self.params['net_h'] = config.get_int('net_h') return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): # Process the data in_image = data_context.input("in_image") in_feat = data_context.input("in_feat") out_image = data_context.output("out_image") # fcn_post process code. # Remove the following code and add your own code here. for buffer_image, buffer_feat in zip(in_image, in_feat): channel = buffer_image.get('channel') width = buffer_image.get('width') height = buffer_image.get('height') image = np.array(buffer_image.as_object(), dtype=np.uint8, copy=False) image = image.reshape(height, width, channel) feat = np.array(buffer_feat.as_object(), dtype=np.float32, copy=False) feat = feat.reshape(self.params['net_h'], self.params['net_w'], self.params['num_classes']) mask = np.argmax(feat, axis=-1).astype(np.uint8) mask = cv2.resize(mask, (width, height), interpolation=cv2.INTER_NEAREST) overlay = np.zeros_like(image) for i in range(1, self.params['num_classes']): color = np.random.randint(0, 255, (3,)).tolist() overlay[mask==i] = color result_image = cv2.addWeighted(image[..., ::-1], 0.5, overlay, 0.5, 0) add_buffer = modelbox.Buffer(self.get_bind_device(), result_image) add_buffer.copy_meta(buffer_image) out_image.push_back(add_buffer) return modelbox.Status.StatusCode.STATUS_SUCCESS def close(self): # Close the flowunit return modelbox.Status() def data_pre(self, data_context): # Before streaming data starts return modelbox.Status() def data_post(self, data_context): # After streaming data ends return modelbox.Status() def data_group_pre(self, data_context): # Before all streaming data starts return modelbox.Status() def data_group_post(self, data_context): # After all streaming data ends return modelbox.Status() 4. 修改应用的流程图FCN工程graph目录下存放流程图,默认的流程图FCN.toml与工程同名:# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. [driver] dir = ["${HILENS_APP_ROOT}/etc/flowunit", "${HILENS_APP_ROOT}/etc/flowunit/cpp", "${HILENS_APP_ROOT}/model", "${HILENS_MB_SDK_PATH}/flowunit"] skip-default = true [profile] profile=false trace=false dir="${HILENS_DATA_DIR}/mb_profile" [graph] format = "graphviz" graphconf = """digraph FCN { node [shape=Mrecord] queue_size = 4 batch_size = 1 input1[type=input,flowunit=input,device=cpu,deviceid=0] httpserver_sync_receive[type=flowunit, flowunit=httpserver_sync_receive_v2, device=cpu, deviceid=0, time_out_ms=5000, endpoint="http://0.0.0.0:1234/v1/FCN", max_requests=100] image_decoder[type=flowunit, flowunit=image_decoder, device=cpu, key="image_base64", queue_size=4] image_resize[type=flowunit, flowunit=resize, device=cpu, deviceid=0, image_width=224, image_height=224] normalize[type=flowunit, flowunit=normalize, device=cpu, deviceid=0, standard_deviation_inverse="0.003921568627450,0.003921568627450,0.003921568627450"] fcn_infer[type=flowunit, flowunit=fcn_infer, device=cpu, deviceid=0, batch_size=1] fcn_post[type=flowunit, flowunit=fcn_post, device=cpu, deviceid=0] httpserver_sync_reply[type=flowunit, flowunit=httpserver_sync_reply_v2, device=cpu, deviceid=0] input1:input -> httpserver_sync_receive:in_url httpserver_sync_receive:out_request_info -> image_decoder:in_encoded_image image_decoder:out_image -> image_resize:in_image image_resize:out_image -> normalize:in_data normalize:out_data -> fcn_infer:Input image_decoder:out_image -> fcn_post:in_image fcn_infer:Output -> fcn_post:in_feat fcn_post:out_image -> httpserver_sync_reply:in_reply_info }""" [flow] desc = "FCN run in modelbox-win10-x64" 在命令行中运行.\create.bat -t editor即可打开ModelBox图编排界面,可以实时修改并查看项目的流程图:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t editor5. 运行应用在FCN工程目录下执行.\bin\main.bat运行应用:PS D:\modelbox-win10-x64-1.5.3> cd D:\modelbox-win10-x64-1.5.3\workspace\FCN PS D:\modelbox-win10-x64-1.5.3\workspace\FCN> .\bin\main.bat在FCN工程data目录下新建test_http.py测试脚本:import cv2 import json import base64 import requests import numpy as np if __name__ == "__main__": port = 1234 ip = "127.0.0.1" url = "/v1/FCN" img_path = "apple_black_rot_google_0056.jpg" img_data = cv2.imread(img_path) img_data = cv2.cvtColor(img_data, cv2.COLOR_BGR2RGB) img_str = cv2.imencode('.jpg', img_data)[1].tobytes() img = base64.b64encode(img_str) img_base64_str = str(img, encoding='utf8') params = {"image_base64": img_base64_str} response = requests.post(f'http://{ip}:{port}{url}', data=json.dumps(params), headers={"Content-Type": "application/json"}) h, w, c = img_data.shape img_array = np.frombuffer(response.content, np.uint8) img_array = img_array.reshape((h, -1, c)) cv2.imwrite("res.jpg", img_array) 在FCN工程data目录下存放测试图片:在另一个终端中进入FCN工程目录data文件夹下:PS D:\modelbox-win10-x64-1.5.3> cd D:\modelbox-win10-x64-1.5.3\workspace\FCN\data首先安装requests依赖包:PS D:\modelbox-win10-x64-1.5.3\workspace\FCN\data> D:\modelbox-win10-x64-1.5.3\python-embed\python.exe -m pip install requests然后运行test_http.py脚本发起HTTP请求测试:PS D:\modelbox-win10-x64-1.5.3\workspace\FCN\data> D:\modelbox-win10-x64-1.5.3\python-embed\python.exe .\test_http.py测试图片的分割结果res.jpg将保存在FCN工程data目录下:三、小结本节介绍了如何使用ModelArts和ModelBox训练开发一个FCN果蔬病虫害分割的AI应用,我们只需要准备模型文件以及简单的配置即可创建一个HTTP服务。同时我们可以了解到FCN网络的基本结构、数据处理和模型训练方法,以及对应推理应用的逻辑。----转自博客:https://bbs.huaweicloud.com/blogs/449045
  • [技术干货] 深海鱼类检测(ModelBox)
    深海鱼类检测(ModelBox)一、模型训练和转换YOLOX是YOLO系列的优化版本,引入了解耦头、数据增强、无锚点以及标签分类等目标检测领域的优秀进展,拥有较好的精度表现,同时对工程部署友好。模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本。在ModelArts的Notebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。二、ModelBox 应用开发1. 创建工程在ModelBox sdk目录下使用create.bat创建fish_det工程:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t server -n fish_det -s car_det ... success: create fish_det in D:\modelbox-win10-x64-1.5.3\workspacecreate.bat工具的参数中,-t参数,表示所创建实例的类型,包括server(ModelBox工程)、python(Python功能单元)、c++(C++功能单元)、infer(推理功能单元)等;-n参数,表示所创建实例的名称;-s参数,表示将使用后面参数值代表的模板创建工程,而不是创建空的工程。2. 修改推理功能单元下载转换好的yolox_fish.onnx模型到fish_det\model目录下,修改推理功能单元yolox_infer.toml模型的配置文件:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. [base] name = "yolox_infer" device = "cpu" version = "1.0.0" description = "fish detection" entry = "./yolox_fish.onnx" # model file path, use relative path type = "inference" virtual_type = "onnx" # inference engine type: win10 now only support onnx group_type = "Inference" # flowunit group attribution, do not change # input port description, suporrt multiple input ports [input] [input.input1] name = "input" type = "float" device = "cpu" # output port description, suporrt multiple output ports [output] [output.output1] name = "output" type = "float" 3. 修改后处理功能单元我们的模型的输入大小为320,类别数量是1,修改fish_det\etc\flowunit\yolox_post目录下的yolox_post.toml配置文件:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. # Basic config [base] name = "yolox_post" # The FlowUnit name device = "cpu" # The device the flowunit runs on,cpu,cuda,ascend。 version = "1.0.0" # The version of the flowunit description = "description" # The description of the flowunit entry = "yolox_post@yolox_postFlowUnit" # Python flowunit entry function type = "python" # Fixed value group_type = "Generic" # flowunit group attribution, change as Input/Output/Image/Generic ... # Flowunit Type stream = false # Whether the flowunit is a stream flowunit condition = false # Whether the flowunit is a condition flowunit collapse = false # Whether the flowunit is a collapse flowunit collapse_all = false # Whether the flowunit will collapse all the data expand = false # Whether the flowunit is a expand flowunit [config] net_h = 320 net_w = 320 num_classes = 1 strides = ['8', '16', '32'] conf_threshold = 0.25 iou_threshold = 0.45 [input] [input.input1] name = "in_feat" type = "float" [output] [output.output1] name = "out_data" type = "string" 4. 修改绘图功能单元我们这里只有一个类别,所以修改coco_car_labels = [0]只检测鱼这个类别:... def decode_car_bboxes(self, bbox_str, input_shape): try: coco_car_labels = [0] # fish det_result = json.loads(bbox_str)['det_result'] if (det_result == "None"): return [] bboxes = json.loads(det_result) car_bboxes = list(filter(lambda x: int(x[5]) in coco_car_labels, bboxes)) except Exception as ex: modelbox.error(str(ex)) return [] else: for bbox in car_bboxes: bbox[0] = int(bbox[0] * input_shape[1]) bbox[1] = int(bbox[1] * input_shape[0]) bbox[2] = int(bbox[2] * input_shape[1]) bbox[3] = int(bbox[3] * input_shape[0]) return car_bboxes ... 5. 修改应用的流程图修改image_resize图像预处理功能单元参数image_width=320, image_height=320与模型的输入大小保持一致:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. [driver] dir = ["${HILENS_APP_ROOT}/etc/flowunit", "${HILENS_APP_ROOT}/etc/flowunit/cpp", "${HILENS_APP_ROOT}/model", "${HILENS_MB_SDK_PATH}/flowunit"] skip-default = true [profile] profile=false trace=false dir="${HILENS_DATA_DIR}/mb_profile" [graph] format = "graphviz" graphconf = """digraph fish_det { node [shape=Mrecord] queue_size = 1 batch_size = 1 input1[type=input,flowunit=input,device=cpu,deviceid=0] data_source_parser[type=flowunit, flowunit=data_source_parser, device=cpu, deviceid=0] video_demuxer[type=flowunit, flowunit=video_demuxer, device=cpu, deviceid=0] video_decoder[type=flowunit, flowunit=video_decoder, device=cpu, deviceid=0, pix_fmt=bgr] image_resize[type=flowunit, flowunit=resize, device=cpu, deviceid=0, image_width=320, image_height=320] image_transpose[type=flowunit, flowunit=packed_planar_transpose, device=cpu, deviceid=0] normalize[type=flowunit, flowunit=normalize, device=cpu, deviceid=0, standard_deviation_inverse="1,1,1"] car_detection[type=flowunit, flowunit=yolox_infer, device=cpu, deviceid=0, batch_size = 1] yolox_post[type=flowunit, flowunit=yolox_post, device=cpu, deviceid=0] draw_car_bbox[type=flowunit, flowunit=draw_car_bbox, device=cpu, deviceid=0] video_out[type=flowunit, flowunit=video_out, device=cpu, deviceid=0] input1:input -> data_source_parser:in_data data_source_parser:out_video_url -> video_demuxer:in_video_url video_demuxer:out_video_packet -> video_decoder:in_video_packet video_decoder:out_video_frame -> image_resize:in_image image_resize:out_image -> image_transpose:in_image image_transpose:out_image -> normalize:in_data normalize:out_data -> car_detection:input car_detection:output -> yolox_post:in_feat video_decoder:out_video_frame -> draw_car_bbox:in_image yolox_post:out_data -> draw_car_bbox:in_bbox draw_car_bbox:out_image -> video_out:in_video_frame }""" [flow] desc = "fish_det run in modelbox-win10-x64" 在命令行中运行.\create.bat -t editor即可打开ModelBox图编排界面,可以实时修改并查看项目的流程图:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t editor6. 配置应用的输入输出下载测试视频到fish_det\data目录下,修改应用fish_det\bin\mock_task.toml配置文件:# 用于本地mock文件读取任务,脚本中已经配置了IVA_SVC_CONFIG环境变量, 添加了此文件路径 ########### 请确定使用linux的路径类型,比如在windows上要用 D:/xxx/xxx 不能用D:\xxx\xxx ########### # 任务的参数为一个压缩并转义后的json字符串 # 直接写需要转义双引号, 也可以用 content_file 添加一个json文件 [common] content = "{\"param_str\":\"string param\",\"param_int\":10,\"param_float\":10.5}" # 任务输入配置,mock模拟目前仅支持一路rtsp或者本地url, 当前支持以下几种输入方式: # 1. rtsp摄像头或rtsp视频流:type="rtsp", url="rtsp://xxx.xxx" (type为rtsp的时候,支持视频中断自动重连) # 2. 设备自带摄像头或者USB摄像头:type="url",url="摄像头编号,比如 0 或者 1 等" (需配合local_camera功能单元使用) # 3. 本地视频文件:type="url",url="视频文件路径" (可以是相对路径 -- 相对这个mock_task.toml文件, 也支持从环境变量${HILENS_APP_ROOT}所在目录文件输入) # 4. http服务:type="url", url="http://xxx.xxx"(指的是任务作为http服务启动,此处需填写对外暴露的http服务地址,需配合httpserver类的功能单元使用) [input] type = "url" url = "${HILENS_APP_ROOT}/data/Test_ROV_video_h264_decim.mp4" # 任务输出配置,当前支持以下几种输出方式: # 1. rtsp视频流:type="local", url="rtsp://xxx.xxx" # 2. 本地屏幕:type="local", url="0:xxx" (设备需要接显示器,系统需要安装桌面) # 3. 本地视频文件:type="local",url="视频文件路径" (可以是相对路径——相对这个mock_task.toml文件, 也支持输出到环境变量${HILENS_DATA_DIR}所在目录或子目录) # 4. http服务:type="webhook", url="http://xxx.xxx" (指的是任务产生的数据上报给某个http服务,此处需填写上传的http服务地址) [output] type = "local" url = "0" 7. 运行应用在fish_det工程目录下执行.\bin\main.bat运行应用,本地屏幕上会自动弹出鱼群的实时检测画面:PS D:\modelbox-win10-x64-1.5.3> cd D:\modelbox-win10-x64-1.5.3\workspace\fish_det PS D:\modelbox-win10-x64-1.5.3\workspace\fish_det> .\bin\main.bat三、小结本节介绍了如何使用ModelArts和ModelBox训练开发一个YOLOX鱼类目标检测的AI应用,我们只需要准备模型并配置对应的toml文件,即可快速实现模型的高效推理和部署。 ----转自博客:https://bbs.huaweicloud.com/blogs/449038
  • [技术干货] 动物分类(ModelBox)
    动物分类(ModelBox)一、模型训练与转换Inception V3,GoogLeNet的改进版本,采用InceptionModule和全局平均池化层,v3一个最重要的改进是分解(Factorization),将7x7分解成两个一维的卷积(1x7,7x1),3x3也是一样(1x3,3x1),这样的好处,既可以加速计算(多余的计算能力可以用来加深网络),又可以将1个conv拆成2个conv,使得网络深度进一步增加,增加了网络的非线性。模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本。在ModelArts的Notebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。二、ModelBox 应用开发1. 创建工程在ModelBox sdk目录下使用create.bat创建InceptionV3工程:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t server -n InceptionV3 ... success: create InceptionV3 in D:\modelbox-win10-x64-1.5.3\workspacecreate.bat工具的参数中,-t参数,表示所创建实例的类型,包括server(ModelBox工程)、python(Python功能单元)、c++(C++功能单元)、infer(推理功能单元)等;-n参数,表示所创建实例的名称;-s参数,表示将使用后面参数值代表的模板创建工程,而不是创建空的工程。2. 创建推理功能单元在ModelBox sdk目录下使用create.bat创建inceptionv3_infer推理功能单元:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t infer -n inceptionv3_infer -p InceptionV3 ... success: create infer inceptionv3_infer in D:\modelbox-win10-x64-1.5.3\workspace\InceptionV3/model/inceptionv3_infercreate.bat工具使用时,-t infer即表示创建的是推理功能单元;-n xxx_infer表示创建的功能单元名称为xxx_infer;-p表示所创建的功能单元属于InceptionV3应用。下载转换好的InceptionV3.onnx模型到InceptionV3\model目录下,修改推理功能单元inceptionv3_infer.toml模型的配置文件:# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. [base] name = "inceptionv3_infer" device = "cpu" version = "1.0.0" description = "your description" entry = "./InceptionV3.onnx" # model file path, use relative path type = "inference" virtual_type = "onnx" # inference engine type: win10 now only support onnx group_type = "Inference" # flowunit group attribution, do not change # Input ports description [input] [input.input1] # input port number, Format is input.input[N] name = "Input" # input port name type = "float" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type: cpu, win10 now copy input from cpu # Output ports description [output] [output.output1] # output port number, Format is output.output[N] name = "Output" # output port name type = "float" # output port data type ,e.g. float or uint83. 创建后处理功能单元在ModelBox sdk目录下使用create.bat创建inceptionv3_post后处理功能单元:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t python -n inceptionv3_post -p InceptionV3 ... success: create python inceptionv3_post in D:\modelbox-win10-x64-1.5.3\workspace\InceptionV3/etc/flowunit/inceptionv3_postcreate.bat工具使用时,-t python即表示创建的是通用功能单元;-n xxx_post表示创建的功能单元名称为xxx_post;-p表示所创建的功能单元属于InceptionV3应用。a. 修改配置文件我们的模型有一个输入和输出,总共包含90种动物类别:# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. # Basic config [base] name = "inceptionv3_post" # The FlowUnit name device = "cpu" # The flowunit runs on cpu version = "1.0.0" # The version of the flowunit type = "python" # Fixed value, do not change description = "description" # The description of the flowunit entry = "inceptionv3_post@inceptionv3_postFlowUnit" # Python flowunit entry function group_type = "Generic" # flowunit group attribution, change as Input/Output/Image/Generic ... # Flowunit Type stream = false # Whether the flowunit is a stream flowunit condition = false # Whether the flowunit is a condition flowunit collapse = false # Whether the flowunit is a collapse flowunit collapse_all = false # Whether the flowunit will collapse all the data expand = false # Whether the flowunit is a expand flowunit # The default Flowunit config [config] num_classes = 90 # Input ports description [input] [input.input1] # Input port number, the format is input.input[N] name = "in_feat" # Input port name type = "float" # Input port type # Output ports description [output] [output.output1] # Output port number, the format is output.output[N] name = "out_data" # Output port name type = "string" # Output port typeb. 修改逻辑代码# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. #!/usr/bin/env python # -*- coding: utf-8 -*- import _flowunit as modelbox import numpy as np import json class inceptionv3_postFlowUnit(modelbox.FlowUnit): # Derived from modelbox.FlowUnit def __init__(self): super().__init__() def open(self, config): # Open the flowunit to obtain configuration information self.params = {} self.params['num_classes'] = config.get_int('num_classes') return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): # Process the data in_feat = data_context.input("in_feat") out_data = data_context.output("out_data") # inceptionv3_post process code. # Remove the following code and add your own code here. for buffer_feat in in_feat: feat_data = np.array(buffer_feat.as_object(), copy=False) clsse = np.argmax(feat_data).astype(np.int32).item() score = feat_data[clsse].astype(np.float32).item() result = {"clsse": clsse, "score":score} result_str = json.dumps(result) out_buffer = modelbox.Buffer(self.get_bind_device(), result_str) out_data.push_back(out_buffer) return modelbox.Status.StatusCode.STATUS_SUCCESS def close(self): # Close the flowunit return modelbox.Status() def data_pre(self, data_context): # Before streaming data starts return modelbox.Status() def data_post(self, data_context): # After streaming data ends return modelbox.Status() def data_group_pre(self, data_context): # Before all streaming data starts return modelbox.Status() def data_group_post(self, data_context): # After all streaming data ends return modelbox.Status() 4. 修改应用的流程图InceptionV3工程graph目录下存放流程图,默认的流程图InceptionV3.toml与工程同名:# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. [driver] dir = ["${HILENS_APP_ROOT}/etc/flowunit", "${HILENS_APP_ROOT}/etc/flowunit/cpp", "${HILENS_APP_ROOT}/model", "${HILENS_MB_SDK_PATH}/flowunit"] skip-default = true [profile] profile=false trace=false dir="${HILENS_DATA_DIR}/mb_profile" [graph] format = "graphviz" graphconf = """digraph InceptionV3 { node [shape=Mrecord] queue_size = 4 batch_size = 1 input1[type=input,flowunit=input,device=cpu,deviceid=0] httpserver_sync_receive[type=flowunit, flowunit=httpserver_sync_receive_v2, device=cpu, deviceid=0, time_out_ms=5000, endpoint="http://0.0.0.0:1234/v1/InceptionV3", max_requests=100] image_decoder[type=flowunit, flowunit=image_decoder, device=cpu, key="image_base64", queue_size=4] image_resize[type=flowunit, flowunit=resize, device=cpu, deviceid=0, image_width=224, image_height=224] normalize[type=flowunit, flowunit=normalize, device=cpu, deviceid=0, standard_deviation_inverse="0.003921568627450,0.003921568627450,0.003921568627450"] inceptionv3_infer[type=flowunit, flowunit=inceptionv3_infer, device=cpu, deviceid=0, batch_size=1] inceptionv3_post[type=flowunit, flowunit=inceptionv3_post, device=cpu, deviceid=0] httpserver_sync_reply[type=flowunit, flowunit=httpserver_sync_reply_v2, device=cpu, deviceid=0] input1:input -> httpserver_sync_receive:in_url httpserver_sync_receive:out_request_info -> image_decoder:in_encoded_image image_decoder:out_image -> image_resize:in_image image_resize:out_image -> normalize:in_data normalize:out_data -> inceptionv3_infer:Input inceptionv3_infer:Output -> inceptionv3_post:in_feat inceptionv3_post:out_data -> httpserver_sync_reply:in_reply_info }""" [flow] desc = "InceptionV3 run in modelbox-win10-x64" 在命令行中运行.\create.bat -t editor即可打开ModelBox图编排界面,可以实时修改并查看项目的流程图:PS D:\modelbox-win10-x64-1.5.3> .\create.bat -t editor5. 运行应用在InceptionV3工程目录下执行.\bin\main.bat运行应用:PS D:\modelbox-win10-x64-1.5.3> cd D:\modelbox-win10-x64-1.5.3\workspace\InceptionV3 PS D:\modelbox-win10-x64-1.5.3\workspace\InceptionV3> .\bin\main.bat在InceptionV3工程data目录下新建test_http.py测试脚本:#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. import os import cv2 import json import base64 import http.client class HttpConfig: '''http调用的参数配置''' def __init__(self, host_ip, port, url, img_base64_str): self.hostIP = host_ip self.Port = port self.httpMethod = "POST" self.requstURL = url self.headerdata = { "Content-Type": "application/json" } self.test_data = { "image_base64": img_base64_str } self.body = json.dumps(self.test_data) def read_image(img_path): '''读取图片数据并转为base64编码的字符串''' img_data = cv2.imread(img_path) img_data = cv2.cvtColor(img_data, cv2.COLOR_BGR2RGB) img_str = cv2.imencode('.jpg', img_data)[1].tobytes() img_bin = base64.b64encode(img_str) img_base64_str = str(img_bin, encoding='utf8') return img_data, img_base64_str def decode_result_str(result_str): try: result = json.loads(result_str) except Exception as ex: print(str(ex)) return [] else: return result labels = ['antelope', 'badger', 'bat', 'bear', 'bee', 'beetle', 'bison', 'boar', 'butterfly', 'cat', 'caterpillar', 'chimpanzee', 'cockroach', 'cow', 'coyote', 'crab', 'crow', 'deer', 'dog', 'dolphin', 'donkey', 'dragonfly', 'duck', 'eagle', 'elephant', 'flamingo', 'fly', 'fox', 'goat', 'goldfish', 'goose', 'gorilla', 'grasshopper', 'hamster', 'hare', 'hedgehog', 'hippopotamus', 'hornbill', 'horse', 'hummingbird', 'hyena', 'jellyfish', 'kangaroo', 'koala', 'ladybugs', 'leopard', 'lion', 'lizard', 'lobster', 'mosquito', 'moth', 'mouse', 'octopus', 'okapi', 'orangutan', 'otter', 'owl', 'ox', 'oyster', 'panda', 'parrot', 'pelecaniformes', 'penguin', 'pig', 'pigeon', 'porcupine', 'possum', 'raccoon', 'rat', 'reindeer', 'rhinoceros', 'sandpiper', 'seahorse', 'seal', 'shark', 'sheep', 'snake', 'sparrow', 'squid', 'squirrel', 'starfish', 'swan', 'tiger', 'turkey', 'turtle', 'whale', 'wolf', 'wombat', 'woodpecker', 'zebra'] def test_image(img_path, ip, port, url): '''单张图片测试''' img_data, img_base64_str = read_image(img_path) http_config = HttpConfig(ip, port, url, img_base64_str) conn = http.client.HTTPConnection(host=http_config.hostIP, port=http_config.Port) conn.request(method=http_config.httpMethod, url=http_config.requstURL, body=http_config.body, headers=http_config.headerdata) response = conn.getresponse().read().decode() print('response: ', response) result = decode_result_str(response) clsse, score = result["clsse"], result["score"] result_str = f"{labels[clsse]}:{round(score, 2)}" cv2.putText(img_data, result_str, (0, 100), cv2.FONT_HERSHEY_TRIPLEX, 4, (0, 255, 0), 2) cv2.imwrite('./result-' + os.path.basename(img_path), img_data[..., ::-1]) if __name__ == "__main__": port = 1234 ip = "127.0.0.1" url = "/v1/InceptionV3" img_folder = './test_imgs' file_list = os.listdir(img_folder) for img_file in file_list: print("\n================ {} ================".format(img_file)) img_path = os.path.join(img_folder, img_file) test_image(img_path, ip, port, url) 在InceptionV3工程data目录下新建test_imgs文件夹存放测试图片:在另一个终端中进入InceptionV3工程目录data文件夹下运行test_http.py脚本发起HTTP请求测试:PS D:\modelbox-win10-x64-1.5.3> cd D:\modelbox-win10-x64-1.5.3\workspace\InceptionV3\data PS D:\modelbox-win10-x64-1.5.3\workspace\InceptionV3\data> D:\modelbox-win10-x64-1.5.3\python-embed\python.exe .\test_http.py ================ 61cf5127ce.jpg ================ response: {"clsse": 63, "score": 0.9996486902236938} ================ 7e2a453559.jpg ================ response: {"clsse": 81, "score": 0.999880313873291} 在InceptionV3工程data目录下即可查看测试图片的推理结果:三、小结本节介绍了如何使用ModelArts和ModelBox训练开发一个InceptionV3动物图片分类的AI应用,我们只需要准备模型文件以及简单的配置即可创建一个HTTP服务。同时我们可以了解到InceptionV3网络的基本结构、数据处理和模型训练方法,以及对应推理应用的逻辑。----转自博客:https://bbs.huaweicloud.com/blogs/449036
  • [技术干货] RK3588 AI 应用开发 (ResNet50V2-关键点检测)
    RK3588 AI 应用开发 (ResNet50V2-关键点检测)一、模型训练与转换ResNet50V2 是改进版的深度卷积神经网络,基于 ResNet 架构发展而来。它采用前置激活(将 BN 和 ReLU 移至卷积前)与身份映射,优化了信息传播和模型训练性能。作为 50 层深度的网络,ResNet50V2 广泛应用于图像分类、目标检测等任务,支持迁移学习,适合快速适配新数据集,具有良好的泛化能力和较高准确率。模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本。在ModelArts的Notebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。二、应用开发1. 开发 Gradio 界面import cv2 import json import base64 import requests import numpy as np import gradio as gr def test_image(image_path): try: image_bgr = cv2.imread(image_path) image_string = cv2.imencode('.jpg', image_bgr)[1].tobytes() image_base64 = base64.b64encode(image_string).decode('utf-8') params = {"image_base64": image_base64} response = requests.post(f'http://{ip}:{port}{url}', data=json.dumps(params), headers={"Content-Type": "application/json"}) if response.status_code == 200: image_base64 = response.json().get("image_base64") image_binary = base64.b64decode(image_base64) image_array = np.frombuffer(image_binary, dtype=np.uint8) image_rgb = cv2.imdecode(image_array, cv2.IMREAD_COLOR) else: image_rgb = None except Exception as e: return None else: return image_rgb if __name__ == "__main__": port = 8000 ip = "127.0.0.1" url = "/v1/ResNet50V2" demo = gr.Interface(fn=test_image, inputs=gr.Image(type="filepath"), outputs=["image"], title="ResNet50V2 猫脸关键点检测") demo.launch(share=False, server_port=3000) /home/orangepi/miniconda3/envs/python-3.10.10/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html from .autonotebook import tqdm as notebook_tqdm * Running on local URL: http://127.0.0.1:3000 * To create a public link, set `share=True` in `launch()`. 2. 编写推理代码class ResNet50V2: def __init__(self, model_path): self.rknn_lite = RKNNLite() self.rknn_lite.load_rknn(model_path) self.rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2) def preprocess(self, image): image = image[:, :, ::-1] image = cv2.resize(image, (224, 224)) return np.expand_dims(image, axis=0) def rknn_infer(self, data): outputs = self.rknn_lite.inference(inputs=[data]) return outputs[0] def post_process(self, pred): feat = pred.squeeze().reshape(-1, 2) return feat def predict(self, image): # 图像预处理 data = self.preprocess(image) # 模型推理 pred = self.rknn_infer(data) # 模型后处理 keypoints = self.post_process(pred) # 绘制关键点检测结果 h, w, _ = image.shape for x, y in keypoints: cv2.circle(image, (int(x * w), int(y * h)), 5, (0, 255, 0), -1) return image[..., ::-1] def release(self): self.rknn_lite.release() 3. 图片批量预测import os import cv2 import numpy as np import matplotlib.pyplot as plt from rknnlite.api import RKNNLite model = ResNet50V2('model/ResNet50V2.rknn') for image in os.listdir("image"): image = cv2.imread(os.path.join("image", image)) image = model.predict(image) plt.imshow(image) plt.axis('off') plt.show() model.release() 4. 创建 Flask 服务import cv2 import base64 import numpy as np from rknnlite.api import RKNNLite from flask import Flask, request, jsonify from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/v1/ResNet50V2', methods=['POST']) def inference(): data = request.get_json() image_base64 = data.get("image_base64") image_binary = base64.b64decode(image_base64) image_array = np.frombuffer(image_binary, dtype=np.uint8) image_bgr = cv2.imdecode(image_array, cv2.IMREAD_COLOR) image_rgb = model.predict(image_bgr) image_string = cv2.imencode('.jpg', image_rgb)[1].tobytes() image_base64 = base64.b64encode(image_string).decode('utf-8') return jsonify({ "image_base64": image_base64 }), 200 if __name__ == '__main__': model = ResNet50V2('model/ResNet50V2.rknn') app.run(host='0.0.0.0', port=8000) model.release() W rknn-toolkit-lite2 version: 2.3.2 * Serving Flask app '__main__' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:8000 * Running on http://192.168.3.50:8000 Press CTRL+C to quit 127.0.0.1 - - [02/May/2025 02:13:40] "POST /v1/ResNet50V2 HTTP/1.1" 200 - 127.0.0.1 - - [02/May/2025 02:13:46] "POST /v1/ResNet50V2 HTTP/1.1" 200 - 5. 上传图片预测三、小结本章介绍了基于 RK3588 的 ResNet50V2 关键点检测应用开发全流程,包括模型训练与转换、Gradio 界面设计、推理代码实现、批量预测处理及 Flask 服务部署,完整实现了从模型到端到端应用的落地。----转自博客:https://bbs.huaweicloud.com/blogs/451999
  • [技术干货] RK3588 AI 应用开发 (FCN-语义分割)
    RK3588 AI 应用开发 (FCN-语义分割)一、模型训练与转换FCN(全卷积网络,Fully Convolutional Networks)是用于语义分割任务的一种深度学习模型架构,引入了跳跃结构(Skip Architecture),通过融合浅层和深层的特征图,保留更多的细节信息,提升分割精度。此外,FCN还利用多尺度上下文聚合,捕捉不同层级的特征,增强了对不同大小目标的识别能力。FCN的成功推动了语义分割领域的发展,成为后续许多先进模型的基础。模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本。在ModelArts的Notebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。二、应用开发1. 开发 Gradio 界面import cv2 import json import base64 import requests import numpy as np import gradio as gr def test_image(image_path): try: image_bgr = cv2.imread(image_path) image_string = cv2.imencode('.jpg', image_bgr)[1].tobytes() image_base64 = base64.b64encode(image_string).decode('utf-8') params = {"image_base64": image_base64} response = requests.post(f'http://{ip}:{port}{url}', data=json.dumps(params), headers={"Content-Type": "application/json"}) if response.status_code == 200: image_base64 = response.json().get("image_base64") image_binary = base64.b64decode(image_base64) image_array = np.frombuffer(image_binary, dtype=np.uint8) image_rgb = cv2.imdecode(image_array, cv2.IMREAD_COLOR) else: image_rgb = None except Exception as e: return None else: return image_rgb if __name__ == "__main__": port = 8000 ip = "127.0.0.1" url = "/v1/FCN" demo = gr.Interface(fn=test_image, inputs=gr.Image(type="filepath"), outputs=["image"], title="FCN 果蔬病虫害分割") demo.launch(share=False, server_port=3000) /home/orangepi/miniconda3/envs/python-3.10.10/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html from .autonotebook import tqdm as notebook_tqdm * Running on local URL: http://127.0.0.1:3000 * To create a public link, set `share=True` in `launch()`. 2. 编写推理代码class FCN: def __init__(self, model_path): self.num_classes = 117 self.rknn_lite = RKNNLite() self.rknn_lite.load_rknn(model_path) self.rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2) self.color_list = np.random.randint(0, 255, size=(self.num_classes, 3), dtype=np.uint8).tolist() def preprocess(self, image): image = image[:, :, ::-1] image = cv2.resize(image, (224, 224)) return np.expand_dims(image, axis=0) def rknn_infer(self, data): outputs = self.rknn_lite.inference(inputs=[data]) return outputs[0] def post_process(self, pred): feat = pred.squeeze() return np.argmax(feat, axis=-1).astype(np.uint8) def predict(self, image): # 图像预处理 data = self.preprocess(image) # 模型推理 pred = self.rknn_infer(data) # 模型后处理 feat = self.post_process(pred) # 生成图像分割结果 canv = np.zeros_like(image) mask = cv2.resize(feat, image.shape[:2][::-1], interpolation=cv2.INTER_NEAREST) for i in range(1, self.num_classes): canv[mask==i] = self.color_list[i] return cv2.addWeighted(image[..., ::-1], 0.5, canv, 0.5, 0) def release(self): self.rknn_lite.release() 3. 图片批量预测import os import cv2 import numpy as np import matplotlib.pyplot as plt from rknnlite.api import RKNNLite model = FCN('model/FCN.rknn') for image in os.listdir("image"): image = cv2.imread(os.path.join("image", image)) image = model.predict(image) plt.imshow(image) plt.axis('off') plt.show() model.release() 4. 创建 Flask 服务import cv2 import base64 import numpy as np from rknnlite.api import RKNNLite from flask import Flask, request, jsonify from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/v1/FCN', methods=['POST']) def inference(): data = request.get_json() image_base64 = data.get("image_base64") image_binary = base64.b64decode(image_base64) image_array = np.frombuffer(image_binary, dtype=np.uint8) image_bgr = cv2.imdecode(image_array, cv2.IMREAD_COLOR) image_rgb = model.predict(image_bgr) image_string = cv2.imencode('.jpg', image_rgb)[1].tobytes() image_base64 = base64.b64encode(image_string).decode('utf-8') return jsonify({ "image_base64": image_base64 }), 200 if __name__ == '__main__': model = FCN('model/FCN.rknn') app.run(host='0.0.0.0', port=8000) model.release() W rknn-toolkit-lite2 version: 2.3.2 I RKNN: [00:06:51.738] RKNN Runtime Information: librknnrt version: 1.4.0 (a10f100eb@2022-09-09T09:07:14) I RKNN: [00:06:51.738] RKNN Driver Information: version: 0.9.6 I RKNN: [00:06:51.739] RKNN Model Information: version: 1, toolkit version: 1.4.0-22dcfef4(compiler version: 1.4.0 (3b4520e4f@2022-09-05T12:50:09)), target: RKNPU v2, target platform: rk3588, framework name: TFLite, framework layout: NHWC * Serving Flask app '__main__' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:8000 * Running on http://192.168.3.50:8000 Press CTRL+C to quit 127.0.0.1 - - [02/May/2025 00:07:17] "POST /v1/FCN HTTP/1.1" 200 - 127.0.0.1 - - [02/May/2025 00:07:24] "POST /v1/FCN HTTP/1.1" 200 - 127.0.0.1 - - [02/May/2025 00:07:31] "POST /v1/FCN HTTP/1.1" 200 - 127.0.0.1 - - [02/May/2025 00:07:39] "POST /v1/FCN HTTP/1.1" 200 - 5. 上传图片预测三、小结本章介绍了基于RK3588平台使用FCN模型进行语义分割的AI应用开发全流程,包括模型训练与转换、Gradio界面开发、推理代码编写、批量预测实现及Flask服务部署。通过该流程,开发者可实现高效的图像分割任务,并在本地或云端进行预测和展示。----转自博客:https://bbs.huaweicloud.com/blogs/451998
  • [技术干货] RK3588 AI 应用开发 (YOLOX-目标检测)
    RK3588 AI 应用开发 (YOLOX-目标检测)一、模型训练和转换YOLOX是YOLO系列的优化版本,引入了解耦头、数据增强、无锚点以及标签分类等目标检测领域的优秀进展,拥有较好的精度表现,同时对工程部署友好。模型的训练与转换教程已经开放在AI Gallery中,其中包含训练数据、训练代码、模型转换脚本。在ModelArts的Notebook环境中训练后,再转换成对应平台的模型格式:onnx格式可以用在Windows设备上,RK系列设备上需要转换为rknn格式。二、应用开发1. 开发 Gradio 界面import cv2 import json import base64 import requests import numpy as np import gradio as gr def test_image(image_path): try: image_bgr = cv2.imread(image_path) image_string = cv2.imencode('.jpg', image_bgr)[1].tobytes() image_base64 = base64.b64encode(image_string).decode('utf-8') params = {"image_base64": image_base64} response = requests.post(f'http://{ip}:{port}{url}', data=json.dumps(params), headers={"Content-Type": "application/json"}) if response.status_code == 200: image_base64 = response.json().get("image_base64") image_binary = base64.b64decode(image_base64) image_array = np.frombuffer(image_binary, dtype=np.uint8) image_rgb = cv2.imdecode(image_array, cv2.IMREAD_COLOR) else: image_rgb = None except Exception as e: return None else: return image_rgb if __name__ == "__main__": port = 8000 ip = "127.0.0.1" url = "/v1/fish_det" demo = gr.Interface(fn=test_image, inputs=gr.Image(type="filepath"), outputs=["image"], title="YOLOX 深海鱼类检测") demo.launch(share=False, server_port=3000) * Running on local URL: http://127.0.0.1:3000 * To create a public link, set `share=True` in `launch()`. 2. 编写推理代码%%writefile YOLOX/yolox/data/datasets/voc_classes.py #!/usr/bin/env python3 # -*- coding:utf-8 -*- # Copyright (c) Megvii, Inc. and its affiliates. # VOC_CLASSES = ( '__background__', # always index 0 VOC_CLASSES = ( "fish", ) Overwriting YOLOX/yolox/data/datasets/voc_classes.pyimport sys sys.path.append("YOLOX") from yolox.utils import demo_postprocess, multiclass_nms, vis from yolox.data.data_augment import preproc as preprocess from yolox.data.datasets.voc_classes import VOC_CLASSESimport cv2 import numpy as np import ipywidgets as widgets from rknnlite.api import RKNNLite from IPython.display import display class YOLOX: def __init__(self, model_path): self.ratio = None self.rknn_lite = RKNNLite() self.rknn_lite.load_rknn(model_path) self.rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2) def preprocess(self, image): start_img, self.ratio = preprocess(image, (320, 320), swap=(0, 1, 2)) return np.expand_dims(start_img, axis=0) def rknn_infer(self, data): outputs = self.rknn_lite.inference(inputs=[data]) return outputs[0] def post_process(self, pred): predictions = demo_postprocess(pred.squeeze(), (320, 320)) boxes = predictions[:, :4] scores = predictions[:, 4:5] * predictions[:, 5:] boxes_xyxy = np.ones_like(boxes) boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2] / 2. boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3] / 2. boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2] / 2. boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3] / 2. boxes_xyxy /= self.ratio dets = multiclass_nms(boxes_xyxy, scores, nms_thr=0.45, score_thr=0.25) return dets def predict(self, image): # 图像预处理 data = self.preprocess(image) # 模型推理 pred = self.rknn_infer(data) # 模型后处理 dets = self.post_process(pred) # 绘制目标检测结果 if dets is not None: final_boxes = dets[:, :4] final_scores, final_cls_inds = dets[:, 4], dets[:, 5] image = vis(image, final_boxes, final_scores, final_cls_inds, conf=0.25, class_names=VOC_CLASSES) return image[..., ::-1] def img2bytes(self, image): """将图片转换为字节码""" return bytes(cv2.imencode('.jpg', image)[1]) def infer_video(self, video_path): """视频推理""" image_widget = widgets.Image(format='jpeg', width=800, height=600) display(image_widget) cap = cv2.VideoCapture(video_path) while True: ret, img_frame = cap.read() if not ret: break image_pred = self.predict(img_frame) image_widget.value = self.img2bytes(image_pred) cap.release() def release(self): """释放资源""" self.rknn_lite.release() 3. 图像预测4. 视频推理5. 创建 Flask 服务import cv2 import base64 import numpy as np from rknnlite.api import RKNNLite from flask import Flask, request, jsonify from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/v1/fish_det', methods=['POST']) def inference(): data = request.get_json() image_base64 = data.get("image_base64") image_binary = base64.b64decode(image_base64) image_array = np.frombuffer(image_binary, dtype=np.uint8) image_bgr = cv2.imdecode(image_array, cv2.IMREAD_COLOR) image_rgb = model.predict(image_bgr) image_string = cv2.imencode('.jpg', image_rgb)[1].tobytes() image_base64 = base64.b64encode(image_string).decode('utf-8') return jsonify({ "image_base64": image_base64 }), 200 if __name__ == '__main__': model = YOLOX('model/yolox_fish.rknn') app.run(host='0.0.0.0', port=8000) model.release() 6. 上传图片预测三、小结本章介绍了基于RK3588平台使用YOLOX进行目标检测的全流程,包括模型训练与转换、Gradio界面开发、推理代码编写、图像和视频预测实现,以及Flask服务部署。整体实现了高效的鱼类检测应用,适用于嵌入式设备部署与实际场景应用。----转自博客:https://bbs.huaweicloud.com/blogs/452001
总条数:82 到第
上滑加载中