您如何在 Android 上处理超高 MP 相机(和图像)? - “画布:尝试绘制太大的位图”

2024-05-22

我有一个活动,用户可以像这样打开相机

getPictureUri(createImageFromFile = true)?.let {
        photoUri = it
        openCameraActivity(REQUEST_IMAGE_CAPTURE, it)
    } ?: photoViewModel.onRequestUriError()

openCamera 是一个 Activity 扩展,如下所示

fun Activity.openCameraActivity(requestCode: Int, pictureUri: Uri) {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
    takePictureIntent.resolveActivity(packageManager)?.also {
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri)
        takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
        startActivityForResult(takePictureIntent, requestCode)
    }
}

}

当我们取回图像时,我们将其写入文件,并将 photoUri 存储在应用程序的某个位置。效果很好!问题是,一些用户拥有超高百万像素相机,例如 64mp,当他们拍照时,它会尝试在 UI 中显示,但会崩溃。

FATAL EXCEPTION: main
Process: co.app.staging, PID: 27859
java.lang.RuntimeException: Canvas: trying to draw too large(256576512bytes) bitmap.

有什么方法可以限制相机分辨率吗?我可以降低质量,但这充其量只是一个创可贴。对此有哪些不错的解决方案?


我尝试用这种方法在画布上绘制位图URI, 尝试这个:

(下面代码给出解释)

private void drawBitmapUriOnCanvas(Uri selectedImage) {
    if (selectedImage != null) {
        BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
        bmpFactoryOptions.inJustDecodeBounds = true;

        try {
            BitmapFactory.decodeStream(getContentResolver().openInputStream(
                    selectedImage), null, bmpFactoryOptions);

            bmpFactoryOptions.inSampleSize = 2;
            bmpFactoryOptions.inJustDecodeBounds = false;
            Bitmap bmp = BitmapFactory
                    .decodeStream(getContentResolver().openInputStream(
                            selectedImage), null, bmpFactoryOptions);

            Bitmap alteredBitmap;
            if (bmp != null) {
                if (getDeviceRam(this) <= 1024) {
                    bmp = getResizedBitmap(bmp, displayWidth);
                }
                bmp = checkAndRotateBitmap(bmp);
                alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp
                        .getHeight(), bmp.getConfig());

                Canvas canvas = new Canvas(alteredBitmap);
                Paint paint = new Paint();
                paint.setColor(Color.WHITE);
                paint.setStrokeWidth(5);
                paint.setAntiAlias(true);
                Matrix matrix = new Matrix();
                canvas.drawBitmap(bmp, matrix, paint);

                (now alteredBitmap is processed bitmap and you can use it)
            }
        } catch (FileNotFoundException e) {
            Toast.makeText(this, "Error reading Image Metadata", Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }
}

其他包含的方法:

public static long getDeviceRam(Context context) {
    ActivityManager actManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
    actManager.getMemoryInfo(memInfo);
    long totalMemory = memInfo.totalMem;
    long fileSizeInKB = totalMemory / 1024;
    // Convert the KB to MegaBytes (1 MB = 1024 KBytes)
    return fileSizeInKB / 1024;
}

public static Bitmap getResizedBitmap(Bitmap image, int maxSize) {
    int width = image.getWidth();
    int height = image.getHeight();

    float bitmapRatio = (float) width / (float) height;
    if (bitmapRatio > 1) {
        width = maxSize;
        height = (int) (width / bitmapRatio);
    } else {
        height = maxSize;
        width = (int) (height * bitmapRatio);
    }
    return Bitmap.createScaledBitmap(image, width, height, true);
}

在这里,首先我使用解码图像 uriBitmapFactory,然后我检查了设备内存,它是 1GB,然后我调整位图的大小,否则原始位图就可以用于下一步,

接下来只是在画布上绘制位图,您将得到alteredBitmap将是处理后的位图,使用起来会很方便。

如果这不起作用,那么还有另一个选项可以完成您的要求,如下所示:

Bitmap bitmap = getBitmapFromGallery(selectedImage.getPath(), 800, 800);

private Bitmap getBitmapFromGallery(String path, int width, int height) {

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, width, height);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeFile(path, options);

    // if you use image from camera then in some cases it will rotated automatically like it was captured landscape but displays portrait then you have to use method `checkAndRotateBitmap()` which was also given below
    //    bitmap = checkAndRotateBitmap(bitmap);
    return bitmap;
}

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

private Bitmap checkAndRotateBitmap(Bitmap bmp) {
    try {
        ExifInterface ei = new ExifInterface(imgPath);
        int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_UNDEFINED);

        Bitmap rotatedBitmap;

        switch (orientation) {

            case ExifInterface.ORIENTATION_ROTATE_90:
                rotatedBitmap = Helper.rotateImage(bmp, 90);
                break;

            case ExifInterface.ORIENTATION_ROTATE_180:
                rotatedBitmap = Helper.rotateImage(bmp, 180);
                break;

            case ExifInterface.ORIENTATION_ROTATE_270:
                rotatedBitmap = Helper.rotateImage(bmp, 270);
                break;

            case ExifInterface.ORIENTATION_NORMAL:
            default:
                rotatedBitmap = bmp;
        }
        return rotatedBitmap;

    } catch (IOException e) {
        e.printStackTrace();
        return bmp;
    }
}

EDIT

有关如何进行的更多信息getBitmapFromGallery()方法有效,你应该看看https://medium.com/android-news/loading-large-bitmaps-efficiently-in-android-66826cd4ad53 https://medium.com/android-news/loading-large-bitmaps-efficiently-in-android-66826cd4ad53

这就是我的全部朋友,现在尝试一种满足您要求的方法,让我知道这是否有效,如果有任何问题,也请帮助我改进我的答案..谢谢!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

您如何在 Android 上处理超高 MP 相机(和图像)? - “画布:尝试绘制太大的位图” 的相关文章

随机推荐

  • JQuery UI Draggable - 如何知道元素是否可拖动初始化?

    我的逻辑是 if this draginited a drag disabled element shouldn t get pass here as it is inited this draggable 我搜索了很多 找不到实现这个逻辑
  • 带有 xml 文件内容的 NuGet 恢复包 - 工作示例?

    使用的工具 Visual Studio 2015 Enterprise Nuget 3 5 NET Framework 4 0 有谁有包含一些 xml 文件和 dll 库的 NuGet 包的工作示例吗 我在很多地方读过关于此的零散注释 但我
  • 在 R 中使用 apply() 时出现未使用参数错误

    当我尝试对日期列使用 apply 条件以返回一组系数时 收到错误消息 我有一个数据集 为简单起见 此处进行了修改 但可重现 ADataset lt data table Epoch c 2007 11 15 2007 11 16 2007
  • 针对 Android 开发优化 Eclipse

    我使用 Eclipse 和 ADT 插件开发 Android 而且速度 很慢 我必须经常重新启动 当我打开各种 Android 项目 当我使用库项目时需要 时 情况会变得更糟 使用 ADT 插件时 是否可以进行任何具体优化来提高 Eclip
  • MATLAB parfor 和 C++ 类 mex 包装器(需要复制构造函数?)

    我正在尝试使用概述的方法将 C 类包装在 matlab mex 包装器中here http www mathworks com matlabcentral newsreader view thread 278243 基本上 我有一个初始化
  • 依赖于 pod 的 Swift 通用框架

    我正在开发一个依赖于 Alamofire 的小型 Swift 框架 我将它用作属于同一工作区的应用程序的嵌入式框架 并且它运行良好 当我想构建一个具有总体目标的通用框架时 问题就出现了 然后 当执行脚本生成框架时 它失败并显示消息No su
  • 如何安装 xCode 命令行工具?如何将 Xcode 9 beta 与 xCode 8 一起安装?

    工具可用吗 我有一个苹果开发者帐户 并且我下载了 Xcode 9 betahttps developer apple com download https developer apple com download Xcode 9 开发者工具
  • 为什么我的 UIPickerView 崩溃了?

    当我使用 UIPickerView 加载视图时出现以下错误 由于未捕获的异常 NSInvalidArgumentException 而终止应用程序 原因 NSCFNumber isEqualToString 无法识别的选择器发送到实例 0x
  • 使用 MySQL 的 CURDATE() 或 PHP 的 date() 更快?

    使用mysql查询是不是更快 SELECT CURDATE as today 或 PHP 语句 curdate date Y m d 同样的答案是否适用于使用date VS MySQL 的NOW and CURTIME 如果您只是执行查询以
  • COM多线程支持

    第一次使用COM我有这个 COM dll 比如 ABCServer dll 我创建了一个 RCW 并在我的项目中添加了对它的引用 现在 我的应用程序创建了多个线程 每个线程从 COM dll 创建某些类并使用它们 但是 当其他线程正在处理
  • Java ASN.1 编译器

    现在我正在使用二进制笔记 http bnotes sourceforge net 解析 ASN 1 文件以在 Java 项目中使用 它采用 ASN 1 定义并生成 Java 类 让我可以操作 ASN 1 文件 我用扩展标记碰壁了 因为它不支
  • 奥尔良谷物任务调用结果

    对于这个长问题我深表歉意 我一直在对奥尔良进行实验 以了解它的各种特性 这些问题在逻辑上都归为一类 第一个测试涉及客户端每 1 秒向特定 Grain 发出一次请求 而 Grain 需要 10 秒来执行请求 代码是这样的 client cod
  • 有用的 Delphi 代码模板

    我用 Delphi 编程已经两年多了 我大概只有 5 个自定义模板 我觉得我应该有更多 如果有人有任何特别有用的东西 那么在 stackoverflow 上有一个很好的存储库就太好了 我不在乎您使用的是 Delphi 2009 语法还是 D
  • Flutter 中声明式编程和命令式编程有什么区别?

    最近 我正在寻找一种方法来增强 Flutter 应用程序中屏幕之间的导航 我发现了新的编程概念 陈述性的 and 至关重要的编程 我需要了解更多关于陈述性的 and 至关重要的范例以及 Flutter 中的示例 声明式路由意味着您的应用程序
  • 使用 pip 或 conda 来管理包? [复制]

    这个问题在这里已经有答案了 我已经使用 matlab 进行机器学习很长一段时间了 最 近切换到 python 并使用其包管理器 pip 安装某些包并成功安装了许多包 几天前 我开始使用 conda 我以前安装的所有软件包都被覆盖 我真的很想
  • 用户警告:MovieWriter ffmpeg 不可用

    尝试在 google colab 上制作动画 收到此警告 用户警告 MovieWriter ffmpeg 不可用 warnings warn MovieWriter s 不可用 writer did pip 安装 ffmpeg 标准化但没有
  • 在一个查询中对同一个表进行多个 COUNT SELECT

    对于某些人来说 这可能看起来很简单 但我就是无法理解 我一遍又一遍地从同一个表中进行多个 MS SQL SELECT 查询 SELECT count Page as tAEC FROM someTable WHERE Page LIKE A
  • 访问 UserDefault 数组 URL 以填充 CollectionView

    这是上一篇文章的后续内容here https stackoverflow com questions 48142238 save and append an array in userdefaults from imagepickercon
  • 按名称显示进程的命令行

    我在cmd中使用以下命令来获取进程命令行 它提供了所有流程的详细信息 WMIC OUTPUT C ProcessList txt PROCESS get Caption Commandline Processid 我想在记事本中按名称获取特
  • 您如何在 Android 上处理超高 MP 相机(和图像)? - “画布:尝试绘制太大的位图”

    我有一个活动 用户可以像这样打开相机 getPictureUri createImageFromFile true let photoUri it openCameraActivity REQUEST IMAGE CAPTURE it ph