图像识别与屏幕监控自动化

Python 办公自动化专题 · 让计算机"看懂"屏幕并自动响应

专题:Python 自动化办公系统学习

关键词:Python, 自动化办公, 图像识别, OpenCV, 屏幕监控, OCR, 模板匹配, 自动化, Python

一、图像识别概述

图像识别是计算机视觉领域的核心任务之一,在屏幕自动化中扮演着"眼睛"的角色。传统基于坐标的自动化脚本在面对窗口位置变化、分辨率差异、UI布局调整时很容易失效,而基于图像识别的自动化方案则能通过视觉信息精确锁定目标元素,显著提高自动化脚本的鲁棒性和通用性。在桌面自动化场景中,图像识别技术主要解决三大问题:第一是"目标在哪里"(定位问题),第二是"屏幕上有什么"(识别问题),第三是"屏幕状态是否发生变化"(监控问题)。

从技术路线来看,图像识别在自动化中的应用可以分为三个层次。最基础的层次是模板匹配(Template Matching),它通过计算模板图像与屏幕截图的相似度来确定目标位置,适用于图标、按钮等固定形状元素的定位。中间层次是特征点匹配(Feature Matching),它提取图像中的关键点(如角点、斑点)并计算特征描述子,能够应对目标被旋转、缩放或部分遮挡的情况。最高层次是基于深度学习的图像识别,利用卷积神经网络(CNN)进行目标检测(如YOLO、SSD)和图像分类(如ResNet、EfficientNet),适合复杂场景下的通用目标识别。

在屏幕自动化中,最常用的Python库主要有三个。一是OpenCV(Open Source Computer Vision Library),它是计算机视觉领域的瑞士军刀,提供了超过2500种优化的算法,覆盖图像处理、特征提取、目标检测等各个方面。二是Pillow(PIL),它是Python最基础的图像处理库,擅长图像的读取、保存、裁剪、缩放和基本像素操作。三是pytesseract,它是Google Tesseract OCR引擎的Python封装,用于从图像中提取文字信息。在实际项目中,这三个库往往配合使用:Pillow负责截图和预处理,OpenCV负责图像分析和匹配,pytesseract负责文字识别。

import cv2 import numpy as np from PIL import ImageGrab import pytesseract # 基本图像读取与显示 img = cv2.imread('screenshot.png') print(f"图像尺寸: {img.shape}") # (高度, 宽度, 通道数) # 转换为灰度图(大多数图像处理的第一步) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 截取屏幕区域 bbox = (100, 100, 800, 600) # (left, top, right, bottom) screen = ImageGrab.grab(bbox) screen_np = np.array(screen) screen_bgr = cv2.cvtColor(screen_np, cv2.COLOR_RGB2BGR) cv2.imshow('Screen Region', screen_bgr) cv2.waitKey(0) cv2.destroyAllWindows()
# 图像缩放与预处理管道 def preprocess_image(image_path): img = cv2.imread(image_path) # 转灰度 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊去噪 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 自适应阈值二值化 binary = cv2.adaptiveThreshold( blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2 ) return binary processed = preprocess_image('screen_capture.png') cv2.imwrite('processed.png', processed)

核心要点:图像识别的本质是将视觉信息转化为可计算的数值特征。在屏幕自动化中,选择合适的技术路线取决于具体场景——固定UI用模板匹配,可变目标用特征匹配,复杂场景考虑深度学习。掌握OpenCV的基本图像读写和预处理操作是后续所有进阶技术的基础。

二、OpenCV模板匹配

模板匹配(Template Matching)是图像识别中最直观、最基础的方法,也是屏幕自动化中最常用的定位技术。其核心思想非常简单:给定一个模板图像(例如一个按钮图标),在目标图像(屏幕截图)上滑动这个模板,逐个像素地计算模板与对应区域的相似度,最终找到与模板最匹配的位置。OpenCV通过 cv2.matchTemplate() 函数实现了这一功能,该函数接收目标图像、模板图像和匹配方法三个参数,返回一个灰度图形式的匹配结果矩阵,矩阵中每个像素的值代表该位置与模板的匹配程度。

OpenCV提供了六种匹配方法,分为两类。第一类是衡量差异的(值越小越匹配):TM_SQDIFF(平方差匹配)和 TM_SQDIFF_NORMED(归一化平方差匹配)。第二类是衡量相似度的(值越大越匹配):TM_CCORR(相关匹配)、TM_CCORR_NORMED(归一化相关匹配)、TM_CCOEFF(相关系数匹配)和 TM_CCOEFF_NORMED(归一化相关系数匹配)。在实际屏幕自动化中,TM_CCOEFF_NORMED 是最常用的方法,因为它对光照变化和对比度变化具有更好的鲁棒性,返回值范围为0到1,便于设置统一的匹配阈值。

多目标匹配是模板匹配的一个重要扩展。在实际屏幕中,同一个图标可能出现在多个位置(如桌面上的多个快捷方式、列表中的多个相同按钮)。此时,仅靠 cv2.minMaxLoc() 只能找到最佳匹配位置。要找到所有匹配位置,需要在结果矩阵上应用阈值筛选:将大于(或小于,取决于匹配方法)某个阈值的所有位置都视为匹配,然后使用 cv2.groupRectangles() 或自定义非极大值抑制(NMS)来合并重叠区域,最终得到所有目标的位置列表。

import cv2 import numpy as np def template_match(screen_path, template_path, threshold=0.8): """在屏幕截图中查找模板图像的位置""" screen = cv2.imread(screen_path) template = cv2.imread(template_path) h, w = template.shape[:2] # 执行模板匹配 result = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED) # 找到最佳匹配位置 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) if max_val >= threshold: top_left = max_loc bottom_right = (top_left[0] + w, top_left[1] + h) # 在屏幕截图上面矩形标注 cv2.rectangle(screen, top_left, bottom_right, (0, 255, 0), 2) cv2.putText(screen, f'Match: {max_val:.2f}', (top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) return top_left, max_val return None, 0.0 # 使用示例 pos, confidence = template_match('desktop.png', 'chrome_icon.png') if pos: print(f"Chrome图标位于: {pos}, 置信度: {confidence:.2f}") else: print("未找到Chrome图标")
def multi_template_match(screen_path, template_path, threshold=0.8): """多目标模板匹配——查找所有匹配位置""" screen = cv2.imread(screen_path) template = cv2.imread(template_path) h, w = template.shape[:2] result = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED) # 找到所有超过阈值的匹配位置 locations = np.where(result >= threshold) points = [] for pt in zip(*locations[::-1]): # 转为(x, y)坐标 points.append(pt) cv2.rectangle(screen, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 1) # 用非极大值抑制合并重叠区域 if points: rects = [(x, y, x + w, y + h) for x, y in points] rects, _ = cv2.groupRectangles(np.array(rects), 1, 0.5) for (x1, y1, x2, y2) in rects: cv2.rectangle(screen, (x1, y1), (x2, y2), (0, 255, 0), 2) print(f"找到 {len(rects)} 个匹配目标") return rects # 查找桌面所有文件夹图标 icons = multi_template_match('desktop.png', 'folder_icon.png', threshold=0.7)
# 实时屏幕模板匹配(持续监控) import time from PIL import ImageGrab def monitor_for_template(template_path, region=None, threshold=0.8, interval=0.5): """持续监控屏幕,直到出现匹配目标""" template = cv2.imread(template_path) print(f"开始监控屏幕,等待目标出现... (间隔: {interval}s)") while True: # 截取屏幕 screen = ImageGrab.grab(region) screen_np = np.array(screen) screen_bgr = cv2.cvtColor(screen_np, cv2.COLOR_RGB2BGR) # 执行匹配 result = cv2.matchTemplate(screen_bgr, template, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(result) if max_val >= threshold: print(f"检测到目标!位置: {max_loc}, 置信度: {max_val:.2f}") return max_loc time.sleep(interval) # 等待"确认"按钮出现 btn_pos = monitor_for_template('confirm_button.png', region=(0, 0, 1920, 1080))

核心要点:模板匹配是屏幕自动化定位的基石,TM_CCOEFF_NORMED 是最推荐的匹配方法,阈值一般设置在0.7-0.9之间。对于多目标场景需要结合阈值筛选和NMS。实时监控模式下,控制采样频率(0.2-1秒)以平衡响应速度和CPU负载。

三、特征点匹配

当模板匹配无法满足需求时——例如目标图像发生了旋转、缩放、透视变形,或者只有部分可见——就需要引入特征点匹配技术。特征点匹配的核心思路是:不是比较整张图像的像素,而是先检测图像中具有独特性的"关键点"(如角点、边缘交点、纹理突变点),然后为这些关键点计算描述子(一个能够唯一标识该点局部特征的向量),最后通过比较描述子的相似度来建立两幅图像之间的对应关系。这种方法对图像的几何变换具有天然的鲁棒性。

OpenCV支持多种特征检测算法。SIFT(Scale-Invariant Feature Transform)是2004年提出的经典算法,它对尺度变化和旋转具有不变性,描述子维度为128维,精度很高但计算速度较慢。SURF(Speeded Up Robust Features)是SIFT的加速版本,利用积分图像和Haar小波来加速特征提取。ORB(Oriented FAST and Rotated BRIEF)是2011年提出的高效算法,结合了FAST角点检测和BRIEF描述子,并加入了旋转不变性,计算速度比SIFT快两个数量级,非常适合实时的屏幕自动化场景。由于SIFT和SURF在OpenCV 4.x之后需要安装 opencv-contrib-python 且部分算法有专利限制,ORB成为屏幕自动化中最实用的选择。

特征匹配阶段常用的策略有两种。一是暴力匹配(Brute-Force Matcher,BFMatcher),它计算每个查询描述子与所有目标描述子的距离,找到最近邻的匹配对,简单直观但计算量大。二是快速最近邻搜索(FLANN),它基于KD-Tree或LSH(局部敏感哈希)来加速高维空间的近邻搜索,适合大规模特征匹配场景。无论使用哪种匹配器,都需要用比率测试(Lowe's ratio test,通常取0.75)来过滤误匹配——对于每个查询特征点,取距离最近和次近的两个匹配,只有当最近距离显著小于次近距离时才认为匹配是可靠的。匹配完成后,可以使用 cv2.findHomography() 计算透视变换矩阵,从而精确计算出目标在图像中的位置边界。

import cv2 import numpy as np def orb_feature_match(target_path, scene_path, min_match_count=10): """使用ORB特征点进行图像匹配与定位""" target = cv2.imread(target_path, cv2.IMREAD_GRAYSCALE) scene = cv2.imread(scene_path, cv2.IMREAD_GRAYSCALE) # 初始化ORB检测器 orb = cv2.ORB_create(nfeatures=2000) # 检测关键点并计算描述子 kp1, des1 = orb.detectAndCompute(target, None) kp2, des2 = orb.detectAndCompute(scene, None) # 暴力匹配器 + 汉明距离(ORB默认使用二进制描述子) bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) matches = bf.match(des1, des2) # 按距离排序,取最佳匹配 matches = sorted(matches, key=lambda x: x.distance) # 如果足够匹配,计算目标位置 if len(matches) >= min_match_count: src_pts = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2) # 用RANSAC计算单应性矩阵 M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) h, w = target.shape if M is not None: pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]] ).reshape(-1, 1, 2) dst = cv2.perspectiveTransform(pts, M) return dst, len(matches) return None, 0 # 使用示例:在场景中查找目标图标(支持旋转和缩放) corners, n_matches = orb_feature_match('target_logo.png', 'screen_full.png') if corners is not None: print(f"目标位置已确定,匹配点数量: {n_matches}")
def flann_feature_match(img1_path, img2_path): """使用SIFT + FLANN进行特征匹配(更高精度)""" # 需要 opencv-contrib-python sift = cv2.SIFT_create() img1 = cv2.imread(img1_path, cv2.IMREAD_GRAYSCALE) img2 = cv2.imread(img2_path, cv2.IMREAD_GRAYSCALE) kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) # FLANN参数——用于高维向量 FLANN_INDEX_KDTREE = 1 index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1, des2, k=2) # Lowe's 比率测试 good_matches = [] for m, n in matches: if m.distance < 0.75 * n.distance: good_matches.append(m) print(f"原始匹配: {len(matches)}, 优质匹配: {len(good_matches)}") return kp1, kp2, good_matches # 查看匹配可视化结果 kp1, kp2, good = flann_feature_match('icon.png', 'screen_region.png') result_img = cv2.drawMatches( cv2.imread('icon.png'), kp1, cv2.imread('screen_region.png'), kp2, good[:50], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS )

核心要点:特征点匹配比模板匹配更灵活,能应对旋转、缩放和部分遮挡。ORB适合实时场景(速度快、无专利限制),SIFT适合高精度场景。比率测试(0.75)和RANSAC(5.0阈值)是消除误匹配的标准组合。对于屏幕自动化,ORB通常能在10-50ms内完成匹配,足以满足实时监控需求。

四、屏幕区域监控

屏幕区域监控是桌面自动化的核心能力之一,它让程序能够"持续注视"屏幕的特定区域,并在区域内容发生变化时做出响应。典型的应用场景包括:监控某个软件的状态变化(如"处理完成"字样出现)、监测系统通知弹窗、跟踪数据报表的实时更新、以及检测网页元素的动态加载等。实现屏幕区域监控需要解决三个基本问题:如何高效截图、如何检测变化、以及如何平衡监控频率与系统资源消耗。

在截图效率方面,Python有多个选择。Pillow的 ImageGrab.grab() 是最简易的方式,适合低频率截图(每秒1-5帧)。对于更高的帧率需求,mss(Multiple Screen Shots)库是更好的选择——它基于底层API(Windows上使用DXGI,macOS上使用CGDisplay),能够轻松达到30-60帧每秒的截图速度。如果需要更精确的窗口截图而非全屏截图,可以使用 pygetwindowwin32gui 获取窗口的屏幕坐标和尺寸,然后截取对应区域。在实际项目中,建议优先使用mss库进行屏幕捕获,因为其速度快、CPU占用低、且支持多显示器。

变化检测策略决定了监控的灵敏度和准确性。最简单的策略是逐像素比较(通过计算两帧图像的绝对差,然后统计变化像素的比例)。但这种方法对光照变化、窗口抖动等噪声非常敏感。更鲁棒的策略包括:计算图像哈希值(如感知哈希pHash),比较两个哈希值的汉明距离;或者计算结构相似性指数(SSIM),它能区分结构变化和光照变化。在屏幕自动化实践中,推荐采用"两级检测"策略——先用快速哈希比较做初筛,只有哈希差异超过阈值时才进行更精确的像素级分析,这样既保证了检测速度又保证了准确度。

import cv2 import numpy as np from PIL import ImageGrab import time def monitor_screen_region(region, callback=None, interval=1.0): """监控屏幕指定区域的变化""" prev_frame = None frame_count = 0 print(f"开始监控区域: {region}, 间隔: {interval}s") try: while True: # 截取区域 screen = ImageGrab.grab(region) frame = np.array(screen) frame_gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) if prev_frame is not None: # 计算帧差 diff = cv2.absdiff(prev_frame, frame_gray) # 阈值化 _, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY) change_pixels = np.sum(thresh > 0) total_pixels = thresh.size change_ratio = change_pixels / total_pixels if change_ratio > 0.01: # 超过1%像素变化视为"有变化" print(f"[变化检测] 帧 {frame_count}: 变化比例 {change_ratio:.4f}") if callback: callback(frame, change_ratio) prev_frame = frame_gray frame_count += 1 time.sleep(interval) except KeyboardInterrupt: print("监控已停止") # 回调:检测到变化时保存截图 def on_change(frame, ratio): timestamp = time.strftime('%Y%m%d_%H%M%S') cv2.imwrite(f'change_{timestamp}.png', cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)) print(f"已保存变化截图: change_{timestamp}.png") # 监控屏幕右上角区域(系统通知区域) monitor_screen_region((1500, 0, 1920, 200), callback=on_change, interval=0.5)
def smart_monitor(region, interval=0.5, hash_size=8, pixel_threshold=0.05): """两级检测:先用感知哈希过滤,再精确检测变化区域""" from PIL import Image def compute_phash(image): """计算感知哈希""" img = cv2.resize(image, (hash_size, hash_size)) img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image avg = img.mean() diff = img > avg return diff.flatten() prev_hash = None prev_frame = None while True: screen = ImageGrab.grab(region) frame = np.array(screen) frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 第一级:感知哈希检测 current_hash = compute_phash(frame_rgb) if prev_hash is not None: hamming_dist = np.sum(current_hash != prev_hash) if hamming_dist > 2: # 哈希差异超过2位 print(f"[哈希变化] 汉明距离: {hamming_dist}") # 第二级:精确变化区域定位 if prev_frame is not None: diff = cv2.absdiff(prev_frame, frame_rgb) gray_diff = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY) _, thresh = cv2.threshold(gray_diff, 30, 255, cv2.THRESH_BINARY) # 找出变化区域的轮廓 contours, _ = cv2.findContours( thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) for cnt in contours: area = cv2.contourArea(cnt) if area > 500: # 忽略太小的变化区域 x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(frame_rgb, (x, y), (x + w, y + h), (0, 0, 255), 2) cv2.imshow('Monitor', frame_rgb) if cv2.waitKey(1) & 0xFF == ord('q'): break prev_hash = current_hash prev_frame = frame_rgb.copy() time.sleep(interval) cv2.destroyAllWindows()

核心要点:屏幕区域监控的核心是平衡效率与准确度。mss库是最快的截图方案,两级检测策略(哈希初筛 + 像素精检)能显著降低CPU使用率。监控间隔根据场景调整——UI变化监控0.5-1秒即可,实时性要求高的场景可缩短到0.1-0.2秒。变化阈值需要根据具体场景调优,避免频繁误报。

五、OCR文字识别

OCR(Optical Character Recognition,光学字符识别)是将图像中的文字信息提取为计算机可编辑文本的技术。在屏幕自动化中,OCR扮演着"阅读者"的角色——当自动化脚本需要读取屏幕上的文字内容(如错误信息、状态提示、数据数值)时,OCR是唯一的可行方案。Tesseract是目前最成熟的开源OCR引擎,最初由HP实验室开发,后由Google维护,支持超过100种语言的文字识别。Python通过 pytesseract 库与Tesseract进行交互,将图像处理(Pillow/OpenCV)和文字识别无缝衔接。

Tesseract的安装和配置需要注意:在Windows上需要从GitHub下载安装包并安装,然后设置环境变量或直接在Python代码中指定tesseract路径。对于中文识别,需要额外下载中文语言包(chi_sim),并在调用时将语言参数设为 lang='chi_sim+eng' 来同时识别中英文。需要注意的是,Tesseract对图像质量要求较高——原始屏幕截图直接送入Tesseract往往识别率不高,必须配合图像预处理来提升识别准确度。

图像预处理是OCR流程中最关键的一环。标准预处理管道包括五个步骤:第一步是灰度化,将彩色图像转为灰度图以减少数据量。第二步是二值化,通过阈值处理将图像转为纯粹的黑白两色,可以用全局阈值(OTSU算法自动确定最优阈值)或自适应阈值(对不同区域采用不同阈值,适合光照不均的图像)。第三步是降噪,使用高斯模糊、中值滤波或形态学操作(开运算、闭运算)去除细小噪点。第四步是倾斜校正,使用霍夫变换或最小面积矩形来检测文本行的倾斜角度,然后通过仿射变换进行纠正。第五步是缩放,将文字区域放大到合适的大小(通常300-600 DPI),因为Tesseract对特定尺寸范围内的文字识别效果最好。每一步预处理都可以显著影响最终的识别准确率,在复杂场景下往往需要反复调优。

import pytesseract from PIL import Image import cv2 import numpy as np # 配置Tesseract路径(Windows环境) pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe' def basic_ocr(image_path): """基础OCR识别""" img = cv2.imread(image_path) # 直接识别 text = pytesseract.image_to_string(img, lang='chi_sim+eng') return text def enhanced_ocr(image_path): """增强预处理后的OCR识别""" img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 1. 预处理管道 # 降噪 denoised = cv2.medianBlur(gray, 3) # 二值化(OTSU自动阈值) _, binary = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 形态学操作:去除小噪点 kernel = np.ones((1, 1), np.uint8) cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # 2. 缩放(放大到合适尺寸) height, width = cleaned.shape if height < 30: scale = 60 / height new_width = int(width * scale) new_height = int(height * scale) cleaned = cv2.resize(cleaned, (new_width, new_height), interpolation=cv2.INTER_CUBIC) # 3. 识别 custom_config = r'--oem 3 --psm 6' text = pytesseract.image_to_string( cleaned, lang='chi_sim+eng', config=custom_config ) return text.strip() # 识别屏幕上的错误提示 result = enhanced_ocr('error_dialog.png') print(f"识别结果: {result}")
def ocr_screen_region(region): """识别屏幕指定区域的文字""" from PIL import ImageGrab # 截取屏幕区域 screen = ImageGrab.grab(region) screen_np = np.array(screen) screen_bgr = cv2.cvtColor(screen_np, cv2.COLOR_RGB2BGR) # 预处理 gray = cv2.cvtColor(screen_bgr, cv2.COLOR_BGR2GRAY) # CLAHE自适应直方图均衡化(改善对比度) clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) enhanced = clahe.apply(gray) # 二值化 _, binary = cv2.threshold(enhanced, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 识别 text = pytesseract.image_to_string(binary, lang='chi_sim+eng', config='--oem 3 --psm 6') return text.strip() def extract_numbers_from_screen(region): """从屏幕区域提取数字""" screen = ImageGrab.grab(region) screen_np = np.array(screen) gray = cv2.cvtColor(screen_np, cv2.COLOR_RGB2GRAY) # 反色处理(如果是黑底白字) mean_val = gray.mean() if mean_val < 127: gray = 255 - gray _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 只识别数字和常用符号 text = pytesseract.image_to_string( binary, config='--oem 3 --psm 6 -c tessedit_char_whitelist=0123456789.-%' ) return text.strip() # 实时OCR循环 def watch_screen_text(region, target_text, interval=1.0): """持续监控屏幕区域直到出现目标文字""" import time while True: text = ocr_screen_region(region) print(f"[OCR] {text[:50]}...") if target_text in text: print(f"检测到目标文本: {target_text}") return True time.sleep(interval)
def deskew_image(image): """倾斜校正——检测文本行的倾斜角度并纠正""" gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) \ if len(image.shape) == 3 else image gray = cv2.bitwise_not(gray) # 阈值化 _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 查找所有非零像素点的坐标 coords = np.column_stack(np.where(binary > 0)) # 计算最小外接矩形,获取倾斜角度 angle = cv2.minAreaRect(coords)[-1] # 调整角度范围 if angle < -45: angle = 90 + angle angle = -angle if abs(angle) < 0.5: # 角度太小,无需校正 return image # 仿射变换纠正 h, w = image.shape[:2] center = (w // 2, h // 2) M = cv2.getRotationMatrix2D(center, angle, 1.0) corrected = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE) return corrected # 倾斜校正前后对比 raw_text = pytesseract.image_to_string('skewed_text.png', lang='chi_sim') corrected = deskew_image(cv2.imread('skewed_text.png')) corrected_text = pytesseract.image_to_string(corrected, lang='chi_sim') print(f"校正前: {raw_text}") print(f"校正后: {corrected_text}")

