前言:目前市场上滑块的种类众多,opencv对每种滑块的识别处理方式也不一样,这里总结大部分的一些滑块的识别经验,如果说的哪里有问题欢迎指出,目前我也是根据我识别滑块的经验得出
该类滑块,一般需要得到两张图片,一张是缺口的图片,一张是带有缺口的原图,这种识别如果没有做很强的干扰在里面的话,这种识别其实是简单的,只要使用opencv的模板匹配功能即可,代码如下
from io import BytesIO
from PIL import Image
import cv2
# 读取二进制图片
image = np.array(Image.open(BytesIO(image)))
gap = np.array(Image.open(BytesIO(gap)))
# 转换颜色通道,这里为什么要BGR转为RGB,因为np.array转换pillow的图像的结果就是BGR格式的
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
gap = cv2.cvtColor(gap, cv2.COLOR_BGR2RGB)
# 转为灰度图
image_gray = cv2.cvtColor(image.copy(), cv2.COLOR_BGR2GRAY)
gap_gray = cv2.cvtColor(gap.copy(), cv2.COLOR_BGR2GRAY)
# 模板匹配
result = cv2.matchTemplate(image_gray, gap_gray, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
# 获取最大相关的x,y
x,y = max_loc
注意:这里使用cv2.TM_CCOEFF_NORMED 来匹配缺口,这种方法得出的结果,结果越大越相关越接近原图的位置,但是我在有的滑块图上去识别,反而是min_loc 也就是相关度越小,实际上越接近,这种我暂时没有找出原因,可能是算法里的一些缺陷,如果max_loc 不对,可以使用min_loc 试试,另外小图可能需要做去除背景干扰
这种方法,滑块的图片的条件比较苛刻,首先必须是原图,如果是截图其他的,opencv读取时就没有alpha透明通道了,当然不排除本身就不存在透明通道的,这样这种方法也不能使用了,原理也很简单,分离出alpha通道后,缺口的边缘其实很明显了,直接用边缘检测就能识别
from io import BytesIO
from PIL import Image
import cv2
# 读取二进制图片
image = Image.open(BytesIO(image))
img = np.array(img)
# 分离alpha通道
b,g,r,alpha = cv2.split(img)
# 边缘检测
v1 = cv2.Canny(alpha,0, 100, apertureSize=3, L2gradient=True)
# 灰度轮廓检测
counts, _ = cv2.findContours(v1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for c in counts:
# 获取轮廓
x, y, w, h = cv2.boundingRect(c)
注意:如果你没有alpha通道的话,或者滑块的缺口不是由alpha通道组成的话,你运行这段代码会报错,具体报错原因看提示修改
这个方法就是纯纯识别滑块的灰度边缘了,当然,这个方法针对不同的滑块识别方式也会大不相同,但只要掌握核心原理,基本上微调就能识别出大部分的滑块灰度边缘,话不多少,先看代码
from io import BytesIO
from PIL import Image
import cv2
# 读取二进制图片
image = Image.open(BytesIO(image))
# 添加透明通道,并将透明通道转换为黑色,这一段针对部分滑块的缺口是透明的,针对直接从一张图上挖空一个缺口的,这样没有灰度边缘,识别出来,
# 所以增加一层黑色通道,如果不是这种情况,这段代码可以不用
x,y = img.size
temp_img = Image.new('RGBA', img.size, (0,0,0))
temp_img.paste(img, (0, 0, x, y), img)
img = temp_img
# 增强灰度边缘,让较暗的部分变的更暗
threshold = 100 # 通过设置阈值,去除不必要的干扰物
for i in range(img.width):
for j in range(img.height):
r,g,b,k = img.getpixel((i,j))
if r < threshold:
r = 0
if g < threshold:
g = 0
if b < threshold:
b = 0
# 转换图片尺寸,确保特征明显,上采样,确保图片不会因为太小识别不出缺口
width = 500
height = 200
img = np.array(img)
img = cv2.resize(img, (width, height)) # 转换图像尺寸
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图
gray = cv2.convertScaleAbs(gray,alpha=1.8,beta=0) # 增强对比度
gray = cv2.normalize(gray,dst=None,alpha=350,beta=0,norm_type=cv2.NORM_MINMAX) #图像归一化
gray = cv2.threshold(gray, 80, 255, cv2.THRESH_BINARY_INV)[1] # 图像二值化
v1 = cv2.Canny(gray,0, 100, apertureSize=3, L2gradient=True) # 边缘检测
priori_box = [] # 识别框
# 轮廓检测,如果检测不到,就一直执行边缘膨胀
while len(priori_box) == 0:
# 如果没找到先验框,增增加灰度边缘
v1 = cv2.dilate(v1,(3, 3),iterations=2) # 边缘膨胀
counts, _ = cv2.findContours(v1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # 轮廓检测
for c in counts:
# 去除较小识别框
if w < 50:continue
if h < 30:continue
priori_box.append([x,y,w,h])
注意,这里只用到一部分的图像增强方法,具体方法里的数值可以根据场景不同更改,边看效果边改数值,还有一部分图像增强的方法需要注意,例如直方图均衡化,针对只需要增强图像灰度边缘这种特殊的图像增强,可以使用直方图均衡化作为图像增强,直方图均衡化会拉伸缺口灰度边缘的灰度概率密度,将灰度拉伸到图像周围,使得灰度边缘对比更明显,识别更准确
基本的识别过程如下:首先增加黑色通道,设置阈值排除干扰,上采样,标准化图像尺寸,灰度图像,图像增强(增强对比度,亮度,图像二值处理,直方图均衡化,图像灰度边缘膨胀等),灰度边缘检测,取出识别框,PS:如果以上方法全都失效(基本上不大可能),那么可以使用深度学习了