Skip to content

Android Custom Camera – Crop image inside Rectangle

I have a custom camera app which have a centered rectangle view, as you can see below:

Camera Screenshot

When I take a picture I want to ignore everything outside the rectangle. The view hasn’t any connection with the Camera Preview or SurfaceView in my XML view, as follows:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
    android:id="@+id/preview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <SurfaceView
        android:id="@+id/cameraview"
        android:name="com.kut.camera.KutCameraFragment"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

    <View android:id="@+id/viewTamanho"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="400px"
        android:layout_marginTop="300px"
        android:layout_marginStart="70px"
        android:layout_marginEnd="70px"
        android:background="@drawable/border" />

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

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:alpha="1"
            android:background="@android:color/black"
            android:orientation="vertical"
            android:padding="10dp" >

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent" >

                <TextView
                    android:id="@+id/textViewReferan"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerHorizontal="true"
                    android:layout_centerVertical="true"
                    android:text="Evidência"
                    android:textColor="@android:color/white"
                    android:textSize="16sp" />

                <Button
                    android:id="@+id/button_exit"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentRight="true"
                    android:layout_alignParentTop="true"
                    android:background="@android:color/transparent"
                    android:text="Sair"
                    android:textColor="#2799CF" />
            </RelativeLayout>

            <LinearLayout
                android:id="@+id/progress_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:gravity="center"
                android:orientation="vertical"
                android:visibility="gone" >

                <ProgressBar
                    android:id="@+id/progressBar1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />

                <TextView
                    android:id="@+id/islem_value_textView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Carregando..." />

            </LinearLayout>
        </LinearLayout>

        <RelativeLayout
            android:id="@+id/RelativeLayout1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:alpha="0.9"
            android:background="@android:color/black"
            android:padding="10dp" >

            <ImageView
                android:id="@+id/imageView_foto"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_centerVertical="true"
                android:src="@drawable/camera" />

            <ImageView
                android:id="@+id/imageView_photo"
                android:layout_width="80dp"
                android:layout_height="100dp"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_centerVertical="true"
                android:layout_marginRight="5dp"
                android:layout_marginEnd="5dp"
                android:padding="5dp"
                android:scaleType="fitCenter"
                android:src="@drawable/fotoicon" />
        </RelativeLayout>
    </RelativeLayout>
</FrameLayout>

Can somebody help me how to crop the image properly? I tried to create a new Bitmap based on my XML but obviously it didn’t work, something like:

Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
    public void onPictureTaken(byte[] data, Camera camera) {
        //.../
        Bitmap imagemOriginal = BitmapFactory.decodeByteArray(data, 0, data.length);
        Bitmap imagemCortada = Bitmap.createBitmap(imagemOriginal, 70, 400, imagemOriginal.getWidth() - 70,
                imagemOriginal.getHeight() - 400);
        //.../
    }

I put those initial values for x and y, and tried to subtract (based on XML values from View referring to marginTop, Bottom, etc…) from width and Height but I hadn’t success because I don’t know how to match View coordinates with the image coordinates taken from Camera. Also, it seems for me Bitmap.createBitmap does limited crop, apparently I can’t crop it to a rectangle directly.

Answer

To control cropping, you must control the picture size and the rectangle size.

It is very important to a) choose the picture size with same aspect ratio as the preview, and b) make sure that the preview is not distorted on the screen. If you prepare your app for a wide range of devices, both tasks are not trivial. To achieve the latter, you need to control both preview size and the SurfaceView size. I have explained this in more detail elsewhere.

To keep it simple, I suggest to ignore the tablets that may have natural Landscape orientation. On phones, the captured image will be “rotated” 90° even if you set camera orientation to portrait (which only effects preview). Don’t expect setRotation() to fix this for you: by the book, it is allowed to simply set the EXIF flag for the captured image.

You set the margins for viewTamanho in px. This may not scale well on variety of screens. I suggest setting the dimensions of this view programmatically, as certain percent of the preview surface. You can use support library to define this in XML, but I am afraid this will not give you enough control.

With all this at hand, let’s say that we have preview of 1280×720, picture of 2560×1440, our screen is 1280×800, and the rectangle is in the middle, 616×400 pixels (which is more or less the size scaled from your screenshot).

The actual preview size on screen will be probably 1000×562, padded on the right and left with black margins of 79px. Then the following code will produce the expected captured picture:

public void onPictureTaken(byte[] data, Camera camera) {
    //.../
    Bitmap imagemOriginal = BitmapFactory.decodeByteArray(data, 0, data.length); // 2560×1440
    float scale = 1280/1000F;
    int left = (int) scale*(imagemOriginal.getWidth()-400)/2;
    int top = (int) scale*(imagemOriginal.getHeight()-616)/2;
    int width = (int) scale*400;
    int height = (int) scale*616;
    Matrix rotationMatrix = new Matrix();
    rotationMatrix.postRotate(90);
    Bitmap imagemCortada = Bitmap.createBitmap(imagemOriginal, left, top, width, height, rotationMatrix, false);
    //.../
}