RK3568 NPU 加速版(v3.0.0) · 加密授权激活 · Android / Java 集成
本算法用于晨检 / 工牌核验场景下的手部异常检测:检测戒指、指甲油、伤口、创可贴、手镯手链等佩戴物, 并判读手掌 / 手背的放置是否合规。模型为 RK3568 专用 INT8 统一模型,由 RKNN NPU 推理。
| 项目 | 说明 |
|---|---|
| 算法包 ID | hand-anomaly-multiclass · v3.0.0(10 类) |
| 运行硬件 | RK3568 主板(Android,arm64-v8a),NPU 驱动 已内置 |
| 输入 | 摄像头一帧 JPEG/PNG 字节(建议 ≤ 1280×720) |
| 输出 | 目标列表,每项 {label, score, bbox[x,y,w,h]}(bbox 为原图像素) |
| 文件 | 用途 |
|---|---|
handanomaly-sdk-rknn-3.0.0.aar | SDK 本体(单文件,唯一必须引入的产物) |
HandAnomalyClient.java | 完整 Java 接入示例 |
| 本手册 | 对接说明 |
activate() 与 recognizeImageBytes()。sdk.activate("ACT-XXXX-XXXX-XXXX")。成功后授权缓存在本机,之后启动自动复用,断网也能识别。sdk.recognizeImageBytes(bytes),返回检测目标列表。android {
compileSdk 34
defaultConfig {
minSdk 24 // ≥ 23(Android Keystore 要求)
ndk { abiFilters 'arm64-v8a' } // RK3568 只需 arm64
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
dependencies {
// ★ 唯一依赖,无需再声明其它库
implementation files('libs/handanomaly-sdk-rknn-3.0.0.aar')
}
<uses-permission android:name="android.permission.INTERNET" />
AAR 自带混淆规则,开启 minifyEnabled 无需额外配置。
| 方式 | 调用 | 适用 |
|---|---|---|
| ① 激活码 | activate("ACT-XXXX-XXXX-XXXX") | 我方提供 16 位激活码(最常用) |
| ② 云端激活 | activateByDevice() | 我方已预登记设备,一键免码激活 |
| ③ 申请激活 | apply(...) → applyStatus(no) | 提交申请,我方审核后自动激活 |
| ④ 快速激活 | generateQuickCode() → activateByQuickCode("1234") | 设备出 4 位码报我方,10 分钟内有效 |
import com.company.handanomaly.sdk.HandAnomalySdk;
// 无参构造:激活地址 / 指纹 / 密文包 / 公钥已全部内置,只传 Context
HandAnomalySdk sdk = new HandAnomalySdk(context.getApplicationContext());
// 仅首次;之后启动自动复用本机授权
if (!sdk.isActivated()) {
sdk.activate("ACT-XXXX-XXXX-XXXX"); // ① 激活码
// sdk.activateByDevice(); // ② 云端激活
}
// bytes:摄像头一帧(JPEG/PNG 字节)
List<Map<String,Object>> results = sdk.recognizeImageBytes(bytes);
for (Map<String,Object> r : results) {
String label = (String) r.get("label"); // 类别名,如 "wound"
float score = ((Number) r.get("score")).floatValue(); // 置信度 0~1
float[] bbox = (float[]) r.get("bbox"); // [x, y, 宽, 高](原图像素)
}
// 要 JSON:String json = sdk.recognizeToJson(bytes);
// 与 CameraX 配合:ImageAnalysis 回调里把帧编码为 JPEG 后交给 SDK 即可
// 释放:sdk.release(); (Activity/Application 退出时调用,下次识别自动重建)
模型同时输出整手框与异常框两类目标,请区分处理:
| ID | 代码 | 含义 | 类型 |
|---|---|---|---|
| 0 | palm | 手掌(手心) | 整手框(放置判定) |
| 1 | back | 手背 | 整手框(放置判定) |
| 2 | ring | 戒指 | 异常框 |
| 3 | nail_polish | 指甲油 | 异常框 |
| 4 | long_nail | 长指甲 | 异常框 |
| 5 | wound | 伤口 | 异常框 |
| 6 | glove | 手套 | 异常框 |
| 7 | bandaid | 创可贴/绷带 | 异常框 |
| 8 | watch | 手表 | 异常框 |
| 9 | bracelet | 手镯/手链 | 异常框 |
boolean hasHand = false;
List<String> anomalies = new ArrayList<>();
for (Map<String,Object> r : results) {
String label = (String) r.get("label");
if ("palm".equals(label) || "back".equals(label)) {
hasHand = true; // 整手框:手已正确放置
} else {
anomalies.add(label); // 异常框:佩戴物/伤患
}
}
// 无整手框 → 未检测到手 / 放置不合规;anomalies 非空 → 存在需提示的异常
bbox 已还原到原图像素,可直接画框。score ≥ 0.25 的目标才会返回(阈值内置,无需配置)。算法包以密文形式内置在 SDK 中,SDK 内不含任何解密密钥。密钥只在激活成功时由授权平台用本机密钥加密下发:
| 现象 | 原因 / 处理 |
|---|---|
SecurityError: 未激活 | 首次使用未调 activate();或授权过期/被吊销。重新激活。 |
UnsatisfiedLinkError: handrknn | APK 未打入 native 库。确认引入的是本 AAR 且 abiFilters 含 arm64-v8a。 |
| NPU 初始化失败 | 勿自行替换 SDK 内置的 NPU 驱动;确认运行设备为 RK3568。 |
| 激活连接失败 / 超时 | 设备无法访问授权服务。检查设备网络、DNS、防火墙出站(HTTPS 443)。 |
| 激活码无效 | 激活码已绑定其他设备或已过期。联系我方重新发放。 |
| 结果始终为空 | 画面无手或置信度低;或传入非 JPEG/PNG 字节。先用附录示例验证。 |
Q:每次运行都要激活吗?
A:不需要。仅首次;之后自动复用本机授权,断网可用。
Q:支持哪些设备?
A:RK3568 主板(Android,arm64)。其它平台(Flutter / Python / 其它芯片)另有交付形态,联系我方。
Q:可以多线程并发识别吗?
A:不建议。请依次调用(可放同一后台线程/队列)。
Q:一帧传多大图?
A:建议 ≤ 1280×720,SDK 内部自动缩放。
完整可编译示例见随包文件 HandAnomalyClient.java,要点如下:
public class HandAnomalyClient {
private final HandAnomalySdk sdk;
public HandAnomalyClient(android.content.Context ctx) {
sdk = new HandAnomalySdk(ctx.getApplicationContext());
}
public void ensureActivated(String activationCode) {
if (!sdk.isActivated()) sdk.activate(activationCode);
}
public void inspect(byte[] jpegBytes) {
List<Map<String,Object>> results = sdk.recognizeImageBytes(jpegBytes);
boolean hasHand = false;
List<String> anomalies = new ArrayList<>();
for (Map<String,Object> r : results) {
String label = (String) r.get("label");
if ("palm".equals(label) || "back".equals(label)) hasHand = true;
else anomalies.add(label);
}
// 业务逻辑:hasHand / anomalies
}
}