谢发荣,张 力,陈宇星
(昆明理工大学信息工程与自动化学院计算机科学与技术系,昆明 650500)
系统主要由图像采集端、嵌入式服务器、服务器端显示、监控终端4部分组成。系统结构如图1所示。图像采集端采用USB摄像头进行现场监控采集,嵌入式服务器移植Boa为Web服务器,用C语言编写CGI程序放在Web服务器上,监控客户端浏览器通过HTTP协议请求CGI网页,Web服务器接收到客户端请求,启动图像采集程序,采集程序可以把采集到的图片直接保存成文件,可以用JPEG压缩处理,也可以把图片显示在服务器端LCD上,同时Web服务器解析CGI网页后给客户端应答,在客户端网页上就能显示动态图像。此外可以在客户端网页上可视化地配置服务端采集程序图像帧率fps(frame per second),图像分辨率,图像压缩比,图像显示格式,服务器端LCD上是否实时显示动态图像等。
图1 系统结构图
系统采用的处理器是Samsung公司生产的ARM体系16/32位ARM920T的RISC处理器S3C2440A。ARM的主要特点是高性能,低功耗,低价格,符合嵌入式产品需求。系统用到的外设主要有USB摄像头和一个LCD等。有关该处理器的详细信息可到Samsung公司官网查看技术文档。操作系统内核主目录上的Makefile文件中有menuconfig,uImage,zImage等目标,在命令行上键入 make menuconfig得到一个可视化选择界面,可以选择要参与编译的选项,系统中使用到USB摄像头,因此要注意选择USB驱动支持和V4L2驱动选项,选择后保存可以生成.config文件,再键入make uImage可生成内核镜像文件,利用bootloader把内核镜像文件下载到开发板的NandFlash,在u-boot的引导下操作系统就可以运行起来了[2]。
Linux平台下第一代视频编程的接口是Video for Linux(V4L),是为统一和规范各种视频设备引入的统一接口,是各设备提供商在为设备编写驱动时遵循的标准,也是编写视频应用程序要遵循的标准。在Linux C下这个标准以头文件的形式放于Linux内核中。在Linux中视频设备是设备文件,可以像访问普通文件一样对其读写,摄像头在/dev/video0下。因为系统中USB摄像头采用V4L2标准,所以以下主要介绍V4L2视频采集接口的编程方法。采集过程定义了一个结构如下。
typedef struct VideoBuffer{
void*start;//指向开始位置
size_t length;//长度
}VideoBuffer;//VideoBuffer类型定义一个缓冲,用来存放映射地址
采集流程如图2所示。初始化包括打开设备文件、查询设备是否具有特定的能力、设置图片属性(如图片亮度、对比度)、设置图片采集帧率。全部缓冲入队列的目的是让缓冲队列的个数为申请到的缓冲个数,一般为5个,缓冲个数太小则会影响采集速度,太大会占用太多内存。申请缓冲并映射包括向驱动申请缓冲,查询缓冲的大小和偏移量,定义一个VideoBuffer*类型数据并分配地址,根据缓冲大小和偏移量将驱动的缓冲映射到用户区域。采用内存映射操作内存就相当于操作文件,对于读取大量数据的文件是相当有效的,而采用直接读写文件的方式读写太频繁,造成读写效率低下速度慢[3-4]。是否可读利用select函数监控设备是否可读,若不可读或监控超时则结束程序,若可读则从队列里取出一个数据处理,再入队列以保持缓冲个数。
图2 V4L2采集流
实验使用的USB摄像头只支持MJPEG格式,是一种动态压缩格式,相对JPEG压缩而言,可在一帧内对一部分压缩,压缩后传输,最后再补齐未压缩的,而JPEG压缩是对整个画面压缩,对每一帧的压缩也是采用JPEG压缩。因 V4L2中没有提供对MJPEG压缩的设置,分辨率640x480的一帧数据达90kB,若直接对其传输无法满足实时性要求,所以对MJPEG数据的处理是用JPEG算法对其解压,再用某些压缩算法压缩,系统仍采用JPEG压缩,在不影响视觉情况下,一帧大小达10KB左右。在Linux下可采用jpeglib开源库对数据进行解压和压缩处理,解压处理过程数据流向是从文件到内存,压缩过程数据流向是从内存到文件。JPEG解压过程和压缩过程可阅读jpeglib的文档。因程序在ARM Linux平台上运行,应该将jpeglib库做交叉编译,并将编译过的库拷贝到ARM Linux下。
Simple Direct Media Layer(简称SDL)是一个跨平台的多媒体库,是直接控制底层的多媒体硬件接口。SDL在嵌入式平台下使用FrameBuffer驱动。SDL是在GNU LGPL Version 2下发布的,这意味着可对SDL源代码修改而不用公开代码,免费用于商业软件,libSDL库可从官网下载[9-10]。用 SDL库可直接将BMP图片显示在LCD上,但要显示JPEG图片,先要把它解压才能显示到LCD上,JPEG解压过程如上节所述。根据SDL库的开发文档,显示过程为先创建屏幕层,再创建图层,在图层上放JPEG解压后的数据,再把图层放到屏幕层上即可显示,循环更改图像层的数据,即可实现动态显示图像。
CGI(Common Gateway Interface)是通用网关接口,是Web服务器提供交互式信息服务的标准接口。Web服务器上执行CGI程序后把输出结果(特定功能的HTML代码)给浏览器显示[1]。CGI可用任何语言来编写,但由于C语言是编译执行的,执行速度快,并且具有很好的移植性,所以嵌入式Linux平台下用C语言写CGI是首选。Web服务器通过调用 CGI程序实现和 Web浏览器的交互,POST方式时CGI通过标准输入STDIN从Web服务器获得输入数据,GET方式时CGI通过环境变量从Web服务器获得输入数据,CGI处理输入的数据后,Web服务器将CGI执行结果(HTML文件)通过stdout给浏览器。编写CGI程序前,要移植Web服务器Boa,它是一个小巧、高性能的自由软件,广泛应用于嵌入式系统[6、8],移植过程可参考其文档。系统中浏览器和Web服务器交互的方法采用GET方法。根据以上介绍,C语言编写CGI程序时用函数getenv()获得环境变量作为输入数据,CGI各个环境变量的作用可参考相关文献[1],环境变量REQUEST_METHOD为浏览器和Web服务器交互方法,QUERY_STRING为GET方法时通过URL传递参数字符串[5、7]。输出 html给 Web服务器解析的方法有两种,第一种,用标准输出函数如printf(),puts(),fprintf(),fwrite()等把HTML代码输出到标准输出stdout,前两个函数就是直接输出到标准输出,而后两个函数要指明stdout,Web服务器对其解析并返回给浏览器。第二种,对于现成的HTML文件,CGI提供了重定向输出,printf("Location: ../image.htm ")。系统采用第二种方法,把动态显示图片网页做成image.htm。
以下制作显示图像页面image.htm。动态显示图片的方法很多。方法一使用Java Applet不断刷新;方法二用<mata content-type=refresh content=n>定时n秒刷新整个页面;方法三用JavaScript脚本语言定时调用网页重载函数setInterval(“window.location.reload()”,600)[8]; 方 法 四 使 用 框 架IFRAM。比较这些方法,方法一可实现动态图像流畅显示,由于Applet程序在浏览器的虚拟机中运行,开发者须确保客户端安装适当版本的Java运行环境。方法二和三都定时刷新整个页面,使Web服务器负担加重且图片显示不流畅。方法四因采用局部刷新页面,效果比方法二、三好,Web服务器负担减轻,但实现复杂,需要两个以上网页嵌套。系统利用JavaSctipt和图像预加载技术实现动态图像显示。
图片预加载技术是指在显示图片前从服务器上加载图片,显示图片时直接从浏览器缓存里显示而不必从服务器下载。用JavaScript实现图片预加载的原理是在HTML中建一个<img>图片,在内存中建立10个Image对象(因在图像采集端采集到的图像0.jpg…9.jpg)。预加载时指定每一张图像的src属性,因每次预加载都要从服务器端下载,所以在src属性的地址后面加上随机数Math.random()防止预加载使用缓存。在显示图片前都调用预加载函数,并指明HTML<img>的src属性就为预加载图片地址,二者地址要相同,否则显示图片时浏览器又要从服务器端下载图片。可适当调整调用预加载函数时间间隔来达到最佳效果,系统使用800ms。比较而言,使用Javascript和图像预加载技术是一种实现简单又效果明显的方法。把以上图像预加载的过程写到image.htm文件,当浏览器请求开始监控时就重定向到image.htm实现了动态图像监控。CGI程序调用监控程序,为确保只有一个监控程序运行,利用了文件锁技术,把监控程序和一个文件相关联,建立一个文件并把监控程序的进程号写入文件,在第一次调用监控程序时给文件加排它锁,若第二次启动读写该文件内容就会被拒绝,则不会再启动监控程序,做到了只有一个监控程序运行。
在浏览器中输入网址即可打开监控主页,可设置系统参数,如采集分辨率、亮度、帧率等,单击开始监控按钮进入监控页面。此时开发板上的LCD和网页都在动态的显示监控图像。如图3所示,LCD在显示动态图像,网页上也在显示动态图像。单击停止按钮,操作系统发送一个停止信号给采集进程,监控结束。
图3 动态图像监控效果
系统利用V4L2编程对USB摄像头采集图片,把采集到的图片用JPEG算法压缩,使图片压缩比可控,并用SDL库将采集到的图像显示在LCD液晶屏上,同时利用JavaScript和图片预加载技术实现了网页动态图像的流畅显示,可在浏览器端观看动态的、实时的图像。除了利用Ajax技术刷新网页中的局部图像,图片预加载技术也是刷新局部图像的好方法。这种方法是一种简单、经济、可行的方法,既可实现动态监控,又可大大减短开发周期。下一步可以把采集到的图像压缩成视频传输或存储,或对图像进行分析以实现智能的数字监控系统。
[1]黄光奇.CGI编程指南[M].北京:电子工业出版社,1999.
[2]方兴.基于S3C2410智能家居监控系统[J].信息技术科技资讯,2010(25):1.
[3]刘升,赵晶晶,范秀丽.基于V4L2的嵌入式视频监控系统[J].微计算机应用,2011(1):3-5.
[4]张军卫,韩佩富,矫春海.基于嵌入式Web服务器的视频监控系统设计[J].计算机安全,2011(2):3.
[5]王芳,王凯,王先超.基于ARM-Linux与DS18B20的温度监测系统[J].计算机工程与设计,2010(12):3-4.
[6]刘辉,刘毅敏.嵌入式Web服务器的研究与设计[J].数字通信,2011(4):3-4.
[7]胡书卫,陆培伦.一种基于Linux的嵌入式视频监控系统实现[J].安防科技,2009(1):2-3.
[8]齐跃峰,刘宇鹏.基于ARM的智能家居视频监控系统[J].电子技术,2010(4):2.
[9]张伟伟.基于ARM的嵌入式视频监控系统设计[D].南京:南京理工大学,2007.
[10]方卫民.基于ARM的嵌入式网络视频监控系统设计与实现[D].北京:北京邮电大学,2008.