yolo自动化项目实例解析(九) 导航

devtools/2024/11/23 12:50:34/

比如我们经常使用的导航,说白了就是寻找两点之间最近的路径,也就是所谓的寻路,我们需要想办法让程序知道他要去哪里,路径包含(起点、轨迹、终点)

一、录制轨迹

从平面角度来看,我们可以把区域视为一张大地图,而我们当前区域附近的区域视为小地图,当我们在大地图中的区域时,我们需要根据当前的区域位置来判断一下当前位于大地图中的什么坐标,首先我们就需要根据大小地图去定位坐标

1、sift特征匹配算法

该函数使用 SIFT(Scale-Invariant Feature Transform,尺度不变特征变换)算法来在一张大图(big_img)中寻找另一张小图(small_img)的位置。SIFT 是一种用于图像处理和计算机视觉领域的特征检测算法,它能够识别出图像中的关键点,并且这些关键点对光照变化、旋转等具有一定的不变性。

    def find_img_all_sift(self, big_img, small_img, roi):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:param roi: 感兴趣区域 (ROI):return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]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 [{"result": tuple(map(int, dst[0][0])), "rectangle": [tuple(map(int, p[0])) for p in dst]}]return []

就是大图匹配小图坐标

使用案例
import cv2
import numpy as npdef find_img_all_sift(big_img, small_img, roi=None):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:param roi: 感兴趣区域 (ROI),默认为None表示整个图像:return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)rectangle = [tuple(map(int, p[0])) for p in dst]center_x = int(sum(p[0] for p in rectangle) / 4)center_y = int(sum(p[1] for p in rectangle) / 4)return [{"result": tuple(map(int, dst[0][0])), "rectangle": rectangle, "center": (center_x, center_y)}]return []# 加载图像
big_image_path = 'datu.png'
small_image_path = 'xiaotu.png'big_image = cv2.imread(big_image_path, cv2.IMREAD_GRAYSCALE)
small_image = cv2.imread(small_image_path, cv2.IMREAD_GRAYSCALE)# 验证图像是否成功加载
if big_image is None:raise FileNotFoundError(f"无法加载大图: {big_image_path}")
if small_image is None:raise FileNotFoundError(f"无法加载小图: {small_image_path}")# 调用函数
results = find_img_all_sift(big_image, small_image, None)
for result in results:print(f"匹配点坐标 {result['result']}   匹配点4个顶点坐标 {result['rectangle']}   中心点坐标 {result['center']}")
绘制小图在大图中的坐标
# 在原图上绘制矩形框和中心点
big_image_color = cv2.cvtColor(big_image, cv2.COLOR_GRAY2BGR)
for result in results:rectangle = result['rectangle']center = result['center']#绘制框架cv2.polylines(big_image_color, [np.array(rectangle)], True, (0, 255, 0), 2)#绘制中心红点cv2.circle(big_image_color, center, 5, (0, 0, 255), -1)  # 显示结果
cv2.imshow('Matched Locations', big_image_color)
cv2.waitKey(0)
cv2.destroyAllWindows()

思路

首先我们先详细怎么才能实现寻路功能

寻路,顾名思义就是寻找正确到达目标的一个路径,但是怎么能让机器知道我们要去哪里呢,
就是通过不断的给他新的坐标,然后进行移动

坐标怎么获取我们上面已经研究过了,通过大图和小图的特征进行匹配截图

我们小图可以看作是我们游戏中的小地图,众所周知,大部分游戏的小地图都是一个轮盘样式的,固定在某个区域内,当我们人物移动的时候,人物会在小地图上的坐标会发生偏移,也就是说我们可以视为,人物本身没有变化,而地图发生了拖拽,小地图在大地图中的坐标发生了偏移

我们上面拿到了小地图在大地图中的坐标和中心点, 我们可以通过在屏幕中循环截取小地图的坐标变化,从而保留出人物在大地图上的运行轨迹

2、图片截取

我们小地图的区域是会不断变化的,我们不可能准备所有区域的小地图,更加希望是在实时获取当前小地图中的信息然后再去做判断,通过这个方法去截取指定区域的图片作为小地图用于识别

