本人常对于Apache Commons的公共项目进行关注,也曾经对多个项目的应用进行了实践,本次分享一下对Apache Commons-Imaging项目的实践,也就只是了解了几个小时,粗浅的进行掌握而已。
1.基本介绍
Apache Commons Imaging,以前称为 Apache Commons Sanselan,是一个读取和写入各种图像格式的库,包括快速解析图像信息(大小、颜色空间、ICC 配置文件等)和元数据。这个库是纯Java的。与本机代码中的典型图像 I/O 库相比,它更可移植,并且应该更可靠且更安全,可以防止损坏/恶意图像,但仍然表现得相当好。它比ImageIO/JAI/java.awt.Toolkit(Sun/Java的图像支持)更容易使用,支持更多格式(并且支持更正确)。它还提供对元数据的轻松访问。
甚至在作为 Apache Commons 组件首次发布之前,成像功能就已经发挥作用并被许多生产项目所使用。该项目是开源的;像自由一样自由,它可在 ASF (Apache) 许可证下使用。
一句话总结:它可以将一些数据以元素据的方式写入至图片中,轻松实现内涵图。
本站在早期有分享使用Windows的copy命令实现的内涵图,本次分享的开源项目是使用Java API的方式读写图片的元素据。
在Windows中查看一个图片的属性时,能够看到图片的宽度、高度、分辨率、像素等信息,若打开一个照相机拍的图片时可以看到更多的属性,比如有拍摄日期、相机型号、焦距、闪光灯模式、EXIF版本等参数,这也说明照相机拍的照片存储了更多的图片数据,所以我们的日常生活中的使用原图发送时就需要注意了,可能会把一些数据给发送出去。
2.为什么使用它
为什么还要另一个图像库?已经有很多了,可以参考以下几点:
(1)Apache Commons Imaging 是用 100% 纯 Java 编写的。它可以在任何 JVM 和任何平台上运行,无需修改。
(2)没有依赖性。Commons Imaging 仅需要 Java 1.8 或更高版本。
(3)它的设计非常易于使用。它有一个简单、干净的界面。大多数操作都是单个成像方法调用。请参阅示例用法部分。
(4)Commons Imaging 的目标是透明。没有隐藏的缓冲区需要处理,没有本机内存需要释放,没有后台线程。
(5)它读取和写入各种图像格式,并支持所有或大多数其他库所遗漏的一些变体和编码。请参阅格式支持列表。
(6)Commons Imaging 的作用不仅仅是读取和写入图像。读取图像信息(图像大小、色彩空间、位深度等)和元数据很容易,并且不需要读取图像像素。它以格式中立的方式呈现图像信息和元数据。它还可以轻松、结构化地访问特定于格式的信息。
(7)它可以读取和写入文件、字节数组或任何输入流/输出流。读取InputStreams只读取它需要的数据,并缓存已读取的数据,因此它在I/O上高效,在慢速存储和网络文件上快速。
(8)它支持以结构化方式读写各种元数据,包括 EXIF 元数据。
(9)它是免费软件/开源。它可根据 Apache 软件许可证使用。
……
3.代码介绍
本次通过简单的代码来读取和写入一个图片的元素据信息,实践案例如下:
(1)对一个照相机拍摄的图片的元数据读取;
(2)对一个手机拍摄的图片的元素据获取;
(3)读取一个截图软件生成的图片的元素据获取;
(4)将截图生成的图片(无元素据)写入元素据;
3.1 解析照相机图片
Exif metadata:
Root:
ImageDescription: ''
Make: 'SONY'
Model: 'ILCE-7M2'
Orientation: 1
XResolution: 350
YResolution: 350
ResolutionUnit: 2
Software: 'ILCE-7M2 v3.10'
DateTime: '2020:10:02 14:30:19'
YCbCrPositioning: 2
ExifOffset: 364
PrintIM: 80, 114, 105, 110, 116, 73, 77, 0, 48, 51, 48, 48, 0, 0, 3, 0, 2, 0, 1, 0, 0, 0, 3, 0, 34, 0, 0, 0, 1, 1, 0, 0, 0, 0, 9, 17, 0, 0, 16, 39, 0, 0, 11, 15, 0, 0, 16, 39, 0, 0, -105... (106)
Exif:
ExposureTime: 1/200 (0.005)
FNumber: 63/10 (6.3)
ExposureProgram: 2
PhotographicSensitivity: 100
Unknown Tag (0x8830): 2
Unknown Tag (0x8832): 100
ExifVersion: 48, 50, 51, 48
DateTimeOriginal: '2020:10:02 14:30:19'
DateTimeDigitized: '2020:10:02 14:30:19'
ComponentsConfiguration: 1, 2, 3, 0
CompressedBitsPerPixel: 4
BrightnessValue: 22334/2560 (8.724)
ExposureCompensation: 0
MaxApertureValue: 1188/256 (4.641)
MeteringMode: 5
LightSource: 0
Flash: 16
FocalLength: 59
MakerNote: 83, 79, 78, 89, 32, 68, 83, 67, 32, 0, 0, 0, 95, 0, 3, 16, 4, 0, 16, 0, 0, 0, 28, 8, 0, 0, 0, 32, 7, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 32, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3... (37312)
UserComment: ''
FlashpixVersion: 48, 49, 48, 48
ColorSpace: 1
ExifImageWidth: 6000
ExifImageLength: 4000
InteropOffset: 38362
FileSource: 3
SceneType: 1
CustomRendered: 0
ExposureMode: 0
WhiteBalance: 0
DigitalZoomRatio: 1
FocalLengthIn35mmFormat: 59
SceneCaptureType: 0
Contrast: 0
Saturation: 0
Sharpness: 0
LensSpecification: 28, 70, 35/10 (3.5), 56/10 (5.6)
LensModel: 'FE 28-70mm F3.5-5.6 OSS'
Interoperability:
InteroperabilityIndex: 'R98'
InteroperabilityVersion: 48, 49, 48, 48
Sub: (jpegImageData)
Compression: 6
ImageDescription: ''
Make: 'SONY'
Model: 'ILCE-7M2'
Orientation: 1
XResolution: 72
YResolution: 72
ResolutionUnit: 2
Software: 'ILCE-7M2 v3.10'
DateTime: '2020:10:02 14:30:19'
JpgFromRawStart: 38654
JpgFromRawLength: 7902
YCbCrPositioning: 2
No Photoshop (IPTC) metadata.
3.2 解析手机图片
Exif metadata:
Root:
ImageWidth: 3000
ImageLength: 4000
Make: 'Meitu'
Model: 'MI CC 9 Meitu Edition'
Orientation: 1
XResolution: 72
YResolution: 72
ResolutionUnit: 2
DateTime: '2023:06:22 17:07:02'
YCbCrPositioning: 1
ExifOffset: 210
Exif:
ExposureTime: 1/786 (0.001)
FNumber: 179/100 (1.79)
ExposureProgram: 2
PhotographicSensitivity: 125
ExifVersion: 48, 50, 50, 48
DateTimeOriginal: '2023:06:22 17:07:02'
DateTimeDigitized: '2023:06:22 17:07:02'
ComponentsConfiguration: 1, 2, 3, 0
ShutterSpeedValue: 9619/1000 (9.619)
ApertureValue: 167/100 (1.67)
BrightnessValue: 441/100 (4.41)
ExposureCompensation: 0
MaxApertureValue: 167/100 (1.67)
MeteringMode: 2
LightSource: 0
Flash: 16
FocalLength: 4740/1000 (4.74)
SubSecTime: '923584'
SubSecTimeOriginal: '923584'
SubSecTimeDigitized: '923584'
FlashpixVersion: 48, 49, 48, 48
ColorSpace: 1
ExifImageWidth: 3000
ExifImageLength: 4000
InteropOffset: 1050
SensingMethod: 1
SceneType: 1
ExposureMode: 0
WhiteBalance: 0
FocalLengthIn35mmFormat: 25
SceneCaptureType: 0
Unknown Tag (0xa503): 1
Unknown Tag (0xa504): '0'
Unknown Tag (0xa505): '50,0'
Unknown Tag (0xa506): 0
Unknown Tag (0xa508): 5057
Unknown Tag (0xa509): '2.46340'
Unknown Tag (0xa50a): '0'
Unknown Tag (0xa50b): '1397,2077,1582,2262'
Unknown Tag (0xa600): 1
Unknown Tag (0xa602): 0
Unknown Tag (0xa603): 1
Unknown Tag (0xa604): 'MFNR(3 frame)'
Unknown Tag (0xa608): 1
Unknown Tag (0xa61a): 5,057
Unknown Tag (0xa61e): 305600/100000 (3.056)
Unknown Tag (0xa627): 107601/1000 (107.601)
Unknown Tag (0xa628): 96635/1000 (96.635)
Unknown Tag (0xa629): 502
Unknown Tag (0xa62a): '36'
Unknown Tag (0xa62c): 'Sw pp'
Unknown Tag (0xa62d): 'Portrait'
Interoperability:
InteroperabilityIndex: 'R98'
InteroperabilityVersion: 48, 49, 48, 48
Sub: (jpegImageData)
Compression: 6
Orientation: 1
XResolution: 72
YResolution: 72
ResolutionUnit: 2
JpgFromRawStart: 1210
JpgFromRawLength: 25163
Unknown Tag (0xa002): 240
Unknown Tag (0xa003): 320
No Photoshop (IPTC) metadata.
3.3 解析任意截图
null
3.4 任意截图写入并解析
@Test
public void testImageMetadata44() throws IOException {
String filePath = this.getClass().getResource("任意截图-写入数据.png").getFile();
File jpegImageFile = new File(URLDecoder.decode(filePath , StandardCharsets.UTF_8.name()));
TiffImagingParameters params = new TiffImagingParameters();
TiffOutputSet outputSet = params.getOutputSet();
if (outputSet == null) {
outputSet = new TiffOutputSet();
}
TiffOutputDirectory tiffOutputFields = outputSet.addRootDirectory();
//标准属性
tiffOutputFields.add(MicrosoftTagConstants.EXIF_TAG_XPTITLE , "标题名字");
tiffOutputFields.add(MicrosoftTagConstants.EXIF_TAG_XPAUTHOR , "作者名字");
tiffOutputFields.add(MicrosoftTagConstants.EXIF_TAG_XPCOMMENT , "备注:https://www.chendd.cn");
tiffOutputFields.add(MicrosoftTagConstants.EXIF_TAG_XPSUBJECT , "主题");
tiffOutputFields.add(MicrosoftTagConstants.EXIF_TAG_XPKEYWORDS , "陈冬冬、Java");
//经度
{
double value = 111.68;
final double latitudeDegrees = (long) value;
value %= 1;
value *= 60.0;
final double latitudeMinutes = (long) value;
value %= 1;
value *= 60.0;
final double latitudeSeconds = value;
tiffOutputFields.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE,
RationalNumber.valueOf(latitudeDegrees) , RationalNumber.valueOf(latitudeMinutes) , RationalNumber.valueOf(latitudeSeconds));
}
//纬度
{
double value = 32.36;
final double latitudeDegrees = (long) value;
value %= 1;
value *= 60.0;
final double latitudeMinutes = (long) value;
value %= 1;
value *= 60.0;
final double latitudeSeconds = value;
tiffOutputFields.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE,
RationalNumber.valueOf(latitudeDegrees) , RationalNumber.valueOf(latitudeMinutes) , RationalNumber.valueOf(latitudeSeconds));
}
//其它数据
tiffOutputFields.add(TiffTagConstants.TIFF_TAG_DATE_TIME , "2020:30:40 11:22:33");
tiffOutputFields.add(TiffTagConstants.TIFF_TAG_COPYRIGHT , "Copyright https://www.chendd.cn", "Copyright Apache");
tiffOutputFields.add(TiffTagConstants.TIFF_TAG_IMAGE_DESCRIPTION , "这是一张来自于陈冬冬个人博客的图片");
tiffOutputFields.add(TiffTagConstants.TIFF_TAG_ORIENTATION , (short) 35);
tiffOutputFields.add(Rfc2301TagConstants.TIFF_TAG_DECODE , RationalNumber.valueOf(Math.PI));
params.setOutputSet(outputSet);
BufferedImage bufferedImage = Imaging.getBufferedImage(jpegImageFile);
TiffImageParser parser = new TiffImageParser();
File outputFile = new File(jpegImageFile.getParentFile() , "任意截图-写入数据-后.png");
parser.writeImage(bufferedImage , new FileOutputStream(outputFile) , params);
//获取新图片的元数据
final ImageMetadata metadata = Imaging.getMetadata(outputFile);
System.out.println(metadata);
System.out.println("新图片文件路径:" + outputFile.getPath());
}
输出结果:
Root: (tiffImageData)
Unknown Tag (0x2): 32, 21, 36
Unknown Tag (0x4): 111, 40, 48
ImageWidth: 1143
ImageLength: 289
BitsPerSample: 8, 8, 8
Compression: 5
PhotometricInterpretation: 2
ImageDescription: '这是一张来自于陈冬冬个人博客的图片'
PreviewImageStart: 1784, 1952, 2108, 2264, 2460, 2896, 3544, 4324, 5092, 5892, 6160, 6360, 6516, 6672, 6864, 7096, 7308, 7528, 7868, 8308, 8996, 9908, 10808, 11836, 13128, 14708, 16280, 17436, 18408, 18756, 18988, 19164, 19380, 19668, 20004, 20352, 20708, 21036, 21364, 21580, 21748, 21936, 22280, 22928, 24016, 25284, 26480, 27772, 28216, 28500, 28676... (145)
Orientation: 35
SamplesPerPixel: 3
RowsPerStrip: 2
PreviewImageLength: 167, 156, 156, 194, 435, 645, 780, 765, 797, 267, 200, 156, 156, 192, 232, 211, 219, 337, 437, 686, 911, 897, 1026, 1290, 1578, 1572, 1156, 971, 345, 231, 176, 214, 286, 334, 345, 355, 327, 325, 214, 165, 188, 344, 646, 1087, 1268, 1195, 1292, 444, 284, 174, 242... (145)
XResolution: 72
YResolution: 72
ResolutionUnit: 2
DateTime: '2020:30:40 11:22:33'
Predictor: 2
Decode: 355/113 (3.142)
Copyright: Copyright https://www.chendd.cn, Copyright Apache
XPTitle: '标题名字'
XPComment: '备注:https://www.chendd.cn'
XPAuthor: '作者名字'
XPKeywords: '陈冬冬、Java'
XPSubject: '主题'
PS:本次示例仅写入了Root节点的数据,包含标准属性、经度、纬度、作者信息、图片描述、拍摄时间等数据。
4.其它说明
(1)本次代码案例在Apache Commons-Imaging源码的基础上编写的,参考项目代码结构如下:
(2)本次给出图片下载,如果有兴趣可将此图片附件下载下来,自行解析。
(3)test目录下的个人编写Test案例包的文件下载。
(4)相关下载可转至原文。
Apache Commons-Imaging 项目入门实践欢迎来到陈冬冬的个人经验分享平台https://www.chendd.cn/blog/article/1678038894406066177.html