机器学习无非分为两大步骤:
训练:收集数据提供给计算机进行学习,学习如何对验证码进行识别,即如何进行分类,最终训练出一个模型,以文件形式存储。
识别(分类):利用训练产生的模型,对新验证码进行识别
这里要弄清一点:训练的过程(程序)是不属于识别程序的,而训练的结果(数据)是识别程序的输入之一。
所有要导入的包
import urllib
import os
import PIL.Image as image
import numpy as np
import sklearn.externals.joblib as job
import sklearn.svm as svm
import io
import requests
训练
1. 准备原始图片素材
首先研究目标网站的登录页面,找到验证码图片的src。
如我学校的网站:
1.png
2.png
可以发现该验证码图片的src是由一个js函数动态获得的
又如这个网站:
3.png
它的验证码是通过后端脚本获得的
只有以上两种方式是本文要讨论的,是需要图像识别出马的。对于给出图片地址的验证码,我们完全不需要利用图像识别技术,因为那种验证码是服务器的静态文件,不是动态生成的,可以通过反复爬取,save all distinctly to get 100% accuracy!
另外,仔细观察以上两种验证码,可以发现:
主要以英文和数字组成(字符种类不是很多)
相同字符的大小基本相同(这一点很重要,之后SVM操作中可以了解到)
字符之间不存在重叠(可以偶尔有几个重叠)
# -*- coding: UTF-8 -*-
import urllib
def scratch_image(url, save_dir):
"""
url直接传入【域名+src】
从url抓取图片,保存到本地,并自动为每张图片命名
:param url: 图片地址,请求到的必须是图片格式
:param save_dir: 保存到本地的目录,不包括文件名
:return: 无
"""
bin = urllib.urlopen(url).read()
no = len(os.listdir(save_dir))
name = "no" + str(no) + ".jpeg"
path = os.path.join(save_dir, name)
f = open(path, "wb")
f.write(bin)
print "Picture saved: " + path
f.close()
可以通过循环此函数实现批量抓取,批量抓取时请务必加上f.close()
2. 图片预处理
经过第一步,我们将训练图片集存储在了硬盘上。为了让图片更容易被机器看懂,我们读取的同时需要做一点初步的加工。
读入一张图片,并将其转换为二值图片数组
def img_to_bin(img_path, threshold):
"""
将图片转化为灰度图,二值化
0为黑,1为白
:param img_path: 图片的本地路径,也可以直接传入文件
:param threshold: 二值化的阈值,一般取255/2左右的值,如130
:return: img_bin
"""
img_bin = np.array(image.open(img_path).convert("L"))
for i in range(len(img_bin)):
for j in range(len(img_bin[i])):
if img_bin[i][j] > threshold:
img_bin[i][j] = 1
else:
img_bin[i][j] = 0
return img_bin
去除噪声
基本原理:如果一个黑点所在的3*3的格子内只有1个或2个黑点(包括自己),则其为噪点。注意处理边界情况。
def remove_noise(img_bin):
"""
0为黑,1为白
初步去除图片的噪声,即去除较为孤立的点
:param img_bin: 二值图像numpy数组
:return: 修改过的二值图像
"""
for i in range(len(img_bin)):
for j in range(len(img_bin[i])):
# 如果是0(黑)则进行判断
if img_bin[i][j] == 0:
count = 1
# 如果是上边界(包括左右两端)
if i == 0:
# 左上角的点
if j == 0:
img_bin[i][j] = 1
# 右上角的点
elif j == len(img_bin[0]) - 1:
img_bin[i][j] = 1
# 上边的点
else:
count += (5 - img_bin[0][j - 1]
- img_bin[0][j + 1]
- img_bin[1][