栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

8、V4L2接口学习、显示摄像头画面、集成项目使用

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

8、V4L2接口学习、显示摄像头画面、集成项目使用

基本思想:因为手中有一块RK3399 PRO的开发板,外接了AHD的摄像头,为了实现实时获取视频帧,所以需要学习一下v4l2编程。。

第一步:先拷贝一张图,整个v4l2的取帧流程

 具体讲解参考这个大佬的bilibili和有道笔记 https://note.youdao.com/ynoteshare/index.html?id=7c4c0e28888d03ec70d339118c374edb&type=note&_time=1652171642765

第一步

查看支持的显示 分辨率 

ubutnu@ubuntu~$: v4l2-ctl --list-formats-ext -d /dev/video0

代码,完成摄像头的帧获取和写入本地显示

#include 
#include 
#include 
#include
#include
#include
#include 
#include
#include
int main() {
    //打开设备
    int fd=open("/dev/video0",O_RDWR);
    if(fd<0){
        perror("open device failn");
        return -1;
    }
    // 获取摄像头支持的格式
    //获取摄像头支持的格式
    struct v4l2_fmtdesc v4fmt;
    v4fmt.index=0;
    v4fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

    int ret=ioctl(fd,VIDIOC_ENUM_FMT,&v4fmt );
    if(ret<0){
        perror("acvquire failn");
    }
    printf("%sn",v4fmt.description);
    unsigned char *p=(unsigned  char*)&v4fmt.pixelformat;
    printf("%c %c %c %cn",p[0],p[1],p[2],p[3]);
    // 设置摄像头支持的格式
    //设置摄像头支持的格式
    struct v4l2_format vFormat;
    vFormat.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vFormat.fmt.pix.width=640;
    vFormat.fmt.pix.height=480;
    vFormat.fmt.pix.pixelformat=V4L2_PIX_FMT_MJPEG;
     ret=ioctl(fd,VIDIOC_S_FMT,&vFormat);
    if(ret<0){
        perror("set failn");
    }
    //申请内核空间
    struct v4l2_requestbuffers vqbuff;
    vqbuff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vqbuff.count=4;
    vqbuff.memory=V4L2_MEMORY_MMAP;
    ret =ioctl(fd,VIDIOC_REQBUFS,&vqbuff);
    if(ret<0){
        perror("buff failn");
    }
    //申请内存空间
    struct v4l2_buffer vbuff;
    vbuff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

    unsigned char * mptr[4];
    for(int i=0;i<4;i++){
        vbuff.index=i;
        ret=ioctl(fd,VIDIOC_QUERYBUF,&vbuff);
        if(ret<0)
        {
            perror("requeire buff failn");
        }
        mptr[i]= (unsigned char *s)mmap(NULL,vbuff.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,vbuff.m.offset);
        //通知完毕
        ret=ioctl(fd,VIDIOC_QBUF,&vbuff);
        if(ret<0){
            perror("put fail");
        }

    }
  // 开始采集
  int type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
  ret=ioctl(fd,VIDIOC_STREAMON,&type);
  if(ret<0){
      perror("open fail");
  }
  //从队列中取数据
  struct v4l2_buffer readbuff;
  readbuff.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
  ret=ioctl(fd,VIDIOC_DQBUF,&readbuff);
  if(ret<0){
      perror("read fail");

  }

FILE* file=fopen("example.jpg","w+");
   fwrite(mptr[readbuff.index],readbuff.length,1,file);
  fclose(file);

  //通知内核 已经使用完
  ret=ioctl(fd,VIDIOC_QBUF,&readbuff);
  if(ret<0){

      perror("put quee failn");
  }
 //停止采集
 ret=ioctl(fd,VIDIOC_STREAMOFF,&type);
    if(ret<0){

        perror("stop eque@e failn");
    }
    //关闭设备
    close(fd);
    printf("close device successfullyn");
    return 0;
}

写到本地的图片

cmakelist.txt

cmake_minimum_required(VERSION 3.16)
project(untitled3)

set(CMAKE_CXX_STANDARD 14)
#sudo apt install libjpeg8-dev
add_executable(untitled3 main.cpp)
target_link_libraries(
        untitled3
        -ljpeg
)

源代码,编译ctrl+alt+f1在命令行执行

#include 
#include 
#include 
#include
#include
#include
#include 
#include
#include
#include 
#include 
#include 

