c实现桌面截图鼠标周边区域及生成dll供python调用

文章目录


前言

为了方便opencv进行fps游戏指针附近目标检测,需要降低截图延迟。自带PIL库的ImageGrab功能速度较慢,这里尝试使用C实现截图后用python调用动态链接库的方法尝试加速,最后实测可快5倍左右,用时与python的mss库相近(该库也是调用c链接库实现)


一、动态链接库编写

1. C实现截图功能

目标是实现以指针为中心,截图桌面一个width*height的区域,我一般截图640*640

c 复制代码
#include <windows.h>
#include <stdio.h>

extern "C" {
    __declspec(dllexport) void CaptureDesktop(int width, int height, unsigned char* buffer) {
        POINT my_cursor;
        int centerX, centerY;
        GetCursorPos(&my_cursor);

        centerX = my_cursor.x;
        centerY = my_cursor.y;

        //
        
        // 获取屏幕设备上下文
        HDC hScreenDC = GetDC(NULL);
        // 创建内存设备上下文
        HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

        // 确保截图区域在屏幕范围内
        int left = centerX - (width / 2);
        int top = centerY - (height / 2);

        // 这一行代码创建了一个与指定设备兼容的位图。
        // CreateCompatibleBitmap 函数用于创建一个与给定设备上下文兼容的位图对象。
        // 在这里,它以屏幕设备上下文 hScreenDC 为基础,创建了一个宽度为 width,高度为 height 的位图对象。
        HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);
        // SelectObject 函数将位图对象 hBitmap 选择(或者说关联)到指定的设备上下文 hMemoryDC 中。
        // 这个步骤将创建的位图与内存设备上下文关联,以便进行后续的绘图操作。
        SelectObject(hMemoryDC, hBitmap);

        // BitBlt 函数执行位图的位块传输操作,从屏幕设备上下文 hScreenDC 中复制指定区域的图像到内存设备上下文 hMemoryDC 中的位图中。
        // 这里,它从屏幕上指定位置 (left, top) 复制一个宽为 width,高为 height 的区域到内存中的位图。
        BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY);

        // 获取位图数据
        BITMAPINFOHEADER bi;
        bi.biSize = sizeof(BITMAPINFOHEADER);
        bi.biWidth = width;
        bi.biHeight = -height; // 垂直反转,使图片正向显示
        bi.biPlanes = 1;
        bi.biBitCount = 24; // 24位色彩
        bi.biCompression = BI_RGB;
        bi.biSizeImage = 0;
        bi.biXPelsPerMeter = 0;
        bi.biYPelsPerMeter = 0;
        bi.biClrUsed = 0;
        bi.biClrImportant = 0;

        //GetDIBits 函数用于检索与设备无关位图的数据。
        // 它将位图 hBitmap 中的像素数据复制到一个缓冲区 buffer 中。
        // BITMAPINFO 结构体 bi 包含了有关位图的信息,如宽度、高度、色彩深度等。
        // 这里的参数设置了从 hMemoryDC 和 hBitmap 中提取数据,并将提取的数据以 RGB 格式存储在 buffer 中。
        GetDIBits(hMemoryDC, hBitmap, 0, height, buffer, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

        // 释放资源
        DeleteObject(hBitmap);
        DeleteDC(hMemoryDC);
        ReleaseDC(NULL, hScreenDC);
    }
}

int main() {
    const int screenWidth = 640;
    const int screenHeight = 640;
    const int bufferSize = screenWidth * screenHeight * 3; // 3 bytes per pixel for 24-bit color depth

    unsigned char* pixelBuffer = new unsigned char[bufferSize]; // 分配足够大的缓冲区
    CaptureDesktop(640, 640, pixelBuffer);
    return 0;
}

2. 生成动态链接库

修改visual studio的项目属性,改exe生成为dll生成

二、python调用

1.使用方法

python 复制代码
import ctypes
import numpy as np
from PIL import Image

# 加载DLL
screenshot_dll = ctypes.CDLL('path/to/your/dll/screenshot.dll')

# 定义函数原型
screenshot_dll.CaptureDesktop.argtypes = [
    ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_ubyte)
]
screenshot_dll.CaptureDesktop.restype = None

def capture_screen(width, height):
    buffer_size = width * height * 3  # 3 channels (RGB)
    buffer = (ctypes.c_ubyte * buffer_size)()
    screenshot_dll.CaptureDesktop(width, height, buffer)

    # 将截图数据转换为NumPy数组
    image_data = np.frombuffer(buffer, dtype=np.uint8)
    image_data = image_data.reshape((height, width, 3))

    return Image.fromarray(image_data)

# 使用示例
width, height = 640, 640  # 截图尺寸

screenshot = capture_screen(width, height)
screenshot.show()  # 显示截图

2.截图计时

将上述python函数封装好,截图100次,大小640*640,本人用时结果如下:

同时,使用python mss库的结果如下:

二者结果几乎一致。

仔细研究发现,C中内存拷贝一句代码就占据了整个过程90%的时间,因此暂时没有更好的降低截图延迟办法


相关推荐
何大春4 分钟前
【弱监督语义分割】Self-supervised Image-specific Prototype Exploration for WSSS 论文阅读
论文阅读·人工智能·python·深度学习·论文笔记·原型模式
在下不上天12 分钟前
Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复
大数据·开发语言·python
SEVEN-YEARS15 分钟前
深入理解TensorFlow中的形状处理函数
人工智能·python·tensorflow
EterNity_TiMe_20 分钟前
【论文复现】(CLIP)文本也能和图像配对
python·学习·算法·性能优化·数据分析·clip
陌小呆^O^26 分钟前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
Suyuoa31 分钟前
附录2-pytorch yolov5目标检测
python·深度学习·yolo
I_Am_Me_41 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
重生之我是数学王子1 小时前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
Ai 编码助手1 小时前
使用php和Xunsearch提升音乐网站的歌曲搜索效果
开发语言·php
学习前端的小z1 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript