- 2024-06-09
-
回复了主题帖:
【AI挑战营终点站】应用落地:部署手写数字识别应用到幸狐RV1106开发板
#AI挑战营终点站#RV1106手写数字识别部署 https://bbs.eeworld.com.cn/thread-1284410-1-1.html
打卡完成,感谢管理员的耐心提醒和宽容,本应早点完成上传的,后因出差半个月导致没有上传,非常抱歉
-
发表了主题帖:
#AI挑战营终点站#RV1106手写数字识别部署
本帖最后由 和和123 于 2024-6-9 15:20 编辑
#AI挑战营终点站#RV1106手写数字识别部署
下载和解压手写工具
参考:
https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-quick-start
安装驱动:
下载:
https://files.luckfox.com/wiki/Luckfox-Pico/Software/DriverAssitant_v5.12.zip
安装:
安装烧写工具:
下载:
https://files.luckfox.com/wiki/Luckfox-Pico/Software/SocToolKit.zip
安装:
解压后执行
选择RV1106
下载固件库方法
① 将 SD 卡装在读卡器并连接到电脑,选择 SD 卡工具。
② 在 USB 磁盘下会显示 SD 卡大小,如果未显示重新插拔读卡器。
③ 选择 SD卡启动。
④ 导入启动文件。(注意:启动文件不包括update.img)
⑤点击创建 SD 卡。
将烧写好的sd卡插入板子,上电后通过adb登入
成功识别到摄像头会生成rkipc.ini文件
通过VLC拉流,查看摄像头内容(rtsp://172.32.0.93/live/0)
设置电脑ip:
查看板子ip信息并将电脑ip设置为同网段。
设置VLC:
配置开发环境
参考
https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-SDK
https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-RKNN-Test
https://github.com/luckfox-eng29/luckfox_pico_rtsp_opencv
下载源码:
git clone git@github.com:luckfox-eng29/luckfox_pico_rtsp_opencv.git
编译:
```
export LUCKFOX_SDK_PATH=<Your Luckfox-pico Sdk Path>
mkdir build
cd build
cmake ..
make && make install
```
将编译生成的文件上传板子,内容如图:
运行前请关闭系统默认的rkipc程序,路径如下:
在上面基础上增加图像获取和预处理
图像获取
使用RK提供的函数,从指定通道获取一帧图片`
s32Ret = RK_MPI_VPSS_GetChnFrame(0, 0, &stVpssFrame, -1);
if (s32Ret == RK_SUCCESS)
{
void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);
...
...
...
}
预处理部分
// 在图像中找到数字的轮廓,同时减小找到轮廓时的抖动
cv::Rect find_digit_contour(const cv::Mat &image) {
// 预处理图像
cv::Mat gray, blurred, edged;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);
cv::Canny(blurred, edged, 30, 150);
// 应用形态学操作
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::dilate(edged, edged, kernel);
cv::erode(edged, edged, kernel);
// 查找轮廓,声明一个变量来存储轮廓
std::vector<std::vector<cv::Point>> contours;
cv::findContours(edged, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
if (contours.empty()) {
return cv::Rect();
}
// 找到最大的轮廓
auto largest_contour = std::max_element(contours.begin(), contours.end(),
[](const std::vector<cv::Point>& a, const std::vector<cv::Point>& b) {
return cv::contourArea(a) < cv::contourArea(b);
});
// **轮廓面积过滤**:在找到轮廓之后,可以排除那些面积过小的轮廓。这样可以减少不必要的小轮廓对整体结果的影响。
if (cv::contourArea(*largest_contour) < 10) {
return cv::Rect();
}
// **轮廓形状过滤**:除了面积外,还可以考虑其他形状特征,如轮廓宽高比。这样可以排除一些不规则的轮廓,从而提高准确性。
cv::Rect bounding_box = cv::boundingRect(*largest_contour);
float aspect_ratio = static_cast<float>(bounding_box.width) / bounding_box.height;
if (aspect_ratio < 0.2 || aspect_ratio > 3) {
return cv::Rect();
}
// **轮廓稳定性检测**:
// 通过比较当前帧和之前几帧的轮廓位置来判断轮廓的稳定性。
// 如果多帧之间的轮廓位置变化较小,则可以认为轮廓比较稳定,不需要进行过多的调整。
static std::vector<cv::Rect> prev_bounding_boxes;
if (prev_bounding_boxes.size() > 5) {
prev_bounding_boxes.erase(prev_bounding_boxes.begin());
}
prev_bounding_boxes.push_back(bounding_box);
if (prev_bounding_boxes.size() == 5) {
float avg_width = 0.0;
float avg_height = 0.0;
for (const auto& box : prev_bounding_boxes) {
avg_width += box.width;
avg_height += box.height;
}
avg_width /= prev_bounding_boxes.size();
avg_height /= prev_bounding_boxes.size();
float width_diff = std::abs(bounding_box.width - avg_width) / avg_width;
float height_diff = std::abs(bounding_box.height - avg_height) / avg_height;
if (width_diff > 0.1 || height_diff > 0.1) {
return cv::Rect();
}
}
// 对图像边框每个方向扩大15个像素
bounding_box.x = std::max(0, bounding_box.x - 15);
bounding_box.y = std::max(0, bounding_box.y - 15);
bounding_box.width = std::min(image.cols - bounding_box.x, bounding_box.width + 30);
bounding_box.height = std::min(image.rows - bounding_box.y, bounding_box.height + 30);
// 返回最大轮廓的边界框
return bounding_box;
}
// 预处理数字区域
cv::Mat preprocess_digit_region(const cv::Mat ®ion)
{
// 将图像转换为灰度图像,然后调整大小为28x28,最后将像素值归一化为0到1之间的浮点数
cv::Mat gray, resized, bitwized, normalized;
cv::cvtColor(region, gray, cv::COLOR_BGR2GRAY);
// 扩大图像中的数字轮廓,使其更容易识别
cv::threshold(gray, gray, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
// 调整图像颜色,将图像颜色中低于127的像素值设置为0,高于200的像素值设置为255
cv::threshold(gray, gray, 127, 255, cv::THRESH_BINARY_INV);
// 对图像黑白进行反转,黑色变成白色,白色变成黑色
cv::bitwise_not(gray, bitwized);
// 手动实现黑白反转
for (int i = 0; i < bitwized.rows; i++)
{
for (int j = 0; j < bitwized.cols; j++)
{
bitwized.at<uchar>(i, j) = 255 - bitwized.at<uchar>(i, j);
}
}
// 将图片大小调整为28x28,图片形状不发生畸变,过短的部分使用黑色填充
cv::resize(bitwized, resized, cv::Size(28, 28), 0, 0, cv::INTER_AREA);
return resized;
}
后处理部分
// 将模型输出进行归一化,并计算输出的概率分布
// Parameters:
// output_attrs: 输出张量属性,包含了零点(zero point)值和缩放因子等信息
// output: 模型输出的数据,以INT8格式存储
// out_fp32: 存储归一化后的浮点数输出数据
static void output_normalization(rknn_tensor_attr* output_attrs, uint8_t *output, float *out_fp32)
{
int32_t zp = output_attrs->zp;
float scale = output_attrs->scale;
// 将INT8格式的输出数据进行反量化为浮点数,并进行存储
for(int i = 0; i < 10; i ++)
out_fp32<i> = deqnt_affine_to_f32(output<i>,zp,scale);
// 计算输出数据的L2范数
float sum = 0;
for(int i = 0; i < 10; i++)
sum += out_fp32<i> * out_fp32<i>;
// 对归一化后的浮点数输出进行归一化处理,确保输出数据的范围在[0,1]之间
float norm = sqrt(sum);
for(int i = 0; i < 10; i++)
out_fp32<i> /= norm;
// 打印输出数据的值
printf("\n===================Output data values:===================\n");
for (int i = 0; i < 10; ++i)
{
printf("%f ", out_fp32<i>);
}
printf("\n");
// 找出最大概率对应的数字,并记录最大概率及其对应的数字
float max_prob = -1.0;
int predicted_digit = -1;
// 计算最大值的索引
for (int i = 0; i < 10; ++i)
{
if (out_fp32<i> > max_prob)
{
max_prob = out_fp32<i>;
predicted_digit = i;
}
}
// 将预测的数字及其对应的概率记录到队列中
predictions_queue.push_back({predicted_digit, max_prob});
// 打印预测的数字与其对应的概率
printf("========Predicted digit: %d, Probability: %.2f========\n\n", predicted_digit, max_prob);
}
结果:
- 2024-05-18
-
加入了学习《T-BOX 与车身电机 TI 解决方案》,观看 eCall in T-Box
- 2024-05-14
-
发表了主题帖:
#AI挑战营第二站#onxx模型转换为rknn模型
1.环境搭建:
安装 rknn-toolkit与其对应的依赖库
下载 https://github.com/airockchip/rknn-toolkit2
根据 doc目录下 01_Rockchip_RKNPU_Quick_Start_RKNN_SDK_V2.0.0beta0_CN.pdf 安装对应的依赖库和rknn-toolkit工具
2. 模型转换
使用下载好的rknn_model_zoo ,\examples\yolov5\python\convert.py python文件转换模型
执行python3 convert.py ../model/test1.onnx rv1106 i8 ../model/test.rknn
遇到错误
按照提示将第一站的脚本进行优化,重新生成onxx模型
由 改为
后重新生成onxx模型,再次执行python3 convert.py ../model/test1.onnx rv1106 i8 ../model/test.rknn即可转换成功。
源码:
import sys
from rknn.api import RKNN
DATASET_PATH = '../../../datasets/COCO/coco_subset_20.txt'
DEFAULT_RKNN_PATH = '../model/test.rknn'
DEFAULT_QUANT = True
def parse_arg():
if len(sys.argv) < 3:
print("Usage: python3 {} onnx_model_path [platform] [dtype(optional)] [output_rknn_path(optional)]".format(sys.argv[0]))
print(" platform choose from [rk3562,rk3566,rk3568,rk3588,rk1808,rv1109,rv1126]")
print(" dtype choose from [i8, fp] for [rk3562,rk3566,rk3568,rk3588]")
print(" dtype choose from [u8, fp] for [rk1808,rv1109,rv1126]")
exit(1)
model_path = sys.argv[1]
platform = sys.argv[2]
do_quant = DEFAULT_QUANT
if len(sys.argv) > 3:
model_type = sys.argv[3]
if model_type not in ['i8', 'u8', 'fp']:
print("ERROR: Invalid model type: {}".format(model_type))
exit(1)
elif model_type in ['i8', 'u8']:
do_quant = True
else:
do_quant = False
if len(sys.argv) > 4:
output_path = sys.argv[4]
else:
output_path = DEFAULT_RKNN_PATH
return model_path, platform, do_quant, output_path
if __name__ == '__main__':
model_path, platform, do_quant, output_path = parse_arg()
# Create RKNN object
rknn = RKNN(verbose=False)
# Pre-process config
print('--> Config model')
rknn.config(mean_values=[[0, 0, 0]], std_values=[
[255, 255, 255]], target_platform=platform)
print('done')
# Load model
print('--> Loading model')
ret = rknn.load_onnx(model=model_path)
if ret != 0:
print('Load model failed!')
exit(ret)
print('done')
# Build model
print('--> Building model')
ret = rknn.build(do_quantization=do_quant, dataset=DATASET_PATH)
if ret != 0:
print('Build model failed!')
exit(ret)
print('done')
# Export rknn model
print('--> Export rknn model')
ret = rknn.export_rknn(output_path)
if ret != 0:
print('Export rknn model failed!')
exit(ret)
print('done')
# Release
rknn.release()
附件:
-
回复了主题帖:
【AI挑战营第二站】算法工程化部署打包成SDK
本帖最后由 和和123 于 2024-5-14 13:24 编辑
1、跟帖回复:什么是ONNX模型、RKNN模型
ONNX是一个开放格式, 用于表示深度学习模型。onnx的主要优势在于它提供了一个跨平台、跨框架的标准,使得不同的深度学习框架能够互相操作。
RKNN,全称Recurrent KernelNeuralNetwork,是一种新型的神经网络模型,具有高效、可解释性强等特点,被广泛应用于自然语言处理、计算机视觉等领域
2、以#AI挑战营第二站#为标题前缀,发帖分享ONNX模型转换成RKNN模型过程,包含环境部署、转换代码、解读代码,并附上RKNN模型。
https://bbs.eeworld.com.cn/thread-1281828-1-1.html
- 2024-05-09
-
回复了主题帖:
入围名单公布:嵌入式工程师AI挑战营(初阶),获RV1106 Linux 板+摄像头的名单
个人信息已确认,领取板卡,可继续完成&分享挑战营第二站和第三站任务。
- 2024-05-01
-
加入了学习《GD32嵌入式开发入门》,观看 搭建GD32F1开发环境详解
-
加入了学习《GD32嵌入式开发入门》,观看 开发板介绍
-
加入了学习《GD32嵌入式开发入门》,观看 ARM发展历程
- 2024-04-26
-
回复了主题帖:
(已颁奖)嵌入式工程师AI挑战营(初阶):基于RV1106,动手部署手写数字识别落地
什么时候公布获取幸狐RV1106开发板名单、开发板派发?
- 2024-04-16
-
回复了主题帖:
免费申请:幸狐 RV1106 Linux 开发板(带摄像头),助力AI挑战营应用落地
本帖最后由 和和123 于 2024-4-16 10:20 编辑
https://bbs.eeworld.com.cn/thread-1277926-1-1.html
预期应用:实现数字识别和车牌识别
-
发表了主题帖:
#AI挑战营第一站#基于pytorch实现mnist手写数字识别
本帖最后由 和和123 于 2024-4-16 10:14 编辑
基础概念:
1. 模型训练的本质是通过大量的数据和算法来学习数据之间的模式和关系,从而使模型能够对新的、未见过的数据做出准确的预测或决策。训练的最终结果是得到一个训练好的模型,它能够对新的输入数据做出准确的预测和分类。
2.pytorch是一个开源的机器学习库,它基于python语言,专为实现深度神经网络而设计。他提供了强大的GPU加速支持,具备动态图机制和自动微分功能,使得模型的开发、调试与优化过程更为高效。
支持的操作系统:windows,macos,linux等
支持的计算平台:cpu,NVIDIA GPU, AMD GPU,TPU等
环境搭建:
debian 12 + vscode + pytorch
pytorch安装:pip install torch torchvision torchaudio
遇到问题:debian使用的为python3.11.X,不支持pip安装
解决方案:使用虚拟python环境,命令如下:
python3 -m venv myenv ------创建自己的python虚拟环境,myenv为自己的虚拟环境名称
source myenv/bin/active ------激活虚拟环境
pip install XXX ------安装需要的python库
deactive ------关闭虚拟环境
源码:
下载测试训练集:
定义训练网络:
开始训练:
测试方法:
测试结果
训练的模型输出:
-
回复了主题帖:
【AI挑战营第一站】模型训练:在PC上完成手写数字模型训练,免费申请RV1106开发板
1. 模型训练的本质是通过大量的数据和算法来学习数据之间的模式和关系,从而使模型能够对新的、未见过的数据做出准确的预测或决策。训练的最终结果是得到一个训练好的模型,它能够对新的输入数据做出准确的预测和分类。
2.pytorch是一个开源的机器学习库,它基于python语言,专为实现深度神经网络而设计。他提供了强大的GPU加速支持,具备动态图机制和自动微分功能,使得模型的开发、调试与优化过程更为高效。
支持的操作系统:windows,macos,linux等
支持的计算平台:cpu,NVIDIA GPU, AMD GPU,TPU等
3.实例链接 https://bbs.eeworld.com.cn/thread-1277926-1-1.html