int read_JPEG_file(unsigned char *jpegData, unsigned char *rgbdata, int size) {
    struct jpeg_error_mgr jerr;
    struct jpeg_decompress_struct cinfo;
    cinfo.err = jpeg_std_error(&jerr);
    //1创建解码对象并且初始化
    jpeg_create_decompress(&cinfo);
    //2.装备解码的数据
    //jpeg_stdio_src(&cinfo, infile);
    jpeg_mem_src(&cinfo, (unsigned char *) jpegData, size);
    //3.获取jpeg图片文件的参数
    (void) jpeg_read_header(&cinfo, TRUE);
    
    //5.开始解码
    (void) jpeg_start_decompress(&cinfo);
    //6.申请存储一行数据的内存空间
    int row_stride = cinfo.output_width * cinfo.output_components;
    unsigned char *buffer = (unsigned char *) (malloc(row_stride));
    int i = 0;
    while (cinfo.output_scanline < cinfo.output_height) {
        //printf("****%dn",i);
        (void) jpeg_read_scanlines(&cinfo, &buffer, 1);
        memcpy(rgbdata + i * 640 * 3, buffer, row_stride);
        i++;
    }
    //7.解码完成
    (void) jpeg_finish_decompress(&cinfo);
    //8.释放解码对象
    jpeg_destroy_decompress(&cinfo);
    return 1;
}


void lcd_show_rgb(unsigned int *lcdptr, int lcd_w, int lcd_h, unsigned char *rgb_data, int width, int height) {
    unsigned int *ptr = lcdptr;
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            memcpy(ptr + j, rgb_data + j * 3, 3);
        }
        ptr += lcd_w;
        rgb_data += width * 3;
    }
}

int main() {
    //打开lcd
    int lcdfd = open("/dev/fb0", O_RDWR);
    if (lcdfd < 0) {
        perror("open lcdfd fail");
        return -1;
    }
    struct fb_var_screeninfo info;
    ioctl(lcdfd, FBIOGET_VSCREENINFO, &info);
    printf("xres = %u, yres = %u.n", info.xres, info.yres);
    printf("xres_virtual = %u, yres_virtual = %u.n", info.xres_virtual, info.yres_virtual);
    printf("bpp = %u.n", info.bits_per_pixel);
    //xu ni ji ubuntu
   // int lcd_w = info.xres_virtual;
    //int lcd_h = info.yres_virtual;
    // arm
     int lcd_w = info.xres;
    int  lcd_h = info.yres;
    unsigned int *lcdptr = (unsigned int *) mmap(NULL, lcd_w * lcd_h * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
    if (lcdptr == NULL) {
        perror("create lcdptr fail");
        return -1;
    }
    //打开设备
    int fd = open("/dev/video0", O_RDWR);
    if (fd < 0) {
        perror("open device failn");
        return -1;
    }
    // 获取摄像头支持的格式
    //获取摄像头支持的格式
    struct v4l2_fmtdesc v4fmt;
    v4fmt.index = 0;
    v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
    if (ret < 0) {
        perror("acquire failn");
        return -1;
    }
    printf("%sn", v4fmt.description);
    unsigned char *p = (unsigned char *) &v4fmt.pixelformat;
    printf("%c %c %c %cn", p[0], p[1], p[2], p[3]);
    // 设置摄像头支持的格式
    //设置摄像头支持的格式
    struct v4l2_format vFormat;
    vFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vFormat.fmt.pix.width = 640;
    vFormat.fmt.pix.height = 480;
    vFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
    ret = ioctl(fd, VIDIOC_S_FMT, &vFormat);
    if (ret < 0) {
        perror("set failn");
        return -1;
    }
    //申请内核空间
    struct v4l2_requestbuffers vqbuff;
    vqbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vqbuff.count = 4;
    vqbuff.memory = V4L2_MEMORY_MMAP;
    ret = ioctl(fd, VIDIOC_REQBUFS, &vqbuff);
    if (ret < 0) {
        perror("buff failn");
        return -1;
    }
    //申请内存空间
    struct v4l2_buffer mptrbuff;
    mptrbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    unsigned char *mptr[4];
    unsigned int mptr_len[4];
    for (int i = 0; i < 4; i++) {
        mptrbuff.index = i;
        ret = ioctl(fd, VIDIOC_QUERYBUF, &mptrbuff);
        if (ret < 0) {
            perror("require buff failn");
            return -1;
        }
        mptr[i] = (unsigned char *) mmap(NULL, mptrbuff.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
                                         mptrbuff.m.offset);
        mptr_len[i] = mptrbuff.length;
        //通知完毕
        ret = ioctl(fd, VIDIOC_QBUF, &mptrbuff);
        if (ret < 0) {
            perror("put fail");
            return -1;
        }

    }
    // 开始采集
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd, VIDIOC_STREAMON, &type);
    if (ret < 0) {
        perror("open fail");
        return -1;
    }
    unsigned char rgb_data[640 * 480 * 3];
    while (true) {
        //从队列中取数据
        struct v4l2_buffer readbuff;
        readbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl(fd, VIDIOC_DQBUF, &readbuff);
        if (ret < 0) {
            perror("read fail");
        }
          read_JPEG_file(mptr[readbuff.index], rgb_data, readbuff.length);
          lcd_show_rgb(lcdptr,lcd_w,lcd_h, rgb_data, 640, 480);

        //  FILE* file=fopen("example.jpg","w+");
        //    fwrite(mptr[readbuff.index],readbuff.length,1,file);
        //fclose(file);

        //通知内核 已经使用完
        ret = ioctl(fd, VIDIOC_QBUF, &readbuff);
        if (ret < 0) {
            perror("put equee failn");

        }
    }
        //停止采集
        ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
        if (ret < 0) {
            perror("stop eque@e failn");

        }

    //释放资源、
    for (int i = 0; i < 4; i++) {
        munmap(mptr[i], mptr_len[i]);
    }
    //关闭设备
    close(fd);
    printf("close device successfullyn");
    return 0;
}

