最近开始做一个机器人操作界面RescueOperator。主要功能有:
- 显示安装在机器人上的摄像头采集到得视频
- 显示机器人传感器状态
- 控制机器人运动
- 控制机器人手臂
昨天刚刚实现了摄像头视频显示功能。为了调试方便,先做了一个测试用的AgentTest。AgentTest采集USB摄像头视频,并通过网络传输给RescueOperator。
我用JMF[1] 来采集视频。JMF支持添加视频,声音等媒体信息,并且支持捕捉,回放,译码。我只是用它来采集显示视频。相关的类有:
- javax.media.CaptureDeviceManager
- javax.media.CaptureDeviceInfo
-
javax.media.MediaLocator
- javax.media.Player
- javax.media.Manager
CaptureDeviceManager负责管理系统中所哟可用的采集设备。在使用前需要通过JMFRegister注册USB摄像头,也可用通过CaptureDeviceManager来注册采集设备。
CaptureDeviceInfo用于保存采集设备的信息,包括设备输出格式,设备名,MediaLocater。
MediaLocator负责描述媒体内容的位置。它与URL相似。采用一种vfw协议。USB摄像头的MediaLocator.toString()为“vfw://0”。
Player是一个接口,继承MediaHandler和Controller。它负责显示,控制媒体数据。
Manager可以让程序访问系统非独立资源,比如Player,DataSources,Processor等。这里,我用它根据MediaLocator创建Player实例。实例化Player后,需要调用其Start()方法。为了显示视频,我们需要调用Player的getVisualComponent()方法来获得一个gui组建。将该组件添加到窗口中,就可以显示摄像头四品了。
对于AgentTest,最关键的是将视频截为一帧帧图片,再发送给操作站。截取视频也可以使用JMF来实现。首先,获得FrameGrabbingControl实例:
然后直接获得当前帧的buffer
最后将buffer转为byte[],这个过程比较复杂(应该有更简单的方法吧)
发送信息中还要添加图片的字节长度,方便操作站接受。这样就大功告成了,直接发送...
接下来就是完成操作站的接受和显示工作。
接受时先读取图片字节长度,然后读取图片数据。然后用java.awt.Toolkit将图片数据生成图片。
然后就只剩把一帧一帧图片画到Canvas上了。调用drawImage()方法时,需要传一个ImageObserver[2] 。调用drawImage()时,image可能还没有完全加载,甚至还没有开始加载。如果drawImage()方法等待图片加载,可能会降低程序响应速度。所以java会在开启一加载图片的线程来监视图片加载。ImageObserver就是用来监视图片加载状态的。java提供三种监视图片加载方法:
- ImageObserver 重写imageUpdate ()方法,手动监视加载图片
- MediaTracker 加载多个图片
- ImageIcon 加载单个图片
显然,我需要MediaTracker。想MediaTracker中添加图片时需要给一个ID。ID决定加载图片的优先级,ID越小,优先级越高。
最后比较一下ImageObserver和MediaTracker的性能。加载连续图片时,MediaTracker用时<20ms,而ImageObserver需要100ms左右,相差一个数量级。
需要改进:
- CPU使用率高
- 向MediaTracker添加图片时,用了ArrayList和HashMap,虽然不会太大,但可能没有必要
- AgentTest中,用Buffer生成图片的方法太繁琐,效率低
进一步工作:
- 完成视频接收显示测试,主要测试延时;
- 确定电机控制协议;
- 实现按键响应->控制信息发送;
- 设计GUI界面;
- 接收图片与接收其他传感器信息分别用两个Socket。因为图片数据是基于字节的,而其他传感器信息是基于数字或字符串的;
- 将图片解析从AgentSenseThread中移出,并添加其他传感器信息的解析模块;
引用:
[1] http://java.sun.com/javase/technologies/desktop/media/jmf/index.jsp
[2] http://www.particle.kth.se/~lindsey/JavaCourse/Book/Part1/Java/Chapter11/loadingImages.html