15

In portrait mode, the images look vertically stretched and in landscape mode it looks horizontally stretched.

Although after capturing the image appears in proper size.

How to resolve this issue?

MKJParekh
  • 34,073
  • 11
  • 87
  • 98
Manjeet
  • 175
  • 1
  • 1
  • 9

6 Answers6

8

You need to choose a preview size that matches your display size. I would suggest changing the preview size setting to match your SurfaceView rather than the other way around. While the preview data is just fine, it's not distorted, it will look distorted when flung onto a surface with different aspect ratio.

If you have a full-screen view, then you should find the camera has a preview size matching that size -- at the least there will be one with the same aspect ratio. For example if your screen is 640x480 then a 320x240 preview size will not appear stretched on a full-screen SurfaceView.

Sean Owen
  • 66,182
  • 23
  • 141
  • 173
  • To set preview size I am using following code but still surface looks stretched:private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) { Camera.Size result = null; for (Camera.Size size : parameters.getSupportedPreviewSizes()) { if (size.width <= width && size.height <= height) { if (result == null) { result = size; } else { int resultArea = result.width * result.height; int newArea = size.width * size.height; if (newArea > resultArea) { result = size; } } } } return (result); } – Manjeet Jun 30 '12 at 14:58
  • Why are you doing it this way? just look for a simple exact match first. Looks like you are copying very old code from Barcode Scanner. – Sean Owen Jun 30 '12 at 21:27
4

You have to constrain your preview size based on (1) available preview sizes (2) your view. Here is my solution if you still need it:

private class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    private void startPreview() {
        try {
        /**
         * Orientation should be adjusted, see http://stackoverflow.com/questions/20064793/how-to-fix-camera-orientation/26979987#26979987
         */

            Camera.Parameters parameters = mCamera.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;
                }
            }

            Log.v(TAG, "preview size: " + previewSize.width + " / " + previewSize.height);
            parameters.setPreviewSize(previewSize.width, previewSize.height);
            mCamera.setParameters(parameters);
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }
 }
Louis GRIGNON
  • 699
  • 6
  • 17
3

I fixed this by adding this before calling camera.startPreview():

Camera.Parameters parameters = camera.getParameters(); 
parameters.setPreviewSize(yourSurfaceView.getWidth(), yourSurfaceView.getHeight());
camera.setParameters(parameters);

It might help someone.

Yellos
  • 1,657
  • 18
  • 33
  • 11
    In many cases this throw a Runtime exception (setParameters failed) because surfaceView's size doesn't match any supported preview size. – Marino Aug 26 '14 at 00:04
  • You must set the preview size as provided by getSupportedPreviewSize() this is according to the docs. It says you must not set any arbitrary values on setPreviewSize(). – Neon Warge Jul 12 '16 at 06:17
0

just add the following function to set the aspect ratio and preview size

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

    if (sizes == null) return null;

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

    int targetHeight = h;

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

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

use like this

 final CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
    try {
        final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);

        final StreamConfigurationMap map =
                characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);


        // For still image captures, we use the largest available size.
        final Size largest =
                Collections.max(
                        Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)),
                        new CompareSizesByArea());

        sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);

        // Danger, W.R.! Attempting to use too large a preview size could  exceed the camera
        // bus' bandwidth limitation, resulting in gorgeous previews but the storage of
        // garbage capture data.
       /* previewSize =
                chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                        inputSize.getWidth(),
                        inputSize.getHeight());*/

        previewSize = getOptimalPreviewSize(Arrays.asList(map.getOutputSizes(SurfaceTexture.class)),textureView.getWidth(),textureView.getHeight());
    } catch (final CameraAccessException e) {
        Log.e(TAG, "Exception!" + e);
    } catch (final NullPointerException e) {
        // Currently an NPE is thrown when the Camera2API is used but not supported on the
        // device this code runs.
        // TODO(andrewharp): abstract ErrorDialog/RuntimeException handling out into new method and
        // reuse throughout app.
        ErrorDialog.newInstance(getString(R.string.camera_error))
                .show(getChildFragmentManager(), FRAGMENT_DIALOG);
        throw new RuntimeException(getString(R.string.camera_error));
    }
-1

The solution is simple! If you use surfaceview under actionbar this maybe the problem. I use this line and i can fix, but i not test using action bar.

use this:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
-3

If you are talking about preview, try setting the SurfaceView's size same as Camera's preview size. That way the preview shouldn't be scaled.

jpa
  • 168
  • 4