Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ghillert/image-metadata

Examples for processing image metadata using Java
https://github.com/ghillert/image-metadata

graalvm image-processing imagemetadata java spring springboot

Last synced: about 2 months ago
JSON representation

Examples for processing image metadata using Java

Awesome Lists containing this project

README

        

:current-version: 1.0.0-SNAPSHOT

= Image Metadata processing using Java

image:https://github.com/ghillert/image-metadata/workflows/Java%20CI%20with%20Maven/badge.svg[CI Image Metadata,link=https://github.com/ghillert/image-metadata/actions]

This repository contains examples for processing image metadata using Java. Currently, the following module
is available:

- *image-metadata-commons-imaging*

This https://spring.io/projects/spring-boot[Spring Boot] 3 application will allow you to upload images (JPG, PNG, GIF)
and the application will extract the metadata from those files using
https://commons.apache.org/proper/commons-imaging/[Apache Commons Imaging]. This includes:

- *EXIF* (Exchangeable image file format)
- *IPTC* (International Press Telecommunications Council)
- *XMP* (eXtensible Metadata Platform)
- Generic *file* metadata

To learn more about the alphabet soup, see my blog post:
https://medium.com/@hillert/read-write-image-metadata-with-java-part-1-d5e2057c80d9[Read & Write Image Metadata with Java — Part 1]
This demo application also provides the following additional features:

- Remove all image metadata
- Populate image metadata such as image title and reference id
- Ability to resize images
- Slightly sharpen resized images using a Gaussian Unsharp Mask
- Add a font label to resized images
- Ability to load images using ImageIO or AWT Toolkit (To study performance differences)
- Ability to download the stored image

NOTE: Image metadata is manipulated during file upload. Any other manipulation such as resizing is done during image
retrieval. This is purely a demo app and not meant to be production ready.

== UI Options

=== File upload

The main input field (and only mandatory input field) is the file upload option. You can select
any image of the following type to extract metadata from:

- JPG
- PNG
- GIF

=== Title

The title input field, will populate

- the XMP `Title` tag of the Dublin Core namespace.
- the IPTC `Object Name` field
- the EXIF `Image Description` Field

In case you also selected the `Remove Metadata` checkbox, we will also populate the `XPTitle` EXIF tag.

- https://exiftool.org/TagNames/XMP.html#dc[XMP Dublin Core (dc) namespace tags]
- https://exiftool.org/TagNames/EXIF.html
- https://exiftool.org/TagNames/IPTC.html#ApplicationRecord

=== Reference ID

Populates the IPTC field `OriginalTransmissionReference` as well as the XMP tag `TransmissionReference`. These two fields
are commonly used as a job identifier. So if you need to tie your images to e.g. a database record, this might be a useful
field to know about. For more information, please also see the following links for more information:

- https://exiftool.org/TagNames/XMP.html#photoshop[XMP photoshop Tags]
- https://exiftool.org/TagNames/IPTC.html#ApplicationRecord[IPTC ApplicationRecord Tags]

=== Remove Metadata

Removes all _IPTC_, _EXIF_ and _XMP_ metadata.

=== Populate Windows Tags

The following tags are the Windows-specific exif tags:

- XPTitle (Populated by EXIF tag ImageDescription)
- XPComment
- XPAuthor (Populated by EXIF tag Artist)
- XPKeywords
- XPSubject
- Rating
- RatingPercent

If the checkbox is selected AND the Caption/Title input field is populated, we will populate the `xptitle` tag.
Please also see the EXIF tag documentation at https://exiftool.org/TagNames/EXIF.html.

== Building from Source

=== Requirements

- https://git-scm.com/[Git] to clone the repository

==== Standard Java

- https://www.oracle.com/java/technologies/downloads/#java21[Java 21] or
- https://www.oracle.com/java/technologies/downloads/#java17[Java 17]

TIP: Use https://sdkman.io/[SDKMAN!] to install any version of Java.

==== Native Compilation:

For the native compilation support, you will need a https://www.graalvm.org/[GraalVM] based implementation of Java. As
this application uses AWT classes, Bellsoft's https://bell-sw.com/liberica-native-image-kit/[Liberica Native Image Kit] (NIK)
is currently (as of Oct 24, 2023) the best implementation. The following version was used for testing:

- https://bell-sw.com/pages/downloads/native-image-kit/[BellSoft 23.1.r21-nik]

[NOTE]
====
Oracle GraalVM does not work on *MacOS*, yet. You will encounter a `No awt in java.library.path`. See
https://github.com/oracle/graal/issues/4124 for details.

If you try, you will see an exception like the following:

[source,bash,indent=0,subs=attributes]
----
2023-02-12T09:38:34.721-10:00 ERROR 65901 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: java.lang.UnsatisfiedLinkError: no awt in java.library.path] with root cause

java.lang.UnsatisfiedLinkError: no awt in java.library.path
...
----
====

TIP: Use https://sdkman.io/[SDKMAN!] to install GraalVM.

=== Check out source code

Check out the project using https://git-scm.com/[Git]:

[source,bash,indent=0]
----
git clone https://github.com/ghillert/image-metadata.git
cd image-metadata
----

