图像 - 识别出图片里的数字和字母
本文给大家分享的是C#识别出图片里的数字和字母的代码,主要是识别以前公司的软件注册码截图里的数字和字母,功能很简单,也存在很大的局限性,这里仅仅是分享,小伙伴们参考下。 一个图片识别小工具,原先主要是识别以前公司的软件注册码截图里的数字和字母(每次要一个一个框复制出来粘贴到注册器里,很麻烦!),因为注册码出现的字母和数字基本就那几个,所以识别库的范围设定的比较少。 原理和算法在代码中做了详细说明,功能存在很大的局限性,但我的想法是把这个思路和实现的办法共享出来。 源码下载地址: http://git.oschina.net/bobo2cj/iamge2text
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Text; 7 using System.Windows.Forms; 8 9 namespace iamge2text 10 { 11 public partial class FormLicense : Form 12 { 13 14 /* 15 * 开发思路:图片灰度处理,二进制,然后和图片中的字二进制库精确对比 16 * 17 * 获取字库:通过下面代码中generateLicense(Bitmap singlepic)方法获得,具体操作: 18 * 从图片中截图出(抠出)一个字符,然后处理得到二维的二进制矩阵,比如下面的字符1对应的二维矩阵 19 * 00000 20 * 00100 21 * 11100 22 * 00100 23 * 00100 24 * 00100 25 * 00100 26 * 00100 27 * 00100 28 * 11111 29 * 00000 30 * 00000 31 * 32 * 注意:【相同字符,比如1,不同字体,字号,不同缩放大小的图片,获得到的二位矩阵中0、1排列和数量都是不同的! 33 * 故按照此方法来写出匹配所有字的话,那字库就大了。。。】 34 * 35 * 36 */ 37 public FormLicense() 38 { 39 InitializeComponent(); 40 buttonGenerate.Enabled = false; //在pictureBox控件中无图片时buttonGenerate按钮不可用 41 richTextBoxLicense.ReadOnly = true; //并且不可以在文本框中改动输出后的字符 42 this.AcceptButton = this.buttonOpen; //回车键作用在打开按钮上 43 } 44 45 #region 在用的字符对应黑白颜色二进制码的字库,我的工具中只需要下面的几个字符,所有不是所有文字都能识别出来 46 static string stringByte0 = "000000001100010010100001100001100001100001100001010010001100000000000000"; 47 static char[] char0 = stringByte0.ToCharArray(); 48 static int BinaryWidth0 = 5, BinaryHeight0 = 11; //0的平面像素长宽(从0开始数起) 49 50 static string stringByte1 = "000000010011100001000010000100001000010000100111110000000000"; 51 static char[] char1 = stringByte1.ToCharArray(); 52 static int BinaryWidth1 = 4, BinaryHeight1 = 11; //1的平面像素长宽(从0开始数起) 53 54 static string stringByte2 = "000000111010001100010000100010001000100010001111110000000000"; 55 static char[] char2 = stringByte2.ToCharArray(); 56 static int BinaryWidth2 = 4, BinaryHeight2 = 11; //2的平面像素长宽(从0开始数起) 57 58 static string stringByte3 = "000000111010001100010011000001000011000110001011100000000000"; 59 static char[] char3 = stringByte3.ToCharArray(); 60 static int BinaryWidth3 = 4, BinaryHeight3 = 11; //3的平面像素长宽(从0开始数起) 61 62 static string stringByte4 = "000010000010000110001010010010010010100010011111000010000111000000000000"; 63 static char[] char4 = stringByte4.ToCharArray(); 64 static int BinaryWidth4 = 5, BinaryHeight4 = 11; //4的平面像素长宽(从0开始数起) 65 66 static string stringByte5 = "000001111110000100001111010001000011000110001011100000000000"; 67 static char[] char5 = stringByte5.ToCharArray(); 68 static int BinaryWidth5 = 4, BinaryHeight5 = 11; //5的平面像素长宽(从0开始数起) 69 70 static string stringByte6 = "000000001111010001100000101110110001100001100001100001011110000000000000"; 71 static char[] char6 = stringByte6.ToCharArray(); 72 static int BinaryWidth6 = 5, BinaryHeight6 = 11; //6的平面像素长宽(从0开始数起) 73 74 static string stringByte7 = "000001111110001100100001000100001000010000100001000000000000"; 75 static char[] char7 = stringByte7.ToCharArray(); 76 static int BinaryWidth7 = 4, BinaryHeight7 = 11; //7的平面像素长宽(从0开始数起) 77 78 static string stringByte8 = "000000011110100001100001010010011110100001100001100001011110000000000000"; 79 static char[] char8 = stringByte8.ToCharArray(); 80 static int BinaryWidth8 = 5, BinaryHeight8 = 11; //8的平面像素长宽(从0开始数起) 81 82 static string stringByte9 = "000000011110100001100001100001100011011101000001100010111100000000000000"; 83 static char[] char9 = stringByte9.ToCharArray(); 84 static int BinaryWidth9 = 5, BinaryHeight9 = 11; //9的平面像素长宽(从0开始数起) 85 86 static string stringByteA = "000000000000000000000000011100100010001110010010100010011111000000000000"; 87 static char[] charA = stringByteA.ToCharArray(); 88 static int BinaryWidthA = 5, BinaryHeightA = 11; //a的平面像素长宽(从0开始数起) 89 90 static string stringByteB = "000000110000010000010000011110010001010001010001010001011110000000000000"; 91 static char[] charB = stringByteB.ToCharArray(); 92 static int BinaryWidthB = 5, BinaryHeightB = 11; //b的平面像素长宽(从0开始数起) 93 94 static string stringByteC = "000000000000000000000111110001100001000010001011100000000000"; 95 static char[] charC = stringByteC.ToCharArray(); 96 static int BinaryWidthC = 4, BinaryHeightC = 11; //c的平面像素长宽(从0开始数起) 97 98 static string stringByteD = "000000000110000010000010011110100010100010100010100010011111000000000000"; 99 static char[] charD = stringByteD.ToCharArray(); 100 static int BinaryWidthD = 5, BinaryHeightD = 11; //d的平面像素长宽(从0开始数起) 101 102 static string stringByteE = "000000000000000000000111010001111111000010001011100000000000"; 103 static char[] charE = stringByteE.ToCharArray(); 104 static int BinaryWidthE = 4, BinaryHeightE = 11; //e的平面像素长宽(从0开始数起) 105 106 static string stringByteF = "000000000111001001001000111110001000001000001000001000011110000000000000"; 107 static char[] charF = stringByteF.ToCharArray(); 108 static int BinaryWidthF = 5, BinaryHeightF = 11; //f的平面像素长宽(从0开始数起) 109 110 static string stringByteP = "000000000000000000000000111110010001010001010001010001011110010000111000"; 111 static char[] charP = stringByteP.ToCharArray(); 112 static int BinaryWidthP = 5, BinaryHeightP = 11; //p的平面像素长宽(从0开始数起) 113 114 static string stringByteY = "000000000000000000000000000011101110100010001010000101000001000000100000100001110000"; 115 static char[] charY = stringByteY.ToCharArray(); 116 static int BinaryWidthY = 6, BinaryHeightY = 11; //y的平面像素长宽(从0开始数起) 117 #endregion 118 119 static int[,] intStartXY = new int[128, 3]; //记录匹配上时的“X坐标”和“Y坐标”对应的“值”以及该“字符像素的宽度” 120 static int numIdentfied = 0; //负责记录总共有多少匹配的字符 121 122 //打开图片按钮 123 private void buttonOpen_Click(object sender, EventArgs e) 124 { 125 try 126 { 127 Bitmap m_Bitmap; //定义个Bitmap型变量存储图片 128 OpenFileDialog openFileDialog = new OpenFileDialog(); //打开图片 129 openFileDialog.Filter = "Bitmap文件(*.bmp)|*.bmp|Jpeg文件(*.jpg)|*.jpg|所有合适文件(*.bmp/*.jpg)|*.bmp/*.jpg"; //设置图片类型 130 openFileDialog.FilterIndex = 1; //打开对话框中默认第一个类型(即上面的Bitmap文件(*.bmp)|*.bmp) 131 openFileDialog.RestoreDirectory = true; //记录最后一次打开的文件路径 132 if (DialogResult.OK == openFileDialog.ShowDialog())//确定打开文件 133 { 134 m_Bitmap = (Bitmap)Bitmap.FromFile(openFileDialog.FileName, false); //通过(Bitmap)将打开的图片类型转换 135 pictureBoxLicense.Image = m_Bitmap; //为pictureBox控件加载所打开的图片 136 AutoScroll = true; 137 AutoScrollMinSize = new Size((int)(m_Bitmap.Width), (int)m_Bitmap.Height); 138 buttonGenerate.Enabled = true; //在pictureBox控件中有图片时buttonGenerate按钮可用 139 this.buttonGenerate.Select(); 140 } 141 } 142 catch { } 143 } 144 145 //提取注册码按钮 146 private void buttonGenerate_Click(object sender, EventArgs e) 147 { 148 try 149 { 150 buttonGenerate.Enabled = false; //该图片只可进行一次提取,之后就不可用除非再加载该图片 151 this.buttonOpen.Select(); 152 numIdentfied = 0; //将计数器清零 153 Bitmap Sourcebm = (Bitmap)pictureBoxLicense.Image; //为了保险起见将pictureBox的图片类型进行格式转换(Bitmap) 154 int iw = Sourcebm.Width; //图片宽度 155 int ih = Sourcebm.Height; //图片高度 156 //下面双循环是图片灰度处理 157 for (int i = 0; i < iw; i++) 158 {//从左到右 159 for (int j = 0; j < ih; j++) 160 {//从上到下 161 Color c = Sourcebm.GetPixel(i, j); //获取该点的颜色 162 int luma = (int)(c.R * 0.3 + c.G * 0.59 + c.B * 0.11); //将颜色转换为数值体现 163 Sourcebm.SetPixel(i, j, Color.FromArgb(luma, luma, luma)); //将这一点进行灰度处理,非白色的部分变黑 164 } 165 } 166 generateLicense(Sourcebm); //通过该方法进行提取字符 167 } 168 catch { } 169 } 170 171 /// <summary> 172 /// 提取出该图片内的字符(将进过灰度处理的图片转化为0、1的二位数组) 173 /// </summary> 174 /// <param name="singlepic">图片来源</param> 175 public void generateLicense(Bitmap singlepic) 176 { 177 try 178 { 179 char[,] charArray = new char[singlepic.Height, singlepic.Width]; //定义个chai型的二维数组记录每个像素上0/1的值,形成一个矩形 180 int imageWidth = 0; //记录图片的像素宽度 181 int imageHeight = 0; //记录图片的像素高度 182 int dgGrayValue = 128; //灰度值 183 Color piexl; 184 //string code = ""; //存储每个像素的0/1 185 for (int posy = 0; posy < singlepic.Height; posy++) 186 {//从上到下 187 string codeCache = ""; //存储每行的像素的0/1 188 for (int posx = 0; posx < singlepic.Width; posx++) 189 {//从左到右 190 piexl = singlepic.GetPixel(posx, posy); 191 if (piexl.R < dgGrayValue) 192 {// 如果该像素的颜色为黑色,值就为“1” 193 codeCache = codeCache + "1"; 194 } 195 else 196 {// 否则该像素的颜色为白色,值就为“0” 197 codeCache = codeCache + "0"; 198 } 199 } 200 char[] array = codeCache.ToCharArray(); //每行的0/1的值用数字保存,以便于进行循环处理 201 //code += codeCache + "\n"; 202 for (imageWidth = 0; imageWidth < array.Length; imageWidth++) 203 charArray[imageHeight, imageWidth] = array[imageWidth]; //通过循环将每行值转存到二维数组中 204 imageHeight++; 205 } //*********************以上代码可用来获取一个字的图片二进制数组,即字库***************************** 206 207 //开始和字库进行匹配(我的工具中只需要下面的几个字符) 208 findWord(charArray, char0, imageHeight, imageWidth, BinaryWidth0, BinaryHeight0, '0'); 209 findWord(charArray, char1, imageHeight, imageWidth, BinaryWidth1, BinaryHeight1, '1'); 210 findWord(charArray, char2, imageHeight, imageWidth, BinaryWidth2, BinaryHeight2, '2'); 211 findWord(charArray, char3, imageHeight, imageWidth, BinaryWidth3, BinaryHeight3, '3'); 212 findWord(charArray, char4, imageHeight, imageWidth, BinaryWidth4, BinaryHeight4, '4'); 213 findWord(charArray, char5, imageHeight, imageWidth, BinaryWidth5, BinaryHeight5, '5'); 214 findWord(charArray, char6, imageHeight, imageWidth, BinaryWidth6, BinaryHeight6, '6'); 215 findWord(charArray, char7, imageHeight, imageWidth, BinaryWidth7, BinaryHeight7, '7'); 216 findWord(charArray, char8, imageHeight, imageWidth, BinaryWidth8, BinaryHeight8, '8'); 217 findWord(charArray, char9, imageHeight, imageWidth, BinaryWidth9, BinaryHeight9, '9'); 218 findWord(charArray, charA, imageHeight, imageWidth, BinaryWidthA, BinaryHeightA, 'a'); 219 findWord(charArray, charB, imageHeight, imageWidth, BinaryWidthB, BinaryHeightB, 'b'); 220 findWord(charArray, charC, imageHeight, imageWidth, BinaryWidthC, BinaryHeightC, 'c'); 221 findWord(charArray, charD, imageHeight, imageWidth, BinaryWidthD, BinaryHeightD, 'd'); 222 findWord(charArray, charE, imageHeight, imageWidth, BinaryWidthE, BinaryHeightE, 'e'); 223 findWord(charArray, charF, imageHeight, imageWidth, BinaryWidthF, BinaryHeightF, 'f'); 224 findWord(charArray, charP, imageHeight, imageWidth, BinaryWidthP, BinaryHeightP, 'p'); 225 findWord(charArray, charY, imageHeight, imageWidth, BinaryWidthY, BinaryHeightY, 'y'); 226 //------------------------------------END--------------------------------------------- 227 richTextBoxLicense.Text += identifySort(); //执行identifySort方法,将我需要的格式在richTextBoxLicense文本框中显示 228 richTextBoxLicense.SelectionStart = richTextBoxLicense.TextLength; //将光标移到最后面 229 } 230 catch { } 231 } 232 233 /// <summary> 234 /// 和字库进行匹配 235 /// </summary> 236 /// <param name="charArray">记录图片中每个像素的二维数组</param> 237 /// <param name="charNum">字库中0/1值一维数组形式的字符</param> 238 /// <param name="imageHeight">图片的像素高度</param> 239 /// <param name="imageWidth">图片的像素宽度</param> 240 /// <param name="binaryWidth">字库中该字符的像素宽度</param> 241 /// <param name="binaryHeight">字库中该字符的像素高度</param> 242 /// <param name="stringChar">字库中该字符</param> 243 public void findWord(char[,] charArray, char[] charNum, int imageHeight, int imageWidth, int binaryWidth, int binaryHeight, char stringChar) 244 { 245 try 246 { 247 int upLeftX, upLeftY, x, y; 248 for (y = 0; y < imageHeight - binaryHeight; y++)//从图片的每行开始 249 { 250 for (x = 0; x < imageWidth - binaryWidth; x++)//从当前行的第一格开始 251 { 252 bool isIdentified = false; //负责辨别是否匹配 253 int count = 0; //负责计数 254 for (upLeftY = 0; upLeftY <= binaryHeight; upLeftY++)//从图片中取出一块进行对比,从的每行开始 255 { 256 for (upLeftX = 0; upLeftX <= binaryWidth; upLeftX++)//从这一块当前行的第一格开始 257 { 258 //下面进行每格的对比,大数字去除的“块”是二维数组,小数组是一维数组 259 if (charArray[y + upLeftY, x + upLeftX] == charNum[upLeftY * (binaryWidth + 1) + upLeftX]) 260 { 261 isIdentified = true; //记录像素点是否比对成功 262 count++; 263 if (count == (binaryWidth + 1) * (binaryHeight + 1))//判断是否对比到了最后一个像素点 264 { 265 intStartXY[numIdentfied, 0] = y; //记录字库中该字符在图片中出现的Y值 266 intStartXY[numIdentfied, 1] = x; //记录字库中该字符在图片中出现的X值 267 intStartXY[numIdentfied, 2] = Convert.ToInt32(stringChar); //将该字符转换为数字型 268 numIdentfied++; //记录图片中总共多少个字库中的数字 269 break; //一旦匹配即将结束比对 270 } 271 } 272 else 273 { 274 isIdentified = false; //此像素点比对不成功 275 break; //如果该像素点值比对不成功即将结束比对 276 } 277 } 278 if (!isIdentified)//如果一个不符就向后退一格,同时小数组的比对又需要从第一格开始 279 break; //并且结束这次的比对 280 } 281 } 282 } 283 } 284 catch { } 285 } 286 287 /// <summary> 288 /// 对比对后的结果通过坐标进行排序 289 /// </summary> 290 /// <returns>提取出的图片中的字符串</returns> 291 public string identifySort() 292 { 293 string stringLicense = ""; //存储该结果 294 try 295 { 296 int intTemp = 0; 297 for (int a = 0; a < numIdentfied; a++) 298 {//从第一列开始 299 for (int b = 0; b < numIdentfied; b++) 300 {//然后从该列中第一行开始对比 301 if (intStartXY[a, 0] < intStartXY[b, 0]) 302 {//通过Y坐标(离顶端距离)判断那个字符在上面,并进行对调 303 for (int c = 0; c < 3; c++) 304 { 305 intTemp = intStartXY[a, c]; 306 intStartXY[a, c] = intStartXY[b, c]; 307 intStartXY[b, c] = intTemp; 308 } 309 } 310 if (intStartXY[a, 0] == intStartXY[b, 0] && intStartXY[a, 1] < intStartXY[b, 1]) 311 {//当Y坐标(离顶端距离)相同时,通过X坐标(离左端距离)判断那个字符在左面,并进行对调 312 for (int c = 0; c < 3; c++) 313 { 314 intTemp = intStartXY[a, c]; 315 intStartXY[a, c] = intStartXY[b, c]; 316 intStartXY[b, c] = intTemp; 317 } 318 } 319 } 320 } 321 322 //------------------------下面是我需要的格式------------------------------------------------------------- 323 /* 324 yp_12_125 325 yp_12_125 326 e4ebf340-563b5e1c-b04957df-baacc576 327 */ 328 for (int h = 0; h < numIdentfied; h++) 329 { 330 stringLicense += Convert.ToChar(intStartXY[h, 2]); 331 if ((intStartXY[h + 1, 0] == intStartXY[h, 0] && intStartXY[h + 1, 1] - intStartXY[h, 1] >= 12 && h < numIdentfied - 32)) 332 stringLicense += "_"; //当同一行时,相差一个下划线距离就显示下划线_ 333 if (intStartXY[h + 1, 0] - intStartXY[h, 0] >= 11 && h < numIdentfied - 17) 334 stringLicense += "\n"; //当不上一行时就输出\n 335 if (h == numIdentfied - 25 || h == numIdentfied - 17 || h == numIdentfied - 9) 336 stringLicense += "-"; //每8个显示一个中划线- 337 } 338 //---------------------------------------END--------------------------------------------------------------------------------- 339 } 340 catch { } 341 return stringLicense + "\n"; //对返回的结果进行换行,保证在richTextBox文本框显示的时候光标始终在下行 342 } 343 344 private void pictureBoxLicense_DoubleClick(object sender, EventArgs e) 345 {//通过双击控件粘贴图片 346 try 347 { 348 IDataObject iData = Clipboard.GetDataObject(); //GetDataObject检索当前剪贴板上的数据 349 if (iData.GetDataPresent(DataFormats.Bitmap))//将数据与指定的格式进行匹配 350 { 351 // GetData检索数据并指定一个格式 352 Bitmap bitmapLicense = (Bitmap)iData.GetData(DataFormats.Bitmap); 353 pictureBoxLicense.Image = bitmapLicense; //加载截屏后的图片 354 buttonGenerate.Enabled = true; //pictureBox中新图片时提取按钮便可使用 355 this.buttonGenerate.Select(); 356 } 357 else 358 { 359 MessageBox.Show("目前剪贴板中数据不是图片\n 请先截图再双击此处!", "Error"); 360 } 361 } 362 catch { } 363 } 364 365 private void FormLicense_Load(object sender, EventArgs e) 366 { 367 368 } 369 } 370 }