Android中的相机方向问题

Posted

技术标签:

【中文标题】Android中的相机方向问题【英文标题】:Camera orientation issue in Android 【发布时间】:2011-08-29 11:22:10 【问题描述】:

我正在构建一个使用相机拍照的应用程序。这是我的源代码:

        File file = new File(Environment.getExternalStorageDirectory(),
            imageFileName);
    imageFilePath = file.getPath();
    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    //Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
    startActivityForResult(intent, ACTIVITY_NATIVE_CAMERA_AQUIRE);

onActivityResult() 方法上,我使用BitmapFactory.decodeStream() 来拾取图像。

当我在 Nexus one 上运行我的应用程序时,它运行良好。但是当我在三星 Galaxy S 或 HTC Inspire 4G 上运行时,图像的方向不正确。

以纵向模式拍摄,真实图像(保存在 SD 卡上)始终旋转 90 度。

拍摄后的图像预览------ SD卡上的真实图像

横向拍摄,一切都很好。

拍摄后的图像预览------SD卡上的真实图像

【问题讨论】:

setRotation(90) 在三星 Galaxy Nexus 中对我有用,但在 Xperia S 中没有旋转图像。 谁能帮我解决这个问题?我有同样的问题***.com/questions/28379130/… ***.com/questions/14066038/… 在这里回答***.com/questions/14066038/… Why does an image captured using camera intent gets rotated on some devices on Android?的可能重复 【参考方案1】:

这里有很多类似的主题和问题。由于您不是在编写自己的相机,因此我认为可以归结为:

有些设备会在保存之前旋转图像,而其他设备只是在照片的 exif 数据中添加方向标签。

我建议检查照片的 exif 数据并特别寻找

ExifInterface exif = new ExifInterface(SourceFileName);     //Since API Level 5
String exifOrientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);

由于照片在您的应用中正确显示,我不确定问题出在哪里,但这肯定会让您走上正确的道路!

【讨论】:

这似乎不适用于某些设备,它在所有方向上都返回 0。我知道它发生在 Galaxy S Infuse 和 Sony Xperia Arc 以及 S II 上。有趣的是当从图库中选择这些相同的图像时,内容提供者具有正确的方向值。有什么想法吗? @Abhijit 是的,我试图解决这个问题(使用 Android 等打开票证)我认为我找到了一个合理的解决方案来处理具有正确和错误方向信息的两部手机。查看我在这里发布的我自己的问题的详细答案; ***.com/a/8864367/137404 @ramz 我试过这个解决方案来找到。但它对所有方向都返回 0。你知道为什么它在所有方向上都返回 0。 此解决方案不起作用的原因有时是 ExifInterface 构造函数中使用了错误的“路径”。主要在 KitKat 上。看这里,如何获得正确的路径:***.com/a/20559175/690777 在我的三星 Galaxy S4 上它总是返回 0 (ExifInterface.ORIENTATION_UNDEFINED)...【参考方案2】:

我刚遇到同样的问题,用这个来纠正方向:

public void fixOrientation() 
    if (mBitmap.getWidth() > mBitmap.getHeight()) 
        Matrix matrix = new Matrix();
        matrix.postRotate(90);
        mBitmap = Bitmap.createBitmap(mBitmap , 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), matrix, true);
    

如果Bitmap的宽度大于高度,则返回的图像是横向的,所以我将它旋转90度。

希望它能帮助其他人解决这个问题。

【讨论】:

如果图像实际上是在横向拍摄怎么办?您的代码仍会旋转它。这不是问题的答案。 我的应用程序强制纵向,所以这不是问题。我在这里只包括了这个问题的替代解决方案。 但是如果应用程序强制使用纵向,您仍然可以拍摄横向图片(宽度 > 高度)并且它将被旋转...至少我正在为所有内容设置 screenOrientation="portrait" ...相机仍然可以拍摄风景照片。 这里有完整和正确的答案***.com/questions/6069122/…【参考方案3】:

需要做两件事:

    相机预览需要与您的旋转相同。设置为camera.setDisplayOrientation(result);

    将拍摄的图片保存为您的相机预览。 通过Camera.Parameters 执行此操作。

    int mRotation = getCameraDisplayOrientation();
    
    Camera.Parameters parameters = camera.getParameters();
    
    parameters.setRotation(mRotation); //set rotation to save the picture
    
    camera.setDisplayOrientation(result); //set the rotation for preview camera
    
    camera.setParameters(parameters);
    

