2015年5月18日
C#实现验证码中粘连字符分割(三)
以验证码图片为例,一般情况下我们关注的字符部分可能只占有原始图片的1/2甚至更少。而粘连字符分割算法中,有多个步骤需要遍历字符像素点,因此在开始执行分割算法之前,需要先统计字符部分的范围,后续遍历动作将在此范围内执行。
写文章时贴代码可能有所遗漏,这里提供完整的工程下载链接:链接: http://pan.baidu.com/s/1sjJoWKt 密码: xdeb
如下图所示,我定义了一个名为ImgBoundary的类来记录字符的范围,该类包含字符左上角和右下角的坐标值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/// <summary> /// 通常我们关注的字符并不会填满整张图片,找到我们所关注字符的边界并将其记录。 /// </summary> public class ImgBoundary { private int _heightMax = 0; public int heightMax { get { return _heightMax; } set { _heightMax = value; } } private int _heightMin = 0; public int heightMin { get { return _heightMin; } set { _heightMin = value; } } private int _widthMax = 0; public int widthMax { get { return _widthMax; } set { _widthMax = value; } } private int _widthMin = 0; public int widthMin { get { return _widthMin; } set { _widthMin = value; } } } |
查找字符边界均在二值化图像中执行,方法为:
(1)外层循环从原点开始沿宽度方向至图像右侧,内层循环从原点开始沿高度方向至图像下方,遇到的第一个黑色点为左侧极限点;
(2)外层循环从图像右侧沿宽度方向到原点,内层循环从原点开始沿高度方向到图像下方,遇到的第一个黑色像素点为右侧极限点;
(3)外层循环从图像下方开始沿高度方向到原点,内层循环从原点开始沿宽度方向至图像右侧,遇到的第一个黑色像素点为下方极限点;
(4)外层循环从原点开始沿高度方向至图像下方,内层循环从原点开始沿宽度方向至图像右侧,遇到的第一个黑色像素点为上方极限点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
/// <summary> ///获取字符所在区域的边界 /// </summary> /// <param name="BinaryArray">二值化图片数组</param> /// <returns>返回字符所在区域的边界</returns> public static ImgBoundary getImgBoundary(Byte[,] BinaryArray) { ImgBoundary boundary = new ImgBoundary(); int imageHeight = BinaryArray.GetLength(0); int imageWidth = BinaryArray.GetLength(1); //记录图像出现的左右极限位置 int widthLeft = 0, widthRight = 0; //记录图像出现的上下极限位置 int heightUp = 0, heightDown = 0; byte loopFlg = 0;//当该变量置为1时将跳出双重循环 //外层循环从原点开始沿宽度方向值图像右侧 for (int x = 0; (x < imageWidth) && (0 == loopFlg); x++) { //内层循环从原点开始沿高度方向值图像下方 for (int y = 0; (y < imageHeight) && (0 == loopFlg); y++) { //遇到的第一个点为最接近原点宽度方向上的第一个点,左侧极限点 if (0 == BinaryArray[y, x]) { widthLeft = x; loopFlg = 1; } } } loopFlg = 0;//当该变量置为零时将跳出双重循环 //外层循环从图像右侧沿宽度方向到原点 for (int x = (imageWidth - 1); (x > 0) && (0 == loopFlg); x--) { //内层循环从原点开始沿高度方向到图像下方 for (int y = 0; (y < imageHeight) && (0 == loopFlg); y++) { //遇到的第一个点离原点最原的右侧极限点 if (0 == BinaryArray[y, x]) { widthRight = x; loopFlg = 1; } } } loopFlg = 0;//当该变量置为零时将跳出双重循环 //外层循环从图像下方开始向上接近原点方向,高度方向 for (int x = (imageHeight - 1); (x > 0) && (0 == loopFlg); x--) { //内层循环从原点开始,沿宽度方向 for (int y = 0; (y < imageWidth) && (0 == loopFlg); y++) { //遇到的第一个点为高度方向离原点最远的点,下方极限点 if (0 == BinaryArray[x, y]) { heightDown = x; loopFlg = 1; } } } loopFlg = 0;//当该变量置为零时将跳出双重循环 //外层循环从原点开始沿高度方向至图像下方 for (int x = 0; (x < imageHeight) && (0 == loopFlg); x++) { //内层循环从原点开始沿宽度方向至图像右侧 for (int y = 0; (y < imageWidth) && (0 == loopFlg); y++) { if (0 == BinaryArray[x, y])//遇到的第一个点为高度方向离原点最远的点,上方极限点 { heightUp = x; loopFlg = 1; } } } boundary.widthMin = widthLeft; boundary.widthMax = widthRight; boundary.heightMin = heightUp; boundary.heightMax = heightDown; return boundary; } } |