import ctypes
import cv2
import numpy as np
import win32gui
import win32uidef screenshot(hwnd, left, top, right, bottom):# 获取窗口设备上下文hwndDC = win32gui.GetWindowDC(hwnd)mfcDC = win32ui.CreateDCFromHandle(hwndDC)saveDC = mfcDC.CreateCompatibleDC()# 获取窗口大小rect = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - top# 创建位图saveBitMap = win32ui.CreateBitmap()saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)saveDC.SelectObject(saveBitMap)# 设置剪切区域saveDC.SetWindowExt((width, height))saveDC.SetViewportExt((width, height))saveDC.SetWindowOrg((left, top))saveDC.SetViewportOrg((0, 0))# 截图result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)if result == 0:print("PrintWindow failed")return Nonebmpinfo = saveBitMap.GetInfo()bmpstr = saveBitMap.GetBitmapBits(True)# 转换为 OpenCV 图像im_cv = np.frombuffer(bmpstr, dtype='uint8')im_cv = im_cv.reshape((height, width, 4))# 清理资源win32gui.DeleteObject(saveBitMap.GetHandle())saveDC.DeleteDC()mfcDC.DeleteDC()win32gui.ReleaseDC(hwnd, hwndDC)return im_cv[:, :, :3]  # 去掉 alpha 通道# 定义
hwnd = 65800  # 桌面句柄
map_region = (100, 100, 200, 200)  # 左  上  右  下
left, top, right, bottom = map_region
small_img = screenshot(hwnd, left, top, right, bottom)# 保存截图
output_path = "screenshot.png"
cv2.imwrite(output_path, small_img)
print(f"截图已保存到: {output_path}")

3、截图坐标判定

我们截取到的图片是一个区域的矩形,我们需要先获取出矩形的中心点,这个中心点也被视为我们自身当前的位置

import time
import pyautoguidef get_screenshot_area(center_x, center_y, width, height):"""根据中心点和宽高计算截图区域:param center_x: 中心点的 x 坐标:param center_y: 中心点的 y 坐标:param width: 截图区域的宽度:param height: 截图区域的高度:return: (left, top, right, bottom)"""half_width = width // 2half_height = height // 2left = center_x - half_widthtop = center_y - half_heightright = center_x + half_widthbottom = center_y + half_heightreturn left, top, right, bottomdef main():# 等待2秒time.sleep(2)# 获取鼠标当前位置mouse_x, mouse_y = pyautogui.position()print(f"鼠标位置: ({mouse_x}, {mouse_y})")# 计算截图区域(这里指定的截图的矩形大小)width = 80  # 你可以根据需要调整宽度height = 80  # 你可以根据需要调整高度left, top, right, bottom = get_screenshot_area(mouse_x, mouse_y, width, height)print(f"截图区域: ({left}, {top}, {right}, {bottom})")if __name__ == "__main__":main()

4、截图与特征匹配

结合上面的代码,当我们当前区域的小地图发生位置变化的时候,会记录下所有在大地图上的坐标

