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

C# 攝像頭程式設計完整實作(一)

     編輯:關於C語言

核心元件:avicap32.dll

Windows 系統安裝攝像頭驅動程式後,會註冊 avicap32.dll(Video for Windows 捕獲介面)。透過 P/Invoke 呼叫此非受控 DLL,即可控制攝像頭擷取影像。

封裝類別:ShowVideo

定義所有必要的 Win32 API 宣告、常數、結構體與委派:

public class ShowVideo
{
    // 建立捕獲視窗
    [DllImport("avicap32.dll")]
    public static extern IntPtr capCreateCaptureWindowA(
        byte[] lpszWindowName, int dwStyle, int x, int y,
        int nWidth, int nHeight, IntPtr hWndParent, int nID);

    // 取得驅動程式描述
    [DllImport("avicap32.dll")]
    public static extern bool capGetDriverDescriptionA(
        short wDriver, byte[] lpszName, int cbName,
        byte[] lpszVer, int cbVer);

    // 視窗訊息傳送(多載)
    [DllImport("User32.dll")]
    public static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam);
    [DllImport("User32.dll")]
    public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, int lParam);
    [DllImport("User32.dll")]
    public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, FrameEventHandler lParam);
    [DllImport("User32.dll")]
    public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref BITMAPINFO lParam);

    // 視窗位置調整
    [DllImport("User32.dll")]
    public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter,
        int x, int y, int cx, int cy, int wFlags);

    // 取得影像格式
    [DllImport("avicap32.dll")]
    public static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);

    // 常數定義
    public const int WM_USER = 0x400;
    public const int WS_CHILD = 0x40000000;
    public const int WS_VISIBLE = 0x10000000;
    public const int SWP_NOMOVE = 0x2;
    public const int SWP_NOZORDER = 0x4;
    public const int WM_CAP_DRIVER_CONNECT = WM_USER + 10;
    public const int WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
    public const int WM_CAP_SET_CALLBACK_FRAME = WM_USER + 5;
    public const int WM_CAP_SET_PREVIEW = WM_USER + 50;
    public const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
    public const int WM_CAP_SET_VIDEOFORMAT = WM_USER + 45;

    // 結構體定義
    [StructLayout(LayoutKind.Sequential)]
    public struct VIDEOHDR
    {
        public int lpData;
        public int dwBufferLength;
        public int dwBytesUsed;
        public int dwTimeCaptured;
        public int dwUser;
        public int dwFlags;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
        public int[] dwReserved;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct BITMAPINFOHEADER
    {
        public int biSize;
        public int biWidth;
        public int biHeight;
        public short biPlanes;
        public short biBitCount;
        public int biCompression;
        public int biSizeImage;
        public int biXPelsPerMeter;
        public int biYPelsPerMeter;
        public int biClrUsed;
        public int biClrImportant;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct BITMAPINFO
    {
        public BITMAPINFOHEADER bmiHeader;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
        public int[] bmiColors;
    }

    // 幀回呼委派
    public delegate void FrameEventHandler(IntPtr lwnd, IntPtr lpVHdr);

    // 協助方法
    public static object GetStructure(IntPtr ptr, ValueType structure)
        => Marshal.PtrToStructure(ptr, structure.GetType());

    public static void Copy(IntPtr ptr, byte[] data)
        => Marshal.Copy(ptr, data, 0, data.Length);

    public static int SizeOf(object structure)
        => Marshal.SizeOf(structure);
}

WebCamera 高階封裝類別

public class WebCamera
{
    private IntPtr _hwndCapture;
    private IntPtr _controlHandle;
    private int _width, _height;
    private ShowVideo.FrameEventHandler _frameHandler;

    public delegate void FrameReceivedEventHandler(byte[] frameData);
    public event FrameReceivedEventHandler FrameReceived;

    public WebCamera(IntPtr controlHandle, int width, int height)
    {
        _controlHandle = controlHandle;
        _width = width; _height = height;
    }

    public void Start()
    {
        byte[] name = new byte[100], ver = new byte[100];
        ShowVideo.capGetDriverDescriptionA(0, name, 100, ver, 100);

        _hwndCapture = ShowVideo.capCreateCaptureWindowA(
            name, ShowVideo.WS_VISIBLE | ShowVideo.WS_CHILD,
            0, 0, _width, _height, _controlHandle, 0);

        if (SendDriverConnect())
        {
            SetPreviewRate(66);
            SetPreview(true);
            SetVideoFormat();
            _frameHandler = FrameCallback;
            SetCallbackOnFrame(_frameHandler);
            ShowVideo.SetWindowPos(_hwndCapture, 0, 0, 0, _width, _height,
                ShowVideo.SWP_NOMOVE | ShowVideo.SWP_NOZORDER);
        }
    }

    public void Stop()
    {
        ShowVideo.SendMessage(_hwndCapture, ShowVideo.WM_CAP_DRIVER_DISCONNECT, 0, 0);
    }

    private bool SendDriverConnect()
        => ShowVideo.SendMessage(_hwndCapture, ShowVideo.WM_CAP_DRIVER_CONNECT, (short)0, 0);

    private void SetPreviewRate(short rate)
        => ShowVideo.SendMessage(_hwndCapture, ShowVideo.WM_CAP_SET_PREVIEWRATE, rate, 0);

    private void SetPreview(bool enable)
        => ShowVideo.SendMessage(_hwndCapture, ShowVideo.WM_CAP_SET_PREVIEW, enable, 0);

    private void SetCallbackOnFrame(ShowVideo.FrameEventHandler handler)
        => ShowVideo.SendMessage(_hwndCapture, ShowVideo.WM_CAP_SET_CALLBACK_FRAME, 0, handler);

    private void SetVideoFormat()
    {
        var bmi = new ShowVideo.BITMAPINFO();
        bmi.bmiHeader.biSize = ShowVideo.SizeOf(bmi.bmiHeader);
        bmi.bmiHeader.biWidth = 352;
        bmi.bmiHeader.biHeight = 288;
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = 24;
        ShowVideo.SendMessage(_hwndCapture, ShowVideo.WM_CAP_SET_VIDEOFORMAT,
            ShowVideo.SizeOf(bmi), ref bmi);
    }

    private void FrameCallback(IntPtr lwnd, IntPtr lpVHdr)
    {
        var vhdr = (ShowVideo.VIDEOHDR)ShowVideo.GetStructure(lpVHdr, new ShowVideo.VIDEOHDR());
        byte[] data = new byte[vhdr.dwBytesUsed];
        ShowVideo.Copy(vhdr.lpData, data);
        FrameReceived?.Invoke(data);
    }
}

使用方式

// 在 Form 載入時
var cam = new WebCamera(pictureBox1.Handle, 320, 240);
cam.FrameReceived += (data) => {
    // 將 byte[] 顯示於 PictureBox(需轉換為 Bitmap)
};
cam.Start();

// 關閉時
cam.Stop();

注意事項

  • 此為 Video for Windows (VFW) 舊版 API,現代專案建議使用 DirectShow 或 Media Foundation
  • 64 位元程式需確保 DLL 相容性,或啟用「Prefer 32-bit」
  • 攝像頭索引 0 代表第一台裝置,多裝置需列舉
  • 即時預覽建議使用雙緩衝減少閃爍
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved