热门标签 | HotTags
当前位置:  开发笔记 > 程序员 > 正文

opencv+tesseract破解教务管理验证码

opencv+tesseract破解教务管理验证码折腾了一天,弄得差不多了,训练器还要继续调试(折腾)。效果如图:改了改还是有好多不准的,需要继续训练,但是我实在训不下

opencv+tesseract破解教务管理验证码

折腾了一天,弄得差不多了,训练器还要继续调试(折腾)。

效果如图:

)}}

  • 程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System.Drawing;

namespace jwglcode
{
///
/// 教务管理验证码识别
///

class jwglCode : IDisposable
{
///
/// 原始验证码图片
///

public Mat Code;
///
/// 处理后图片
///

public Mat CodeReduction;
///
/// 存储单个字符的数组
///

public Mat[] CodeSingleArr;
///
/// 存储旋转后单个字符的数组
///

public Mat[] CodeSingleRotateArr;
///
/// 验证码长度
///

public int CodeLength;
///
/// 验证码数据(解析结果)
///

public string CodeStr;

///
/// 验证码识别
///

/// 原图像
/// 验证码长度
public jwglCode(Mat src,int codeLength = 4)
{
Code = src;
CodeLength = codeLength;
run();
}

///
/// 判断数据是否在上下限内
///

/// 数据
/// 目标值
/// 误差
///
private bool rangeTest(byte data, byte todata, byte r)
{
if (data >= todata - r && data <= todata + r)
return true;
else
return false;
}

///
/// 过滤干扰点
/// (如果一个点和他上下左右都不一样,则将此点颜色改为周围颜色)
///

/// 图像数据
///
private Mat filter(Mat src)
{
Image src1 = src.ToImage();
for (int i = 1; i 1; i++)
{
for (int j = 1; j 1; j++)
{
//richTextBox1.AppendText(string.Format("[{0},{1}] => {2}\n", i, j, src1.Data[i, j, 0]));
if (src1.Data[i, j, 0] != src1.Data[i + 1, j, 0] &&
src1.Data[i, j, 0] != src1.Data[i - 1, j, 0] &&
src1.Data[i, j, 0] != src1.Data[i, j - 1, 0] &&
src1.Data[i, j, 0] != src1.Data[i, j + 1, 0])
{

src1.Data[i, j, 0] = (byte)((byte)255 - src1.Data[i, j, 0]);
}
}
}
return src1.Mat;
}

///
/// 错误信息
///

/// 信息
private void error(string msg)
{
//System.Windows.Forms.MessageBox.Show(msg);
throw new Exception(msg);
}

///
/// 提示消息
///

/// 信息
private void info(string msg)
{
//System.Windows.Forms.MessageBox.Show(msg);
}

///
/// 运行全部计算
///

private void run ()
{
//修图
CodeReduction = picReduction(Code);
//分割
CodeSingleArr = SplitPic(CodeReduction);

//转正
if (CodeSingleArr.Length == CodeLength)
{
List codeSingleRotateList = new List();
foreach (var code in CodeSingleArr)
{
codeSingleRotateList.Add(rotate(code));
}
CodeSingleRotateArr = codeSingleRotateList.ToArray();
}
else
{
error(string.Format("字符长度错误!目标值:{0},搜索值:{1}!", CodeLength, CodeSingleArr.Length));
}

//识别
if(CodeSingleRotateArr.Length == CodeLength)
{
string codeStr = "";
foreach(var code in CodeSingleRotateArr)
{
string filename = saveMatByRandomName(code);
codeStr += ocrChar(filename);
}
CodeStr = codeStr;
}
else
{
error(string.Format("字符长度错误!目标值:{0},搜索值:{1}!", CodeLength, CodeSingleArr.Length));
}
}

///
/// 图像处理
///

/// 图像
/// 处理后图像
private Mat picReduction(Mat src)
{
Mat ret = new Mat();
try
{
Image src1 = src.ToImage();
for (int i = 0; i {
for (int j = 0; j {
if (rangeTest(src1.Data[i, j, 0], (byte)150, 20) &&
rangeTest(src1.Data[i, j, 1], (byte)10, 10) &&
rangeTest(src1.Data[i, j, 2], (byte)10, 10))
{
src1.Data[i, j, 0] = 0;
src1.Data[i, j, 1] = 0;
src1.Data[i, j, 2] = 0;
}
else
{
src1.Data[i, j, 0] = 255;
src1.Data[i, j, 1] = 255;
src1.Data[i, j, 2] = 255;
}
}
}
CvInvoke.CvtColor(src1.Mat, ret, ColorConversion.Bgr2Gray);
return filter(ret);
}
catch (Exception ex)
{
error(ex.Message);
}
return new Mat ();
}

///
/// 分割图像
///

/// 原图像
/// 分割后数组
private Mat[] SplitPic(Mat src)
{
Image m = src.ToImage();
Imagebyte> img = new Imagebyte>(m.Cols, m.Rows, new Gray(0));

for (int j = 0; j //垂直投影
{
int d = 0;
for (int i = 0; i //计算投影
{
if (m.Data[i, j, 0] == 0)
d++;
}
for (int i = 0; i //绘制投影
{
img.Data[i, j, 0] = 255;
}
if (d <2)//绘制分割线
for (int i = 0; i {
m.Data[i, j, 0] = 127;
}
}
//分割字符组
int charwidth = 0;
List mats = new List();
for (int j = 0; j {
if (m.Data[m.Rows - 1, j, 0] != 127)//判最后一行,貌似最后一行都没占用
{
charwidth++;
}
else
{
if (charwidth != 0)
{
int picnums = (int)((double)charwidth / 11 + 0.5);//四舍五入
picnums = (picnums == 0) ? 1 : picnums;//i/1/l太细了...
int Onewidth= (picnums > 1) ? charwidth / picnums + 1 : charwidth / picnums;

for (int pic = 0; pic {
mats.Add(new Mat(m.Mat, new Rectangle(j - charwidth + pic * onewidth, 0, onewidth, m.Rows - 1)));
}
}
charwidth = 0;
}
}
//切字完成
return mats.ToArray();
}

///
/// 图像大小归一化
///

/// 图像
/// 图像大小
/// 归一化图像
private Mat SizeNormalLize(Mat src,int sizehw = 30)
{
Imagebyte> mm = new Imagebyte>(sizehw, sizehw, new Gray(255));
Image mI = src.ToImage();

int h = (sizehw - src.Width) / 2, w = (sizehw - src.Height) / 2;
for (int i = 0; i {
for (int j = 0; j {
if (mI.Data[i, j, 0] == 0)
mm.Data[i + w - 1, j + h - 1, 0] = mI.Data[i, j, 0];
}
}

return mm.Mat;
}

///
/// 字符图像转正,大小归一化
///

/// 单个字符图像
/// /// 单个字符图像
/// 转正后图像
private Mat rotate(Mat src,int r_range = 30)
{
Image sourceImage = SizeNormalLize(src).ToImage();

int r_perfect = 0;
int hLengthMin = 999;
for (int r = -r_range; r <= r_range; r++)
{
Imagebyte> rotateImagetemp = sourceImage.Rotate(r, new Gray(255));

int hLength = 0;
for (int j = 0; j {
for (int i = 0; i {
if(rotateImagetemp.Data[i,j,0] == 0)
{
hLength++;//计算水平投影长度
break;
}
}
}

hLengthMin = (hLengthMin //保存最小水平投影长度
r_perfect = (hLengthMin //保存最佳角度
}

Imagebyte> rotateImage = sourceImage.Rotate(r_perfect, new Gray(255));
return rotateImage.Mat;
}

///
/// 字符图像转正,大小归一化
///

/// 单个字符图像
/// 转正后图像
[Obsolete("This methods is obsolete; use methods rotate instead.")]
private Mat rotate2(Mat m)
{
int sizehw = 30;
Imagebyte> mm = new Imagebyte>(sizehw, sizehw, new Gray(255));
Image mI = m.ToImage();

int h = (sizehw - m.Width) / 2, w = (sizehw - m.Height) / 2;
for (int i = 0; i {
for (int j = 0; j {
if (mI.Data[i, j, 0] == 0)
mm.Data[i + w - 1, j + h - 1, 0] = mI.Data[i, j, 0];
}
}

Image sourceImage = mm;

int smallr_l = 999;//正无穷
int r_perfect = 0;
int r_range = 30;
for (int r = -r_range; r <= r_range; r++)
{
Imagebyte> rotateImagetemp = sourceImage.Rotate(r, new Gray(255));

int minj = 999, maxj = 0;
for (int j = 0; j {
for (int i = 0; i {
if (rotateImagetemp.Data[i, j, 0] == 0)
{
minj = (minj maxj = (maxj > j) ? maxj : j;
}
}
}
smallr_l = (smallr_l <(maxj - minj)) ? smallr_l : (maxj - minj);
r_perfect = (smallr_l <(maxj - minj)) ? r_perfect : r;
}

info(string.Format("r = {0},w = {1}\n", r_perfect, smallr_l));
Imagebyte> rotateImage = sourceImage.Rotate(r_perfect, new Gray(255));
return rotateImage.Mat;
}

///
/// 使用随机名称保存bmp图像
///

/// 图像
/// 名称
private string saveMatByRandomName(Mat src)
{
Random seed = new Random();
string filename = seed.Next().ToString() + ".bmp";
while (true)
{
if (!File.Exists(filename))//文件存在
{
SaveMat(src, filename);
break;
}
filename = seed.Next().ToString() + ".bmp";
}
return filename;
}

///
/// 保存图像
///

/// 图像
/// 名称
private void SaveMat (Mat src,string filename)
{
src.Save(filename);
}

///
/// 识别图像文件中字符
///

/// 图像名称
/// 识别结果
private string ocrChar(string filename)
{
RunTesseract(string.Format("-psm 10 -l jwgl {0} out", filename));
//这个自己训练...懒得训练的直接用eng吧
//RunTesseract(string.Format("-psm 10 -l jwgl+eng {0} out", filename));
//RunTesseract(string.Format("-psm 10 -l eng {0} out", filename));
string r = "";
try
{
FileStream f = File.OpenRead("out.txt");
StreamReader fr = new StreamReader(f, Encoding.UTF8);
r = ((char)fr.Read()).ToString();
//r = fr.ReadLine();
fr.Close();
f.Close();
}
catch (Exception ex)
{
error(ex.Message);
}
return r;
}

///
/// 调用Tesseract
///

/// 参数
/// 读取到的数据
private static string RunTesseract(string command)
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "tesseract.exe";
p.StartInfo.Arguments = command;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
bool b = p.Start();
string output = "";
output = p.StandardOutput.ReadToEnd();
p.WaitForExit(5000);
p.Close();
return output;
}

#region IDisposable Support
private bool disposedValue = false; // 要检测冗余调用

protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: 释放托管状态(托管对象)。
Code.Dispose();
CodeReduction.Dispose();
foreach (var a in CodeSingleArr)
a.Dispose();
foreach(var a in CodeSingleRotateArr)
a.Dispose();
}

// TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
// TODO: 将大型字段设置为 null。

disposedValue = true;
}
}

// TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
// ~jwglCode() {
// // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
// Dispose(false);
// }

// 添加此代码以正确实现可处置模式。
public void Dispose()
{
// 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
Dispose(true);
// TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
// GC.SuppressFinalize(this);
}
#endregion
}
}


推荐阅读
author-avatar
冷月荐向_878
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有