You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

OpenCV-Android:JavaCameraView存图画质低、方向异常及适配问题

解决OpenCV Android相机开发的三个问题:低画质、预览异常、分辨率设置

我来帮你逐个搞定这些OpenCV相机开发里的常见问题,直接结合你的代码给出修改方案:


1. 保存的图像画质极低

原因:你当前调用takePicture时用的是相机默认的拍照尺寸(通常很小),而且没有设置JPEG压缩质量,导致保存的图片分辨率低、画质差。

修改方案:在CamViewtakePicture方法中,先设置相机的拍照尺寸为支持的最大尺寸,并拉高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;
}

解决预览仅显示中间的问题

原因:预览尺寸和控件尺寸比例不匹配,导致画面被裁剪。

修改方案

  1. 确保activity_main.xmlCamView的布局属性正确:
<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" />
  1. MainActivity中设置默认预览分辨率为支持的最大尺寸(匹配控件比例):
    mLoaderCallbackSUCCESS分支添加:
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. 保存的图片仍旋转?处理拍照后的图像方向

刚才解决了预览旋转,但拍照后的图片可能还是会有方向问题,需要在保存前旋转图像:

修改CamViewonPictureTaken方法:

@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里只加了静态权限,记得在MainActivityonCreate里添加动态权限申请逻辑,否则可能会出现拍照或保存失败的情况。

内容的提问来源于stack exchange,提问作者Nisha

火山引擎 最新活动