36

背景: メッセンジャー プログラム用のカメラ アプリを作成しています。キャプチャしたイメージを永続ディスクに保存できません。カメラはすべての向きをサポートする必要があります。私の実装は、おなじみの Surfaceview の例の実装です。Display クラスを使用して向きを検出し、それに応じてカメラを回転させます。takePicture jpeg コールバックでは、縦横比の問題を回避するために、byte[] からビットマップを作成します。カメラ API: クロス デバイスの問題

問題の説明: 一部のデバイスでは、ROTATION_270 (時計回りに 90 度回転したデバイス) で作成されたビットマップが上下逆になります。これまでのところ、それはサムスンのようです。おそらくカメラが逆にはんだ付けされているか、何か影響があるのではないかと推測できますが、それはあちらこちらでもありません。ビットマップが横向きであるかどうかを確認できますが、寸法によって上下が逆になっているかどうかを論理的に確認することはできないため、EXIF データにアクセスする必要があります。

Android はこのhttp://developer.android.com/reference/android/media/ExifInterface.htmlのパーサーを提供しますが、残念ながら、ファイルを受け入れる単一のコンストラクターがあります... 私は持っておらず、望んでいません. 直感的にバイト配列のコンストラクターを作成できますが、ネイティブコードへの呼び出しを考えると、それは非常に苦痛に思えますhttp://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2 .1_r1/android/media/ExifInterface.java

私の質問には2つの部分があります:

  1. byte[] 配列に完全な EXIF jpeg ヘッダー データがそのまま含まれているのか、それとも BitmapFactory.decode(...) / BitmapFactory.compress(...) を介したパスが何らかの方法で追加されているのか、誰かが知っていますか?

  2. この EXIF データがバイト配列に存在する場合、信頼できる方法で方向情報を解析するにはどうすればよいですか?

2012 年 10 月 18 日を編集

以下の pcans の回答には、私の質問のパート 2 が含まれます。彼の回答の下のコメントで指摘したように、そのパーサーを使用したい場合は、ソースをプロジェクトに組み込む必要があります。そのリンクされた SO 投稿で言及されている変更は既に行われており、ここに再投稿されています: https://github.com/strangecargo/metadata-extractor

新しいバージョンのmetadata-extractorは変更なしで Android で直接動作し、Maven を介して利用できることに注意してください。

ただし、パート 1 に関しては、takePicture から取得したバイト配列を使用してパーサーを実行すると、パーサーから 0 個のタグが返されます。必要なデータがバイト配列にないのではないかと心配しています。これについては引き続き調査しますが、さらなる洞察を歓迎します。

4

8 に答える 8

34

Drew NoakesによるJavaのメタデータ抽出ライブラリのバージョン 2.9.1を使用して、画像からメタデータ/EXIF を読み取るにはbyte[]( に便利Camera.takePicture()) :

try
{
    // Extract metadata.
    Metadata metadata = ImageMetadataReader.readMetadata(new BufferedInputStream(new ByteArrayInputStream(imageData)), imageData.length);

    // Log each directory.
    for(Directory directory : metadata.getDirectories())
    {
        Log.d("LOG", "Directory: " + directory.getName());

        // Log all errors.
        for(String error : directory.getErrors())
        {
            Log.d("LOG", "> error: " + error);
        }

        // Log all tags.
        for(Tag tag : directory.getTags())
        {
            Log.d("LOG", "> tag: " + tag.getTagName() + " = " + tag.getDescription());
        }
    }
}
catch(Exception e)
{
    // TODO: handle exception
}

画像の EXIF方向(サムネイルの方向ではありません) を読み取るには:

try
{
    // Get the EXIF orientation.
    final ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
    if(exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION))
    {
        final int exifOrientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);

        /* Work on exifOrientation */
    }
    else
    {
        /* Not found */
    }
}
catch(Exception e)
{
    // TODO: handle exception
}

方向は 1 ~ 8です。こちらこちらこちら、またはこちらを参照してください。


EXIF 方向に基づいてビットマップを変換するには:

try
{
    final Matrix bitmapMatrix = new Matrix();
    switch(exifOrientation)
    {
        case 1:                                                                                     break;  // top left
        case 2:                                                 bitmapMatrix.postScale(-1, 1);      break;  // top right
        case 3:         bitmapMatrix.postRotate(180);                                               break;  // bottom right
        case 4:         bitmapMatrix.postRotate(180);           bitmapMatrix.postScale(-1, 1);      break;  // bottom left
        case 5:         bitmapMatrix.postRotate(90);            bitmapMatrix.postScale(-1, 1);      break;  // left top
        case 6:         bitmapMatrix.postRotate(90);                                                break;  // right top
        case 7:         bitmapMatrix.postRotate(270);           bitmapMatrix.postScale(-1, 1);      break;  // right bottom
        case 8:         bitmapMatrix.postRotate(270);                                               break;  // left bottom
        default:                                                                                    break;  // Unknown
    }

    // Create new bitmap.
    final Bitmap transformedBitmap = Bitmap.createBitmap(imageBitmap, 0, 0, imageBitmap.getWidth(), imageBitmap.getHeight(), bitmapMatrix, false);
}
catch(Exception e)
{
    // TODO: handle exception
}
于 2013-01-08T03:41:29.087 に答える
20

悪いニュース:

Stream残念ながら、Android Apiでは、 からのみexif データを読み取ることができませんFile
ExifInterfaceには、InputStream. したがって、jpeg コンテンツを自分で解析する必要があります。

良いニュース:

このための API は純粋な Java に存在します。これを使用できます: https://drewnoakes.com/code/exif/
これはオープン ソースであり、Apache License 2 の下で公開され、Maven パッケージとして利用できます。

次のコンストラクタがありますInputStreampublic ExifReader(java.io.InputStream is)

次のような を使用して、InputStream裏打ちされたを構築できます。byte[]ByteArrayInputStream

InputStream is = new ByteArrayInputStream(decodedBytes);
于 2012-10-18T10:18:56.827 に答える
4

私の編集と pcans の提案を使用して、画像データを取得しましたが、期待したものではありませんでした。具体的には、すべてのデバイスが方向を示すわけではありません。このパスをたどる場合は、

  • 私が指摘する「Android 修正済み」ExifReader ライブラリは、実際には編集された 2.3.1 であり、これは数リリース前のものです。ウェブサイトとソースの新しい例は、彼が API を大幅に変更した最新の 2.6.x に関するものです。2.3.1 インターフェースを使用すると、次のようにして byte[] からすべての EXIF データをダンプできます。

            Metadata header;    
            try {
                ByteArrayInputStream bais= new ByteArrayInputStream(data);
                ExifReader reader = new ExifReader(bais);
                header = reader.extract();
                Iterator<Directory> iter = header.getDirectoryIterator();
                while(iter.hasNext()){
                   Directory d = iter.next();
                   Iterator<Tag> iterTag = d.getTagIterator();
                   while(iterTag.hasNext()){
                      Tag t = iterTag.next();
                      Log.e("DEBUG", "TAG: " + t.getTagName() + " : " + t.getDescription());
                   }
                }
            } catch (JpegProcessingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (MetadataException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    

数値のタグ値が必要な場合は、単に置き換えます

t.getDescription()

d.getInt(t.getTagType())
  • ExifReader には byte[] を使用したコンストラクターがありますが、データ配列で直接使用しようとすると、返されたディレクトリのタグに到達するため、期待を誤解したに違いありません。

答えに関する限り、私は本当に多くを追加しなかったので、pcansの答えを受け入れています。

于 2012-10-18T20:54:40.793 に答える
3

Glide ライブラリを使用している場合は、InputStream から Exif 方向を取得できます。

InputStream is=getActivity().getContentResolver().openInputStream(originalUri);
int orientation=new ImageHeaderParser(is).getOrientation();
于 2015-12-01T00:05:44.547 に答える
0

content://タイプが の場合Uri、Android は を介し​​て API を提供し、ContentResolver外部ライブラリを使用する必要はありません。

public static int getExifAngle(Context context, Uri uri) {
    int angle = 0;
    Cursor c = context.getContentResolver().query(uri,
            new String[] { MediaStore.Images.ImageColumns.ORIENTATION },
            null,
            null,
            null);

    if (c != null && c.moveToFirst()) {
        int col = c.getColumnIndex( MediaStore.Images.ImageColumns.ORIENTATION );
        angle = c.getInt(col);
        c.close();
    }
    return angle;
}

MediaStore.Images.ImageColumns緯度や経度など、 で見つかった他の値を読み取ることもできます。

これは現在 Uris では機能しませんfile:///が、簡単に微調整できます。

于 2016-01-08T23:47:49.330 に答える