2015年5月21日
C#实现验证码中粘连字符分割(六)
《C#实现验证码中粘连字符分割》发布了一系列文章,上一篇我们讲到了谷点的过滤。谷点过滤完成后,我们就可以根据最终确定的谷点来执行滴水算法,从而得到最终的分割路径。含四个字符的验证码图片可通过滴水算法得到3条分割路径,为方便程序统一处理,左侧与右侧边界会作为两条分割路径存储在路径集中。这样对于四个字符的图片而言,就有五条分割路径,每两条路径之间得到一个字符。
写文章时贴代码可能有所遗漏,这里提供完整的工程下载链接:链接: http://pan.baidu.com/s/1sjJoWKt 密码: xdeb
粘连字符分割原则:
(1)分割后的图片大小与原图片一致,分割后的字符将从宽度上均分该图片,高度值遵循原字符所在高度。
(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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
/// <summary> ///根据谷点获取图片的分割路径 /// </summary> /// <param name="valleyCollection">谷点列表</param> /// <param name="Boundary">图片中字符所在区域的边界</param> /// <param name="BinaryArray">二值化图片数组</param> /// <returns>返回粘粘图片的分割路径</returns> public static ArrayList getSegmentPath(ImgValley valleyCollection, ImgBoundary Boundary, Byte[,] BinaryArray) { ArrayList segPathList = new ArrayList(); int yIndex2 = Boundary.widthMax; int yIndex1 = Boundary.widthMin; int xIndex2 = Boundary.heightMax; int xIndex1 = Boundary.heightMin; int valleyNum = valleyCollection.upValley.Count; //将图像最左侧的点所在直线作为第一条分割路径 // int validHeight = xIndex2 - xIndex1; ArrayList pathList0 = new ArrayList(); for (int a = xIndex1; a < xIndex2; a++) { PixPos newPos = new PixPos(); newPos.widthPos = yIndex1; newPos.heightPos = a; pathList0.Add(newPos); } segPathList.Add(pathList0); for (int i = 0; i < valleyNum; i ++) { PixPos pos = (PixPos)valleyCollection.upValley[i]; ArrayList pathList = new ArrayList(); int iniPointY = pos.widthPos; int iniPointX = xIndex1;//任一分割路径的高度起始点均为有效字符出现的第一个点 byte leftRightFlg = 0;//该标志位置位时,表示情况5与情况6可能会循环出现 while ((iniPointX < xIndex2) && (iniPointY < yIndex2)) { Byte pointLeft = BinaryArray[iniPointX, (iniPointY - 1)]; Byte pointRight = BinaryArray[iniPointX, (iniPointY + 1)]; Byte pointDown = BinaryArray[(iniPointX + 1), iniPointY]; Byte pointDownLeft = BinaryArray[(iniPointX + 1), (iniPointY - 1)]; Byte pointDownRight = BinaryArray[(iniPointX + 1), (iniPointY + 1)]; PixPos newPos = new PixPos(); newPos.widthPos = iniPointY; newPos.heightPos = iniPointX; pathList.Add(newPos); if (((0 == pointLeft) && (0 == pointRight) && (0 == pointDown) && (0 == pointDownLeft) && ((0 == pointDownRight)))// all black || ((255 == pointLeft) && (255 == pointRight) && (255 == pointDown) && (255 == pointDownLeft) && ((255 == pointDownRight))//all white || ((0 == pointDownLeft) && (255 == pointDown))))//情况1与情况3 {//down iniPointX = iniPointX + 1; leftRightFlg = 0; } else if ((0 == pointLeft) && (0 == pointRight) && (0 == pointDown) && (255 == pointDownLeft) && (0 == pointDownRight)) {//left down //情况2 iniPointX = iniPointX + 1; iniPointY = iniPointY - 1; leftRightFlg = 0; } else if ((0 == pointDown) && (0 == pointDownLeft) && (255 == pointDownRight)) {//情况4 iniPointX = iniPointX + 1; iniPointY = iniPointY + 1; leftRightFlg = 0; } else if ((255 == pointRight) && (0 == pointDown) && (0 == pointDownLeft) && (0 == pointDownRight)) {//情况5 if (0 == leftRightFlg) { iniPointY = iniPointY + 1; leftRightFlg = 1; } else//标志位为1时说明上一个点出现在情况6,而本次循环的点出现在情况5,此种情况将垂直渗透 { iniPointX = iniPointX + 1; leftRightFlg = 0; } } else if ((255 == pointLeft) && (0 == pointDown) && (0 == pointDownLeft) && (0 == pointDownRight)) {//情况6 if (0 == leftRightFlg) { iniPointY = iniPointY - 1; leftRightFlg = 1; } else//标志位为1时说明上一个点出现在情况5,而本次循环的点出现在情况6,此种情况将垂直渗透 { iniPointX = iniPointX + 1; leftRightFlg = 0; } } else { iniPointX = iniPointX + 1; } } segPathList.Add(pathList); } //将图像最右侧的点所在直线作为最后一条分割路径 ArrayList pathListLast = new ArrayList(); for (int a = xIndex1; a < xIndex2; a++) { PixPos newPos = new PixPos(); newPos.widthPos = yIndex2; newPos.heightPos = a; pathListLast.Add(newPos); } segPathList.Add(pathListLast); return segPathList; } /// <summary> ///根据分割路径分割粘粘字符并另存为位图 /// </summary> /// <param name="valleyCollection">谷点列表</param> /// <param name="Boundary">图片中字符所在区域的边界</param> /// <param name="BinaryArray">二值化图片数组</param> /// <returns>返回粘粘图片的分割路径</returns> public static Bitmap getDivideImg(Byte[,] BinaryArray, ArrayList SegPathList) { int imageHeight = BinaryArray.GetLength(0); int imageWidth = BinaryArray.GetLength(1); int segCount = SegPathList.Count; int locationVal = imageWidth / (segCount - 1); int locationPos = 0; Byte[,] divideArray = new Byte[imageHeight, imageWidth]; for (Int32 x = 0; x < imageHeight; x++) { for (Int32 y = 0; y < imageWidth; y++) { divideArray[x, y] = 255; } } PixPos divPosRight = new PixPos(); PixPos divPosLeft = new PixPos(); ArrayList pathListLeft = new ArrayList(); ArrayList pathListRight = new ArrayList(); int indexWidthRight = 0; int indexWidthLeft = 0; int indexHeightRight = 0; int indexHeightLeft = 0; int pointIndexLeft = 0; int pointIndexRight = 0; int posCountLeft = 0; int posCountRight = 0; for (int i = 0; i < segCount - 1; i++) { //每条分割路径的起始点与终点均相同,且不会出现同一个高度值对应两个宽度值的状况,应此每条路径的点数均相同 pathListLeft = (ArrayList)SegPathList[i]; pathListRight = (ArrayList)SegPathList[i + 1]; posCountLeft = pathListLeft.Count; posCountRight = pathListRight.Count; /* int posCount = 0; if (posCountLeft < posCountRight) { posCount = posCountLeft; } else { posCount = posCountRight; } */ locationPos = 5 + locationVal * i; pointIndexLeft = 0; pointIndexRight = 0; //目前所用的滴水算法下,同一高度值最多同时对应两个宽度值 while ((pointIndexLeft < posCountLeft) && (pointIndexRight < posCountRight)) { divPosRight = (PixPos)pathListRight[pointIndexRight]; divPosLeft = (PixPos)pathListLeft[pointIndexLeft]; pointIndexLeft++; pointIndexRight++; indexWidthLeft = divPosLeft.widthPos; indexWidthRight = divPosRight.widthPos; indexHeightRight = divPosLeft.heightPos; indexHeightLeft = divPosRight.heightPos; if(pointIndexLeft < posCountLeft) { divPosLeft = (PixPos)pathListLeft[pointIndexLeft]; if (indexHeightLeft == divPosLeft.heightPos)//若下一点高度值相同,索引值加1,宽度值更新 { pointIndexLeft++; indexWidthLeft = divPosLeft.widthPos; } } if (pointIndexRight < posCountRight) { divPosRight = (PixPos)pathListRight[pointIndexRight]; if (indexHeightRight == divPosRight.heightPos)//若下一点高度值相同,索引值加1,宽度值更新 { pointIndexRight++; indexWidthRight = divPosRight.widthPos; } } for (Int32 y = indexWidthLeft; y < indexWidthRight; y++) { divideArray[indexHeightRight, (y - indexWidthLeft + locationPos)] = BinaryArray[indexHeightRight, y]; } } } Bitmap GrayBmp = ImageBinarization.ImageUtils.BinaryArrayToBinaryBitmap(divideArray); return GrayBmp; } |