0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

英创信息技术Linux工控主板摄像头应用简介

英创信息技术 来源:英创信息技术 作者:英创信息技术 2020-02-03 09:01 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

近年来,随着计算机、网络以及图像处理、传输技术的飞速发展,摄像头在工业控制领域的应用也越来越广泛了,目前市面上的摄像头可以分为两类,一种是符合UVC规范的摄像头,比如罗技的摄像头就是UVC摄像头。另一种是non-UVC摄像头,即不符合UVC规范。UVC全称为:USB video class (USB视频类)在Linux-2.6.4及以上的版本都已经集成了UCV设备的驱动,而non-UVC摄像头如果要使用,就需要硬件厂商提供专用的驱动。比如中星微的摄像头就是non-UVC设备,需要专用的驱动。

1、Linux内核配置

本文以英创嵌入式板卡EM335x 为例来介绍对于USB摄像头的支持,EM335x内核版本为Linux-3.12.10,USB摄像头选用中星微的ZC301摄像头,该摄像头以其高性价比得以广泛应用,同时在Linux内核中已经包括了对于ZC3XX系列摄像头的驱动支持。

内核配置如下:
<*> Multimedia support --->
[*] Cameras/video grabbers support
[*] Media USB Adapters --->
<*> USB Video Class (UVC)
[*] UVC input events device support
<*> GSPCA based webcams --->
ZC3XX USB Camera Driver

编译成功后,即可得到zc3xx系列USB摄像头驱动文件:gspca_zc3xx.ko。

在EM335x板卡上,该文件放置在根文件系统/lib/modules/3.12.10/目录下。应用时只需调用以下命令,即可完成对于USB摄像头的驱动加载。

insmod /lib/modules/3.12.10/gspca_zc3xx.ko

驱动加载成功后,会自动生成设备节点:“/dev/video0',应用程序可以操作该设备节点对摄像头进行图像的采集和控制。因为中星微的摄像头为non-UVC设备,所以需要再加专用的gspca_zc3xx.ko,如果是其他的UVC摄像头,内核中已经集成了驱动,插上后就可以识别出来,不用再加载其他驱动。

2、Qt摄像头应用程序简介

UVC和non-UVC摄像头都是用了V4L2驱动提供的API来操作摄像头。Video for Linux two简称V4L2,是V4L的改进版。V4L2是Linux操作系统下用于采集图片、视频和音频数据的API接口,配合适当的视频采集设备和相应的驱动程序,可以实现图片、视频、音频等的采集。在视频监控系统和嵌入式多媒体终端中都有广泛的应用。V4L2支持两种方式来采集图像:内存映射方式(mmap)和直接读取方式(read)。在这里我们使用内存映射的方式来进行视频采集。应用程序通过V4L2接口采集视频数据可以分为五个步骤:

①打开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;
②申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;
③将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;
④驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;
⑤停止视频采集。

可以参考下图:

可以看到每一个步骤都是通过ioctl这个接口去设置一些参数来实现的, 启动视频采集后,驱动程序开始采集数据,并把采集的数据放入视频采集输入队列的第一个帧缓冲区,当一帧数据采集完成,也就是第一个帧缓冲区存满数据以后,驱动程序将这一个缓冲区移至视频采集输出队列,等待应用程序取出。驱动程序接下来继续采集下一帧数据,并放入第二个帧缓冲区,同样帧缓冲区存满数据后,被放入视频采集输出队列。

应用程序从视频采集输出队列中取出含有视频数据的帧缓冲区,处理帧缓冲区中的视频数据,如存储或压缩。如果需要连续采集,应用程序需要将处理完数据的帧缓冲区重新放入视频采集输入队列,如图所示。

接下来结合程序来具体看一看通过V4L2接口来操作摄像头的一些重要的步骤:

打开设备文件:
int fd;
fd=open('/dev/video0',O_RDWR);

获取设备的基本信息,包括驱动版本号,设备支持操作等:
struct v4l2_capability cap;
ret=ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(ret<0)
{
printf('failture VIDIOC_QUERYCAP ');
return -1;
}
printf('DriverName:%s Card Name:%s Bus info:%s DriverVersion:%u.%u.%u ',cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0xFF,(cap.version>>8)&0xFF,cap.version&0xFF);

显示所支持的格式:
memset(&fmtdesc, 0, sizeof(fmtdesc));
fmtdesc.index = 0;
//数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
printf('/t%d.%s/n',fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}

设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等:
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
fmt.fmt.pix.width = 640;//宽,必须是16的倍数
fmt.fmt.pix.height = 480;//高,必须是16的倍数
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;//视频数据存储类型//V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YVU420;//V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
//设置当前驱动的频捕获格式
ret = ioctl (fd, VIDIOC_S_FMT, &fmt);
if(ret<0)
{
printf('failture VIDIOC_S_FMT ');
return -1;
}

向驱动申请帧缓冲,一般不超过五个:
struct v4l2_requestbuffers req;
req.count=1;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
//申请帧缓冲
ret=ioctl(fd,VIDIOC_REQBUFS,&req);
if(ret<0)
{
printf('failture VIDIOC_REQBUFS ');
return -1;
}
if (req.count < 1)
{
printf('Insufficient buffer memory ');
return -1;
}

将申请到的帧缓冲映射到用户空间,这样就能够直接操作帧缓冲了:
buffers =(buffer*)calloc (req.count, sizeof (*buffers));
if (!buffers) {
fprintf (stderr,'Out of memory/n');
exit(EXIT_FAILURE);
}
for (n_buffers = 0; n_buffers < req.count; ++n_buffers)
{
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory =V4L2_MEMORY_MMAP;
buf.index =n_buffers;
//查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小
if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
{
printf('failture VIDIOC_QUERYBUF ');
return -1;
}
buffers[n_buffers].length= buf.length;
//映射内存
buffers[n_buffers].start=mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);
if (MAP_FAILED == buffers[n_buffers].start)
{
printf('failture mmap ');
return -1;
}
}

将申请到的帧缓冲全部入队列,以便存放采集到的数据:
for (i = 0; i< req.count; ++i)
{
struct v4l2_buffer buffer;
buffer.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory =V4L2_MEMORY_MMAP;
buffer.index = i;
//将缓冲帧放入队列尾
ioctl (fd,VIDIOC_QBUF, &buffer);
}

开始视频的采集:
type =V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd,VIDIOC_STREAMON, &type);

取出队列中以取得采集数据的帧缓冲,获得原始采集数据,因为这个摄像头支持的格式为JPG,所以程序中将原始数据保存在新建的一个*.jpg文件中:
struct v4l2_buffer camera_buf;
CLEAR (camera_buf);
camera_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
camera_buf.memory = V4L2_MEMORY_MMAP;
//取出一个缓冲帧
i1 = ioctl (fd, VIDIOC_DQBUF, &camera_buf);
if(i1<0)
{
printf('failture ');
return -1;
}
fwrite(buffers[camera_buf.index].start, buffers[camera_buf.index].length, 1, file_fd);//将其写入文件中

将缓冲帧重新入队列尾,这样可以循环采集:
//将缓冲重新入队列尾
i1=ioctl (fd, VIDIOC_QBUF, &camera_buf);
if(i1<0)
{
printf('failture VIDIOC_QBUF ');
return -1;
}

如果需要关闭摄像头,先停止视屏采集,释放申请的帧缓冲,最后关闭设备节点:
//停止视频的采集。VIDIOC_STREAMOFF
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))
printf('VIDIOC_STREAMOFF');
for (i = 0; i < n_buffers; ++i)
if (-1 == munmap (buffers->start, buffers->length))
printf ('munmap error');
free(buffers);
//关闭视频设备
close (fd);

所以通过这一套通用的V4L2接口来操作摄像头的工作流程:

打开设备-> 检查和设置设备属性->设置帧格式-> 设置一种输入输出方法(缓冲区管理)-> 循环获取数据-> 关闭设备。通过这几个步骤已经可以操作摄像头来获取数据,下面来看看如何与Qt结合,将前面的代码与Qt界面结合起来。

在Qt中主要就是实现两个功能,一个是通过界面控制摄像头的数据获取,另一个是通过界面显示摄像头所拍摄下来的图片。摄像头的初始化设置,包括格式等参数的设置可以在Qt界面的构造函数中完成。

通过界面来控制摄像头,可以在Qt的界面上做一个按钮,在按钮的单击事件槽中调用摄像头采集数据的部分即可:
void MainWindow::on_init_camera_clicked()//按钮单击事件
{
for (;;)//这一段涉及到异步IO
{
fd_set fds;
struct timeval tv;
int r;
FD_ZERO (&fds);//将指定的文件描述符集清空
FD_SET (fd, &fds);//在文件描述符集合中增加新的文件描述符
tv.tv_sec = 0;
tv.tv_usec = 500000;
r = select (fd + 1, &fds, NULL, NULL, &tv);//判断是否可读(即摄像头是否准备好),tv是定时
if (-1 == r)
{
if (EINTR == errno)
continue;
printf ('select err ');
}
if (read_frame ())//如果可读,执行read_frame ()函数,并跳出循环
break;
else
{
QMessageBox::information(this, tr('失败'), tr('拍摄图片失败') , QMessageBox::Ok);
}
}
}

关于拍摄图片的显示问题,Qt中提供了很多实现的方法,比如可以在界面中采用一个label来显示,这里采用GraphicsView来显示,主要代码如下:
image=new QImage(pictrue_name);
image->load(pictrue_name);
scene = new QGraphicsScene;
scene->addPixmap(QPixmap::fromImage(*image));
ui->graphicsView->setScene(scene);
ui->graphicsView->setAlignment(Qt::AlignCenter);
ui->graphicsView->show();//显示

将摄像头获取的数据写入文件中,再通过GraphicsView显示出来。这样就实现了Qt程序和摄像头操作的结合,详细的代码请参考例程。

例程的效果如下图所示:

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11628

    浏览量

    217971
  • 嵌入式主板
    +关注

    关注

    7

    文章

    6107

    浏览量

    36950
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    车载双目摄像头如何“看见”世界?

    源自:网络 车载双目摄像头(也称立体视觉摄像头,Stereo Camera)模仿人眼的视觉机制,通过两个略有间距的摄像头同时拍摄同一场景,比较两幅图像之间的差异,从而计算出深度信息
    的头像 发表于 11-13 09:17 658次阅读
    车载双目<b class='flag-5'>摄像头</b>如何“看见”世界?

    新时代的硬核基石:龙芯工控主板助力信产业新基建

    在数字技术重塑产业格局的今天,信产业已成为保障国家信息技术安全的核心战略。作为工业自动化系统的大脑,工控主板的自主可控程度直接决定信创新基
    的头像 发表于 11-05 13:54 132次阅读

    创世SD NAND贴片卡:智能摄像头存储难题的完美解决方案 #人工智能 #智能摄像头 #摄像头

    摄像头
    深圳市雷龙发展有限公司
    发布于 :2025年08月05日 11:17:13

    红外摄像头模组是什么?科技时代的眼睛

    在现代科技迅速发展的背景下,红外摄像头模组逐渐成为各类智能设备中的重要组成部分。无论是在安防监控、智能家居还是汽车领域,红外摄像头模组以其独特的功能和优越的性能,正在改变我们的生活方式。本文将
    的头像 发表于 07-31 10:07 691次阅读
    红外<b class='flag-5'>摄像头</b>模组是什么?科技时代的眼睛

    庐山派上用v1.9的linux+RTSmart SDK,修改摄像头接cs1报错怎么解决?

    庐山派上用v1.9的linux+RTSmart SDK跑ai_poc里face_detection例程,目前摄像头接csi2接口能在LCD上跑通,但是把摄像头接到cs1接口就提示如下错误,两种方式修改的地方如下:
    发表于 07-22 06:52

    【EASY EAI Orin Nano开发板试用体验】--USB摄像头使用

    可用设备节点ls /dev/video* 3.定位接入USB摄像头节点设备cd /sys/class/video4linux/cat /sys/class/video4linux/video22
    发表于 07-13 18:21

    【Milk-V Duo S 开发板免费体验】4 - OV5647 摄像头摄像头测试

    引言 以前使用Milk-V Duo的时候,觉得它的摄像头支持比较弱,只能支持特定型号的摄像头,还不好买。我有个学生甚至因为这个原因退出了集赛。这次特意测试了一下它的摄像头。 DuoS
    发表于 07-08 21:26

    庐山派K230可以直接使用usb摄像头吗,可以的话摄像头有啥要求吗?

    庐山派K230可以直接使用usb摄像头吗,可以的话摄像头有啥要求吗
    发表于 06-24 07:07

    canmv-k230使用摄像头出现紫色画面,并且显示不完全怎么修复?

    摄像头显示画面程序设置为开机自启动,插type-C开机,有一定概率出现紫色画面的问题,并且摄像头画面显示不完整,一直刷。 软硬件版本信息 01studio主板,gc2093
    发表于 04-28 07:02

    嘉立庐山派,摄像头和处理后的画面有办法通过无线的方式传递给电脑吗?

    如题:嘉立庐山派,摄像头和处理后的画面有办法通过无线的方式传递给电脑吗?求大佬指点。
    发表于 04-27 08:34

    社区安装IPC摄像头,跟安装一般安防监控摄像头有什么区别?

    为了保护社区或其他公共场所的安全,往往需要安装监控摄像头。但随着越来越多的公共安全事件发生,传统只单纯拍摄和记录画面的监控摄像头已不能满足社区安防的需求,需要更多功能的安防监控摄像头来进行更智能化
    的头像 发表于 04-03 10:00 1606次阅读
    社区安装IPC<b class='flag-5'>摄像头</b>,跟安装一般安防监控<b class='flag-5'>摄像头</b>有什么区别?

    车载摄像头的EMC问题案例

    一前言 如今,车载摄像头已经不再仅仅是一个辅助设备,而是逐渐成为了智能驾驶系统中不可或缺的一部分。360度全景监控、行车记录仪以及自动驾驶辅助功能,车载摄像头的应用范围不断拓展,技术也在不断创新
    的头像 发表于 12-18 11:11 2036次阅读
    车载<b class='flag-5'>摄像头</b>的EMC问题案例

    车载摄像头的EMC案例分享

    如今,车载摄像头已经不再仅仅是一个辅助设备,而是逐渐成为了智能驾驶系统中不可或缺的一部分。360度全景监控、行车记录仪以及自动驾驶辅助功能,车载摄像头的应用范围不断拓展,技术也在不断创新。它能够识别道路标志、检测障碍物、监控驾驶
    的头像 发表于 12-18 11:07 1975次阅读
    车载<b class='flag-5'>摄像头</b>的EMC案例分享

    安防监控摄像头气密性测试案例-连拓精密#摄像头气密检测设备

    摄像头
    连拓精密科技
    发布于 :2024年12月11日 15:00:21

    多光谱火焰检测摄像头

    ,成为现代火灾监测领域的一项重要技术。多光谱火焰检测摄像头通过捕捉不同波段的光谱信息来识别火焰。与传统摄像头仅能获取可见光信息不同,多光谱
    的头像 发表于 12-11 10:50 1095次阅读
    多光谱火焰检测<b class='flag-5'>摄像头</b>