核心要点:OCR的识别率高度依赖图像预处理质量。标准管道:灰度化 → 降噪 → 二值化(OTSU) → 倾斜校正 → 缩放。PSM模式6(假设为统一的文本块)适合大部分屏幕文字场景。chi_sim+eng 混合语言模式能同时识别中英文。对于特定格式(如纯数字),使用 tessedit_char_whitelist 限制字符集可显著提升准确率。

六、UI元素定位

UI元素定位是图像识别在桌面自动化中最直接的应用。与Web自动化中的CSS选择器不同,桌面UI没有标准化的DOM结构可供查询,必须通过视觉特征来定位目标元素。一个完整的UI元素定位系统需要解决四个问题:找得到(元素检测)、找得准(精确定位)、点得对(坐标映射)、跑得通(多分辨率适配)。这四个问题环环相扣,任何一个环节出问题都可能导致自动化流程失败。

在元素检测层面,模板匹配是最常用的方法,将预期UI元素的截图作为模板,在当前屏幕截图中搜索。但实际场景中,UI元素可能呈现多种状态:按下、悬停、禁用、加载中,每种状态的视觉表现都不同。因此,需要为同一个UI元素准备多张模板图像,并采用"按置信度降序"的匹配策略——先尝试高置信度的常态模板,如果匹配不到再尝试低一档的其他状态模板。对于动态内容的UI元素(如带有文字标签的按钮),可以结合模板匹配和OCR:先用模板匹配定位按钮的大致区域,再用OCR识别区域内的文字来确认是否为预期元素。

坐标映射是多分辨率适配的关键技术。不同电脑的屏幕分辨率不同,同一应用程序在不同分辨率下的UI元素位置也不同。解决这个问题的方法不是为每种分辨率都准备一套模板,而是使用"基准分辨率"策略:在一个固定分辨率(如1920x1080)下标注所有UI元素的坐标,然后通过缩放系数(当前分辨率宽/基准宽,当前分辨率高/基准高)将坐标映射到实际屏幕上。更精确的做法是使用 win32guipygetwindow 获取目标窗口的位置和尺寸,将UI坐标相对于窗口而非屏幕来计算,这样即使窗口被移动或缩放,自动化脚本仍然能正确操作。

import cv2 import numpy as np from PIL import ImageGrab import pyautogui class UIElementLocator: """UI元素定位器""" def __init__(self, template_dir, base_resolution=(1920, 1080)): self.templates = {} self.base_w, self.base_h = base_resolution self.current_w, self.current_h = pyautogui.size() def load_template(self, name, image_path): """加载UI元素模板""" template = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) self.templates[name] = template print(f"已加载模板 [{name}]: {template.shape}") def find_element(self, name, screen=None, threshold=0.8): """在屏幕中定位UI元素""" if name not in self.templates: raise ValueError(f"模板 '{name}' 未加载") if screen is None: screen = ImageGrab.grab() screen = np.array(screen) screen_gray = cv2.cvtColor(screen, cv2.COLOR_RGB2GRAY) else: screen_gray = cv2.cvtColor(screen, cv2.COLOR_RGB2GRAY) \ if len(screen.shape) == 3 else screen template = self.templates[name] h, w = template.shape # 多尺度搜索(适配不同分辨率) found = None for scale in [1.0, 0.9, 0.8, 1.1, 1.2]: if scale != 1.0: new_w = int(w * scale) new_h = int(h * scale) if new_w < 20 or new_h < 20: continue resized = cv2.resize(template, (new_w, new_h)) else: resized = template result = cv2.matchTemplate(screen_gray, resized, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(result) if max_val > threshold and (found is None or max_val > found[0]): found = (max_val, max_loc, resized.shape) if found: confidence, top_left, (th, tw) = found center_x = top_left[0] + tw // 2 center_y = top_left[1] + th // 2 return { 'center': (center_x, center_y), 'bbox': (*top_left, top_left[0] + tw, top_left[1] + th), 'confidence': confidence } return None
def click_element(locator_result, button='left'): """安全地点击定位到的UI元素""" if locator_result is None: print("元素未找到,无法点击") return False cx, cy = locator_result['center'] confidence = locator_result['confidence'] print(f"点击元素: ({cx}, {cy}), 置信度: {confidence:.2f}") pyautogui.click(cx, cy, button=button) return True def find_and_click(name, locator, threshold=0.8): """查找并点击UI元素的快捷方法""" result = locator.find_element(name, threshold=threshold) return click_element(result) def locate_multi_state_button(name, locator): """多状态按钮定位——依次尝试不同状态的模板""" states = [f'{name}_normal', f'{name}_hover', f'{name}_pressed', f'{name}_disabled'] for state in states: try: result = locator.find_element(state, threshold=0.75) if result: print(f"找到按钮 [{name}] 状态: {state}") return result except ValueError: continue print(f"按钮 [{name}] 所有状态均未找到") return None # 使用示例 locator = UIElementLocator('templates/') locator.load_template('save_btn_normal', 'templates/save_normal.png') locator.load_template('save_btn_hover', 'templates/save_hover.png') pos = locate_multi_state_button('save_btn', locator) if pos: click_element(pos)
class AdaptiveLocator(UIElementLocator): """自适应分辨率UI定位器""" def __init__(self, template_dir, base_resolution=(1920, 1080)): super().__init__(template_dir, base_resolution) self.setup_resolution_adaptation() def setup_resolution_adaptation(self): """设置分辨率适配参数""" import win32gui import win32api # 获取实际的屏幕尺寸 self.current_w = win32api.GetSystemMetrics(0) self.current_h = win32api.GetSystemMetrics(1) # 计算缩放系数 self.scale_x = self.current_w / self.base_w self.scale_y = self.current_h / self.base_h print(f"基准分辨率: {self.base_w}x{self.base_h}") print(f"当前分辨率: {self.current_w}x{self.current_h}") print(f"缩放系数: x={self.scale_x:.2f}, y={self.scale_y:.2f}") def get_window_relative_pos(self, window_title, element_pos): """获取窗口相对坐标""" import win32gui import win32con try: hwnd = win32gui.FindWindow(None, window_title) if hwnd: # 获取窗口在屏幕上的位置 rect = win32gui.GetWindowRect(hwnd) win_x, win_y = rect[0], rect[1] # 计算相对于窗口的坐标 rel_x = element_pos[0] - win_x rel_y = element_pos[1] - win_y return (rel_x, rel_y) except Exception as e: print(f"获取窗口位置失败: {e}") return element_pos # 自适应定位 adapter = AdaptiveLocator('templates/') adapter.load_template('confirm_btn', 'templates/confirm_1920.png') result = adapter.find_element('confirm_btn') if result: # 坐标自适应映射 adapted_x = int(result['center'][0] / adapter.scale_x) adapted_y = int(result['center'][1] / adapter.scale_y) print(f"自适应坐标: ({adapted_x}, {adapted_y})") pyautogui.click(adapted_x, adapted_y)

