如何使用 SurfaceView android 处理来自相机的帧
How can I process the frames from camera using SurfaceView android
我在项目中工作,我从 Android 相机捕获帧 fps。在将每个帧转换为位图以使用带有 opencv SDK 的本机库之后,以及在 ImageView 中显示此位图之后。此过程会消耗堆内存并影响应用程序的性能。我在manifest.xml中添加下一个标签来增加堆内存:
<application
android:name="app"
android:largeHeap="true" />
这样,应用程序在调试模式下生成 apk 版本时可以正常工作,但是在发布模式下生成 apk 版本时,应用程序非常慢并且不能正常工作,存在问题性能。
我使用 SurfaceView 组件从相机获取帧的逻辑工作,我使用 SurfaceView 因为我需要在应用程序内部的后台使用,我想问你,我如何解决这个问题,计数在你的帮助下。
我正在使用此逻辑来获取帧:
public synchronized void onPreviewFrame(final byte[] data, final Camera camera) {
executorService.execute(new Runnable() {
public void run() {
Camera.Parameters parameters = camera.getParameters();
orientation = getOrientationDevice().getOrientation();
orientation = normalize(orientation);
//converter yuv to bitmap
int width = parameters.getPreviewSize().width;
int height = parameters.getPreviewSize().height;
YuvImage yuv = new YuvImage(data,PreviewFormat,width, height, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, width, height), 80, out);
byte[] bytes = out.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// rotate image
if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT){
matrix.setScale(-1, 1);
matrix.postTranslate(bitmap.getWidth(), 0);
matrix.postRotate(rotation + 90);
} else {
matrix.postRotate(rotation + 90);
}
// process bitmap from method jni
....
// to pass bitmap a ImageView to show
mCameraFrame.onFrame(bitmap.copy(bitmap.getConfig(), false));
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
camera.addCallbackBuffer(data);
return;
});
}
};
在 UI 回调中接收位图以在 Imageview 中显示
@Override
public void onFrame(final Bitmap mBitmap) {
this.bitmapWeakReference = new WeakReference<Bitmap>(mBitmap);
if (imageViewReference != null && bitmapWeakReference.get() != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmapWeakReference.get());
}
});
}
}
在高帧率下使用相机 API 需要将相机回调推离主 UI 线程,为此您需要一个 separate Handler thread。摆脱 onPreviewFrame() 回调中所有不必要的处理。您可以预先计算相机参数。避免 new,即使对于 Rect:垃圾收集可能会降低性能。
您可以通过 JNI 将 YUV 帧提供给基于 OpenCV 的本机 JNI,并且所有转换和转换的执行速度可能会快得多。
如果您不对每个位图都使用 OpenCV,则可以将 YUV 转换为 RGB 而无需通过 JPEG。通过这样的转换,可以一步完成旋转90°。
如果你运行在多核设备上,你可以用newFixedThreadPool()启动executorService,但是释放相机缓冲区(调用camera.addCallbackBuffer()
)可能需要精细的管理,特别是如果你希望帧速率是统一的。
如果只是想显示两份预览流,可以使用OpenGL,CPU根本不会涉及到旋转和YUV到RGB的转换。您甚至可以对通过 OpenGL 显示的纹理应用一些高级图像处理。
我在项目中工作,我从 Android 相机捕获帧 fps。在将每个帧转换为位图以使用带有 opencv SDK 的本机库之后,以及在 ImageView 中显示此位图之后。此过程会消耗堆内存并影响应用程序的性能。我在manifest.xml中添加下一个标签来增加堆内存:
<application
android:name="app"
android:largeHeap="true" />
这样,应用程序在调试模式下生成 apk 版本时可以正常工作,但是在发布模式下生成 apk 版本时,应用程序非常慢并且不能正常工作,存在问题性能。
我使用 SurfaceView 组件从相机获取帧的逻辑工作,我使用 SurfaceView 因为我需要在应用程序内部的后台使用,我想问你,我如何解决这个问题,计数在你的帮助下。
我正在使用此逻辑来获取帧:
public synchronized void onPreviewFrame(final byte[] data, final Camera camera) {
executorService.execute(new Runnable() {
public void run() {
Camera.Parameters parameters = camera.getParameters();
orientation = getOrientationDevice().getOrientation();
orientation = normalize(orientation);
//converter yuv to bitmap
int width = parameters.getPreviewSize().width;
int height = parameters.getPreviewSize().height;
YuvImage yuv = new YuvImage(data,PreviewFormat,width, height, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, width, height), 80, out);
byte[] bytes = out.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// rotate image
if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT){
matrix.setScale(-1, 1);
matrix.postTranslate(bitmap.getWidth(), 0);
matrix.postRotate(rotation + 90);
} else {
matrix.postRotate(rotation + 90);
}
// process bitmap from method jni
....
// to pass bitmap a ImageView to show
mCameraFrame.onFrame(bitmap.copy(bitmap.getConfig(), false));
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
camera.addCallbackBuffer(data);
return;
});
}
};
在 UI 回调中接收位图以在 Imageview 中显示
@Override
public void onFrame(final Bitmap mBitmap) {
this.bitmapWeakReference = new WeakReference<Bitmap>(mBitmap);
if (imageViewReference != null && bitmapWeakReference.get() != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmapWeakReference.get());
}
});
}
}
在高帧率下使用相机 API 需要将相机回调推离主 UI 线程,为此您需要一个 separate Handler thread。摆脱 onPreviewFrame() 回调中所有不必要的处理。您可以预先计算相机参数。避免 new,即使对于 Rect:垃圾收集可能会降低性能。
您可以通过 JNI 将 YUV 帧提供给基于 OpenCV 的本机 JNI,并且所有转换和转换的执行速度可能会快得多。
如果您不对每个位图都使用 OpenCV,则可以将 YUV 转换为 RGB 而无需通过 JPEG。通过这样的转换,可以一步完成旋转90°。
如果你运行在多核设备上,你可以用newFixedThreadPool()启动executorService,但是释放相机缓冲区(调用camera.addCallbackBuffer()
)可能需要精细的管理,特别是如果你希望帧速率是统一的。
如果只是想显示两份预览流,可以使用OpenGL,CPU根本不会涉及到旋转和YUV到RGB的转换。您甚至可以对通过 OpenGL 显示的纹理应用一些高级图像处理。