【问题标题】:Controlling the camera to take pictures in portrait doesn't rotate the final images控制相机以纵向拍照不会旋转最终图像
【发布时间】:2013-03-26 09:07:55
【问题描述】:

我正在尝试控制 Android 相机在纵向应用中拍照,但是当我保存图片时,它是横向的。我已经使用setCameraDisplayOrientation() 方法将图像旋转了 90 级,但不起作用。

然后我找到了这个post,但TAG_ORIENTATION0(未定义)。如果我抓住这个值并应用一个旋转值,也不起作用。

如何拍摄纵向照片并以正确的方向保存?

    /** Initializes the back/front camera */
private boolean initPhotoCamera() {
    try {
        camera = getCameraInstance(selected_camera);

        Camera.Parameters parameters = camera.getParameters();
   //           parameters.setPreviewSize(width_video, height_video);
   //           parameters.set("orientation", "portrait");
   //           parameters.set("rotation", 1);
   //           camera.setParameters(parameters);


        checkCameraFlash(parameters);

   //            camera.setDisplayOrientation( 0);
        setCameraDisplayOrientation(selected_camera, camera);


        surface_view.getHolder().setFixedSize(width_video, height_video);


        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(width_video, height_video);
        surface_view.setLayoutParams(lp);

        camera.lock();

        surface_holder = surface_view.getHolder();
        surface_holder.addCallback(this);
        surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        setPreviewCamera();

    } catch (Exception e) {
        Log.v("RecordVideo", "Could not initialize the Camera");
        return false;
    }
    return true;
}

public void setCameraDisplayOrientation(int cameraId, Camera camera) {
     Camera.CameraInfo info = new Camera.CameraInfo();
     Camera.getCameraInfo(cameraId, info);
     int rotation = 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;  // compensate the mirror
     } else {  // back-facing
         result = (info.orientation - degrees + 360) % 360;
     }
     camera.setDisplayOrientation(result);
 }

     public static Bitmap rotate(Bitmap bitmap, int degree) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    Matrix mtx = new Matrix();
   //       mtx.postRotate(degree);
    mtx.setRotate(degree);

    return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
}

@Override
public void onPictureTaken(byte[] data, Camera camera) {



    String timeStamp = Calendar.getInstance().getTime().toString();
    output_file_name = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + timeStamp + ".jpeg";

    File pictureFile = new File(output_file_name);
    if (pictureFile.exists()) {
        pictureFile.delete();
    }

    try {
        FileOutputStream fos = new FileOutputStream(pictureFile);
        fos.write(data);

        Bitmap realImage = BitmapFactory.decodeFile(output_file_name);

        ExifInterface exif=new ExifInterface(pictureFile.toString());

        Log.d("EXIF value", exif.getAttribute(ExifInterface.TAG_ORIENTATION));
        if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("6")){
            realImage= rotate(realImage, 90);
        } else if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("8")){
            realImage= rotate(realImage, 270);
        } else if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("3")){
            realImage= rotate(realImage, 180);
        } else if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("0")){
            realImage= rotate(realImage, 45);
        }

        boolean bo = realImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);

        fos.close();

        Log.d("Info", bo + "");

    } catch (FileNotFoundException e) {
        Log.d("Info", "File not found: " + e.getMessage());
    } catch (IOException e) {
        Log.d("TAG", "Error accessing file: " + e.getMessage());
    }
}

【问题讨论】:

标签: android android-camera android-orientation


【解决方案1】:

问题是当我保存图像时我做得不好。

@Override
public void onPictureTaken(byte[] data, Camera camera) {

    String timeStamp = new SimpleDateFormat( "yyyyMMdd_HHmmss").format( new Date( ));
    output_file_name = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + timeStamp + ".jpeg";

    File pictureFile = new File(output_file_name);
    if (pictureFile.exists()) {
        pictureFile.delete();
    }

    try {
        FileOutputStream fos = new FileOutputStream(pictureFile);

        Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);

        ExifInterface exif=new ExifInterface(pictureFile.toString());

        Log.d("EXIF value", exif.getAttribute(ExifInterface.TAG_ORIENTATION));
        if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("6")){
            realImage= rotate(realImage, 90);
        } else if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("8")){
            realImage= rotate(realImage, 270);
        } else if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("3")){
            realImage= rotate(realImage, 180);
        } else if(exif.getAttribute(ExifInterface.TAG_ORIENTATION).equalsIgnoreCase("0")){
            realImage= rotate(realImage, 90);
        }

        boolean bo = realImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);

        fos.close();

        ((ImageView) findViewById(R.id.imageview)).setImageBitmap(realImage);

        Log.d("Info", bo + "");

    } catch (FileNotFoundException e) {
        Log.d("Info", "File not found: " + e.getMessage());
    } catch (IOException e) {
        Log.d("TAG", "Error accessing file: " + e.getMessage());
    }
}

public static Bitmap rotate(Bitmap bitmap, int degree) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    Matrix mtx = new Matrix();
   //       mtx.postRotate(degree);
    mtx.setRotate(degree);

    return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
}

【讨论】:

  • 能贴一下rotate方法体吗?
  • 使用这种方式,保存的图片尺寸变大了
  • 这是一个很棒的解决方案。在大量设备上进行了测试。还没有发现问题。
  • @ivarajet 同意了。最好将质量(100)降低到70左右。如果你想要无损,你可以保存为PNG
  • @Cbas PNG的问题是处理的时间,经过测试,PNG需要更多的时间,比JPG的4倍。
【解决方案2】:

setCameraDisplayOrientation() 方法可让您更改 预览 的显示方式不影响图像的记录方式 (source)。

为了改变实际记录的图像,您需要设置Camerarotation参数。你这样做:

//STEP #1: Get rotation degrees
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info);
int rotation = mActivity.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 rotate = (info.orientation - degrees + 360) % 360;

//STEP #2: Set the 'rotation' parameter
Camera.Parameters params = mCamera.getParameters();
params.setRotation(rotate); 
mCamera.setParameters(params);

您的解决方案是一种解决方法,因为您在图像已记录后对其进行了修改。此解决方案更简洁,并且在保存图像之前不需要所有这些“if”语句。

【讨论】:

  • 对我不起作用,如果我处于纵向模式,那么它仍然会以横向模式保存图片。
  • @Justin,你用什么设备测试过?
  • 在三星 Note3 中也无法使用。但是,在 HTC OneX 上,它可以正常工作。
  • 也不适用于三星 S4 或 Xperia Z3。不是一个可靠的解决方案,因为旋转参数可以被相机忽略。
  • 我简化了以下代码:int degrees = mActivity.getWindowManager().getDefaultDisplay().getRotation(); 以摆脱 switch 构造。工作正常。
【解决方案3】:

当您使用前置摄像头时,您可以使用以下方法正确生成预览。

此代码进入相机预览的surfaceChanged方法

@Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
     int angleToRotate=CommonMethods.getRoatationAngle(mActivity, Camera.CameraInfo.CAMERA_FACING_FRONT);
     mCamera.setDisplayOrientation(angleToRotate);
}

这段代码可以放到静态类中

 /**
     * Get Rotation Angle
     * 
     * @param mContext
     * @param cameraId
     *            probably front cam
     * @return angel to rotate
     */
    public static int getRoatationAngle(Activity mContext, int cameraId) {
        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = mContext.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; // compensate the mirror
        } else { // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }

您可以通过这种方式旋转图像。这仅在拍摄图像并且我们即将保存图像时使用

public static Bitmap rotate(Bitmap bitmap, int degree) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();

        Matrix mtx = new Matrix();
        mtx.postRotate(degree);

        return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
    }

将用于拍照的方法

  @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        int angleToRotate = getRoatationAngle(MainActivity.this, Camera.CameraInfo.CAMERA_FACING_FRONT);
        // Solve image inverting problem
        angleToRotate = angleToRotate + 180;
        Bitmap orignalImage = BitmapFactory.decodeByteArray(data, 0, data.length);
        Bitmap bitmapImage = rotate(orignalImage, angleToRotate);
    }

bitmapImage 包含正确的图像。

【讨论】:

  • 在 5 台设备上花费 2 天工作和测试后,我可以说这是解决此问题的最佳方法,因为:stackoverflow.com/a/28024859/1332549。不要忘记照顾镜像照片(我更新了答案)。
  • 如何处理在 Bitmap.createBitmap(..) 方法期间可能引发的 OutOfMemoryException?
  • 在旋转方法上挣扎了一段时间,因为在某些设备上,在纵向模式下添加照片时,简单的旋转会使图像拉伸。找到的解决方案是:code Matrix matrix = new Matrix(); matrix.postTranslate(-width / 2, -height / 2); matrix.postRotate(angle); matrix.postTranslate(width / 2, height / 2);
  • 这很好用,谢谢。把注意力放在这条线上也很棒:结果 = (360 - 结果) % 360; // 补偿镜像,因为我不知道为什么我的照片都是镜像的!
