OpenCV-Android:JavaCameraView存图画质低、方向异常及适配问题
解决OpenCV Android相机开发的三个问题:低画质、预览异常、分辨率设置
我来帮你逐个搞定这些OpenCV相机开发里的常见问题,直接结合你的代码给出修改方案:
1. 保存的图像画质极低
原因:你当前调用takePicture时用的是相机默认的拍照尺寸(通常很小),而且没有设置JPEG压缩质量,导致保存的图片分辨率低、画质差。
修改方案:在CamView的takePicture方法中,先设置相机的拍照尺寸为支持的最大尺寸,并拉高JPEG质量:
public void takePicture(final String fileName) { Log.i(TAG, "Taking picture"); this.mPictureFileName = fileName; mCamera.setPreviewCallback(null); // 添加:设置拍照参数 Camera.Parameters params = mCamera.getParameters(); // 获取支持的所有拍照尺寸,按面积从大到小排序 List<Camera.Size> pictureSizes = params.getSupportedPictureSizes(); Collections.sort(pictureSizes, new Comparator<Camera.Size>() { @Override public int compare(Camera.Size s1, Camera.Size s2) { return Integer.compare(s2.width * s2.height, s1.width * s1.height); } }); // 设置最大的拍照尺寸 if (!pictureSizes.isEmpty()) { Camera.Size largestPicSize = pictureSizes.get(0); params.setPictureSize(largestPicSize.width, largestPicSize.height); } // 设置JPEG最高质量(100为最大值) params.setJpegQuality(100); mCamera.setParameters(params); // 执行拍照 mCamera.takePicture(null, null, this); }
2. 预览仅显示中间+画面旋转270度
这是两个关联问题,都是相机预览方向和布局适配的问题:
解决画面旋转问题
原因:Android相机的原生预览方向和屏幕方向不匹配,需要手动计算并设置预览方向。
修改方案:在CamView中重写connectCamera方法,添加预览方向设置:
private int mCameraId = 0; // 相机ID,0为后置,1为前置 @Override protected boolean connectCamera(int width, int height) { boolean result = super.connectCamera(width, height); if (result) { // 获取当前相机ID(通过反射获取JavaCameraView的mCameraId字段) try { Field field = JavaCameraView.class.getDeclaredField("mCameraId"); field.setAccessible(true); mCameraId = field.getInt(this); } catch (Exception e) { e.printStackTrace(); mCameraId = 0; } // 计算并设置预览方向 Activity activity = (Activity) getContext(); int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(mCameraId, info); int displayAngle; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { displayAngle = (info.orientation + degrees) % 360; displayAngle = (360 - displayAngle) % 360; // 前置相机需要镜像补偿 } else { displayAngle = (info.orientation - degrees + 360) % 360; } mCamera.setDisplayOrientation(displayAngle); } return result; }
解决预览仅显示中间的问题
原因:预览尺寸和控件尺寸比例不匹配,导致画面被裁剪。
修改方案:
- 确保
activity_main.xml中CamView的布局属性正确:
<com.nisha.scanner.CamView android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="visible" <!-- 直接设为visible,不用动态修改 --> android:id="@+id/main_activity_java_surface_view" />
- 在
MainActivity中设置默认预览分辨率为支持的最大尺寸(匹配控件比例):
在mLoaderCallback的SUCCESS分支添加:
Log.i(TAG, "OpenCV loaded successfully"); mOpenCvCameraView.enableView(); // 添加:设置最大预览分辨率 mResolutionList = mOpenCvCameraView.getResolutionList(); Collections.sort(mResolutionList, new Comparator<Size>() { @Override public int compare(Size s1, Size s2) { return Integer.compare(s2.width * s2.height, s1.width * s1.height); } }); if (!mResolutionList.isEmpty()) { mOpenCvCameraView.setResolution(mResolutionList.get(0)); }
3. 保存的图片仍旋转?处理拍照后的图像方向
刚才解决了预览旋转,但拍照后的图片可能还是会有方向问题,需要在保存前旋转图像:
修改CamView的onPictureTaken方法:
@Override public void onPictureTaken(byte[] data, Camera camera) { Log.i(TAG, "Saving a bitmap to file"); // 重启预览 mCamera.startPreview(); mCamera.setPreviewCallback(this); // 1. 把字节数组转成Bitmap Bitmap originalBitmap = BitmapFactory.decodeByteArray(data, 0, data.length); // 2. 计算需要旋转的角度(和预览方向计算逻辑一致) Activity activity = (Activity) getContext(); int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(mCameraId, info); int rotateAngle; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { rotateAngle = (info.orientation + degrees) % 360; rotateAngle = (360 - rotateAngle) % 360; } else { rotateAngle = (info.orientation - degrees + 360) % 360; } // 3. 旋转Bitmap Matrix matrix = new Matrix(); matrix.postRotate(rotateAngle); Bitmap rotatedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(), originalBitmap.getHeight(), matrix, true); // 4. 保存旋转后的图片 try { FileOutputStream fos = new FileOutputStream(mPictureFileName); rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.close(); // 回收Bitmap避免内存泄漏 originalBitmap.recycle(); rotatedBitmap.recycle(); } catch (java.io.IOException e) { Log.e("PictureDemo", "Exception in photoCallback", e); } }
最后提醒一下:Android 6.0以上需要动态申请相机和存储权限,你当前的Manifest里只加了静态权限,记得在MainActivity的onCreate里添加动态权限申请逻辑,否则可能会出现拍照或保存失败的情况。
内容的提问来源于stack exchange,提问作者Nisha




