如何使用 C++ 和 OpenCV 實現截屏


前言

實現屏幕截屏需要用到 Windows API,所以需要包括 Windows.h 頭文件。同時我們想要對截圖做進一步的處理,就需要用到 OpenCV。關於 OpenCV 的安裝與編譯可以參見 《再整理:Visual Studio Code(vscode)下的基於C++的OpenCV的最新搭建攻略解析》,親測有效,但是 OpenCV 還有 MinGW 的版本最好和博客中保持一致,不然編譯可能會失敗。下面進入正題。

代碼

頭文件

Screenshot.h

#pragma once
#include <Windows.h>
#include <opencv2/opencv.hpp>

class Screenshot
{
public:
    Screenshot();
    double static getZoom();
    cv::Mat getScreenshot();
    cv::Mat getScreenshot(int x, int y, int width, int height);

private:
    int m_width;
    int m_height;
    HDC m_screenDC;
    HDC m_compatibleDC;
    HBITMAP m_hBitmap;
    LPVOID m_screenshotData = nullptr;
};

源文件

在截圖之前需要獲取屏幕的分辨率,一種很直觀的想法就是調用 GetSystemMetrics(SM_C*SCREEN) 函數來獲取寬度或者高度。如下圖所示,設置屏幕縮放 125% 之后,得到的值會偏小。如果是 1920 × 1080 的分辨率,GetSystemMetrics(SM_CXSCREEN)GetSystemMetrics(SM_CYSCREEN) 返回分辨率會是 (1920, 1080) / 1.25 = (1536, 864)。所以我們需要先計算屏幕的縮放率。這個任務由 Screenshot::getZoom() 完成。剩下的步驟注釋中解釋的很充分了,不再贅述。

屏幕縮放

Screenshot.cpp

#include "Screenshot.h"
using cv::Mat;

Screenshot::Screenshot()
{
    double zoom = getZoom();
    m_width = GetSystemMetrics(SM_CXSCREEN) * zoom;
    m_height = GetSystemMetrics(SM_CYSCREEN) * zoom;
    m_screenshotData = new char[m_width * m_height * 4];
    memset(m_screenshotData, 0, m_width);

    // 獲取屏幕 DC
    m_screenDC = GetDC(NULL);
    m_compatibleDC = CreateCompatibleDC(m_screenDC);

    // 創建位圖
    m_hBitmap = CreateCompatibleBitmap(m_screenDC, m_width, m_height);
    SelectObject(m_compatibleDC, m_hBitmap);
}

/* 獲取整個屏幕的截圖 */
Mat Screenshot::getScreenshot()
{
    // 得到位圖的數據
    BitBlt(m_compatibleDC, 0, 0, m_width, m_height, m_screenDC, 0, 0, SRCCOPY);
    GetBitmapBits(m_hBitmap, m_width * m_height * 4, m_screenshotData);

    // 創建圖像
    Mat screenshot(m_height, m_width, CV_8UC4, m_screenshotData);

    return screenshot;
}

/** @brief 獲取指定范圍的屏幕截圖
 * @param x 圖像左上角的 X 坐標
 * @param y 圖像左上角的 Y 坐標
 * @param width 圖像寬度
 * @param height 圖像高度
 */
Mat Screenshot::getScreenshot(int x, int y, int width, int height)
{
    Mat screenshot = getScreenshot();
    return screenshot(cv::Rect(x, y, width, height));
}

/* 獲取屏幕縮放值 */
double Screenshot::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;
}

測試

對於 1920 × 1080 的分辨率,截一次屏在 30ms 左右,下面是測試代碼:

#include "Screenshot.h"
using namespace cv;

int main()
{
    Screenshot screenshot;
    Mat img = screenshot.getScreenshot();
    Mat img_ = screenshot.getScreenshot(1040, 132, 800, 880);
    imwrite("screenshot.jpg", img);
    imwrite("screenshot_part.jpg", img_);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM