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

Android相机捕获图片压缩画质劣化:如何轻度压缩保画质?

解决Android相机捕获图片轻度压缩且保画质的问题

兄弟,你这代码的问题根本不是压缩的锅——你用data.getExtras().get("data")拿到的根本不是相机拍的原图,是系统自动生成的低分辨率缩略图!哪怕你用PNG 100%压缩,画质也不可能好,因为源素材本身就糊。

给你一套完整的解决方案,既能拿到高清原图,又能做轻度压缩保画质:

第一步:启动相机时指定输出路径,直接保存原图

别再用系统返回的缩略图了,让相机直接把原图存到你指定的文件里,这样拿到的就是完整分辨率的照片:

// 先写个创建图片文件的工具方法
private File createImageFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    // 用APP私有存储目录,不用申请外部存储权限(Android 10+)
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    return File.createTempFile(
        imageFileName,  // 文件名前缀
        ".jpg",         // 后缀,JPEG比PNG更适合照片压缩
        storageDir      // 存储目录
    );
}

// 启动相机的代码
private static final int REQUEST_IMAGE_CAPTURE = 1;
private File mPhotoFile; // 保存图片文件的引用,方便后续读取

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // 先确认有相机应用能处理这个Intent
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        try {
            mPhotoFile = createImageFile();
        } catch (IOException ex) {
            // 处理文件创建失败的情况,比如弹个Toast提示
            ex.printStackTrace();
        }
        // 如果文件创建成功,继续
        if (mPhotoFile != null) {
            // Android 7.0+要用FileProvider,不能直接传file://Uri
            Uri photoURI = FileProvider.getUriForFile(this,
                    "com.yourapp.package.fileprovider", // 替换成你的APP包名+fileprovider
                    mPhotoFile);
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }
}

注意:配置FileProvider(Android 7.0+必需)

在AndroidManifest.xml里添加Provider配置:

<application>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.yourapp.package.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
    ...
</application>

然后在res/xml目录下新建file_paths.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="my_images" path="Pictures" />
</paths>

第二步:读取原图并做轻度压缩

相机拍照完成后,在onActivityResult里读取我们保存的原图,然后按需压缩(既控体积又保画质):

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        // 读取原图文件
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 先不加载到内存,只获取原图的宽高信息
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(mPhotoFile.getAbsolutePath(), options);
        
        // 计算采样率:如果原图太大,适度缩小(比如缩到1080P),避免OOM且保画质
        int targetWidth = 1920;
        int targetHeight = 1080;
        int sampleSize = calculateInSampleSize(options, targetWidth, targetHeight);
        
        // 加载缩小后的高清图
        options.inJustDecodeBounds = false;
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565; // 比ARGB_8888省一半内存,画质几乎无损失
        Bitmap bitmap = BitmapFactory.decodeFile(mPhotoFile.getAbsolutePath(), options);
        
        // 最后做轻度压缩:JPEG格式设85-90的质量,人眼看不出差异,体积却小很多
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 要是你的图需要透明通道,再换成Bitmap.CompressFormat.PNG
        bitmap.compress(Bitmap.CompressFormat.JPEG, 90, baos);
        byte[] compressedData = baos.toByteArray();
        
        // 记得回收Bitmap,避免内存泄漏
        if (bitmap != null && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
        
        // 这里就可以用compressedData做后续操作啦,比如上传、保存
    }
}

// 计算采样率的工具方法,保证采样率是2的幂,Android系统优化过这种缩放
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    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;

        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

几个关键小贴士

  • 别乱用PNG压缩:PNG是无损压缩,照片用它的话体积会大到离谱,JPEG在90%质量下,画质和原图几乎没区别,但体积只有PNG的1/5甚至更小。只有当图片需要透明通道时再用PNG。
  • 采样率很重要:现在手机相机动辄4800万像素,直接加载原图会爆内存,用采样率缩到1080P足够日常使用,还能省内存。
  • 权限问题:Android 10+用getExternalFilesDir不需要申请外部存储权限,省心很多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:19:21