核心要点:UI元素定位需要系统性设计。多状态模板、多尺度搜索、自适应分辨率映射是三大关键策略。对于"同元素不同外观"的问题(如不同语言版本),准备多套模板;对于"同元素不同位置"的问题(如窗口拖动),采用相对坐标(窗口相对坐标而非屏幕绝对坐标)来解决。

七、图像变化检测

图像变化检测是屏幕监控自动化中的核心算法模块。与前面提到的模板匹配和OCR不同,变化检测不需要知道目标长什么样,只需要判断"图像是否发生了变化"以及"变化发生在哪里"。这在许多场景下非常有用:监控下载进度条的变化、检测软件安装完成的状态切换、监测视频流中是否出现新物体、以及发现屏幕上突然弹出的异常提示等。变化检测算法的核心输入是两帧或多帧图像,输出是变化区域的掩码(Mask)和变化程度的量化指标。

最基础的变化检测方法是帧差法(Frame Differencing),即计算前后两帧图像的像素绝对差,然后通过阈值化将差值图转为二值掩码。帧差法的优点是计算极快(几毫秒即可完成),对硬件要求低。但缺点也很明显:对光照变化敏感(屏幕亮度微变会导致大量误检)、对相机抖动敏感(窗口移动或刷新会导致大范围伪变化)、且无法检测缓慢变化(因为相邻帧差异太小)。改进版是三帧差分法——使用三帧连续图像,计算前两帧的差和后两帧的差,然后取交集,这样可以有效消除运动拖尾和噪声。

更高级的变化检测方法是结构相似性(SSIM,Structural Similarity Index)。SSIM从三个维度衡量两幅图像的相似度:亮度(luminance)、对比度(contrast)和结构(structure)。与逐像素比较不同,SSIM考虑的是像素之间的相互关系,因此能更好地感知图像的结构变化而忽略细微的噪声和光照变化。SSIM的返回值在-1到1之间,1表示两幅图完全相同。在OpenCV中,可以通过 scikit-image 库的 compare_ssim 函数来计算。通过SSIM差异图(1 - SSIM),可以直观地看到变化区域。此外,对于监控视频流或高频屏幕捕获的场景,还可以使用高斯混合模型(GMM)或KNN背景减除法,它们能够建立动态背景模型,从而区分"前景变化"(值得关注的目标变化)和"背景变化"(光照变化、屏幕闪烁等噪声)。

