我使用Android的Camera2 API,想在相机预览帧上执行一些图像处理,然后在预览中显示更改(TextureView)。
从常见的camera2video示例开始,我在openCamera()中设置了ImageReader。
mImageReader = ImageReader.newInstance(mVideoSize.getWidth(),
mVideoSize.getHeight(), ImageFormat.YUV_420_888, mMaxBufferedImages);
mImageReader.setOnImageAvailableListener(mImageAvailable, mBackgroundHandler);
在我的startPreview()中,我已将Surfaces设置为从CaptureRequest接收帧。
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
List<Surface> surfaces = new ArrayList<>();
// Here is where we connect the mPreviewSurface to the mTextureView.
mPreviewSurface = new Surface(texture);
surfaces.add(mPreviewSurface);
mPreviewBuilder.addTarget(mPreviewSurface);
// Connect our Image Reader to the Camera to get the preview frames.
Surface readerSurface = mImageReader.getSurface();
surfaces.add(readerSurface);
mPreviewBuilder.addTarget(readerSurface);
然后我将修改OnImageAvailableListener()回调中的图像数据。
ImageReader.OnImageAvailableListener mImageAvailable = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
try {
Image image = reader.acquireLatestImage();
if (image == null)
return;
final Image.Plane[] planes = image.getPlanes();
// Do something to the pixels.
// Black out part of the image.
ByteBuffer y_data_buffer = planes[0].getBuffer();
byte[] y_data = new byte[y_data_buffer.remaining()];
y_data_buffer.get(y_data);
byte y_value;
for (int row = 0; row < image.getHeight() / 2; row++) {
for (int col = 0; col < image.getWidth() / 2; col++) {
y_value = y_data[row * image.getWidth() + col];
y_value = 0;
y_data[row * image.getWidth() + col] = y_value;
}
}
image.close();
} catch (IllegalStateException e) {
Log.d(TAG, "mImageAvailable() Too many images acquired");
}
}
};
据我了解,我现在将图像发送到2个Surface实例,一个用于mTextureView,另一个用于我的ImageReader。
如何让我的mTextureView使用与ImageReader相同的Surface,或者我应该直接从mTextureView的Surface操作图像数据?
由于
答案 0 :(得分:1)
如果您只想显示修改后的输出,那么我不确定为什么要配置两个输出(TextureView和ImageReader)。
一般来说,如果你想要像
这样的东西camera -> in-app edits -> display
您有多种选择,具体取决于您想要的编辑类型,以及编码的简易性,性能等之间的各种权衡。
最有效的选项之一是将您的编辑作为OpenGL着色器。 在这种情况下,GLSurfaceView可能是最简单的选择。 创建一个SurfaceTexture对象,其纹理ID在GLSurfaceView的EGL上下文中未使用,并将SurfaceTexture创建的Surface传递给相机会话和请求。 然后在SurfaceView绘图方法中,调用SurfaceTexture的updateTexImage()方法,然后使用纹理ID根据需要渲染输出。
这确实需要很多OpenGL代码,所以如果你不熟悉它,那就很有挑战性。
您也可以使用RenderScript获得类似的效果;在那里你将有一个输出SurfaceView或TextureView,然后是一个RenderScript脚本,它从Camera的输入Allocation中读取并写入到View的输出Allocation;您可以从Surface创建此类分配。 Google HdrViewfinderDemo camera2示例应用使用此方法。它的样板很少。
第三,您可以像现在一样使用ImageReader,但是您必须自己进行大量转换才能将其写入屏幕。最简单(但最慢)的选项是从SurfaceView或ImageView获取Canvas,然后逐个写入像素。或者你可以通过ANativeWindow NDK来做到这一点,这更快但是需要编写JNI代码并且仍然需要你自己进行YUV-> RGB转换(或者使用未记录的API将YUV推入ANativeWindow并希望它能够工作)。