Quantcast
Channel: Sam的技术Blog
Viewing all articles
Browse latest Browse all 158

OpenCV底层对Camera的操作

$
0
0
作者: Sam (甄峰)   sam_code@hotmail.com

OpenCV提供接口,可以对Camera进行操作。


0.  OpenCV对Camera的操作所属目录文件:
OpenCV对Camera的操作,有多种方式。从VideoCapture class开始。

在modules/videoio/include/opencv2/videoio.hpp中,声明了一个class:
class CV_EXPORTS_W VideoCapture
{
};

它利用__attribute__ ((visibility ("default"))) 把class作为可见。


1. Camera读取接口:
OpenCV读取Camera的方法随OpenCV版本升级而提供了多种方式,我们先从VideoCapture class的成员函数看起:

1.1: VideoCapture的构造函数:
VideoCapture::VideoCapture(int index)
{
    open(index);
}


1.2:VideoCapture的open()函数:
1.2.1:打开/dev/videoX:
bool VideoCapture::open(int index)
{
    if (isOpened()) release();
    icap = IVideoCapture_create(index);
    if (!icap.empty())
        return true;
    cap.reset(cvCreateCameraCapture(index));
    return isOpened();
}
这里会调用cap.reset(cvCreateCameraCapture(index));

其中cvCreateCameraCapture(index)会创建capture.
它会根据编译OpenCV时的设置, 依次判断采用哪个硬件或架构:
HAVE_MSMF,Microsoft Media Foundation
HAVE_VFW, platform native
HAVE_CAMERAVL2, HAVE_LIBV4L, 采用libv4l,或者v4l2.
HAVE_GSTREAMER, GStreamer,
CV_CAP_FIREWIRE, 1394等等。


1.2.2: 打开文件:
bool VideoCapture::open(const String& filename, int apiPreference)
{
    if (isOpened()) release();
    icap = IVideoCapture_create(filename);
    if (!icap.empty())
        return true;

    cap.reset(cvCreateFileCaptureWithPreference(filename.c_str(), apiPreference));
    return isOpened();
}

cvCreateFileCaptureWithPreference() 这创建capture时,可以选择ffmpeg等。


1.3: open()的内部细节:
当选中了某个设置时,则capture由这个设置的函数具体构建,例如,我们选择在Linux下使用v4l2做OpenCV Camera接口的实现。则:
CvCapture* cvCreateCameraCapture_V4L( int index )
{
    cv::CvCaptureCAM_V4L* capture = new cv::CvCaptureCAM_V4L();

    if(capture->open(index))
        return capture;

    delete capture;
    return NULL;
}

其中:
struct CvCaptureCAM_V4L : public CvCapture, CvCaptureCAM_V4L结构体继承自CvCapture

这里会打开/dev/videoX设备(try_init_v4l2();), 并利用V4L2--ioctl()设置Camera。
获取Camera 各设置Range(v4l2_scan_controls())
设置帧数 (v4l2_set_fps())
设置Buffer, mmap等。


1.3:VideoCapture的read()函数:

bool VideoCapture::read(OutputArray image)
{
    if(grab())
        retrieve(image);
    else
        image.release();
    return !image.empty();
}
依次调用grab(), retrieve()函数。


bool VideoCapture::grab()
{
    if (!icap.empty())
        return icap->grabFrame();
    return cvGrabFrame(cap) != 0;
}


CV_IMPL int cvGrabFrame( CvCapture* capture )
{
    return capture ? capture->grabFrame() : 0;
}
这里,capture根据不同设置,由不同模块创建,我们使用V4L2.所以看V4L2(cap_v4l.cpp)的对应函数。
bool CvCaptureCAM_V4L::grabFrame()
{
    return icvGrabFrameCAM_V4L( this );
}

最终拿数据的是:
static void mainloop_v4l2(CvCaptureCAM_V4L* capture) {
    unsigned int count;

    count = 1;

    while (count-- > 0) {
        for (;;) {
            fd_set fds;
            struct timeval tv;
            int r;

            FD_ZERO (&fds);
            FD_SET (capture->deviceHandle, &fds);

           
            tv.tv_sec = 10;
            tv.tv_usec = 0;

            r = select (capture->deviceHandle+1, &fds, NULL, NULL, &tv);

            if (-1 == r) {
                if (EINTR == errno)
                    continue;

                perror ("select");
            }

            if (0 == r) {
                fprintf (stderr, "select timeout\n");

               
                break;
            }

            if (read_frame_v4l2 (capture))
                break;
        }
    }
}


这个函数很有玄机,它并不是直接DQBUF, 而是使用一个循环,这样就可以保证,每次获取的数据都是最新数据。

static int read_frame_v4l2(CvCaptureCAM_V4L* capture) {
    v4l2_buffer buf = v4l2_buffer();

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    if (-1 == ioctl (capture->deviceHandle, VIDIOC_DQBUF, &buf)) {
        switch (errno) {
        case EAGAIN:
            return 0;

        case EIO:
        if (!(buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)))
        {
          if (ioctl(capture->deviceHandle, VIDIOC_QBUF, &buf) == -1)
          {
            return 0;
          }
        }
        return 0;

        default:
           
            perror ("VIDIOC_DQBUF");
            return 1;
        }
   }

   assert(buf.index < capture->req.count);

   memcpy(capture->buffers[MAX_V4L_BUFFERS].start,
      capture->buffers[buf.index].start,
      capture->buffers[MAX_V4L_BUFFERS].length );
   capture->bufferIndex = MAX_V4L_BUFFERS;
   //printf("got data in buff %d, len=%d, flags=0x%X, seq=%d, used=%d)\n",
   //   buf.index, buf.length, buf.flags, buf.sequence, buf.bytesused);

   if (-1 == ioctl (capture->deviceHandle, VIDIOC_QBUF, &buf))
       perror ("VIDIOC_QBUF");

   //set timestamp in capture struct to be timestamp of most recent frame
   capture->timestamp = buf.timestamp;

   return 1;
}


 

Viewing all articles
Browse latest Browse all 158

Trending Articles