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

如何在 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 数据,旋转图像,并在压缩时确保图像方向正确。

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.rhkb.cn/news/494849.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

怎么设置电脑密码?Windows和Mac设置密码的方法

为电脑设置密码是保护个人信息安全的重要措施。无论是Windows系统还是MacOS系统&#xff0c;设置密码的步骤都相对简单&#xff0c;但需要根据不同的操作系统选择不同的方法。 一、Windows系统电脑密码设置 方法一&#xff1a;通过控制面板设置账户密码 点击桌面左下角的“开…

谷歌浏览器的网络安全检测工具介绍

作为全球最受欢迎的浏览器之一&#xff0c;谷歌浏览器不仅提供了快速、便捷的浏览体验&#xff0c;还内置了一系列强大的网络安全检测工具&#xff0c;帮助用户识别潜在的网络威胁&#xff0c;保护个人隐私和数据安全。本文将详细介绍谷歌浏览器中的几项关键网络安全检测功能&a…

一个比RTK或redux更轻量级更易使用的 React 第三方状态管理工具库的配置与使用

本文由作者 Samdy_Chan 原创,未经作者同意,请勿随意转载! 使用轻量级第三方的 React 状态管理库 zustand 管理共享状态数据 在 react 框架应用中,开发者应该大多数都是采用 redux 状态管理工具库来管理应用的共享状态数据,但用过 redux 的人都知道,其配置和使用相当复杂…

菜鸟带新鸟——基于EPlan2022的部件库制作

首先&#xff0c;我们需要了解一些概念&#xff1a; Eplan的部件制作内容 以上内容是制作一个完整的部件所需要的。如果公司要求没有那么严格&#xff0c;我们就可以制作1-4级的内容就可以满足日常的使用啦&#xff01; 部件的创建方式 部件创建方式有4类&#xff1a; 1、单…

Charles安装证书过程(手机)

背景&#xff1a;使用模拟器抓包时&#xff0c;发现https请求无法抓取&#xff0c;需要安装相应证书。我自己是因为模拟器升级了安卓7&#xff0c;发现之前安装的证书无效了&#xff0c;需要重新安装。 参考博客&#xff1a;夜神模拟器12Charles进行Https抓包_模拟器抓包ssl-C…

Windows、CentOS环境下搭建自己的版本管理资料库:GitBlit

可以搭建属于公司内部或者个人的Git服务器&#xff0c;方便程序代码及文档版本管理。 官网&#xff1a;http://www.gitblit.com/ Windows环境下安装 提前已经安装好了JDK。 官网下载Windows版的GitBlit。 将zip包解压到自己想要放置的文件夹下。 建立版本库路径&#xff0c…

数据库操作【JDBC HIbernate Mybatis】

JDBC JDBC编程 在java开发中&#xff0c;以前都是通过JDBC&#xff08;Java Data Base Connectivity&#xff09;与数据库打交道的&#xff0c;至少在ORM&#xff08;Object Relational Mapping&#xff09;框架没出现之前是这样&#xff0c;目前常用的ORM框架有JPA、hibernat…

Linux 常见用例汇总

注&#xff1a;本文为 Linux 常见用例文章合辑。 部分内容已过时&#xff0c;未更新整理。 检查 Linux 上的 glibc 版本 译者&#xff1a;joeren | 2014-11-27 21:33 问&#xff1a;检查 Linux 系统上的 GNU C 库&#xff08;glibc&#xff09;的版本&#xff1f; GNU C 库&…

web-密码安全口令

目录 一、密码安全概述 二、密码安全现状 三、破解方式 四、暴力破解 五、字典破解 六、密码字典 学习心得&#xff1a; 一、密码安全概述 现在很多地方都是以用户名&#xff08;账号&#xff09;和口令&#xff08;密码&#xff09;作为鉴权的方式&#xff0c;口令&am…

工控触摸屏用winForms来构建框架,效果还是很不错的

工控触摸屏采用 winForms 构建框架具有诸多优势。winForms 提供了丰富的控件和强大的开发工具&#xff0c;使得界面设计更加高效。它具有良好的稳定性和兼容性&#xff0c;能够适应工控环境的复杂要求。通过 winForms 可以实现直观的操作界面&#xff0c;方便操作人员进行监控和…

开发一个DApp项目:DeFi、DApp开发与公链DApp开发

随着区块链技术的快速发展&#xff0c;去中心化应用&#xff08;DApp&#xff09;逐渐成为创新技术的核心之一。DApp能够利用区块链去中心化的特点&#xff0c;提供更高的安全性、透明性和效率&#xff0c;吸引了越来越多的开发者和投资者关注。本文将围绕如何开发一个DApp项目…

网络下载ts流媒体

网络下载ts流媒体 查看下载排序合并 很多视频网站&#xff0c;尤其是微信小程序中的长视频无法获取到准确视频地址&#xff0c;只能抓取到.ts片段地址&#xff0c;下载后发现基本都是5~8秒时长。 例如&#xff1a; 我们需要将以上地址片段全部下载后排序后再合成新的长视频。 …

性能优化!突破性能瓶颈的尖兵CPU Cache

缓存这个专业术语&#xff0c;在计算机世界中是经常使用到的。它并不是CPU所独有的&#xff0c;比如cdn缓存网站信息&#xff0c;浏览器缓存网页的图像视频等&#xff0c;但本文讲述的是狭义Cache&#xff0c;主要指的是CPU Cache&#xff0c;本文将其简称为"缓存"或…

Redis+注解实现限流机制(IP、自定义等)

简介 在项目的使用过程中&#xff0c;限流的场景是很多的&#xff0c;尤其是要提供接口给外部使用的时候&#xff0c;但是自己去封装的话&#xff0c;相对比较耗时。 本方式可以使用默认&#xff08;方法&#xff09;&#xff0c;ip、自定义参数进行限流&#xff0c;根据时间…

010 Qt_输入类控件(LineEdit、TextEdit、ComboBox、SpinBox、DateTimeEdit、Dial、Slider)

文章目录 前言一、QLineEdit1.简介2.常见属性及说明3.重要信号及说明4.示例一&#xff1a;用户登录界面5.示例二&#xff1a;验证两次输入的密码是否一致显示密码 二、TextEdit1.简介2.常见属性及说明3.重要信号及说明4.示例一&#xff1a;获取多行输入框的内容5.示例二&#x…

RabbitMQ 的7种工作模式

RabbitMQ 共提供了7种⼯作模式,进⾏消息传递,. 官⽅⽂档:RabbitMQ Tutorials | RabbitMQ 1.Simple(简单模式) P:⽣产者,也就是要发送消息的程序 C:消费者,消息的接收者 Queue:消息队列,图中⻩⾊背景部分.类似⼀个邮箱,可以缓存消息;⽣产者向其中投递消息,消费者从其中取出消息…

WebAPI编程(第一天,第二天)

WebAPI编程&#xff08;第一天&#xff0c;第二天&#xff09; day01 - Web APIs 1.1. Web API介绍 1.1.1 API的概念1.1.2 Web API的概念1.1.3 API 和 Web API 总结 1.2. DOM 介绍 1.2.1 什么是DOM1.2.2. DOM树 1.3. 获取元素 1.3.1. 根据ID获取1.3.2. 根据标签名获取元素1.3.…

java如何使用poi-tl在word模板里渲染多张图片

1、poi-tl官网地址 http://deepoove.com/poi-tl/ 2、引入poi-tl的依赖 <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.1</version></dependency>3、定义word模板 释义&#xf…

web三、 window对象,延时器,定时器,时间戳,location对象(地址),本地存储-localStorage,数组去重new Set

一、window对象 window对象 是一个全局对象&#xff0c;也可以说是JavaScript中的 顶级对象 像document、alert()、console.log()这些都是window的属性&#xff0c;基本BOM的属性和方法都是window的 所有通过 var定义 在全局作用域中的 变量 、 函数 都会变成window对象的属…

利用Spring Cloud Gateway Predicate优化微服务路由策略

利用Spring Cloud Gateway Predicate优化微服务路由策略 一、Predicate简介 Spring Cloud Gateway 是 Spring 生态系统中用于构建 API 网关的框架&#xff0c;它基于 Project Reactor 和 Netty 构建&#xff0c;旨在提供一种高效且灵活的方式来处理 HTTP 请求和响应。 Spring …