Giter Club home page Giter Club logo

kim's Introduction

Kim - Kotlin Image Metadata

Kotlin JVM Android iOS Windows Linux macOS JS WASM Coverage

Kim is a Kotlin Multiplatform library for reading and writing image metadata.

It's part of Ashampoo Photo Organizer.

Features

  • JPG: Read & Write EXIF, IPTC & XMP
  • PNG: Read & Write eXIf chunk & XMP
    • Also read non-standard EXIF & IPTC from tEXt/zTXt chunk
  • WebP: Read & Write EXIF & XMP
  • HEIC / AVIF: Read EXIF & XMP
  • JXL: Read & Write EXIF & XMP of uncompressed files
  • TIFF / RAW: Read EXIF & XMP
    • Full support for Adobe DNG, Canon CR2 & Fujifilm RAF
    • Support for Nikon NEF, Sony ARW & Olympus ORF without lens info
    • Support for Panasonic RW2 without lens info and image size
    • API for preview image extraction of DNG, CR2, RAF, NEF, ARW & RW2
  • Handling of XMP content through XMP Core for Kotlin Multiplatform
  • Convenient Kim.update() API to perform updates to the relevant places
    • JPG: Lossless rotation by modifying only one byte (where present)

The future development of features on our part is driven entirely by the needs of Ashampoo Photo Organizer, which, in turn, is driven by user community feedback.

Installation

implementation("com.ashampoo:kim:0.18.4")

For the targets wasmJs & js you also need to specify this:

implementation(npm("pako", "2.1.0"))

Sample usages

Read metadata

Kim.readMetadata() takes kotlin.ByteArray on all platforms and depending on the platform also kotlinx.io.files.Path, Ktor ByteReadPacket & ByteReadChannel, java.io.File, java.io.InputStream, NSData and String paths.

val bytes: ByteArray = loadBytes()

val metadata = Kim.readMetadata(bytes)

/* ImageMetadata has a proper toString() similar to the output of ExifTool */
println(metadata)

val orientation = metadata.findShortValue(TiffTag.TIFF_TAG_ORIENTATION)

println("Orientation: $orientation")

val takenDate = metadata.findStringValue(ExifTag.EXIF_TAG_DATE_TIME_ORIGINAL)

println("Taken date: $takenDate")

Create high level summary object

This creates an instance of PhotoMetadata. It contains the following:

  • Image size
  • Orientation
  • Taken date
  • GPS coordinates
  • Camera make & model
  • Lens make & model
  • ISO, Exposure time, F-Number, Focal length
  • Rating
  • Keywords
  • Faces (XMP-mwg-rs regions, used by Picasa and others)
  • Persons in image
val bytes: ByteArray = loadBytes()

val photoMetadata = Kim.readMetadata(bytes).convertToPhotoMetadata()

Change orientation using low level API

val inputFile = File("myphoto.jpg")
val outputFile = File("myphoto_changed.jpg")

val metadata = Kim.readMetadata(inputFile)

val outputSet: TiffOutputSet = metadata.exif?.createOutputSet() ?: TiffOutputSet()

val rootDirectory = outputSet.getOrCreateRootDirectory()

rootDirectory.removeField(TiffTag.TIFF_TAG_ORIENTATION)
rootDirectory.add(TiffTag.TIFF_TAG_ORIENTATION, 8)

OutputStreamByteWriter(outputFile.outputStream()).use { outputStreamByteWriter ->

    JpegRewriter.updateExifMetadataLossless(
        byteReader = JvmInputStreamByteReader(inputFile.inputStream(), inputFile.length()),
        byteWriter = outputStreamByteWriter,
        outputSet = outputSet
    )
}

See the example project for more details.

Change orientation using Kim.update() API

val bytes: ByteArray = loadBytes()

val newBytes = Kim.update(
    bytes = bytes,
    update = MetadataUpdate.Orientation(TiffOrientation.ROTATE_RIGHT)
)

See AbstractUpdaterTest for more samples.

Update thumbnail using Kim.update() API

val bytes: ByteArray = loadBytes()
val thumbnailBytes: ByteArray = loadThumbnailBytes()

val newBytes = Kim.updateThumbnail(
    bytes = bytes,
    thumbnailBytes = thumbnailBytes
)

Using Java

See the Java example project how to use Kim in Java projects.

Limitations

  • Inability to update EXIF, IPTC and XMP in JPG files simultaneously.
  • Does not read the image size and orientation for HEIC, AVIF & JPEG XL.
  • Does not read brotli compressed metadata of JPEG XL due to missing brotli KMP libs.
  • MakerNote support is experimental and limited.
    • Can't extract preview image of ORF as offsets are burried into MakerNote.
    • Can't identify lens info of NEF, ARW, RW2 & ORF because this is constructed from MakerNote fields.
    • Missing image size for RW2 as this is also burried in MakerNotes.
  • There is right now no convienient tooling for GeoTiff like there is for GPS.

Regarding HEIC & AVIF metadata

In the processing of HEIC and AVIF files, we handle them as standard ISOBMFF-based files, adhering rigorously to the EIC/ISO 14496-12 specification. To preempt potential legal issues, we intentionally omit certain boxes outlined in the HEIC specification, notably the image size ("ispe") and image rotation ("irot") boxes. This approach extends to AVIF images, as they repurpose the same boxes.

Contributions

Contributions to Ashampoo Kim are welcome! If you encounter any issues, have suggestions for improvements, or would like to contribute new features, please feel free to submit a pull request.

Acknowledgements

License

This code is under the Apache License 2.0.

See the NOTICE.txt file for required notices and attributions.

kim's People

Contributors

garydgregory avatar stefanoltmann avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

kim's Issues

WebP support

KIM cannot extract image size from WEBP images. The following test fragment fails for the attached test image.

val metadata = Kim.readMetadata(encodedBytes)
assertNotNull(metadata)
assertNotNull(metadata.imageSize)

skia-generated-test-image.webp.zip

WASM support

Are you planning to support the new WASM target in the near future? I am just evaluating the feasibility of converting my current project to a WASM browser project.

Missing support for iosX64

Hi,
when I try to build a new project of mine I get the following error.

Execution failed for task ':apptools:apptools-core:transformCommonMainDependenciesMetadata'.
> Could not resolve all files for configuration ':apptools:apptools-core:iosX64CompilationDependenciesMetadata'.
   > Could not resolve com.ashampoo:kim:0.13.
     Required by:

In contrast to my other projects this one is also intended to run on iOS. That's probably the reason why I did not notice this before. This target is needed to run on the simulator on X64 macOS hardware.

Write XMP face tags

We need to implement this:

is MetadataUpdate.Faces -> {
error("Writing of faces is not supported right now.")
// // TODO Write faces
// if (update.faces.isNotEmpty()) {
//
// /* Delete existing entries, if any */
// xmpMeta.deleteProperty(NS_MWG_RS, "Regions")
//
// xmpMeta.setStructField(
// NS_MWG_RS, "Regions/mwg-rs:AppliedToDimensions",
// XMPConst.TYPE_DIMENSIONS, "w",
// xmpMeta.widthPx.toString()
// )
//
// xmpMeta.setStructField(
// NS_MWG_RS, "Regions/mwg-rs:AppliedToDimensions",
// XMPConst.TYPE_DIMENSIONS, "h",
// xmpMeta.heightPx.toString()
// )
//
// xmpMeta.setStructField(
// NS_MWG_RS, "Regions/mwg-rs:AppliedToDimensions",
// XMPConst.TYPE_DIMENSIONS, "unit", "pixel"
// )
//
// xmpMeta.setStructField(
// NS_MWG_RS, "Regions", NS_MWG_RS, "RegionList", null,
// orderedArrayOptions
// )
//
// // How to proceed further?
// }
}

Android phones do not give TiffTag for rotation

I have rotated pictures in regular Compose/Kotlin before, and gotten the Exif orientation value out, but there is nothing here, so this is pretty useless for rotating pictures the right way up. This happens on using webcamera on emulator as well as a real Sony Xperia 1V device. On iPad I get orientation 1 out, so that works, but I am depending on getting it out on all devices...

Any tips are welcome.

Support lower JDK versions

This library looks really useful! Unfortunately, I got this error:

Exception in thread "main" java.lang.UnsupportedClassVersionError: com/ashampoo/kim/Kim has been compiled by a more recent version of the Java Runtime (class file version 62.0), this version of the Java Runtime only recognizes class file versions up to 61.0

Class file version 62.0 corresponds to Java version 18. Why support 18 but not 17, given that 17 is LTS?

I will eventually one day upgrade my JDK version, so I am unsure if it would be worth it to support lower versions of JDK just for me for the time being. But on the other hand, I cannot imagine I am the only one still using JDK 17?

Add lens info for NEF, ARW, RW2 & ORF

The formats NEF, ARW, RW2 & ORF are missing the lens information, because it's not written to standard EXIF fields and must be constructed from MakerNote data.

These documentations describe how to do so:

This document provides an overview:
https://exiftool.org/makernote_types.html

metadata-extractor has logic to identify the kind of MakerNote that is present:
https://github.com/drewnoakes/metadata-extractor/blob/67dfddd72e3cfbb7bf802a672eacc70b42fba601/Source/com/drew/metadata/exif/ExifTiffHandler.java#L426

Extract preview image of ORF files

Olympus ORF files store the offset to the preview image within their MakerNotes. To access this information, we must first interpret the MakerNotes data.

Android version lower than 31 failed to extract metadata

Hi, I'm Lucas, developper on Android - Kotlin, I'm using your plugin to extract metadata for my application and we are working with a lots of devices from 24 to 34 and we have a problem to extract metadata from device under 31.

Your minSdk = 23

Our MinSdk : 24
Our Kim version : v0.9.3

To reproduce I've just create an device emulator in Android Studio using an Pixel 7 on android 29 and I receive an error :

The line use in my kotlin :
val metadata = Kim.readMetadata(file.absolutePath) ?: return null

The error :
Caused by: java.lang.NoSuchMethodError: No virtual method readNBytes(I)[B in class Ljava/io/InputStream; or its super classes (declaration of 'java.io.InputStream' appears in /apex/com.android.runtime/javalib/core-oj.jar)

Can you help ? Do you have some information ?

Thanks Lucas ๐Ÿ˜„

Manifest :
compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 }

