Java 中压缩图片并应用 EXIF 旋转信息

server/2024/12/27 8:20:00/

如何在 Java 中压缩图片并应用 EXIF 旋转信息

在图像处理中,特别是当你需要处理从相机或手机获取的照片时,图像的方向是一个常见问题。许多相机和手机在拍摄照片时会存储图像的方向信息,通常会保存在图像的 EXIF 元数据 中。Windows 和其他图像查看器会根据这些 EXIF 数据自动调整图像的显示方向,但 Java 默认并不会应用这些旋转信息。因此,在进行图像压缩时,必须确保图像的方向与 Windows 等系统中的预览一致。

在本文中,我们将探讨如何在 Java 中处理图像压缩,同时自动应用 EXIF 中的旋转信息,使得压缩后的图像方向正确。

关键步骤

  1. 读取 EXIF 数据:提取图片的 EXIF 元数据中的 Orientation 标签。
  2. 根据 EXIF 方向旋转图像:如果需要,旋转图像至正确的方向。
  3. 压缩图像:在旋转后的图像上执行压缩操作,确保图像尺寸符合需求。
  4. 保存压缩图像:保存压缩后的图像到目标文件。

依赖库

为了读取 EXIF 元数据,我们使用了 metadata-extractor 库。这个库能够帮助我们从图片中提取 EXIF 信息,尤其是 Orientation 标签。

Maven 依赖

如果你使用 Maven,可以在 pom.xml 文件中添加以下依赖:

<dependency><groupId>com.drewnoakes</groupId><artifactId>metadata-extractor</artifactId><version>2.16.0</version>
</dependency>

示例代码

以下是一个 Java 程序,它展示了如何读取图像的 EXIF 数据,旋转图像,并在压缩时确保图像方向正确。

