You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提取相机拍照后对应SquareImageView遮罩区域的Bitmap?

解决SquareImageView遮罩区域Bitmap提取问题

嘿,我来帮你搞定这个方形区域提取的问题!你前面几步都走通了,卡在裁剪对应区域真的太常见了——毕竟相机拍照生成的Bitmap,和屏幕上预览、遮罩的尺寸比例往往不一样,直接按遮罩的坐标裁剪肯定不对,得做一套坐标转换才行。

核心思路

问题的根源在于:

  • 相机默认以横屏输出图像,而我们的界面可能是竖屏的,导致Bitmap的宽高和屏幕显示的宽高是颠倒的
  • 遮罩View的坐标是相对于屏幕的,需要先转换成相对于预览View的坐标,再缩放匹配Bitmap的分辨率

修改后的代码实现

把你的jpegCallback改成下面这样,注释里已经写清了每一步的作用:

Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
    @SuppressWarnings("deprecation")
    public void onPictureTaken(byte[] data, Camera camera) {
        // 1. 将拍照字节数组转为原始Bitmap
        Bitmap originalBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        if (originalBitmap == null) {
            camera.startPreview();
            return;
        }

        // 2. 获取遮罩View和预览View的实例
        View maskView = findViewById(R.id.mask_square);
        View previewView = findViewById(R.id.your_preview_view_id); // 替换成你的预览View的ID

        // 3. 获取遮罩View在屏幕上的绝对位置
        int[] maskLocation = new int[2];
        maskView.getLocationOnScreen(maskLocation);
        int maskScreenLeft = maskLocation[0];
        int maskScreenTop = maskLocation[1];
        int maskWidth = maskView.getWidth();
        int maskHeight = maskView.getHeight();

        // 4. 获取预览View在屏幕上的绝对位置,计算遮罩相对于预览View的坐标
        int[] previewLocation = new int[2];
        previewView.getLocationOnScreen(previewLocation);
        int maskRelativeLeft = maskScreenLeft - previewLocation[0];
        int maskRelativeTop = maskScreenTop - previewLocation[1];

        // 5. 处理屏幕旋转,修正Bitmap的宽高逻辑(相机默认横屏输出)
        int screenRotation = getWindowManager().getDefaultDisplay().getRotation();
        boolean isPortrait = screenRotation == Surface.ROTATION_0 || screenRotation == Surface.ROTATION_180;
        
        float bitmapRealWidth = isPortrait ? originalBitmap.getHeight() : originalBitmap.getWidth();
        float bitmapRealHeight = isPortrait ? originalBitmap.getWidth() : originalBitmap.getHeight();

        // 6. 计算预览View和Bitmap的缩放比例
        float scaleX = bitmapRealWidth / previewView.getWidth();
        float scaleY = bitmapRealHeight / previewView.getHeight();

        // 7. 将遮罩的相对坐标转换为Bitmap上的坐标
        int cropLeft = Math.round(maskRelativeLeft * scaleX);
        int cropTop = Math.round(maskRelativeTop * scaleY);
        int cropWidth = Math.round(maskWidth * scaleX);
        int cropHeight = Math.round(maskHeight * scaleY);

        // 8. 检查裁剪区域是否越界,避免抛出异常
        cropLeft = Math.max(0, cropLeft);
        cropTop = Math.max(0, cropTop);
        cropWidth = Math.min(cropWidth, (int) bitmapRealWidth - cropLeft);
        cropHeight = Math.min(cropHeight, (int) bitmapRealHeight - cropTop);

        // 9. 裁剪Bitmap
        Bitmap croppedBitmap = Bitmap.createBitmap(originalBitmap, cropLeft, cropTop, cropWidth, cropHeight);

        // 10. 如果是竖屏,将裁剪后的Bitmap旋转回正确方向
        if (isPortrait) {
            Matrix matrix = new Matrix();
            matrix.postRotate(90);
            Bitmap rotatedBitmap = Bitmap.createBitmap(croppedBitmap, 0, 0, croppedBitmap.getWidth(), croppedBitmap.getHeight(), matrix, true);
            croppedBitmap.recycle(); // 回收中间Bitmap
            croppedBitmap = rotatedBitmap;
        }

        // 现在croppedBitmap就是遮罩对应的图像区域了,你可以用来显示或保存
        // 示例:设置到SquareImageView
        SquareImageView squareImageView = findViewById(R.id.your_square_image_view_id);
        squareImageView.setImageBitmap(croppedBitmap);

        // 11. 回收不用的Bitmap,避免内存泄漏
        originalBitmap.recycle();
        // 记得在合适的时机回收croppedBitmap,比如页面销毁时

        // 重启相机预览
        camera.startPreview();
    }
};

关键注意事项

  • 替换代码中的your_preview_view_id和your_square_image_view_id为你实际的View ID
  • 如果你的预览是用TextureView实现的,处理逻辑完全一样,只要获取它的宽高和位置即可
  • 务必检查裁剪区域的边界,否则Bitmap.createBitmap会抛出IllegalArgumentException
  • 记得及时回收Bitmap,避免内存溢出

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:44:45