如何做律师网站,在线设计平台崭露头角,建设网站后怎么发布,实名域名购买大致介绍在python爬虫爬取某些网站的验证码的时候可能会遇到验证码识别的问题#xff0c;现在的验证码大多分为四类#xff1a;1、计算验证码2、滑块验证码3、识图验证码4、语音验证码这篇博客主要写的就是识图验证码#xff0c;识别的是简单的验证码#xff0c;要想让识别…大致介绍 在python爬虫爬取某些网站的验证码的时候可能会遇到验证码识别的问题现在的验证码大多分为四类 1、计算验证码 2、滑块验证码 3、识图验证码 4、语音验证码 这篇博客主要写的就是识图验证码识别的是简单的验证码要想让识别率更高识别的更加准确就需要花很多的精力去训练自己的字体库。 识别验证码通常是这几个步骤 1、灰度处理 2、二值化 3、去除边框如果有的话 4、降噪 5、切割字符或者倾斜度矫正 6、训练字体库 7、识别 这6个步骤中前三个步骤是基本的4或者5可根据实际情况选择是否需要并不一定切割验证码识别率就会上升很多有时候还会下降 这篇博客不涉及训练字体库的内容请自行搜索。同样也不讲解基础的语法。 用到的几个主要的python库 Pillow(python图像处理库)、OpenCV(高级图像处理库)、pytesseract(识别库)灰度处理二值化 灰度处理就是把彩色的验证码图片转为灰色的图片。 二值化是将图片处理为只有黑白两色的图片利于后面的图像处理和识别 在OpenCV中有现成的方法可以进行灰度处理和二值化处理后的效果 代码# 自适应阀值二值化def _get_dynamic_binary_image(filedir, img_name): filename ./out_img/ img_name.split(.)[0] -binary.jpg img_name filedir / img_name print(..... img_name) im cv2.imread(img_name) im cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) #灰值化 # 二值化 th1 cv2.adaptiveThreshold(im, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1) cv2.imwrite(filename,th1) return th1去除边框 如果验证码有边框那我们就需要去除边框去除边框就是遍历像素点找到四个边框上的所有点把他们都改为白色我这里边框是两个像素宽 注意在用OpenCV时图片的矩阵点是反的就是长和宽是颠倒的 代码# 去除边框def clear_border(img,img_name): filename ./out_img/ img_name.split(.)[0] -clearBorder.jpg h, w img.shape[:2] for y in range(0, w): for x in range(0, h): if y 2 or y w - 2: img[x, y] 255 if x 2 or x h -2: img[x, y] 255 cv2.imwrite(filename,img) return img 效果降噪 降噪是验证码处理中比较重要的一个步骤我这里使用了点降噪和线降噪 线降噪的思路就是检测这个点相邻的四个点图中标出的绿色点判断这四个点中是白点的个数如果有两个以上的白色像素点那么就认为这个点是白色的从而去除整个干扰线但是这种方法是有限度的如果干扰线特别粗就没有办法去除只能去除细的干扰线 代码1 # 干扰线降噪 2 def interference_line(img, img_name): 3 filename ./out_img/ img_name.split(.)[0] -interferenceline.jpg 4 h, w img.shape[:2] 5 # opencv矩阵点是反的 6 # img[1,2] 1:图片的高度2图片的宽度 7 for y in range(1, w - 1): 8 for x in range(1, h - 1): 9 count 010 if img[x, y - 1] 245:11 count count 112 if img[x, y 1] 245:13 count count 114 if img[x - 1, y] 245:15 count count 116 if img[x 1, y] 245:17 count count 118 if count 2:19 img[x, y] 25520 cv2.imwrite(filename,img)21 return img 点降噪的思路和线降噪的差不多只是会针对不同的位置检测的点不一样,注释写的很清楚了 代码# 点降噪def interference_point(img,img_name, x 0, y 0): 9邻域框,以当前点为中心的田字框,黑点个数 :param x: :param y: :return: filename ./out_img/ img_name.split(.)[0] -interferencePoint.jpg # todo 判断图片的长宽度下限 cur_pixel img[x,y]# 当前像素点的值 height,width img.shape[:2] for y in range(0, width - 1): for x in range(0, height - 1): if y 0: # 第一行 if x 0: # 左上顶点,4邻域 # 中心点旁边3个点 sum int(cur_pixel) int(img[x, y 1]) int(img[x 1, y]) int(img[x 1, y 1]) if sum 2 * 245: img[x, y] 0 elif x height - 1: # 右上顶点 sum int(cur_pixel) int(img[x, y 1]) int(img[x - 1, y]) int(img[x - 1, y 1]) if sum 2 * 245: img[x, y] 0 else: # 最上非顶点,6邻域 sum int(img[x - 1, y]) int(img[x - 1, y 1]) int(cur_pixel) int(img[x, y 1]) int(img[x 1, y]) int(img[x 1, y 1]) if sum 3 * 245: img[x, y] 0 elif y width - 1: # 最下面一行 if x 0: # 左下顶点 # 中心点旁边3个点 sum int(cur_pixel) int(img[x 1, y]) int(img[x 1, y - 1]) int(img[x, y - 1]) if sum 2 * 245: img[x, y] 0 elif x height - 1: # 右下顶点 sum int(cur_pixel) int(img[x, y - 1]) int(img[x - 1, y]) int(img[x - 1, y - 1]) if sum 2 * 245: img[x, y] 0 else: # 最下非顶点,6邻域 sum int(cur_pixel) int(img[x - 1, y]) int(img[x 1, y]) int(img[x, y - 1]) int(img[x - 1, y - 1]) int(img[x 1, y - 1]) if sum 3 * 245: img[x, y] 0 else: # y不在边界 if x 0: # 左边非顶点 sum int(img[x, y - 1]) int(cur_pixel) int(img[x, y 1]) int(img[x 1, y - 1]) int(img[x 1, y]) int(img[x 1, y 1]) if sum 3 * 245: img[x, y] 0 elif x height - 1: # 右边非顶点 sum int(img[x, y - 1]) int(cur_pixel) int(img[x, y 1]) int(img[x - 1, y - 1]) int(img[x - 1, y]) int(img[x - 1, y 1]) if sum 3 * 245: img[x, y] 0 else: # 具备9领域条件的 sum int(img[x - 1, y - 1]) int(img[x - 1, y]) int(img[x - 1, y 1]) int(img[x, y - 1]) int(cur_pixel) int(img[x, y 1]) int(img[x 1, y - 1]) int(img[x 1, y]) int(img[x 1, y 1]) if sum 4 * 245: img[x, y] 0 cv2.imwrite(filename,img) return img 效果 其实到了这一步这些字符就可以识别了没必要进行字符切割了现在这三种类型的验证码识别率已经达到50%以上了字符切割 字符切割通常用于验证码中有粘连的字符粘连的字符不好识别所以我们需要将粘连的字符切割为单个的字符在进行识别 字符切割的思路就是找到一个黑色的点然后在遍历与他相邻的黑色的点直到遍历完所有的连接起来的黑色的点找出这些点中的最高的点、最低的点、最右边的点、最左边的点记录下这四个点认为这是一个字符然后在向后遍历点直至找到黑色的点继续以上的步骤。最后通过每个字符的四个点进行切割 图中红色的点就是代码执行完后标识出的每个字符的四个点然后就会根据这四个点进行切割图中画的有些误差懂就好 但是也可以看到m2是粘连的代码认为他是一个字符所以我们需要对每个字符的宽度进行检测如果他的宽度过宽我们就认为他是两个粘连在一起的字符并将它在从中间切割 确定每个字符的四个点代码def cfs(im,x_fd,y_fd): 用队列和集合记录遍历过的像素坐标代替单纯递归以解决cfs访问过深问题 # print(**********) xaxis[] yaxis[] visited set() q Queue() q.put((x_fd, y_fd)) visited.add((x_fd, y_fd)) offsets[(1, 0), (0, 1), (-1, 0), (0, -1)]#四邻域 while not q.empty(): x,yq.get() for xoffset,yoffset in offsets: x_neighbor,y_neighbor xxoffset,yyoffset if (x_neighbor,y_neighbor) in (visited): continue # 已经访问过了 visited.add((x_neighbor, y_neighbor)) try: if im[x_neighbor, y_neighbor] 0: xaxis.append(x_neighbor) yaxis.append(y_neighbor) q.put((x_neighbor,y_neighbor)) except IndexError: pass # print(xaxis) if (len(xaxis) 0 | len(yaxis) 0): xmax x_fd 1 xmin x_fd ymax y_fd 1 ymin y_fd else: xmax max(xaxis) xmin min(xaxis) ymax max(yaxis) ymin min(yaxis) #ymin,ymaxsort(yaxis) return ymax,ymin,xmax,xmindef detectFgPix(im,xmax): 搜索区块起点 h,w im.shape[:2] for y_fd in range(xmax1,w): for x_fd in range(h): if im[x_fd,y_fd] 0: return x_fd,y_fddef CFS(im): 切割字符位置 zoneL[]#各区块长度L列表 zoneWB[]#各区块的X轴[起始终点]列表 zoneHB[]#各区块的Y轴[起始终点]列表 xmax0#上一区块结束黑点横坐标,这里是初始化 for i in range(10): try: x_fd,y_fd detectFgPix(im,xmax) # print(y_fd,x_fd) xmax,xmin,ymax,ymincfs(im,x_fd,y_fd) L xmax - xmin H ymax - ymin zoneL.append(L) zoneWB.append([xmin,xmax]) zoneHB.append([ymin,ymax]) except TypeError: return zoneL,zoneWB,zoneHB return zoneL,zoneWB,zoneHB分割粘连字符代码# 切割的位置 im_position CFS(im) maxL max(im_position[0]) minL min(im_position[0]) # 如果有粘连字符如果一个字符的长度过长就认为是粘连字符并从中间进行切割 if(maxL minL minL * 0.7): maxL_index im_position[0].index(maxL) minL_index im_position[0].index(minL) # 设置字符的宽度 im_position[0][maxL_index] maxL // 2 im_position[0].insert(maxL_index 1, maxL // 2) # 设置字符X轴[起始终点]位置 im_position[1][maxL_index][1] im_position[1][maxL_index][0] maxL // 2 im_position[1].insert(maxL_index 1, [im_position[1][maxL_index][1] 1, im_position[1][maxL_index][1] 1 maxL // 2]) # 设置字符的Y轴[起始终点]位置 im_position[2].insert(maxL_index 1, im_position[2][maxL_index]) # 切割字符要想切得好就得配置参数通常 1 or 2 就可以 cutting_img(im,im_position,img_name,1,1)切割粘连字符代码def cutting_img(im,im_position,img,xoffset 1,yoffset 1): filename ./out_img/ img.split(.)[0] # 识别出的字符个数 im_number len(im_position[1]) # 切割字符 for i in range(im_number): im_start_X im_position[1][i][0] - xoffset im_end_X im_position[1][i][1] xoffset im_start_Y im_position[2][i][0] - yoffset im_end_Y im_position[2][i][1] yoffset cropped im[im_start_Y:im_end_Y, im_start_X:im_end_X] cv2.imwrite(filename -cutting- str(i) .jpg,cropped)效果 识别 识别用的是typesseract库主要识别一行字符和单个字符时的参数设置识别中英文的参数设置代码很简单就一行我这里大多是filter文件的操作 代码# 识别验证码 cutting_img_num 0 for file in os.listdir(./out_img): str_img if fnmatch(file, %s-cutting-*.jpg % img_name.split(.)[0]): cutting_img_num 1 for i in range(cutting_img_num): try: file ./out_img/%s-cutting-%s.jpg % (img_name.split(.)[0], i) # 识别字符 str_img str_img image_to_string(Image.open(file),lang eng, config-psm 10) #单个字符是10一行文本是7 except Exception as err: pass print(切图%s % cutting_img_num) print(识别为%s % str_img)最后这种粘连字符的识别率是在30%左右而且这种只是处理两个字符粘连如果有两个以上的字符粘连还不能识别但是根据字符宽度判别的话也不难有兴趣的可以试一下 无需切割字符识别的效果 需要切割字符的识别效果 这种只是能够识别简单验证码复杂的验证码还要靠大家了嵌入式物联网资料分享交流群707159742 入群有全套学习视频资料电子书免费赠送快速学习python基础www.makeru.com.cn