使用竖屏相机应用拍摄的图像的EXIF方向标记值始终为0。

41

我有一个竖屏摄像头应用程序,可以从前置和后置摄像头拍照。我将图像保存到SD卡中,并尝试找到相应的exif值,但始终得到0。但是,我能够获取设备中存储的其他图像(如下载的图片)的预期exif方向值。

我该如何解决这个问题?有人可以帮帮我吗?

以下是用于保存图片并查找方向的代码:

PictureCallback myPictureCallback_JPG = new PictureCallback() {

    @Override
    public void onPictureTaken(byte[] arg0, Camera arg1) {
        // TODO Auto-generated method stub


                try {
                    File APP_FILE_PATH = new File(Environment.getExternalStorageDirectory()
                            .getPath() + "/Myapp/");
                    if (!APP_FILE_PATH.exists()) {
                        APP_FILE_PATH.mkdirs();
                    }
                    File file = new File(APP_FILE_PATH, "image.jpg");

                    FileOutputStream fos = new FileOutputStream(file);
                    fos.write(arg0);
                    fos.close();
imageFileUri=Uri.fromfile(file);                           getApplicationContext().getContentResolver().notifyChange(
                          imageFileUri, null);
                sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
                            Uri.parse("file://"
                                    + Environment.getExternalStorageDirectory())));
                    ExifInterface exif = new ExifInterface(file.getAbsolutePath());
                    int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);



                } catch (Exception e) {

                }



    }
};

以下是创建和更改表面代码的函数

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
List<Size> sizes = parameters.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, width, height);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);

camera.setParameters(parameters);
camera.startPreview();
 startPreview();

}
 @Override
 public void surfaceCreated(SurfaceHolder holder) {


 try {

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
          Camera.CameraInfo info=new Camera.CameraInfo();

          for (int i=0; i < Camera.getNumberOfCameras(); i++) {
            Camera.getCameraInfo(i, info);

            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
              camera=Camera.open(i);
              defaultCameraId = i;
            }
          }
        }

        if (camera == null) {
          camera=Camera.open();
        }




        try {
            camera.setPreviewDisplay(surfaceHolder);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        Camera.Parameters parameters = camera.getParameters();
        android.hardware.Camera.CameraInfo info =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(defaultCameraId, info);
        int rotation = this.getWindowManager().getDefaultDisplay()
                .getRotation();
        if (Integer.parseInt(Build.VERSION.SDK) >= 8)
        {

             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;  
             } else {  // back-facing
                 result = (info.orientation - degrees + 360) % 360;
             }
             camera.setDisplayOrientation(result);

        }
        else
        {
            parameters.set("orientation", "portrait");
        }


        camera.setParameters(parameters);



} catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (SecurityException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} 


}
 private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
            final double ASPECT_TOLERANCE = 0.1;
            double targetRatio = (double) w / h;
            if (sizes == null) return null;

            Size optimalSize = null;
            double minDiff = Double.MAX_VALUE;

            int targetHeight = h;


            for (Size size : sizes) {
                double ratio = (double) size.width / size.height;
                if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }


            if (optimalSize == null) {
                minDiff = Double.MAX_VALUE;
                for (Size size : sizes) {
                    if (Math.abs(size.height - targetHeight) < minDiff) {
                        optimalSize = size;
                        minDiff = Math.abs(size.height - targetHeight);
                    }
                }
            }
            return optimalSize;
        }

你能否发布一些示例代码,展示如何拍摄照片以及保存照片的过程? - Maks
实际上,这似乎以前就出现过 https://dev59.com/emoy5IYBdhLWcg3wlvGX#8864367 - Maks
@Maks 我也尝试过那个方法,但从来没有成功过。 - hacker
1
无法工作,链接中的解决方案适用于由另一个应用程序(相机应用程序)拍摄的图像,而不是使用Camera API由您自己拍摄的图像。 - ALiGOTec
@RobinHood 是的,它是竖屏模式...你能帮我吗? - hacker
显示剩余5条评论
4个回答

24

我在三星设备上也遇到了同样的问题,后来我实施了ExifInterface并且成功地解决了。

无论以何种模式拍摄图像,它都只会以纵向模式存储,并且在提取时也会返回纵向模式。下面的代码是我用来实现我的目标的,在后置摄像头中实现了这一点,但不确定前置摄像头是否适用。

相机意图@

Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
            startActivityForResult(intent, 1212);   

onActivityResult@

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

        if (requestCode == 1212) {
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
        Bitmap bitmap;
        //bitmap=GlobalMethods.decodeSampledBitmapFromResource(_path, 80, 80);
        bitmap=GlobalMethods.decodeFile(_path);
        if (bitmap == null) {
            imgMed.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.add_photo));
        } 
        else {
            imgMed.setImageBitmap(bitmap);
            imgMed.setScaleType(ScaleType.FIT_XY);

        }
    }
        }

解码文件@

    public static Bitmap decodeFile(String path) {

          int orientation;

             try {

                 if(path==null){

                     return null;
                 }
                 // decode image size
                 BitmapFactory.Options o = new BitmapFactory.Options();
                 o.inJustDecodeBounds = true;

                 // Find the correct scale value. It should be the power of 2.
                 final int REQUIRED_SIZE = 70;
                 int width_tmp = o.outWidth, height_tmp = o.outHeight;
                 int scale = 4;
                 while (true) {
                     if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
                         break;
                     width_tmp /= 2;
                     height_tmp /= 2;
                     scale++;
                 }
                 // decode with inSampleSize
                 BitmapFactory.Options o2 = new BitmapFactory.Options();
                 o2.inSampleSize=scale;
                 Bitmap bm = BitmapFactory.decodeFile(path,o2);


                 Bitmap bitmap = bm;

                 ExifInterface exif = new ExifInterface(path);
                 orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
                 Log.e("orientation",""+orientation);
                 Matrix m=new Matrix();

                 if((orientation==3)){

                 m.postRotate(180);
                 m.postScale((float)bm.getWidth(), (float)bm.getHeight());

//               if(m.preRotate(90)){
                 Log.e("in orientation",""+orientation);

                 bitmap = Bitmap.createBitmap(bm, 0, 0,bm.getWidth(),bm.getHeight(), m, true);
                 return  bitmap;
                 }
                 else if(orientation==6){

                  m.postRotate(90);

                  Log.e("in orientation",""+orientation);

                  bitmap = Bitmap.createBitmap(bm, 0, 0,bm.getWidth(),bm.getHeight(), m, true);
                     return  bitmap;
                 }

                 else if(orientation==8){

                  m.postRotate(270);

                  Log.e("in orientation",""+orientation);

                  bitmap = Bitmap.createBitmap(bm, 0, 0,bm.getWidth(),bm.getHeight(), m, true);
                     return  bitmap;
                 }
                 return bitmap;
             }
             catch (Exception e) {
             }
             return null;
         }

有人能帮忙吗?https://dev59.com/cofca4cB1Zd3GeqPlaPJ - user4050065
ExifInterface是一个类 - 你如何实现它? - Cbas
为什么不使用 ExifInterface 常量来表示方向?为什么只用整数? - Sergei Bubenshchikov

3
问题在于您必须手动在 onPictureTaken 函数中添加exif信息。
在保存图片(jpg)后,您必须创建一个Exif接口并自己设置参数:
....
exif = new ExifInterface(file.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, orientation_detected_by_you_application);
exif.saveAttributes();

你手机里的其他照片是使用一款应用程序拍摄的,该应用程序会将exif信息嵌入照片中。
要检测方向:
public void enableOrientationListener(){

    if (mOrientationEventListener == null) {            
        mOrientationEventListener = new OrientationEventListener(getContext(), SensorManager.SENSOR_DELAY_NORMAL) {

            @Override
            public void onOrientationChanged(int orientation) {

                // determine our orientation based on sensor response
                int lastOrientation = mOrientation;

                Display display = null;
                if(parentActivity == null){
                    display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
                }else{
                    display = parentActivity.getWindowManager().getDefaultDisplay();
                }


                if (display.getOrientation() == Surface.ROTATION_0) {   // landscape oriented devices
                    if (orientation >= 315 || orientation < 45) {
                        if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {                         
                            mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
                        }
                    } else if (orientation < 315 && orientation >= 225) {
                        if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
                            mOrientation = ORIENTATION_PORTRAIT_INVERTED;
                        }                       
                    } else if (orientation < 225 && orientation >= 135) {
                        if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
                            mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
                        }                       
                    } else if (orientation <135 && orientation > 45) { 
                        if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {
                            mOrientation = ORIENTATION_PORTRAIT_NORMAL;
                        }                       
                    }                       
                } else {  // portrait oriented devices
                    if (orientation >= 315 || orientation < 45) {
                        if (mOrientation != ORIENTATION_PORTRAIT_NORMAL) {                          
                            mOrientation = ORIENTATION_PORTRAIT_NORMAL;
                        }
                    } else if (orientation < 315 && orientation >= 225) {
                        if (mOrientation != ORIENTATION_LANDSCAPE_NORMAL) {
                            mOrientation = ORIENTATION_LANDSCAPE_NORMAL;
                        }                       
                    } else if (orientation < 225 && orientation >= 135) {
                        if (mOrientation != ORIENTATION_PORTRAIT_INVERTED) {
                            mOrientation = ORIENTATION_PORTRAIT_INVERTED;
                        }                       
                    } else if (orientation <135 && orientation > 45) { 
                        if (mOrientation != ORIENTATION_LANDSCAPE_INVERTED) {
                            mOrientation = ORIENTATION_LANDSCAPE_INVERTED;
                        }                       
                    }
                }
            }
        };
    }
    if (mOrientationEventListener.canDetectOrientation()) {
        mOrientationEventListener.enable();
    }
}


private static final int ORIENTATION_PORTRAIT_NORMAL    =  1;
private static final int ORIENTATION_PORTRAIT_INVERTED  =  2;
private static final int ORIENTATION_LANDSCAPE_NORMAL   =  3;
private static final int ORIENTATION_LANDSCAPE_INVERTED =  4;


public void disableOrientationListener(){
    if(mOrientationEventListener != null){
        mOrientationEventListener.disable();
    }
}

您需要将mOrientation设置为图像的方向属性。


我的相机应用程序检测到任何拍摄的照片的方向为0。根据您的指示,代码应该是这样的: exif = new ExifInterface(file.getAbsolutePath()); exif.setAttribute(ExifInterface.TAG_ORIENTATION,0); exif.saveAttributes(); - hacker
如果我设置成这样,方向值就会是1、2、3或4对吧?但我们希望不同图像的方向值为3、6和8。这怎么实现? - hacker
抱歉代码有些混乱,它用于设置视图的旋转,但您可以从上面的代码开始,并根据链接进行更正。 - ALiGOTec

2

这个问题很久以前就已经解决了,但是我遇到了一些困难,无法使相机正常工作,所以这里是我的最终解决方案,而不使用exif。我希望这可以帮助其他人:

public void startPreview() {
        try {
            Log.i(TAG, "starting preview: " + started);

            // ....
            Camera.CameraInfo camInfo = new Camera.CameraInfo();
            Camera.getCameraInfo(cameraIndex, camInfo);
            int cameraRotationOffset = camInfo.orientation;
            // ...

            Camera.Parameters parameters = camera.getParameters();
            List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
            Camera.Size previewSize = null;
            float closestRatio = Float.MAX_VALUE;

            int targetPreviewWidth = isLandscape() ? getWidth() : getHeight();
            int targetPreviewHeight = isLandscape() ? getHeight() : getWidth();
            float targetRatio = targetPreviewWidth / (float) targetPreviewHeight;

            Log.v(TAG, "target size: " + targetPreviewWidth + " / " + targetPreviewHeight + " ratio:" + targetRatio);
            for (Camera.Size candidateSize : previewSizes) {
                float whRatio = candidateSize.width / (float) candidateSize.height;
                if (previewSize == null || Math.abs(targetRatio - whRatio) < Math.abs(targetRatio - closestRatio)) {
                    closestRatio = whRatio;
                    previewSize = candidateSize;
                }
            }

            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            int degrees = 0;
            switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break; // Natural orientation
            case Surface.ROTATION_90:
                degrees = 90;
                break; // Landscape left
            case Surface.ROTATION_180:
                degrees = 180;
                break;// Upside down
            case Surface.ROTATION_270:
                degrees = 270;
                break;// Landscape right
            }
            int displayRotation;
            if (isFrontFacingCam) {
                displayRotation = (cameraRotationOffset + degrees) % 360;
                displayRotation = (360 - displayRotation) % 360; // compensate
                                                                    // the
                                                                    // mirror
            } else { // back-facing
                displayRotation = (cameraRotationOffset - degrees + 360) % 360;
            }

            Log.v(TAG, "rotation cam / phone = displayRotation: " + cameraRotationOffset + " / " + degrees + " = "
                    + displayRotation);

            this.camera.setDisplayOrientation(displayRotation);

            int rotate;
            if (isFrontFacingCam) {
                rotate = (360 + cameraRotationOffset + degrees) % 360;
            } else {
                rotate = (360 + cameraRotationOffset - degrees) % 360;
            }

            Log.v(TAG, "screenshot rotation: " + cameraRotationOffset + " / " + degrees + " = " + rotate);

            Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
            parameters.setPreviewSize(previewSize.width, previewSize.height);
            parameters.setRotation(rotate);
            camera.setParameters(parameters);
            camera.setPreviewDisplay(mHolder);
            camera.startPreview();

            Log.d(TAG, "preview started");

            started = true;
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

1
在我看来,如果你自己拍照并且可以访问相机...这是正确的解决方案,也与Android文档推荐的有些类似:请参见setRotation()http://developer.android.com/reference/android/hardware/Camera.Parameters.html基本上,您可以使用camera.info以及传感器方向来确定在postRotate调用中使用多少度数。我花了很多时间在这上面...如果您可以访问相机,这种方法是最可靠和简单的。 - tronious
在搜索了许多答案和论坛后,我发现这是可行的解决方案。谢谢! - Dharmendra
代码太多了,一个方法无法搞定。这样就像一团乱麻。 - loshkin
是的,我也是一个代码整洁爱好者,但对于一个SO答案来说,它非常容易阅读。这里并不是代码整洁的重点,你显然可以在自己的项目中拆分这个方法 ;) - Louis GRIGNON

0

您正在忽略一个异常:

    try {
        // Code
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
    } catch (Exception e) {

    }

尝试:

    try {
        // Code
        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
    } catch (Exception e) {
        e.printStackTrace();
    }

并且发布logcat。抛弃异常从来都不是明智的选择。


如果你已经检查过并且没有异常,那么一定是其他问题。你忽略了至少四个FileNotFoundException和IOException。只是想确认一下。 - Binoy Babu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接