代码1:
# -*-coding:utf-8-*-
import os
def test(path):
img = Image.open(path)
w, h = img.size
for x in range(w):
for y in range(h):
r, g, b = img.getpixel((x, y))
if 190 <= r <= 255 and 170 <= g <= 255 and 0 <= b <= 140:
img.putpixel((x, y), (0, 0, 0))
if 0 <= r <= 90 and 210 <= g <= 255 and 0 <= b <= 90:
img.putpixel((x, y), (0, 0, 0))
img = img.convert('L').point([0] * 150 + [1] * (256 - 150), '1')
return img
# -*-coding:utf-8-*-
# coding:utf-8
from PIL import Image, ImageDraw
# 二值数组
t2val = {}
def twoValue(image, G):
for y in range(0, image.size[1]):
for x in range(0, image.size[0]):
g = image.getpixel((x, y))
if g > G:
t2val[(x, y)] = 1
else:
t2val[(x, y)] = 0
# 根据一个点A的RGB值,与周围的8个点的RBG值比较,设定一个值N(0 <N <8),当A的RGB值与周围8个点的RGB相等数小于N时,此点为噪点
# G: Integer 图像二值化阀值
# N: Integer 降噪率 0 <N <8
# Z: Integer 降噪次数
# 输出
# 0:降噪成功
# 1:降噪失败
def clearNoise(image, N, Z):
for i in range(0, Z):
t2val[(0, 0)] = 1
t2val[(image.size[0] - 1, image.size[1] - 1)] = 1
for x in range(1, image.size[0] - 1):
for y in range(1, image.size[1] - 1):
nearDots = 0
L = t2val[(x, y)]
if L == t2val[(x - 1, y - 1)]:
nearDots += 1
if L == t2val[(x - 1, y)]:
nearDots += 1
if L == t2val[(x - 1, y + 1)]:
nearDots += 1
if L == t2val[(x, y - 1)]:
nearDots += 1
if L == t2val[(x, y + 1)]:
nearDots += 1
if L == t2val[(x + 1, y - 1)]:
nearDots += 1
if L == t2val[(x + 1, y)]:
nearDots += 1
if L == t2val[(x + 1, y + 1)]:
nearDots += 1
if nearDots < N:
t2val[(x, y)] = 1
def saveImage(filename, size):
image = Image.new("1", size)
draw = ImageDraw.Draw(image)
for x in range(0, size[0]):
for y in range(0, size[1]):
draw.point((x, y), t2val[(x, y)])
image.save(filename)
# -*-coding:utf-8-*-
from PIL import Image
import pytesseract
def recognize_captcha(img_path):
im = Image.open(img_path)
# threshold = 140
# table = []
# for i in range(256):
# if i < threshold:
# table.append(0)
# else:
# table.append(1)
#
# out = im.point(table, '1')
num = pytesseract.image_to_string(im)
return num
if __name__ == '__main__':
dir = '../download/'
# dir = './image/'
# dir = './'
correct_count = 0 # 图片总数
total_count = 0 # 识别正确的图片数量
# 遍历figures下的png,jpg文件
for file in os.listdir(dir):
if file.endswith('.png') or file.endswith('.jpg'):
img_path = '%s/%s' % (dir, file) # 图片路径
res = recognize_captcha(img_path)
strs = res.split("\n")
if len(strs) >= 1:
print(file + ":" + strs[0])
代码2:
import os
import pytesseract
from PIL import Image
from collections import defaultdict
# tesseract.exe所在的文件路径
pytesseract.pytesseract.tesseract_cmd = 'G://tesseract/tesseract.exe'
# 获取图片中像素点数量最多的像素
def get_threshold(image):
pixel_dict = defaultdict(int)
# 像素及该像素出现次数的字典
rows, cols = image.size
for i in range(rows):
for j in range(cols):
pixel = image.getpixel((i, j))
pixel_dict[pixel] += 1
count_max = max(pixel_dict.values()) # 获取像素出现出多的次数
pixel_dict_reverse = {v: k for k, v in pixel_dict.items()}
threshold = pixel_dict_reverse[count_max] # 获取出现次数最多的像素点
return threshold
# 按照阈值进行二值化处理
# threshold: 像素阈值
def get_bin_table(threshold):
# 获取灰度转二值的映射table
table = []
for i in range(256):
rate = 0.1 # 在threshold的适当范围内进行处理
if threshold * (1 - rate) <= i <= threshold * (1 + rate):
table.append(1)
else:
table.append(0)
return table
# 去掉二值化处理后的图片中的噪声点
def cut_noise(image):
rows, cols = image.size # 图片的宽度和高度
change_pos = [] # 记录噪声点位置
# 遍历图片中的每个点,除掉边缘
for i in range(1, rows - 1):
for j in range(1, cols - 1):
# pixel_set用来记录该店附近的黑色像素的数量
pixel_set = []
# 取该点的邻域为以该点为中心的九宫格
for m in range(i - 1, i + 2):
for n in range(j - 1, j + 2):
if image.getpixel((m, n)) != 1: # 1为白色,0位黑色
pixel_set.append(image.getpixel((m, n)))
# 如果该位置的九宫内的黑色数量小于等于4,则判断为噪声
if len(pixel_set) <= 4:
change_pos.append((i, j))
# 对相应位置进行像素修改,将噪声处的像素置为1(白色)
for pos in change_pos:
image.putpixel(pos, 1)
return image # 返回修改后的图片
# 识别图片中的数字加字母
# 传入参数为图片路径,返回结果为:识别结果
def OCR_lmj(img_path):
image = Image.open(img_path) # 打开图片文件
imgry = image.convert('L') # 转化为灰度图
# 获取图片中的出现次数最多的像素,即为该图片的背景
max_pixel = get_threshold(imgry)
# 将图片进行二值化处理
table = get_bin_table(threshold=max_pixel)
out = imgry.point(table, '1')
# 去掉图片中的噪声(孤立点)
out = cut_noise(out)
# 保存图片
# out.save('E://figures/img_gray.jpg')
# 仅识别图片中的数字
# text = pytesseract.image_to_string(out, config='digits')
# 识别图片中的数字和字母
text = pytesseract.image_to_string(out)
# 去掉识别结果中的特殊字符
exclude_char_list = ' .:\\|\'\"?![],()~@#$%^&*_+-={};<>/¥'
text = ''.join([x for x in text if x not in exclude_char_list])
# print(text)
return text
def main():
# 识别指定文件目录下的图片
# 图片存放目录figures
dir = '../download/'
# dir = './image/'
# dir = './'
correct_count = 0 # 图片总数
total_count = 0 # 识别正确的图片数量
# 遍历figures下的png,jpg文件
for file in os.listdir(dir):
if file.endswith('.png') or file.endswith('.jpg'):
# print(file)
image_path = '%s/%s' % (dir, file) # 图片路径
answer = file.split('.')[0] # 图片名称,即图片中的正确文字
recognizition = OCR_lmj(image_path) # 图片识别的文字结果
print((answer, recognizition))
if recognizition == answer: # 如果识别结果正确,则total_count加1
correct_count += 1
total_count += 1
print('Total count: %d, correct: %d.' % (total_count, correct_count))
'''
# 单张图片识别
image_path = 'E://figures/code (1).jpg'
OCR_lmj(image_path)
'''
main()
代码3:
from PIL import ImageDraw, Image
from pytesseract import pytesseract
def getPixel(image, x, y):
L = image.getpixel((x, y)) # 获取当前像素点的像素
if L == 0: # 判读此像素点是否为黑,因为如果是白的就没必要处理了
nearDots = 0 # 初始化记录周围有没有黑像素数量的值
# 判断周围像素点
if L - image.getpixel((x - 1, y - 1)):
nearDots += 1
if L - image.getpixel((x - 1, y)):
nearDots += 1
if L - image.getpixel((x - 1, y + 1)):
nearDots += 1
if L - image.getpixel((x, y - 1)):
nearDots += 1
if L - image.getpixel((x, y + 1)):
nearDots += 1
if L - image.getpixel((x + 1, y - 1)):
nearDots += 1
if L - image.getpixel((x + 1, y)):
nearDots += 1
if L - image.getpixel((x + 1, y + 1)):
nearDots += 1
if nearDots == 8: # 这里如果周围八个全是白点那么就返回一个白点,实现去黑点的操作
return 1
# 这里主要是有俩个黑点连在一起,所有周围会有七个黑点扩大范围进一步判断
elif nearDots == 7:
nearDots = 0
if L - image.getpixel((x - 2, y - 2)):
nearDots += 1
if L - image.getpixel((x - 2, y)):
nearDots += 1
if L - image.getpixel((x - 2, y + 2)):
nearDots += 1
if L - image.getpixel((x, y - 2)):
nearDots += 1
if L - image.getpixel((x, y + 2)):
nearDots += 1
if L - image.getpixel((x + 2, y - 2)):
nearDots += 1
if L - image.getpixel((x + 2, y)):
nearDots += 1
if L - image.getpixel((x + 2, y + 2)):
nearDots += 1
if nearDots == 8:
return 1 # 返回白点
else:
return 0 # 返回黑点
else:
return 1
def clearNoise(image):
draw = ImageDraw.Draw(image)
# 循环遍历每个像素点
for x in range(0, image.size[0]):
for y in range(0, image.size[1]):
color = getPixel(image, x, y)
draw.point((x, y), color)
return image
im = Image.open('../download/32.jpg') # 用pil打开这个图片
im = im.convert('L')
# im = im.point(lambda x: 0 if x<50 else 255)
im = im.point(lambda x: 0 if x < 142 else x >= 142, '1')
# im = im.point(lambda x: 0 if x < 143 else 255)
im.show()
# 将上一步处理完成的im对象传给clearNoise()函数
im = clearNoise(im)
im.show()
# lang只用哪个库来识别 默认有个eng库,config 指代识别单行还是多行-psm 7只的是单行
result = pytesseract.image_to_string(im, lang='eng', config="--psm 10")
print(result)
代码4:
import os
from fnmatch import fnmatch
from queue import Queue
import cv2
from PIL import Image
from pytesseract import *
import subprocess
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 ==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] = 255
cv2.imwrite(filename, img)
return img
def interference_line(img, img_name):
'''
干扰线降噪
'''
filename = './out_img/' + img_name.split('.')[0] + '-interferenceline.jpg'
h, w = img.shape[:2]
# !!!opencv矩阵点是反的
# img[1,2] 1:图片的高度,2:图片的宽度
for y in range(1, w - 2):
for x in range(1, h - 2):
count = 0
if img[x, y - 1] > 245 or img[x, y - 2] > 245:
count = count + 1
if img[x, y + 1] > 245 or img[x, y + 2] > 245:
count = count + 1
if img[x - 1, y] > 245 or img[x - 2, y] > 245:
count = count + 1
if img[x + 1, y] > 245 or img[x + 1, y] > 245:
count = count + 1
if count > 2:
img[x, y] = 255
cv2.imwrite(filename, img)
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
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
def _get_static_binary_image(img, threshold=140):
'''
手动二值化
'''
img = Image.open(img)
img = img.convert('L')
pixdata = img.load()
w, h = img.size
for y in range(h):
for x in range(w):
if pixdata[x, y] < threshold:
pixdata[x, y] = 0
else:
pixdata[x, y] = 255
return img
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, y = q.get()
for xoffset, yoffset in offsets:
x_neighbor, y_neighbor = x + xoffset, y + yoffset
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,ymax=sort(yaxis)
return ymax, ymin, xmax, xmin
def 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_fd
def 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 - 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
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)
def main():
filedir = '../download/'
for file in os.listdir(filedir):
if 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_line(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 // 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)
# 识别验证码
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)
if __name__ == '__main__':
main()
这些验证码都有一个相同思路:
- 灰度处理
- 二值化
- 降噪(其中最核心的就是降噪算法,通过对 周围点的判断来处理是否消除这个点,但是如果这个噪声点比较大会导致误判)
- 切割字符或者倾斜度矫正(可以有)
- 训练字体库(可以有)
- 识别
参考:
https://www.cnblogs.com/qqandfqr/p/7866650.html