python与C++进行桌面截图和模板匹配对比——以梦幻西游为例

目录

项目简介

项目调用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都能得到下面模板匹配的截图,至于速度的对比结果请见开头

show4