测是的MJPEG的格式的图片显示

 yuyv转bgr显示 640*480

#include 
#include 
#include 
#include
#include
#include
#include 
#include
#include
#include 
#include 
#include 

int read_JPEG_file(unsigned char *jpegData, unsigned char *rgbdata, int size) {
    struct jpeg_error_mgr jerr;
    struct jpeg_decompress_struct cinfo;
    cinfo.err = jpeg_std_error(&jerr);
    //1创建解码对象并且初始化
    jpeg_create_decompress(&cinfo);
    //2.装备解码的数据
    //jpeg_stdio_src(&cinfo, infile);
    jpeg_mem_src(&cinfo, (unsigned char *) jpegData, size);
    //3.获取jpeg图片文件的参数
    (void) jpeg_read_header(&cinfo, TRUE);
    
    //5.开始解码
    (void) jpeg_start_decompress(&cinfo);
    //6.申请存储一行数据的内存空间
    int row_stride = cinfo.output_width * cinfo.output_components;
    unsigned char *buffer = (unsigned char *) (malloc(row_stride));
    int i = 0;
    while (cinfo.output_scanline < cinfo.output_height) {
        //printf("****%dn",i);
        (void) jpeg_read_scanlines(&cinfo, &buffer, 1);
        memcpy(rgbdata + i * 640 * 3, buffer, row_stride);
        i++;
    }
    //7.解码完成
    (void) jpeg_finish_decompress(&cinfo);
    //8.释放解码对象
    jpeg_destroy_decompress(&cinfo);
    return 1;
}
void yuyv_to_rgb(unsigned char *yuyvdata, unsigned char *rgbdata, int w, int h)
{
    //码流Y0 U0 Y1 V1 Y2 U2 Y3 V3 --》YUYV像素[Y0 U0 V1] [Y1 U0 V1] [Y2 U2 V3] [Y3 U2 V3]--》RGB像素
    int r1, g1, b1;
    int r2, g2, b2;
    for(int i=0; i[Y0 U0 V1] [Y1 U0 V1]
        r1 = Y0+1.4075*(V1-128); if(r1>255)r1=255; if(r1<0)r1=0;
        g1 =Y0- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g1>255)g1=255; if(g1<0)g1=0;
        b1 = Y0 + 1.779 * (U0-128);  if(b1>255)b1=255; if(b1<0)b1=0;

        r2 = Y1+1.4075*(V1-128);if(r2>255)r2=255; if(r2<0)r2=0;
        g2 = Y1- 0.3455 * (U0-128) - 0.7169*(V1-128); if(g2>255)g2=255; if(g2<0)g2=0;
        b2 = Y1 + 1.779 * (U0-128);  if(b2>255)b2=255; if(b2<0)b2=0;
// rgb的数据
//        rgbdata[i*6+0]=r1;
//        rgbdata[i*6+1]=g1;
//        rgbdata[i*6+2]=b1;
//        rgbdata[i*6+3]=r2;
//        rgbdata[i*6+4]=g2;
//        rgbdata[i*6+5]=b2;
//bgr的数据
        rgbdata[i*6+0]=b1;
        rgbdata[i*6+1]=g1;
        rgbdata[i*6+2]=r1;
        rgbdata[i*6+3]=b2;
        rgbdata[i*6+4]=g2;
        rgbdata[i*6+5]=r2;
    }
}

void lcd_show_rgb(unsigned int *lcdptr, int lcd_w, int lcd_h, unsigned char *rgb_data, int width, int height) {
    unsigned int *ptr = lcdptr;
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            memcpy(ptr + j, rgb_data + j * 3, 3);
        }
        ptr += lcd_w;
        rgb_data += width * 3;
    }
}

