opencv-mobile 现已支持 milkv-duo/duo256m MIPI CSI 摄像头和vpss硬件加速
TL;DR
- opencv-mobile highgui 模块实现基于 cvi-mmf 访问摄像头流
- 在运行时自动动态加载 ae+awb+isp+cvi_bin 库实现 ISP 图像调节
- 在运行时自动动态加载 vpss 库实现 crop + YUV2BGR 硬件加速
- 无需修改代码,调用 cv::VideoCapture 便自动支持,支持设置分辨率
- 关闭摄像头后,还能正常再打开摄像头,不必每次关摄像头后reboot(说的就是
./CviIspTool.sh 64M
- 因为只测试验证了 milkv-duo / milkv-duo-256m 搭配 GC2083 摄像头,白名单暂时只有这俩
下载新版本 opencv-mobile milkv-duo 预编译包
解压到项目目录中,cmake 中设置 OpenCV_DIR 路径,find_package 找到
project(testcam)
cmake_minimum_required(VERSION 3.5)
set(CMAKE_BUILD_TYPE release)
# opencv4 requires c++11
set(CMAKE_CXX_STANDARD 11)
set(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/opencv-mobile-4.8.1-milkv-duo/lib/cmake/opencv4")
find_package(OpenCV REQUIRED)
add_executable(testcam main.cpp)
target_link_libraries(testcam ${OpenCV_LIBS})
opencv-mobile
opencv-mobile 通过调整编译参数,删减部分opencv源码,来最小化编译的 opencv 库
提供了 opencv 常用的功能,如读写图片,处理,矩阵操作等等 版本与上游同步,无第三方依赖
在绝大多数情况下,以 1/10 的体积无痛替换官方 opencv,尤其适合对体积有特殊要求的移动端和嵌入式环境
milkv-duo / milkv-duo 256m
Milk-V Duo 是基于 CV1800B 芯片的超紧凑型嵌入式开发平台。Duo 256M 是以 SG2002 为主控制器设计的 Duo 的升级版,内存升级到 256M,支持标准的 Linux 系统和应用,可以满足大内存、大存储量的应用需求。Duo 256M 是以 SG2002 为主控制器设计的 Duo 的升级版,支持标准 Linux 系统和应用程序。
milkv 小板子真可爱
cvi-mmf
CVITEK所提供的多媒体软件架构(Multimedia Framework,简称MMF),用以缩短应用程序开发所需的时间。
此架构屏蔽了处理器端的复杂底层设计和差异,对应用程序提供统一且便捷的MMF Programming Interface编程接口。
一些实现细节和限制
运行时加载 cvi-mmf 动态库
为了减少编译耦合,opencv-mobile中采用运行时 dlopen/dlsym 方式加载 libsys libvpu libae libawb libisp libcvi_bin libsns_gc2083,即便编译时候缺库依然兼容可用
这种方式也能自动适配后期系统库升级
libsns_gc2083.so 在 mmf sdk 中并没有直接提供库,但可以从开发板上 /mnt/system/lib/libsns_gc2083.so
拷出来到本地做链接测试
吐嘈一下:cvi-mmf 的 so 库之间有相互依赖,还有 unresolved symbol,dlopen相当费劲。加载顺序的先后区别可能影响调用导致segfault(x
设备检测和白名单
优化代码在 milkv-duo / milkv-duo 256m 上做了验证测试
加载cvi-mmf库时,额外判断 /proc/device-tree/model 是否为 milkv-duo 设备
milkv-duo 和 milkv-duo-256m cvi-mmf 接口源码级兼容,但实际上 sns ini 配置文件却是不同的,如果用的不对会导致无法正常获取图像帧,报错
isp_err_chk:6343(): CSIBDG_A CH0 frm height less than setting(1080)
根据model信息区分两种型号,加载对应配置后恢复工作
分辨率自适应
摄像头原生分辨率是 1920x1080 30fps
- 用户请求尺寸超出 1080p,自动保持比例降低尺寸到 1080p 范围内
- 用户请求小于 1080p,会自动居中裁切并保持比例缩小到需要的分辨率
vb 内存池数量
虽然理论上1个内存块就可以一直复用,提供给NV21取帧,但是测试中发现当 vb 内存块太少时会发生 vb 内存耗尽的错误,导致一段时间后再也无法正常取帧
参照 cvi-mmf sample 代码开辟4个内存块用于存放NV21数据,不再发生vb耗尽问题
调用示例
- 用
cv::VideoCapture
打开摄像头,设置分辨率320x240 - 每隔1秒获取1帧图像
- 关闭摄像头
- 最后把9张图拼接在一起保存
开头1帧因为isp还在统计图像信息,来不及ISP自动处理,所以是黑的
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <unistd.h> // sleep()
int main()
{
cv::VideoCapture cap;
cap.set(cv::CAP_PROP_FRAME_WIDTH, 320);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 240);
cap.open(0);
const int w = cap.get(cv::CAP_PROP_FRAME_WIDTH);
const int h = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
fprintf(stderr, "%d x %d\n", w, h);
cv::Mat bgr[9];
for (int i = 0; i < 9; i++)
{
cap >> bgr[i];
sleep(1);
}
cap.release();
// combine into big image
{
cv::Mat out(h * 3, w * 3, CV_8UC3);
bgr[0].copyTo(out(cv::Rect(0, 0, w, h)));
bgr[1].copyTo(out(cv::Rect(w, 0, w, h)));
bgr[2].copyTo(out(cv::Rect(w * 2, 0, w, h)));
bgr[3].copyTo(out(cv::Rect(0, h, w, h)));
bgr[4].copyTo(out(cv::Rect(w, h, w, h)));
bgr[5].copyTo(out(cv::Rect(w * 2, h, w, h)));
bgr[6].copyTo(out(cv::Rect(0, h * 2, w, h)));
bgr[7].copyTo(out(cv::Rect(w, h * 2, w, h)));
bgr[8].copyTo(out(cv::Rect(w * 2, h * 2, w, h)));
cv::imwrite("out.jpg", out);
}
return 0;
}
终端信息输出
ISP Vipipe(0) Allocate pa(0x8bf30000) va(0x0x3fe8214000) size(291120)
awbInit ver 6.8@2021500
0 R:1400 B:3100 CT:2850
1 R:1500 B:2500 CT:3900
2 R:2300 B:1600 CT:6500
Golden 1024 1024 1024
WB Quadratic:0
isWdr:0
ViPipe:0,===GC2083 1080P 30fps 10bit LINE Init OK!===
binName = /mnt/cfg/param/cvi_sdr_bin
********************************************************************************
cvi_bin_isp message
gerritId: 36403 commitId: c69c5863e
md5: cab880835a2ad5184de5ed7762404b84
sensorNum 1
sensorName0 2083
PQBIN message
gerritId: 80171 commitId: 5c9d8fc5d
md5: ba5a510e093ad42db6788e6c2d13169e
sensorNum 3
sensorName0 2053
author: wanqiang.he desc: 思博慧CV1812H_GC2083_RGB_mode_V1.0.0
createTime: 2023-08-04 16:48:08version: V1.1
tool Version: v3.0.5.24 mode:
********************************************************************************
sensorName(0) mismatch, mwSns:2083 != pqBinSns:2053
320 x 240
0 R:1165 B:3087 CT:2688
1 R:1464 B:2327 CT:3937
2 R:1974 B:1613 CT:7225
Golden 1464 1024 2327
wdrLEOnly:1
ISP Vipipe(0) Free pa(0x8bf30000) va(0x0x3fe8214000)
gc2083_standby
拉取到本地查看结果图片