在某些 Gingerbread 设备上,使用 ACTION_IMAGE_CAPTURE 拍摄的图像对于 ExifInterface.TAG_ORIENTATION 始终返回 1

发布于 2024-12-20 08:41:27 字数 991 浏览 5 评论 0原文

我在使用 ACTION_IMAGE_CAPTURE 活动时遇到方向问题。我使用了TAG_ORIENTATION,以便相应地旋转图片。但现在我们发现在一些较新的设备上这不起作用。事实上,它对于所有方向都返回 1。

以下是我们观察到这一情况的设备列表;

  • Samsung Infuse 4G (2.3.3)
  • Samsung Galaxy SII X (2.3.5)
  • Sony Xperia Arc (2.3.3)

有趣的是,一旦此图像成为图库,它就会正确显示,如果我选择它,TAG_ORIENTATION 已正确填充。因此,OS 会以某种方式正确填充此信息,但不会在 ActivityResult 上填充。

确定方向最可靠的方法是什么?另一个问题上的有人建议比较高度和宽度,但是当获得这些时,它们会根据方向正确切换(另一个谜团)

编辑:似乎这可能与另一个错误有关,其中操作系统复制了在图库中拍摄的图像(这只是应该将图像保存在我们指定的 URL 中),问题是图库中的该图像具有 ORIENTATION 信息,而指定位置中的图像则没有。

这就是错误; http://code.google.com/p/android/issues/detail?id=19268< /a>

EDIT-2: 我已经向 Android 提交了一个新错误。我很确定这是与上述错误相关的操作系统错误。 http://code.google.com/p/android/issues/detail?id=22822< /a>

I had the orientation issue when working with ACTION_IMAGE_CAPTURE activity. I have used the TAG_ORIENTATION so that I would rotate the picture accordingly. But now we found that on some newer devices this doesn't work. In fact it returns 1 for all orientations.

Here's the list of devices we observed this on;

  • Samsung Infuse 4G (2.3.3)
  • Samsung Galaxy SII X (2.3.5)
  • Sony Xperia Arc (2.3.3)

Interesting thing is that once this image is the gallery it shows up properly and if I select it, the TAG_ORIENTATION is populated properly. So somehow the OS fills this information properly but not on ActivityResult.

What's the most reliable way to figure the orientation? Someone on another question suggested comparing height and width but when getting these, they are properly switched based on orientation (another mystery)

EDIT: It seems that this could be connected to another bug where the OS duplicates the image taken in the gallery (it's only supposed to save the image in the URL specified by us), the thing is this image in gallery has the ORIENTATION information while the one in the specified location doesn't.

This is the bug; http://code.google.com/p/android/issues/detail?id=19268

EDIT-2: I've filed a new bug with Android. I'm pretty sure this is an OS bug related the aforementioned bug.
http://code.google.com/p/android/issues/detail?id=22822

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

紫瑟鸿黎 2024-12-27 08:41:27

好吧,伙计们,这个 Android 的 bug 似乎暂时不会被修复。虽然我找到了一种实现 ExifInformation 的方法,以便两个设备(具有正确 Exif 标签的设备和不正确的 exif 标签一起工作)。

所以问题出在某些(较新的)设备上,存在一个使拍摄的照片保存下来的错误在你的应用程序文件夹中没有正确的 exif 标签,而正确旋转的图像保存在 android 默认文件夹中(即使它不应该)。

现在我要做的是,记录我启动相机应用程序的时间我的应用程序。然后,根据活动结果,我查询媒体提供程序以查看在我保存的时间戳之后是否保存了任何图片。这意味着,很可能操作系统将正确旋转的图片保存在默认文件夹中,当然在媒体存储中放置一个条目,我们可以使用该行中的旋转信息。现在,为了确保我们看到的是正确的图像,我将此文件的大小与我有权访问的文件的大小(保存在我自己的应用程序文件夹中)进行比较;

    int rotation =-1;
    long fileSize = new File(filePath).length();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] {MediaStore.Images.ImageColumns.ORIENTATION, MediaStore.MediaColumns.SIZE }, MediaStore.MediaColumns.DATE_ADDED + ">=?", new String[]{String.valueOf(captureTime/1000 - 1)}, MediaStore.MediaColumns.DATE_ADDED + " desc");

    if (mediaCursor != null && captureTime != 0 && mediaCursor.getCount() !=0 ) {
        while(mediaCursor.moveToNext()){
            long size = mediaCursor.getLong(1);
            //Extra check to make sure that we are getting the orientation from the proper file
            if(size == fileSize){
                rotation = mediaCursor.getInt(0);
                break;
            }
        }
    }

现在,如果此时的旋转仍然为 -1,则意味着这是具有正确旋转信息的手机之一。此时,我们可以在返回到 onActivityResult 的文件上使用常规 exif 方向。

    else if(rotation == -1){
        rotation = getExifOrientationAttribute(filePath);
    }

您可以轻松了解如何查找 exif 方向,如本问题中的答案 Android 中的相机方向问题

另请注意,ExifInterface 仅在 Api 级别 5 之后受支持。因此,如果您想要支持 2.0 之前的手机,那么你可以使用我为 java 找到的这个方便的库,由 Drew Noakes 提供; http://www.drewnoakes.com/code/exif/

祝您的图像旋转顺利!

编辑:因为有人问,我使用的意图以及我如何开始是这样的

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//mediaFile is where the image will be saved
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mediaFile));
startActivityForResult(intent, 1);

Ok guys, it seems like this bug for android won't be fixed for a while. Although I found a way to implement the ExifInformation so that both devices (ones with proper Exif tag, and also improper exif tags work together)..

So the issue is on some (newer) devices, there's a bug that makes the picture taken saved in your app folder without proper exif tags while a properly rotated image is saved in the android default folder (even though it shouldn't be)..

Now what I do is, i record the time when I'm starting the camera app from my app. THen on activity result, I query the Media Provider to see if any pictures were saved after this timestamp I saved. That means that, most likely OS saved the properly rotated picture in the default folder and of course put a entry in the media store and we can use the rotation information from this row. Now to make sure we are looking at the right image, i compare the size of this file to the one I have access to (saved in my own app folder);

    int rotation =-1;
    long fileSize = new File(filePath).length();

    Cursor mediaCursor = content.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] {MediaStore.Images.ImageColumns.ORIENTATION, MediaStore.MediaColumns.SIZE }, MediaStore.MediaColumns.DATE_ADDED + ">=?", new String[]{String.valueOf(captureTime/1000 - 1)}, MediaStore.MediaColumns.DATE_ADDED + " desc");

    if (mediaCursor != null && captureTime != 0 && mediaCursor.getCount() !=0 ) {
        while(mediaCursor.moveToNext()){
            long size = mediaCursor.getLong(1);
            //Extra check to make sure that we are getting the orientation from the proper file
            if(size == fileSize){
                rotation = mediaCursor.getInt(0);
                break;
            }
        }
    }

Now if the rotation at this point is still -1, then that means this is one of the phones with proper rotation information. At this point, we can use the regular exif orientation on the file that's returned to our onActivityResult

    else if(rotation == -1){
        rotation = getExifOrientationAttribute(filePath);
    }

You can easily find out how to find exif orientations like the answer in this question Camera orientation issue in Android

Also note that ExifInterface is only supported after Api level 5.. So if you want to support phones before 2.0, then you can use this handy library I found for java courtesy of Drew Noakes; http://www.drewnoakes.com/code/exif/

Good luck with your image rotating!

EDIT: Because it was asked, the intent I've used and how i started was like this

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//mediaFile is where the image will be saved
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mediaFile));
startActivityForResult(intent, 1);
浅忆流年 2024-12-27 08:41:27

你也可以这样走:

Matrix matrix = new Matrix();
// rotate the Bitmap (there a problem with exif so we'll query the mediaStore for orientation
Cursor cursor = getApplicationContext().getContentResolver().query(selectedImage,
      new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
if (cursor.getCount() == 1) {
cursor.moveToFirst();
    int orientation =  cursor.getInt(0);
    matrix.preRotate(orientation);
    }

you can go this way too:

Matrix matrix = new Matrix();
// rotate the Bitmap (there a problem with exif so we'll query the mediaStore for orientation
Cursor cursor = getApplicationContext().getContentResolver().query(selectedImage,
      new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
if (cursor.getCount() == 1) {
cursor.moveToFirst();
    int orientation =  cursor.getInt(0);
    matrix.preRotate(orientation);
    }
南巷近海 2024-12-27 08:41:27

确实是一个有问题的错误!我不确定我是否喜欢建议的解决方法,所以这是另一个:)

关键是使用 EXTRA_OUTPUT 并在捕获图像时查询它!显然,只有当您允许自己指定文件名时,这才有效。

protected void takePictureSequence() {      
    try {
        ContentValues values = new ContentValues();  
        values.put(MediaStore.Images.Media.TITLE, UUID.randomUUID().toString() + ".jpg");  
        newPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

        Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, newPhotoUri);

        startActivityForResult(intent, ActivityResults.TAKE_NEW_PICTURE_RESULT);
    } catch (Exception e) {
        Toast.makeText(this, R.string.could_not_initalize_camera, Toast.LENGTH_LONG).show();
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == ActivityResults.TAKE_NEW_PICTURE_RESULT) {
        if (resultCode == RESULT_OK) {
            try {
                String[] projection = { MediaStore.Images.Media.DATA }; 
                CursorLoader loader = new CursorLoader(this, newPhotoUri, projection, null, null, null);
                Cursor cursor = loader.loadInBackground();

                int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                cursor.moveToFirst();

                // Rotation is stored in an EXIF tag, and this tag seems to return 0 for URIs.
                // Hence, we retrieve it using an absolute path instead!
                int rotation = 0;
                String realPath = cursor.getString(column_index_data);
                if (realPath != null) {
                    rotation = ImageHelper.getRotationForImage(realPath);
                }

                // Now we can load the bitmap from the Uri, using the correct rotation.
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public int getRotationForImage(String path) {
    int rotation = 0;

    try {
        ExifInterface exif = new ExifInterface(path);
        rotation = (int)exifOrientationToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL));
    } catch (IOException e) {
        e.printStackTrace();
    }

    return rotation;
}

Indeed a problematic bug! I'm not sure I like the suggested workaround, so here's another :)

The key is to use EXTRA_OUTPUT and query it when the image has been captured! Obviously, this only works if you allow yourself to specify the filename.

protected void takePictureSequence() {      
    try {
        ContentValues values = new ContentValues();  
        values.put(MediaStore.Images.Media.TITLE, UUID.randomUUID().toString() + ".jpg");  
        newPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

        Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, newPhotoUri);

        startActivityForResult(intent, ActivityResults.TAKE_NEW_PICTURE_RESULT);
    } catch (Exception e) {
        Toast.makeText(this, R.string.could_not_initalize_camera, Toast.LENGTH_LONG).show();
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == ActivityResults.TAKE_NEW_PICTURE_RESULT) {
        if (resultCode == RESULT_OK) {
            try {
                String[] projection = { MediaStore.Images.Media.DATA }; 
                CursorLoader loader = new CursorLoader(this, newPhotoUri, projection, null, null, null);
                Cursor cursor = loader.loadInBackground();

                int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                cursor.moveToFirst();

                // Rotation is stored in an EXIF tag, and this tag seems to return 0 for URIs.
                // Hence, we retrieve it using an absolute path instead!
                int rotation = 0;
                String realPath = cursor.getString(column_index_data);
                if (realPath != null) {
                    rotation = ImageHelper.getRotationForImage(realPath);
                }

                // Now we can load the bitmap from the Uri, using the correct rotation.
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public int getRotationForImage(String path) {
    int rotation = 0;

    try {
        ExifInterface exif = new ExifInterface(path);
        rotation = (int)exifOrientationToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL));
    } catch (IOException e) {
        e.printStackTrace();
    }

    return rotation;
}
烟凡古楼 2024-12-27 08:41:27

我最近了解到,如果调整图像大小,通常会丢失 EXIF 信息。所以你想为新文件提供旧的 EXIF 信息。

来源。

What I've learned recently is that if you resize the image, it usually loses its EXIF information. So you want to give the new file the old EXIF information.

Source.

嘿嘿嘿 2024-12-27 08:41:27

我的解决方案。在 LG G2 手机上测试。我注意到,当我使用相机拍摄新照片时,一切正常。 ExifInterface 返回正确的方向。所以它一定是路径中的某个东西,因为我的路径在这行代码中为空:

exif = new ExifInterface(path);

但是当我使用绝对路径时,我的应用程序崩溃了。不过解决办法就是下面这个方法,因为这取决于你的sdk版本。还要注意的是,我仅使用绝对路径来选择图库图片,因为如果我将其用于相机,我的应用程序就会崩溃。我是编程新手,刚刚花了 2 天时间来解决这个问题。希望它能帮助某人。

   public String getRealPathFromURI(Uri uri) {
        if(Build.VERSION.SDK_INT >= 19){
            String id = uri.getLastPathSegment().split(":")[1];
            final String[] imageColumns = {MediaStore.Images.Media.DATA };
            final String imageOrderBy = null;
            Uri tempUri = getUri();
            Cursor imageCursor = getContentResolver().query(tempUri, imageColumns,
                    MediaStore.Images.Media._ID + "="+id, null, imageOrderBy);
            if (imageCursor.moveToFirst()) {
                return imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }else{
                return null;
            }
        }else{
            String[] projection = { MediaStore.MediaColumns.DATA };
            Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
            if (cursor != null) {
                int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                cursor.moveToFirst();
                return cursor.getString(column_index);
            } else
                return null;
        }
    }

所以我在 onActivityResult 方法中获取 ExifInterface

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == GALLERY_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
        try {
            exif = new ExifInterface(getRealPathFromURI(data.getData()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        showImage(data.getData());
    } else if (requestCode == CAMERA_IMAGE_REQUEST && resultCode == RESULT_OK) {
        try {
            exif = new ExifInterface(Uri.fromFile(getCameraFile()).getPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        showImage(Uri.fromFile(getCameraFile()));
    }
}

,而我的显示图像方法如下所示

public void showImage(Uri uri) {
    if (uri != null) {
        try {

            Bitmap bitmap = scaleBitmapDown(MediaStore.Images.Media.getBitmap(getContentResolver(), uri), IMAGE_SIZE);

            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

            bitmap = rotateBitmap(bitmap, orientation);



            if (whatPlayer.equals("Player1")) {
                mImagePlayer1.setImageBitmap(bitmap);

                bitmapPlayer1 = bitmap; //*save picture in static variable so other activity can use this
            }
            if (whatPlayer.equals("Player2")) {
                mImagePlayer2.setImageBitmap(bitmap);

                bitmapPlayer2 = bitmap;
            }

        } catch (IOException e) {
            Log.d(TAG, "Image picking failed because " + e.getMessage());
            Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show();
        }
    } else {
        Log.d(TAG, "Image picker gave us a null image.");
        Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show();
    }
}

My solution for this. Tested on LG G2 mobile. I noticed that when I use camera and take new picture everything works fine. ExifInterface returns right orientation. So it must be something in the path because my path was null in this line of code:

exif = new ExifInterface(path);

but when i used absolute path my app crash. But the solution is in this method below, because it depends on your sdk version. One more note to mention I used absolute path only for selecting gallery picture because if i used it for Camera my app crashed. Im new in programing and just lost 2 days to solve this. Hope it will help someone.

   public String getRealPathFromURI(Uri uri) {
        if(Build.VERSION.SDK_INT >= 19){
            String id = uri.getLastPathSegment().split(":")[1];
            final String[] imageColumns = {MediaStore.Images.Media.DATA };
            final String imageOrderBy = null;
            Uri tempUri = getUri();
            Cursor imageCursor = getContentResolver().query(tempUri, imageColumns,
                    MediaStore.Images.Media._ID + "="+id, null, imageOrderBy);
            if (imageCursor.moveToFirst()) {
                return imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }else{
                return null;
            }
        }else{
            String[] projection = { MediaStore.MediaColumns.DATA };
            Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
            if (cursor != null) {
                int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                cursor.moveToFirst();
                return cursor.getString(column_index);
            } else
                return null;
        }
    }

So I get my ExifInterface in onActivityResult method

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == GALLERY_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
        try {
            exif = new ExifInterface(getRealPathFromURI(data.getData()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        showImage(data.getData());
    } else if (requestCode == CAMERA_IMAGE_REQUEST && resultCode == RESULT_OK) {
        try {
            exif = new ExifInterface(Uri.fromFile(getCameraFile()).getPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        showImage(Uri.fromFile(getCameraFile()));
    }
}

and my show image method look like this

public void showImage(Uri uri) {
    if (uri != null) {
        try {

            Bitmap bitmap = scaleBitmapDown(MediaStore.Images.Media.getBitmap(getContentResolver(), uri), IMAGE_SIZE);

            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);

            bitmap = rotateBitmap(bitmap, orientation);



            if (whatPlayer.equals("Player1")) {
                mImagePlayer1.setImageBitmap(bitmap);

                bitmapPlayer1 = bitmap; //*save picture in static variable so other activity can use this
            }
            if (whatPlayer.equals("Player2")) {
                mImagePlayer2.setImageBitmap(bitmap);

                bitmapPlayer2 = bitmap;
            }

        } catch (IOException e) {
            Log.d(TAG, "Image picking failed because " + e.getMessage());
            Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show();
        }
    } else {
        Log.d(TAG, "Image picker gave us a null image.");
        Toast.makeText(this, R.string.image_picker_error, Toast.LENGTH_LONG).show();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文