首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MATLAB 呼叫 Windows API 實現螢幕截圖 (MEX 擴充)

     編輯:關於VC++

前言

MATLAB 內建函數雖強大,但缺乏直接存取作業系統底層功能 (如螢幕截圖) 的介面。透過 MEX (MATLAB Executable) 機制,可將 C/C++ 撰寫、呼叫 Windows API 的程式編譯為 .mexw32/.mexw64 動態連結庫,在 MATLAB 中像呼叫內建函數一樣使用。本文完整示範截圖 MEX 函數的開發流程。

MEX 介面原型

void mexFunction(int nlhs, mxArray *plhs[],
                 int nrhs, const mxArray *prhs[])
  • nlhs:輸出參數個數
  • plhs:輸出 mxArray 指標陣列
  • nrhs:輸入參數個數
  • prhs:輸入 mxArray 指標陣列

完整 C 原始碼 (screensnap.c)

// 編譯: mex screensnap.c user32.lib gdi32.lib
// 使用: a = screensnap(0); imshow(a);  % 0=隱藏 MATLAB 視窗, 1=包含
#include 
#include 
#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    int cx, cy, recnum;
    int dims[3];
    char *pchar, *mloc;
    bool bShowMatlab;
    HWND hWin, hActWin;
    HDC hDC, hMemDC;
    RECT rect;
    HBITMAP hBmp, hOldBmp;
    BITMAPINFOHEADER bih;

    // 1. 參數檢查
    if (nrhs != 1) mexErrMsgTxt("Need 1 argument!");
    if (!mxIsDouble(prhs[0])) mexErrMsgTxt("Input must be numeric!");
    bShowMatlab = (*(double*)mxGetData(prhs[0]) != 0);

    // 2. 取得桌面視窗 DC 與尺寸
    hActWin = GetForegroundWindow(); // MATLAB 視窗
    hWin = GetDesktopWindow();
    hDC = GetWindowDC(hWin);
    GetWindowRect(hWin, &rect);
    cx = rect.right - rect.left;
    cy = rect.bottom - rect.top;

    // 3. 建立相容記憶體 DC 與位圖
    hMemDC = CreateCompatibleDC(hDC);
    hBmp = CreateCompatibleBitmap(hDC, cx, cy);
    if (!hBmp) mexErrMsgTxt("CreateCompatibleBitmap failed!");
    hOldBmp = (HBITMAP)SelectObject(hMemDC, hBmp);

    // 4. 選擇性隱藏 MATLAB 視窗避免截到自己
    if (!bShowMatlab) {
        ShowWindow(hActWin, SW_HIDE);
        Sleep(100); // 等待視窗消失
    }

    // 5. 螢幕複製到記憶體 DC
    if (!BitBlt(hMemDC, 0, 0, cx, cy, hDC, 0, 0, SRCCOPY))
        mexErrMsgTxt("BitBlt failed!");

    // 6. 準備輸出 mxArray (cy x cx x 3, uint8)
    dims[0] = cy; dims[1] = cx; dims[2] = 3;
    plhs[0] = mxCreateNumericArray(3, dims, mxUINT8_CLASS, mxREAL);
    pchar = (char*)mxGetData(plhs[0]);

    // 7. 設定 DIB 標頭 (自下而上, 24-bit RGB)
    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = cx;
    bih.biHeight = -cy; // 負值 = 自上而下
    bih.biPlanes = 1;
    bih.biBitCount = 24;
    bih.biCompression = BI_RGB;
    bih.biSizeImage = 0;
    bih.biXPelsPerMeter = bih.biYPelsPerMeter = 0;
    bih.biClrUsed = bih.biClrImportant = 0;

    // 8. 取得 DIB 像素資料
    mloc = (char*)mxMalloc(cx * cy * 3);
    recnum = GetDIBits(hMemDC, hBmp, 0, cy, mloc, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
    mexPrintf("Copied %d lines\n", recnum);

    // 9. 資料重排: C 列優先 → MATLAB 列優先 (行優先)
    // C: [row][col][channel] → MATLAB: [col][row][channel]
    for (int k = 0; k < 3; ++k)
        for (int j = 0; j < cy; ++j)
            for (int i = 0; i < cx; ++i)
                pchar[i*cy + j + k*cx*cy] = mloc[(j*cx + i)*3 + 2 - k]; // BGR→RGB

    mxFree(mloc);

    // 10. 清理 GDI 物件
    SelectObject(hMemDC, hOldBmp);
    DeleteDC(hMemDC);
    ReleaseDC(hWin, hDC);
    DeleteObject(hBmp);

    // 11. 恢復 MATLAB 視窗
    if (!bShowMatlab) ShowWindow(hActWin, SW_SHOW);
}

編譯與測試

> mex screensnap.c user32.lib gdi32.lib
> a = screensnap(0);  % 隱藏 MATLAB 視窗截圖
> imshow(a);          % 顯示截圖

輸出為 cy x cx x 3 的 uint8 陣列,直接相容 imshow、imwrite 等影像處理函數。

關鍵技術點

項目說明
參數驗證mexErrMsgTxt 拋出 MATLAB 可捕獲的錯誤
記憶體管理mxMalloc/mxFree 配合 MATLAB 記憶體管理器
陣列建立mxCreateNumericArray 指定維度/類型/實數
資料存取mxGetData 取得資料指標
像素格式轉換Windows DIB 為 BGR + 列優先,MATLAB 為 RGB + 行優先,需雙重轉換
視窗閃爍避免截圖前 SW_HIDE MATLAB 視窗,Sleep(100) 確保重繪完成

擴充應用

  • 區域截圖:傳入 rect 參數,調整 BitBlt 來源座標。
  • 多螢幕支援:使用 EnumDisplayMonitors 取得各螢幕範圍。
  • 即時影像串流:在迴圈中呼叫,配合 drawnow 顯示。
  • 直接操作位圖像素:省去 GetDIBits,使用 LockBits (GDI+) 更高效。

結語

MEX 機制讓 MATLAB 無縫整合 C/C++ 與作業系統 API,極大擴展了應用範圍。本範例展示了從參數檢查、GDI 截圖、像素格式轉換到 MATLAB 陣列建構的完整流程,可作為開發其他 MEX 擴充 (如硬體控制、高效演算法、第三方庫封裝) 的範本。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved