如何在Windows上使用Rust获取特定窗口的屏幕截图?

huangapple go评论145阅读模式
英文:

How to get a screenshot of a specific window with Rust on Windows?

问题

我想知道如何截取特定窗口的部分屏幕截图。应用程序上可能会有一个覆盖物(游戏覆盖层),它会隐藏我感兴趣的内容。我想找到一种方法,仅截取应用程序的屏幕截图,忽略覆盖层或位于顶部的内容。

我想知道是否有可能进行优化,以获得每秒大约5张屏幕截图

目前,我尝试了这个screenshots cargo包,使用以下代码:

use opencv::{core, highgui, imgcodecs};
use screenshots::Screen;
use std::{time::Instant};
use opencv::core::{log, Mat};

const WIDTH: i32 = 275;
const HEIGHT: i32 = 275;

fn get_img(screen: Screen) -> Mat {
    let image = screen.capture().unwrap();
    let buffer: Vec<u8> = image.into();

    // 将图像类型更改为OpenCV Mat
    let original_image: Mat = imgcodecs::imdecode(&core::Mat::from_slice(buffer.as_slice()).unwrap(), imgcodecs::IMREAD_COLOR).unwrap();
    return original_image;
}

fn main() {
    let window_name = "test".to_owned();
    highgui::named_window(&window_name, highgui::WINDOW_NORMAL).unwrap();
    highgui::resize_window(&window_name, WIDTH, HEIGHT).unwrap();


    let screens = Screen::all().unwrap();
    let screen = screens[1].clone();


    let mut img = get_img(screen);


    loop {
        let now = Instant::now();
        img = get_img(screen);

        // 在控制台中打印处理图像所需的时间
        println!("{} ms", now.elapsed().as_millis());
    }
}

但似乎不可能只截取覆盖层后面的特定窗口的屏幕截图。

我使用 cargo run --release

目标操作系统是Windows,我也在Windows下进行开发。

英文:

I would like to know how to take screenshots of a part of a specific window. There may be an overlay on top of the application (a game overlay) that hides what I'm interested in. I would like to find a way to take a screenshot of the application only, ignoring the overlay or what would be on top.

And I wonder if it's possible to optimize it in order to have ~5 screenshots/seconds

For now i tried the screenshots cargo package with the following code :

use opencv::{core, highgui, imgcodecs};
use screenshots::Screen;
use std::{time::Instant};
use opencv::core::{log, Mat};

const WIDTH: i32 = 275;
const HEIGHT: i32 = 275;

fn get_img(screen: Screen) -> Mat {
    let image = screen.capture().unwrap();
    let buffer: Vec<u8> = image.into();

    // Change image type to OpenCV Mat
    let original_image: Mat = imgcodecs::imdecode(&core::Mat::from_slice(buffer.as_slice()).unwrap(), imgcodecs::IMREAD_COLOR).unwrap();
    return original_image;
}

fn main() {
    let window_name = "test".to_owned();
    highgui::named_window(&window_name, highgui::WINDOW_NORMAL).unwrap();
    highgui::resize_window(&window_name, WIDTH, HEIGHT).unwrap();


    let screens = Screen::all().unwrap();
    let screen = screens[1].clone();


    let mut img = get_img(screen);


    loop {
        let now = Instant::now();
        img = get_img(screen);

        // print in console the time it took to process the image
        println!("{} ms", now.elapsed().as_millis());
    }
}

But it seem not possible to take screenshot of just a specific window behind an overlay.

> I use cargo run --release

> The target os is Windows and I'm also developing under Windows.

ps : I convert my image to OpenCV Mat for the next part of my code

答案1

得分: 3

你可以使用 winapiuser32 存储库来获取任何窗口的屏幕截图。

你之前可能错误地使用了在Windows上不起作用的opencv库。

以下是我的示例代码。假设我们有一个名为 'Chrome' 的窗口。

use winapi::um::winuser::{FindWindowA, GetWindowRect, GetDC, ReleaseDC};
use winapi::shared::windef::RECT;
use std::ptr::null_mut;

fn main() {
    let window_title = "Chrome";
    let hwnd = unsafe { FindWindowA(null_mut(), window_title.as_ptr() as *const i8) };
    let mut rect = RECT::default();
    unsafe {
        GetWindowRect(hwnd, &mut rect);
        let width = rect.right - rect.left;
        let height = rect.bottom - rect.top;
        let hdc = GetDC(hwnd);
        let mut buf: Vec<u32> = vec![0; (width * height) as usize];
        let pitch = width * std::mem::size_of::<u32>() as i32;
        winapi::um::wingdi::BitBlt(hdc, 0, 0, width, height, hdc, 0, 0, winapi::um::wingdi::SRCCOPY);
        winapi::um::wingdi::GetDIBits(hdc, null_mut(), 0, height as u32, buf.as_mut_ptr() as *mut _, 
            &mut winapi::um::wingdi::BITMAPINFO {
                bmiHeader: winapi::um::wingdi::BITMAPINFOHEADER {
                    biSize: std::mem::size_of::<winapi::um::wingdi::BITMAPINFOHEADER>() as u32,
                    biWidth: width,
                    biHeight: height * -1,
                    biPlanes: 1,
                    biBitCount: 32,
                    ..Default::default()
                },
                ..Default::default()
            }, winapi::um::wingdi::DIB_RGB_COLORS);
        ReleaseDC(hwnd, hdc);
    }
}

你可以通过输入 cargo run 来运行它。

英文:

You can use winapi and user32 repo to get any window screenshot.

Wrong that you had made maybe using onencv library that not working on Windows.

Here's my example code. Let's say we have a window call 'Chrome'

use winapi::um::winuser::{FindWindowA, GetWindowRect, GetDC, ReleaseDC};
use winapi::shared::windef::RECT;
use std::ptr::null_mut;

fn main() {
    let window_title = &quot;Chrome&quot;;
    let hwnd = unsafe { FindWindowA(null_mut(), window_title.as_ptr() as *const i8) };
    let mut rect = RECT::default();
    unsafe {
        GetWindowRect(hwnd, &amp;mut rect);
        let width = rect.right - rect.left;
        let height = rect.bottom - rect.top;
        let hdc = GetDC(hwnd);
        let mut buf: Vec&lt;u32&gt; = vec![0; (width * height) as usize];
        let pitch = width * std::mem::size_of::&lt;u32&gt;() as i32;
        winapi::um::wingdi::BitBlt(hdc, 0, 0, width, height, hdc, 0, 0, winapi::um::wingdi::SRCCOPY);
        winapi::um::wingdi::GetDIBits(hdc, null_mut(), 0, height as u32, buf.as_mut_ptr() as *mut _, 
            &amp;mut winapi::um::wingdi::BITMAPINFO {
                bmiHeader: winapi::um::wingdi::BITMAPINFOHEADER {
                    biSize: std::mem::size_of::&lt;winapi::um::wingdi::BITMAPINFOHEADER&gt;() as u32,
                    biWidth: width,
                    biHeight: height * -1,
                    biPlanes: 1,
                    biBitCount: 32,
                    ..Default::default()
                },
                ..Default::default()
            }, winapi::um::wingdi::DIB_RGB_COLORS);
        ReleaseDC(hwnd, hdc);
    }
}

you can run it by typing cargo run.

huangapple
  • 本文由 发表于 2023年3月7日 04:06:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75655361.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定