效果图,简单的还行,复杂的。。。拉跨
懒得写讲解了,全部源码直接上吧
/// <summary>
/// 验证码识别
/// </summary>
public partial class FrmCodeIdentify : FrmBase
{
private string _filePath;
// 原图像
Image<Bgr, byte> inputImage;
// 灰度图像
Image<Gray, byte> inputGray;
// 二值化图像
Image<Gray, byte> binaryImage;
// 去噪音图像
Image<Gray, byte> denoisingImage;
// 修复图像
Image<Gray, byte> repairImage;
// 图像增强
Image<Bgr, byte> enhanceImage;
public FrmCodeIdentify()
{
InitializeComponent();
}
/// <summary>
/// 选择图像
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
using OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = ImageExtension;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
string selectedFile = openFileDialog.FileName;
_filePath = selectedFile;
this.pictureBox1.Image?.Dispose();
this.pictureBox1.Image = new Bitmap(_filePath);
inputImage?.Dispose();
inputImage = new Image<Bgr, byte>(_filePath);
if (checkBox1.Checked)
{
ImageEnhancement();
Grayscale();
Binarization();
Denoising();
RepairImage();
ShowAllTxt();
}
else
{
var bitmap = inputImage.ToBitmap();
ShowTxt(bitmap, label3);
}
}
}
private void ShowAllTxt()
{
var bitmap3 = inputImage.ToBitmap();
ShowTxt(bitmap3, label3);
var bitmap4 = enhanceImage.ToBitmap();
ShowTxt(bitmap4, label4);
var bitmap5 = inputGray.ToBitmap();
ShowTxt(bitmap5, label5);
var bitmap1 = binaryImage.ToBitmap();
ShowTxt(bitmap1, label1);
var bitmap6 = denoisingImage.ToBitmap();
ShowTxt(bitmap6, label6);
var bitmap7 = repairImage.ToBitmap();
ShowTxt(bitmap7, label7);
}
private void ShowTxt(Bitmap bitmap, Label label)
{
var data = BitmapToByteArray(bitmap);
bitmap.Dispose();
var txt = ExtractedText(data);
label.Text = $"识别结果:{txt}";
}
private void FrmCodeIdentify_Load(object sender, EventArgs e)
{
}
private void FrmCodeIdentify_FormClosing(object sender, FormClosingEventArgs e)
{
repairImage?.Dispose();
enhanceImage?.Dispose();
denoisingImage?.Dispose();
binaryImage?.Dispose();
inputImage?.Dispose();
inputGray?.Dispose();
engine?.Dispose();
this.Dispose();
}
/// <summary>
/// 灰度化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
Grayscale();
var bitmap5 = inputGray.ToBitmap();
ShowTxt(bitmap5, label5);
}
private void Grayscale()
{
inputGray?.Dispose();
if (string.IsNullOrEmpty(_filePath))
{
MessageBox.Show("请选择图像");
return;
}
//using Matrix<float> kernel = new Matrix<float>(kernelData);
// 应用卷积操作
//CvInvoke.Filter2D(inputImage, inputImage, kernel, new Point(-1, -1));
//ShowPictureBox(pictureBox1, inputImage);
//inputImage = PerformTextCorrection(inputImage);
inputGray = enhanceImage.Convert<Gray, byte>();
// 应用直方图均衡化
//inputGray._EqualizeHist();
this.pictureBox2.Image?.Dispose();
this.pictureBox2.Image = inputGray.ToBitmap();
}
/// <summary>
/// 二值化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
Binarization();
var bitmap1 = binaryImage.ToBitmap();
ShowTxt(bitmap1, label1);
}
private void Binarization()
{
if (inputGray == null)
{
MessageBox.Show("需要灰度化图像");
return;
}
binaryImage?.Dispose();
using var inputGrayOut = new Image<Gray, byte>(inputGray.Size);
// 自适应阈值
//binaryImage= new Image<Gray, byte>(inputGray.Size);
//CvInvoke.AdaptiveThreshold(inputGray, binaryImage, 255, AdaptiveThresholdType.MeanC, ThresholdType.Binary, 11, 2);
// 计算OTSU阈值
var threshold = CvInvoke.Threshold(inputGray, inputGrayOut, 0, 255, ThresholdType.BinaryInv | ThresholdType.Otsu);
// 二值化图像
binaryImage = inputGrayOut.ThresholdBinary(new Gray(threshold), new Gray(255));
pictureBox3.Image?.Dispose();
pictureBox3.Image = binaryImage.ToBitmap();
}
/// <summary>
/// 去噪音
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
Denoising();
var bitmap6 = denoisingImage.ToBitmap();
ShowTxt(bitmap6, label6);
}
private void Denoising()
{
if (binaryImage == null)
{
MessageBox.Show("需要二值化图像");
return;
}
int mksize = (int)numericUpDown6.Value;
if ((mksize & 1) == 0)
{
MessageBox.Show("MedianBlur的ksize必须为奇数");
return;
}
denoisingImage?.Dispose();
denoisingImage = new Image<Gray, byte>(binaryImage.Size);
// 中值滤波
//CvInvoke.MedianBlur(binaryImage, binaryImage, mksize);
// 创建结构元素
using Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size((int)numericUpDown1.Value, (int)numericUpDown2.Value), new Point(-1, -1));
// 进行开运算
CvInvoke.MorphologyEx(binaryImage, denoisingImage, MorphOp.Open, element, new Point(-1, -1), (int)numericUpDown5.Value, BorderType.Default, new MCvScalar());
//CvInvoke.BitwiseNot(denoisingImage, denoisingImage);
// 中值滤波
CvInvoke.MedianBlur(denoisingImage, denoisingImage, mksize);
ShowPictureBox(pictureBox4, denoisingImage);
}
/// <summary>
/// 修复
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button7_Click(object sender, EventArgs e)
{
RepairImage();
var bitmap7 = repairImage.ToBitmap();
ShowTxt(bitmap7, label7);
}
private void RepairImage()
{
if (denoisingImage == null)
{
MessageBox.Show("请操作去噪音");
return;
}
repairImage?.Dispose();
// 创建结构元素
using Mat element = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size((int)numericUpDown8.Value, (int)numericUpDown7.Value), new Point(-1, -1));
repairImage = new Image<Gray, byte>(denoisingImage.Size);
CvInvoke.MorphologyEx(denoisingImage, repairImage, MorphOp.Close, element, new Point(-1, -1), 1, BorderType.Default, new MCvScalar());
ShowPictureBox(pictureBox6, repairImage);
}
// 创建卷积核
float[,] kernelData = {
{ -1, -1, -1 },
{ -1, 9, -1 },
{ -1, -1, -1 }
};
// 定义锐化滤波器
float[,] kernel = new float[,]
{
{ 0, -1, 0 },
{ -1, 5, -1 },
{ 0, -1, 0 }
};
/// <summary>
/// 图像增强
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button5_Click(object sender, EventArgs e)
{
ImageEnhancement();
var bitmap = enhanceImage.ToBitmap();
ShowTxt(bitmap, label4);
}
private void ImageEnhancement()
{
if (inputImage == null)
{
MessageBox.Show("需要添加图片");
return;
}
int gksize = (int)numericUpDown4.Value;
if ((gksize & 1) == 0)
{
MessageBox.Show("需要为奇数");
return;
}
enhanceImage = new Image<Bgr, byte>(inputImage.Size);
// 应用高斯模糊滤波器
CvInvoke.GaussianBlur(inputImage, enhanceImage, new Size(gksize, gksize), 0);
using Matrix<float> kernels = new Matrix<float>(kernel);
// 应用卷积操作
CvInvoke.Filter2D(enhanceImage, enhanceImage, kernels, new Point(-1, -1));
// 创建锐化滤波器的内核
//using Matrix<float> kernelMatrix = new Matrix<float>(kernelData);
// 应用锐化滤波器
//CvInvoke.Filter2D(enhanceImage, enhanceImage, kernelMatrix, new Point(-1, -1));
// 将图像转换为 YCrCb 颜色空间
Image<Ycc, byte> yccImage = enhanceImage.Convert<Ycc, byte>();
// 将 YCrCb 图像的亮度通道应用自适应直方图均衡化
CvInvoke.CLAHE(yccImage[0], 40.0, new Size(8, 8), yccImage[0]);
enhanceImage.Dispose();
// 将 YCrCb 图像转换回 Bgr 颜色空间
enhanceImage = yccImage.Convert<Bgr, byte>();
ShowPictureBox(pictureBox5, enhanceImage);
}
/// <summary>
/// 文字识别
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button6_Click(object sender, EventArgs e)
{
//ExtractedText();
}
TesseractEngine engine = new TesseractEngine("D:\\个人\\tessdata-master", "eng", EngineMode.Default);
private string ExtractedText(byte[] data)
{
if (repairImage == null)
{
MessageBox.Show("需要增强图像");
return "";
}
// 加载图像
using var image = Tesseract.Pix.LoadFromMemory(data);
// 将图像传递给Tesseract引擎进行文字提取
using var page = engine.Process(image);
// 获取提取的文字
string extractedText = page.GetText();
//var mc = page.GetMeanConfidence();
extractedText = FilterExtractedText(extractedText);
return extractedText;
}
public byte[] BitmapToByteArray(Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);
return stream.ToArray();
}
}
public static Image<Bgr, byte> PerformTextCorrection(Image<Bgr, byte> inputImage)
{
// 转换为灰度图像
using Image<Gray, byte> grayImage = inputImage.Convert<Gray, byte>();
// 进行边缘检测
using Image<Gray, byte> edges = grayImage.Canny(50, 150);
// 进行直线检测
LineSegment2D[] lines = CvInvoke.HoughLinesP(edges, 1, Math.PI / 180, 100, 30, 10);
// 计算所有直线的平均角度
double averageAngle = lines.Average(line => Math.Atan2(line.P2.Y - line.P1.Y, line.P2.X - line.P1.X) * 180 / Math.PI);
// 计算旋转中心
PointF center = new PointF(inputImage.Width / 2f, inputImage.Height / 2f);
// 创建旋转矩阵
using Mat rotationMatrix = new Mat();
CvInvoke.GetRotationMatrix2D(center, -averageAngle, 1.0, rotationMatrix);
// 进行旋转
using Image<Bgr, byte> rotatedImage = inputImage.WarpAffine(rotationMatrix, Inter.Linear, Warp.Default, BorderType.Constant, new Bgr(255, 255, 255));
return rotatedImage;
}
private string FilterExtractedText(string extractedText)
{
string txt = string.Empty;
foreach (var item in extractedText)
{
if (char.IsDigit(item))
txt += item;
else
{
int temp = (char)item;
if (temp >= 65 && temp <= 90 || temp >= 97 && temp <= 122)
txt += item;
}
}
return txt;
}
}