import cv2 import numpy as np from PIL import ImageGrab import time def frame_diff_method(prev_frame, curr_frame, threshold=30): """帧差法——检测两帧之间的变化""" # 转灰度 prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_RGB2GRAY) \ if len(prev_frame.shape) == 3 else prev_frame curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_RGB2GRAY) \ if len(curr_frame.shape) == 3 else curr_frame # 计算绝对差 diff = cv2.absdiff(prev_gray, curr_gray) # 阈值化 _, thresh = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY) # 形态学操作:去除孤立噪点 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) clean = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) # 计算变化指标 change_pixels = np.sum(clean > 0) total_pixels = clean.size change_ratio = change_pixels / total_pixels return clean, change_ratio def three_frame_diff(frames): """三帧差分法——减少运动拖尾""" # 计算两对帧差 diff1 = cv2.absdiff(frames[0], frames[1]) diff2 = cv2.absdiff(frames[1], frames[2]) # 阈值化 _, thresh1 = cv2.threshold(diff1, 30, 255, cv2.THRESH_BINARY) _, thresh2 = cv2.threshold(diff2, 30, 255, cv2.THRESH_BINARY) # 取交集(像素级与操作) combined = cv2.bitwise_and(thresh1, thresh2) return combined # 实时帧差法监控 region = (0, 0, 800, 600) prev = None print("启动帧差法监控(按Ctrl+C停止)...") try: while True: screen = ImageGrab.grab(region) frame = np.array(screen) if prev is not None: mask, ratio = frame_diff_method(prev, frame) if ratio > 0.02: # 超过2%区域变化 print(f"检测到显著变化: {ratio:.2%}") # 绘制变化轮廓 contours, _ = cv2.findContours( mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) display = frame.copy() for cnt in contours: if cv2.contourArea(cnt) > 100: x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(display, (x, y), (x + w, y + h), (0, 0, 255), 2) cv2.imshow('Change Detection', display) if cv2.waitKey(1) & 0xFF == ord('q'): break prev = frame time.sleep(0.1) except KeyboardInterrupt: print("监控停止") cv2.destroyAllWindows()
from skimage.metrics import structural_similarity as ssim import cv2 import numpy as np def ssim_change_detection(prev_frame, curr_frame): """基于结构相似性(SSIM)的变化检测""" # 转灰度 prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_RGB2GRAY) \ if len(prev_frame.shape) == 3 else prev_frame curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_RGB2GRAY) \ if len(curr_frame.shape) == 3 else curr_frame # 计算SSIM score, diff = ssim(prev_gray, curr_gray, full=True, data_range=255) diff = (1 - diff) * 255 # 差异图:差异越大值越大 diff = diff.astype(np.uint8) # 阈值化提取变化区域 _, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY) # 形态学清理 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) clean = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) clean = cv2.morphologyEx(clean, cv2.MORPH_OPEN, kernel) return score, clean # 使用示例 frame_a = cv2.imread('screen_before.png') frame_b = cv2.imread('screen_after.png') similarity, change_mask = ssim_change_detection(frame_a, frame_b) print(f"SSIM相似度: {similarity:.4f} (1=完全相同)") if similarity < 0.95: print("屏幕内容发生显著变化!") # 显示变化区域 cv2.imshow('Changes', change_mask) cv2.imwrite('change_mask.png', change_mask)
class BackgroundSubtractorDetector: """基于背景减除法的变化检测(GMM模型)""" def __init__(self, history=500, var_threshold=16): self.bg_subtractor = cv2.createBackgroundSubtractorMOG2( history=history, varThreshold=var_threshold, detectShadows=True ) def detect_motion(self, frame, min_area=500): """检测前景运动目标""" # 应用背景减除法 fg_mask = self.bg_subtractor.apply(frame) # 去除阴影 _, fg_mask = cv2.threshold(fg_mask, 200, 255, cv2.THRESH_BINARY) # 形态学清理 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_OPEN, kernel) fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_CLOSE, kernel) # 查找轮廓 contours, _ = cv2.findContours( fg_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) changes = [] for cnt in contours: area = cv2.contourArea(cnt) if area > min_area: x, y, w, h = cv2.boundingRect(cnt) changes.append({ 'bbox': (x, y, x + w, y + h), 'area': area, 'center': (x + w // 2, y + h // 2) }) return changes # 使用示例 detector = BackgroundSubtractorDetector() screen = ImageGrab.grab((0, 0, 800, 600)) frame = np.array(screen) changes = detector.detect_motion(frame) print(f"检测到 {len(changes)} 个变化区域")

核心要点:变化检测有三层技术栈可供选择:速度最快的是帧差法(毫秒级,适合实时监控),精度最好的是SSIM(亚像素级,适合精确比对),适应最强的是背景减除法(GMM/KNN,适合动态场景)。实际项目中推荐采用混合策略:先用帧差法做快速过滤,当帧差结果可疑时再用SSIM做二次确认,兼顾速度和准确度。

八、实战案例

将前面所学的各项技术综合起来,可以构建出功能强大的屏幕自动化应用。本节通过四个典型实战案例,展示图像识别与屏幕监控自动化在实际工作流中的完整应用。每个案例都结合了多种技术手段,并且提供了可直接运行的代码框架,读者可以根据自己的具体场景进行调整和扩展。

案例一:简单验证码自动识别

验证码识别是OCR技术的一个典型挑战场景。虽然现代深度学习在验证码识别上已经取得了很高的准确率,但对于简单的数字字母组合验证码,结合图像预处理和Tesseract仍然是一种快速有效的方案。核心技巧包括:对验证码图像进行适当的预处理(去背景、二值化、字符分割)、使用字符白名单限制识别范围、以及结合图像形态学操作去除干扰线。

def simple_captcha_recognizer(image_path): """简单验证码识别""" img = cv2.imread(image_path) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 1. 去噪 denoised = cv2.medianBlur(gray, 3) # 2. 二值化 _, binary = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 3. 去除干扰线(形态学操作) kernel = np.ones((2, 2), np.uint8) cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) # 4. 膨胀连通字符区域 kernel = np.ones((2, 2), np.uint8) dilated = cv2.dilate(cleaned, kernel, iterations=1) # 5. OCR识别(限定字符集) config = r'--oem 3 --psm 8 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' text = pytesseract.image_to_string(dilated, config=config) return text.strip() # 识别验证码 captcha_text = simple_captcha_recognizer('captcha_sample.png') print(f"验证码识别结果: {captcha_text}")

案例二:UI自动化测试——自动填写表单

将UI元素定位与鼠标键盘模拟结合,可以构建完整的UI自动化测试流程。以下示例演示了如何定位表单中的输入框、下拉菜单和提交按钮,并自动完成填写和提交操作。系统会在每个操作后截图留证,并通过OCR验证操作是否成功。

class FormAutomationTester: """UI表单自动化测试""" def __init__(self, template_dir): self.locator = UIElementLocator(template_dir) self.screenshots = [] def run_test_flow(self): """执行完整的表单测试流程""" test_steps = [ ('username_field', 'click_and_type', 'admin'), ('password_field', 'click_and_type', 'test1234'), ('login_button', 'click', None), ('menu_item', 'click', None), ('form_field_1', 'click_and_type', '测试数据'), ('submit_button', 'click', None), ] for step_name, action, value in test_steps: print(f"\n[步骤] {step_name} -> {action} {value or ''}") # 定位UI元素 result = self.locator.find_element(step_name) if not result: print(f" FAILED: 未找到元素 {step_name}") self.take_screenshot(f'failed_{step_name}') continue # 执行操作 cx, cy = result['center'] if action == 'click': pyautogui.click(cx, cy) elif 'type' in action: pyautogui.click(cx, cy) time.sleep(0.2) pyautogui.typewrite(value, interval=0.05) time.sleep(0.5) self.take_screenshot(f'step_{step_name}') print(f" PASSED: {step_name}") print("\n测试流程完成") return True def take_screenshot(self, name): timestamp = time.strftime('%H%M%S') filename = f'{name}_{timestamp}.png' pyautogui.screenshot(filename) self.screenshots.append(filename) # 运行测试 tester = FormAutomationTester('templates/') tester.run_test_flow()