int main() {
    //打开lcd
    int lcdfd = open("/dev/fb0", O_RDWR);
    if (lcdfd < 0) {
        perror("open lcdfd fail");
        return -1;
    }
    struct fb_var_screeninfo info;
    ioctl(lcdfd, FBIOGET_VSCREENINFO, &info);
    printf("xres = %u, yres = %u.n", info.xres, info.yres);
    printf("xres_virtual = %u, yres_virtual = %u.n", info.xres_virtual, info.yres_virtual);
    printf("bpp = %u.n", info.bits_per_pixel);
    //xu ni ji ubuntu
    // int lcd_w = info.xres_virtual;
    //int lcd_h = info.yres_virtual;
    // arm
    int lcd_w = info.xres;
    int  lcd_h = info.yres;
    unsigned int *lcdptr = (unsigned int *) mmap(NULL, lcd_w * lcd_h * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcdfd, 0);
    if (lcdptr == NULL) {
        perror("create lcdptr fail");
        return -1;
    }
    //打开设备
    int fd = open("/dev/video0", O_RDWR);
    if (fd < 0) {
        perror("open device failn");
        return -1;
    }
    // 获取摄像头支持的格式
    //获取摄像头支持的格式
    struct v4l2_fmtdesc v4fmt;
    v4fmt.index = 0;
    v4fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    int ret = ioctl(fd, VIDIOC_ENUM_FMT, &v4fmt);
    if (ret < 0) {
        perror("acquire failn");
        return -1;
    }
    printf("%sn", v4fmt.description);
    unsigned char *p = (unsigned char *) &v4fmt.pixelformat;
    printf("%c %c %c %cn", p[0], p[1], p[2], p[3]);
    // 设置摄像头支持的格式
    //设置摄像头支持的格式
    struct v4l2_format vFormat;
    vFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vFormat.fmt.pix.width = 640;
    vFormat.fmt.pix.height = 480;
    vFormat.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    ret = ioctl(fd, VIDIOC_S_FMT, &vFormat);
    if (ret < 0) {
        perror("set failn");
        return -1;
    }
    //申请内核空间
    struct v4l2_requestbuffers vqbuff;
    vqbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vqbuff.count = 4;
    vqbuff.memory = V4L2_MEMORY_MMAP;
    ret = ioctl(fd, VIDIOC_REQBUFS, &vqbuff);
    if (ret < 0) {
        perror("buff failn");
        return -1;
    }
    //申请内存空间
    struct v4l2_buffer mptrbuff;
    mptrbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    unsigned char *mptr[4];
    unsigned int mptr_len[4];
    for (int i = 0; i < 4; i++) {
        mptrbuff.index = i;
        ret = ioctl(fd, VIDIOC_QUERYBUF, &mptrbuff);
        if (ret < 0) {
            perror("require buff failn");
            return -1;
        }
        mptr[i] = (unsigned char *) mmap(NULL, mptrbuff.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
                                         mptrbuff.m.offset);
        mptr_len[i] = mptrbuff.length;
        //通知完毕
        ret = ioctl(fd, VIDIOC_QBUF, &mptrbuff);
        if (ret < 0) {
            perror("put fail");
            return -1;
        }

    }
    // 开始采集
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd, VIDIOC_STREAMON, &type);
    if (ret < 0) {
        perror("open fail");
        return -1;
    }
    unsigned char rgb_data[640 * 480 * 3];
    while (true) {
        //从队列中取数据
        struct v4l2_buffer readbuff;
        readbuff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl(fd, VIDIOC_DQBUF, &readbuff);
        if (ret < 0) {
            perror("read fail");
        }
        yuyv_to_rgb(mptr[readbuff.index], rgb_data, 640, 480);
        //read_JPEG_file(mptr[readbuff.index], rgb_data, readbuff.length);
        lcd_show_rgb(lcdptr,lcd_w,lcd_h, rgb_data, 640, 480);

        //  FILE* file=fopen("example.jpg","w+");
        //    fwrite(mptr[readbuff.index],readbuff.length,1,file);
        //fclose(file);

        //通知内核 已经使用完
        ret = ioctl(fd, VIDIOC_QBUF, &readbuff);
        if (ret < 0) {
            perror("put equee failn");

        }
    }
    //停止采集
    ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
    if (ret < 0) {
        perror("stop eque@e failn");

    }

    //释放资源、
    for (int i = 0; i < 4; i++) {
        munmap(mptr[i], mptr_len[i]);
    }
    //关闭设备
    close(fd);
    printf("close device successfullyn");
    return 0;
}

测试图片 

参考

6、读取YUV数据并理解YUV数据_sxj731533730的博客-CSDN博客_解析yuv数据

有道云笔记

 Linux应用开发【第七章】摄像头V4L2编程应用开发 - 知乎

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/882329.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号