基于opencv,开发摄像头播放程序

前言 Windows下实现摄像视频捕捉有多种实现方式;各种方式的优劣,本文不做对比。但是,opencv是一款老牌开发库,在图像处理领域声名显赫。采用opencv来处理摄像视频,在性能和稳定性上,是有保障的。并且,opencv包含很多图像处理函数,可以更方便的对视频处理。

  执行程序是用wpf开发的,所以先将opencv封装成c语言接口,以供调用。opencv也不可能提供现成的控件供wpf使用,两种不同的开发语言“沟通”起来有些困难。其实稍作变通,就可以实现摄像头播放功能。

1 对opencv封装

opencv的类VideoCapture封装了对摄像头的操作,使用起来也非常简单。

 bool open(int device); device为摄像头设备序号。

如果有多个摄像头,怎么知道哪个摄像头的序号那?可以通过如下函数,获取摄像头列表。摄像头在list中索引即为设备序号。

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打开摄像头非常简单。

打开之后,就是获取摄像头图像。视频其实就是图像的集合;每秒钟获取25幅图像,将其在控件上显示,就是视频。

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处理后,就可以在控件上显示。

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);
}

函数定义如下

class CameraInfo
{
public:
    VideoCapture * _pCapture;

    CameraInfo()
    {
        _pCapture = NULL;
    }

    ~CameraInfo()
    {
        if (_pCapture != NULL)
            delete _pCapture;
    }
};

int Camera_GetCameraName(char* listName)
{
    vector<wstring> listName2;
    GetCameraDevices(listName2);

    wstring all;
    for (size_t i = 0; i < listName2.size(); i++)
    {
        all += listName2[i];
        all += L";";
    }

    int n = all.length();
    memcpy(listName, all.data(), n * 2);
    return listName2.size();
}

INT64 Camera_CreateHandle()
{
    CameraInfo *pCameraInfo = new CameraInfo();
    return (INT64)pCameraInfo;
}

void Camera_CloseHandle(INT64 handle)
{
    CameraInfo *pCameraInfo = (CameraInfo*)handle;
    delete pCameraInfo;
}

BOOL Camera_IsOpen(INT64 handle)
{
    CameraInfo *pCameraInfo = (CameraInfo*)handle;
    if (pCameraInfo->_pCapture == NULL)
        return FALSE;

    BOOL open = pCameraInfo->_pCapture->isOpened();
    return open;
}

int Camera_Open(INT64 handle, int index)
{
    Camera_Close(handle);
    CameraInfo *pCameraInfo = (CameraInfo*)handle;
    pCameraInfo->_pCapture = new VideoCapture();

    bool open = pCameraInfo->_pCapture->open(index);
    if (open)
        return 0;
    return 1;
}

int Camera_Close(INT64 handle)
{
    CameraInfo *pCameraInfo = (CameraInfo*)handle;
    if (pCameraInfo->_pCapture == NULL)
        return 0;

    delete pCameraInfo->_pCapture;
    pCameraInfo->_pCapture = NULL;
    return 0;
}

//depth == enum{CV_8U=0,CV_8S=1,CV_16U=2,CV_16S=3,CV_32S=4,CV_32F=5,CV_64F=6}
int Camera_GetImgInfo(INT64 handle, int& width, int& height, int& channel, int& step, int& depth)
{
    CameraInfo *pCameraInfo = (CameraInfo*)handle;

    Mat cameraImg;
    *(pCameraInfo->_pCapture) >> cameraImg;
    if (!cameraImg.empty())
    {
        height = cameraImg.rows;
        width = cameraImg.cols;
        channel = cameraImg.channels();
        step = cameraImg.step;
        depth = cameraImg.depth();
        return 0;
    }
    else
    {
        return 1;
    }
}

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

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

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

        return 0;
    }
    else
    {
        return 1;
    }
}

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

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

    if (!cameraImg.empty())
    {
        Mat dest;
        if (flipCode != 100)
        {
            flip(cameraImg, dest, flipCode);
        }
        else
        {
            dest = cameraImg;
        }

        int height = dest.rows;
        int width = dest.cols;
        int channel = dest.channels();
        int dataLen = height * cameraImg.step;
        memcpy(imgBuffer, dest.data, dataLen);

        return 0;
    }
    else
    {
        return 1;
    }
}

BOOL  MatToCompressData(const Mat& mat, std::vector<unsigned char>& buff)
{
    if (mat.empty())
    {
        return FALSE;
    }

    //提前分配内存 否则会报错
    int n = mat.rows*mat.cols*mat.channels();
    buff.reserve(n * 2);

    std::vector<int> param(2);
    param.push_back(CV_IMWRITE_JPEG_QUALITY);
    param.push_back(95);// default(95) 0-100
    return cv::imencode(".jpg", mat, buff, param);
}

int Camera_ImgData_Compress(int rows, int cols, int type, void* imgBuffer,
    int param, void* destBuffer, int* destLen)
{
    Mat src(rows, cols, type, imgBuffer);

    std::vector<unsigned char> buff;
    MatToCompressData(src, buff);
    if (*destLen < buff.size())
        return -1;

    memcpy(destBuffer, buff.data(), buff.size());
    *destLen = buff.size();

    return 0;
}

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();