java">import com.drewnoakes.metadata.exif.ExifDirectoryBase;
import com.drewnoakes.metadata.metadata.Directory;
import com.drewnoakes.metadata.metadata.Metadata;
import com.drewnoakes.metadata.extractor.ImageMetadataReader;import javax.imageio.*;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;public class ImageCompressorWithOrientation {public static void main(String[] args) throws Exception {File inputImage = new File("path_to_your_input_image.jpg");File outputImage = new File("path_to_your_output_image.jpg");compressImage(inputImage, outputImage);}public static void compressImage(File inputImage, File outputImage) throws Exception {BufferedImage image = ImageIO.read(inputImage);// 获取文件扩展名String extension = getFileExtension(inputImage);if ("jpg".equalsIgnoreCase(extension) || "jpeg".equalsIgnoreCase(extension)) {// 读取 EXIF 信息并根据 Orientation 旋转图像int orientation = getExifOrientation(inputImage);image = rotateImageBasedOnOrientation(image, orientation);// 压缩 JPG 文件compressJpgImage(image, outputImage);} else if ("png".equalsIgnoreCase(extension)) {// 对 PNG 文件进行无损压缩保存,不改变尺寸ImageIO.write(image, "png", outputImage);}}// 获取文件扩展名private static String getFileExtension(File file) {String filename = file.getName();int dotIndex = filename.lastIndexOf(".");if (dotIndex > 0) {return filename.substring(dotIndex + 1);}return "";}// 获取 EXIF 中的 Orientation 信息public static int getExifOrientation(File imageFile) throws Exception {Metadata metadata = ImageMetadataReader.readMetadata(imageFile);for (Directory directory : metadata.getDirectories()) {if (directory.containsTag(ExifDirectoryBase.TAG_ORIENTATION)) {return directory.getInt(ExifDirectoryBase.TAG_ORIENTATION);}}return 1; // 默认值,如果没有 Orientation 信息,认为是正向}// 根据 EXIF Orientation 信息旋转图像public static BufferedImage rotateImageBasedOnOrientation(BufferedImage image, int orientation) {BufferedImage rotatedImage = image;switch (orientation) {case 3:rotatedImage = rotateImage(image, 180);break;case 6:rotatedImage = rotateImage(image, 90);break;case 8:rotatedImage = rotateImage(image, 270);break;default:// 如果 Orientation 为 1,表示无需旋转break;}return rotatedImage;}// 旋转图像的方法public static BufferedImage rotateImage(BufferedImage img, int angle) {double radians = Math.toRadians(angle);int width = img.getWidth();int height = img.getHeight();int newWidth = (int) Math.abs(width * Math.cos(radians)) + (int) Math.abs(height * Math.sin(radians));int newHeight = (int) Math.abs(height * Math.cos(radians)) + (int) Math.abs(width * Math.sin(radians));BufferedImage rotatedImg = new BufferedImage(newWidth, newHeight, img.getType());Graphics2D g = rotatedImg.createGraphics();g.translate((newWidth - width) / 2, (newHeight - height) / 2);g.rotate(radians, width / 2, height / 2);g.drawImage(img, 0, 0, null);g.dispose();return rotatedImg;}// 压缩 JPG 图像public static void compressJpgImage(BufferedImage image, File outputImage) throws IOException {ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();try (ImageOutputStream ios = ImageIO.createImageOutputStream(outputImage)) {writer.setOutput(ios);ImageWriteParam param = writer.getDefaultWriteParam();if (param.canWriteCompressed()) {param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);param.setCompressionQuality(0.3f); // 压缩质量,0.0 最小质量,1.0 最高质量}// 将压缩图像写入文件writer.write(null, new javax.imageio.IIOImage(image, null, null), param);} finally {writer.dispose();}}
}

代码解析

  1. 读取 EXIF 数据
    我们通过 metadata-extractor 库来读取图像的 EXIF 元数据,特别是 Orientation 标签。Orientation 标签会告诉我们图像的正确方向。常见的 Orientation 值有:

    • 1:无旋转
    • 3:旋转 180°
    • 6:旋转 90° 顺时针
    • 8:旋转 270° 顺时针
  2. 旋转图像
    根据 EXIF 中的 Orientation 信息,我们用 rotateImageBasedOnOrientation 方法来旋转图像。如果 Orientation 为 6 或 8,表示图像需要旋转 90° 或 270°,如果是 3,表示需要旋转 180°。

  3. 压缩 JPG 图像
    使用 ImageWriter 将旋转后的图像压缩并保存,压缩质量通过 setCompressionQuality 方法控制。设置为 0.3f 表示较高的压缩比。

  4. 处理 PNG 图像
    对于 PNG 图像,我们直接保存,不进行旋转,也不改变其尺寸。

总结

在这篇文章中,我们探讨了如何在 Java 中处理图像压缩,同时确保图像应用 EXIF 中的旋转信息。这对于来自相机或手机的照片尤为重要,因为这些设备通常会存储旋转信息,Windows 等操作系统会根据该信息自动旋转图像。而 Java 默认不会应用这些旋转信息,因此我们需要在处理图像时手动调整方向。

通过使用 metadata-extractor 库,我们能够读取 EXIF 信息并在压缩图像时应用正确的方向。这样,我们就能确保压缩后的图像在各个平台上的显示一致,避免了由于旋转方向问题导致的显示错误。


http://www.ppmy.cn/server/153567.html

相关文章

SpringBoot3-第四篇(基础特性)

系列文章目录 SpringBoot3-第一篇&#xff08;快速入门&#xff09; SpringBoot3-第二篇&#xff08;Web开发&#xff09; SpringBoot3-第三篇&#xff08;数据访问&#xff09; SpringBoot3-第四篇&#xff08;基础特性&#xff09; 文章目录 系列文章目录1. SpringApplicati…

oscp学习之路,Kioptix Level2靶场通关教程

oscp学习之路&#xff0c;Kioptix Level2靶场通关教程 靶场下载&#xff1a;Kioptrix Level 2.zip 链接: https://pan.baidu.com/s/1gxVRhrzLW1oI_MhcfWPn0w?pwd1111 提取码: 1111 搭建好靶场之后输入ip a看一下攻击机的IP。 确定好本机IP后&#xff0c;使用nmap扫描网段&…

基于python+django的旅游信息网站-旅游景点门票管理系统

标题:基于 PythonDjango 的旅游信息网站-旅游景点门票管理系统 内容:1.摘要 基于 PythonDjango 的旅游信息网站-旅游景点门票管理系统的摘要&#xff1a; 随着旅游业的快速发展&#xff0c;旅游景点门票管理系统的重要性日益凸显。本文旨在设计并实现一个基于 PythonDjango 的…

Ps:在 Photoshop 中编辑视频

Photoshop 不仅是图像编辑的强大工具&#xff0c;它还提供了丰富的视频编辑功能&#xff0c;使用户可以轻松创建和编辑视频项目。 通过基于剪辑的“时间轴”面板&#xff0c;Photoshop 提供了类似于 Adobe Premiere Pro 等专业视频编辑器的操作体验。 Ps菜单&#xff1a;窗口/时…

微信小程序UI自动化测试实践 !

微信小程序UI自动化测试实践 引言&#xff1a; 随着微信小程序的快速发展&#xff0c;越来越多的企业和开发者开始开发小程序来满足用户的需求。而在开发小程序的过程中&#xff0c;UI自动化测试是一个必不可少的环节&#xff0c;可以帮助开发者减少人工测试的工作量&#xff…

红黑树C/CPP

#include <stdio.h> #include <stdlib.h>// 红黑树节点颜色定义 typedef enum { RED, BLACK } NodeColor;// 红黑树节点结构 typedef struct RBTreeNode {int key; // 节点的键值NodeColor color; // 节点的颜色&#xff08…

OpenCV相机标定与3D重建(35)计算两幅图像之间本质矩阵(Essential Matrix)的函数findEssentialMat()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 从两幅图像中的对应点计算本质矩阵。 cv::findEssentialMat 是 OpenCV 库中用于计算两幅图像之间本质矩阵&#xff08;Essential Matrix&#xf…

基于 Paimon x Spark 采集分析半结构化 JSON 的优化实践

摘要&#xff1a;本文整理自 阿里巴巴 A 数据湖架构师康凯老师和 Paimon PMC Member 毕岩老师在11月15日 Apache Spark & Paimon Meetup&#xff0c;助力 Lakehouse 架构生产落地上的分享。 文章介绍了阿里巴巴 A 业务基于 Variant 类型的 JSON 链路优化&#xff0c;并从技…