边缘检测对于缺口明细的图形非常好用例如这种:

android opencv识别验证码 opencv 滑动验证码_轮廓提取


在或者是

android opencv识别验证码 opencv 滑动验证码_边缘检测_02


两种都是缺口边缘明显,只要稍加处理就可以得到轮廓边缘

直接开搞

读取文件

# 读取文件
  image = Image.open(path)
  img = image.copy()  # 复制
  img = np.array(img)  # 转化为numpy
  img = cv2.resize(img, (268, 100))  # 用cv2resize
  img = img[:, :, (2, 1, 0)]  #RGB BGR图像

这里用的 PIL的读取图片而不是opencv 因为我的图片路径存在中文名称用opencv的imread是读取不了的。 所以先用pil读取 再转化为numpy格式最后放到cv2.resize重组,注意一点是PIL读入图片默认通道顺序是RGB,接下来的操作要用cv所以转化为cv.show读取的BGR通道顺序

看一下结果:

android opencv识别验证码 opencv 滑动验证码_android opencv识别验证码_03

图像增强

这里图像增强的方式是通过观察缺口都处于阴影所以该位置通道值要小于150的区域变为黑色,否则转为白色,方便为后续的边缘检测做铺垫

# 将通道值小于150的转变为黑色 
    for h in range(img.shape[0]):
        for w in range(img.shape[1]):
            if img[h, w, 0] < 150 and img[h, w, 1] < 150 and img[h, w, 2] < 150:
                for c in range(3):
                    img[h, w, c] = 0
            else:
                for c in range(3):
                    img[h, w, c] = 255

android opencv识别验证码 opencv 滑动验证码_边缘检测_04


此时可以看出缺口位置非常明显。

边缘检测+轮廓提取

直接使用canny算法进行轮廓提取

canny_img = cv2.Canny(img, 0, 100)  # 边缘检测

结果:

android opencv识别验证码 opencv 滑动验证码_轮廓提取_05


此时可以看出轮廓非常清晰了。只需要进行轮廓提取就可以了。

counts, _ = cv2.findContours(canny_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 轮廓检测
    for c in counts:
        x, y, w, h = cv2.boundingRect(c)
        #  去除较小先验框
        if w < 20:
            continue
        if h < 20:
            continue
        cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 0, 255), 2)
        print(f"左上点的坐标为:{x, y},右下点的坐标为{x + w, y + h}")

边缘检测出来的轮廓肯定有很多种我们不需要的将其通过宽和高过滤掉即可。

如果不过滤呈现的是:

android opencv识别验证码 opencv 滑动验证码_边缘检测_06


将其过滤后:

android opencv识别验证码 opencv 滑动验证码_轮廓提取_07


得到坐标

左上点的坐标为:(190, 44),右下点的坐标为(222, 76)

完整代码

def hk(path):
    # 读取文件
    image = Image.open(path)
    img = image.copy()  # 复制
    img = np.array(img)  # 转化为numpy
    img = cv2.resize(img, (268, 100))  # 用cv2resize
    img = img[:, :, (2, 1, 0)]  # BGR图像转RGB
    img1 = img.copy()
    # 将通道值小于150的转变为黑色
    for h in range(img.shape[0]):
        for w in range(img.shape[1]):
            if img[h, w, 0] < 150 and img[h, w, 1] < 150 and img[h, w, 2] < 150:
                for c in range(3):
                    img[h, w, c] = 0
            else:
                for c in range(3):
                    img[h, w, c] = 255
    canny_img = cv2.Canny(img, 0, 100)  # 边缘检测
    counts, _ = cv2.findContours(canny_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)  # 轮廓检测
    for c in counts:
        x, y, w, h = cv2.boundingRect(c)
        #  去除较小先验框
        if w < 20:
            continue
        if h < 20:
            continue
        cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 0, 255), 2)
        print(f"左上点的坐标为:{x, y},右下点的坐标为{x + w, y + h}")
    return img1

透明通道问题

android opencv识别验证码 opencv 滑动验证码_读取文件_08


android opencv识别验证码 opencv 滑动验证码_读取文件_09

类似于这种图片我们肉眼看到的缺口计算机是看不到的,原因是该图片存在透明通道,也就是rgba。
解决此类型的图片思路就是 将透明通道区域的颜色变为对比度强的颜色如黑色,在进行边缘检测和轮廓提取。

我们用cv读取之后的样子是

android opencv识别验证码 opencv 滑动验证码_opencv_10


那么缺口在哪里呢?

image = cv2.imread(path, -1)
cv2.imshow("image", image)
b, g, r, a = cv2.split(image)
b[a != 255] = 0
g[a != 255] = 0
r[a != 255] = 0
img = cv2.merge([b, g, r])
cv2.imshow("image", image)
``

 - [ ] List item

我们先将图片读取进来用imread -1的参数将全部通道读取进来

然后将其通道分割,将找到透明通道不等于255的位置,再将其bgr通道转为黑色

得到的结果为:

android opencv识别验证码 opencv 滑动验证码_android opencv识别验证码_11


之后就和上面一样进行边缘检测,轮廓提取之后再找到缺口位置。

结果展示

android opencv识别验证码 opencv 滑动验证码_opencv_12


android opencv识别验证码 opencv 滑动验证码_轮廓提取_13


此方法仅适用图像类型简单,缺口较为明显。