希望对您有所帮助。

【讨论】:

就是这样! Camera.parameters 真的很方便保存快照而不渲染到中间位图 请将此标记为最简单的答案!这做的工作!对这个简单的解决方案非常满意 如果这个sn-p照原样,我们可以说parameters.setRotation(result),不是吗? 这假设您直接使用 Camera 类,当您使用 ACTION_IMAGE_CAPTURE 意图时,您无法指定相同的选项。 什么是结果和getCameraDisplayOrientation()?【参考方案4】:
            int rotate = 0;
            try 
                File imageFile = new File(sourcepath);
                ExifInterface exif = new ExifInterface(
                        imageFile.getAbsolutePath());
                int orientation = exif.getAttributeInt(
                        ExifInterface.TAG_ORIENTATION,
                        ExifInterface.ORIENTATION_NORMAL);

                switch (orientation) 
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
                
             catch (Exception e) 
                e.printStackTrace();
            
            Matrix matrix = new Matrix();
    matrix.postRotate(rotate);
    bitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

【讨论】:

解释一下您的代码在做什么以及它与其他答案的不同之处会有所帮助(这个问题毕竟已经很老了,因此其他答案可能非常成熟)。【参考方案5】:

另一种选择是像这样在结果屏幕中旋转位图:

ImageView img=(ImageView)findViewById(R.id.ImageView01);
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.refresh);
// Getting width & height of the given image.
int w = bmp.getWidth();
int h = bmp.getHeight();
// Setting post rotate to 90
Matrix mtx = new Matrix();
mtx.postRotate(90);
// Rotating Bitmap
Bitmap rotatedBMP = Bitmap.createBitmap(bmp, 0, 0, w, h, mtx, true);
BitmapDrawable bmd = new BitmapDrawable(rotatedBMP);

img.setImageDrawable(bmd);

【讨论】:

这种方法行不通,因为它还会从正确处理方向的设备中旋转图像。 这里有完整和正确的答案***.com/questions/6069122/…【参考方案6】:

对于某些设备,我也遇到了同样的问题:

private void rotateImage(final String path) 

    Bitmap scaledBitmap = Bitmap.createScaledBitmap(Conasants.bm1, 1000,
            700, true);
    Bitmap rotatedBitmap = null;
    try 
        ExifInterface ei = new ExifInterface(path);
        int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);
        Matrix matrix = new Matrix();
        switch (orientation) 
        case ExifInterface.ORIENTATION_ROTATE_90:
            matrix.postRotate(90);
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            matrix.postRotate(180);
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            matrix.postRotate(270);
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        default:
            rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
                    scaledBitmap.getWidth(), scaledBitmap.getHeight(),
                    matrix, true);
            break;
        
     catch (Throwable e) 
        e.printStackTrace();
    
    cropImage.setImageBitmap(rotatedBitmap);
    rotatedBitmap = null;
    Conasants.bm1 = null;

【讨论】:

【参考方案7】:

试试这个方法: 静态 Uri image_uri; 静态位图take_image=null;

            image_uri=fileUri; // file where image has been saved

      taken_image=BitmapFactory.decodeFile(image_uri.getPath());
      try
        
            ExifInterface exif = new ExifInterface(image_uri.getPath()); 

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


            switch(orientation) 
                case ExifInterface.ORIENTATION_ROTATE_90:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 90);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 180);

                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 270);

                    break;
                case ExifInterface.ORIENTATION_NORMAL:
                    taken_image=decodeScaledBitmapFromSdCard(image_uri.getPath(), 200, 200);
                    RotateBitmap(taken_image, 0);

                    break;
            

        
        catch (OutOfMemoryError e)
        
            Toast.makeText(getActivity(),e+"\"memory exception occured\"",Toast.LENGTH_LONG).show();


        



public Bitmap RotateBitmap(Bitmap source, float angle) 
      Matrix matrix = new Matrix();
      matrix.postRotate(angle);

      round_Image = source;
      round_Image = Bitmap.createBitmap(source, 0, 0, source.getWidth(),   source.getHeight(), matrix, true);


  return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);

【讨论】:

【参考方案8】:

不再检查照片的 exif 数据。 Glide 让您轻松上手。

Google 向我们介绍了一个由bumptech 开发的Android Image Loader Library,名为Glide,作为Google 推荐的一个库。迄今为止,它已在许多 Google 开源项目中使用,包括 Google I/O 2014 官方应用程序。

例如:Glide.with(context).load(uri).into(imageview);

更多:https://github.com/bumptech/glide

【讨论】:

【参考方案9】:
public void setCameraPicOrientation()
        int rotate = 0;
        try 
            File imageFile = new File(mCurrentPhotoPath);
            ExifInterface exif = new ExifInterface(
                    imageFile.getAbsolutePath());
            int orientation = exif.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);

            switch (orientation) 
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
            
         catch (Exception e) 
            e.printStackTrace();
        
        Matrix matrix = new Matrix();
        matrix.postRotate(rotate);
        int targetW = 640;
        int targetH = 640;

        /* Get the size of the image */
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;

        /* Figure out which way needs to be reduced less */
        int scaleFactor = 1;
        if ((targetW > 0) || (targetH > 0)) 
            scaleFactor = Math.min(photoW/targetW, photoH/targetH);
        

        /* Set bitmap options to scale the image decode target */
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inPurgeable = true;

        /* Decode the JPEG file into a Bitmap */
        Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        bitmap= Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
            /* Associate the Bitmap to the ImageView */
      imageView.setImageBitmap(bitmap);
    

希望这会有所帮助!谢谢

【讨论】:

【参考方案10】:
    public static  int mOrientation =  1;

    OrientationEventListener myOrientationEventListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.takephoto);

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);


        myOrientationEventListener
        = new OrientationEventListener(getApplicationContext()) 

            @Override
            public void onOrientationChanged(int o) 
                // TODO Auto-generated method stub
                if(!isTablet(getApplicationContext()))
                
                    if(o<=285 && o>=80)
                        mOrientation = 2;
                    else
                        mOrientation = 1;
                
                else
                
                    if(o<=285 && o>=80)
                        mOrientation = 1;
                    else
                        mOrientation = 2;
                

            
        ;

        myOrientationEventListener.enable();

    



    public static boolean isTablet(Context context) 
        return (context.getResources().getConfiguration().screenLayout
                & Configuration.SCREENLAYOUT_SIZE_MASK)
                >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    


我希望这会有所帮助。谢谢!

【讨论】:

【参考方案11】:

在这里遇到同样的问题,下面的代码 sn-p 对我有用:

private static final String[] CONTENT_ORIENTATION = new String[] 
        MediaStore.Images.ImageColumns.ORIENTATION
;

static int getExifOrientation(ContentResolver contentResolver, Uri uri) 
    Cursor cursor = null;

    try 
        cursor = contentResolver.query(uri, CONTENT_ORIENTATION, null, null, null);
        if (cursor == null || !cursor.moveToFirst()) 
            return 0;
        
        return cursor.getInt(0);
     catch (RuntimeException ignored) 
        // If the orientation column doesn't exist, assume no rotation.
        return 0;
     finally 
        if (cursor != null) 
            cursor.close();
        
    

希望这会有所帮助:)

【讨论】:

【参考方案12】:

在surfaceChanged回调中试试这个:

Camera.Parameters parameters=mCamera.getParameters();
if(this.getResources().getConfiguration().orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
    parameters.setRotation(90);
else
    parameters.setRotation(0);

mCamera.setParameters(parameters);

【讨论】:

【参考方案13】:

//按钮点击

btnCamera.setOnClickListener( new View.OnClickListener() 
        @Override
        public void onClick(View view) 

                try 
                    ContentValues values;
                    values = new ContentValues();
                    values.put(MediaStore.Images.Media.TITLE, "New Picture");
                    values.put(MediaStore.Images.Media.DESCRIPTION, "From your Camera");
                    imageUri = context.getContentResolver().insert(
                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
                    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                    startActivityForResult(intent, CAMERA_REQUEST);
                catch (Exception e)

            );

//onActivityResult方法

   if (requestCode==CAMERA_REQUEST)
        try 
            if (imageUri!=null) 
                path = String.valueOf(new File(FilePath.getPath(context, imageUri)));
                     
        catch (Exception e)
            toast("please try again "+e.getMessage());
            Log.e("image error",e.getMessage());
        
    

//创建类文件路径

公共类文件路径

public static String getPath(final Context context, final Uri uri) 

    // check here to KITKAT or new version
    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) 

        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) 
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) 
                return Environment.getExternalStorageDirectory() + "/"
                        + split[1];
            
        
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) 

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"),
                    Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        
        // MediaProvider
        else if (isMediaDocument(uri)) 
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) 
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
             else if ("video".equals(type)) 
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
             else if ("audio".equals(type)) 
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            

            final String selection = "_id=?";
            final String[] selectionArgs = new String[]  split[1] ;

            return getDataColumn(context, contentUri, selection,
                    selectionArgs);
        
    
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) 

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) 
        return uri.getPath();
    

    return null;


