粘粘字符“花式分割”___loop and guideline
loop and guideline是本系列文章讨论的算法的两个核心,loop principle可以将包含自然loop的区块完全分割,可惜不是每张图片都包含自然loop。对于执行loop principle之后仍粘连的状况将完全交由guideline principle来处理。讲述这两者的合作方式便是本文的中心思想,仍然以TAOBAO四字符白底蓝字的验证码为研究对象。
1.loop and guideline的合作方式
1.1 loop分割完成后,获取已得到的分割路径的数量。
1.2 若路径数量等于5,则分割结束,执行1.6步;若路径数量小于5,那么执行第1.3步。
1.3 根据已得到的路径数量获取路径之间的区块信息,该信息中包含每个区块有效字符像素的数量,每个区块所占据区域的左下角和右下角坐标以及每个区块的二值化数组。
1.4 根据区块数量与相关信息判断每个区块的粘连状况,然后根据guideline特征进行分割。
1.4.1 1个区块,当前仍有四个字符粘连。
1.4.2 2个区块,当前的粘连状况可能为1+3或者2+2。以区块有效字符像素的数量为区分标准,若区块1 > 2*区块2,或者区块2>2*区块1, 则认为区块1或区块2为3字符粘连区域,否则,认为区块1与区块2均为2字符粘连区域。
1.4.3 3个区块,当前的粘连状况为2+1+1。以区域有效字符像素的数量为区分标准来找到仍然粘连的区块:字符像素数比另外两个区块均大的认为是粘连区域。
1.5 得到新的分割路径后,回到第2步判断是否需要继续。
1.6 分割结束,根据分割路径将原图进行分割。
2.粘连区块信息记录
粘连区块信息非常重要,它记录的区块左上角与右下角用于准确定位粘连区块,记录的有效字符数量用于区域分割已完成区域和未完成区域,数组则用于记录当前区块的二值化信息,该数组包含且仅包含当前区块的字符信息,获取粘连区块信息的函数如下。
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
public class SegAreaInfo { //已分割区域包含的有效字符像素的数量 private int _blackPixCount; public int blackPixCount { get { return _blackPixCount; } set { _blackPixCount = value; } } private Boundary _segAreaB; public Boundary segAreaB { get { return _segAreaB; } set { _segAreaB = value; } } private byte[,] _binaryArr; public byte[,] binaryArr { get { return _binaryArr; } set { _binaryArr = value; } } } /// <summary> /// 根据两条分割路径获取已分割路径之间区域的如下信息: /// 1.两个路径之间包含的有效字符的像素点数(黑色像素的数量) /// 2.已分割区域的与baseline的交点 /// 3.已分割区域的与meanine的交点 /// 4.已分割区域的最高点(离原点最远的,X坐标较大值) /// 5.已分割区域的最低点(离原地最低点,X坐标较小值) /// </summary> public static SegAreaInfo GetSegInfo(ArrayList pathLeft, ArrayList pathRight) { //遍历两条路径之间的区域用 Point posRight = new Point(); Point posLeft = new Point(); int widthRight = 0; int widthLeft = 0; int heightRight = 0; int heightLeft = 0; int indexLeft = 0; int indexRight = 0; int posCountLeft = 0; int posCountRight = 0; int bPixCount = 0; SegAreaInfo segArea = new SegAreaInfo(); Boundary tmpBoundary = new Boundary(); //用于存储已分割区域的二值化数组 Byte[,] binaryArr = new Byte[ImageHeight, ImageWidth]; for(int m = 0; m < ImageHeight; m++) { for (int n = 0; n < ImageWidth;n ++ ) { binaryArr[m, n] = 255; } } bPixCount = 0; posCountLeft = pathLeft.Count; posCountRight = pathRight.Count; indexLeft = 0; indexRight = 0; //两条路径之间的点数可能不一致,可能会出现同一个X值对应多个Y值的状况 while ((indexLeft < posCountLeft) && (indexRight < posCountRight)) { posRight = (Point)pathRight[indexRight]; posLeft = (Point)pathLeft[indexLeft]; indexLeft++; indexRight++; widthLeft = posLeft.Y; widthRight = posRight.Y; heightRight = posLeft.X; heightLeft = posRight.X; //路径中可能会出现多个宽度值对应同一高度值的状况,找到同一高度值下最左侧的宽度值 while (indexLeft < posCountLeft) { posLeft = (Point)pathLeft[indexLeft]; if (widthLeft != posLeft.X) { break; } else { if (widthLeft > posLeft.Y) { widthLeft = posLeft.Y; } indexLeft++; } } //路径中可能会出现多个宽度值对应同一高度值的状况,找到同一高度值下最右侧的宽度值 while (indexRight < posCountRight) { posRight = (Point)pathRight[indexRight]; if (heightRight != posRight.X) { break; } else { if (widthRight < posRight.Y) { widthRight = posRight.Y; } indexRight++; } } //从左至右,从上至下,遍历两条路径之间的像素点 for (Int32 y = widthLeft; y < widthRight; y++) { binaryArr[heightLeft, y] = BinaryArray[heightLeft, y]; if (0 == BinaryArray[heightLeft, y]) { //将两条路径之间的有效像素点的总数 bPixCount++; } } } tmpBoundary = Preprocess.Preprocess.getImgBoundary(ImageHeight, ImageWidth, binaryArr); segArea.blackPixCount = bPixCount; segArea.segAreaB = tmpBoundary; segArea.binaryArr = binaryArr; return segArea; } |
粘粘字符“花式分割”___fix broken characters
粘粘字符“花式分割”___loop and guideline
粘粘字符“花式分割”___guideline principle