项目简介
项目调用opencv配合dxgi完成对桌面和窗口的截图并进行不规则形状的模板匹配,并以梦幻西游为例,用来搜查鼠标位置(其实就是为了能写点游戏脚本玩玩)。
但最终目的其实是分别用纯C++、python搭配C++打包的dll、纯python 对比3种渠道的速度,对比结果如下
程序/数据集下载
本文章只发布于博客园、爆米算法和CSDN,被抄袭后可能排版错乱或下载失效,作者:爆米LiuChen
C++部分
FindWindow.h,窗口定位头文件
#pragma once
#include <dwmapi.h>
#include <windows.h>
#include <vector>
#include <string>
#include <iostream>
struct WindowData {
HWND handle;//窗口句柄
char title[256];//窗口标题
};
struct WindowRect
{
int x;
int y;
int width;
int height;
};
BOOL CALLBACK WindowEnumerationCallback(HWND hwnd, LPARAM lParam);
HWND getWindowHWND(std::string titleSection);
RECT getWindowLoc(HWND hwnd);
WindowRect getWindowRect(std::string titleSection);
extern WindowRect windowRect;
extern std::vector<WindowData> windowDatas;
FindWindow.cpp,核心函数getWindowRect,输入桌面窗口标题包含的字符串,返回窗口的位置坐标
#include "FindWindow.h"
static std::vector<WindowData> windowDatas;
WindowRect windowRect{ 0,0,0,0 };
// 声明回调函数
BOOL CALLBACK WindowEnumerationCallback(HWND hwnd, LPARAM lParam) {
// 通过IsWindow函数检查窗口是否有效
if (IsWindow(hwnd)) {
// 通过IsWindowEnabled函数检查窗口是否启用
if (IsWindowEnabled(hwnd)) {
// 通过IsWindowVisible函数检查窗口是否可见
if (IsWindowVisible(hwnd)) {
// 获取窗口的文本,并打印
char windowText[256];
GetWindowTextA(hwnd, windowText, sizeof(windowText));
WindowData windowData;
windowData.handle = hwnd;
memcpy(windowData.title, windowText, 256);
windowDatas.push_back(windowData);
}
}
}
// 继续枚举其他窗口
return TRUE;
}
//返回包含titleSection的桌面窗口句柄
HWND getWindowHWND(std::string titleSection)
{
HWND handle = NULL;
//每次都要清空容器
windowDatas.clear();
// 使用EnumWindows函数枚举所有窗口,并传递给回调函数处理
EnumWindows(WindowEnumerationCallback, NULL);
//一个个找包含指定字符串的
for (auto it = windowDatas.begin(); it != windowDatas.end(); it++)
{
char title[256];
memcpy(title, it->title, 256);
std::string windowTitle(title);
if (windowTitle.find(titleSection) != std::string::npos)
{
handle = it->handle;
}
}
return handle;
}
//根据窗口句柄和桌面缩放获得窗口尺寸和位置
RECT getWindowLoc(HWND hwnd)
{
RECT frame;
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));
//std::cout << "窗口位置:(" << frame.left << ", " << frame.top << ")" << std::endl;
//std::cout << "窗口大小:(" << frame.right-frame.left << ", " << frame.bottom-frame.top << ")" << std::endl;
return frame;
}
//根据窗口名获得窗口位置尺寸
WindowRect getWindowRect(std::string titleSection)
{
windowRect = {0,0,0,0};
HWND hwnd = getWindowHWND(titleSection);
if (hwnd == NULL) {
printf("cant find window contains %s\n", titleSection.c_str());
return windowRect;
}
//如果窗口小化 就将其展示
if (IsIconic(hwnd)) {
ShowWindow(hwnd, SW_RESTORE);
}
SetForegroundWindow(hwnd); // 将窗口置顶
RECT rect = getWindowLoc(hwnd); // 窗口位置
windowRect.x = rect.left;
windowRect.y = rect.top;
windowRect.width = rect.right - rect.left;
windowRect.height = rect.bottom - rect.top;
return windowRect;
}
DxgiCapture.h 截图头文件
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <stdio.h>
#include <opencv2/opencv.hpp>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#include <time.h>
#include "FindWindow.h"
struct MatInfo
{
int windowX;//窗口在桌面的x坐标
int windowY;//窗口在桌面的Y坐标
int windowWidth;//窗口宽
int windowHeight;//窗口高
uchar* pMatUchar;
};
extern "C" __declspec(dllexport) void initDxgi();
extern "C" __declspec(dllexport) cv::Mat getDesktopMat();
extern "C" __declspec(dllexport) cv::Mat getWindowMat(std::string titleSection = "desktop");
extern "C" __declspec(dllexport) MatInfo getWindowMatInfo(const char* pTitleSection="desktop");
DxgiCapture.cpp 核心函数为getWindowMatInfo,输入窗口标题包含的字符串,返回窗口坐标和opencv的矩阵uchar*
#include "DxgiCapture.h"
static cv::Mat regionMat;
static cv::Mat regionMatClone;
static cv::Mat emptyMat;
static cv::Mat desktopMat;
static DXGI_OUTDUPL_FRAME_INFO frameInfo;
static HRESULT hr;
static D3D_DRIVER_TYPE DriverTypes[3];
static UINT NumDriverTypes;
static D3D_FEATURE_LEVEL FeatureLevels[4];
static UINT NumFeatureLevels;
static D3D_FEATURE_LEVEL FeatureLevel;
static ID3D11Device* _pDX11Dev;
static ID3D11DeviceContext* _pDX11DevCtx;
static IDXGIDevice* _pDXGIDev;
static IDXGIAdapter* _pDXGIAdapter;
static IDXGIOutput* _pDXGIOutput;
static DXGI_OUTPUT_DESC DesktopDesc;
static IDXGIOutput1* _pDXGIOutput1;
static IDXGIOutputDuplication* _pDXGIOutputDup;
static DXGI_MAPPED_RECT MappedSurface;
static IDXGIResource* desktopResource;
static ID3D11Texture2D* _pDX11Texture;
static ID3D11Texture2D* _pCopyBuffer;
static int desktopWidth, desktopHeight;
static byte* pDesktopByte;
/* 获取屏幕缩放值 */
double getZoom()
{
// 获取窗口当前显示的监视器
HWND hWnd = GetDesktopWindow();
HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
// 获取监视器逻辑宽度
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
GetMonitorInfo(hMonitor, &monitorInfo);
int cxLogical = (monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
// 获取监视器物理宽度
DEVMODE dm;
dm.dmSize = sizeof(dm);
dm.dmDriverExtra = 0;
EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
int cxPhysical = dm.dmPelsWidth;
return cxPhysical * 1.0 / cxLogical;
}
//获得桌面二进制数据指针
cv::Mat getDesktopMat()
{
desktopResource = nullptr;
hr = _pDXGIOutputDup->AcquireNextFrame(10, &frameInfo, &desktopResource);
if (FAILED(hr))
{ //没有可用的刷新帧 返回上一帧
if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
if (desktopResource) {
desktopResource->Release();
desktopResource = nullptr;
}
hr = _pDXGIOutputDup->ReleaseFrame();
return desktopMat;
}
else
{
return emptyMat;
}
}
_pDX11Texture = nullptr;
// query next frame staging buffer 查询下一帧暂存缓冲区
if (desktopResource == nullptr) {
return emptyMat;
}
hr = desktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&_pDX11Texture));
desktopResource->Release();
desktopResource = nullptr;
if (FAILED(hr)) {
return emptyMat;
}
_pCopyBuffer = nullptr;
D3D11_TEXTURE2D_DESC desc;
// copy old description 复制旧描述
if (_pDX11Texture)
{
_pDX11Texture->GetDesc(&desc);
}
else if (_pCopyBuffer)
{
_pCopyBuffer->GetDesc(&desc);
}
else
{
return emptyMat;
}
// create a new staging buffer for fill frame image 为填充帧图像创建一个新的暂存缓冲区
if (_pCopyBuffer == nullptr) {
D3D11_TEXTURE2D_DESC CopyBufferDesc;
CopyBufferDesc.Width = desc.Width;
CopyBufferDesc.Height = desc.Height;
CopyBufferDesc.MipLevels = 1;
CopyBufferDesc.ArraySize = 1;
CopyBufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
CopyBufferDesc.SampleDesc.Count = 1;
CopyBufferDesc.SampleDesc.Quality = 0;
CopyBufferDesc.Usage = D3D11_USAGE_STAGING;
CopyBufferDesc.BindFlags = 0;
CopyBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
CopyBufferDesc.MiscFlags = 0;
hr = _pDX11Dev->CreateTexture2D(&CopyBufferDesc, nullptr, &_pCopyBuffer);
if (FAILED(hr)) {
return emptyMat;
}
}
if (_pDX11Texture)
{
// copy next staging buffer to new staging buffer 将下一个暂存缓冲区复制到新的暂存缓冲区
_pDX11DevCtx->CopyResource(_pCopyBuffer, _pDX11Texture);
}
IDXGISurface* CopySurface = nullptr;
// create staging buffer for map bits 为映射位创建暂存缓冲区
hr = _pCopyBuffer->QueryInterface(__uuidof(IDXGISurface), (void**)&CopySurface);
if (FAILED(hr)) {
return emptyMat;
}
// copy bits to user space 将位复制到用户空间
hr = CopySurface->Map(&MappedSurface, DXGI_MAP_READ);
CopySurface->Unmap();
hr = CopySurface->Release();
CopySurface = nullptr;
if (_pDXGIOutputDup)
{
hr = _pDXGIOutputDup->ReleaseFrame();
}
cv::Mat mat = cv::Mat(desktopHeight, desktopWidth, CV_8UC4, MappedSurface.pBits);
cv::cvtColor(mat, desktopMat, cv::COLOR_BGRA2BGR);// 转换为3通道图片
_pCopyBuffer->Release();
_pDX11Texture->Release();
_pDX11DevCtx->Release();
return desktopMat;
}
//根据窗口标题是否包含该字符串,获得窗口截图
cv::Mat getWindowMat(std::string titleSection)
{
getDesktopMat();
WindowRect rect;
if (titleSection != "desktop") {
rect = getWindowRect(titleSection);
regionMatClone = desktopMat(cv::Range(rect.y, rect.y + rect.height), cv::Range(rect.x, rect.x + rect.width));
regionMat = regionMatClone.clone();
return regionMat;
}
windowRect = { 0,0,desktopWidth,desktopHeight };//桌面截图 则它的窗口信息就是这样
regionMat = desktopMat;
return desktopMat;
}
void initDxgi()
{
double zoom = getZoom();
desktopWidth = GetSystemMetrics(SM_CXSCREEN) * zoom;
desktopHeight = GetSystemMetrics(SM_CYSCREEN) * zoom;
_pDXGIOutputDup = nullptr;
_pDX11Dev = nullptr;
D3D_DRIVER_TYPE DriverTypes[] = { D3D_DRIVER_TYPE_HARDWARE,D3D_DRIVER_TYPE_WARP,D3D_DRIVER_TYPE_REFERENCE, D3D_DRIVER_TYPE_UNKNOWN };
UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_1,D3D_FEATURE_LEVEL_10_0,D3D_FEATURE_LEVEL_9_1 };
UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
_pDX11DevCtx = nullptr;
// Create D3D device 创建D3D设备
for (UINT index = 0; index < NumDriverTypes; index++)
{
hr = D3D11CreateDevice(nullptr,
DriverTypes[index],
nullptr, 0,
NULL,
0,
D3D11_SDK_VERSION,
&_pDX11Dev,
&FeatureLevel,
&_pDX11DevCtx);
if (SUCCEEDED(hr)) {
break;
}
}
_pDXGIOutput = nullptr;
_pDXGIOutput1 = nullptr;
_pDXGIDev = nullptr;
// Get DXGI device 获取 DXGI 设备
hr = _pDX11Dev->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&_pDXGIDev));
if (FAILED(hr)) {
return;
}
IDXGIAdapter* _pDXGIAdapter = nullptr;
// Get DXGI adapter 获取 DXGI 适配器
hr = _pDXGIDev->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&_pDXGIAdapter));
if (FAILED(hr)) {
return;
}
UINT i = 0;
// Get output 获取输出
hr = _pDXGIAdapter->EnumOutputs(i, &_pDXGIOutput);
if (FAILED(hr)) {
return;
}
// Get output description struct 获取输出描述结构
_pDXGIOutput->GetDesc(&DesktopDesc);
// QI for Output1 请求接口给Output1
hr = _pDXGIOutput->QueryInterface(__uuidof(IDXGIOutput1), reinterpret_cast<void**>(&_pDXGIOutput1));
if (FAILED(hr)) {
return;
}
// Create desktop duplication 创建桌面副本
hr = _pDXGIOutput1->DuplicateOutput(_pDX11Dev, &_pDXGIOutputDup);
if (FAILED(hr)) {
return;
}
getWindowMat();
getWindowMat();
}
MatInfo getWindowMatInfo(const char* pTitleSection)
{
std::string titleSection;
titleSection.insert(0, pTitleSection);
MatInfo matInfo{0,0,0,0,nullptr};
getWindowMat(titleSection);
if (regionMat.empty()) {
return matInfo;
}
matInfo.windowHeight = regionMat.rows;
matInfo.windowWidth = regionMat.cols;
matInfo.windowX = windowRect.x;
matInfo.windowY = windowRect.y;
matInfo.pMatUchar = static_cast<uchar*>(regionMat.data);
return matInfo;
}
OpenDetect.h 模板匹配头文件
#pragma once
#include "DxgiCapture.h"
#include <unordered_map>
struct MatchResult
{
int xInWindow;//模板匹配在窗口中的x坐标
int yInWindow;//模板匹配在窗口中的Y坐标
int xInDesktop;//模板匹配在桌面中的X坐标
int yInDesktop;//模板匹配在桌面中的Y坐标
int windowX;//窗口在桌面的x坐标
int windowY;//窗口在桌面的Y坐标
int windowWidth;//窗口宽
int windowHeight;//窗口高
int templateWidth;//模板宽
int templateHeight;//模板高
float diff;//匹配指标 差异度 越小越好
bool success;//匹配是否成功
};
struct TemplateAndMask
{
cv::Mat templateMat;//模板矩阵
cv::Mat maskMat;//掩膜矩阵
};
extern "C" __declspec(dllexport) void addTemplateMask(const char* pKey, const char* pTemplatePath, const char* pMaskPath = "");
extern "C" __declspec(dllexport) MatchResult matchInWindow(const char* pKey, const char* pTitleSection = "desktop");
OpenDetect.cpp 模板匹配,核心函数matchInWindow,输入模板名和窗口标题返回模板匹配后的位置,这里要配合掩膜才能匹配不规则模板
#include "OpenDetect.h"
static MatchResult matchResult;
static std::unordered_map<std::string, TemplateAndMask> templateMatMap;
//添加模板和掩膜到字典 入参 关键词 模板路径 掩膜路径
void addTemplateMask(const char* pKey, const char* pTemplatePath, const char* pMaskPath)
{
std::string key, templatePath, maskPath;
key.insert(0, pKey);
templatePath.insert(0, pTemplatePath);
maskPath.insert(0, pMaskPath);
cv::Mat maskMat;
cv::Mat templateMat = cv::imread(templatePath);
if (maskPath != "") {
maskMat = cv::imread(maskPath, -1);
// 根据透明通道修改像素值 透明通道值为0 则像素值置0
for (int y = 0; y < maskMat.rows; ++y) {
for (int x = 0; x < maskMat.cols; ++x) {
// 获取像素值,注意通道顺序是BGR(A)而不是RGBA
cv::Vec4b pixel = maskMat.at<cv::Vec4b>(y, x);
if (pixel[3] == 0) {
maskMat.at<cv::Vec4b>(y, x) = cv::Vec4b(0, 0, 0, 0);
}
}
}
//已经根据透明通道做了置0,已经不需要透明通道
cv::cvtColor(maskMat, maskMat, cv::COLOR_BGRA2BGR);
//转为灰度图
cv::cvtColor(maskMat, maskMat, cv::COLOR_BGR2GRAY);
//二值化
cv::threshold(maskMat, maskMat, 1, 255, cv::THRESH_BINARY);
//转换为3通道图片 因为每个通道有单独的遮罩
std::vector<cv::Mat> channels = { maskMat, maskMat, maskMat };
cv::merge(channels, maskMat);
}
if (maskMat.empty()) {
printf("add %s template without mask\n", key.c_str());
}
else {
printf("add %s template with mask\n", key.c_str());
}
TemplateAndMask tam = { templateMat,maskMat };
templateMatMap[key] = tam;
}
//寻找窗口标题若为desktop则就是对桌面截图 根据掩膜和模板匹配结果
MatchResult matchInWindow(const char* pKey, const char* pTitleSection)
{
std::string key, titleSection;
key.insert(0, pKey);
titleSection.insert(0, pTitleSection);
TemplateAndMask tam = templateMatMap[key];
cv::Mat templateMat = tam.templateMat;
cv::Mat maskMat = tam.maskMat;
cv::Mat windowMat = getWindowMat(titleSection);
if (windowMat.empty()) {
matchResult.success = FALSE;//没找到窗口
return matchResult;
}
cv::Mat result;//模板匹配
//cv::matchTemplate(windowMat, templateMat, result, cv::TM_SQDIFF_NORMED);//不用mask的对比
cv::matchTemplate(windowMat, templateMat, result, cv::TM_SQDIFF_NORMED, maskMat);//mask的对比
double minVal, maxVal;
cv::Point minLoc, maxLoc;
cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);
//匹配结果返回 取差异化最小值
matchResult.xInWindow = minLoc.x;
matchResult.yInWindow = minLoc.y;
matchResult.diff = minVal;
matchResult.templateWidth = templateMat.cols;
matchResult.templateHeight = templateMat.rows;
matchResult.xInDesktop = minLoc.x + windowRect.x;
matchResult.yInDesktop = minLoc.y + windowRect.y;
matchResult.windowX = windowRect.x;
matchResult.windowY = windowRect.y;
matchResult.windowWidth = windowRect.width;
matchResult.windowHeight = windowRect.height;
matchResult.success = TRUE;
return matchResult;
}
main.cpp 调用上面的函数 进行测试
#include "OpenDetect.h"
#include <opencv2/opencv.hpp>
#include <time.h>
int main()
{
clock_t start, finish;
double durationS;
cv::Mat mat;
MatchResult result;
initDxgi();
printf("开始截屏测试\n");
start = clock();
for (int i=0;i<1000;i++)
{
//MatInfo matInfo = getWindowMatInfo();
//cv::Mat mat(matInfo.windowHeight, matInfo.windowWidth, CV_8UC3, matInfo.pMatUchar);
mat = getDesktopMat();
}
finish = clock();
durationS = (double)(finish - start) / CLOCKS_PER_SEC;
printf("1000次桌面截图并转为opencv平均%.1f毫秒/帧\n", durationS);
printf("开始模板匹配测试\n");
addTemplateMask("mouse1","1号鼠标模板.png","1号鼠标遮罩.png");
addTemplateMask("mouse2", "2号鼠标模板.png", "");
getWindowMatInfo("梦幻");
start = clock();
for (int i = 0; i < 10; i++) {
result = matchInWindow("mouse1", "梦幻");
}
finish = clock();
durationS = (double)(finish - start) / CLOCKS_PER_SEC;
printf("10次桌面模板匹配平均%.0f毫秒/次\n", durationS/10*1000);
mat = getWindowMat();
cv::rectangle(mat, cv::Rect(result.xInDesktop, result.yInDesktop,result.templateWidth, result.templateHeight), cv::Scalar(0, 0, 255), 2);
cv::imshow("", mat);
cv::waitKey(0);
return 0;
}
python部分
将上面的C++代码打包成dll,然后python调用dll与不调用作对比
import cv2
import ctypes
import numpy as np
import d3dshot
import time
print("python-----------------------------------------")
d = d3dshot.create(capture_output="numpy")
d.display = d.displays[0]
start = time.time()
for i in range(1000):
img = d.screenshot()
end = time.time()
print("python 1000次桌面截图平均耗时%.1f毫秒/次"%(end-start))
#读取模板 遮罩
template = cv2.imdecode(np.fromfile("1号鼠标模板.png", dtype=np.uint8), -1)[:, :, :3]
mask = cv2.imdecode(np.fromfile("1号鼠标遮罩.png", dtype=np.uint8), -1)[:, :, :4]
w, h = template.shape[0],template.shape[1]
#遮罩透明点设为0权重
for i in range(mask.shape[0]):
for j in range(mask.shape[1]):
if mask[i,j,3]==0:
mask[i,j,:3]=(0,0,0)
mask = mask[:,:,:3]
#遮罩二值化
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
_,mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)
mask = np.stack([mask,mask,mask],axis=-1)
w, h = template.shape[0],template.shape[1]
#模板匹配
start = time.time()
for i in range(10):
img = d.screenshot()
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
res = cv2.matchTemplate(img,template,cv2.TM_SQDIFF_NORMED,mask=mask)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
end = time.time()
print("python 10次桌面模板匹配平均耗时%.1f毫秒/次"%((end-start)/10*1000))
#可视化
top_left = min_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img, top_left, bottom_right, (0,0,255), 2)
cv2.imshow("test", img)
cv2.waitKey(0)
raise Exception("注释掉啊混蛋")
print("注意运行下面代码需要注释掉上面的代码 因为dxgi重复初始化 python+dll-----------------------------------------")
#载入dll
openDetect = ctypes.WinDLL('OpenDetectDLL.dll')
openDetect.initDxgi()#dxgi截图需要初始化
#窗口截图矩阵信息
class MatInfo(ctypes.Structure):
_fields_ = [("windowX",ctypes.c_int),("windowY",ctypes.c_int),("windowWidth",ctypes.c_int),("windowHeight",ctypes.c_int),("pMatByte",ctypes.POINTER(ctypes.c_uint8))]
#getWindowMatInfo函数 输入 窗口标题 返回 窗口截图矩阵信息
openDetect.getWindowMatInfo.argtypes = [ctypes.c_char_p,]
openDetect.getWindowMatInfo.restype = MatInfo
start = time.time()
for i in range(1000):
t = time.time()
matInfo = openDetect.getWindowMatInfo(bytes("desktop","gbk"))
mat = np.ctypeslib.as_array(matInfo.pMatByte, shape=(matInfo.windowHeight,matInfo.windowWidth, 3))
end = time.time()
print("python+dll 1000次桌面截图平均耗时%.1f毫秒/次"%(end-start))
#addTemplateMask 增加模板遮罩对 函数输入 模板名 模板路径 遮罩路径
openDetect.addTemplateMask.argtypes = [ctypes.c_char_p,ctypes.c_char_p,ctypes.c_char_p,]
openDetect.addTemplateMask.restype = None
openDetect.addTemplateMask(bytes("mouse1","gbk"),bytes("1号鼠标模板.png","gbk"),bytes("1号鼠标遮罩.png","gbk"));
openDetect.addTemplateMask(bytes("mouse2","gbk"), bytes("2号鼠标模板.png","gbk"), bytes("","gbk"));
#模板匹配结果
class MatchResult(ctypes.Structure):
_fields_ = [
("xInWindow",ctypes.c_int),("yInWindow",ctypes.c_int),
("xInDesktop",ctypes.c_int),("yInDesktop",ctypes.c_int),
("windowX",ctypes.c_int),("windowY",ctypes.c_int),
("windowWidth",ctypes.c_int),("windowHeight",ctypes.c_int),
("templateWidth",ctypes.c_int),("templateHeight",ctypes.c_int),
("diff",ctypes.c_float),("success",ctypes.c_bool),
]
#matchInWindow 模板匹配 输入 模板名 窗口包含文字 返回匹配结构体
openDetect.matchInWindow.argtypes = [ctypes.c_char_p,ctypes.c_char_p,]
openDetect.matchInWindow.restype = MatchResult
openDetect.getWindowMatInfo(bytes("梦幻","gbk"))#让梦幻西游窗口前置
start = time.time()
for i in range(10):
result = openDetect.matchInWindow(bytes("mouse1","gbk"),bytes("desktop","gbk"))
end = time.time()
print("python+dll 10次桌面模板匹配平均耗时%.1f毫秒/次"%((end-start)/10*1000))
matInfo = openDetect.getWindowMatInfo(bytes("desktop","gbk"))
mat = np.ctypeslib.as_array(matInfo.pMatByte, shape=(matInfo.windowHeight,matInfo.windowWidth, 3))
cv2.rectangle(mat, (result.xInDesktop, result.yInDesktop), (result.xInDesktop+result.templateWidth, result.yInDesktop+result.templateHeight), (0,0, 255), 2)
cv2.imwrite("result.png",mat)
cv2.imshow("test", mat)
cv2.waitKey(0)
经测试,C++、python+dll、python都能得到下面模板匹配的截图,至于速度的对比结果请见开头