这次目标是实现指针仪表方向纠正及指针识别。这现实情况下,要做指针仪表识别,因为各种拍摄角度、光线与指针值的不同,通常需要标注大量素材,而且这种标注相对复杂。因此为解决该问题,我使用了迁移学习的方法,下面将详细讲解。
将由三部分讲解整个过程:
- 搭建Django服务,利用ECharts报表生成虚拟仪表图,作为第一阶段的训练素材。
- 编写模型训练与识别代码,并利用虚拟图做预训练,进行方向纠正与指针识别。
- 人手标注现实仪表图,并转换成训练素材,进行迁移学习。
一、创建服务
创建Django服务可参考官方文档(中文),快速入门部分,文档链接:https://docs.djangoproject.com/zh-hans/3.0/
# 创建项目
django-admin startproject ai_server
# 进入项目目录
cd ai_server
# 创建应用
python manage.py startapp ai_api
二、生成图表
创建虚拟仪表图生成页面,通过将生成仪表图提交到服务,由服务保存到本地。
具体参数可查询ECharts官方文档:https://echarts.apache.org/zh/option.html#series-gauge
在ai_api/static/gauge路径下创建generate_image_save.html文件,内容如下:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
</head>
<body>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="main" style="height:400px; width: 400px;"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<!-- ECharts单文件引入 -->
<script src="http://echarts.baidu.com/build/dist/echarts.js"></script>
<script type="text/javascript">
function rgb() {//rgb颜色随机
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
var rgb = '(' + r + ',' + g + ',' + b + ')';
return rgb;
}
function color16() {//十六进制颜色随机
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
var color = '#' + r.toString(16) + g.toString(16) + b.toString(16);
return color;
}
// 路径配置
require.config({
paths: {
echarts: 'http://echarts.baidu.com/build/dist'
}
});
// 使用
require(
[
'echarts',
'echarts/chart/gauge' // 按需加载
],
function (ec) {
// 基于准备好的dom,初始化echarts图表
let myChart = ec.init(document.getElementById('main'));
// 为echarts对象加载数据
// myChart.setOption(option);
let updateFun = () => {
// let startAngle = Math.round(Math.random() * 360)
// let endAngle = startAngle - Math.round(Math.random() * 300) - 30
let startAngle = 180 + Math.round(Math.random() * 90) - 45
let endAngle = 30 + Math.round(Math.random() * 120) - 60
let option = {
animation: false,
// tooltip: {
// formatter: "{a} <br/>{b} : {c}%"
// },
// toolbox: {
// show: true,
// feature: {
// mark: { show: true },
// restore: { show: true },
// saveAsImage: { show: true }
// }
// },
series: [
{
name: '业务指标',
type: 'gauge',
legendHoverLink: false,
splitNumber: Math.round(Math.random() * 10), // 分割段数,默认为5
startAngle: startAngle,
endAngle: endAngle,
axisLine: { // 坐标轴线
lineStyle: { // 属性lineStyle控制线条样式
color: [[0.2, color16()], [0.8, color16()], [1, color16()]],
width: Math.round(Math.random() * 10) + 3
}
},
axisTick: { // 坐标轴小标记
splitNumber: Math.round(Math.random() * 10), // 每份split细分多少段
length: Math.round(Math.random() * 20), // 属性length控制线长
lineStyle: { // 属性lineStyle控制线条样式
color: color16()
}
},
axisLabel: { // 坐标轴文本标签,详见axis.axisLabel
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: color16()
}
},
splitLine: { // 分隔线
show: true, // 默认显示,属性show控制显示与否
length: Math.round(Math.random() * 20) + 25, // 属性length控制线长
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
color: color16()
}
},
pointer: {
length: (Math.round(Math.random() * 40) + 60) + '%',
width: Math.round(Math.random() * 8) + 1,
color: color16()
},
title: {
show: false,
offsetCenter: [0, '-40%'], // x, y,单位px
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
fontWeight: 'bolder'
}
},
detail: {
show: false,
formatter: '{value}%',
textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE
color: color16(),
fontWeight: 'bolder'
}
},
data: [{ value: 50, name: '' }]
}
]
};
option.series[0].data[0].value = (Math.random() * 100).toFixed(2) - 0;
myChart.setOption(option, true);
let img = myChart.getDataURL();
// console.log('图片数据:', img);
axios.post('/ai_api/gauge/gauge_save', {
img_data: img,
value: option.series[0].data[0].value
})
.then(function (response) {
console.log(response);
timeTicket = setTimeout(updateFun, 1);
})
.catch(function (error) {
console.log(error);
});
}
timeTicket = setTimeout(updateFun, 1);
}
);
</script>
</body>
修改ai_server\settings.py文件,在文件最后增加下面代码:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
"ai_api/static",
]
DATA_UPLOAD_MAX_MEMORY_SIZE = 52428800 #默认设置为50M
创建视图文件,ai_api\views\gauge.py,将图片保存到本地,代码如下:
def gauge_save(request):
'''保存训练图片'''
global save_num
save_num += 0.000001
request_data = json.loads(request.body)
# print('request_data:', request_data)
img_data = request_data['img_data'].split(',')[1]
img_data = image_helpler.base64ToBytes(img_data)
img = image_helpler.bytesToOpenCVImage(img_data)
value = request_data['value']
img_name = ('%s_%.2f.jpg') % (str(save_num), value)
path = "./image_data/" + img_name
image_helpler.openCVImageToFile(path, img)
jsonObj = {
"value": value,
}
return HttpResponse(json.dumps(jsonObj), content_type="application/json")
增加接口路由,实际路由地址为:/ai_api/gauge/gauge_save
增加路由方法请参考Django文档。
最后用浏览器打开:http://127.0.0.1:8000/static/generate_image_save.html,页面会自动生成图片到文件夹。推荐生成20000以上文件用于作为下一节训练素材。
效果图
下一节,将会讲解如何建立模型,进行方向纠正与指针识别。源码会在第三节发布,敬请关注!