一
背景
{
"data": {
"errorCode": 0,
"errorMsg": "",
"bg": "https://example.com/ENZg076503962.jpg",
"front": "https://example.com/ENZg076503963.png",
"token": "eyJhbGci...",
"width": 765,
"height": 396
}
}
{
"m": "[{"x":395,"y":256},{"x":89,"y":273},{"x":670,"y":140}]",
"token": "eyJhbGci..."
}
二
方案
1.提取图片
2.即然提取出了目标图片,那么就开始下一步,将目标图的位置找出来。
本来想通过颜色精准分割,但有些图案并不是整体粘连的,经过观察发现目标图片的三个图案排列位置都是固定的,所以直接记录出他的坐标进行像素分割:
rectangle_list = [[9, 9, 75, 75], [109, 9, 175, 75], [209, 9, 275, 75]]
for idx, rectangle in enumerate(rectangle_list, start=1):
cropped_image = white_front[rectangle[1]:rectangle[3], rectangle[0]:rectangle[2]]
gray_bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
gray_front = cv2.cvtColor(front, cv2.COLOR_BGR2GRAY)
res = cv2.matchTemplate(gray_front, gray_bg, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
cv2.putText(bg, str(idx), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (7, 249, 151), 2)
show(bg)
3.优化匹配方案
gray_bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
_, strong_contrast_bg = cv2.threshold(gray_bg, 250, 255, cv2.THRESH_BINARY)
gray_bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
_, strong_contrast_bg = cv2.threshold(gray_bg, 250, 255, cv2.THRESH_BINARY)
res = cv2.matchTemplate(
strong_contrast_bg,
cv2.bitwise_not(cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)),
cv2.TM_CCOEFF_NORMED
)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
x, y = max_loc
x, y = x + 20, y + 20
贴出代码:
import logging
import cv2
import numpy as np
def ProcessCaptcha(bg_path: str, front_path: str):
result = []
"""
加载图像
背景为jpg格式的普通图像 像素为 765x396
目标为png格式的包含透明通道图像 像素为 300x84
"""
bg = cv2.imread(bg_path)
front = cv2.imread(front_path, cv2.IMREAD_UNCHANGED)
"""
由于目标图为透明黑色图片,直接加载会导致图像全黑,
为了避免全黑情况,创建与图像尺寸相同的白色背景图像,再提取图像的透明度通道,将透明部分的像素值设置为白色
这样加载完后的图像就变成了白底黑色目标的图像
"""
white_front = np.ones_like(front) * 255
alpha_channel = front[:, :, 3]
white_front[:, :, 0:3] = cv2.bitwise_and(
white_front[:, :, 0:3],
white_front[:, :, 0:3],
mask=cv2.bitwise_not(alpha_channel)
)
"""
为了按序点击,需要提取目标区域的矩形方块
由于目标图像较为规律有序,于是计算出3个目标图像像素坐标,直接按像素截取
"""
rectangle_list = [[9, 9, 75, 75], [109, 9, 175, 75], [209, 9, 275, 75]]
for rectangle in rectangle_list:
cropped_image = white_front[rectangle[1]:rectangle[3], rectangle[0]:rectangle[2]]
"""
将背景图片转换为黑白两色的图片,只保留RGB(250-255)的图像
如此能保留绝大部分目标图像轮廓
将目标图像转换为黑色背景白色轮廓
如此便与背景的颜色逻辑一致
"""
gray_bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
_, strong_contrast_bg = cv2.threshold(gray_bg, 250, 255, cv2.THRESH_BINARY)
strong_contrast_front = cv2.bitwise_not(cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY))
res = cv2.matchTemplate(
strong_contrast_bg,
strong_contrast_front,
cv2.TM_CCOEFF_NORMED
)
"""
使用 TM_CCOEFF_NORMED 算法匹配到最佳
由于此时的 X,Y 坐标为左上角坐标,需要加20进行偏移处理,获取到点击坐标
"""
x, y = cv2.minMaxLoc(res)[3]
result.append((x + 20, y + 20))
logging.info(f"Done process: "+str(result).replace('n', ' '))
return result
三
滑动验证码
def process(bg_path: str, front_path: str) -> None:
# flags0是灰度模式
image = cv2.imread(front_path, 0)
template = cv2.imread(bg_path, 0)
template = cv2.resize(template, (340, 191), interpolation=cv2.INTER_CUBIC)
# 寻找最佳匹配
res = cv2.matchTemplate(_tran_canny(image), _tran_canny(template), cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
x, y = max_loc
w, h = image.shape[::-1]
cv2.rectangle(template, (x, y), (x + w, y + h), (7, 249, 151), 2)
show(template)
看雪ID:暮至夜寒
https://bbs.kanxue.com/user-home-959049.htm
# 往期推荐
2、CVE-2020-9802:Incorrect CSE for ArithNegate 导致的越界访问
5、CVE-2023-4427:ReduceJSLoadPropertyWithEnumeratedKey 中的越界访问
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):Python OpenCV 过点击式和滑动式图形验证码的校验