import timeimport cv2
import numpy as npdef find_img_all_sift(big_img, small_img, roi=None):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:param roi: 感兴趣区域 (ROI),默认为None表示整个图像:return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)rectangle = [tuple(map(int, p[0])) for p in dst]center_x = int(sum(p[0] for p in rectangle) / 4)center_y = int(sum(p[1] for p in rectangle) / 4)return [{"result": tuple(map(int, dst[0][0])), "rectangle": rectangle, "center": (center_x, center_y)}]return []import ctypes
import cv2
import numpy as np
import win32gui
import win32uidef screenshot(hwnd, left, top, right, bottom):# 获取窗口设备上下文hwndDC = win32gui.GetWindowDC(hwnd)mfcDC = win32ui.CreateDCFromHandle(hwndDC)saveDC = mfcDC.CreateCompatibleDC()# 获取窗口大小rect = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - top# 创建位图saveBitMap = win32ui.CreateBitmap()saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)saveDC.SelectObject(saveBitMap)# 设置剪切区域saveDC.SetWindowExt((width, height))saveDC.SetViewportExt((width, height))saveDC.SetWindowOrg((left, top))saveDC.SetViewportOrg((0, 0))# 截图result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)if result == 0:print("PrintWindow failed")return Nonebmpinfo = saveBitMap.GetInfo()bmpstr = saveBitMap.GetBitmapBits(True)# 转换为 OpenCV 图像im_cv = np.frombuffer(bmpstr, dtype='uint8')im_cv = im_cv.reshape((height, width, 4))# 清理资源win32gui.DeleteObject(saveBitMap.GetHandle())saveDC.DeleteDC()mfcDC.DeleteDC()win32gui.ReleaseDC(hwnd, hwndDC)return im_cv[:, :, :3]  # 去掉 alpha 通道#定义截图的句柄、窗口大小
hwnd = 65800  # 桌面句柄
map_region = (100, 100, 200, 200)  # 左  上  右  下
left, top, right, bottom = map_region
#定义大地图
datu = 'datu.png'
#格式化地图为NumPy 数组
big_image = cv2.imread(datu, cv2.IMREAD_GRAYSCALE)while True:time.sleep(2)# 获取截取的小地图(获取的已经是NumPy 数组不需要再转换)small_image = screenshot(hwnd, left, top, right, bottom)# #匹配大小地图的特征results = find_img_all_sift(big_image, small_image, None)#变量输出参数for result in results:print(f"匹配点坐标 {result['result']}   匹配点4个顶点坐标 {result['rectangle']}   中心点坐标 {result['center']}")

5、录制区域检查

我们绘制路径的时候一定不希望在切换窗口的时候依旧在绘制,这样会存在大量的误差,我们更希望在立刻指定窗口后暂停录制,下面做个判断

import timeimport cv2
import numpy as np
import win32apidef find_img_all_sift(big_img, small_img, roi=None):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:param roi: 感兴趣区域 (ROI),默认为None表示整个图像:return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)rectangle = [tuple(map(int, p[0])) for p in dst]center_x = int(sum(p[0] for p in rectangle) / 4)center_y = int(sum(p[1] for p in rectangle) / 4)return [{"result": tuple(map(int, dst[0][0])), "rectangle": rectangle, "center": (center_x, center_y)}]return []import ctypes
import cv2
import numpy as np
import win32gui
import win32uidef screenshot(hwnd, left, top, right, bottom):# 获取窗口设备上下文hwndDC = win32gui.GetWindowDC(hwnd)mfcDC = win32ui.CreateDCFromHandle(hwndDC)saveDC = mfcDC.CreateCompatibleDC()# 获取窗口大小rect = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - top# 创建位图saveBitMap = win32ui.CreateBitmap()saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)saveDC.SelectObject(saveBitMap)# 设置剪切区域saveDC.SetWindowExt((width, height))saveDC.SetViewportExt((width, height))saveDC.SetWindowOrg((left, top))saveDC.SetViewportOrg((0, 0))# 截图result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)if result == 0:print("PrintWindow failed")return Nonebmpinfo = saveBitMap.GetInfo()bmpstr = saveBitMap.GetBitmapBits(True)# 转换为 OpenCV 图像im_cv = np.frombuffer(bmpstr, dtype='uint8')im_cv = im_cv.reshape((height, width, 4))# 清理资源win32gui.DeleteObject(saveBitMap.GetHandle())saveDC.DeleteDC()mfcDC.DeleteDC()win32gui.ReleaseDC(hwnd, hwndDC)return im_cv[:, :, :3]  # 去掉 alpha 通道#检查活动窗口
def get_window_handle_at_mouse_position():#获取当前活动窗口# active_hwnd = ctypes.windll.user32.GetForegroundWindow()# return active_hwndpoint = win32api.GetCursorPos()hwnd = win32gui.WindowFromPoint(point)return hwnddef main():hwnd = 65800   #窗口句柄map_region = (100, 100, 200, 200) #截图矩形left, top, right, bottom = map_region#定义大地图及格式化datu = 'datu.png'# 格式化地图为NumPy 数组big_image = cv2.imread(datu, cv2.IMREAD_GRAYSCALE)#循环截图匹配特征while True:#检查鼠标所在的窗口是否为指定的窗口id,不是就停止if get_window_handle_at_mouse_position() != hwnd:print("鼠标离开了指定窗口!")time.sleep(0.5)continuetime.sleep(2)# 获取截取的小地图(获取的已经是NumPy 数组不需要再转换)small_image = screenshot(hwnd, left, top, right, bottom)# #匹配大小地图的特征results = find_img_all_sift(big_image, small_image, None)#变量输出参数for result in results:print(f"匹配点坐标 {result['result']}   匹配点4个顶点坐标 {result['rectangle']}   中心点坐标 {result['center']}")if __name__ == '__main__':main()

