Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support metadata from Multi-Picture Format images #654

Open
hakanai opened this issue Feb 5, 2024 · 0 comments
Open

Support metadata from Multi-Picture Format images #654

hakanai opened this issue Feb 5, 2024 · 0 comments

Comments

@hakanai
Copy link

hakanai commented Feb 5, 2024

Multi-Picture Format is a standard by CIPA (Camera and Imaging Products Association) whereby multiple images are stored into a single file. Files of this sort are typically called "Multi-Picture Objects", and come with the file extension .mpo. They were commonly used for photos taken on the 3DS, but it looks like some Sony cameras with stereographic support have adopted the format as well.

There are a couple of samples here.

The structure is basically "EXIF with a different preamble", and it's stored in the APP2 segment, so you can read the values out like this:
(Apologies for it being Kotlin, that's just what I was using to do experiments... it isn't too different from Java anyway.)

class MultiPictureFormatReader : JpegSegmentMetadataReader {
    val MPF_PREAMBLE: String = "MPF\u0000"

    override fun getSegmentTypes(): Iterable<JpegSegmentType> {
        return listOf(JpegSegmentType.APP2)
    }

    override fun readJpegSegments(segments: Iterable<ByteArray>, metadata: Metadata, segmentType: JpegSegmentType) {
        assert(segmentType == JpegSegmentType.APP2)

        for (segmentBytes in segments) {
            // Segment must have the expected preamble
            if (startsWithMpfPreamble(segmentBytes)) {
                ExifReader().extract(
                    ByteArrayReader(segmentBytes),
                    metadata,
                    MPF_PREAMBLE.length,
                    null
                )
            }
        }
    }

    private fun startsWithMpfPreamble(bytes: ByteArray): Boolean {
        return bytes.size >= MPF_PREAMBLE.length &&
                String(bytes, 0, MPF_PREAMBLE.length) == MPF_PREAMBLE
    }
}

This reads the right tag IDs and the right values, but the names come out wrong.

The tag name mappings look like this:

class MPIndexIFDDirectory : ExifDirectoryBase() {
    private val _tagNameMap = hashMapOf(
        0xB000 to "MP Format Version Number",
        0xB001 to "Number of Images",
        0xB002 to "MP Entry",
        0xB003 to "Individual Image Unique ID List",
        0xB004 to "Total Number of Captured Frames",
    )

    override fun getName() = "MP Index IFD"

    override fun getTagNameMap() = _tagNameMap
}
class MPAttributeIFDDirectory : ExifDirectoryBase() {
    private val _tagNameMap = hashMapOf(
        0xB000 to "MP Format Version",
        0xB101 to "MP Individual Image Number",
        0xB201 to "Panorama Scanning Orientation",
        0xB202 to "Panorama Horizontal Overlap",
        0xB203 to "Panorama Vertical Overlap",
        0xB204 to "Base Viewpoint Number",
        0xB205 to "Convergence Angle",
        0xB206 to "Baseline Length",
        0xB207 to "Divergence Angle",
        0xB208 to "Horizontal Axis Distance",
        0xB209 to "Vertical Axis Distance",
        0xB20A to "Collimation Axis Distance",
        0xB20B to "Yaw Angle",
        0xB20C to "Pitch Angle",
        0xB20D to "Roll Angle",
    )

    override fun getName() = "MP Attribute IFD"

    override fun getTagNameMap() = _tagNameMap
}

I haven't figured out yet how to connect the two bits together to get the right directory to be used for the right IFD.

For the first image in the file, the first IFD is the "MP Index IFD" and the second is the "MP Attribute IFD".
For subsequent images in the same file, the first and only IFD is the "MP Attribute IFD".

As far as the overall file structure, the multiple files are stacked one after another in the file. The samples I've seen so far also seem to insert padding between the images to align the images to start at a multiple of 0x1000 bytes, but I'm not sure this is required.

Reading the multiple images out of the file is sort of out of scope for this ticket anyway, but if an InputStream for one of the subsequent images were passed into readMetadata (either by manually splitting the file up, or by programmatically positioning an InputStream at the right offset), I'd expect the "MP Attribute IFD" to be extractable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant