3

I have an app that takes picture in portrait mode and everything is working except that the preview is a different size than the image after the picture is take. The preview seems distorted and blurry but fills the entire screen. After the picture is taken, the image is clear but is either shorter than the device height or the width is smaller (depending on what device is being used).

Below are some image examples and some code that I'm using.

Before Picture Taken: pic1

After Picture Taken: pic2

Camera Preview Code to Show in Portrait:

protected int getRotationDegrees() {
    android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(0, info);
    int rotation = ((WindowManager) mContext
            .getSystemService(Context.WINDOW_SERVICE)).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 = (info.orientation - degrees + 360) % 360;
    return result;
}

Code to Rotate Image After Picture Taken:

Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        Bitmap pictureBitmap = null;

        pictureBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

        int rotationDegrees = mCameraPreview.getRotationDegrees();
        //mRotation = setCameraDisplayOrientation(MainActivity.this, Camera.CameraInfo.CAMERA_FACING_BACK, mCamera);
        Matrix matrix = new Matrix();
        matrix.postRotate(rotationDegrees);

        //Bitmap scaledBitmap = Bitmap.createScaledBitmap(pictureBitmap ,previewWidth ,previewHeight ,true);
        //Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap , 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
        Bitmap rotatedBitmap = Bitmap.createBitmap(pictureBitmap , 0, 0, pictureBitmap.getWidth(), pictureBitmap.getHeight(), matrix, true);

        //rotatedBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        ImageView previewImage = (ImageView) findViewById(R.id.preview_image);
        previewImage.setImageBitmap(rotatedBitmap);
        mCameraPreview.setVisibility(View.INVISIBLE);
        mButtonCapture.setVisibility(View.INVISIBLE);
        //flCameraPreview.setVisibility(View.INVISIBLE);
    }
};

Has anyone come across this issue before? I'm still trying to understand the camera API. Thanks in advance!

EDIT: Code that I use to select my preview and picture sizes.

private Camera.Size getOptimalSize(List<Camera.Size> sizes, int h, int w) {
    final double ASPECT_TOLERANCE = 0.05;
    double targetRatio = (double) w/h;

    if (sizes == null) {
        return null;
    }

    Camera.Size optimalSize = null;

     double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    for (Camera.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 (Camera.Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }

    return optimalSize;
}
Jay
  • 324
  • 2
  • 14

2 Answers2

2

I found this comment in another post that solved my issue!

It changes the size of the view so that the image isn't stretched while always trying to fill the screen.

Community
  • 1
  • 1
Jay
  • 324
  • 2
  • 14
0

This situation is quite typical. The aspect ratio of preview frame may not be exactly the same as the captured image. You probably iterate through the supported preview sizes to choose one that best fits your SurfaceView (I hope you do this correctly). You need similar iteration through the supported picture sizes. If you cannot find a satisfactory pair where the aspect ratios are the same, you need to "crop" visually either the SurfaceView or the ImageView. The easiest way to achieve such crop is to overlay the unwanted parts of the view with some other non-transparent view.

It is also possible to force a view (SurfaceView or ImageView) to "hang out", be partially out of the screen.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Do you have any examples of hanging an image over the edge? I don't think that cropping the view will work for me, because the preview image looks to have a different aspect ratio than the picture taken (the objects look shorter and squatty in the preview). I've added the code that I use to select my sizes in the original post. Also, I'm setting the preview to a FrameLayout, not a SurfaceView or ImageView. Is this correct and working because my preview class extends SurfaceView? – Jay Nov 23 '15 at 16:07
  • I don't see your project, but I guess that you add a view derived from SurfaceView to a FrameLayout. This makes it very easy to "hang out": first choose the preview size, and after that add this view to the frame, with negative margins. Same holds for the ImageView. I also recommend to setPictureSize() to best suiting supported size. – Alex Cohn Nov 23 '15 at 17:21
  • Great, thanks. Is it just guess and check and then hard-code the negative margins on the view? Or can I do that programmatically with the sizes that I choose for setPreviewSize and setPictureSize? – Jay Nov 23 '15 at 18:03
  • No guess, you can know the size of the FrameLayout and you should know the chosen sizes for preview and picture. Only keep in mind that Android will do scale-down for you, so for 1920x1080 preview it is perfectly OK if your SurfaceView after applying the negative margins is 1280x720. – Alex Cohn Nov 23 '15 at 18:55