6、导航运行轨迹

import time
import traceback
import cv2
import numpy as np
import win32api
import win32gui
import win32ui
import ctypesdef find_img_all_sift(big_img, small_img):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)rectangle = [tuple(map(int, p[0])) for p in dst]center_x = int(sum(p[0] for p in rectangle) / 4)center_y = int(sum(p[1] for p in rectangle) / 4)return [{"result": tuple(map(int, dst[0][0])), "rectangle": rectangle, "center": (center_x, center_y)}]return []def screenshot(hwnd, left, top, right, bottom):# 获取窗口设备上下文hwndDC = win32gui.GetWindowDC(hwnd)mfcDC = win32ui.CreateDCFromHandle(hwndDC)saveDC = mfcDC.CreateCompatibleDC()# 获取窗口大小rect = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - top# 创建位图saveBitMap = win32ui.CreateBitmap()saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)saveDC.SelectObject(saveBitMap)# 设置剪切区域saveDC.SetWindowExt((width, height))saveDC.SetViewportExt((width, height))saveDC.SetWindowOrg((left, top))saveDC.SetViewportOrg((0, 0))# 截图result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)if result == 0:print("PrintWindow failed")return Nonebmpinfo = saveBitMap.GetInfo()bmpstr = saveBitMap.GetBitmapBits(True)# 转换为 OpenCV 图像im_cv = np.frombuffer(bmpstr, dtype='uint8')im_cv = im_cv.reshape((height, width, 4))# 清理资源win32gui.DeleteObject(saveBitMap.GetHandle())saveDC.DeleteDC()mfcDC.DeleteDC()win32gui.ReleaseDC(hwnd, hwndDC)return im_cv[:, :, :3]  # 去掉 alpha 通道def get_window_handle_at_mouse_position():point = win32api.GetCursorPos()hwnd = win32gui.WindowFromPoint(point)return hwnddef main():hwnd = 65800  # 窗口句柄map_region = (100, 100, 200, 200)  # 截图矩形left, top, right, bottom = map_region# 定义大地图及格式化datu = 'datu.png'big_image = cv2.imread(datu, cv2.IMREAD_GRAYSCALE)big_img = cv2.imdecode(np.fromfile(file=datu, dtype=np.uint8), cv2.IMREAD_COLOR)  # 加载大图big_height, big_width, _ = big_img.shapebig_img_yt = big_img.copy()# 创建一个窗口来显示大图,并设置窗口大小cv2.namedWindow('Matched Image', cv2.WINDOW_NORMAL)cv2.resizeWindow('Matched Image', big_width, big_height)while True:# 检查鼠标所在的窗口是否为指定的窗口id, 不是就停止if get_window_handle_at_mouse_position() != hwnd:print("鼠标离开了指定窗口!")time.sleep(0.5)continuetime.sleep(2)# 获取截取的小地图(获取的已经是NumPy 数组不需要再转换)small_image = screenshot(hwnd, left, top, right, bottom)if small_image is not None:# 匹配大小地图的特征results = find_img_all_sift(big_image, small_image)print(results)# 在大图上标记匹配点for result in results:result_post = [result["center"][0], result["center"][1]]cv2.circle(big_img_yt, result_post, 2, (255, 0, 0), -1)# 显示标记后的图像cv2.imshow('Matched Image', big_img_yt)# 按 'q' 键退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放资源cv2.destroyAllWindows()if __name__ == '__main__':main()

