Android - 如何在编程中加载或保存JPEG文件时获取或设置(打印)DPI(每英寸点数)?

9

我开发了一个 Android 应用程序,支持 Android 4.0 及以上版本(该应用程序不支持 Android 4.0 [冰激凌三明治] 版本以下的设备)。

这个问题与不同图像 (例如 jpeg 或 png) 格式的 (打印) DPI 相关。

此问题与 Android 设备的屏幕 DPI 或尺寸无关。它也与在设备屏幕大小上显示位图无关。

我正在使用以下代码加载图像文件到 'Bitmap' 中,然后将其裁剪并以 jpeg 压缩格式保存到另一个文件中。 我已经能够通过以下代码实现此操作,但我无法获得已加载或设置保存图像文件的 DPI。

所以,我有两个问题。

1)如何在加载 'Bitmap' 时从 JPEG 文件中获取(打印)DPI?

2)在保存新生成的 'Bitmap' 时,如何再次设置 JPEG 文件的 DPI?

以下是参考代码片段:

    FileInputStream inputStream = new FileInputStream(theSourcePhotoFilePathName);

    Bitmap bitmap = null;
    BitmapRegionDecoder decoder = null;

    BitmapFactory.Options options = new BitmapFactory.Options();        
    options.inSampleSize = 1;
    options.inDensity = 300;   // Tried this but not working.

    try {
        decoder = BitmapRegionDecoder.newInstance(in, false);
        bitmap = decoder.decodeRegion(region, options);      // the region has cropping coordinates.
    } catch (IllegalArgumentException e){
        Log.d("First Activity", "Failed to recycle bitmap for rect=" + region, e);
    } catch (IOException e) {
        Log.d("First Activity", "Failed to decode into rect=" + region, e);
    } finally {
        if (decoder != null) decoder.recycle();
    }

    inputStream.close();
    inputStream = null;

    FileOutputStream fos = new FileOutputStream( theTargetTempFolderDestFilePath );
    bitmap.compress(CompressFormat.JPEG, jpegCompressionRatio, fos);
    fos.flush();
    fos.close();
    fos = null;

我尝试从stackoverflow和其他网站上通过谷歌搜索找到了答案,但是没有得到相关的正确答案。因此,我决定在这个论坛上问一下。

欢迎您提出任何提示和建议。

Sanjay。

3个回答

14

为方便起见,这里提供了一个可供复制粘贴的方法:

public static void saveBitmapToJpg(Bitmap bitmap, File file, int dpi) throws IOException {
    ByteArrayOutputStream imageByteArray = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageByteArray);
    byte[] imageData = imageByteArray.toByteArray();

    setDpi(imageData, dpi);

    FileOutputStream fileOutputStream = new FileOutputStream(file);
    fileOutputStream.write(imageData);
    fileOutputStream.close();
}

private static void setDpi(byte[] imageData, int dpi) {
    imageData[13] = 1;
    imageData[14] = (byte) (dpi >> 8);
    imageData[15] = (byte) (dpi & 0xff);
    imageData[16] = (byte) (dpi >> 8);
    imageData[17] = (byte) (dpi & 0xff);
}

保存的文件将具有正确设置的图像 DPI值:

输入图片说明文字


Gr8 - 是新安卓开发者的简化解决方案。 - SHS
这个程序可以在JPEG文件格式上运行,但是有没有人知道PNG文件怎么办? - Rahul Mandaliya

5

您是指作为JPEG文件一部分编写的DPI元数据吗?我最近也遇到了这个问题,但找到了解决方案。如果您已经解决了它,这个答案可以帮助其他遇到同样问题的人。

在Android中将位图压缩为JPEG时,它以JFIF段格式保存。请参见此处的文章(http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format)。我还附上了一个Android jpeg图像在十六进制编辑器中打开的屏幕截图,以便您可以看到它的匹配情况。

enter image description here

要编辑该值,您需要首先创建一个byte[]数组来存储Bitmap.compress()。以下是我编写的一部分代码,其中我正是这样做的(输入为源位图)。
ByteArrayOutputStream uploadImageByteArray = new ByteArrayOutputStream();
input.compress(Bitmap.CompressFormat.JPEG, 100, uploadImageByteArray);
byte[] uploadImageData = uploadImageByteArray.toByteArray();

基于JFIF结构,您需要编辑字节数组中的第13、14、15、16和17个索引。第13个指定密度类型,第14和15个指定X分辨率,第16和17个保存Y分辨率。在我的情况下,我将元数据中的X和Y分辨率更改为500dpi。这转换为0000 0001 1111 0100,即16进制的1F4。然后,我将byte []写入文件,从手机复制到计算机,并在查看图像属性时验证了详细信息。

                uploadImageData[13] = 00000001;
                uploadImageData[14] = 00000001;
                uploadImageData[15] = (byte) 244
                uploadImageData[16] =  00000001;
                uploadImageData[17] = (byte) 244


                 File image = new  File(Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                .getAbsolutePath() + "/meta Images/", imageFileName);
                FileOutputStream sdCardOutput = new FileOutputStream(image);
                sdCardOutput.write(uploadImageData);

注意:Java使用有符号字节系统,如果您输入的二进制值超过127,则编译器会报错。我曾经遇到过这个问题,在将F4输入为字节时出现了错误。解决方法是将您的值转换为十进制,然后使用(byte)进行强制类型转换。

希望这可以帮助您!


太棒了,我使用第三方库sanselanandroid也能做到同样的事情。但是这种方法更好且更快。这种方法会更改Exif信息中的数据。通过这种方法,我们能够更改jpeg的“X分辨率”和“Y分辨率”,其中图像没有缩略图。当我尝试使用从专业相机拍摄的照片(其中包含缩略图和更多exif信息)时,我无法以直接或Sanselan的方式设置DPI。 - SHS

1
提供的答案是关于如何设置dpi的。为了获取图像的dpi:

   final ImageInfo imageInfo = Sanselan.getImageInfo(image_file);

   final int physicalWidthDpi = imageInfo.getPhysicalWidthDpi();
   final int physicalHeightDpi = imageInfo.getPhysicalHeightDpi();

Gradle依赖项:implementation group: 'org.apache.sanselan', name: 'sanselan', version: '0.97-incubator'

(注:此段为原文,已翻译完成)

感谢@Talha,展示了使用Sanselan获取dpi的逻辑。 - SHS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接