至此,就可以显示摄像头图像了。

原文地址:https://www.cnblogs.com/yuanchenhui/p/11332728.html

时间: 2024-11-13 06:44:17

基于opencv,开发摄像头播放程序的相关文章

用Visual C#开发基于OpenCV的Windows应用程序

http://blog.163.com/wangxh_jy/blog/static/28233883201001581640283/ 关于详细的配置及程序运行截图,请下载:http://download.csdn.net/source/1127474名为<用Visual C#开发基于OpenCV的Windows应用程序>的文章. 由于百度允许的字数太少了,所以就不贴全部程序了.有需要源程序的话,请下载:http://download.csdn.net/source/1127477 下面是主要的

基于opencv网络摄像头在ubuntu下的视频获取

 基于opencv网络摄像头在ubuntu下的视频获取 1  工具 原料 平台 :UBUNTU12.04 安装库  Opencv-2.3 2  安装编译运行步骤 安装编译opencv-2.3  参考http://blog.csdn.net/xiabodan/article/details/23547847 3  测试代码 编译 g++ cameraCaptrue.cpp -o test `pkg-config --libs --cflags opencv` cameraCaptrue.cpp

基于opencv在摄像头ubuntu根据视频获取

 基于opencv在摄像头ubuntu根据视频获取 1  工具 原料 平台 :UBUNTU12.04 安装库  Opencv-2.3 2  安装编译执行步骤 安装编译opencv-2.3  參考http://blog.csdn.net/xiabodan/article/details/23547847 提前下载OPENCV源代码包 官方:http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/ 我的:http://dow

基于C#开发数据库应用程序的基本应用

基于C#开发数据库应用程序 一.思路 1.窗体上使用DataGridView控件来实现对数据库表格内容的显示 2.绑定数据源到DataGridView控件(应用程序连接到数据库) 3.在应用程序上实现对数据库文件的增.删.改.查 二.界面 三.实现过程 1.绑定数据源到DataGridView控件(应用程序连接到数据库) 方法引用: 1 private void Form1_Load(object sender, EventArgs e) 2 { 3 //Binding Mode Get Dat

基于opencv的摄像头脸部识别抓取及格式储存(python)

刚接触opencv,参照opencv的sample例子做了一个视频头像抓取的小代码,顺便一起学习着用,先上视频抓取及存储代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # -*- coding: cp936 -*- import cv2 capture=cv2.VideoCapture(0) #将capture保存为motion-jpeg,cv_fourcc为保存格式 size

基于opencv的摄像头的标定

四个坐标系分别为:世界坐标系(Ow),摄像机坐标系(Oc),图像物理坐标系(O1,单位mm),图像像素坐标系(O,位于视野平面的左上角,单位pix). 空间某点P到其像点p的坐标转换过程主要是通过这四套坐标系的三次转换实现的,首先将世界坐标系进行平移和转换得到摄像机坐标系,然后根据三角几何变换得到图像物理坐标系,最后根据像素和公制单位的比率得到图像像素坐标系.(实际的应用过程是这个的逆过程,即由像素长度获知实际的长度) OpenCV中使用的求解焦距和成像原点的算法是基于张正友的方法( pdf )

基于OpenCV读取摄像头进行人脸检测和人脸识别

前段时间使用OpenCV的库函数实现了人脸检测和人脸识别,笔者的实验环境为VS2010+OpenCV2.4.4,opencv的环境配置网上有很多,不再赘述.检测的代码网上很多,记不清楚从哪儿copy的了,识别的代码是从OpenCV官网上找到的:http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_api.html 需要注意的是,opencv的FaceRecogizer目前有三个类实现了它,特征脸和fisherface方法

调试opencv调用摄像头程序时碰到的问题

昨天晚上想把opencv学习笔记整理一下,当跑opencv调用摄像头的程序的时候老是出现Assertion failed (size.width>0 && size.height>0) in cv::imshow,最后查阅资料知道了是因为调用imshow()函数的时候传入的Mat为空导致的,可能是刚开始的时候摄像头并没有初始化成功,就调用imshow()导致程序已启动就down掉了,只要在while循环中判断一下传入的Mat是否为空就解决了 #include <openc

使用 Equinox 开发 OSGi 应用程序

本教程着重介绍 OSGi 框架知识.环境搭建.服务使用.应用设计.部署. 开始之前 关于本教程 OSGi 是目前动态模块系统的事实上的工业标准,虽然一开始只是作为嵌入式设备和家庭网关的框架来使用,但是实际上它适用于任何需要模块化.面向服务.面向组件的应用程序.而 Equinox 则是的 Eclipse 所使用的 OSGi 框架,是 Eclipse 强大的插件体系的基础,Eclipse 的稳定可靠性也为该框架带来了声誉. 本教程就将演示如何在 Eclipse 环境下利用 Equinox 框架进行