我们上面实时显示的时候发现,我们拖动图片向下,他就记录上面的坐标
反之,我们向上的时候他就记录向下的坐标 ,这里的圆心我们可以视为人物,当地图拖拽的时候我们人物进行移动

7、添加开关

import time
import traceback
import cv2
import keyboard
import numpy as np
import win32api
import win32gui
import win32ui
import ctypes
import threadingdef find_img_all_sift(big_img, small_img):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)rectangle = [tuple(map(int, p[0])) for p in dst]center_x = int(sum(p[0] for p in rectangle) / 4)center_y = int(sum(p[1] for p in rectangle) / 4)return [{"result": tuple(map(int, dst[0][0])), "rectangle": rectangle, "center": (center_x, center_y)}]return []def screenshot(hwnd, left, top, right, bottom):# 获取窗口设备上下文hwndDC = win32gui.GetWindowDC(hwnd)mfcDC = win32ui.CreateDCFromHandle(hwndDC)saveDC = mfcDC.CreateCompatibleDC()# 获取窗口大小rect = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - top# 创建位图saveBitMap = win32ui.CreateBitmap()saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)saveDC.SelectObject(saveBitMap)# 设置剪切区域saveDC.SetWindowExt((width, height))saveDC.SetViewportExt((width, height))saveDC.SetWindowOrg((left, top))saveDC.SetViewportOrg((0, 0))# 截图result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)if result == 0:print("PrintWindow failed")return Nonebmpinfo = saveBitMap.GetInfo()bmpstr = saveBitMap.GetBitmapBits(True)# 转换为 OpenCV 图像im_cv = np.frombuffer(bmpstr, dtype='uint8')im_cv = im_cv.reshape((height, width, 4))# 清理资源win32gui.DeleteObject(saveBitMap.GetHandle())saveDC.DeleteDC()mfcDC.DeleteDC()win32gui.ReleaseDC(hwnd, hwndDC)return im_cv[:, :, :3]  # 去掉 alpha 通道def get_window_handle_at_mouse_position():point = win32api.GetCursorPos()hwnd = win32gui.WindowFromPoint(point)return hwnddef main():hwnd = 65800  # 窗口句柄map_region = (100, 100, 200, 200)  # 截图矩形left, top, right, bottom = map_region# 定义大地图及格式化datu = 'datu.png'big_image = cv2.imread(datu, cv2.IMREAD_GRAYSCALE)big_img = cv2.imdecode(np.fromfile(file=datu, dtype=np.uint8), cv2.IMREAD_COLOR)  # 加载大图big_height, big_width, _ = big_img.shapebig_img_yt = big_img.copy()# 创建一个窗口来显示大图,并设置窗口大小cv2.namedWindow('Matched Image', cv2.WINDOW_NORMAL)cv2.resizeWindow('Matched Image', big_width, big_height)# 使用 threading.Event 来管理录制状态recording_event = threading.Event()def start_recording():recording_event.set()print("开始录制...")def stop_recording():recording_event.clear()print("停止录制...")# 注册按键监听keyboard.add_hotkey('f7', start_recording)keyboard.add_hotkey('f8', stop_recording)while True:# 检查鼠标所在的窗口是否为指定的窗口id, 不是就停止if get_window_handle_at_mouse_position() != hwnd:print("鼠标离开了指定窗口!")time.sleep(0.5)continuetime.sleep(2)# 基于按键判断if recording_event.is_set():print(111)# 获取截取的小地图(获取的已经是NumPy 数组不需要再转换)small_image = screenshot(hwnd, left, top, right, bottom)if small_image is not None:# 匹配大小地图的特征results = find_img_all_sift(big_image, small_image)print(results)# 在大图上标记匹配点for result in results:result_post = [result["center"][0], result["center"][1]]cv2.circle(big_img_yt, result_post, 2, (255, 0, 0), -1)# 显示标记后的图像cv2.imshow('Matched Image', big_img_yt)# 按 'q' 键退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放资源cv2.destroyAllWindows()if __name__ == '__main__':main()

