7

Google Camera の新しい深度マップ生成機能を利用することになっている Android アプリケーションを開発しています。

ここで使用するメタデータは基本的に Google が説明しています

ほとんどのメタデータにアクセスできますが、残念ながら最も重要なデータは extendedXmp としてエンコードされており、それを正しく解析するための XMP 解析ライブラリを取得できません!

Commons-Imaging、metadata-extractor、そして最近では Adob​​e XMPCore を試しました

XMPCore は拡張バージョンを処理できる可能性がありますが、生の XMP データが渡されることを前提として、JPG ファイルからデータを解析する方法を説明するドキュメントはありません。

JPGファイルの拡張部分を含むXMP解析の正しい実装はありますか、それとも何か間違っていますか?

これが私の試みです:

Commons-Imaging の場合:

                try {
                    String imageParser = new JpegImageParser().getXmpXml(new ByteSourceInputStream(imageStream, "img.jpg"), new HashMap<String, Object>());

                    Log.v(TAG, imageParser);

                } catch (ImageReadException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }

メタデータ抽出機能付き

                Metadata metadata = ImageMetadataReader.readMetadata(
                        new BufferedInputStream(imageStream), false);


                XmpDirectory xmp = metadata
                        .getDirectory(XmpDirectory.class);
                XMPMeta xmpMeta = xmp.getXMPMeta();



                String uri = "http://ns.google.com/photos/1.0/depthmap/";

                Log.v(TAG, xmpMeta.doesPropertyExist(uri, "GDepth:Format") + " " );

                try {
                    XMPProperty hasExtendedXMP = xmpMeta.getProperty("http://ns.adobe.com/xmp/note/", "xmpNote:HasExtendedXMP");

                    Log.v(TAG, hasExtendedXMP.getValue().toString() + " " + new String(Base64.decode(hasExtendedXMP.getValue().toString(), Base64.DEFAULT)));

                } catch (XMPException e) {
                    e.printStackTrace();
                }
4

3 に答える 3

8

当初、Adobe は、XMP データの長さが 1 つの JPEG セグメント (約 64K) の制限を超えるとは予想しておらず、XMP 仕様では、XMP データが 1 つに収まる必要があると述べていました。その後、単一の JPEG APP1 セグメントでは XMP データを保持するのに十分な大きさではないことが判明したため、XMP データ全体に対して複数の APP1 セグメントを使用できるように仕様を変更しました。データは、標準 XMP と ExtendedXMP の 2 つの部分に分割されます。標準 XMP 部分は、パッケージ ラッパーを持つ「通常の」XMP 構造ですが、ExtendedXMP 部分にはパッケージ ラッパーがありません。ExtendedXMP データは、複数の APP1 に収まるようにさらに分割できます。

次の引用は、JPEG APP1 としての ExtendedXMP チャンクに関する Adob​​e XMP 仕様パート 3 からのものです。

各チャンクは、個別の APP1 マーカー セグメント内の JPEG ファイルに書き込まれます。各 ExtendedXMP マーカー セグメントには以下が含まれます。

  • 「 http://ns.adobe.com/xmp/extension/ 」の null で終わる署名文字列。
  • 32 バイトの ASCII 16 進文字列として格納された 128 ビットの GUID、大文字の AF、null 終端なし。GUID は、完全な ExtendedXMP シリアル化の 128 ビット MD5 ダイジェストです。
  • 32 ビットの符号なし整数としての ExtendedXMP シリアル化の全長
  • 32 ビット符号なし整数としてのこの部分のオフセット。
  • ExtendedXMP の一部

ExtendedXMP データの ID として null で終わる文字列の他に、標準の XMP 部分にあるものと同じ値である GUID もあることがわかります。オフセットは、ExtendedXMP のさまざまな部分を結合するために使用されます。そのため、ExtendedXMP APP1 のシーケンスが正しくない場合もあります。次に、実際のデータ部分が来ます。これが、@Matt の回答に文字列を修正する方法が必要な理由です。もう 1 つの値があります。ExtendedXMP シリアライゼーションの完全な長さです。これは、データの整合性をチェックし、データを結合するためのバッファー サイズを提供するという 2 つの目的を果たします。

ExtendedXMP セグメントが見つかったら、現在のデータを他の ExtendedXMP セグメントと結合し、最終的に ExtendedXMP データ全体を取得する必要があります。次に、2 つの XML ツリーを結合して (標準の XMP 部分から GUID も削除します)、XMP データ全体を取得します。

XMP と ExtendedXMP を抽出および挿入できるライブラリicafeを Java で作成しました。ExtendedXMP のユースケースの 1 つは、Google の深度マップ データです。これは、実際にはメタデータとして実際の画像内に隠されているグレースケール画像であり、JPEG の場合は XMP データとして隠されています。深度マップ画像は、たとえば元の画像をぼかすために使用できます。通常、深度マップ データは大きく、標準および拡張 XMP 部分に分割する必要があります。データ全体は Base64 でエンコードされており、PNG 形式にすることができます。

以下は、イメージと抽出された深度マップの例です。

ここに画像の説明を入力

元画像はこちらから。

注: 最近、JPEG XMP データに埋め込まれた画像と音声の両方を利用できる Google Cardboard Camera アプリについて説明している別のWeb サイトを見つけました。ICAFEは、そのような画像からの画像と音声の抽出をサポートするようになりました。使用例は、次の呼び出しでここにありますJPEGTweaker.extractDepthMap()

これは、Google Cardboard Camera アプリについて話している Web サイトの元の画像から ICAFE によって抽出された画像です。

ここに画像の説明を入力

残念ながら、ここに MP4 オーディオを挿入する方法が見つかりません。

于 2015-03-01T01:02:26.753 に答える
3

XMP に保存されている Picasa の顔データも、metadata-extractorライブラリと XMP プロパティを介したイテレータを使用して読み取ることができました。

try {
    Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
    XmpDirectory xmpDirectory = metadata.getDirectory(XmpDirectory.class);
    XMPMeta xmpMeta = xmpDirectory.getXMPMeta();
    XMPIterator itr = xmpMeta.iterator();
    while (itr.hasNext()) {
        XMPPropertyInfo pi = (XMPPropertyInfo) itr.next();
        if (pi != null && pi.getPath() != null) {
            if ((pi.getPath().endsWith("stArea:w")) || (pi.getPath().endsWith("mwg-rs:Name")) || (pi.getPath().endsWith("stArea:h")))
                System.out.println(pi.getValue().toString());
        }
    }
} catch (final NullPointerException npe) {
  // ignore
}
于 2014-05-11T17:50:06.673 に答える