基於opencv,開發攝像頭播放程序

前言 Windows下實現攝像視頻捕捉有多種實現方式;各類方式的優劣,本文不作對比。可是,opencv是一款老牌開發庫,在圖像處理領域聲名顯赫。採用opencv來處理攝像視頻,在性能和穩定性上,是有保障的。而且,opencv包含不少圖像處理函數,能夠更方便的對視頻處理。c#

  執行程序是用wpf開發的,因此先將opencv封裝成c語言接口,以供調用。opencv也不可能提供現成的控件供wpf使用,兩種不一樣的開發語言「溝通」起來有些困難。其實稍做變通,就能夠實現攝像頭播放功能。ide

1 對opencv封裝函數

opencv的類VideoCapture封裝了對攝像頭的操做,使用起來也很是簡單。性能

 bool open(int device); device爲攝像頭設備序號。this

若是有多個攝像頭,怎麼知道哪一個攝像頭的序號那?能夠經過以下函數,獲取攝像頭列表。攝像頭在list中索引即爲設備序號。spa

int GetCameraDevices(vector<wstring>& list)
{
    ICreateDevEnum *pDevEnum = NULL;
    IEnumMoniker *pEnum = NULL;
    int deviceCounter = 0;
    CoInitialize(NULL);
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
        CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
        reinterpret_cast<void**>(&pDevEnum));

    if (SUCCEEDED(hr))
    {
        // Create an enumerator for the video capture category.
        hr = pDevEnum->CreateClassEnumerator(
            CLSID_VideoInputDeviceCategory,
            &pEnum, 0);

        if (hr == S_OK) {

            //if (!silent)printf("SETUP: Looking For Capture Devices\n");
            IMoniker *pMoniker = NULL;

            while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {

                IPropertyBag *pPropBag;
                hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
                    (void**)(&pPropBag));

                if (FAILED(hr)) {
                    pMoniker->Release();
                    continue;  // Skip this one, maybe the next one will work.
                }

                // Find the description or friendly name.
                VARIANT varName;
                VariantInit(&varName);
                hr = pPropBag->Read(L"Description", &varName, 0);

                if (FAILED(hr))
                    hr = pPropBag->Read(L"FriendlyName", &varName, 0);

                if (SUCCEEDED(hr)) {

                    hr = pPropBag->Read(L"FriendlyName", &varName, 0);

                    int count = 0;
                    wstring str2 = varName.bstrVal;
                    list.push_back(str2);
                }

                pPropBag->Release();
                pPropBag = NULL;

                pMoniker->Release();
                pMoniker = NULL;

                deviceCounter++;
            }

            pDevEnum->Release();
            pDevEnum = NULL;

            pEnum->Release();
            pEnum = NULL;
        }
    }
    return deviceCounter;
}

總之,使用opencv打開攝像頭很是簡單。code

打開以後,就是獲取攝像頭圖像。視頻其實就是圖像的集合;每秒鐘獲取25幅圖像,將其在控件上顯示,就是視頻。orm

Mat cameraImg;
_pCapture >> cameraImg;

Mat類封裝了對圖像的操做。c#不可能操做Mat,須要將Mat中純圖像部分數據傳遞出來,圖像才能被c#利用。視頻

int Camera_GetImgData(INT64 handle, char* imgBuffer)
{
    CameraInfo *pCameraInfo = (CameraInfo*)handle;

    Mat cameraImg;
    *(pCameraInfo->_pCapture) >> cameraImg;

    if (!cameraImg.empty())
    {
        int height = cameraImg.rows;int dataLen = height * cameraImg.step;
        memcpy(imgBuffer, cameraImg.data, dataLen);

        return 0;
    }
    else
    {
        return 1;
    }
}

cameraImg.data中存有圖像數據,data的大小能夠根據圖像的高度、每行圖像的步幅計算出來。c#調用此函數後,imgBuffer存放圖像數據。對數據imgBuffer處理後,就能夠在控件上顯示。blog

c語言對opencv封裝函數列表以下:

extern "C"
{
    OpenCVCamera_API int Camera_GetCameraName(char* listName);

    OpenCVCamera_API INT64 Camera_CreateHandle();
    OpenCVCamera_API void Camera_CloseHandle(INT64 handle);

    OpenCVCamera_API BOOL Camera_IsOpen(INT64 handle);
    OpenCVCamera_API int Camera_Open(INT64 handle, int index);
    OpenCVCamera_API int Camera_Close(INT64 handle);

    OpenCVCamera_API int Camera_GetImgInfo(INT64 handle,int& width,int& height,int& channel,
        int& step, int& depth);
    OpenCVCamera_API int Camera_GetImgData(INT64 handle, char* imgBuffer);

    //flipCode >0: 沿y-軸翻轉, 0: 沿x-軸翻轉, <0: x、y軸同時翻轉
    OpenCVCamera_API int Camera_GetImgData_Flip(INT64 handle, char* imgBuffer, int flipCode);
    OpenCVCamera_API int Camera_ImgData_Compress(int rows, int cols, int type, void* imgBuffer,
        int param,void* destBuffer,int* destLen);
}

2 WPF實現視頻播放

WPF的Image控件實現圖像的顯示。實現視頻播放的邏輯爲:設定一個定時器(時間間隔爲40毫秒),每隔一段時間從opencv獲取圖像,在控件中顯示。

 <Image x:Name="imageVideoPlayer" Stretch="Uniform" ></Image>

實現圖像顯示代碼

  BitmapSource bitmapSource = _openCVCamera.GetBitmapSource();
            if (bitmapSource == null)
                return false;

 imageVideoPlayer.Source = bitmapSource;

實現圖像顯示的關鍵是構建BitmapSource,暨:如何從opencv中獲取圖像數據構建BitmapSource。

  //獲取圖像數據
   if (!GetImgData(out byte[] imgData))
                    return null;

   //構建WriteableBitmap
   WriteableBitmap img = new WriteableBitmap(_imgWidth, _imgHeight, 96, 96, PixelFormats.Bgr24, null);
   img.WritePixels(new Int32Rect(0, 0, _imgWidth, _imgHeight),
                   imgData, img.BackBufferStride, 0);
   img.Freeze();

至此,就能夠顯示攝像頭圖像了。

相關文章
相關標籤/搜索