8、去除动态显示,添加保存轨迹图

import time
import traceback
import cv2
import keyboard
import numpy as np
import win32api
import win32gui
import win32ui
import ctypes
import threadingdef find_img_all_sift(big_img, small_img):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)rectangle = [tuple(map(int, p[0])) for p in dst]center_x = int(sum(p[0] for p in rectangle) / 4)center_y = int(sum(p[1] for p in rectangle) / 4)return [{"result": tuple(map(int, dst[0][0])), "rectangle": rectangle, "center": (center_x, center_y)}]return []def screenshot(hwnd, left, top, right, bottom):# 获取窗口设备上下文hwndDC = win32gui.GetWindowDC(hwnd)mfcDC = win32ui.CreateDCFromHandle(hwndDC)saveDC = mfcDC.CreateCompatibleDC()# 获取窗口大小rect = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - top# 创建位图saveBitMap = win32ui.CreateBitmap()saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)saveDC.SelectObject(saveBitMap)# 设置剪切区域saveDC.SetWindowExt((width, height))saveDC.SetViewportExt((width, height))saveDC.SetWindowOrg((left, top))saveDC.SetViewportOrg((0, 0))# 截图result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)if result == 0:print("PrintWindow failed")return Nonebmpinfo = saveBitMap.GetInfo()bmpstr = saveBitMap.GetBitmapBits(True)# 转换为 OpenCV 图像im_cv = np.frombuffer(bmpstr, dtype='uint8')im_cv = im_cv.reshape((height, width, 4))# 清理资源win32gui.DeleteObject(saveBitMap.GetHandle())saveDC.DeleteDC()mfcDC.DeleteDC()win32gui.ReleaseDC(hwnd, hwndDC)return im_cv[:, :, :3]  # 去掉 alpha 通道def get_window_handle_at_mouse_position():point = win32api.GetCursorPos()hwnd = win32gui.WindowFromPoint(point)return hwnddef main():hwnd = 65800  # 窗口句柄map_region = (100, 100, 200, 200)  # 截图矩形left, top, right, bottom = map_region# 定义大地图及格式化datu = 'datu.png'big_image = cv2.imread(datu, cv2.IMREAD_GRAYSCALE)big_img = cv2.imdecode(np.fromfile(file=datu, dtype=np.uint8), cv2.IMREAD_COLOR)  # 加载大图big_height, big_width, _ = big_img.shapebig_img_yt = big_img.copy()# 使用 threading.Event 来管理录制状态recording_event = threading.Event()def start_recording():recording_event.set()print("开始录制...")def stop_recording():recording_event.clear()print("停止录制...")# 保存带有标记的图片cv2.imwrite('111.png', big_img_yt)print("图片已保存为 111.png")# 注册按键监听keyboard.add_hotkey('f7', start_recording)keyboard.add_hotkey('f8', stop_recording)while True:# 检查鼠标所在的窗口是否为指定的窗口id, 不是就停止if get_window_handle_at_mouse_position() != hwnd:print("鼠标离开了指定窗口!")time.sleep(0.5)continuetime.sleep(2)# 基于按键判断if recording_event.is_set():print(111)# 获取截取的小地图(获取的已经是NumPy 数组不需要再转换)small_image = screenshot(hwnd, left, top, right, bottom)if small_image is not None:# 匹配大小地图的特征results = find_img_all_sift(big_image, small_image)print(results)# 在大图上标记匹配点for result in results:result_post = [result["center"][0], result["center"][1]]cv2.circle(big_img_yt, result_post, 2, (255, 0, 0), -1)# 释放资源cv2.destroyAllWindows()if __name__ == '__main__':main()

9、基于轨迹查询坐标

我们关于标记还需要做一个改变,就是每次循环的时候,不是都会标记当前图片的点位,因为我可能这次循环的时候坐标没有发生变化,所以我需要判断只有坐标发生变化的时候才会在图中进行标记,而且当第一次进行标记的时候使用绿色标记,当录制结束的时候,最后一次标记的时候使用红色标记,而两次标记中间的标记都使用蓝色