【解决方案4】:

这个应该可以,ExifInterface 不适用于所有制造商,因此请改用 CameraInfo,只需让相机以默认旋转捕获图像,然后在 PictureCallback 上旋转结果数据

private PictureCallback mPicture = new PictureCallback() {
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        File dir = new File(Constant.SDCARD_CACHE_PREFIX);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File pictureFile = new File(Constant.SDCARD_TAKE_PHOTO_CACHE_PREFIX);                       
        try {
            Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
            android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
            android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
            Bitmap bitmap = rotate(realImage, info.orientation);

            FileOutputStream fos = new FileOutputStream(pictureFile);               
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.close();                
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        }

        resultFileUri = Uri.fromFile(pictureFile);
        startEffectFragment();
    }
};

public static Bitmap rotate(Bitmap bitmap, int degree) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    Matrix mtx = new Matrix();
    mtx.postRotate(degree);

    return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
}

【讨论】:

  • 我确认此解决方案不适用于 Nexus 6P Android 8.0
  • @PaoloMoschini 您找到适用于 Android 8 的解决方案了吗?
【解决方案5】:

当您的布局固定为纵向模式时,这是最好的使用方法(如下所述)。

@Override
protected void onResume() {
    super.onResume();
    if (!openCamera(CameraInfo.CAMERA_FACING_BACK)) {
        alertCameraDialog();
    }
    if (cOrientationEventListener == null) {
        cOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {


            public void onOrientationChanged(int orientation) {

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


                    if (orientation == ORIENTATION_UNKNOWN) return;
                    Camera.CameraInfo info =
                            new android.hardware.Camera.CameraInfo();
                    android.hardware.Camera.getCameraInfo(cameraId, info);
                    orientation = (orientation + 45) / 90 * 90;
                    int rotation = 0;
                    if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
                        rotation = (info.orientation - orientation + 360) % 360;
                    } else {  // back-facing camera
                        rotation = (info.orientation + orientation) % 360;
                    }
                        Parameters params = camera.getParameters();
                        params.setRotation(rotation);
                        camera.setParameters(params);




            }

            };


        }


    if (cOrientationEventListener.canDetectOrientation()) {
        cOrientationEventListener.enable();
    }
    }

您将使用 OrientEventListener 并实现此回调方法。 每当方向发生变化时都会调用 onOrientationChanged,因此您的相机旋转将被设置,并且图片将在您保存时旋转。

      private PictureCallback myPictureCallback_JPG = new PictureCallback() 

       { 

   @Override
   public void onPictureTaken(byte[] arg0, Camera arg1) {
    try {  
               File pictureFile = getOutputMediaFile();
           if (pictureFile == null) {
               return;
           }
               FileOutputStream fos = new FileOutputStream(pictureFile);
               fos.write(arg0);
               fos.close();     
              camera.startPreview();
       } catch (Exception e) {
           e.printStackTrace();
       }
   } 
 };

