基于逻辑回归和图像处理的项目
输入:一个验证码图像
输出:这个验证码图像中的数字(字母)
步骤
对图像进行处理
(1)二值化:首先把图像从RGB3通道转化成Gray1通道,然后把灰度图(0~255)转化成二值图(0,1)
(2)降噪:通过处理孤立点,对二值化的图进行降噪
(3)图片切割:根据像素格,把图片中的所有(5个)数字,分别保存到对应的0~9文件夹下
至此:数据处理就完成了
把数据带入逻辑回归进行建模
(1)把切割好的数据,按照X(二位数组),Y(一维数组)的方式传入logisticRegression.fit()函数进行拟合,我们可以通过网格搜索(GridSearch)来进行调参
(2)通过joblib包,把模型保存到本地
得到模型后,进行图像验证
(1)根据步骤1,重复操作新的图像
(2)对切割好的每个图像,独立的进行预测
(3)把最后预测结果进行拼接
用到的技术:
分类:逻辑回归
模型选择:网格搜索, 查准率 查全率, 混淆矩阵,准确率(score)
图像处理的技术:RGB转灰度转二值,8位降噪,图像切割
# encoding=utf-8
"""
Date:2019-08-10 09:53
User:LiYu
Email:[email protected]
"""
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import random
def getRandomStr():
return str(random.randint(0, 9))
def getRandomColor():
R = random.randint(0, 255)
G = random.randint(0, 255)
B = random.randint(0, 255)
if R == 255 and G == 255 and B == 255:
R, G, B = 0, 0, 0
return (R, G, B)
def generate_captcha():
# 画布
image = Image.new('RGB', (150, 50), (255, 255, 255))
# 画笔
draw = ImageDraw.Draw(image)
# 字体
font = ImageFont.truetype('LiberationSans-Bold.ttf', size=32)
label = '' # 保存随机到的五个数字字符串用于后续图片命名
for i in range(5): # 验证码为 五 个数字
random_char = getRandomStr() # 获取随机一个数字字符
label += random_char # 存起来
# left, top = 10 + i * 30, 0 text = random_char 随机颜色getRandomColor() 指定字体
draw.text((10 + i * 30, 0), random_char, getRandomColor(), font=font)
# 画噪线噪点
width = 150
height = 30
# 画线
for i in range(3): # 三条线
x1 = random.randint(0, width)
x2 = random.randint(0, width)
y1 = random.randint(0, height)
y2 = random.randint(0, height)
# (起始X,Y, 终止X,Y) 颜色
draw.line((x1, y1, x2, y2), fill=(0, 0, 0))
# 画点
for i in range(5): # 五个彩色点,五个黑色弧线(相当于占四个像素的点)
x = random.randint(0, width)
y = random.randint(0, height)
# 画点(x, y) 颜色
draw.point((random.randint(0, width), random.randint(0, height)), fill=getRandomColor())
# 画弧 (起始x, y, 终止x, y) 起始弧度0 终止弧度90 颜色
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=(0, 0, 0))
# image.save(open(''.join(['captcha_images/', label, '.png']), 'wb'), 'png')
image.save(open(''.join(['captcha_images_test/', 'unknow0.png']), 'wb'), 'png')
print(label)
if __name__ == '__main__':
for i in range(150):
generate_captcha()
# encoding=utf-8
"""
Date:2019-08-10 10:50
User:LiYu
Email:[email protected]
"""
import os
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
def binarization(path):
"""彩色验证码转为灰度图"""
img = Image.open(path)
# convert("L")变为灰度图
img_gray = img.convert('L')
img_gray = np.array(img_gray)
# print(img_gray)
w, h = img_gray.shape
for x in range(w):
for y in range(h):
gray = img_gray[x, y]
if gray <= 250:
img_gray[x, y] = 0
else:
img_gray[x, y] = 1
return img_gray
def noiseReduction(img_gray, label):
"""去除噪线噪点, 8位降噪"""
w, h = img_gray.shape
# 如果一个黑像素周围的黑像素少于四个,就把它变为白色
for x in range(w):
for y in range(h):
count = 0
if img_gray[x, y] == 1:
continue
try:
if img_gray[x - 1, y - 1] == 0:
count += 1
except:
pass
try:
if img_gray[x - 1, y] == 0:
count += 1
except:
pass
try:
if img_gray[x - 1, y + 1] == 0:
count += 1
except:
pass
try:
if img_gray[x, y + 1] == 0:
count += 1
except:
pass
try:
if img_gray[x + 1, y + 1] == 0:
count += 1
except:
pass
try:
if img_gray[x + 1, y] == 0:
count += 1
except:
pass
try:
if img_gray[x + 1, y - 1] == 0:
count += 1
except:
pass
try:
if img_gray[x, y - 1] == 0:
count += 1
except:
pass
if count < 4:
img_gray[x, y] = 1
plt.figure('')
plt.imshow(img_gray, cmap='gray')
plt.axis('off')
# plt.show()
# plt.savefig(''.join(['captcha_images_clean/', label, '.png']))
plt.savefig(''.join(['captcha_images_test/', label])) # 预测用
# return img_gray
def getAllFileLabels(dir):
"""获取文件夹里的所有图片名,只保留数字部分"""
imgFileList = os.listdir(dir)
labels = []
for imgFile in imgFileList:
labels.append(imgFile.rstrip('.png'))
return labels
def img2clean(labels):
"""清洗验证码图片,变成灰度图并降噪"""
for label in labels:
imgFile = ''.join(['captcha_images/', label, '.png'])
img_gray = binarization(imgFile)
noiseReduction(img_gray, label)
def cutImg(label):
"""切割图片并按数字分组保存"""
img = Image.open(''.join(['captcha_images_clean/', label, '.png']))
for i in range(5):
pic = img.crop((100 * (1 + i), 170, 100 * (1 + i) + 100, 280))
plt.imshow(pic)
seq = getSaveSeq(label[i])
pic.save(''.join(['cut_number/', str(label[i]), '/', str(seq), '.png']))
def getSaveSeq(num):
"""获取下一个数字图片的保存索引值(现有的图片最大索引加一)"""
numList = os.listdir(''.join(['cut_number/', num, '/']))
if len(numList) == 0 or numList is None:
return 0
else:
max_file = 0
for f in numList:
if int(f.split('.')[0]) > max_file:
max_file = int(f.split('.')[0])
return int(max_file) + 1
def makeDir():
if not os.path.exists('captcha_images_clean'):
os.makedirs('captcha_images_clean')
if not os.path.exists('cut_number'):
os.makedirs('cut_number')
for i in range(10):
if not os.path.exists(''.join(['cut_number/', str(i)])):
os.makedirs(''.join(['cut_number/', str(i)]))
if __name__ == '__main__':
makeDir()
labels = getAllFileLabels('captcha_images')
img2clean(labels)
labels = getAllFileLabels('captcha_images_clean')
for label in labels:
cutImg(label)
# encoding=utf-8
"""
Date:2019-08-10 13:43
User:LiYu
Email:[email protected]
"""
import os
from PIL import Image
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.externals import joblib
from 处理切分验证码图片 import *
def img2data(num):
X = []
Y = []
labels = getAllFileLabels(''.join(['cut_number/', str(num)]))
for label in labels:
img = Image.open(''.join(['cut_number/', str(num), '/', label, '.png']))
# convert("L")变为灰度图
img_gray = img.convert('L')
img_gray = np.array(img_gray)
w, h = img_gray.shape
for x in range(w):
for y in range(h):
gray = img_gray[x, y]
if gray <= 250:
img_gray[x, y] = 0
else:
img_gray[x, y] = 1
img_gray = np.reshape(img_gray, (1, -1))[0]
# print("img_gray: ", img_gray)
X.append(img_gray)
Y.append(num)
# print('X: ', X)
# print('Y: ', Y)
return X, Y
def getAllFileLabels(dir):
"""获取文件夹里的所有图片名,只保留数字部分"""
imgFileList = os.listdir(dir)
labels = []
for imgFile in imgFileList:
labels.append(imgFile.rstrip('.png'))
return labels
def loadData():
X = []
Y = []
for i in range(10):
Xi, Yi = img2data(i)
for x in Xi:
X.append(x)
for y in Yi:
Y.append(y)
X = np.array(X)
Y = np.array(Y)
print("X: ", X)
print("Y: ", Y)
return X, Y
def generatrModel(X, Y):
# X_train, X_test, Y_train, Y_test = train_test_split(X, Y)
logReg = LogisticRegression(multi_class='ovr', solver='sag')
logReg.fit(X, Y)
print(logReg.score(X, Y))
joblib.dump(logReg, 'captcha_model/captcha_model.model')
def getModel():
model = joblib.load('captcha_model/captcha_model.model')
return model
def captcha_predict(path):
img_gray = binarization('captcha_images_test/' + path)
noiseReduction(img_gray, path)
labels = ['0', '1', '2', '3', '4']
img = Image.open('captcha_images_test/' + path)
for i in range(5):
pic = img.crop((100 * (1 + i), 170, 100 * (1 + i) + 100, 280))
plt.imshow(pic)
pic.save(''.join(['captcha_images_test/', labels[i], '.png']))
result = ''
model = getModel()
for i in range(5):
path = ''.join(['captcha_images_test/', labels[i], '.png'])
img = Image.open(path)
img_gray = img.convert('L')
img_gray = np.array(img_gray)
w, h = img_gray.shape
for x in range(w):
for y in range(h):
gray = img_gray[x, y]
if gray <= 250:
img_gray[x, y] = 0
else:
img_gray[x, y] = 1
img_gray = np.reshape(img_gray, (1, -1))
X = img_gray[0]
Y_pre = model.predict([X])
result = ''.join([result, str(Y_pre[0])])
return result
if __name__ == '__main__':
# # 建模过程
# X, Y = loadData()
# generatrModel(X, Y)
# 测试
imgs = ['unknow2.png', 'unknow3.png', 'unknow4.png']
for img in imgs:
result = captcha_predict(img)
print(result)