使用OpenCV原生的JavaCamera2View
打开相机,发现相机帧率非常的低,只有3-4帧,而且色彩也不对。
1.帧率低的解决方案:
这个问题的原因在于OpenCV在设置预览长宽的时候,首先是获取相机支持的最大尺寸,以这个尺寸作为基准和connect_camera的API传进来的View的长宽做条件匹配,如果View的长宽不满足OpenCV的要求,那么会直接使用相机的最大输出尺寸进行预览,而这个就是预览帧率低的根本原因。
一般市面上的所有相机都会支持到640*480的尺寸,因此我们就用这个尺寸,改下源码即可
org\opencv\android\CameraBridgeViewBase.java
private void onEnterStartedState() {
Log.d(TAG, "call onEnterStartedState");
/* Connect camera */
if (!connectCamera(640, 480)) {
org\opencv\android\JavaCamera2View.java
boolean calcPreviewSize(final int width, final int height) {
Log.i(LOGTAG, "calcPreviewSize: " + width + "x" + height);
if (mCameraID == null) {
Log.e(LOGTAG, "Camera isn't initialized!");
return false;
}
CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
try {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraID);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
int bestWidth = 0, bestHeight = 0;
float aspect = (float) width / height;
android.util.Size[] sizes = map.getOutputSizes(ImageReader.class);
bestWidth = width;
bestHeight = height;
// for (android.util.Size sz : sizes) {
// int w = sz.getWidth(), h = sz.getHeight();
// Log.d(LOGTAG, "trying size: " + w + "x" + h);
// if (width >= w && height >= h && bestWidth <= w && bestHeight <= h
// && Math.abs(aspect - (float) w / h) < 0.2) {
// bestWidth = w;
// bestHeight = h;
// }
// }
Log.i(LOGTAG, "best size: " + bestWidth + "x" + bestHeight);
assert(!(bestWidth == 0 || bestHeight == 0));
if (mPreviewSize.getWidth() == bestWidth && mPreviewSize.getHeight() == bestHeight)
return false;
else {
mPreviewSize = new android.util.Size(bestWidth, bestHeight);
return true;
}
} catch (CameraAccessException e) {
Log.e(LOGTAG, "calcPreviewSize - Camera Access Exception", e);
} catch (IllegalArgumentException e) {
Log.e(LOGTAG, "calcPreviewSize - Illegal Argument Exception", e);
} catch (SecurityException e) {
Log.e(LOGTAG, "calcPreviewSize - Security Exception", e);
}
return false;
}
重新build下apk,你会发现帧率能够达到25fps左右了。其实还有改善空间的,OpenCV的相机是使用的SurfaceView进行的渲染,我们知道SurfaceView是通过CPU去渲染的,同时OpenCV对CPU的算力占用也是比较大的,因此可以尝试将预览图像的渲染交给GPU去完成,这样能够提升预览的帧率。
BTW,之前写过一个GPU渲染的demo,帧率可以达到基本30fps,Anyway,对于Camera的图像处理的操作,我始终认为最好的方案还是在HAL去完成,因为预览数据从HAL通过CallBack的机制传递到APP非常耗时,以前也看过Camera在Android的数据流,这个过程中预览数据至少要发生2次拷贝。
2.图像色彩不对的解决方案
这问题也是比较坑爹的,就直接上代码吧。
org\opencv\android\JavaCamera2View.java
@Override
public Mat rgba() {
if (mPreviewFormat == ImageFormat.NV21)
Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4);
else if (mPreviewFormat == ImageFormat.YV12)
Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGB_I420, 4); // COLOR_YUV2RGBA_YV12 produces inverted colors
else if (mPreviewFormat == ImageFormat.YUV_420_888) {
assert (mUVFrameData != null);
//Imgproc.cvtColorTwoPlane(mYuvFrameData, mUVFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21);
Imgproc.cvtColorTwoPlane(mYuvFrameData, mUVFrameData, mRgba, Imgproc.COLOR_YUV2BGRA_NV21);
} else
throw new IllegalArgumentException("Preview Format can be NV21 or YV12");
return mRgba;
}
也就是Mat和Bitmap对于图像处理的差异性导致的