Camera display / preview in full screen does not maintain aspect ratio - image is skewed, stretched in order to fit on the screen

Paul picture Paul · May 24, 2013 · Viewed 42.3k times · Source

I have developed a small application for displaying camera preview in full screen. I'm using Camera API for this.

This is the activity layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <!-- This is the container for the camera preview screen -->
    <FrameLayout android:id="@+id/camera_preview"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"/>
</LinearLayout>

When device is in portrait, the display is vertically scaled in order to match the height of device screen - so the aspect ratio is not the same as the one from native camera. These are 2 images which explains better what I'm saying:

enter image description hereenter image description here

The first image is made with native camera device. The second image is made with my app, with camera in full screen - the image is skewed, stretched in order to fit on the screen.

I need the camera preview to be full screen, regardless of the preview size given by getSupportedPreviewSizes() mehod and without distortion. Is there a way to accomplish this? Is there a way to maintain proper aspect ratio when camera preview is in full screen? I expected this to be done automatically by OS - cropping the image to match the requested resolution while maintaining aspect ratio, but this does not happen.

I've tried to make the SurfaceView larger than the display (following this question: Fitting a camera preview to a SurfaceView larger than the display), but is not ok in my case since I'm capture snapshots (6 frames/second) and those are not what user sees on the screen (the frame contains all camera preview even if not all of it is visible on the screen).

I posted here: https://www.dropbox.com/s/3d52xt8kazynsae/CameraFullScreen.7z?v=0mcn the entire project I've made.

Any idea/solution is more than important to me. Thanks a lot.

========================================================================

Update due to ss1271 answer:

I'll analyze a little the resolutions you wrote above for Samsung Galaxy Ace II.

I. Screen resolution: 480x800 - aspect ratio 3:5 = 0,6

II. getSupportedPreviewSizes - I'm almost sure that these values are from back camera. Below are the aspects ratio for these resolutions:

   2560x1920   - 0,75

   2560x1536   - 0,60

   2048x1536   - 0,75

   2048x1232   - 0,60

   960x720     - 0,75

   640x480     - 0,75

So, your method will return a Size corresponding to 2560x1536 or to 2048x1232 - these having the same aspect ratio as screen resolution and using these values will not distort the picture. The problem for me is that I cannot use so big resolutions since I capture 6 frames/second and these needs to be saved at a lower resolution.

I'll present below some results from a Samsung S2 device:

I. Screen resolution: 480 x 800 - aspect ratio 3:5 = 0,6

II. Back camera
a). getSupportedPreviewSizes:

800 / 480   - 480/800 = 0,60
800 / 450   - 450/800 = 0,56
720 / 480   - 0,66 
640 / 480   - 0,75
352 / 288   - 0,81
320 / 240   - 0,75
176 / 144   - 0,81

b). Native camera resolutions:

3264 / 2448  - 0,75  - not full screen
3264 / 1968  - 0,60  - FULL SCREEN (since has the same aspect ratio as device screen)
2048 / 1536  - 0,75  - not full screen
2048 / 1232  - 0,60  - FULL SCREEN (same aspect ratio as device screen)
800 / 480    - 0,60  - FULL SCREEN (same aspect ratio as device screen)
640 / 480    - 0, 75 - not full screen

III. Front camera
a). getSupportedPreviewSizes:

640 / 480   - 0,75
352 / 288   - 0,81
320 / 240   - 0,75
176 / 144   - 0,81

b). Native camera is not in full screen and I cannot choose a resolution - the option is disabled.

For S2, back camera, I'm wonder why getSupportedPreviewSizes() method do not return the same resolutions as Native camera does or the ones displayed by native camera are the picture sizes ? I'm wonder why I do not have options like 3264 / 1968, 2048 / 1232 given by getSupportedPreviewSizes() method ? : enter image description here

Answer

dumbfingers picture dumbfingers · May 24, 2013

In a nutshell, you can do a camera preview in full screen however you'll need to find out the appropriate sizes among the supported preview sizes by yourself, only if a customized camera preview is what you want.

For your question, according to the Android Developers -- Camera

If you want to set a specific size for your camera preview, set this in the surfaceChanged() method as noted in the comments above. When setting preview size, you must use values from getSupportedPreviewSizes(). Do not set arbitrary values in the setPreviewSize() method.

It seemed you can't manually pass the size you want other than the sizes provides by getSupportedPreviewSizes(). With a closer examine the sizes supported by your phones camera, you'll find the ratio of sizes supported might not exactly the same as your screen's ratio.

For example, Samsung Galaxy Ace II has 480x800 resolution screen, by reading the Size returned from getSupportedPreviewSizes(), its camera supports:

2560x1920

2560x1536

2048x1536

2048x1232

960x720

640x480

and if you want to display your camera preview in full screen correctly (without stretch), you'll need to calculate, compare and apply the suitable ratio in these supported preview sizes.

Implementation of finding a proper preview size is not that complicated stuff. A common method for doing this would be something like this:

    /**
     * Calculate the optimal size of camera preview
     * @param sizes
     * @param w
     * @param h
     * @return
     */
    private Size getOptimalSize(List<Size> sizes, int w, int h) {

        final double ASPECT_TOLERANCE = 0.2;        
        double targetRatio = (double) w / h;         
        if (sizes == null)             
            return null;          
        Size optimalSize = null;         
        double minDiff = Double.MAX_VALUE;          
        int targetHeight = h;          
        // Try to find an size match aspect ratio and size         
        for (Size size : sizes) 
        {             
//          Log.d("CameraActivity", "Checking size " + size.width + "w " + size.height + "h");            
            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);             
            }         
        }          
        // Cannot find the one match the aspect ratio, ignore the requirement     

        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); 
                }
            }
        }

        SharedPreferences previewSizePref;
        if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
            previewSizePref = getSharedPreferences("PREVIEW_PREF",MODE_PRIVATE);
        } else {
            previewSizePref = getSharedPreferences("FRONT_PREVIEW_PREF",MODE_PRIVATE);
        }

        SharedPreferences.Editor prefEditor = previewSizePref.edit();
        prefEditor.putInt("width", optimalSize.width);
        prefEditor.putInt("height", optimalSize.height);

        prefEditor.commit();

//      Log.d("CameraActivity", "Using size: " + optimalSize.width + "w " + optimalSize.height + "h");            
        return optimalSize;     
    }

And you can also do the similar to find out the suitable camera sizes (the output picture size).

Note: I found the original version of the code above from Internet and I did some modification/optimisation for my own purpose.

Please let me know if this works for you.