并且红色和绿色的标记都置顶,不会被覆盖

import time
import traceback
import cv2
import keyboard
import numpy as np
import win32api
import win32gui
import win32ui
import ctypes
import threadingdef find_img_all_sift(big_img, small_img):"""使用 SIFT 特征匹配在大图中找到小图的匹配位置:param big_img: 大图:param small_img: 小图:return: 匹配结果列表"""# 使用 SIFT 特征匹配sift = cv2.SIFT_create()kp1, des1 = sift.detectAndCompute(big_img, None)kp2, des2 = sift.detectAndCompute(small_img, None)# 确保描述符类型为 float32des1 = des1.astype(np.float32)des2 = des2.astype(np.float32)bf = cv2.BFMatcher()matches = bf.knnMatch(des1, des2, k=2)good = []for m, n in matches:if m.distance < 0.75 * n.distance:good.append([m])if len(good) > 10:src_pts = np.float32([kp1[m[0].queryIdx].pt for m in good]).reshape(-1, 1, 2)dst_pts = np.float32([kp2[m[0].trainIdx].pt for m in good]).reshape(-1, 1, 2)M, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC, 5.0)h, w = small_img.shape[:2]pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, M)rectangle = [tuple(map(int, p[0])) for p in dst]center_x = int(sum(p[0] for p in rectangle) / 4)center_y = int(sum(p[1] for p in rectangle) / 4)return [{"result": tuple(map(int, dst[0][0])), "rectangle": rectangle, "center": (center_x, center_y)}]return []def screenshot(hwnd, left, top, right, bottom):# 获取窗口设备上下文hwndDC = win32gui.GetWindowDC(hwnd)mfcDC = win32ui.CreateDCFromHandle(hwndDC)saveDC = mfcDC.CreateCompatibleDC()# 获取窗口大小rect = win32gui.GetWindowRect(hwnd)width = right - leftheight = bottom - top# 创建位图saveBitMap = win32ui.CreateBitmap()saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)saveDC.SelectObject(saveBitMap)# 设置剪切区域saveDC.SetWindowExt((width, height))saveDC.SetViewportExt((width, height))saveDC.SetWindowOrg((left, top))saveDC.SetViewportOrg((0, 0))# 截图result = ctypes.windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)if result == 0:print("PrintWindow failed")return Nonebmpinfo = saveBitMap.GetInfo()bmpstr = saveBitMap.GetBitmapBits(True)# 转换为 OpenCV 图像im_cv = np.frombuffer(bmpstr, dtype='uint8')im_cv = im_cv.reshape((height, width, 4))# 清理资源win32gui.DeleteObject(saveBitMap.GetHandle())saveDC.DeleteDC()mfcDC.DeleteDC()win32gui.ReleaseDC(hwnd, hwndDC)return im_cv[:, :, :3]  # 去掉 alpha 通道def get_window_handle_at_mouse_position():point = win32api.GetCursorPos()hwnd = win32gui.WindowFromPoint(point)return hwnddef main():hwnd = 65800  # 窗口句柄map_region = (100, 100, 200, 200)  # 截图矩形left, top, right, bottom = map_region# 定义大地图及格式化datu = 'datu.png'big_image = cv2.imread(datu, cv2.IMREAD_GRAYSCALE)big_img = cv2.imdecode(np.fromfile(file=datu, dtype=np.uint8), cv2.IMREAD_COLOR)  # 加载大图big_height, big_width, _ = big_img.shapebig_img_yt = big_img.copy()# 使用 threading.Event 来管理录制状态recording_event = threading.Event()# 记录上一次的坐标last_center = Nonefirst_mark = Truemarks = []  # 用于存储所有标记def start_recording():nonlocal first_markfirst_mark = Truerecording_event.set()print("开始录制...")def stop_recording():nonlocal last_center, first_markrecording_event.clear()print("停止录制...")# 获取最后一次的截图small_image = screenshot(hwnd, left, top, right, bottom)if small_image is not None:results = find_img_all_sift(big_image, small_image)if results:current_center = results[0]["center"]if last_center is None or last_center != current_center:marks.append((current_center, (0, 0, 255)))  # 红色标记last_center = current_center# 绘制所有标记for mark_center, color in marks:cv2.circle(big_img_yt, mark_center, 2, color, -1)# 保存带有标记的图片cv2.imwrite('111.png', big_img_yt)print("图片已保存为 111.png")# 注册按键监听keyboard.add_hotkey('f7', start_recording)keyboard.add_hotkey('f8', stop_recording)while True:# 检查鼠标所在的窗口是否为指定的窗口id, 不是就停止if get_window_handle_at_mouse_position() != hwnd:print("鼠标离开了指定窗口!")time.sleep(0.5)continuetime.sleep(2)# 基于按键判断if recording_event.is_set():# 获取截取的小地图(获取的已经是NumPy 数组不需要再转换)small_image = screenshot(hwnd, left, top, right, bottom)if small_image is not None:# 匹配大小地图的特征results = find_img_all_sift(big_image, small_image)print(results)if results:current_center = results[0]["center"]if last_center is None or last_center != current_center:# 标记点color = (255, 0, 0)  # 蓝色if first_mark:color = (0, 255, 0)  # 绿色first_mark = Falsemarks.append((current_center, color))last_center = current_center# 释放资源cv2.destroyAllWindows()if __name__ == '__main__':main()

