成果展示
序章
这个故事是这样开始的,学校为了防控,图书馆需要预约,然后一次400个人…基本瞬间没了…,大家都在拼命刷手机,企图抢先一步…
每天7点开放预约,还要早起…真的累…
所以想偷懒,就开始研究能不能设计一个自动预约的…小程序…你好我好大家好…
想法很美好…一上来就被泼了冷水…登录需要验证码
开始有两个想法
使用cookie…这个想法是好…但如果想设置定时预约可能不太方便…
第二个想法,验证码能不能绕过,比如说验证码直接发送过来了,(之前一些网站是这么做的),然后找了半天发现…并没有什么突破口…
为了生存,只能走向验证码识别这条路
当然我也是一边学习一边做,可能会比较慢…如果有好的思路也欢迎大家分享
当然我只是个菜鸡…可能高深的也讲不了…我就只能用我有效的知识…希望大家看了之后可以理解这些内容,然后可以通过我的代码来实现大家自己独特的功能
这就是本次需要识别的二维码
那么我们正式开始
一共会有4小节,前三节主要是关于识别验证码,最后一节就是具体的http请求发送相关的内容
首先安装相关的库
pip3 install opencv-python
pip3 install numpy
头文件需要导入的库
import cv2
import numpy as np
import time
import os
cv2就是我们刚才的安装的opencv-python库,用来处理我们的图像
numpy是我们需要导入的一个数学库,之后做卷积运算需要用这个生成高斯核
time是后面我们用来区分每个图片的,利用时间戳命名,省事
os对应着我们读写文件操作
第一步,读取我们的图片
import cv2
import numpy as np
import time
import os
files = os.listdir("test")#指定我们需要读取图片的目录,这个可以写相对目录,也可以写绝对目录
for file in files:
filepath = os.path.join("test", file)#这里用到了这个目录拼接函数,由于我们的图片是在刚才指定的目录下面,所以通过这个拼接使得我们的图片路径变得正确,比如说这个图片是在/test/1.jpg由于第一步读出拉爹file只是1.jpg如果直接用的话路径就不对,所以和test拼接就变成了test/1.jpg这就保障了路径的正确
im = cv2.imread(filepath)#图片读取函数
cv2.imshow('imshow', im)#imshow是指定我们生成的这个窗口名字,这个任意,第二个参数是我们需要展示的图片变量,也就是我们的im
cv2.waitKey(0)#一个和键盘绑定的函数,它的作用是等待一个键盘的输入(因为我们创建的图片窗口如果没有这个函数的话会闪一下就消失了)这里就是反正图片消失,而且这个跟我们之后标记图片也有关系
cv2.destroyAllWindows()#摧毁所有的展示窗口
这里为了效果,所以我们把读取的图片也展示出来
点击一下图片然后键盘上随便敲击一个键,就可以看到窗口关闭,运行结束
第二步,把图片变成灰度图片
import cv2
import numpy as np
import time
import os
files = os.listdir("test")
for file in files:
filepath = os.path.join("test", file)
im = cv2.imread(filepath)
im_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)#多了一步灰度化操作,第一个参数就是我们处理前的图片,第二个参数是我们转化的类型,由于这个函数还可以把这个图片处理成其他色彩的图片,而这里我们需要的是灰度图片,便于处理,返回的结果就是我们生成的灰度图片
cv2.imshow('imshow', im_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
第三步,将图片二值化处理
二值化处理简单的讲就是非黑即白,一系列处理的目的就是使得我们的图片可读性更高(对于计算机来讲)
import cv2
import numpy as np
import time
import os
files = os.listdir("test")
for file in files:
filepath = os.path.join("test", file)
im = cv2.imread(filepath)
im_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,im_inv=cv2.threshold(im_gray,127,255,cv2.THRESH_BINARY_INV)
#第一个参数原图像,第二个进行分类的阈值,根据这个值来分割,第三个是高于(低于)阈值时赋予的新值,第四个参数分割的方法
#有如下两个方法 cv2.THRESH_BINARY(黑白二值),cv2.THRESH_BINARY_INV(黑白二值反转)
#如果使用第一个黑白二值的方法,当图像中的灰度值大于127的重置像素值为255(白色).小于127就被置于0(黑色)
#如果使用本次使用的方法就是大于127也就是原先白色的部分变成0(黑色),而小于127就是原先数字(黑色部分)变成白色,这样做也是有利于我们识别
#该函数有两个返回值,第一个retVal(得到的阈值值,不过这个时候我们用不上),第二个就是阈值化后的图像
cv2.imshow('imshow', im_inv)
cv2.waitKey(0)
cv2.destroyAllWindows()
第三步,对图片进行降噪
我这里可能不太明显,有些验证码会有一些小的雪花(噪声)我们可以用卷积把相应部分模糊掉,然后再做一遍二值处理,就可以起到一定的效果,不过具体原理的话就涉及一些数学知识了,也不展开了
import cv2
import numpy as np
import time
import os
files = os.listdir("test")
for file in files:
filepath = os.path.join("test", file)
im = cv2.imread(filepath)
im_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,im_inv=cv2.threshold(im_gray,127,255,cv2.THRESH_BINARY_INV)
kernel=1/16*np.array([[1,2,1],[2,4,2],[1,2,1]])
im_blur=cv2.filter2D(im_inv,-1,kernel)
cv2.imshow('imshow', im_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
这个看到我们多了两部,第一步是生成高斯核,这个数组是np里面独特的一种,所以我们调用了这个函数,然后关于这个里面的数据,最开始我也不是很理解,知道看了一篇文章也才明白
Image Kernels Explained Visually
这个里面讲的有关于卷积内核
以下面这个图为例
可以看到左边的九宫格,通过我们刚才的卷积核,对这九个点的值做类似于加权平均,然后得到的值就是我们九宫格中间的那个点的像素
然后我们这里的取值也是一个经典的取值
这也叫做模糊核,然后中间那个-1代表的是处理后的图像和原图像保持一样的深度
其实可以看到图片明显模糊了,因为算法模糊了噪声,同时也模糊了噪声周围的数字,所以我们需要再做一次二值化处理
第四步,再来一次二值处理
import cv2
import numpy as np
import time
import os
files = os.listdir("test")
for file in files:
filepath = os.path.join("test", file)
im = cv2.imread(filepath)
im_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,im_inv=cv2.threshold(im_gray,127,255,cv2.THRESH_BINARY_INV)
kernel=1/16*np.array([[1,2,1],[2,4,2],[1,2,1]])
im_blur=cv2.filter2D(im_inv,-1,kernel)
ret,im_fin=cv2.threshold(im_blur,127,255,cv2.THRESH_BINARY)
cv2.imshow('imshow', im_fin)
cv2.waitKey(0)
cv2.destroyAllWindows()
我们的第一步就算是完成了,下面是我们处理后的图片,我们可以把他保存下来
import cv2
import numpy as np
import time
import os
dirname="test"#修改成你自己的目录位置
files = os.listdir(dirname)
for file in files:
filepath = os.path.join(dirname, file)
im = cv2.imread(filepath)
im_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,im_inv=cv2.threshold(im_gray,127,255,cv2.THRESH_BINARY_INV)
kernel=1/16*np.array([[1,2,1],[2,4,2],[1,2,1]])
im_blur=cv2.filter2D(im_inv,-1,kernel)
ret,im_fin=cv2.threshold(im_blur,127,255,cv2.THRESH_BINARY)
timestamp = int(time.time() * 1e6)
filename = "{}.jpg".format(timestamp)#保存的格式是上面生成的时间戳,有利于我们的区分
filepath = os.path.join(dirname, filename)#拼接路径,当然这里也可以在新建一个目录,然后保存到另一个目录去,这个可以自己调整
cv2.imwrite(filepath, im_fin)#保存照片,第一个参数是路径,第二个是图片,也就是我们第一部分处理后的图片
为什么那样计算时间戳,看看下面这个图应该就理解了
为了后面的使用方便,把这一小节进行的操作定义为函数
import cv2
import numpy as np
import time
import os
def photo_to_gray(im):
im_gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,im_bin=cv2.threshold(im_gray,127,255,cv2.THRESH_BINARY_INV)
kernel=1/16*np.array([[1,2,1],[2,4,2],[1,2,1]])
im_blur=cv2.filter2D(im_bin,-1,kernel)
ret,im_fin=cv2.threshold(im_blur,127,255,cv2.THRESH_BINARY)
return im_fin
dirname="test"#修改成你自己的目录位置,这个目录存放的是原始的验证码
files = os.listdir(dirname)
for file in files:
filepath = os.path.join(dirname, file)
im = cv2.imread(filepath)
im_fin=photo_to_gray(im)