创客神器NanoPi
直播中

ypw

9年用户 135经验值
擅长:嵌入式技术 控制/MCU
私信 关注
[经验]

【NanoPi2试用体验】NanoPi2安装OpenCV,驱动摄像头

本帖最后由 ypw 于 2015-12-21 23:58 编辑

首先你需要配置好WiFi,能上网,然后:
  1. apt-get update
  2. apt-get install libcv-dev libopencv-dev libv4l-dev

有一点比较特别的是NanoPi2的摄像头插上以后,设备是/dev/video9而不是通常情况下的/dev/video0。

测试代码如下(参考自VideoCapture):
  1. #include
  2. #include
  3. #include

  4. using namespace cv;

  5. int main()
  6. {
  7.     VideoCapture cap(9);

  8.     Mat frame;
  9.     while(1)
  10.     {
  11.         cap.read(frame);
  12.         imshow("test",frame);
  13.         waitKey(30);
  14.     }
  15.     return 0;
  16. }
  1. g++ opencv.cpp -o testcv -lopencv_core -lopencv_highgui -lopencv_imgproc
  2. ./testcv


这里出现了 HIGHGUI ERROR: V4L: index 9 is not correct! 这样的错误提示,具体原因不明,不过我们想到了一个好的解决方法,那就是直接使用v4l2获取摄像头的数据,然后再转为OpenCV的cv::Mat。
下面的代码就是OpenCV结合V4L2的程序:
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include
  10. #include
  11. #include
  12. #include

  13. #include
  14. #include
  15. #include
  16. using namespace cv;

  17. char videodevice[] = "/dev/video9";
  18. int imageWidth = 320;
  19. int imageHeight = 240;

  20. #define CLEAR(x) memset(&(x), 0, sizeof(x))

  21. struct buffer {
  22.     void   *start;
  23.     size_t length;
  24. };

  25. static void xioctl(int fh, int request, void *arg)
  26. {
  27.     int r;
  28.     do {
  29.         r = v4l2_ioctl(fh, request, arg);
  30.     } while (r == -1 && ((errno == EINTR) || (errno == EAGAIN)));

  31.     if (r == -1) {
  32.         fprintf(stderr, "error %d, %sn", errno, strerror(errno));
  33.         exit(EXIT_FAILURE);
  34.     }
  35. }

  36. int main()
  37. {
  38.     //打开摄像头设备
  39.     int fd = -1;
  40.     //fd = open (videodevice, O_RDWR | O_NONBLOCK, 0);
  41.     fd = open (videodevice, O_RDWR, 0);
  42.     if(fd<0){
  43.         printf("failed to openn");
  44.         exit(EXIT_FAILURE);
  45.     }
  46.     printf("打开摄像头设备成功,%sn", videodevice);

  47.     //查询摄像头信息,VIDIOC_QUERYCAP
  48.     printf("------------n");
  49.     struct v4l2_capability cap;
  50.     xioctl (fd, VIDIOC_QUERYCAP, &cap);
  51.     printf("Driver Name:%snCard Name:%snBus info:%snDriver Version:%u.%u.%unn",cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&0XFF);


  52.     //查询摄像头视频格式,VIDIOC_ENUM_FMT
  53.     printf("------------n");
  54.     struct v4l2_fmtdesc fmtdesc;
  55.     fmtdesc.index=0;
  56.     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
  57.     printf("Supportformat:n");
  58.     while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
  59.     {
  60.         printf("t%d.%c%c%c%ct%sn",fmtdesc.index+1,fmtdesc.pixelformat & 0xFF, (fmtdesc.pixelformat >> 8) & 0xFF,(fmtdesc.pixelformat >> 16) & 0xFF, (fmtdesc.pixelformat >> 24) & 0xFF,fmtdesc.description);
  61.         fmtdesc.index++;
  62.     }

  63.     struct v4l2_format            fmt;
  64.     struct v4l2_buffer            buf;
  65.     struct v4l2_requestbuffers    req;

  66.     CLEAR(fmt);
  67.     CLEAR(buf);
  68.     CLEAR(req);

  69.     printf("------------n");
  70.     printf("设置摄像头视频数据格式n");
  71.     //设置摄像头视频数据格式,VIDIOC_S_FMT
  72.     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  73.     fmt.fmt.pix.width = imageWidth;
  74.     fmt.fmt.pix.height = imageHeight;
  75.     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
  76.     fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
  77.     //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
  78.     //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420;
  79.     //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
  80.     xioctl(fd, VIDIOC_S_FMT, &fmt);

  81.     if ((fmt.fmt.pix.width != imageWidth) || (fmt.fmt.pix.height != imageHeight))
  82.         printf("Warning: driver is sending image at %dx%dn", fmt.fmt.pix.width, fmt.fmt.pix.height);
  83.     else {
  84.         printf("fmt.type is %dn",fmt.type);
  85.         printf("Width = %dn",fmt.fmt.pix.width);
  86.         printf("Height= %dn",fmt.fmt.pix.height);
  87.         printf("Sizeimage = %dn",fmt.fmt.pix.sizeimage);
  88.     }

  89.     printf("------------n");
  90.     printf("请求分配视频缓冲区n");
  91.     //请求分配视频缓冲区,VIDIOC_REQBUFS
  92.     req.count = 4;
  93.     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  94.     req.memory = V4L2_MEMORY_MMAP;
  95.     xioctl(fd, VIDIOC_REQBUFS, &req);

  96.     printf("------------n");
  97.     printf("查询缓冲信息,将内核空间映射到用户空间n");
  98.     //查询缓冲信息,将内核空间映射到用户空间,VIDIOC_QUERYBUF
  99.     struct buffer *buffers;
  100.     unsigned int i, n_buffers;

  101.     buffers = (struct buffer*)calloc(req.count, sizeof(*buffers));
  102.     for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
  103.         CLEAR(buf);

  104.         buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  105.         buf.memory      = V4L2_MEMORY_MMAP;
  106.         buf.index       = n_buffers;
  107.         //把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
  108.         xioctl(fd, VIDIOC_QUERYBUF, &buf);

  109.         buffers[n_buffers].length = buf.length;
  110.         buffers[n_buffers].start = v4l2_mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);

  111.         if (MAP_FAILED == buffers[n_buffers].start) {
  112.             perror("mmap");
  113.             exit(EXIT_FAILURE);
  114.         }
  115.     }

  116.     //投放一个空的视频缓冲区到视频缓冲区输入队列中,VIDIOC_QBUF
  117.     printf("------------n");
  118.     printf("投放一个空的视频缓冲区到视频缓冲区输入队列中n");
  119.     for (i = 0; i < n_buffers; ++i) {
  120.         CLEAR(buf);
  121.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  122.         buf.memory = V4L2_MEMORY_MMAP;
  123.         buf.index = i;
  124.         xioctl(fd, VIDIOC_QBUF, &buf);
  125.     }

  126.     //启动视频采集命令,VIDIOC_STREAMON
  127.     printf("------------n");
  128.     printf("启动视频采集n");
  129.     enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  130.     xioctl(fd, VIDIOC_STREAMON, &type);



  131.     fd_set fds;
  132.     int r;
  133.     struct timeval tv;

  134.     do {
  135.         FD_ZERO(&fds);
  136.         FD_SET(fd, &fds);

  137.         /* Timeout. */
  138.         tv.tv_sec = 2;
  139.         tv.tv_usec = 0;

  140.         r = select(fd + 1, &fds, NULL, NULL, &tv);
  141.     } while ((r == -1 && (errno = EINTR)));
  142.     if (r == -1) {
  143.         perror("select");
  144.         return errno;
  145.     }
  146.     Mat frame;
  147.     while(1)
  148.     {
  149.         CLEAR(buf);
  150.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  151.         buf.memory = V4L2_MEMORY_MMAP;
  152.         //从视频缓冲区的输出队列中取得一个已经保存有一帧视频数据的视频缓冲区,VIDIOC_DQBUF
  153.         xioctl(fd, VIDIOC_DQBUF, &buf);
  154.         char * p = (char *)(buffers[buf.index].start);
  155.         printf("------------n");
  156.         printf("取得一帧,%fkbn", buf.bytesused/1024.0);

  157.         vector data(p, p + buf.bytesused);
  158.         frame = imdecode(Mat(data), CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_COLOR);
  159.         imshow("test", frame);
  160.         int c = waitKey(10)&0xFF;
  161.         if(c == 27){
  162.             break;
  163.         }

  164.         xioctl(fd, VIDIOC_QBUF, &buf);
  165.     }


  166.     //停止视频采集命令,VIDIOC_STREAMOFF
  167.     printf("------------n");
  168.     printf("停止视频采集n");
  169.     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  170.     xioctl(fd, VIDIOC_STREAMOFF, &type);
  171.     for (i = 0; i < n_buffers; ++i) {
  172.         v4l2_munmap(buffers[i].start, buffers[i].length);
  173.     }
  174.     v4l2_close(fd);
  175.     exit (EXIT_SUCCESS);
  176. }
  1. g++ test.cpp -o test -lopencv_core -lopencv_highgui -lopencv_imgproc -lv4l2

注意,凡是带imshow的程序都必须到触摸屏上操作,不然就会出现错误,比如
ssh中的: (test:1500): Gtk-WARNING **: cannot open display: ,或者vnc中的: (test:1521): GdkGLExt-WARNING **: Window system doesn't support OpenGL.。注意2,本程序只支持MJPEG摄像头,不支持H264或者YUV格式的摄像头。



如果需要在Python中开发OpenCV,可以执行下面的命令:
  1. apt-get install libcv-dev libopencv-dev python-dev python-opencv python-imaging python-pip
  2. pip install -U numpy
但是,目前在Python使用OpenCV是不现实的,因为numpy装不上。


  • Snip20151221_1.png
  • Snip20151221_3.png
  • WeChat_1450686824.jpeg

回帖(2)

chengtao

2016-1-12 17:58:29
不错,opencv是个好方向
举报

林瑞

2018-5-10 16:33:26
USB摄像头帧率好像不高啊,都没有树莓派CSI摄像头跑的快啊,为什么啊???
举报

更多回帖

发帖
×
20
完善资料,
赚取积分