案例三:屏幕特定图像出现告警

在运维监控场景中,经常需要等待屏幕出现特定图像后再执行后续操作。例如,等待软件安装完成界面的"完成"按钮出现、等待数据导出完成的提示图标出现、或等待某个错误弹窗出现并自动报警。以下实现了一个通用的"图像出现告警"系统,支持多目标同时监控、告警级别设定和自定义回调。

import threading import winsound class ScreenAlertMonitor: """屏幕图像出现告警监控器""" def __init__(self): self.watchers = [] def add_watcher(self, name, template_path, region=None, threshold=0.8, callback=None, alert_type='info'): """添加一个监控目标""" template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE) self.watchers.append({ 'name': name, 'template': template, 'region': region, 'threshold': threshold, 'callback': callback, 'alert_type': alert_type, 'triggered': False }) def start_monitoring(self, interval=0.5): """启动多目标同时监控""" print(f"启动屏幕告警监控,监控 {len(self.watchers)} 个目标...") try: while True: for watcher in self.watchers: if watcher['triggered']: continue # 截取屏幕 screen = ImageGrab.grab(watcher['region']) screen_np = np.array(screen) screen_gray = cv2.cvtColor(screen_np, cv2.COLOR_RGB2GRAY) # 模板匹配 result = cv2.matchTemplate( screen_gray, watcher['template'], cv2.TM_CCOEFF_NORMED ) _, max_val, _, _ = cv2.minMaxLoc(result) if max_val >= watcher['threshold']: watcher['triggered'] = True msg = f"[警报] 检测到 '{watcher['name']}'!置信度: {max_val:.2f}" print(msg) # 声音告警 if watcher['alert_type'] in ('warning', 'error'): winsound.Beep(1000, 500) # 执行回调 if watcher['callback']: watcher['callback'](watcher['name'], max_val) time.sleep(interval) except KeyboardInterrupt: print("监控已停止") # 告警回调函数 def alert_callback(name, confidence): pyautogui.screenshot(f'alert_{name}_{int(time.time())}.png') print(f"告警截图已保存,目标: {name}") # 使用示例 monitor = ScreenAlertMonitor() monitor.add_watcher('安装完成', 'finish_btn.png', alert_type='info', callback=alert_callback) monitor.add_watcher('错误弹窗', 'error_dialog.png', threshold=0.7, alert_type='error', callback=alert_callback) monitor.start_monitoring(interval=0.3)

案例四:游戏画面数据监控

对于游戏自动化场景,图像识别技术可以用于监控游戏画面中的数值变化、状态图标和事件提示。以下示例展示了如何监控游戏屏幕中的血量数值、状态图标和系统公告,并将数据记录到日志文件中,便于后续分析。

class GameScreenMonitor: """游戏画面数据监控""" def __init__(self, game_window_region): self.region = game_window_region self.data_log = [] def monitor_hp_mp(self, hp_region, mp_region, interval=1.0): """监控血量和魔法值""" while True: # 截取血量区域 hp_screen = ImageGrab.grab(self._adjust_region(hp_region)) hp_np = np.array(hp_screen) hp_text = pytesseract.image_to_string( hp_np, config='--psm 7 -c tessedit_char_whitelist=0123456789/' ).strip() # 截取魔法区域 mp_screen = ImageGrab.grab(self._adjust_region(mp_region)) mp_np = np.array(mp_screen) mp_text = pytesseract.image_to_string( mp_np, config='--psm 7 -c tessedit_char_whitelist=0123456789/' ).strip() timestamp = time.strftime('%H:%M:%S') log_entry = f"[{timestamp}] HP: {hp_text} | MP: {mp_text}" print(log_entry) self.data_log.append(log_entry) time.sleep(interval) def detect_combat_state(self, combat_icon_template, threshold=0.7): """检测是否处于战斗状态""" screen = ImageGrab.grab(self.region) screen_np = np.array(screen) screen_gray = cv2.cvtColor(screen_np, cv2.COLOR_RGB2GRAY) result = cv2.matchTemplate(screen_gray, combat_icon_template, cv2.TM_CCOEFF_NORMED) _, max_val, _, _ = cv2.minMaxLoc(result) return max_val >= threshold def _adjust_region(self, rel_region): """根据游戏窗口位置调整区域坐标""" return ( self.region[0] + rel_region[0], self.region[1] + rel_region[1], self.region[0] + rel_region[2], self.region[1] + rel_region[3] ) def export_log(self, filename='game_monitor_log.txt'): """导出监控日志""" with open(filename, 'w', encoding='utf-8') as f: for entry in self.data_log: f.write(entry + '\n') print(f"日志已导出到: {filename}") # 使用示例(假设游戏窗口在左上角 0,0) monitor = GameScreenMonitor((0, 0, 1920, 1080)) # monitor.monitor_hp_mp((50, 50, 200, 80), (50, 90, 200, 120))

核心要点:实战中需要根据场景灵活组合各项技术。验证码识别注重预处理技巧;UI自动化测试需要建立完善的元素定位库和测试流程;屏幕告警系统需要权衡监控频率和误报率;游戏监控则更注重OCR数值提取的准确性。无论哪种场景,日志记录和异常处理都是不可或缺的重要环节——自动化脚本只有具备了完善的自我诊断能力,才能真正投入生产环境使用。