二、基于轨迹获取坐标

三、校对方向


http://www.ppmy.cn/devtools/136296.html

相关文章

Linux搭建MiniO

1、第一步&#xff0c;进入/opt目录&#xff0c;创建minio文件夹 输入以下命令 cd /opt mkdir minio 2、wget下载安装包 wget https://dl.minio.io/server/minio/release/linux-amd64/minio 3、在minio文件夹中创建log文件 cd /minio touch minio.log 4、修改权限 chmod 77…

Mybatis框架之单例模式 (Singleton Pattern)

MyBatis 框架中也使用到了单例模式 (Singleton Pattern)&#xff0c;主要体现在 SqlSessionFactory 的创建和管理上。通过单例模式&#xff0c;MyBatis 可以确保整个应用程序中只创建一个 SqlSessionFactory 实例&#xff0c;从而有效地管理数据库连接资源并提高性能。下面将详…

uView开发笔记

1.富文本框输入 <u-input input-alignright type"textarea" height"100" v-model"value" /> ​ return{textarea:textarea} 2.页面下拉刷新 onPullDownRefresh(){ } 3.swiper轮播图图片展示不全问题 增加属性&#xff1a; :img-mode&…

c与c++比较

实现将十六进制字符串转换为字节数组 c实现 - #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h>unsigned char* hexStringToByteArray(const char* hex_str, size_t* out_len) {size_t len = strlen(hex_str);size_…

原生微信小程序在顶部胶囊左侧水平设置自定义导航兼容各种手机模型

无论是在什么手机机型下&#xff0c;自定义的导航都和右侧的胶囊水平一条线上。如图下 以上图iphone12&#xff0c;13PRo 以上图是没有带黑色扇帘的机型 以下是调试器看的wxml的代码展示 注意&#xff1a;红色阔里的是自定义导航&#xff08;或者其他的logo啊&#xff0c;返回之…

气象指数推进光伏“靠天吃饭”?

随着可再生能源在电力系统中占比的不断提升&#xff0c;光伏发电“靠天吃饭”的特性引发看发电企业、用电企业及电网等主体对天气风险的关注。据小编所知&#xff0c;鹧鸪云光伏气象仿真系统采用Meteonorm作为气象数据源&#xff0c;真实可靠&#xff0c;助力新能源体系建设。 …

JavaWeb-表格标签-06

表格标签 table code: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>表格标签</title><…

TCP 三次握手和四次挥手

一、三次握手 (Three-Way Handshake) 概念&#xff1a; 三次握手用于在 TCP 连接建立时&#xff0c;确保客户端和服务器之间的通信信道可靠&#xff0c;并同步双方的初始序列号。 三次握手过程 第一次握手&#xff1a;客户端向服务器发送 SYN (synchronize) 报文&#xff0c;…