getOutputMediaFile

  private static File getOutputMediaFile() {
            File mediaStorageDir = new File(
               Environment


         .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            "MyCameraApp");
         if (!mediaStorageDir.exists()) {
        if (!mediaStorageDir.mkdirs()) {
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }
    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
            .format(new Date());
    File mediaFile;
    mediaFile = new File(mediaStorageDir.getPath() + File.separator
            + "IMG_" + timeStamp + ".jpg");

    return mediaFile;
  }

来源Here

【讨论】:

    【解决方案6】:

    我没有代表发表评论,所以我不得不留下另一个答案,尽管 Nvhausid 的答案很棒并且值得称赞。简单、优雅,它适用于三星设备上的前置和后置摄像头,而 Exif 和 Media Cursor 不适用。

    对我来说,唯一缺少的答案是处理来自面向用户的摄像头的镜像。

    这里是代码更改:

    Bitmap bitmap = rotate(realImage, info.orientation, info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT);
    

    还有新的旋转方法:

    public static Bitmap rotate(Bitmap bitmap, int degree, boolean mirror) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
    
        Matrix mtx = new Matrix();
        if(mirror)mtx.setScale(1,-1);
        mtx.postRotate(degree);
    
        return Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
    }
    

    【讨论】:

      【解决方案7】:

      我为您找到了强有力的答案,我只是遇到了同样的问题并在不保存文件的情况下解决了它。解决方案是注册一个 OrientationEventListener 以在它发生变化时获取方向。http://www.androidzeitgeist.com/2013/01/fixing-rotation-camera-picture.html 这里给出详细信息。我的代码是如下:

      private CameraOrientationListener myOrientationListener;
      private int rotation;    
      
      protected void onCreate(Bundle savedInstanceState) {
        setListeners();
        rotation = setCameraDisplayOrientation(CameraActivity.this, Camera.getNumberOfCameras()-1, mCamera);
      }
      
      public void setListeners(){
          myOrientationListener = new CameraOrientationListener(this);
          if(myOrientationListener.canDetectOrientation())
              myOrientationListener.enable();
      }
      
      public static int setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
           CameraInfo info = new CameraInfo();
           Camera.getCameraInfo(cameraId, info);
           int rotation = activity.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 == CameraInfo.CAMERA_FACING_FRONT) {
               result = (info.orientation + degrees) % 360;
               result = (360 - result) % 360;  // compensate the mirror
           } else {  // back-facing
               result = (info.orientation - degrees + 360) % 360;
           }
           camera.setDisplayOrientation(result);
      
           return result;
       }
      
      /*
       * record the rotation when take photo
       */
      public void takePhoto(){
          myOrientationListener.rememberOrientation();
          rotation += myOrientationListener.getRememberedOrientation();
          rotation = rotation % 360;
      
          mCamera.takePicture(null, null, mPicture);
      }    
      
      class CameraOrientationListener extends OrientationEventListener {
          private int currentNormalizedOrientation;
          private int rememberedNormalizedOrientation;
      
          public CameraOrientationListener(Context context) {
              super(context, SensorManager.SENSOR_DELAY_NORMAL);
          }
      
          @Override
          public void onOrientationChanged(int orientation) {
              // TODO Auto-generated method stub
              if (orientation != ORIENTATION_UNKNOWN) {
                  currentNormalizedOrientation = normalize(orientation);
              }
          }
      
          private int normalize(int degrees) {
               if (degrees > 315 || degrees <= 45) {
                  return 0;
              }
      
              if (degrees > 45 && degrees <= 135) {
                  return 90;
              }
      
              if (degrees > 135 && degrees <= 225) {
                  return 180;
              }
      
              if (degrees > 225 && degrees <= 315) {
                  return 270;
              }
      
              throw new RuntimeException("The physics as we know them are no more. Watch out for anomalies.");
          }
      
          public void rememberOrientation() {
              rememberedNormalizedOrientation = currentNormalizedOrientation;
          }
      
          public int getRememberedOrientation() {
              return rememberedNormalizedOrientation;
          }
      }
      

      希望对你有帮助:)

      【讨论】:

      • 没用(Nexus 5x)在我的情况下,我需要立即拍照,而您的方法没有帮助。也许是因为听者中没有关于改变方向的事件,因为一切都发生得非常快。同样在您提供的文章中,他们正在创建一个位图,而您却没有……为什么?
      【解决方案8】:

      我使用新的 camera2 api 来获取传感器方向,然后相应地旋转它:

        private void detectSensorOrientation()
        {
          CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
          try
          {
            for (String cameraId : manager.getCameraIdList())
            {
              CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
      
              // We don't use a front facing camera in this sample.
              Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
              if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
              {
                continue;
              }
      
              cameraOrientaion = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
            }
          } catch (CameraAccessException e)
          {
            e.printStackTrace();
          }
        }
      

      然后在 cameraOrientation 参数的帮助下,我旋转了我的 cameraPhoto:

        private void generateRotatedBitmap()
        {
          if (cameraOrientaion != 0)
          {
            Matrix matrix = new Matrix();
            matrix.postRotate(cameraOrientaion);
            rotatedPhoto =
                Bitmap.createBitmap(cameraPhoto, 0, 0, cameraPhoto.getWidth(), cameraPhoto.getHeight(),
                    matrix, true);
            cameraPhoto.recycle();
          }
        }
      

      【讨论】:

      • 我在三星 Edge7 上试过你的代码。我在拍照时调用了DetectSensor 例程,让相机保持垂直。我收到了 90 的 cameraOrientation。当我将相机水平放置时,我收到了相同的值。
      猜你喜欢
      • 2013-10-03
      • 2021-11-22
      • 1970-01-01
      • 2015-05-12
      • 1970-01-01
      • 2014-03-13
      • 1970-01-01
      • 2013-04-14
      • 1970-01-01
      相关资源
      最近更新 更多