/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context
 *            The context.
 * @param uri
 *            The Uri to query.
 * @param selection
 *            (Optional) Filter used in the query.
 * @param selectionArgs
 *            (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
public static String getDataColumn(Context context, Uri uri,
                                   String selection, String[] selectionArgs) 

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection =  column ;

    try 
        cursor = context.getContentResolver().query(uri, projection,
                selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst()) 
            final int index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        
     finally 
        if (cursor != null)
            cursor.close();
    
    return null;


/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) 
    return "com.android.externalstorage.documents".equals(uri
            .getAuthority());


/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) 
    return "com.android.providers.downloads.documents".equals(uri
            .getAuthority());


/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) 
    return "com.android.providers.media.documents".equals(uri
            .getAuthority());


/**
 * @param uri
 *            The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) 
    return "com.google.android.apps.photos.content".equals(uri
            .getAuthority());

【讨论】:

【参考方案14】:

代码在功能上适用于横向和纵向 @frontCameraID = 变量得到了想要的展示相机的经典方法

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 

    if(holder.getSurface() == null) 
        return;
    
    try
        camera.stopPreview();
     catch (Exception e)
    

    try

        int orientation = getDisplayOrientation(frontCameraID);

        Camera.Parameters parameters = camera.getParameters();
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) 
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        

        parameters.setRotation(rotationPicture);
        camera.setParameters(parameters);
        camera.setDisplayOrientation(orientation);
        camera.startPreview();

     catch (Exception e) 
        Log.i("ERROR", "Camera error changed: " + e.getMessage());
    

获取方向y旋转以保存图片和显示方向的方法 @result = 相机预览视图上的方向 @rotationPicture = 正确保存图片所需的旋转

private int getDisplayOrientation(int cameraId) 

    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    int rotation = ((Activity) context).getWindowManager().getDefaultDisplay().getRotation();
    int degrees = 0;
    switch (rotation) 
        case Surface.ROTATION_0: degrees = 0; break;
        case Surface.ROTATION_90: degrees = 90; break;
        case Surface.ROTATION_180: degrees = 180; break;
        case Surface.ROTATION_270: degrees = 270; break;
    

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) 
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;
        rotationPicture = (360 - result) % 360;
     else 
        result = (info.orientation - degrees + 360) % 360;
        rotationPicture = result;
    

    return result;

有人对代码有疑问,请告诉我。

【讨论】:

【参考方案15】:

使用 Picasso 和 glide 库的两个 One line 解决方案

在花了很多时间解决图像旋转问题后,我终于找到了两个简单的解决方案。我们不需要做任何额外的工作。 Picasso 和 Glide 是一个非常强大的库,用于处理应用程序中包含的图像。它将读取图像 EXIF 数据并自动旋转图像。

使用 glide 库 https://github.com/bumptech/glide

Glide.with(this).load("http url or sdcard url").into(imgageView);

使用毕加索图书馆 https://github.com/square/picasso

Picasso.with(context).load("http url or sdcard url").into(imageView);

【讨论】:

欢迎来到 SO。请不要要求投票:meta.stackexchange.com/questions/194061/…

以上是关于Android中的相机方向问题的主要内容,如果未能解决你的问题,请参考以下文章

android方向 - 意图问题(相机)

Android如何修复相机方向

android相机surfaceview方向

自拍的 Android 原生相机应用程序方向问题

如何实现 Android 相机以任何屏幕方向拍照?

在 android 中设置显示方向后,相机相机预览不正确