完整代码已上传github,https://github.com/54wall/LibgdxAndroidCamera
开发背景
简单介绍下自己,目前在人脸识别从事android开发与测试,公司的产品是人脸识别SDK,成品就是各种终端上的人脸识别设备,总监大人希望最好能有一个跨平台的开发框架,能够以一种语言为主,输出各种app。于是他找到了libGDX,一个多平台开发游戏的框架, 语言为java。libGDX是一个游戏开发工具,确切的说也就是一个java框架,按他的套路进行编写即可。
开始稍微看看游戏,有些误入歧途,然后他说要看看怎么掉摄像头。然后我又误入歧途的寻找寻找了一些有意思的东西:一个是抓幽灵的游戏ChaseWhisplyProject,来自 https://github.com/tvbarthel/ChaseWhisplyProject
另外一个beyondar,来自 https://github.com/BeyondAR/beyondar
当然,上边两个多事在Android内实现的,而非在libgdx中实现,感谢Github,在libGDX的介绍中,有达人分享了他使用libGDX调用android摄像头的实例,他在libGDX中调用摄像头的目的简单来说就是AR,使用者可以通过APP看到设备背后的路面,或者和真实世界有交互,就像PokemonGO一样,当然全部的这些也需要算法支持。但是时间是2013年,我拷贝下来,进行了微调,发现是可以实现的,鉴于目前网络上还真没有许多的实例说明如何在LibGDX中调用android摄像头,我搜索libgdx camera 大部分给我的结果都是libgdx中的镜头,就是跟随演员的镜头,而不是设备摄像头,这里也对这个实例进行一个记录和补充,以及简单的实现。
wiki地址:https://github.com/libgdx/libgdx/wiki/Integrating-libgdx-and-the-device-camera
下面我也是基于这篇文章进行翻译,顺序当然有所改变,还有原代码中有包含一个类似初始页面的SplashActivity的我没有实现,目前还没有去了解libGDX的三维编程,代码的源码已上传至GitHub,再好的文字也比不上源码。
完整代码已上传github,https://github.com/54wall/LibgdxAndroidCamera
libGDX中到底是如何实现调用摄像头
很简单,就是在一个透明的画布上绘制libGDX的舞台演员,在画布的后边放置摄像头的预览画面。而文章主要解决的问题就是如何显示摄像头预览画面,和如何调用摄像头。
如何显示摄像头预览画面
我们知道,libGDX的代码主要全部在core项目中,其他各个平台的代码都是经过一个简单的代码启动启动然后,剩下的就去调用core项目中的代码,首先在libGDX初始化时,要进行一定的设置,才能让摄像头的预览画面显示在libGDX框架生成的app中,进入android项目组,修改AndroidLauncher.class代码如下
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
cfg.r = 8;
cfg.g = 8;
cfg.b = 8;
cfg.a = 8;
DeviceCameraControl cameraControl = new AndroidDeviceCameraController(this);
initialize(new MyGdxGame0606(cameraControl), cfg);
你可以点击进入AndroidApplicationConfiguration去看看r,g,b,a原来数值如下
/** number of bits per color channel **/
public int r = 5, g = 6, b = 5, a = 0;
rgba的具体值指的是每种颜色的比特位数,这里改为8位,大概的意思就是每种颜色深度为8位,也就是2的8次幂,也就是256,和photoshop中意义一样。这么设置的原因是为了libGDX中的画布的后边能够正常的显示android的摄像头,不然颜色和在android上调用摄像头有出入,色彩不会同样的丰富。
同样在AndroidLauncher.class中,将OpenGL surface 模式设置成TRANSLUCENT
if (graphics.getView() instanceof SurfaceView) {
SurfaceView glView = (SurfaceView) graphics.getView();
// force alpha channel - I'm not sure we need this as the GL surface
// is already using alpha channel
glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
然后是,在AndroidLauncher.class新建一个post方法,用来帮助唤起一些异步线程
public void post(Runnable r) {
handler.post(r);
}
以上就是AndroidLauncher.class所要完成的全部内容。
如何让屏幕透明
很简单,在core项目中MyGdxGame0606.class主类中的render()渲染方法中,要注意使用glClearColor()清屏时,参数的选择必须全部是0,这样相机预览的画面才会显示在render画面的后边
Gdx.gl20.glClearColor(0f, 0.0f, 0.0f, 0.0f);
清屏准备完毕,现在就是要调用android摄像头的时候到了
首先了解一下,把大象装冰箱中总共分几步?对于调用android摄像头的步骤则可以分如下几部?Android相机有特别的工作顺序必须遵守,通过application的callbacks回调函数能管理相机的状态,机器的工作状态将由AndroidDeviceCameraController.class进行管理(desktop没有实现,Android已实现)。
相机的工作状态一次是 Ready -> Preview -> autoFocusing -> ShutterCalled -> Raw PictureData -> Postview PictureData -> Jpeg PictureData -> Ready (可以做一个表格)
本实例代码实现的顺序也可以概括为 Ready -> Preview -> autoFocusing -> Jpeg PictureData -> Ready
新建AndroidDeviceCameraController
在core项目中,新建一个DeviceCameraController类,而为了配合core中DeviceCameraControl,在android项目中,新建一个AndroidDeviceCameraController 类,来控制设备的摄像头,它要继承DeviceCameraControl,同时还要实现Camera.PictureCallback:(android.hardware.Camera.PictureCallback)Camera.AutoFocusCallback(android.hardware.Camera.AutoFocusCallback),共计三个接口,来实现android摄像头从准备到拍摄的过程。
public class AndroidDeviceCameraController implements DeviceCameraControl, Camera.PictureCallback, Camera.AutoFocusCallback {
}
AndroidDeviceCameraController 新建后,逐步实现摄像头该有的各个功能。
1.准备显示预览信息的CameraSurface
我们产生一个CameraSurface类来负责管理摄像头和它收集的图像,这里我和android摄像相关的代码一致
public class CameraSurface extends SurfaceView implements SurfaceHolder.Callback {
private Camera camera;
public CameraSurface( Context context ) {
super( context );
// We're implementing the Callback interface and want to get notified
// about certain surface events.
getHolder().addCallback( this );
// We're changing the surface to a PUSH surface, meaning we're receiving
// all buffer data from another component - the camera, in this case.
getHolder().setType( SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS );
}
public void surfaceCreated( SurfaceHolder holder ) {
// Once the surface is created, simply open a handle to the camera hardware.
camera = Camera.open();
}
public void surfaceChanged( SurfaceHolder holder, int format, int width, int height ) {
// This method is called when the surface changes, e.g. when it's size is set.
// We use the opportunity to initialize the camera preview display dimensions.
Camera.Parameters p = camera.getParameters();
p.setPreviewSize( width, height );
camera.setParameters( p );
// We also assign the preview display to this surface...
try {
camera.setPreviewDisplay( holder );
} catch( IOException e ) {
e.printStackTrace();
}
}
public void surfaceDestroyed( SurfaceHolder holder ) {
// Once the surface gets destroyed, we stop the preview mode and release
// the whole camera since we no longer need it.
camera.stopPreview();
camera.release();
camera = null;
}
public Camera getCamera() {
return camera;
}
}
2.在android项目中增加相机预览视图
在android项目的AndroidDeviceController类,使用activity.addContentView,直接将cameraSurface显示在android设备的屏幕上
@Override
public void prepareCamera() {
if (cameraSurface == null) {
cameraSurface = new CameraSurface(activity);
}
activity.addContentView( cameraSurface, new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ) );
}
@Override
public synchronized void prepareCamera() {
if (cameraSurface == null) {
cameraSurface = new CameraSurface(activity);
}
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams (680,680);
params.rightMargin=150;//可以通过设置rightMargin控制组件的实际位置
params.leftMargin=200;//可以通过设置rightMargin控制组件的实际位置
params.topMargin=100;
activity.addContentView(cameraSurface, params);
}
prepareCamera方法应该在libgdx渲染过程中异步调用
@Override
public void prepareCameraAsync() {
Runnable r = new Runnable() {
public void run() {
prepareCamera();
}
};
activity.post(r);
}