25、使用Baidu的paddle自动进行验证码的识别、并计算验证码的数值
基本思想:隨手記錄一下,驗證碼需要計算之后才能登陸的py腳本開發~
一、下載 paddle 檢測和識別模型
https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/models_list.md
檢測模型:https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_det_infer.tar
識別模型:https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_rec_infer.tar
python -m pip install paddlepaddle==2.0.1 -i https://mirror.baidu.com/pypi/simple樣例圖片:來自百度測試sample的圖片
首先先測試一下
from paddleocr import PaddleOCR, draw_ocr from PIL import Image img_path = r'F:\tt\source\1.png' ocr = PaddleOCR(cls_thresh=0,use_gpu=False,det_model_dir=r'F:\tt\ch_ppocr_mobile_v2.0_det_infer', rec_model_dir=r'F:\tt\ch_ppocr_mobile_v2.0_rec_infer') # need to run only once to download and load model into memoryresult = ocr.ocr(img_path, cls=True) print(result) for line in result:print(line) image = Image.open(img_path).convert('RGB') boxes = [line[0] for line in result] txts = [line[1][0] for line in result] scores = [line[1][1] for line in result] im_show = draw_ocr(image, boxes, txts, scores) im_show = Image.fromarray(im_show) im_show.save('result.jpg')結果
"D:\Program Files\Python36\python.exe" D:/nanodet-main/test.py Namespace(cls_batch_num=6, cls_image_shape='3, 48, 192', cls_model_dir='C:\\Users\\sxj/.paddleocr/2.1/cls', cls_thresh=0, det=True, det_algorithm='DB', det_db_box_thresh=0.5, det_db_thresh=0.3, det_db_unclip_ratio=1.6, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_east_score_thresh=0.8, det_limit_side_len=960, det_limit_type='max', det_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_det_infer', drop_score=0.5, enable_mkldnn=False, gpu_mem=8000, image_dir='', ir_optim=True, label_list=['0', '180'], lang='ch', max_text_length=25, rec=True, rec_algorithm='CRNN', rec_batch_num=6, rec_char_dict_path='./ppocr/utils/ppocr_keys_v1.txt', rec_char_type='ch', rec_image_shape='3, 32, 320', rec_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_rec_infer', use_angle_cls=False, use_dilation=False, use_gpu=False, use_pdserving=False, use_space_char=True, use_tensorrt=False, use_zero_copy_run=False) [2021/04/17 15:42:58] root WARNING: Since the angle classifier is not initialized, the angle classifier will not be uesd during the forward process [2021/04/17 15:42:59] root INFO: dt_boxes num : 36, elapse : 0.9523482322692871 [2021/04/17 15:43:01] root INFO: rec_res num : 36, elapse : 1.8610682487487793 [[[[704.0, 17.0], [820.0, 17.0], [820.0, 57.0], [704.0, 57.0]], ('PASS', 0.9926876)], [[[158.0, 30.0], [351.0, 26.0], [352.0, 66.0], [159.0, 71.0]], ('登機牌', 0.99803185)], [[[424.0, 27.0], [653.0, 23.0], [654.0, 56.0], [425.0, 61.0]], ('BOARDING', 0.9355928)], [[[678.0, 101.0], [740.0, 100.0], [740.0, 117.0], [678.0, 119.0]], ('座位號', 0.9993448)], [[[490.0, 106.0], [648.0, 102.0], [648.0, 119.0], [491.0, 122.0]], ('序號SERIALNO', 0.9721721)], [[[397.0, 107.0], [455.0, 105.0], [455.0, 122.0], [397.0, 124.0]], ('CLASS', 0.99292123)], [[[340.0, 109.0], [384.0, 107.0], [385.0, 122.0], [341.0, 124.0]], ('艙位', 0.95519966)], [[[749.0, 100.0], [830.0, 97.0], [831.0, 114.0], [750.0, 116.0]], ('SEAT NO', 0.91536474)], [[[215.0, 111.0], [255.0, 110.0], [256.0, 125.0], [216.0, 127.0]], ('日期', 0.99764645)], [[[66.0, 114.0], [189.0, 111.0], [190.0, 128.0], [66.0, 131.0]], ('航班FLIGHT', 0.9937089)], [[[248.0, 110.0], [314.0, 110.0], [314.0, 125.0], [248.0, 125.0]], ('DATE', 0.98909116)], [[[509.0, 131.0], [566.0, 131.0], [566.0, 156.0], [509.0, 156.0]], ('035', 0.9830131)], [[[406.0, 135.0], [430.0, 135.0], [430.0, 157.0], [406.0, 157.0]], ('W', 0.99706143)], [[[84.0, 142.0], [212.0, 139.0], [213.0, 158.0], [84.0, 162.0]], ('MU 2379', 0.9289481)], [[[211.0, 141.0], [324.0, 139.0], [325.0, 156.0], [211.0, 158.0]], ('03DEG', 0.8540861)], [[[489.0, 175.0], [572.0, 173.0], [572.0, 193.0], [489.0, 195.0]], ('登機口', 0.9996843)], [[[568.0, 176.0], [612.0, 176.0], [612.0, 191.0], [568.0, 191.0]], ('GATE', 0.99684405)], [[[345.0, 177.0], [409.0, 177.0], [409.0, 194.0], [345.0, 194.0]], ('始發地', 0.9996207)], [[[400.0, 177.0], [466.0, 173.0], [467.0, 191.0], [401.0, 194.0]], ('FROM', 0.9926705)], [[[68.0, 181.0], [168.0, 178.0], [168.0, 199.0], [69.0, 203.0]], ('目的地TO', 0.98151416)], [[[680.0, 172.0], [806.0, 166.0], [807.0, 186.0], [681.0, 192.0]], ('登機時間BD1', 0.9584261)], [[[98.0, 208.0], [168.0, 208.0], [168.0, 227.0], [98.0, 227.0]], ('福州', 0.8741772)], [[[339.0, 220.0], [474.0, 216.0], [475.0, 235.0], [339.0, 239.0]], ('TAIYUAN', 0.93135375)], [[[507.0, 217.0], [552.0, 214.0], [553.0, 232.0], [508.0, 235.0]], ('G11', 0.84185356)], [[[90.0, 231.0], [200.0, 229.0], [200.0, 247.0], [90.0, 250.0]], ('FUZHOU', 0.98411864)], [[[347.0, 243.0], [481.0, 239.0], [482.0, 254.0], [347.0, 258.0]], ('身份識別IDNO', 0.9800327)], [[[67.0, 253.0], [170.0, 250.0], [170.0, 267.0], [68.0, 270.0]], ('姓名NAME', 0.9965892)], [[[77.0, 279.0], [262.0, 275.0], [262.0, 294.0], [78.0, 299.0]], ('ZHANGQIWET', 0.92218363)], [[[464.0, 299.0], [576.0, 297.0], [577.0, 314.0], [464.0, 316.0]], ('票號TKTNO', 0.9938587)], [[[103.0, 314.0], [208.0, 313.0], [208.0, 334.0], [104.0, 336.0]], ('張祺偉', 0.945729)], [[[70.0, 345.0], [164.0, 343.0], [165.0, 362.0], [71.0, 365.0]], ('票價FARE', 0.99907106)], [[[347.0, 350.0], [659.0, 349.0], [659.0, 366.0], [348.0, 367.0]], ('ETKT7813699238489/1', 0.97859323)], [[[101.0, 459.0], [829.0, 442.0], [829.0, 462.0], [102.0, 478.0]], ('登機口于起飛前10分鐘關閉GATESCLOSE10MINUTESBEFOREDEPARTURETIME', 0.9942007)]] [[[704.0, 17.0], [820.0, 17.0], [820.0, 57.0], [704.0, 57.0]], ('PASS', 0.9926876)] [[[158.0, 30.0], [351.0, 26.0], [352.0, 66.0], [159.0, 71.0]], ('登機牌', 0.99803185)] [[[424.0, 27.0], [653.0, 23.0], [654.0, 56.0], [425.0, 61.0]], ('BOARDING', 0.9355928)] [[[678.0, 101.0], [740.0, 100.0], [740.0, 117.0], [678.0, 119.0]], ('座位號', 0.9993448)] [[[490.0, 106.0], [648.0, 102.0], [648.0, 119.0], [491.0, 122.0]], ('序號SERIALNO', 0.9721721)] [[[397.0, 107.0], [455.0, 105.0], [455.0, 122.0], [397.0, 124.0]], ('CLASS', 0.99292123)] [[[340.0, 109.0], [384.0, 107.0], [385.0, 122.0], [341.0, 124.0]], ('艙位', 0.95519966)] [[[749.0, 100.0], [830.0, 97.0], [831.0, 114.0], [750.0, 116.0]], ('SEAT NO', 0.91536474)] [[[215.0, 111.0], [255.0, 110.0], [256.0, 125.0], [216.0, 127.0]], ('日期', 0.99764645)] [[[66.0, 114.0], [189.0, 111.0], [190.0, 128.0], [66.0, 131.0]], ('航班FLIGHT', 0.9937089)] [[[248.0, 110.0], [314.0, 110.0], [314.0, 125.0], [248.0, 125.0]], ('DATE', 0.98909116)] [[[509.0, 131.0], [566.0, 131.0], [566.0, 156.0], [509.0, 156.0]], ('035', 0.9830131)] [[[406.0, 135.0], [430.0, 135.0], [430.0, 157.0], [406.0, 157.0]], ('W', 0.99706143)] [[[84.0, 142.0], [212.0, 139.0], [213.0, 158.0], [84.0, 162.0]], ('MU 2379', 0.9289481)] [[[211.0, 141.0], [324.0, 139.0], [325.0, 156.0], [211.0, 158.0]], ('03DEG', 0.8540861)] [[[489.0, 175.0], [572.0, 173.0], [572.0, 193.0], [489.0, 195.0]], ('登機口', 0.9996843)] [[[568.0, 176.0], [612.0, 176.0], [612.0, 191.0], [568.0, 191.0]], ('GATE', 0.99684405)] [[[345.0, 177.0], [409.0, 177.0], [409.0, 194.0], [345.0, 194.0]], ('始發地', 0.9996207)] [[[400.0, 177.0], [466.0, 173.0], [467.0, 191.0], [401.0, 194.0]], ('FROM', 0.9926705)] [[[68.0, 181.0], [168.0, 178.0], [168.0, 199.0], [69.0, 203.0]], ('目的地TO', 0.98151416)] [[[680.0, 172.0], [806.0, 166.0], [807.0, 186.0], [681.0, 192.0]], ('登機時間BD1', 0.9584261)] [[[98.0, 208.0], [168.0, 208.0], [168.0, 227.0], [98.0, 227.0]], ('福州', 0.8741772)] [[[339.0, 220.0], [474.0, 216.0], [475.0, 235.0], [339.0, 239.0]], ('TAIYUAN', 0.93135375)] [[[507.0, 217.0], [552.0, 214.0], [553.0, 232.0], [508.0, 235.0]], ('G11', 0.84185356)] [[[90.0, 231.0], [200.0, 229.0], [200.0, 247.0], [90.0, 250.0]], ('FUZHOU', 0.98411864)] [[[347.0, 243.0], [481.0, 239.0], [482.0, 254.0], [347.0, 258.0]], ('身份識別IDNO', 0.9800327)] [[[67.0, 253.0], [170.0, 250.0], [170.0, 267.0], [68.0, 270.0]], ('姓名NAME', 0.9965892)] [[[77.0, 279.0], [262.0, 275.0], [262.0, 294.0], [78.0, 299.0]], ('ZHANGQIWET', 0.92218363)] [[[464.0, 299.0], [576.0, 297.0], [577.0, 314.0], [464.0, 316.0]], ('票號TKTNO', 0.9938587)] [[[103.0, 314.0], [208.0, 313.0], [208.0, 334.0], [104.0, 336.0]], ('張祺偉', 0.945729)] [[[70.0, 345.0], [164.0, 343.0], [165.0, 362.0], [71.0, 365.0]], ('票價FARE', 0.99907106)] [[[347.0, 350.0], [659.0, 349.0], [659.0, 366.0], [348.0, 367.0]], ('ETKT7813699238489/1', 0.97859323)] [[[101.0, 459.0], [829.0, 442.0], [829.0, 462.0], [102.0, 478.0]], ('登機口于起飛前10分鐘關閉GATESCLOSE10MINUTESBEFOREDEPARTURETIME', 0.9942007)]Process finished with exit code 0圖片
二、但是識別驗證碼,就是碎了一地的效果,
"D:\Program Files\Python36\python.exe" D:/nanodet-main/test.py Namespace(cls_batch_num=6, cls_image_shape='3, 48, 192', cls_model_dir='C:\\Users\\sxj/.paddleocr/2.1/cls', cls_thresh=0, det=True, det_algorithm='DB', det_db_box_thresh=0.5, det_db_thresh=0.3, det_db_unclip_ratio=1.6, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_east_score_thresh=0.8, det_limit_side_len=960, det_limit_type='max', det_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_det_infer', drop_score=0.5, enable_mkldnn=False, gpu_mem=8000, image_dir='', ir_optim=True, label_list=['0', '180'], lang='ch', max_text_length=25, rec=True, rec_algorithm='CRNN', rec_batch_num=6, rec_char_dict_path='./ppocr/utils/ppocr_keys_v1.txt', rec_char_type='ch', rec_image_shape='3, 32, 320', rec_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_rec_infer', use_angle_cls=False, use_dilation=False, use_gpu=False, use_pdserving=False, use_space_char=True, use_tensorrt=False, use_zero_copy_run=False) [2021/04/17 14:57:31] root WARNING: Since the angle classifier is not initialized, the angle classifier will not be uesd during the forward process [2021/04/17 14:57:31] root INFO: dt_boxes num : 1, elapse : 0.5834400653839111 [2021/04/17 14:57:31] root INFO: rec_res num ?: 1, elapse : 0.03889632225036621 []Process finished with exit code 0三、如果圖稍微截取的大一點,就可以識別,但是識別的存在錯誤
"D:\Program Files\Python36\python.exe" D:/nanodet-main/test.py Namespace(cls_batch_num=6, cls_image_shape='3, 48, 192', cls_model_dir='C:\\Users\\sxj/.paddleocr/2.1/cls', cls_thresh=0.9, det=True, det_algorithm='DB', det_db_box_thresh=0.5, det_db_thresh=0.3, det_db_unclip_ratio=1.6, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_east_score_thresh=0.8, det_limit_side_len=960, det_limit_type='max', det_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_det_infer', drop_score=0.5, enable_mkldnn=False, gpu_mem=8000, image_dir='', ir_optim=True, label_list=['0', '180'], lang='ch', max_text_length=25, rec=True, rec_algorithm='CRNN', rec_batch_num=6, rec_char_dict_path='./ppocr/utils/ppocr_keys_v1.txt', rec_char_type='ch', rec_image_shape='3, 32, 320', rec_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_rec_infer', use_angle_cls=False, use_dilation=False, use_gpu=False, use_pdserving=False, use_space_char=True, use_tensorrt=False, use_zero_copy_run=False) [2021/04/17 15:50:32] root WARNING: Since the angle classifier is not initialized, the angle classifier will not be uesd during the forward process [2021/04/17 15:50:32] root INFO: dt_boxes num : 1, elapse : 0.6196780204772949 [2021/04/17 15:50:32] root INFO: rec_res num : 1, elapse : 0.02003645896911621 [[[[340.0, 269.0], [432.0, 272.0], [432.0, 302.0], [339.0, 299.0]], ('2789', 0.8799714)]] [[[340.0, 269.0], [432.0, 272.0], [432.0, 302.0], [339.0, 299.0]], ('2789', 0.8799714)]Process finished with exit code 0四、做一下處理,首先參考了代碼,進行修改。Python驗證碼識別 - 老板丶魚丸粗面 - 博客園
from PIL import Image from pytesseract import * from fnmatch import fnmatch from queue import Queue import cv2 import os def clear_border(img,img_name):'''去除邊框'''h, w = img.shape[:2]for y in range(0, w):for x in range(0, h):# if y ==0 or y == w -1 or y == w - 2:if y < 4 or y > w -4:img[x, y] = 255# if x == 0 or x == h - 1 or x == h - 2:if x < 4 or x > h - 4:img[x, y] = 255return imgdef interference_line(img, img_name):'''干擾線降噪'''h, w = img.shape[:2]# !!!opencv矩陣點是反的# img[1,2] 1:圖片的高度,2:圖片的寬度for y in range(1, w - 1):for x in range(1, h - 1):count = 0if img[x, y - 1] > 245:count = count + 1if img[x, y + 1] > 245:count = count + 1if img[x - 1, y] > 245:count = count + 1if img[x + 1, y] > 245:count = count + 1if count > 2:img[x, y] = 255return imgdef interference_point(img,img_name, x = 0, y = 0):"""點降噪9鄰域框,以當前點為中心的田字框,黑點個數:param x::param y::return:"""# 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] = 0elif 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] = 0else: # 最上非頂點,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] = 0elif 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] = 0elif 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] = 0else: # 最下非頂點,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] = 0else: # 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] = 0elif 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] = 0else: # 具備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] = 0return imgdef _get_dynamic_binary_image(filedir, img_name):'''自適應閥值二值化'''img_name =os.path.join(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)return th1def _get_static_binary_image(img, threshold = 140):'''手動二值化'''img = Image.open(img)img = img.convert('L')pixdata = img.load()w, h = img.sizefor y in range(h):for x in range(w):if pixdata[x, y] < threshold:pixdata[x, y] = 0else:pixdata[x, y] = 255return imgdef 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,y=q.get()for xoffset,yoffset in offsets:x_neighbor,y_neighbor = x+xoffset,y+yoffsetif (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 + 1xmin = x_fdymax = y_fd + 1ymin = y_fdelse:xmax = max(xaxis)xmin = min(xaxis)ymax = max(yaxis)ymin = min(yaxis)#ymin,ymax=sort(yaxis)return ymax,ymin,xmax,xmindef detectFgPix(im,xmax):'''搜索區塊起點'''h,w = im.shape[:2]for y_fd in range(xmax+1,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軸[起始,終點]列表xmax=0#上一區塊結束黑點橫坐標,這里是初始化for i in range(10):try:x_fd,y_fd = detectFgPix(im,xmax)# print(y_fd,x_fd)xmax,xmin,ymax,ymin=cfs(im,x_fd,y_fd)L = xmax - xminH = ymax - yminzoneL.append(L)zoneWB.append([xmin,xmax])zoneHB.append([ymin,ymax])except TypeError:return zoneL,zoneWB,zoneHBreturn zoneL,zoneWB,zoneHBdef cutting_img(destdir,im,im_position,img,xoffset = 1,yoffset = 1):filename = os.path.join(destdir,img.split('.')[0])# 識別出的字符個數im_number = len(im_position[1])# 切割字符img = Image.new('RGB', (100, 100), (0, 0, 0))pre_im_start_y=im_position[2][0][0]- yoffsetpre_im_end_Y =im_position[2][0][1]+ yoffsetfor i in range(im_number):im_start_X = im_position[1][i][0] - xoffsetim_end_X = im_position[1][i][1] + xoffsetim_start_Y = min(im_position[2][i][0] - yoffset,pre_im_start_y)im_end_Y = max(im_position[2][i][1] + yoffset,pre_im_end_Y)#print(im_start_Y," ",im_end_Y," ", im_start_X," ",im_end_X)pre_im_start_y=im_position[2][i][0] - yoffsetpre_im_end_Y=im_position[2][i][1] + yoffsetcropped = im[im_start_Y:im_end_Y, im_start_X:im_end_X]cropped = cv2.resize(cropped, (0, 0), fx=2, fy=2, interpolation=cv2.INTER_CUBIC)cropped = cv2.bitwise_not(cropped)#腐蝕圖片進行處理,paddle 檢測效果更好cv2.imwrite(filename + str(i) + '.jpg', cropped)img1 = Image.open(filename + str(i) + '.jpg')img.paste(img1, (int(35), int(35), int(35) + img1.size[0], int(35) + img1.size[1]))img.save(filename + str(i) + '.jpg')img1.close()return im_numberdef preprocess(desdir):for item in os.listdir(desdir):img = cv2.imread(os.path.join(desdir,item))hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)erode = cv2.erode(hsv, None, iterations=1)dilate = cv2.dilate(erode, None, iterations=1)dilate = cv2.cvtColor(dilate, cv2.COLOR_HSV2RGB)cv2.imwrite(os.path.join(desdir,item), dilate)def main():filedir = r'F:\tt\source'destdir=r'F:\tt\destination'for file in os.listdir(filedir):if fnmatch(file, '*.png') or fnmatch(file, '*.jpg'):img_name = file# 自適應閾值二值化im = _get_dynamic_binary_image(filedir, img_name)# 去除邊框im = clear_border(im,img_name)# 對圖片進行干擾線降噪im = interference_line(im,img_name)# 對圖片進行點降噪im = interference_point(im,img_name)# 切割的位置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 // 2im_position[0].insert(maxL_index + 1, maxL // 2)# 設置字符X軸[起始,終點]位置im_position[1][maxL_index][1] = im_position[1][maxL_index][0] + maxL // 2im_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_num=cutting_img(destdir,im,im_position,img_name,1,1)preprocess(destdir)print('切圖:%s' % cutting_img_num)if __name__ == '__main__':main()處理結果,
? ????? ??? ??? ?? ??
檢測效果仍然很差 運算符檢測不出來~~~~mmp,怎么辦 怎么辦?真的懶得去訓練。。。我在思考 paddleOCR如果是藍底白字是不是效果會變好呢?試試吧~~~
from PIL import Image import numpy as np from fnmatch import fnmatch from queue import Queue import cv2 import os from paddleocr import PaddleOCR, draw_ocrdef clear_border(img,img_name):'''去除邊框'''h, w = img.shape[:2]for y in range(0, w):for x in range(0, h):# if y ==0 or y == w -1 or y == w - 2:if y < 4 or y > w -4:img[x, y] = 255# if x == 0 or x == h - 1 or x == h - 2:if x < 4 or x > h - 4:img[x, y] = 255return imgdef interference_line(img, img_name):'''干擾線降噪'''h, w = img.shape[:2]# !!!opencv矩陣點是反的# img[1,2] 1:圖片的高度,2:圖片的寬度for y in range(1, w - 1):for x in range(1, h - 1):count = 0if img[x, y - 1] > 245:count = count + 1if img[x, y + 1] > 245:count = count + 1if img[x - 1, y] > 245:count = count + 1if img[x + 1, y] > 245:count = count + 1if count > 2:img[x, y] = 255return imgdef interference_point(img,img_name, x = 0, y = 0):"""點降噪9鄰域框,以當前點為中心的田字框,黑點個數:param x::param y::return:"""# 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] = 0elif 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] = 0else: # 最上非頂點,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] = 0elif 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] = 0elif 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] = 0else: # 最下非頂點,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] = 0else: # 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] = 0elif 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] = 0else: # 具備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] = 0return imgdef _get_dynamic_binary_image(filedir, img_name):'''自適應閥值二值化'''img_name =os.path.join(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)return th1def _get_static_binary_image(img, threshold = 140):'''手動二值化'''img = Image.open(img)img = img.convert('L')pixdata = img.load()w, h = img.sizefor y in range(h):for x in range(w):if pixdata[x, y] < threshold:pixdata[x, y] = 0else:pixdata[x, y] = 255return imgdef 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,y=q.get()for xoffset,yoffset in offsets:x_neighbor,y_neighbor = x+xoffset,y+yoffsetif (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 + 1xmin = x_fdymax = y_fd + 1ymin = y_fdelse:xmax = max(xaxis)xmin = min(xaxis)ymax = max(yaxis)ymin = min(yaxis)#ymin,ymax=sort(yaxis)return ymax,ymin,xmax,xmindef detectFgPix(im,xmax):'''搜索區塊起點'''h,w = im.shape[:2]for y_fd in range(xmax+1,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軸[起始,終點]列表xmax=0#上一區塊結束黑點橫坐標,這里是初始化for i in range(10):try:x_fd,y_fd = detectFgPix(im,xmax)# print(y_fd,x_fd)xmax,xmin,ymax,ymin=cfs(im,x_fd,y_fd)L = xmax - xminH = ymax - yminzoneL.append(L)zoneWB.append([xmin,xmax])zoneHB.append([ymin,ymax])except TypeError:return zoneL,zoneWB,zoneHBreturn zoneL,zoneWB,zoneHBdef cutting_img(destdir,im,im_position,img,xoffset = 1,yoffset = 1):filename = os.path.join(destdir,img.split('.')[0])# 識別出的字符個數im_number = len(im_position[1])# 切割字符img = Image.new('RGB', (100, 100), (0, 0, 0))pre_im_start_y=im_position[2][0][0]- yoffsetpre_im_end_Y =im_position[2][0][1]+ yoffsetfor i in range(im_number):im_start_X = im_position[1][i][0] - xoffsetim_end_X = im_position[1][i][1] + xoffsetim_start_Y = min(im_position[2][i][0] - yoffset,pre_im_start_y)im_end_Y = max(im_position[2][i][1] + yoffset,pre_im_end_Y)#print(im_start_Y," ",im_end_Y," ", im_start_X," ",im_end_X)pre_im_start_y=im_position[2][i][0] - yoffsetpre_im_end_Y=im_position[2][i][1] + yoffsetcropped = im[im_start_Y:im_end_Y, im_start_X:im_end_X]cropped = cv2.resize(cropped, (0, 0), fx=2, fy=2, interpolation=cv2.INTER_CUBIC)cropped = cv2.bitwise_not(cropped)#cropped = cv2.medianBlur(cropped, 5)# 中值濾波#腐蝕圖片進行處理,paddle 檢測效果更好cv2.imwrite(filename + str(i) + '.jpg', cropped)img1 = Image.open(filename + str(i) + '.jpg')img.paste(img1, (int(35), int(35), int(35) + img1.size[0], int(35) + img1.size[1]))img.save(filename + str(i) + '.jpg')img1.close()return im_number#R:5;G:39;B:175 def image2label(dilate):data = np.array(dilate, dtype='int32')# 修改B通道的像素值for i in range(data[:, :, 0].shape[0]):for j in range(data[:, :, 0].shape[1]):if data[:, :, 0][i][j] > 155:data[:, :, 0][i][j] = 255else:data[:, :, 0][i][j] = 175# 修改G通道的像素值for i in range(data[:, :, 1].shape[0]):for j in range(data[:, :, 1].shape[1]):if data[:, :, 1][i][j] > 155:data[:, :, 1][i][j] = 255else:data[:, :, 1][i][j] = 39# 修改R通道的像素值for i in range(data[:, :, 2].shape[0]):for j in range(data[:, :, 2].shape[1]):if data[:, :, 2][i][j] > 155:data[:, :, 2][i][j] = 255else:data[:, :, 2][i][j] = 5return datadef preprocess(desdir):for item in os.listdir(desdir):img = cv2.imread(os.path.join(desdir,item))hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)erode = cv2.erode(hsv, None, iterations=1)#開運算dilate = cv2.dilate(erode, None, iterations=1)#閉運算dilate = cv2.cvtColor(dilate, cv2.COLOR_HSV2RGB)dilate=image2label(dilate)cv2.imwrite(os.path.join(desdir,item), dilate)def main():filedir = r'F:\tt\source'destdir=r'F:\tt\destination'ocr = PaddleOCR(use_angle_cls=True, cls_thresh=0, det_east_score_thresh=0, use_gpu=False,det_model_dir=r'F:\tt\ch_ppocr_mobile_v2.0_det_infer',rec_model_dir=r'F:\tt\ch_ppocr_mobile_v2.0_rec_infer') # need to run only once to download and load model into memoryfor file in os.listdir(filedir):if fnmatch(file, '*.png') or fnmatch(file, '*.jpg'):img_name = file# 自適應閾值二值化im = _get_dynamic_binary_image(filedir, img_name)# 去除邊框im = clear_border(im,img_name)# 對圖片進行干擾線降噪im = interference_line(im,img_name)# 對圖片進行點降噪im = interference_point(im,img_name)# 切割的位置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 // 2im_position[0].insert(maxL_index + 1, maxL // 2)# 設置字符X軸[起始,終點]位置im_position[1][maxL_index][1] = im_position[1][maxL_index][0] + maxL // 2im_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_num=cutting_img(destdir,im,im_position,img_name,1,1)preprocess(destdir)for item in os.listdir(destdir):filename=os.path.join(destdir,item)result = ocr.ocr(filename, cls=True)for line in result:print(line[-1][0])if __name__ == '__main__':main()產生的圖片為
? ?? ???
果不其然 可以識別了 符號和數字了 ~~
0
"D:\Program Files\Python36\python.exe" D:/nanodet-main/test.py Namespace(cls_batch_num=6, cls_image_shape='3, 48, 192', cls_model_dir='C:\\Users\\sxj/.paddleocr/2.1/cls', cls_thresh=0, det=True, det_algorithm='DB', det_db_box_thresh=0.5, det_db_thresh=0.3, det_db_unclip_ratio=1.6, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_east_score_thresh=0, det_limit_side_len=960, det_limit_type='max', det_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_det_infer', drop_score=0.5, enable_mkldnn=False, gpu_mem=8000, image_dir='', ir_optim=True, label_list=['0', '180'], lang='ch', max_text_length=25, rec=True, rec_algorithm='CRNN', rec_batch_num=6, rec_char_dict_path='./ppocr/utils/ppocr_keys_v1.txt', rec_char_type='ch', rec_image_shape='3, 32, 320', rec_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_rec_infer', use_angle_cls=True, use_dilation=False, use_gpu=False, use_pdserving=False, use_space_char=True, use_tensorrt=False, use_zero_copy_run=False) [2021/04/18 09:40:36] root INFO: dt_boxes num : 1, elapse : 0.4519474506378174 [2021/04/18 09:40:36] root INFO: cls num : 1, elapse : 0.01795220375061035 [2021/04/18 09:40:36] root INFO: rec_res num : 1, elapse : 0.006981849670410156 [[[[39.0, 38.0], [71.0, 40.0], [69.0, 79.0], [37.0, 77.0]], ('0', 0.7298239)]] [[[39.0, 38.0], [71.0, 40.0], [69.0, 79.0], [37.0, 77.0]], ('0', 0.7298239)]Process finished with exit code 0+
"D:\Program Files\Python36\python.exe" D:/nanodet-main/test.py Namespace(cls_batch_num=6, cls_image_shape='3, 48, 192', cls_model_dir='C:\\Users\\sxj/.paddleocr/2.1/cls', cls_thresh=0, det=True, det_algorithm='DB', det_db_box_thresh=0.5, det_db_thresh=0.3, det_db_unclip_ratio=1.6, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_east_score_thresh=0, det_limit_side_len=960, det_limit_type='max', det_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_det_infer', drop_score=0.5, enable_mkldnn=False, gpu_mem=8000, image_dir='', ir_optim=True, label_list=['0', '180'], lang='ch', max_text_length=25, rec=True, rec_algorithm='CRNN', rec_batch_num=6, rec_char_dict_path='./ppocr/utils/ppocr_keys_v1.txt', rec_char_type='ch', rec_image_shape='3, 32, 320', rec_model_dir='F:\\tt\\ch_ppocr_mobile_v2.0_rec_infer', use_angle_cls=True, use_dilation=False, use_gpu=False, use_pdserving=False, use_space_char=True, use_tensorrt=False, use_zero_copy_run=False) [2021/04/18 09:37:24] root INFO: dt_boxes num : 1, elapse : 0.42177367210388184 [2021/04/18 09:37:24] root INFO: cls num : 1, elapse : 0.015623092651367188 [2021/04/18 09:37:24] root INFO: rec_res num : 1, elapse : 0.015619516372680664 [[[[37.0, 46.0], [71.0, 47.0], [70.0, 78.0], [36.0, 77.0]], ('十', 0.72421145)]] [[[37.0, 46.0], [71.0, 47.0], [70.0, 78.0], [36.0, 77.0]], ('十', 0.72421145)]Process finished with exit code 0但是 仍然存在一些識別錯誤的問題~~~? 心塞, 進步調查原因,難道是沒有檢測到嗎??? 換了服務器版本的模型也不可以。。。看樣子無藥可救了
? ??? ?? ??
??
六、首先先爬蟲點數據集吧~,
? ? ? (1)查看一下自己的google瀏覽器的版本號
? ??
然后下載https://sites.google.com/a/chromium.org/chromedriver/downloads?,放入F:\\EXEDIR\\
因為下載google刷新網頁驅動需要翻墻下載,請自行下載,let's go 爬驗證碼圖片吧~~
import urllib.request import time import os from selenium import webdriverdef refresh(exedir,url,second):driver = webdriver.Chrome(os.path.join(exedir,"chromedriver.exe"))driver.get(url)time.sleep(second)driver.refresh()driver.close()def getHtml(url):html = urllib.request.urlopen(url).read()return htmldef saveHtml(destdir, file_content,i):# 注意windows文件命名的禁用符,比如 /with open(os.path.join(destdir,".".join([str(i),"jpg"])), "wb") as f:# 寫文件用bytes而不是str,所以要轉碼f.write(file_content) destdir="F:\\TESTIMG" exedir="F:\\EXEDIR" second=1 num=10000 for i in range(num):aurl = "https://******.cn/m/tmri/captcha/math?nocache=1618204715553"html = getHtml(aurl)saveHtml(destdir, html,i)refresh(exedir,aurl,second) print("下載成功")下載成功的樣子大概這樣,有500張吧
直接標注原圖吧,不分割單個字符標注在訓練了(也可以簡單的處理一下)
from fnmatch import fnmatch import cv2 import osdef _get_dynamic_binary_image(img_name):print('.....' + img_name)im = cv2.imread(img_name)frame = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)height = frame.shape[0]weight = frame.shape[1]print("weight : %s, height : %s" % (weight, height))for row in range(height): # 遍歷高for col in range(weight): # 遍歷寬pv = frame[row, col]# print(pv)if pv > 100:frame[row, col] = 255return frame def clear_border(img):'''去除邊框'''h, w = img.shape[:2]for y in range(0, w):for x in range(0, h):# if y ==0 or y == w -1 or y == w - 2:if y < 4 or y > w -4:img[x, y] = 255# if x == 0 or x == h - 1 or x == h - 2:if x < 4 or x > h - 4:img[x, y] = 255return imgdef main():filedir = r'F:\tt\source'destdir = r'F:\tt\destination'for file in os.listdir(filedir):if fnmatch(file, '*.png') or fnmatch(file, '*.jpg'):img_name = os.path.join(filedir, file)img = _get_dynamic_binary_image(img_name)img_out=clear_border(img)cv2.imwrite(os.path.join(destdir,file),img_out)if __name__ == '__main__':main()訓練自己的paddleOCR數據,這里按照灰度圖進行訓練吧,先下載源碼進行數據標注,標注仍然以切分數字圖片和運算符為主,色彩隨機設置,以豐富數據集~(注:本項目的某一部分僅需要這樣的功能,其它需求暫不考慮)
ubuntu@ubuntu:~$ git clone https://github.com/PaddlePaddle/PaddleOCR ubuntu@ubuntu:~$ cd PaddleOCR ubuntu@ubuntu:~$ pip install -r requirements.txt ubuntu@ubuntu:~PaddleOCR$ cd PPOCRLabel ubuntu@ubuntu:~PaddleOCR/PPOCRLabel$ python PPOCRLabel.py --lang ch未完待續
總結
以上是生活随笔為你收集整理的25、使用Baidu的paddle自动进行验证码的识别、并计算验证码的数值的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS中的nth-child和nth-o
- 下一篇: 二级菜单选中,一级菜单背景变换