识别快递单号(1) - 图像处理

前言: 

   最近一个项目要发快递, 发完以后需要把单号录入后台. 暂时想到两方案:
  1. 手机拍照上传服务器, 服务器识别. (优点 不用开发手机端; 缺点 费流量, 虽然手机画质调最小一张照片也就100k)
  2. 做手机混合App, 手机端识别. (优点 省流量, 更便捷; 缺点 仅会一点Android, 苹果完全没玩过)

Anyway, 先把图像识别做出来, 其他都好说. 因为各个快递公司条码编码方式不同, 图像识别也有两种方案:
 1. 识别条码. (优点 识别简单; 缺点 需要学习多种编码方式)
 2. 识别条码下方数字单号. (优点 直接; 缺点 需要进行机器学习, 不过应该不难, 毕竟都是打印的数字)

大概流程是这样的: a. 获得照片(一般是快递单条码的特写), b. 找到识别部分(条码或单号), c. 识别.
ac暂且不说, 先做做b.

这个系列是我一边做一边写的, 所以并不能保证写的步骤, 代码就一定没问题没Bug, 有些东西可能在以后的博文中会有所调整.

  转载请声明出处: http://www.cnblogs.com/zaiyuzhong/p/purify-photo.html

正文:

一. 图像二值化
  我首先想到的就是这个, 因为条码和单号都是黑色, 快递单又是多层的, 下层的单号很容易就透上来了.
  1. 用RGB平均值将图片灰度化; (与人眼无关不需要用加权平均, RGB最大值得出图像偏亮)
  2. 因为拍照环境的光线问题, 使用的OTSU算法不能很有效的去除手机遮挡光源产生的阴影, 所以我没有取1/2点作为阀值.
  测试几张图片后发现1/2.1~1/2.5之间效果较好, 于是取1/2.3, 这个需要再多一些图片做测试, 但是我准备换个算法.
原图 1/2灰度点阀值 1/3灰度点阀值
          (白色块是后补的, 位置稍微不同别介意, 绿色边框是CSS)

  测试了一些算法发现Yen算法效果最好(测试图片有限, 没有代表性), 如下图:
Yen法效果
  具体算法见参考资料.

二. 去除多余图像
  这一步我也不确定应该做什么, 首先想到的是做闭操作(将黑色像素周边白色像素染黑, 然后再将白色像素周边染白, 这样能连通邻近的黑色像素)将条码连成一片, 再找最大的黑块. 后来发现所有快递单条码下方都是填写信息的表格, 而人工拍照时条码都在图片中央, 四周空白, 于是考虑先将表格去除.
  我的做法是先在水平方向上找到超过一定长度的线, 然后将该线第一个像素竖直方向有黑色点的...... 好复杂, 一会直接上代码吧, 先看效果.
去除多余图像
 1 private static void RemoveTable(Bitmap img)
 2 {
 3     var thrY = LongLineY(img);
 4     for (int i = 0; i < img.Height; i++)
 5     {
 6         var r = img.GetPixel(0, i).R;
 7         if (r == 0)
 8         {
 9             if (r > thrY)
10                 for (int x = 0; x < img.Width; x++)
11                     for (int y = 0; y < thrY; y++)
12                         img.SetPixel(x, y, Color.White);
13             else
14                 for (int x = 0; x < img.Width; x++)
15                     for (int y = img.Height - 1; y >= thrY; y--)
16                         img.SetPixel(x, y, Color.White);
17         }
18     }
19 }
20 
21 // 找到一条长线的Y坐标
22 private static int LongLineY(Bitmap img)
23 {
24     int thr = img.Width / 5;
25     for (int y = 0; y < img.Height; y++)
26     {
27         int black = 0, white = 0;
28         for (int x = 0; x < img.Width; x++)
29         {
30             var r = img.GetPixel(x, y).R;
31             if (r == 0) { black++; white = 0; }
32             else if (r == 255) { white++; black = 0; }
33             if (white > thr) break;
34             if (black > thr) return y;
35         }
36     }
37     return img.Height;
38 }
嗯~ 就是找到一条比较长的线(我这里定义的是超过宽度的五分之一), 看它里上边近还是下边近, 那边近删掉那边.

三. 确定二维码位置
  我想在做这步之前把多余的白色像素去掉这步可以在上面的过程中处理.

突然发现有个问题需要考虑一下, 请看下篇: 加载图片到canvas

参考资料: 十三种基于直方图的图像全局二值化算法原理、实现、代码及效果

本系列目录: 1. 图像处理

         2. 加载图片到canvas

                3. 

posted @ 2015-02-13 16:23  在风里  阅读(6872)  评论(0编辑  收藏  举报