/// <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; }
/// <summary> ///根据粘连区域与Guideline的关系对粘连区域进行分割 /// </summary> /// <param name="mergedArea">粘连区域的信息</param> /// <param name="newPos">此次分割得到的路径在总路径中的位置</param> /// <param name="num">当前粘连区域包含的字符数</param> public static void SegByGuideLine(SegAreaInfo mergedArea, int newPos, int num) { //循环用变量 int k = 0; //SegAreaInfo Boundary mBoundary = mergedArea.segAreaB; //当前粘连区域粘连字符宽度判定阈值 int thVal = (mBoundary.rightUpPoint.Y - mBoundary.leftDownPoint.Y) / (num + 1); int bThVal = mergedArea.blackPixCount / (num + 2); Point segP = new Point(); ArrayList pathListA = new ArrayList(); ArrayList pathListB = new ArrayList(); //用于标注占据三格的字符根据meanline分割的路径是否为有效路径 bool bValid = false; //获取当前粘连区域在meanline之上区域的竖直投影 // Point point1 = new Point(); //获取meanline以上区域竖直投影的右上角的点 segP.X = GuidePoint.Y; segP.Y = mBoundary.rightUpPoint.Y; VerPro meanArr = Preprocess.Preprocess.VerticalProjection(mBoundary.leftDownPoint, segP, 2, mergedArea.binaryArr); int mCount = meanArr.vRange.Count; segP.X = GuidePoint.X; segP.Y = mBoundary.leftDownPoint.Y; VerPro baseArr = Preprocess.Preprocess.VerticalProjection(segP, mBoundary.rightUpPoint, 1, mergedArea.binaryArr); int bCount = baseArr.vRange.Count; segP.X = GuidePoint.Y; segP.Y = mBoundary.leftDownPoint.Y; Point tmpPoint = new Point(); tmpPoint.X = GuidePoint.X; tmpPoint.Y = mBoundary.rightUpPoint.Y; VerPro midArr = Preprocess.Preprocess.VerticalProjection(segP, tmpPoint, 0, mergedArea.binaryArr); int midCount = midArr.vRange.Count; //以GuideLine作为分割依据主要是粘连区域占据四线三格中格数的状况 if ((mCount > 0) && (bCount > 0)) { //1.若占据三格,则meanline与粘连区域的左侧交点若不位于边界线,则将其作为一个分割点进行分割 if (meanArr.vInter.X > (mBoundary.leftDownPoint.Y + thVal)) { segP.X = GuidePoint.Y; segP.Y = meanArr.vInter.X; bValid = SegByMeanLeft(segP, meanArr, mBoundary, bThVal, newPos); } //若meanline区域分割无效,或者meanline无有效分割点,将以baseline为分割依据 //取baseline与字符在左侧的第一个交点,若该交点不位于边界,则以该点为分割点 //若该点位于边界,则以baseline和字符在右侧的第一个交点为分割点 if ((!bValid) && (baseArr.vInter.X > (mBoundary.leftDownPoint.Y + thVal))) { segP.X = GuidePoint.X; segP.Y = baseArr.vInter.X; bValid = SegByBaseLine(segP, meanArr, mBoundary, bThVal, newPos, true); } //若baseline与meanline的左侧交点均未得到有效分割线,则以baseline和meanline的右侧交点来进行分割 //以meanline的右侧交点为分割点 if ((!bValid) && (meanArr.vInter.Y < (mBoundary.rightUpPoint.Y - thVal))) { segP.X = GuidePoint.Y; segP.Y = meanArr.vInter.Y; bValid = SegByMeanRight(segP, meanArr, mBoundary, bThVal, newPos); } //以baseline的右侧交点为分割点 if ((!bValid) && (baseArr.vInter.Y < (mBoundary.leftDownPoint.Y - thVal))) { segP.X = GuidePoint.X; segP.Y = baseArr.vInter.Y; bValid = SegByBaseLine(segP, meanArr, mBoundary, bThVal, newPos, false); } //理论上而言,经过上述几项分割后,应该会得到一条有效路径,若仍未得到有效路径,将考虑用投影特征来分割,目前暂不处理此状况 } else if ((mCount > 0) && (0 == bCount)) { //目前粘连区域位于baseline以上的两个格 //1.若meanline以上区域位于右侧区域,则以meanline与字符的左侧交点为分割点 if (meanArr.vInter.X > (mBoundary.leftDownPoint.Y + thVal)) { segP.X = GuidePoint.Y; segP.Y = meanArr.vInter.X; bValid = SegByMeanLeft(segP, meanArr, mBoundary, bThVal, newPos); } //2.若meanline以上区域位于左侧区域,则以meanline与字符的右侧交点为分割点 if ((!bValid) && (meanArr.vInter.Y < (mBoundary.rightUpPoint.Y - thVal))) { segP.X = GuidePoint.Y; segP.Y = meanArr.vInter.Y; bValid = SegByMeanRight(segP, meanArr, mBoundary, bThVal, newPos); } //3.若上述两个条件均不成立,则以竖直投影为分割依据 thVal = (mBoundary.rightUpPoint.Y + mBoundary.leftDownPoint.Y) / 2; //3.1 meanline以上区域粘连,以下区域分割,则以其分割区域靠近中间的投影点作为分割点 if (!bValid) { if ((1 == mCount) && (midCount > 1)) { segP = (Point)midArr.vRange[0]; tmpPoint = (Point)midArr.vRange[1]; if (Math.Abs(thVal - segP.Y) < Math.Abs(thVal - tmpPoint.X)) { Point pt1 = new Point(); pt1.X = GuidePoint.Y; pt1.Y = segP.Y; pathListA = dropFallUp(pt1); pathListB = dropFallDown(pt1); } else { Point pt1 = new Point(); pt1.X = GuidePoint.Y; pt1.Y = tmpPoint.X; pathListA = dropFallUp(pt1); pathListB = dropFallDown(pt1); } } else if ((1 == midCount) && (mCount > 1)) { //3.2 meanline以上区域分割,以下区域粘连,则以其分割区域靠近中间的投影点作为分割点 segP = (Point)meanArr.vRange[0]; tmpPoint = (Point)meanArr.vRange[1]; if (Math.Abs(thVal - segP.Y) < Math.Abs(thVal - tmpPoint.X)) { Point pt1 = new Point(); pt1.X = GuidePoint.Y; pt1.Y = segP.Y; pathListA = dropFallUp(pt1); pathListB = dropFallDown(pt1); } else { Point pt1 = new Point(); pt1.X = GuidePoint.Y; pt1.Y = tmpPoint.X; pathListA = dropFallUp(pt1); pathListB = dropFallDown(pt1); } } else { //3.3 上下均粘连,则暂不处理 } if ((pathListA.Count > 0) && (pathListB.Count > 0)) { for (k = 0; k < pathListA.Count; k++) { pathListB.Add(pathListA[k]); } SegPathList.Insert(newPos, pathListB); bValid = true; } } } else if ((0 == mCount) && (bCount > 0)) { //目前粘连区域位于meanline以下的两个格 //若baseline以下区域位于粘连区域的右侧,则以baseline和字符在左侧的交点为分割点 if ((!bValid) && (baseArr.vInter.X > (mBoundary.leftDownPoint.Y + thVal))) { segP.X = GuidePoint.X; segP.Y = baseArr.vInter.X; bValid = SegByBaseLine(segP, meanArr, mBoundary, bThVal, newPos, true); } //若baseline以下区域位于粘连区域的左侧,则以baseline和字符在右侧的交点为分割点 if ((!bValid) && (baseArr.vInter.Y < (mBoundary.rightUpPoint.Y - thVal))) { segP.X = GuidePoint.X; segP.Y = baseArr.vInter.Y; bValid = SegByBaseLine(segP, meanArr, mBoundary, bThVal, newPos, false); } //3.若上述两个条件均不成立,则以竖直投影为分割依据 if (!bValid) { //3.1 baseline以上区域粘连,以下区域分割,则以其分割区域靠近中间的投影点作为分割点 //3.2 baseline以上区域分割,以下区域粘连,则以其分割区域靠近中间的投影点作为分割点 //3.3 上下均粘连,则暂不处理 //3.若上述两个条件均不成立,则以竖直投影为分割依据 thVal = (mBoundary.rightUpPoint.Y + mBoundary.leftDownPoint.Y) / 2; //3.1 meanline以上区域粘连,以下区域分割,则以其分割区域靠近中间的投影点作为分割点 if (!bValid) { if ((1 == bCount) && (midCount > 1)) { segP = (Point)midArr.vRange[0]; tmpPoint = (Point)midArr.vRange[1]; if (Math.Abs(thVal - segP.Y) < Math.Abs(thVal - tmpPoint.X)) { segP.X = GuidePoint.X; pathListA = dropFallUp(segP); pathListB = dropFallDown(segP); } else { tmpPoint.X = GuidePoint.X; pathListA = dropFallUp(tmpPoint); pathListB = dropFallDown(tmpPoint); } } else if ((1 == midCount) && (bCount > 1)) { //3.2 meanline以上区域分割,以下区域粘连,则以其分割区域靠近中间的投影点作为分割点 segP = (Point)baseArr.vRange[0]; tmpPoint = (Point)baseArr.vRange[1]; if (Math.Abs(thVal - segP.Y) < Math.Abs(thVal - tmpPoint.X)) { pathListA = dropFallUp(segP); pathListB = dropFallDown(segP); } else { pathListA = dropFallUp(tmpPoint); pathListB = dropFallDown(tmpPoint); } } else { //3.3 上下均粘连,则暂不处理 } for (k = 0; k < pathListA.Count; k++) { pathListB.Add(pathListA[k]); } // pathListA = (ArrayList)SegPathList[newPos - 1]; // SegAreaInfo tmpInfo = GetSegInfo(pathListA, pathListB); // SegAreaList.Add(tmpInfo); SegPathList.Insert(newPos, pathListB); bValid = true; } } else { //目前粘连区域位于中间一格,暂不处理 } } }
public static bool SegByBaseLine(Point segP, VerPro baseArr, Boundary mBoundary, int bThVal, int newPos, bool bLeft) { bool bValid = false; //以交点为起点分别执行向上滴落和向下滴落 ArrayList pathListA = dropFallUp(segP); ArrayList pathListB = dropFallDown(segP); SegAreaInfo tmpInfo = new SegAreaInfo(); int k = 0; for (k = 0; k < pathListA.Count; k++) { pathListB.Add(pathListA[k]); } //按字符的规则而言,符合该条件的可能是字符y,j,那么分割完成后,左侧区域有一定几率没有有效字符 //确认按照该规则分割出的左侧区域是否包含有效字符像素 pathListA.Clear(); //当前粘连区域左侧竖线作为判断基准 if (bLeft) { for (k = mBoundary.leftDownPoint.X; k < mBoundary.rightUpPoint.X; k++) { Point tmpP = new Point(); tmpP.X = k; tmpP.Y = mBoundary.leftDownPoint.Y; pathListA.Add(tmpP); } tmpInfo = GetSegInfo(pathListA, pathListB); } else { for (k = mBoundary.leftDownPoint.X; k < mBoundary.rightUpPoint.X; k++) { Point tmpP = new Point(); tmpP.X = k; tmpP.Y = mBoundary.rightUpPoint.Y; pathListA.Add(tmpP); } tmpInfo = GetSegInfo(pathListB, pathListA); } if (tmpInfo.blackPixCount > bThVal)//当前分割路径为有效路径 { //SegAreaList.Add(tmpInfo); SegPathList.Insert(newPos, pathListB); bValid = true; } return bValid; }
/// <summary> ///根据loop分割后的区块判断是否还有粘连字符,若有则根据guideLine原则继续分割 /// </summary> public static void GetMergedArea()//divideInfo divideData, Point guideLine, Byte[,] BinaryArray, ImgBoundary Boundary) { int areaNum = SegAreaList.Count; //记录单个粘连区域内独立有效的竖直投影区域 ArrayList proLoops = new ArrayList(); //记录当前粘连区域的Y轴范围 SegAreaInfo mAreaRange = new SegAreaInfo(); SegAreaInfo mAreaRange_1 = new SegAreaInfo(); SegAreaInfo mAreaRange_2 = new SegAreaInfo(); //记录不同区块的黑色像素数,用于辅助判断粘连字符区域 int blackCount_0 = 0; int blackCount_1 = 0; int blackCount_2 = 0; //记录当前验证码图片中单个字符的平均像素 int bThVal = bPixSum / 4; // int newPathPos = 0;//记录新的分割路径该插入的位置 switch (areaNum) { case 1://当前验证码仍有4个字符粘连 mAreaRange = (SegAreaInfo)SegAreaList[0]; SegByGuideLine(mAreaRange, 1, 4); break; case 2://当前验证码有3个字符粘连与1个分割完成的字符;或者2个字符与2个字符粘连 //根据两个区域的黑色像素数来判断哪个区块为粘连区 mAreaRange = (SegAreaInfo)SegAreaList[0]; blackCount_0 = mAreaRange.blackPixCount; mAreaRange_1 = (SegAreaInfo)SegAreaList[1]; blackCount_1 = mAreaRange_1.blackPixCount; if (blackCount_0 > (2 * bThVal + bThVal / 2))//dArray[0]为3个字符粘连区域 { SegByGuideLine(mAreaRange, 1, 3); } else if (blackCount_1 > (2 * bThVal + bThVal / 2))//dArray[1]为3个字符粘连区域 { SegByGuideLine(mAreaRange_1, 2, 3); } else { SegByGuideLine(mAreaRange, 1, 2); } break; case 3://当前验证码有2个字符粘连与2个独立字符,根据区域黑色像素数来找出粘连区 mAreaRange = (SegAreaInfo)SegAreaList[0]; blackCount_0 = mAreaRange.blackPixCount; mAreaRange_1 = (SegAreaInfo)SegAreaList[1]; blackCount_1 = mAreaRange_1.blackPixCount; mAreaRange_2 = (SegAreaInfo)SegAreaList[2]; blackCount_2 = mAreaRange_2.blackPixCount; if ((blackCount_0 > blackCount_1) && (blackCount_0 > blackCount_2))//dArray[0]为2个字符粘连区域 { SegByGuideLine(mAreaRange,1, 2); } else if ((blackCount_1 > blackCount_0) && (blackCount_1 > blackCount_2))//dArray[1]为2个字符粘连区域 { SegByGuideLine(mAreaRange_1, 2, 2); } else { SegByGuideLine(mAreaRange_2, 3, 2); } break; default: break; } }