All the log :
2024-01-17 10:33:38.778 5579-5579 System.err com.syslor.easyscan.preprod.debug W com.ashampoo.kim.common.ImageReadException: Failed to read image. 2024-01-17 10:33:38.782 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.ashampoo.kim.Kim.readMetadata(Kim.kt:262) 2024-01-17 10:33:38.782 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.ashampoo.kim.JvmKimExtensionsKt.readMetadata(JvmKimExtensions.kt:26) 2024-01-17 10:33:38.782 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.ashampoo.kim.JvmKimExtensionsKt.readMetadata(JvmKimExtensions.kt:37) 2024-01-17 10:33:38.788 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.ashampoo.kim.JvmKimExtensionsKt.readMetadata(JvmKimExtensions.kt:30) 2024-01-17 10:33:38.788 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.syslor.easyscan.utils.MapUtils.extractOrthomosaicMetadata(MapUtils.kt:117) 2024-01-17 10:33:38.788 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.syslor.easyscan.presentation.viewmodel.MapViewEasyScanViewModel.proceedFeatureToOrthomosaicData(MapViewEasyScanViewModel.kt:282) 2024-01-17 10:33:38.791 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.syslor.easyscan.presentation.viewmodel.MapViewEasyScanViewModel.access$proceedFeatureToOrthomosaicData(MapViewEasyScanViewModel.kt:51) 2024-01-17 10:33:38.791 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.syslor.easyscan.presentation.viewmodel.MapViewEasyScanViewModel$proceedFeatureToOrthomosaicData$1.invokeSuspend(Unknown Source:22) 2024-01-17 10:33:38.791 5579-5579 System.err com.syslor.easyscan.preprod.debug W at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 2024-01-17 10:33:38.791 5579-5579 System.err com.syslor.easyscan.preprod.debug W at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) 2024-01-17 10:33:38.797 5579-5579 System.err com.syslor.easyscan.preprod.debug W at android.os.Handler.handleCallback(Handler.java:883) 2024-01-17 10:33:38.797 5579-5579 System.err com.syslor.easyscan.preprod.debug W at android.os.Handler.dispatchMessage(Handler.java:100) 2024-01-17 10:33:38.797 5579-5579 System.err com.syslor.easyscan.preprod.debug W at android.os.Looper.loop(Looper.java:214) 2024-01-17 10:33:38.797 5579-5579 System.err com.syslor.easyscan.preprod.debug W at android.app.ActivityThread.main(ActivityThread.java:7356) 2024-01-17 10:33:38.797 5579-5579 System.err com.syslor.easyscan.preprod.debug W at java.lang.reflect.Method.invoke(Native Method) 2024-01-17 10:33:38.801 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 2024-01-17 10:33:38.801 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 2024-01-17 10:33:38.811 5579-5579 System.err com.syslor.easyscan.preprod.debug W Caused by: java.lang.NoSuchMethodError: No virtual method readNBytes(I)[B in class Ljava/io/InputStream; or its super classes (declaration of 'java.io.InputStream' appears in /apex/com.android.runtime/javalib/core-oj.jar) 2024-01-17 10:33:38.811 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.ashampoo.kim.input.JvmInputStreamByteReader.readBytes(JvmInputStreamByteReader.kt:36) 2024-01-17 10:33:38.811 5579-5579 System.err com.syslor.easyscan.preprod.debug W at com.ashampoo.kim.Kim.readMetadata(Kim.kt:78) 2024-01-17 10:33:38.811 5579-5579 System.err com.syslor.easyscan.preprod.debug W ... 16 more

Better handling for broken files

In the test data, we have included photo_21.jpg, which has a corrupted IFD0.

Currently, the library fails to read the metadata because of this corruption. However, other tools like ExifTool can gracefully handle such issues by ignoring the broken parts of EXIF and reading the rest of the data.

To address this problem in our library, we should implement a similar approach. We need to modify our code to ignore the corrupted sections of the EXIF metadata and continue reading the valid parts.

Once we've implemented this solution, we can uncomment the code that skips the broken file since it will no longer be necessary after the fix.

Why is minsdk 29? Couldn't it be lower?

Hi
Thanks for nice library, I was thinking about using it, but then minsdk version is set to 29.
And I'm not sure which part forces it to be so high? I've checked it out and compile with minsdk 23 and it compiles and jvm tests pass. Could you please reduce version requirement? since otherwise projects including it won't compile with error:

Execution failed for task ':androidApp:processDebugMainManifest'.
> Manifest merger failed : uses-sdk:minSdkVersion 21 cannot be smaller than version 29 declared in library [com.ashampoo:kim-android:0.1.1]

JPG: Update EXIF, IPTC and XMP simultaneously

Currently, the update processes for EXIF, IPTC, and XMP metadata are handled by distinct methods, which have been inherited from Apache Commons Imaging. These methods are utilized in the JpegUpdater class.

To improve the code and streamline the updating process, we should refactor JpegRewriter to support a single-shot update for all three metadata locations (EXIF, IPTC, and XMP). By doing so, we can enhance the efficiency and maintainability of the codebase.

Get corners of geotiff

Is there a way to get the corners of the geo tiff from the metadata? I see location but I assuming that is the center point.

PNG: Support date field used by Windows

If a date is configured in Windows Explorer or Microsoft Photo, the system does not generate EXIF or XMP metadata for it. Instead, it creates a tEXt chunk with the label Creation Time.

To enhance compatibility with Windows, the library should be capable of both reading and writing this specific field.

Users may anticipate that Ashampoo Photos will showcase the date taken as seen in Windows Explorer. Any disparity might lead them to believe that Photos is malfunctioning, especially considering that even Windows 11 does not accurately display PNG EXIF data.

Using https://stefan-oltmann.de/exif-viewer/ the output for the relevant part of the test.png is this:

Bildschirmfoto 2024-01-16 um 21 15 29

Detect unused space and use it

A Fuji X-T4's out-of-camera JPEG is constrained in its ability to accommodate additional bytes/metadata, primarily due to a substantial portion of unused space immediately following the thumbnail. This unused space might serve as a placeholder.

ExifTool can detect this, maybe due to interpretation of MakerNotes. We should do something similar.

exiftool_htmldump

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.