=== Build

[source,bash,indent=0]
----
./mvnw clean package
----

=== Run

[source,bash,indent=0,subs=attributes]
----
java -jar ./image-metadata-commons-imaging/target/image-metadata-commons-imaging-{current-version}.jar
----

Open your browser and go to http://localhost:8080/

=== Build + Run in one go

[source,bash,indent=0,subs=attributes]
----
./mvnw spring-boot:run -pl image-metadata-commons-imaging
----

== Going Native

Native compilation has been a bit of a moving target when using AWT classes. Luckily, the situation is
continuously improving. For instance, until recently the story on Windows was a bit more involved, requiring
to compile the project using the *x64 Native Tools Command Prompt for VS 2022* (See the following
https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311[blog post]
for more information.)

Luckily as of GraalVM for JDK 17.0.8 the situation on Windows is much improved -
See the following https://medium.com/graalvm/a-new-graalvm-release-and-new-free-license-4aab483692f5[blog post]
for details.

*IMPORTANT*

When using AWT and ImageIO classes etc. the native GraalVM Native Image metadata seems to be specific to the relevant platform.
Therefore, you may need to rename the respective folder under
`image-metadata-commons-imaging/src/main/resource/META-INF` to `native-image`. I provide the following folders:

- native-image-linux
- native-image-mac
- native-image-windows

Alternatively, you can run the *native-image-agent* as described under *Development* below.

=== Creating a Local Native Image

Creating a local image should be as easy as:

[source,bash,indent=0,subs=attributes]
----
./mvnw clean package -DnativeCompile
----

This shorthand system property will activate the Maven profiles:

- native
- nativeCompile

So you can also use:

[source,bash,indent=0,subs=attributes]
----
./mvnw clean package -Pnative,nativeCompile
----

The result (if successful) will be an executable binary at: `image-metadata-commons-imaging/target/image-metadata-commons-imaging`

TIP: You can use https://upx.github.io/[Ultimate Packer for eXecutables] (UPX) to further compress the binary. E.g.
`upx -9 image-metadata-commons-imaging/target/image-metadata-commons-imaging`

NOTE: UPX is currently broken on MacOS 13. See the relevant https://github.com/upx/upx/issues/612[GitHub issue ticket]
for details.

==== macOS

As the application uses some AWT classes for image processing, the native version for macOS will not run using Oracle
GraalVM. There is an https://github.com/oracle/graal/issues/4124[open GitHub issue] at the GraalVM project.

However, compilation will succeed and the application will run when using
https://bell-sw.com/pages/downloads/native-image-kit/[Liberica Native Image Kit] (NIK).

[source,bash]
----
./image-metadata-commons-imaging/target/image-metadata-commons-imaging
----

==== Windows

On Windows (With the latest version of GraalVM), things got super-easy, just compile and run:

[source,bash]
----
image-metadata-commons-imaging/target/image-metadata-commons-imaging.exe
----

==== Linux

Once compiled you need to provide the `java.home` to the executable. This is needed to load the font sub-system. However,
the contents of `java.home` just needs one file `fontconfig.properties` with the contents:

[source,properties]
----
version=1
sequence.allfonts=default
----

[NOTE]
====
This was previously for the Windows executable as well but seems to work now without. So maybe this may not be needed
eventually. For more information see:

- https://github.com/adoptium/temurin-build/issues/693
- https://www.jianshu.com/p/a53ae350f845?v=1669292961020
====

For simplicity, the project provides a `fontconfig.properties` file. Therefore, once the binary is created, launch the
application using:

[source,bash]
----
./image-metadata-commons-imaging/target/image-metadata-commons-imaging \
-Djava.home=iage-metadata-commons-imaging/src/lib/
----

[NOTE]
====
You may also need to install `libfreetype6-dev`:

[source,bash]
----
apt install gcc zlib1g-dev build-essential libfreetype6-dev
----

See also: https://github.com/graalvm/mandrel/issues/189
====

=== Docker

_image-metadata-commons-imaging_ uses fonts, which on Linux requires `fontconfig` to be installed. That's why I use
`paketobuildpacks/builder:full` and not the default `paketobuildpacks/builder:tiny`.

IMPORTANT: Please make sure your Docker daemon is running.

[source,bash,indent=0,subs=attributes]
----
./mvnw -Pnative spring-boot:build-image -pl :image-metadata-commons-imaging
docker run --rm -p 8080:8080 docker.io/library/image-metadata-commons-imaging:{current-version}
----

== Development

When adding functionality, it may be necessary to run the *native-image-agent*. First build the jar with the
`native` Maven profile:

[source,bash,indent=0,subs=attributes]
----
./mvnw clean package -Pnative
----

[source,bash,indent=0,subs=attributes]
----
java -Dspring.aot.enabled=true \
-agentlib:native-image-agent=config-output-dir=image-metadata-commons-imaging/src/main/resources/META-INF/native-image \
-jar image-metadata-commons-imaging/target/image-metadata-commons-imaging-{current-version}.jar
----

== License

Image Metadata is licensed under the link:LICENSE[Apache License] (ASL), Version 2.0.