1908600638/android-shape-color
1908600638/android-shape-color has 7 GitHub stars and 0 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-14 19:54 UTC. Last push 2026-11-11.
Measured star growth
- in 24h
- 0
- in 7 days
- Collecting history
- in 15 days
- Collecting history
- in 1 month
- Collecting history
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
About
Android 图形形状与颜色识别示例:CameraX + ncnn + 两个 YOLOv5 模型,按 IOU 配对形状与颜色
Machine LearningREADME.md
Original图形形状和颜色识别
形状 5 类:圆形、矩形、菱形、五角星、三角形。 颜色 8 类:红、绿、蓝、黄、品红、青、黑、白。
功能
- 摄像头实时预览,点「识别」抓取当前这一帧
- 一次可识别多个图形,每个图形输出「形状(颜色)」和置信度
- 结果在图上画框标注,点一下画面回到实时预览
实现原理
整体流程
摄像头抓帧(JPEG)
│
▼
ImageSource.fromJpeg() ──▶ Bitmap(最长边 ≤ 2048,已旋正)
│
▼
ShapeColorEngine.scan()
├── 首次调用:initShape() + initColor() 从 assets 加载两个模型
│
├── detectShape(bitmap) ──▶ 形状框(圆/矩/菱/星/三角)
└── detectColor(bitmap) ──▶ 颜色框(8 色)
│
▼
两个结果按检测框重合度(IOU > 0.5)配对
│
▼
List<Detect>:每个形状拼成「圆形(红色)」+ 平均置信度
│
▼
BoxPainter 画框 ──▶ 预览层 + 结果文本
1. 模型信息
形状和颜色是两个独立训练的 YOLOv5 模型:
| 模型文件 | 类别数 | 输出 |
|---|---|---|
shape.bin / shape.param |
5 | circle、rectangle、diamond、star、triangle |
TFT-B-color-sim-opt.bin / .param |
8 | red、green、blue、yellow、magenta、cyan、black、white |
2. 形状与颜色配对
两个模型各自出框,同一个图形上,形状框和颜色框位置会略有偏差。用 IOU(交并比) 判断是不是同一个物体:
IOU = 交集面积 / (形状框面积 + 颜色框面积 - 交集面积)
遍历所有形状,对每个形状找出 IOU 最大且 > 0.5 的颜色框作为它的颜色。配不上颜色的形状显示为「颜色未知」。
两个模型的坐标原点都是左上角(
x/y是左上角,w/h是宽高),所以 IOU 直接按轴对齐矩形算。
3. 推理引擎
用的是腾讯 ncnn——为手机端优化的 C++ 推理框架,以静态库形式随仓库附带,通过 CMake 编译成 libyolov5ncnn.so。
Java 层通过 JNI 调用,暴露四个方法:
boolean initShape(AssetManager) // 加载形状模型
Obj[] detectShape(Bitmap, boolean) // 形状推理
boolean initColor(AssetManager) // 加载颜色模型
Obj[] detectColor(Bitmap, boolean) // 颜色推理
4. YOLOv5 后处理
yolov5ncnn_jni.cpp 里是完整的 YOLOv5 后处理:
| 步骤 | 说明 |
|---|---|
| letterbox | 等比缩放到 640×640,空白补灰边,避免拉伸变形 |
| 前向 | ncnn 输出 3 个尺度特征图(stride 8 / 16 / 32) |
| 解码 | 每个格子按 3 个 anchor 解出 cx, cy, w, h 和类别置信度 |
| NMS | 去掉重叠框,阈值 0.45 |
| 反 letterbox | 把框坐标按同样比例映射回原图尺寸 |
置信度阈值 0.25。
这个 JNI 文件是从一个包含 7 个模型的工程里裁出来的,只保留了形状和颜色这两个模型的 Init/Detect,从 2000 多行裁到 631 行,其余 5 个模型的代码全部删除。
代码架构
app/src/main/
├── jni/
│ ├── CMakeLists.txt 链接 ncnn 并编译 libyolov5ncnn.so
│ ├── yolov5ncnn_jni.cpp 631 行 推理 + YOLOv5 后处理(形状 + 颜色两个模型)
│ └── ncnn-20230816-android-vulkan/ ncnn 静态库与头文件(31 MB,第三方)
├── assets/
│ ├── shape.bin 26.8 MB 形状模型权重
│ ├── shape.param 形状模型结构
│ ├── TFT-B-color-sim-opt.bin 26.8 MB 颜色模型权重
│ └── TFT-B-color-sim-opt.param 颜色模型结构
└── java/com/shizi/shape/
├── MainActivity.java 178 行 摄像头预览、抓帧、结果展示
├── img/
│ └── ImageSource.java 74 行 JPEG 解码 + 降采样 + 旋正
├── scan/
│ ├── Scanner.java 13 行 引擎接口
│ ├── Detect.java 17 行 一条结果
│ ├── ScanOutcome.java 17 行 结果集合 + 汇总文本
│ ├── BoxPainter.java 84 行 画框与标签
│ ├── YoloDetector.java 23 行 native 方法声明
│ └── ShapeColorEngine.java 180 行 ★ 双模型推理 + 配对逻辑
└── util/
└── UiKit.java 11 行 Toast
各文件职责
| 文件 | 职责 |
|---|---|
MainActivity |
相机权限 → CameraX 预览 → ImageCapture 抓帧 → 调引擎 → 显示标注图和文字 |
ImageSource |
JPEG 解码、降采样到最长边 2048、按元数据旋转角旋正 |
Scanner |
引擎接口,scan(bitmap, callback) + close(),让 UI 层不依赖具体实现 |
Detect |
一条结果:label(框上短标签)、note(详细说明)、score(置信度)、box(位置) |
ScanOutcome |
结果列表 + 汇总多行文本 |
BoxPainter |
把每个 Detect 的框和标签画到图片副本上 |
YoloDetector |
声明 4 个 native 方法,System.loadLibrary("yolov5ncnn") |
ShapeColorEngine |
核心:懒加载两个模型、跑两次推理、IOU 配对、英文标签转中文 |
yolov5ncnn_jni.cpp |
核心:YOLOv5 完整推理与后处理实现 |
核心代码位置
| 位置 | 作用 |
|---|---|
ShapeColorEngine.scan() |
懒加载模型 + 两次推理 + 组装结果 |
ShapeColorEngine.combine() |
配对核心:为每个形状找对应的颜色框 |
ShapeColorEngine.bestColor() |
取 IOU 最大且超过阈值的颜色框 |
ShapeColorEngine.iou() |
交并比计算 |
ShapeColorEngine.shapeName() / colorName() |
模型输出的英文标签转中文 |
yolov5ncnn_jni.cpp → initShape / initColor |
从 assets 读模型并建 ncnn::Net |
yolov5ncnn_jni.cpp → detectShape / detectColor |
letterbox、前向、解码、NMS、坐标还原 |
yolov5ncnn_jni.cpp → generate_proposals |
单尺度候选框解码 |
yolov5ncnn_jni.cpp → nms_sorted_bboxes |
非极大值抑制 |
环境要求
| 项 | 版本 |
|---|---|
| Android Studio | 2021.3.1(Dolphin)及以上 |
| JDK | 11 或 17(Android Studio 自带的 JBR 即可) |
| Android Gradle Plugin | 7.3.1 |
| Gradle | 7.4 |
| compileSdk / targetSdk | 34 |
| minSdk | 24(Android 7.0) |
| NDK | 23.1.7779620 |
| CMake | 3.10.2 |
| ABI | 只编 arm64-v8a |
| 设备 | ARM64 真机 + 摄像头 |
NDK 和 CMake 在 Android Studio 的 SDK Manager → SDK Tools 里勾选安装。版本对不上时改 app/build.gradle 里的 cmake { version } 和 SDK Manager 里已装的版本即可。
只出 arm64-v8a 是因为附带的 ncnn 静态库只有 arm64-v8a 和 armeabi-v7a 两套,为控制体积只留了 64 位。要 32 位就把
abiFilters改成'arm64-v8a', 'armeabi-v7a'。
编译运行
- 用 Android Studio 打开本目录
- 确认 SDK Manager 里装了 NDK 23.1.7779620 和 CMake 3.10.2
- 首次打开会自动生成
local.properties(记录本机 SDK 路径),不要提交到版本库 - 连接 ARM64 真机,点 Run
- 首次启动请求相机权限,允许后看到实时画面
- 对准图形,点「识别」
命令行构建:
./gradlew assembleDebug
首次构建会先跑 CMake 编译 C++,比纯 Java 工程慢(约 20 秒)。产物在 app/build/outputs/apk/debug/app-debug.apk。
工程放在中文路径下时 AGP 会报非 ASCII 路径错误,本工程已在
gradle.properties加了一行android.overridePathCheck=true。
常见编译问题
| 现象 | 原因与处理 |
|---|---|
CMake Error: could not find ncnn |
NDK/CMake 版本不对,或 ncnn-20230816-android-vulkan/<abi>/lib/cmake/ncnn 目录缺失 |
error: expected '}' |
yolov5ncnn_jni.cpp 末尾的匿名命名空间右括号被误删,文件末尾应有两级 } |
Duplicate class kotlin.* |
Kotlin stdlib 版本冲突,app/build.gradle 末尾的 configurations.configureEach { exclude ... } 已经处理 |
依赖
| 依赖 | 用途 | 许可 |
|---|---|---|
| ncnn 20230816(含 Vulkan) | 端侧推理框架,静态库与头文件随仓库附带 | BSD-3-Clause,版权所有 Tencent,全文见 app/src/main/jni/ncnn-20230816-android-vulkan/LICENSE |
| CameraX 1.3.0(core / camera2 / lifecycle / view) | 摄像头预览与抓帧 | Apache-2.0 |
| AndroidX AppCompat、Activity、Core | 基础 UI | Apache-2.0 |
shape.bin / TFT-B-color-sim-opt.bin |
YOLOv5 训练得到的形状、颜色模型 | 仅用于学习演示 |