使用Android相机拍摄的照片奇怪

时间:2014-12-03 09:42:37

标签: android camera

我正在开发Android应用程序,使用Android Camera API打开预览,并从中获取照片。应用程序必须仅在纵向模式下工作,并且必须可以同时使用设备的前后摄像头(如果设备同时具有两个摄像头)。

我已在我的应用中打开预览,我已正确设置了显示方向(使用方法:mCamera.setDisplayOrientation(90)将其旋转90度),以便能够以纵向模式查看应用程序预览,而我添加了一个按钮,可以在前后摄像头之间切换。所有这些都在应用程序中正常工作。

当我拍照时问题是:为了以正确的方式(以肖像方式)旋转拍摄的照片,我得到了设备的方向,并将图片旋转为获得的方向。但是,当照片保存到图库时,照片尺寸很奇怪:如果使用前置摄像头拍摄照片,则全屏显示,而如果照片是使用后置摄像头拍摄的话。我的目标是始终全屏拍照。

这两个截图显示了问题:

--- BACK-CAMERA照片:

BACK-CAMERA PHOTO

--- FRONT-CAMERA照片:

FRONT-CAMERA PHOTO

这里有我的CameraPreview代码:

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    if (mHolder.getSurface() == null){
        return;
    }

    try {
        mCamera.stopPreview();
    } catch (Exception e){
    }

    try {
        // Start preview in portrait mode
        mCamera.setDisplayOrientation(90);

        // Set the list of supported preview size in the related variable
        if(mCamera != null){
            if(mCamera.getParameters().getSupportedPreviewSizes() != null){
                mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            }
        }

        // Get the parameters of camera
        Camera.Parameters parameters = mCamera.getParameters();

        // Set output format to NV21 (which is guranteed to be supported on all devices)
        parameters.setPreviewFormat(ImageFormat.NV21);

        // Set the correct preview size (after applying the getOptimalPreviewSize)
        if(mPreviewSize != null) {
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            Log.d(TAG,"Preview size is ("+mPreviewSize.width+";"+mPreviewSize.height+")");
            // initializing bitmap and pixels
            bitmap = Bitmap.createBitmap(mPreviewSize.width, mPreviewSize.height, Bitmap.Config.ARGB_8888);
            pixels = new int[mPreviewSize.width * mPreviewSize.height];
        }

        // Correct the size - orientation of picture taken
        if(isTablet(getContext()) == Boolean.FALSE){
            onOrientationChanged(getScreenRotationOnPhone(),parameters);
        }else{
            onOrientationChanged(getScreenRotationOnTablet(),parameters);
        }

        mCamera.setPreviewDisplay(mHolder);
        // Set to turn the Flash ON
        // parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);

        mCamera.setParameters(parameters);
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();

        // Call the setPreviewCallback and onPreviewFrame to get the incoming frame
        mCamera.setPreviewCallback(new Camera.PreviewCallback() {
            @Override
            public void onPreviewFrame ( byte[] data, Camera camera){
                Log.i(TAG, "Ma entro nella onPreviewFrame?");
                Camera.Parameters parameters = mCamera.getParameters();
                int format = parameters.getPreviewFormat();
                Log.i(TAG, "Il formato del frame e': " + format);
                //YUV formats require more conversion
                if (format == ImageFormat.NV21 || format == ImageFormat.YUY2 || format == ImageFormat.NV16) {
                    int w = parameters.getPreviewSize().width;
                    int h = parameters.getPreviewSize().height;                        
                }
            }
        });

    } catch (Exception e){
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
}

private int getScreenRotationOnPhone() {
    final Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

    if(display.getRotation() == Surface.ROTATION_0){
        System.out.println("SCREEN_ORIENTATION_PORTRAIT");
        return Surface.ROTATION_0;
    }else if(display.getRotation() == Surface.ROTATION_90){
        System.out.println("SCREEN_ORIENTATION_LANDSCAPE");
        return Surface.ROTATION_90;
    }else if(display.getRotation() == Surface.ROTATION_180){
        System.out.println("SCREEN_ORIENTATION_REVERSE_PORTRAIT");
        return Surface.ROTATION_180;
    }else if(display.getRotation() == Surface.ROTATION_270){
        System.out.println("SCREEN_ORIENTATION_REVERSE_LANDSCAPE");
        return Surface.ROTATION_270;
    }else{
        System.out.println("SCREEN_ORIENTATION_NOT_ADMISSIBLE");
        return -1;
    }
}

private int getScreenRotationOnTablet() {
    final Display display = ((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

    if(display.getRotation() == Surface.ROTATION_0){
        System.out.println("SCREEN_ORIENTATION_LANDSCAPE");
        return Surface.ROTATION_0;
    }else if(display.getRotation() == Surface.ROTATION_90){
        System.out.println("SCREEN_ORIENTATION_REVERSE_PORTRAIT");
        return Surface.ROTATION_90;
    }else if(display.getRotation() == Surface.ROTATION_180){
        System.out.println("SCREEN_ORIENTATION_REVERSE_LANDSCAPE");
        return Surface.ROTATION_180;
    }else if(display.getRotation() == Surface.ROTATION_270){
        System.out.println("SCREEN_ORIENTATION_PORTRAIT");
        return Surface.ROTATION_270;
    }else{
        System.out.println("SCREEN_ORIENTATION_NOT_ADMISSIBLE");
        return -1;
    }
}

public boolean isTablet(Context context) {
    boolean xlarge = ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == 4);
    boolean large = ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE);
    return (xlarge || large);
}

public void onOrientationChanged(int orientation, Camera.Parameters mParameters) {
    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(CameraActivity.getOpenedCamera(), info);
    Log.i(TAG, "onOrientationChanged -> Camera opened actually is: "+CameraActivity.getOpenedCamera());
    orientation = (orientation + 45) / 90 * 90;
    int rotation = 0;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        rotation = (info.orientation - orientation + 360) % 360;
    } else {  // back-facing camera
        rotation = (info.orientation + orientation) % 360;
    }
    Log.i(TAG, "onOrientationChanged -> Orientation of pictures setted to: "+rotation);
    mParameters.setRotation(rotation);
}

这里有CameraActivity的onPictureTaken代码和切换摄像头的方法:

@Override
public void onPictureTaken(byte[] data, Camera camera) {
    // TODO Auto-generated method stub
    File pictureFile = Utility.getOutputMediaFile();
    if (pictureFile == null){
        Toast.makeText(this, "Couldn't create file", Toast.LENGTH_SHORT).show();
        Log.d(TAG,"Couldn't create file");
        return;//?
    }else{
        try{
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.flush();
            fos.close();
        }
        catch (FileNotFoundException e){
            Toast.makeText(this, "File not found exception", Toast.LENGTH_SHORT).show();
            Log.d(TAG,"File not found: "+e.getMessage());
        }
        catch (IOException e){
            Toast.makeText(this, "IO Exception", Toast.LENGTH_SHORT).show();
            Log.d(TAG, "Error accessing file: "+e.getMessage());
        }
        //Per farle comparire subito nella cartella le foto:
        this.mPictureFile = pictureFile;
        MediaScannerConnection.scanFile(getApplicationContext(),
                new String[]{this.mPictureFile.toString()}, null,
                new MediaScannerConnection.OnScanCompletedListener() {
                    public void onScanCompleted(String path, Uri uri) {
                        Log.i(TAG, "ExternalStorage Scanned " + path + ":");
                        Log.i(TAG, "ExternalStorage -> uri=" + uri);
                    }
                });
        camera.startPreview();
        imageSaved.sendEmptyMessage(0);
    }
}

public void switchCam(){
    if (hasFrontCam == Boolean.TRUE && hasBackCam == Boolean.TRUE) {
        // The phone has front camera and back camera
        if(openedCam==Camera.CameraInfo.CAMERA_FACING_BACK){
            //Chiudi la preview
            if (mCamera!=null){
                mCamera.setPreviewCallback(null);
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
            }
            //Apri la nuova camera
            mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
            init();
            openedCam=Camera.CameraInfo.CAMERA_FACING_FRONT;
        }else{
            //openedCam==Camera.CameraInfo.CAMERA_FACING_FRONT
            //Chiudi la preview
            if (mCamera!=null){
                mCamera.setPreviewCallback(null);
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
            }
            //Apri la nuova camera
            mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
            //riprendi la preview
            init();
            openedCam=Camera.CameraInfo.CAMERA_FACING_BACK;
        }
    }
}

使用这种实用方法:

/** Create a File for saving an image */
public static File getOutputMediaFile(){
    String state = Environment.getExternalStorageState();
    if(state.equals(Environment.MEDIA_MOUNTED)){
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyApp");
        if (!mediaStorageDir.exists()){
            if (!mediaStorageDir.mkdirs()){
                Log.d(TAG,"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;
    }else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        Log.d(TAG,"External storage not writable but only readable");
        return null;
    }else{
        Log.d(TAG,"External storage not writable");
        return null;
    }
}

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

1 个答案:

答案 0 :(得分:0)

我已经解决了这个问题。

我在背面和前置摄像头支持的图片尺寸列表中找到了解决方案。

在我的情况下,我使用的是三星S4进行开发,支持的背景照片尺寸列表如下:

0) (4128*3096) aspect_ratio=1.333 ---> Choosen automatically
1) (4128*2322) aspect_ratio=1.777
2) (3264*2448) aspect_ratio=1.333
3) (3264*1836) aspect_ratio=1.777
4) (2048*1536) aspect_ratio=1.333
5) (2048*1152) aspect_ratio=1.777
6) (1280*720) aspect_ratio=1.777
7) (640*480) aspect_ratio=1.777

对于前置摄像头:

0) (1920*1080) aspect_ratio=1.777 ---> Choosen automatically
1) (1440*1080) aspect_ratio=1.333
2) (1280*720) aspect_ratio=1.777
3) (960*720) aspect_ratio=1.333
4) (720*480) aspect_ratio=1.5
5) (640*480) aspect_ratio=1.333
6) (320*240) aspect_ratio=1.333

假设尺寸为(宽度;高度),纵横比计算为:宽度/高度。

Android会自动为前后摄像头选择最佳图片尺寸;它不会检查前后摄像头选择的尺寸是否具有相同的宽高比。但纵横比必须相同,才能在相同尺寸的图库照片中使用。