Apache Commons-Imaging 项目入门实践


placeholder image
admin 发布于:2023-07-09 21:50:23
阅读:loading

本人常对于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, -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, 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, 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, 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源码的基础上编写的,参考项目代码结构如下:

image.png

(2)本次给出图片下载,如果有兴趣可将此图片附件下载下来,自行解析,《任意截图-写入数据.png》。

(3)test目录下的个人编写Test案例包的文件下载《百度云下载.txt》。

 点赞


 发表评论

当前回复:作者

 评论列表


留言区