中文验证码识别模块开发

开发一个中文验证码识别模块并不复杂,但要找到最佳的参数、系数和权重以获得最佳结果,这个过程会耗费一定的CPU时间。选择了一种简单的方法,没有使用通用算法或神经网络,而是使用了常见的for循环,这种方法虽然效率不高,但足以满足需求。最终,实现了大约75%的准确率,这对来说已经足够了。

最优解决方案涉及到相当复杂的嵌套循环,这并不是最优雅的解决方案。例如,以下是C#代码的一个示例:

int lowMaskLevel = 0; while (lowMaskLevel <= 90) { int topMaskLevel = lowMaskLevel; while (topMaskLevel <= 255) { int positiveFinalKoef = 0; while (positiveFinalKoef <= 100) { int negativeFinalKoef = 0; while (negativeFinalKoef <= 100) { int finePositiveKoef = 0; while (finePositiveKoef <= 30) { int fineNegativeKoef = 0; while (fineNegativeKoef <= 20) { int hightLightKoef = 1; while (hightLightKoef <= 3) { // ... var value = TestParameters(lowMaskLevel, topMaskLevel, positiveFinalKoef, negativeFinalKoef, finePositiveKoef, fineNegativeKoef, hightLightKoef); if (value >= bestValue) { bestLowMaskLevel = lowMaskLevel; bestTopMaskLevel = topMaskLevel; bestPositiveFinalKoef = positiveFinalKoef; bestNegativeFinalKoef = negativeFinalKoef; bestFinePositiveKoef = finePositiveKoef; bestFineNegativeKoef = fineNegativeKoef; bestHightLightKoef = hightLightKoef; // ... } hightLightKoef++; } fineNegativeKoef++; } finePositiveKoef++; } negativeFinalKoef++; } positiveFinalKoef++; } topMaskLevel++; } lowMaskLevel++; }

这种方法的维护难度相当高,因为参数、规则和限制的数量可能会发生变化。因此,放弃了这种方法,转而采用了一种更易于管理的解决方案,即通过一个辅助类来处理迭代,使事情变得清晰和可维护。

现在,可以像定义常规方法参数一样定义迭代器参数,看起来更整洁、更易于支持或进行任何更改。以下是C#代码的一个示例:

int bestValue = 0; object[] bestArguments = null; Iterator.Execute(() => TestParameters( Iterator.Range(0, 90), Iterator.Range(args => (int)args[0], args => 255), Iterator.Range(0, 100, 10), Iterator.Range(0, 100), Iterator.Range(0, 30), Iterator.Range(0, 20), Iterator.Range(0, 3) ), invocationResult => { if (invocationResult.Value >= bestValue) { bestValue = invocationResult.Value; bestArguments = invocationResult.Arguments; } });

要使用它,需要添加对WestWayConsulting.Tools.Iterator.dll程序集的引用。请注意,这个程序集引用了Immutable Collections,因此它是用.NET Framework 4.5编译的。该工具有一个Facade类Iterator,用于定义和执行迭代。

要执行迭代,需要调用静态Execute方法,该方法有两个重载版本——一个用于调用返回值的方法,另一个用于调用动作方法。Execute方法接受以下参数:

  • expression: 定义方法调用的表达式。可以是静态方法、实例方法或类型构造函数。如果方法返回值,Execute方法将返回执行结果的列表;
  • callbackAction(可选): 在每次方法执行后执行的方法委托。该方法接受InvocationResult类的实例,该类包含方法执行的结果、参数数组、异常;
  • throwExceptionOnError(可选,默认为true): 定义如果在方法调用过程中发生任何异常,是否应该抛出异常或忽略;

当在迭代器中定义方法时,仍然可以使用任何类型的参数,就像通常做的那样,并将它们与迭代器定义混合使用。因此,以下语句将有效:

Iterator.Execute(() => MyMethod( 1, DateTime.Now, Iterator.Range(0, 100, 10), Iterator.RangeParallel(0, 100), new WebClient() ));

请注意,不能在主方法调用之外定义迭代器。以下语句将抛出InvalidOperationException异常:

Iterator.Execute(() => MyMethod( new DateTime(2013, 1, Iterator.Range(0, 100, 10)) ));

在当前版本中,提供了以下迭代器:

  • Range(from, to, step): 生成从值到值的序列迭代器,具有指定的步长。有用于生成整数和双精度序列的重载。相当于:
  • RangeParallel(from, to, step): 与Range相同,但执行在并行中运行。相当于:
  • Random(minValue, maxValue, count): 生成指定数量的随机数序列(count),介于minValue和maxValue之间。有用于生成整数和双精度序列的重载。相当于:
  • RandomParallel(minValue, maxValue, count): 与Random相同,但执行在并行中运行。相当于:
  • Enumerator(enumerable): 从enumerable执行迭代。相当于:
  • EnumeratorParallel(enumerable): 与Enumerator相同,但执行在并行中运行。相当于:

每个定义都有带有动态参数的重载。通过使用它们,可以使用复杂的规则定义迭代参数。定义动态参数的函数接受来自上述参数的值数组。

Iterator.Execute(() => MyMethod( 1, // arg[0] DateTime.Now, // arg[1] Iterator.Range(args => (int)args[0], args => 255), // arg[2] Iterator.RandomParallel(args => (int)args[2], args => DateTime.Now.Seconds + 255), // arg[3] new WebClient() ));

工作原理:

Execute方法解析Lambda表达式中的顶级参数,并生成一个参数处理程序的链接链。每个处理程序都知道要执行的下一个处理程序。

Iterator.Execute(() => TestParameters( Iterator.Range(0, 90), // _iterator {arg[0]} Iterator.Range(args => (int)args[0], args => 255), // _iterator {arg[0], arg[1]} DateTime.Now, // _method as argument {arg[0], arg[1], arg[2], arg[3]} ));
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485