8

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.

bl4ck code
  • 95
  • 1
  • 1
  • 8
  • 1
    What exactly didn't work? The major problem with your code is that the original bitmap may be huge and your app can run out of memory. Maybe you did not configure your camera correctly, especially did not call `setPictureSize()` correctly. – Alex Cohn Jan 04 '17 at 18:21
  • I said I don't know how to crop it correctly to fit the coordinates of the rectangle. You see in Bitmap.createBitmap I put initial values for x and y, and try to subtract values from width and height but I can't figure out the correct values. I simply put those values there because that's what I put from marginTop, marginBottom in XML. Also, how can I know what are the correct values for *setPictureSize()* if screen dimensions are ranged. – bl4ck code Jan 05 '17 at 10:10
  • 1
    tehere is croping ui library https://android-arsenal.com/details/1/207 chk it out if you can use it ... or take any inspiration – Aman Satija Jan 05 '17 at 10:23
  • Kindly check this answer which works for me: https://stackoverflow.com/a/63801992/6631601 – Hantash Nadeem Sep 08 '20 at 22:06

2 Answers2

11

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 1000x562, 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);
    //.../
}
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • hi .. its working.. but for specific devices.. how can we make it generalised? – Varun Chandran Mar 26 '18 at 02:50
  • @VarunChandran what generalization do you need? What have you tried? – Alex Cohn Mar 26 '18 at 03:02
  • I think its because of the picture size in camera, i have used setpicturesize(1600,1200). but its not supported in all devices? – Varun Chandran Mar 26 '18 at 06:01
  • How to select picture size? – Varun Chandran Mar 26 '18 at 06:04
  • Right, you must use one of the picture sizes that your device [supports](https://developer.android.com/reference/android/hardware/Camera.Parameters.html#getSupportedPictureSizes()). As I explained above, *choose a picture size with same aspect ratio as the preview*. After that, I hope you can redo the arithmetics. The tricky part is to calculate the actual preview size on the screen, over which the rectangle is drawn. – Alex Cohn Mar 26 '18 at 08:54
  • "choose a picture size with same aspect ratio as the preview", can you please explain this with example??? – Varun Chandran Mar 26 '18 at 12:19
  • Often, Android cameras support both 'traditional' 4:3 picture sizes (e.g. 640x480, 1600x1200, *etc.*) and 'wide' 16:9 picture sizes (e.g. 1280x720). Usually, you want a picture size higher than the preview size, and the Camera API does not complain if you mix 16:9 picture with 4:3 preview, but such mix produces bad results when you need to manipulate the captured image based on what you know about preview images. – Alex Cohn Mar 26 '18 at 15:06
  • float scale = 1280/1000F;? how is this selected?? – Varun Chandran Mar 27 '18 at 13:05
  • If your preview surface is 1000 pixels high, and the camera preview is 1280 pixels high, then the scale in above formulae is 1.28 (= 1280/1000F). Note that the preview in above example was rotated by 90º, so 1280 was actually the **width** used for setPreviewSize(). To compensate for that, I add the rotation matrix to create the bitmap. – Alex Cohn Mar 27 '18 at 13:18
  • Is there any mobile which doesn't support 640 x 480 picture size? – Varun Chandran Mar 27 '18 at 13:48
  • I have not seen one yet, but never say never. And note that all devices I ever used support 640x480 *preview*, too. So, there is little reason to **takePicture()** of that size: you can pick this image from preview, usually resulting in a sleeker UX. – Alex Cohn Mar 27 '18 at 14:12
  • Thanks a lot. any other picture size crashes in some devices. This remains fine till now. – Varun Chandran Mar 28 '18 at 05:20
  • imageOriginal = BitmapFactory.decodeByteArray(data, 0, data.length); float scale = 1280 / 1000; left = (int) (scale * (imageOriginal.getWidth() - 180) / 2); top = (int) (scale * (imageOriginal.getHeight() - 320) / 2); width = (int) (scale * 320); height = (int) (scale * 320); //} Bitmap imageConverted = Bitmap.createBitmap(imageOriginal, left, top, width, height, null, false); This is what i used to crop image, its wrking perfectly in 4.7 display, but little bigger in 5.5 – Varun Chandran Mar 28 '18 at 05:21
  • On your 5.5" device, the preview surface is probably not 1000px high, and/or the crop rectangle is probably not 320x180px. – Alex Cohn Mar 28 '18 at 09:16
  • So that means, we have to write different calc for different device screens or anyother method to generalise it in this code?? – Varun Chandran Mar 28 '18 at 09:36
  • 2
    https://stackoverflow.com/questions/48642695/capture-image-using-dynamic-co-ordinates-through-the-camera please try to answer this – Varun Chandran Mar 28 '18 at 12:05
  • @AlexCohn sir can you please tell, that how you are calculating preview size 1000, when your camera preview is 1028 pixels high? – Babar Shamsi Mar 31 '21 at 06:56
  • @AlexCohn and why are you dividing by 2, i am doing by 3 then getting a proper result, so can you please tell the reason – Babar Shamsi Mar 31 '21 at 07:46
  • @BabarShamsi 1000 was derived for the very specific scenario of the original question; it's natural that in your case this may be different; also, the scaling factor (2 or 3) depends on your specific needs. I am glad if you could adopt this answer to your scenario and get proper results! – Alex Cohn Mar 31 '21 at 16:50
1

enter image description here

Here i used this

implementation 'com.otaliastudios:cameraview:2.6.2'

library for getting bitmap image after taking picture.

Here is the code how i achieved this

public void croppingRectangle (Bitmap bitmap, CameraView mCameraView, FrameLayout rectangle) {

int cameraHeight = mCameraView.getHeight();
int cameraWidth = mCameraView.getWidth();

    DisplayMetrics displayMetrics = new DisplayMetrics();
    getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

    int leftAndRightMarginsInPixels = Math.round(Math.round(getResources().getDimension(Your_dimens) * 2));`

here i did scaling, after taking picture

    int left = (int) Math.round(((double)rectangle.getX()/(double)cameraWidth) * bitmap.getWidth();
    int top = (int) Math.round(((double)rectangle.getY()/(double)cameraHeight) * bitmap.getHeight();
    int width =
            (int) (capturedWidth - Math.round(((double)leftAndRightMarginsInPixels / (double) displayMetrics.widthPixels) * bitmap.getWidth())); 
    int height = (int) Math.round(((double) rectangle.getHeight() / (double) displayMetrics.heightPixels) * bitmap.getHeight());

    Matrix rotationMatrix = new Matrix();
    rotationMatrix.postRotate(0);
    Bitmap croppedImage = Bitmap.createBitmap(bitmap, left, top, width, height, rotationMatrix, false);
}

P.S : I have achieved 95% accuracy for top & bottom, 70% in getting width of a frame

  • How did you get the above rectangular view? I used the same lib it's simply opening the camera without controls. How can I enable them? I used it in XML as mentioned here: https://github.com/natario1/CameraView#setup – Shailendra Madda Jun 12 '21 at 10:40
  • Hey @ShailendraMadda , you need to get deep dive into this library, and for capture image using cameraView you have to use it's method mentioned in library. I mentioned above was about cropping image after capturing picture. Thanks! let me know if you have any questions :) cheers – Babar Shamsi Jun 24 '21 at 17:22
  • May I know what is the method to enable the controls which is showing the preview images here https://github.com/natario1/CameraView#setup? – Shailendra Madda Jun 24 '21 at 17:32
  • i set my custom images(drawables) using FrameLayout, on top of mCameraView, library does not support these type of UI stuff. you have to do by yourself. thanks :) – Babar Shamsi Jun 25 '21 at 05:44
  • This method you can override after taking picture. `@Override public void onPictureTaken(@NonNull PictureResult result) { super.onPictureTaken(result); stopCamera(); processPicture(result.getData()); }` – Babar Shamsi Jun 25 '21 at 05:47
  • 1
    Yeah, that's what I am wondering why they have not provided that UI or controls as showing the pictures. I already did as you said and it's working too. But I want to know can we enable these icons in the library like Edit, Capture, Snap, video icons etc as showing in the pictures. – Shailendra Madda Jun 25 '21 at 09:53
  • This library i think mainly focused on Camera Handling, and bitmap pics after taking picture, idk about video stuff though. But for UI purpose you have to work manually. :) – Babar Shamsi Jun 25 '21 at 14:18
  • Thanks, I did the same but checking is there any possibility to enable them. Also, I have noticed why they provided all the XML elements to enable or disable if there are no controls? Example: `app:cameraHdr="on|off"` `app:cameraFlash="on|auto|torch|off"` – Shailendra Madda Jun 25 '21 at 14:25
  • They would have controls, but it would be for picture quality, and enable/disable any camera functionalities etc. But not specific for UI elements, like drawing of rectangle and capture button etc. – Babar Shamsi Jun 25 '21 at 18:42
  • I don't see those buttons too. – Shailendra Madda Jun 27 '21 at 03:41