【57行代码搞定8600万美元项目】用开源工具DIY车牌识别系统
【新智元导读】作者Tiait Brown 用57行代码和开源工具 DIY 了一个车牌自动识别系统,基本实现了澳大利亚政府投资8600万美元想要的效果。
(文/Tiait Brown)维多利亚警察局是澳大利亚维多利亚州的主要执法机构。过去一年,维多利亚州共有超过1.6万辆车被盗,费用约为1.7亿美元,警方正在试验各种技术驱动的解决方案来打击汽车盗窃。
为了防止盗用车辆的欺诈性销售,管理部门VicRoads启用了一项基于网络的服务来检查车辆注册状况。VicRoads还投资购买了一个固定牌照扫描仪——一个固定的三脚架摄像机,可以扫描过往车辆,自动识别被盗车辆。
不要问我为什么,但有天下午,我突然想要制作一个车载车牌扫描仪,如果有车辆被盗或被未注册,这个扫描仪将自动通知你。我知道有一些单独的组件存在,于是想知道将它们组装在一起会有多难。
但是,谷歌搜索一番后我发现,维多利亚警察局已经就投资类似装置进行了问询,估计的费用在8600万美元左右。 一位精明的评论员指出,这个系统要配置在220辆车上,由于成本总共8600万美元,相当于每辆车支出390,909美元。
当然,我们能比这做得更好。
现有的车牌识别系统(白车+摄像头)
成功方案
在开始之前,我简要介绍了产品设计的几个关键要求。
1. 图像处理必须在本地处理 将直播视频流传给中央处理器似乎是解决这个问题最没有效率的方法。除了数据流量带来的巨额帐单,还会产生网络延迟问题,何况速度可能原本就不高。虽然集中式的机器学习算法随着时间的推移会变得更加准确,但我想知道使用本地的设备实现会不会“就够了”。
2. 必须能够处理低质量的图像 因为我没有Raspberry Pi摄像头或USB网络摄像头,所以我使用 dashcam 镜头——随时可用,而且是个理想的样本数据源。此外,dashcam视频也代表了从车载摄像头能够获得的视频的整体质量水平。
3. 需要使用开源技术搭建 不可能使用有专利的技术,否则每次都会收费。使用开源技术是没的说的选择。
解决方案
从一个高的层面看,我的解决方案从一个 dashcam 视频中获取图像,通过一个安装在本地设备上的开源车牌识别系统,请求注册检查服务,然后返回结果,进行显示。
返回到安装在执法车辆上的设备的数据,包括车辆制造商和型号(只有被盗时才会进行验证)、登记状态和车辆报告被盗时的通知。
如果你觉得听起来很简单,那是因为这确实很简单。例如,图像处理都可以由openalpr库处理。涉及到识别车牌上的字符,你需要做的真的就是这样了:
openalpr.IdentifyLicense(imagePath, function (error, output) {
// handle result
});
一个小小的警告
VicRoads API 公开访问不可用,因此我这款原型的牌照检测需要从网上扒下来再做。虽然一般人都不太认可这个行为,但我这只是一个概念的证明,我也没攻击任何人的服务器。
以下是我的概念验证:
看不清?没关系,可以再来一次(因为真的不长):
// Open form and submit enquire for `rego`
function getInfo(rego) {
horseman
.userAgent('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0')
.open(url)
.type('#registration-number-ctrl input[type=text]', rego)
.click('.btn-holder input')
.waitForSelector('.ctrl-holder.ctrl-readonly')
.html()
.then(function(body) {
console.log(processInfo(body, rego));
return horseman.close();
});
}
// Scrape the results for key info
function processInfo(html, rego) {
var $ = cheerio.load(html);
var vehicle = $('label.label').filter(function() {
return $(this).text().trim() === 'Vehicle:';
}).next().text().trim();
var stolen = $('label.label').filter(function() {
return $(this).text().trim() === 'Stolen status:';
}).next().text().trim();
var registration = $('label.label').filter(function() {
return $(this).text().trim() === 'Registration status & expiry date:';
}).next().text().trim();
return {
rego,
vehicle,
stolen,
registration
};
}
结果
我必须说结果是个惊喜。
我预计开源车牌识别相当垃圾。此外,图像识别算法可能没有针对澳大利亚牌照进行优化。
结果,我的解决方案能够在各种视野中识别车牌。
当然,个别字符识别起来还是有问题。
但是……算法最后还是成功了。
正如你在上述两幅图像中看到的那样,几秒钟后的图像处理从87%的置信度评分上升到超过91%。
我相信准确性可以通过提高采样率来提高,然后按最高置信度排序。或者,可以调整阈值,在置信度超过90%的情况下才接受验证注册号。
这些都是很直接的 code-first 修复,并且不排除使用本地数据集训练车牌识别软件。
一个 8600万美元的问题
公平起见,我完全不知道8600万美元这个数字都包含了些什么,也不知道本地化训练的开源工具的准确性跟 pilot BlueNet 系统相比会有什么结果。
我预计这一预算里包括更新几个遗留数据库和软件应用程序,支持每秒对多辆车进行高速、低延迟的车牌查询。
另一方面,每辆车的识别成本在 39.1万美元左右看上去真的挺贵——特别是如果BlueNet的识别结果不是特别准确,而且没有大规模的IT项目停产或升级相关系统。
未来应用
虽然在想法上很容易陷入乔治奥威尔式的“总是看着你”,但这种技术还时有很多积极的应用。试想,一个被动识别摩托车的系统,在扫描到绑匪后自动通报当局和家属绑匪目前的位置和方向。
特斯拉斯车辆已经有摄像头和传感器,能够接收OTA更新——想象一下把这些车都变成虚拟的绑匪监测器(samaritans)会怎样。Uber 和 Lyft 司机也可以配备这些设备,大大增加覆盖面积。
总之,使用开源技术和现有组件,做出一个回报率更高的解决方案是有可能的——而投资远远低于8600万美元。
编译来源:https://medium.freecodecamp.org/how-i-replicated-an-86-million-project-in-57-lines-of-code-277031330ee9
- 神经网络-感知器
- NSNotificationCenter 通知的使用方法详解你要知道的KVC、KVO、Delegate、Notification都在这里
- Protocol与Delegate 使用方法详解你要知道的KVC、KVO、Delegate、Notification都在这里
- iOS多线程——你要知道的GCD都在这里你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
- NSCopying和NSCoding对象序列化反序列化基础详解你要知道的NSCopying、NSCoding协议及对象序列化和反序列化都在这里
- KVO 正确使用姿势进阶及底层实现你要知道的KVC、KVO、Delegate、Notification都在这里
- iOS多线程——你要知道的NSOperation都在这里你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
- iOS多线程——你要知道的NSThread都在这里你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里
- KVO、Delegate、Notification 区别及相关使用场景你要知道的KVC、KVO、Delegate、Notification都在这里
- Core ML简介及实时目标检测及Caffe TensorFlow coremltools模型转换
- iOS网络——NSURLSession详解及SDWebImage源码解析你要知道的NSURLSession都在这里
- BZOJ3585: mex(主席树)
- 01.LoT.UI 前后台通用框架分解系列之——小图片背景全屏显示(可自动切换背景)
- 02.LoT.UI 前后台通用框架分解系列之——灵活的菜单栏
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何使用OpenCV在Python中访问IP摄像头
- 分享我学习Pandas使用的资料,可能是新手入门Pandas最好的教程!
- 原创 | Git入门教程,详解Git文件的四大状态
- 原创 | ACMer不得不会的线段树,究竟是种怎样的数据结构?
- dplyr包summarize的使用
- Mysql的binlog和relay-log到底长啥样?
- 原创 | matplotlib绘图教程,设置标签与图例
- 原创 | 从抽象类开始,详解责任链模式
- 开发一个渐进式Web应用程序(PWA)前都需要了解什么?
- 知识图谱入门(二)
- 常见编程模式之循环排序
- Python中浅拷贝与深拷贝的骚操作
- 数据概览神器—Pandas-profiling
- 推荐一款MD神器吧:重度MD用户必备神器
- 鸿蒙系统开源