帮我分析这几个方法,修改不合理的部分,并注释修改,将主要逻辑流程说明就好,多余的文字去除,方法如下:
def checkImage(self, src_path, similar=0.95, loc=5, debug=False):
"""
检测当前屏幕是否存在目标图片(基于图像匹配)
:param src_path: 目标图片路径(相对或绝对路径)
:param similar: 相似度阈值(0-1之间,默认0.95)
:param loc: 匹配位置参数(具体含义依赖ImgUtils.quick_find实现)
:param debug: 是否开启调试模式(默认False)
:return: 布尔值(True:找到目标图片;False:未找到)
"""
# 步骤1:获取当前屏幕截图路径
screenshot_dir = os.path.join(os.getcwd(), "resource/superapp")
os.makedirs(screenshot_dir, exist_ok=True)
current_screenshot_path = os.path.join(screenshot_dir, "test.png")
# 步骤2:校验目标图片是否存在
resource_root = os.path.join(os.getcwd(), "resource", "superapp")
target_image_path = os.path.join(resource_root, src_path)
if not os.path.isfile(target_image_path):
print(f"{src_path}不存在!!!")
# 步骤3:校验截图文件是否有效
if not os.path.exists(current_screenshot_path) or not os.path.isfile(current_screenshot_path):
print(f"{current_screenshot_path}无效!!!")
# 步骤4:校验相似度阈值有效性
if not 0 <= similar <= 1:
raise ValueError(f"相似度阈值必须在0到1之间,当前值:{similar}")
try:
# 步骤5:调用图片匹配算法(返回匹配坐标和匹配得分)
match_position, match_score = self.quick_find(
current_screenshot_path,
target_image_path,
similar=similar,
loc="all",
debug=debug
)
print(f"匹配结果-坐标:{match_position},得分:{match_score}")
# 步骤6:判定是否匹配成功(坐标和得分均不为None)
is_matched = (match_position is not None) and (match_score is not None)
print(f"匹配结果:{is_matched}")
return is_matched
except Exception as e:
print(f"图片匹配异常:{str(e)}")
return False
def quick_find(self,fp1: str, fp2: str,similar: float = 1,density: float = None,rect: tuple = None,loc: str = "all", debug: bool = False) -> any:
"""在背景图像中搜索目标图像的位置(核心功能:图像匹配)"""
# -------------------- 调试模式初始化(可选计时) --------------------
if debug:
# from time import time # 示例计时工具(可替换为自定义TS)
# start_time = time() # 记录开始时间
print(f"此处废弃调试计时")
# -------------------- 步骤1:加载背景图和目标图 --------------------
background_img = Image.open(fp1) # 加载背景图(待搜索的大图)
target_img = Image.open(fp2) # 加载目标图(需要查找的小图)
# -------------------- 步骤2:确定搜索区域 --------------------
bg_width, bg_height = background_img.size # 获取背景图尺寸(宽度, 高度)
# 若未手动指定搜索区域rect,则根据loc自动计算默认区域(调用get_rect)
if rect is None:
rect = self.get_rect( width=bg_width, height=bg_height, loc=loc)
# -------------------- 步骤3:预处理图像为数组 --------------------
# 注:img2arr可能是将PIL图像转为numpy数组的工具函数(需自定义实现)
if rect:
cropped_bg = background_img.crop(rect)
else:
cropped_bg = background_img
background_arr = self.img2arr(cropped_bg) # 背景图数组
target_arr = self.img2arr(target_img) # 目标图数组
# -------------------- 调试模式:输出预处理耗时 --------------------
if debug:
print(f"此处废弃调试计时")
# -------------------- 步骤4:执行图像匹配 --------------------
# 调用核心匹配函数(假设find_arr是自定义匹配函数,需自行实现)
return self.find_arr( im1=background_arr, im2=target_arr, similar=1, density=density, rect=rect, debug=False )
def get_rect(self,width: int, height: int, loc: str = "all", region_ratio: float = 0.5) -> tuple:
"""
根据位置字符串计算背景图的搜索区域矩形坐标(适配quick_find的loc参数)
"""
center_x, center_y = width // 2, height // 2
half_w = int(width * region_ratio / 2)
half_h = int(height * region_ratio / 2)
if loc == "all":
return (0, 0, width, height)
elif loc == "top_left":
return (0, 0, width // 2, height // 2)
elif loc == "top_right":
return (width // 2, 0, width, height // 2)
elif loc == "bottom_left":
return (0, height // 2, width // 2, height)
elif loc == "bottom_right":
return (width // 2, height // 2, width, height)
elif loc == "center":
return ( center_x - half_w, center_y - half_h, center_x + half_w, center_y + half_h )
else:
raise ValueError(
f"无效的loc参数:{loc}。\n"
f"可选值:'all'(全图)、'top_left'(左上)、'top_right'(右上)、'bottom_left'(左下)、'bottom_right'(右下)、'center'(中心)"
)
def img2arr(slef,img, rect=None, convert=True):
"""
将图像转换为像素值的二维数组
参数:
img: PIL.Image 对象,输入图像
rect: 元组 (x_start, y_start, x_end, y_end),可选,指定要提取的矩形区域坐标
convert: 布尔值,是否将图像转换为灰度模式(默认True)
返回:
二维列表,每个元素是对应坐标的像素值(0-255)
"""
# 转换为灰度图(如果需要)
if convert and img.mode != 'L':
img = img.convert('L') # 'L' 表示8位灰度模式
# 获取图像尺寸(宽、高)
width, height = img.size
# 加载像素访问对象(用于获取具体坐标的像素值)
pixels = img.load()
# 情况1:需要提取指定矩形区域的像素
if rect:
x_start, y_start, x_end, y_end = rect # 解包矩形区域坐标
# 遍历y轴范围(高度方向),再遍历x轴范围(宽度方向)
return [
[
pixels[x, y] # 获取坐标(x, y)的像素值
for x in range(width) # 遍历图像宽度方向所有x坐标
if x_start <= x <= x_end # 仅保留x在指定区域内的像素
]
for y in range(height) # 遍历图像高度方向所有y坐标
if y_start <= y <= y_end # 仅保留y在指定区域内的像素
]
# 情况2:无矩形区域,返回全图像素数组
return [
[
pixels[x, y] # 获取坐标(x, y)的像素值
for x in range(width) # 遍历宽度方向所有x坐标
]
for y in range(height) # 遍历高度方向所有y坐标
]
def find_arr(self,im1, im2, similar=1, density=None, rect=None, debug=False):
"""
在主图像数组中查找模板图像的位置(基于像素匹配的定位算法)
参数:
im1: 二维列表,主图像的像素数组(行×列结构)
im2: 二维列表,待查找的模板图像的像素数组(行×列结构)
similar: 相似度阈值(0-1之间),值越大要求匹配越严格(默认1)
density: 元组(density_x, density_y),采样密度参数(控制匹配时的采样点数)
rect: 元组(x_start, y_start, x_end, y_end),可选,限制搜索区域的矩形框
debug: 布尔值,是否开启调试模式(打印耗时等信息,默认False)
返回:
(相似度, 位置元组) 或 (None, None)
位置元组格式: (x1, y1, x2, y2) 表示模板在主图中的左上和右下坐标
"""
# 调试模式:记录开始时间
if debug:
print(f"此处废弃调试计时")
# -------------------- 初始化基础参数 --------------------
template_width = len(im2[0]) # 模板图像的宽度(列数)
template_height = len(im2) # 模板图像的高度(行数)
# 主图像中模板可滑动的最大横向/纵向位置数(避免越界)
max_x = len(im1[0]) - template_width + 1 # 横向最大起始x坐标范围
max_y = len(im1) - template_height + 1 # 纵向最大起始y坐标范围
# -------------------- 处理采样密度 --------------------
# 采样密度:控制匹配时的采样点数(密度越大,采样点越少,计算越快)
if not density:
# 未指定密度时,通过工具类获取默认采样密度(假设返回横向/纵向的位移位数)
density_x, density_y = self.get_density(template_width, template_height)
else:
density_x, density_y = density # 使用用户指定的密度参数
# 计算采样步长(通过右移操作实现降采样)
step_y = template_height >> density_y # 纵向采样步长(行数方向)
step_x = template_width >> density_x # 横向采样步长(列数方向)
total_samples = step_y * step_x # 总采样点数(用于计算相似度)
max_fail = (1 - similar) * total_samples # 最大允许不匹配点数(超过则判定不匹配)
# 调试模式:打印采样参数
if debug:
print(f"denXX: {step_x}; denYY: {step_y}; total: {total_samples}")
print(f"maxFailNum {max_fail}")
# -------------------- 搜索超时控制(5分钟) --------------------
start_time = time.time()
timeout = start_time + 5 * 60 # 最大允许运行时间5分钟
# -------------------- 遍历主图像所有可能的位置 --------------------
# 外层循环:纵向遍历主图像(y坐标范围)
for y in range(max_y):
# 内层循环:横向遍历主图像(x坐标范围)
for x in range(max_x):
# 超时检测
if time.time() > timeout:
return None, None # 超时返回空
mismatch = 0 # 当前位置的不匹配点数计数
is_match = True # 是否匹配的标记
# 遍历所有采样点(纵向×横向)
for sample_y in range(step_y):
for sample_x in range(step_x):
# 计算采样点在模板中的实际坐标(通过左移还原原始位置)
template_x = sample_x << density_x # 模板中的列坐标 = 采样x × 2^density_x
template_y = sample_y << density_y # 模板中的行坐标 = 采样y × 2^density_y
# 获取模板采样点的像素值
template_pixel = im2[template_y][template_x]
# 获取主图像对应位置的像素值(主图起始位置(x,y) + 模板偏移(template_x, template_y))
main_pixel = im1[y + template_y][x + template_x]
# 像素不匹配时计数
if main_pixel != template_pixel:
mismatch += 1
# 超过最大允许不匹配数时,提前终止检查
if mismatch >= max_fail:
is_match = False
break
if not is_match:
break # 内层循环提前终止
# 匹配成功时返回结果
if is_match:
# 调试模式:记录结束时间
if debug:
print(f"此处废弃调试计时")
# 处理区域限制(rect参数):调整返回的位置坐标
if rect:
x_start, y_start, x_end, y_end = rect
# 最终位置 = 原始位置 + rect的偏移量
return (
1 - (mismatch / total_samples), # 相似度 = 1 - 不匹配率
(
x + x_start, # 左上角x坐标
y + y_start, # 左上角y坐标
x + template_width + x_start, # 右下角x坐标
y + template_height + y_start # 右下角y坐标
)
)
else:
# 无区域限制时直接返回原始位置
return (
1 - (mismatch / total_samples),
(x, y, x + template_width, y + template_height)
)
# 调试模式:记录结束时间
if debug:
print(f"此处废弃调试计时")
# 遍历完所有位置未找到匹配
return None, None
def get_density(self,width, height, maxWNum=4, maxHNum=4):
"""
计算图像匹配时的横向/纵向采样密度(通过右移次数控制采样点数)
核心逻辑:
通过循环右移操作(等价于不断除以2取整),将图像的宽/高缩小到不超过maxWNum/maxHNum,
最终返回需要右移的次数(即密度参数)。该参数用于控制匹配时的采样间隔。
参数:
width: 原始图像的宽度(列数)
height: 原始图像的高度(行数)
maxWNum: 横向最大允许采样点数(默认4)
maxHNum: 纵向最大允许采样点数(默认4)
返回:
(density_x, density_y): 横向/纵向的采样密度(右移次数)
"""
# 初始化密度参数(右移次数)
density_x, density_y = 0, 0
# -------------------- 计算横向采样密度 --------------------
# 当宽度超过maxWNum时,持续右移(每次右移1位,即宽度减半)
while width > maxWNum:
density_x += 1 # 右移次数+1(密度增加)
width = int(width >> 1) # 宽度右移1位(等价于整除2)
# -------------------- 计算纵向采样密度 --------------------
# 当高度超过maxHNum时,持续右移(每次右移1位,即高度减半)
while height > maxHNum:
density_y += 1 # 右移次数+1(密度增加)
height = int(height >> 1) # 高度右移1位(等价于整除2)
return density_x, density_y
最新发布