// by conmajia
// 10 JUN, 2012
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows.Forms;
using AForge.Imaging;
using System.Drawing;
using AForge.Imaging.Filters;
using AForge;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing.Imaging;
namespace QQValidationCodeRecognition
{
public partial class Form1 : Form
{
#region vars
// imagechar
List<ImageChar> imgChars;
// images
Bitmap srcImg = null;
Bitmap preImg = null;
// preprocess sequence
FiltersSequence preprocessFilters;
// histogram
int[] histo;
// histo ranges
IntRange[] ranges;
#endregion
#region Inits
public Form1()
{
InitializeComponent();
// stores
imgChars = new List<ImageChar>();
preprocessFilters = new FiltersSequence();
preprocessFilters.Add(Grayscale.CommonAlgorithms.BT709);
//preprocessFilters.Add(new BradleyLocalThresholding());
preprocessFilters.Add(new OtsuThreshold());
}
#endregion
#region Image helpers
Bitmap preprocess(Bitmap bmp, FiltersSequence fs)
{
Bitmap tmp = bmp.Clone() as Bitmap;
tmp = fs.Apply(bmp);
return tmp;
}
Bitmap projectHistogram(Bitmap bmp)
{
// stats
if (histo != null)
histo = null;
int width = bmp.Width;
int height = bmp.Height;
histo = new int[width];
for (int y = 0; y < height; y )
for (int x = 0; x < width; x )
{
Color color = bmp.GetPixel(x, y);
if (color.R < 50 && color.G < 50 && color.B < 50)
//if (color.A > 200)
histo[x] ;
}
// draw
//int max = getMax(histo);
Bitmap tmp = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(tmp))
for (int i = 0; i < width; i )
g.DrawLine(
Pens.Black,
i,
height,
i,
height - histo[i]);
return tmp;
}
// get rid of noises
Bitmap denoise(Bitmap bmp, Size size)
{
Bitmap ivbmp = bmp.Clone() as Bitmap;
Invert iv = new Invert();
iv.ApplyInPlace(ivbmp);
BlobCounter bc = new BlobCounter();
bc.FilterBlobs = true;
bc.MinWidth = bc.MinHeight = 0;
bc.MaxWidth = size.Width;
bc.MaxHeight = size.Height;
bc.ProcessImage(ivbmp);
Rectangle[] rects = bc.GetObjectsRectangles();
Bitmap tmp = new Bitmap(bmp);
using (Graphics g = Graphics.FromImage(tmp))
//g.DrawRectangles(Pens.Red, rects);
if (rects.Length > 0)
g.FillRectangles(Brushes.White, rects);
//iv.ApplyInPlace(tmp);
return tmp;
}
// draw some division lines over bitmap
Bitmap getRangedBitmap(Bitmap bmp, IntRange[] ranges)
{
Bitmap tmp = bmp.Clone() as Bitmap;
Graphics g = Graphics.FromImage(tmp);
for (int i = 0; i < ranges.Length; i )
{
g.DrawLine(
Pens.Purple,
ranges[i].Min,
0,
ranges[i].Min,
tmp.Height);
g.DrawLine(
Pens.Purple,
ranges[i].Max,
0,
ranges[i].Max,
tmp.Height);
}
return tmp;
}
#endregion
#region analyze helpers
int getMax(int[] array)
{
int max = 0;
foreach (int b in histo)
{
if (b > max)
max = b;
}
return max;
}
// TODO: optimize algorithm
IntRange[] getRanges(int[] data, int threshold, int min)
{
List<IntRange> tmp = new List<IntRange>();
int max = getMax(data);
threshold = (int)(threshold * max / (float)byte.MaxValue);
for (int i = 0; i < data.Length; i )
if (data[i] >= threshold)
for (int j = i; j < data.Length; j )
if (data[j] < threshold)
if (j - i >= min)
{
tmp.Add(new IntRange(i, j));
i = j - 1;
break;
}
// calculate average width
if (tmp.Count > 0)
{
int total = tmp[tmp.Count - 1].Max - tmp[0].Min;
float aver = (float)total / (float)tmp.Count;
List<IntRange> newtmp = new List<IntRange>(tmp);
int rcount = tmp.Count;
for (int i = 0; i < rcount; i )
{
// devide large into small
if (tmp[i].Length > 1.8 * aver)
{
int count = (int)(tmp[i].Length / aver) 1;
for (int j = 0; j < count; j )
{
int offset = (int)(tmp[i].Min j * aver);
IntRange r = new IntRange(offset, offset (int)aver);
tmp.Insert(i, r);
}
tmp.RemoveAt(i);
}
}
}
return tmp.ToArray();
}
#endregion
#region UI helpers
// clear all
void clearAll()
{
foreach (Control c in this.Controls)
{
if (c is PictureBox && c.Name != "pictureBox1")
(c as PictureBox).Image = null;
}
histo = null;
}
#endregion
#region ctrls
// Load image
private void loadImgLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
srcImg = AForge.Imaging.Image.FromFile(ofd.FileName);
}
catch
{
}
if (srcImg != null)
{
clearAll();
srcPic.Image = srcImg;
}
}
}
//******************
// analyze image
private void recognizeButton_Click(object sender, EventArgs e)
{
if (srcImg == null)
return;
// clear
srcPic.Image = null;
// rotate
srcPic.Image = srcImg;
// preprocess
int noiseWidth = (int)noiseNumeric.Value;
preImg = denoise(preprocess(srcPic.Image as Bitmap, preprocessFilters), new Size(noiseWidth, noiseWidth));
preImg = denoise(preprocess(preImg, preprocessFilters), new Size(noiseWidth, 3));
preImg = denoise(preprocess(preImg, preprocessFilters), new Size(3, noiseWidth));
prePic.Image = preImg;
// histogram
histoPic.Image = projectHistogram(prePic.Image as Bitmap);
}
// picture box click, send to check pic
private void picClick(object sender, EventArgs e)
{
PictureBox pb = sender as PictureBox;
checkPic.Image = pb.Image;
// get ranges
ranges = getRanges(histo, histoPic.Threshold, 2);
rangeLabel.Text = string.Format(
"Range:{0}",
ranges.Length);
// divide
divideCharPic.Image = getRangedBitmap(preImg, ranges);
}
// change color of check pic
private void checkColorClick(object sender, EventArgs e)
{
Control c = sender as Control;
checkPic.BackColor = c.BackColor;
}
private void invertCheckBox_CheckedChanged(object sender, EventArgs e)
{
// simple remove
if (preprocessFilters.Count == 3)
preprocessFilters.RemoveAt(2);
else
preprocessFilters.Add(new Invert());
recognizeButton_Click(null, null);
}
// mouse moves over histogram, updates the threshold label
private void histoPic_MouseMove(object sender, MouseEventArgs e)
{
thresholdLabel.Text = string.Format(
"阈值:{0}",
histoPic.Threshold);
}
#endregion
}
#region ImageChar class
public class ImageChar
{
Bitmap img = null;
public Bitmap Image
{
get { return img; }
set { img = value; }
}
char ch;
public char Char
{
get { return ch; }
set { ch = value; }
}
RectangleF bounds;
public RectangleF Bounds
{
get { return bounds; }
set { bounds = value; }
}
}
#endregion
}
评论