v4l2采集图片初探遇到的问题【日积月累计划】

背景

本次需要用摄像头采集图片,但是不想用opencv,因为需要重新编译。搜了下相关文章,挺多的。但是实际上却远没有我想的那么简单。绕了很大的弯。

问题

其实说起来用v4l2的api操纵摄像头捕获视频步骤并不复杂:打开摄像头描述符、(可先查询)、设置摄像头、(可检查以下设置的参数是否正确)、获取图像、处理图像。其中最重要的函数ioctl。
我的问题发生在获取图像上。之前看一篇csdn的文章说可以用read来获取图像,不用先将图像存在内核空间然后用mmap映射导出图像。
代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <linux/v4l2-common.h>

const char uvc_name[] = "/dev/video1";
const int IMG_HEIGHT = 800;
const int IMG_WIDTH = 600;
const int IMG_PIX_SIZE = 3;

const int IMG_BUFF_SIZE = IMG_HEIGHT * IMG_WIDTH * IMG_PIX_SIZE;
uint8_t img_buff[IMG_BUFF_SIZE];

int main(void)
{
    /*open camera*/
    int uvcfd = open(uvc_name, O_RDWR, 0);
    if (uvcfd < 0)
    {
        perror("Open Camera: ");
        return -1;
    }

    /*ask the information of uvc*/
    struct v4l2_capability cap;
    if (ioctl(uvcfd, VIDIOC_QUERYCAP, &cap) < 0)
    {
        perror("ioctl: ");
    }
    printf("Driver Caps:\n"
           "  Driver: \"%s\"\n"
           "  Card: \"%s\"\n"
           "  Bus: \"%s\"\n"
           "  Version: %d\n"
           "  Capabilities: %x\n",
           cap.driver,
           cap.card,
           cap.bus_info,
           cap.version,
           cap.capabilities);
    if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
    {
        printf("Camera device %s: support capture\n", uvc_name);
    }
    if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
    {
        printf("Camera device %s: support streaming.\n", uvc_name);
    }
    /*get supported video format*/
    struct v4l2_fmtdesc fmtdesc;
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    printf("Support format: \r\n");
    while (ioctl(uvcfd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
    {
        printf("\t%d. %s\r\n", fmtdesc.index + 1, fmtdesc.description);
        fmtdesc.index++;
    }

    /*setup camera*/
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = IMG_WIDTH;
    fmt.fmt.pix.height = IMG_HEIGHT;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    //fmt.fmt.pix.field = V4L2_FIELD_ANY;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    if (ioctl(uvcfd, VIDIOC_S_FMT, &fmt) < 0)
    {
        perror("file to set camera\r\n");
        close(uvcfd);
        return -1;
    }
    if (ioctl(uvcfd, VIDIOC_G_FMT, &fmt) < 0)
    {
        printf("Unable to get format\r\n");
        return -1;
    }
    printf("fmt.type:\t%d\n", fmt.type);
    printf("pix.pixelformat:\t%c%c%c%c\n", fmt.fmt.pix.pixelformat & 0xFF,
           (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
           (fmt.fmt.pix.pixelformat >> 16) & 0xFF,
           (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
    printf("pix.width:\t%d\n", fmt.fmt.pix.width);
    printf("pix.height:\t%d\n", fmt.fmt.pix.height);
    printf("pix.field:\t%d\n", fmt.fmt.pix.field);

    int size = read(uvcfd, img_buff, IMG_BUFF_SIZE);

    /*save image*/
    if (size > 0)
    {
        int filefd = open("./img.jpg", O_RDWR | O_CREAT, 0777);
        write(filefd, img_buff, size);
        close(filefd);
    }
    else
    {
        perror("Read Camera:");
        printf("The image size is %d\r\n", size);
        printf("Can not capture image\r\n");
    }
    close(uvcfd);
    return 0;
}

以上代码不工作
我的摄像头支持mjpeg和yuyv,但是检查了很多次。尽管有一些小问题,但是每次size = read(uvcfd, img_buff, IMG_BUFF_SIZE)获得的size是-1.这意味这不正确。
上述代码用的是read方法,因为我想自己的应用是按照要求在适当的时候拍一张照,不像别的用户需要用队列的方式来实现,所以没有用多数用户用的buffer的方式。
请问一下是不是我用错了?

解决方法

改成我之前不像用的复杂的方法就ok了。
这里不上代码了,别人比我写的好:
1、比较详细的
2、老外的
3、更多的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例代码,可以在Ubuntu上使用V4L2接口来捕获摄像头图像并保存为JPEG图片。 ```c++ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <linux/videodev2.h> #include <jpeglib.h> #define VIDEO_DEVICE "/dev/video0" #define IMAGE_WIDTH 640 #define IMAGE_HEIGHT 480 #define IMAGE_QUALITY 80 int main(int argc, char **argv) { int fd = open(VIDEO_DEVICE, O_RDWR); if (fd == -1) { perror("Failed to open video device"); return 1; } struct v4l2_capability cap; if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) { perror("Failed to query video device capabilities"); close(fd); return 1; } struct v4l2_format format; format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; format.fmt.pix.width = IMAGE_WIDTH; format.fmt.pix.height = IMAGE_HEIGHT; format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; format.fmt.pix.field = V4L2_FIELD_NONE; if (ioctl(fd, VIDIOC_S_FMT, &format) == -1) { perror("Failed to set video device format"); close(fd); return 1; } struct v4l2_requestbuffers reqbuf; reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuf.memory = V4L2_MEMORY_MMAP; reqbuf.count = 1; if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) == -1) { perror("Failed to request video device buffers"); close(fd); return 1; } struct v4l2_buffer buffer; buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buffer.memory = V4L2_MEMORY_MMAP; buffer.index = 0; if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) == -1) { perror("Failed to query video device buffer"); close(fd); return 1; } void *mem = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer.m.offset); if (mem == MAP_FAILED) { perror("Failed to map video device buffer"); close(fd); return 1; } if (ioctl(fd, VIDIOC_STREAMON, &buffer.type) == -1) { perror("Failed to start video device stream"); munmap(mem, buffer.length); close(fd); return 1; } struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; unsigned char *jpeg_data; unsigned long jpeg_size; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_mem_dest(&cinfo, &jpeg_data, &jpeg_size); cinfo.image_width = IMAGE_WIDTH; cinfo.image_height = IMAGE_HEIGHT; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, IMAGE_QUALITY, true); if (ioctl(fd, VIDIOC_QBUF, &buffer) == -1) { perror("Failed to queue video device buffer"); munmap(mem, buffer.length); close(fd); return 1; } if (ioctl(fd, VIDIOC_DQBUF, &buffer) == -1) { perror("Failed to dequeue video device buffer"); munmap(mem, buffer.length); close(fd); return 1; } jpeg_start_compress(&cinfo, true); JSAMPROW row_pointer[1]; for (int y = 0; y < IMAGE_HEIGHT; y++) { row_pointer[0] = (JSAMPROW)((unsigned char *)mem + y * buffer.bytesused); jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); FILE *fp = fopen("image.jpg", "wb"); if (fp == NULL) { perror("Failed to open image file"); munmap(mem, buffer.length); close(fd); return 1; } fwrite(jpeg_data, jpeg_size, 1, fp); fclose(fp); jpeg_destroy_compress(&cinfo); munmap(mem, buffer.length); close(fd); return 0; } ``` 注意:这个示例代码仅仅是一个基础的例子,实际使用时需要考虑更多的情况,例如错误处理、缓冲区管理、图像分辨率、图像格式、图像质量等。此外,还需要安装libjpeg库,否则会编译错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值