0

I am working on a project where image is capturing from Android camera. Everything works fine.

But now one issue is facing that when preview is showing that is fine but after capturing the image both are not 100% same. After capturing image sometimes image is stretch or squeezed.

CameraPreview class:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private Context mContext;
    float mDist = 0;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mContext = context;
        mCamera = camera;
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // create the surface and start camera preview
            if (mCamera == null) {
                Camera.Parameters params = mCamera.getParameters();
                if (params.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
                    params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                }
                mCamera.setParameters(params);
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            }
        } catch (IOException e) {
            Log.d(VIEW_LOG_TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void refreshCamera(Camera camera) {
        if (mHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }
        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }
        // set preview size and make any resize, rotate or
        // reformatting changes here
        // start preview with new settings
        setCamera(camera);

        // TODO: don't hardcode cameraId '0' here... figure this out later.
        //setCameraDisplayOrientation(mContext, Camera.CameraInfo.CAMERA_FACING_FRONT, mCamera);

        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setDisplayOrientation(90);
            mCamera.startPreview();
        } catch (Exception e) {
            Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    public static void setCameraDisplayOrientation(Context context, int cameraId, Camera camera) {
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        int rotation = wm.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;
        }

        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            // Compensate for the mirror image.
            result = (360 - result) % 360;
        } else {
            // Back-facing camera.
            result = (info.orientation - degrees + 360) % 360;
        }
        camera.setDisplayOrientation(result);
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        refreshCamera(mCamera);
    }

    public void setCamera(Camera camera) {
        //method to set a camera instance
        mCamera = camera;
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        // mCamera.release();

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Get the pointer ID
        Camera.Parameters params = mCamera.getParameters();
        int action = event.getAction();

        if (event.getPointerCount() > 1) {
            // handle multi-touch events
            if (action == MotionEvent.ACTION_POINTER_DOWN) {
                mDist = getFingerSpacing(event);
            } else if (action == MotionEvent.ACTION_MOVE
                    && params.isZoomSupported()) {
                mCamera.cancelAutoFocus();
                handleZoom(event, params);
            }
        } else {
            // handle single touch events
            if (action == MotionEvent.ACTION_UP) {
                handleFocus(event, params);
            }
        }
        return true;
    }

    private void handleZoom(MotionEvent event, Camera.Parameters params) {
        int maxZoom = params.getMaxZoom();
        int zoom = params.getZoom();
        float newDist = getFingerSpacing(event);
        if (newDist > mDist) {
            // zoom in
            if (zoom < maxZoom)
                zoom++;
        } else if (newDist < mDist) {
            // zoom out
            if (zoom > 0)
                zoom--;
        }
        mDist = newDist;
        params.setZoom(zoom);
        mCamera.setParameters(params);
    }

    public void handleFocus(MotionEvent event, Camera.Parameters params) {
        int pointerId = event.getPointerId(0);
        int pointerIndex = event.findPointerIndex(pointerId);
        // Get the pointer's current position
        float x = event.getX(pointerIndex);
        float y = event.getY(pointerIndex);

        List<String> supportedFocusModes = params.getSupportedFocusModes();
        if (supportedFocusModes != null
                && supportedFocusModes
                .contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            mCamera.autoFocus(new Camera.AutoFocusCallback() {
                @Override
                public void onAutoFocus(boolean b, Camera camera) {
                    // currently set to auto-focus on single touch
                }
            });
        }
    }

    /** Determine the space between the first two fingers */
    private float getFingerSpacing(MotionEvent event) {
        // ...
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float)Math.sqrt(x * x + y * y);
    }
}

Below is the xml layout where inside FrameLayout, camera preview under LinearLayout(@+id/cameraFrame) is used.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:baselineAligned="false"
    tools:context=".view.activity.PhotoCaptureActivity">

    <RelativeLayout
        android:id="@+id/mainLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="0dp"
            android:layout_marginLeft="0dp"
            android:layout_marginRight="0dp"
            android:layout_marginTop="0dp"
            android:orientation="horizontal"
            android:weightSum="3">

            <FrameLayout
                android:id="@+id/cameraFrame_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <LinearLayout
                    android:id="@+id/cameraFrame"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="horizontal" />

                <ImageView
                    android:id="@+id/captured_image"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:scaleType="fitXY"
                    android:scaleX="-1"
                    />

                <ImageView
                    android:id="@+id/captured_image_back"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical"
                    android:scaleType="fitXY"
                    android:scaleX="-1"
                    android:scaleY="-1"
                    android:visibility="gone"/>

                <ImageButton
                    android:id="@+id/button_capture"
                    android:layout_width="64dp"
                    android:layout_height="64dp"
                    android:layout_gravity="center_horizontal|bottom"
                    android:layout_marginBottom="5dp"
                    android:background="@drawable/ic_camera_capture_image" />

                <ImageButton
                    android:id="@+id/swicth_camera"
                    android:layout_width="36dp"
                    android:layout_height="36dp"
                    android:layout_gravity="right|bottom"
                    android:layout_marginBottom="10dp"
                    android:layout_marginEnd="20dp"
                    android:background="@drawable/ic_camera_switch" />

                <ImageButton
                    android:id="@+id/flash_on"
                    android:layout_width="36dp"
                    android:layout_height="36dp"
                    android:layout_gravity="left|bottom"
                    android:layout_marginBottom="10dp"
                    android:layout_marginStart="20dp"
                    android:background="@drawable/ic_gallery"
                    android:visibility="gone"/>

                <ImageButton
                    android:id="@+id/fab_send_photo"
                    android:layout_width="48dp"
                    android:layout_height="48dp"
                    android:layout_gravity="right|bottom"
                    android:layout_marginBottom="10dp"
                    android:layout_marginEnd="20dp"
                    app:srcCompat="@drawable/ic_send_blue"
                    android:visibility="gone" />

            </FrameLayout>

        </LinearLayout>

    </RelativeLayout>
</RelativeLayout>

I have shared my Activity class, Camera Preview class and xml file in google drive. https://drive.google.com/drive/folders/1WETYNhUZiTZOOChzEynRC-cEB6RsYrN5?usp=sharing

Before capturing image:

enter image description here

After capturing image:

enter image description here

You can see in the left hand side letter'F' is now visible after capturing the image. But letter 'F' was not in the preview. And top bar "hp" logo vanishes after capturing image but "hp" logo was in the preview.

I tried few solutions from stack overflow but it did not suit in my case. When I tried Android Camera Preview Stretched solution, then camera preview was not showing so it was not possible for me to identify whether it was solved or not.

Please let me know what can done to get a optimal solution for this issue?

Saidur Rahman
  • 546
  • 2
  • 4
  • 20
  • I have shared my Activity class, Camera Preview class and xml file in google docs -> [**Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers.**](https://stackoverflow.com/help/on-topic) – 2Dee Sep 25 '18 at 08:17
  • @2Dee may I know what you have not understood? I think is very much clear that from image You can see letter 'F' is now visible after capturing the image. But letter 'F' was not in the preview. – Saidur Rahman Sep 25 '18 at 10:57
  • ok, What is Size of your cameraview ? – Abhay Koradiya Oct 06 '18 at 05:40
  • @AbhayKoradiya my cameraview size is - android:layout_width="match_parent" and android:layout_height="match_parent". I am using huawei y7 pro 2018 where width is 720dp and height is 1360dp is coming in debug. I have also added xml layout for your reference. – Saidur Rahman Oct 06 '18 at 10:42
  • This issue occur due to preview Aspect ratio. – Abhay Koradiya Oct 06 '18 at 10:45
  • You need to crop Bitmap as You want. – Abhay Koradiya Oct 06 '18 at 10:45
  • @AbhayKoradiya it would be great if you could share the guideline how to crop bitmap to solve this aspect ratio issue. Note: I tried few of the solutions from this link https://stackoverflow.com/questions/19577299/android-camera-preview-stretched but somehow I did not get the solution. – Saidur Rahman Oct 06 '18 at 10:53

1 Answers1

0

You need to crop bitmap after taking image. Just put

public static void cropToJpeg2(final byte[] jpeg, final AspectRatio targetRatio, final int jpegCompression, final CameraUtils.BitmapCallback callback) {

final Handler ui = new Handler();
WorkerHandler.run(new Runnable() {
    @Override
    public void run() {
        Bitmap image = decodeBitmap(jpeg, Integer.MAX_VALUE, Integer.MAX_VALUE);
        Rect cropRect = computeCrop(image.getWidth(), image.getHeight(), targetRatio);
        final Bitmap crop = Bitmap.createBitmap(image, cropRect.left, cropRect.top, cropRect.width(), cropRect.height());
        image.recycle();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        crop.compress(Bitmap.CompressFormat.JPEG, jpegCompression, out);

        ui.post(new Runnable() {
            @Override
            public void run() {
                callback.onBitmapReady(crop);
            }
        });

    }
});
}

computeCrop Functuion

private static Rect computeCrop(int currentWidth, int currentHeight, AspectRatio targetRatio) {
    AspectRatio currentRatio = AspectRatio.of(currentWidth, currentHeight);
    logger.e("CropHelper", "computeCrop: currentRatio " + currentRatio);
    logger.e("CropHelper", "computeCrop: targetRatio " + targetRatio);
    int x, y, width, height;
    if (currentRatio.toFloat() > targetRatio.toFloat()) {
        height = currentHeight;
        width = (int) (height * targetRatio.toFloat());
        y = 0;
        x = (currentWidth - width) / 2;
    } else {
        width = currentWidth;
        height = (int) (width / targetRatio.toFloat());
//            y = (currentHeight - height) / 2;
        y = 0;  //change above line for crop exact image from camera (remove heading).
        x = 0;
    }
    return new Rect(x, y, x + width, y + height);
}

check WorkerHandler class.

NOTE :- decodeBitmap Function return Bitmap After Exif Calculation for Rotation Purpose.

Abhay Koradiya
  • 2,068
  • 2
  • 15
  • 40