公司最近来了一批智能锁产品~,
使用NB-IOT移动物联平台接入,我方负责锁产品的后台统一管理,包括控制智能锁密码下发等。
概要
这里需要给锁安装人员提供一个快捷的录入锁IMEI 编号的功能,
鉴于之前调研过图片识别相关的东西,这里顺便做了个锁IMEi图片的识别测试。
锁如下:
识别
输入
需要识别的
就是锁上贴着的白色小纸片,需要识别出拍摄图片上的IMEI码。
如果客户端通过各种手段能直接截取到方形图片的画,识别就是草鸡简单,没什么难度了,当然这个也是后续的真正方案了~
这里我自己扩展了下,也算又练了一把手,
直接识别随便手机拍摄的如上图片,包含高光点、杂乱背景、歪着拍的, 可能还有90度或者180度拍摄等情况。
综合处理了下,最后结果基本还行,不过总的来说最好还是固定样式的好
基本正常拍摄的图片.
歪着拍的图片
处理时,判断分析出的边界矩形长宽,进行旋转操作。
这里只简单处理了接近90度的图片
所以,这里如果要能处理全部的图像,可能还需要对偏移角度更进一步分析。
光照不均匀图片
又有光照又有背景的图片
,这个识别好像还有点问题,不过可以综合其他方法处理,比如数字前缀等.
或者多做几次边界处理,先一步截取最外层背景。
这里可以看出来,图片识别还是要对输入的图片有一个统一标准,不然识别的难度那是无限增加~
不知道那些搞机器人,自动汽车啥的,是咋搞的,真心应该很复杂。
实现
下面说下实现思路,
其实也没啥,定位白色贴条区域,截取,识别。
那么问题来了,怎么定位白色贴条区域?
定位
针对此特定问题,我们可以看到整个图片基本上都是深色居多,白色贴条部位除外。
那么如果我们对图片作水平方向的灰度投影计算,即将整张图片上每一行的灰度像素值水平累计,生成出一张图像,
也就是上面结果中的各个图上右下角那个图形,人眼可以非常明显的看出贴纸的那一块的上下边界。
灰度像素值基本比较平滑,比最大值少一半左右,
这个就是关键了。
算法
算法目标已经基本很明确了,计算出上述直方图中,比较平滑的一段数据的起始y和结束y.
再抽象一点,就是给定一组数据
([0,a0],[1,a1],[2,a2].....[ x,ax]) , 数组数量>1800。
找出其中一段连续数据,记录起始x1,x2. 其中x1-x2的所有对应a值都应该小于最大a值的一半,
并且x1-x2连续波动幅度不大的,即相连两个a值差别不应太大,假定不大于35(灰度像素)同时x2-x1的最小长度有要求,最低x2-x1>200。
即最少要有连续200个点,同时改连续段落位于给定数据中间(不会在接近头部或者尾部)
自己都被自己说糊涂了。。就酱吧。
实现这个算法搞了快一天,果然现在写程序都是机械化的,真正用到算法的地方不多呢,
随便搞点这种算法,脑袋就不好使了(。・`ω´・)
我这里的实现步骤:
得到y投影
循环y,假设当前为段落起点.记录 起始相关值startj, startv。
继续循环,对比前后两个值,及当前值与段落起始值 startv 差距。是否满足,
满足: 记录连续次数times, 继续,
否:是否满足最小连续次数times,
是:找到一段,记录值
否:从新重新计算相关值startj, startv,继续循环.
这样用文字写一遍下来,好像思路更清晰了,果然有些东西还是要写写。
代码实现如下:
Double max = 0D;
// 单纯计算y投影
Mat hist = canyStepOne(input, output, max);
for (int j = 0; j < imgheight; j++) {
double num = hist.get(0, j)[0];
if (max < num)
max = num;
}
// 计算纵向区域
List<Rect> rects = new ArrayList<>();
boolean isstart = false;
double start_val1 = 0;// 前一个y值
double start_val2 = 0;// 后一个y值
int startj = 0; // 确定一行开始的y值
double startv = 0;// 开始行的值
int times = 0;
int maxtimes = 5;// 像素值突然增大为区域开始
boolean isend = false;
for (int j = 0; j < imgheight; j++) {
double num = hist.get(0, j)[0];
start_val1 = start_val2;
start_val2 = num;
if (!isstart) {
// 假设开始
// 找到开始 ,剔除开头/结尾,图片位于图像当中
if (j > 100 && j < imgheight - 100 && start_val1 / start_val2 > 1 && start_val2 < max * 2 / 3) {
isstart = true;
startj = j;
startv = num;
Rect r = new Rect(0, j, imgwidth, 0);
rects.add(r);
}
} else {
if (Math.abs(startv - start_val2) < 35)
times++;// 前后相差不多,计数
else {
// 差距开始变大
// 满足连续一段/同时 数字开始增大
if (times > 100 && start_val2 > start_val1) {
isend = true;
isstart = false;
isend = false;
start_val1 = 0;
// 更新当前找到的行
Rect r = rects.get(rects.size() - 1);
r.height = j - startj;
rects.remove(rects.size() - 1);
rects.add(r);
times = 0;// 归0
} else {
// 重新开始计算
isstart = false;
times = 0;// 相差过大,重新开始
// 移除之前的添加的不满足的区域.
Rect r = rects.get(rects.size() - 1);
if (r.height < 50)
rects.remove(rects.size() - 1);
}
}
}
}
github:
https://github.com/kxjl168/opencv2/tree/master/opencvTest
有兴趣欢迎围观~(= ̄ω ̄=)
本文基于CC BY-NC-ND 4.0 许可协议发布,作者:野生的喵喵。 固定链接: 【智能锁IMEI图片识别】 转载请注明
相关文章: