将自定义滤镜应用于相机输出
如何将自定义滤镜应用到相机输出中的单个帧,并显示它们。
我到目前为止所尝试的是:
mCamera.setPreviewCallback(new CameraGreenFilter()); public class CameraGreenFilter implements PreviewCallback { @Override public void onPreviewFrame(byte[] data, Camera camera) { final int len = data.length; for(int i=0; i<len; ++i){ data[i] *= 2; } } }
-
虽然它的名字包含“绿色”,但我实际上只是想修改这些值(在这种情况下,颜色会变得更强)。 长话短说,这是行不通的。
-
我发现字节数组'data'是相机输出的副本; 但这并没有真正的帮助,因为我需要“真正的”缓冲区。
-
我听说你可以用openGL来实现这个。 这听起来很复杂。
有一个更简单的方法吗? 否则,这个openGL-surfaceView映射将如何工作?
好的,有几种方法可以做到这一点。 但是性能有一个很大的问题。 来自摄像机的字节[]是YUV格式,如果要显示它,必须将其转换为某种RGB格式。 这种转换是相当昂贵的操作,并显着降低输出fps。
这取决于你真正想用相机预览来做什么。 因为最好的解决scheme是在没有callback的情况下绘制相机预览,并对相机预览产生一些影响。 这是做有争议的重要性的常用方法。
但是,如果你真的需要手动显示输出,有几种方法可以做到这一点。 你的例子不起作用的原因有几个。 首先,你根本不显示图像。 如果你打电话给这个:
mCamera.setPreviewCallback(new CameraGreenFilter()); mCamera.setPreviewDisplay(null);
比你的相机根本不显示预览,你必须手动显示它。 而且你不能在onPreviewFrame方法中进行任何昂贵的操作,因为数据的生命周期是有限的,它会覆盖下一帧。 一个提示,使用setPreviewCallbackWithBuffer ,它的速度更快,因为它重用了一个缓冲区,并且不必在每个帧上分配新的内存。
所以你必须做这样的事情:
private byte[] cameraFrame; private byte[] buffer; @Override public void onPreviewFrame(byte[] data, Camera camera) { cameraFrame = data; addCallbackBuffer(data); //actually, addCallbackBuffer(buffer) has to be called once sowhere before you call mCamera.startPreview(); } private ByteOutputStream baos; private YuvImage yuvimage; private byte[] jdata; private Bitmap bmp; private Paint paint; @Override //from SurfaceView public void onDraw(Canvas canvas) { baos = new ByteOutputStream(); yuvimage=new YuvImage(cameraFrame, ImageFormat.NV21, prevX, prevY, null); yuvimage.compressToJpeg(new Rect(0, 0, width, height), 80, baos); //width and height of the screen jdata = baos.toByteArray(); bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length); canvas.drawBitmap(bmp , 0, 0, paint); invalidate(); //to call ondraw again }
为了使这个工作,你需要在类的构造函数或其他地方调用setWillNotDraw(false) 。
在onDraw中,例如,如果要修改颜色,则可以应用paint.setColorFilter(filter) 。 如果你愿意,我可以发表一些例子。
所以这将工作,但性能会低(小于8fps),导致BitmapFactory.decodeByteArray缓慢。 你可以尝试使用本地代码和android NDK将数据从YUV转换为RGB,但是这很复杂。
另一个select是使用openGL ES。 你需要GLSurfaceView,在那里你把相机帧作为一个纹理(在GLSurfaceView中实现Camera.previewCallback,所以你使用onPreviewFrame和在常规曲面中一样)。 但也有同样的问题,你需要转换YUV数据。 有一个机会 – 因为YUV中的字节数组的前半部分只是没有颜色的亮度数据,所以只能从预览(灰度图像)中只显示亮度数据。 所以在onPreviewFrame你使用arraycopy来复制数组的前半部分,而不是像这样绑定纹理:
gl.glGenTextures(1, cameraTexture, 0); int tex = cameraTexture[0]; gl.glBindTexture(GL10.GL_TEXTURE_2D, tex); gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_LUMINANCE, this.prevX, this.prevY, 0, GL10.GL_LUMINANCE, GL10.GL_UNSIGNED_BYTE, ByteBuffer.wrap(this.cameraFrame)); //cameraFrame is the first half od byte[] from onPreviewFrame gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
你不能以这种方式获得大约16-18 fps,你可以使用openGL来制作一些filter。 如果你愿意的话,我可以给你发更多的代码,但是在这里放置太长时间了。
对于一些更多的信息,你可以看到我的相似问题 ,但是没有一个好的解决scheme…