Android Tutorial Take pictures with the internal camera

Status
Not open for further replies.

Erel

Administrator
Staff member
Licensed User
It is recommend to use the new CameraEx class. It provides more functionality.
The new Camera library allows you to take pictures using the back facing camera.

The following steps are required for taking a picture:
- Initialize a camera object. The Initialize method expects a Panel. The preview images will be displayed on this panel.
- Wait for the Ready event.
- In the Ready event Sub, call Camera.StartPreview to show the images preview.
- Call TakePicture to take a picture.
- The PictureTaken event will be raised with the picture passed as a bytes array (in JPEG format).
- Call StartPreview again to restart the images preview.

Only one process can access the camera at a time. Therefore you should release the camera when your activity is paused.
Usually you will initialize the camera during Activity_Resume and pause it during Activity_Pause.



The following code saves the image:
B4X:
Sub Camera1_PictureTaken (Data() As Byte)
    Dim out As OutputStream
    out = File.OpenOutput(File.DirRootExternal, "1.jpg", False)
    out.WriteBytes(data, 0, data.Length)
    out.Close
    ToastMessageShow("Image saved: " & File.Combine(File.DirRootExternal, "1.jpg"), True)
You can see in the attached code that the "take picture" button is only enabled when the camera is ready.

For now the camera orientation is always set to landscape mode. Usually you will want to force the activity to also be in landscape mode. This is done by checking 'Lansdcape' in Project - Orientations Supported menu.

On the emulator the preview images will show a moving check board.
 

Attachments

mshihrer

Member
Licensed User
I get an error

when I run this example, I get an error after I click on the button to take a picture. Here is what it says:

An error has occurred in sub:main_camera1_picturetaken
(java line:225)
java.io.FileNotFoundException: /sdcard/1.jpg
Continue?

also, after looking at the code, I noticed there is a SUB that NEVER gets called, the sub is :
B4X:
Sub Camera1_Ready (Success As Boolean)
   If success Then
      Camera1.StartPreview
      btnTakePicture.Enabled = True
   Else
      ToastMessageShow("Cannot open camera.", True)
   End If
End Sub
not sure what if I am doing something wrong.

edit:

unless after you create Camera1, this sub becomes an event. This is so confusing.
 
Last edited:

mshihrer

Member
Licensed User
ok, thanks, that was it. I still had it connected to usb.
Sorry about the event question, I get it now. I never noticed the section in the documentation for the libraries that list the events. All good now, thanks again.
 

jota

Active Member
Licensed User
problem orientation

Hi, I found that if the orientation of the application is Both the preview does not display if the orientation is Portrait everything goes right. How can we solve this? Thanks


my English = google translator (sorry)
 

jota

Active Member
Licensed User
And is there any way to change the view of an activity without fixing the view of the entire application?
 

Erel

Administrator
Staff member
Licensed User
You can manually edit the manifest file and set the orientation property of a specific activity. Then set the file to be read-only so it would not be overwritten.
The file - AndroidManifest.Xml is located in <source folder>\Objects.
B4X:
 <activity android:windowSoftInputMode="stateHidden" android:launchMode="singleTop" android:name=".main"
                  android:label="Flickr Viewer" android:screenOrientation="unspecified">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:windowSoftInputMode="stateHidden" android:launchMode="singleTop" android:name="activity2" android:label="Activity2" android:screenOrientation="unspecified"></activity>
You should change "unspecified" to "landscape" for the required activity.

It is recommended to do this change when your application is more or less ready. Once you set the file to read-only it will not get updated with new permissions / updated version number and so on.
 

fabianr

Member
Licensed User
How I can set the resolution of the photograph?
By default is 1600x1200 :/
Thank you.
 

Erel

Administrator
Staff member
Licensed User
Currently you cannot change it.

I'm posting the library code. You are all welcomed to modify it:
B4X:
package anywheresoftware.b4a.objects;

import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;

import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BALayout;
import anywheresoftware.b4a.BA.ActivityObject;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Permissions;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.keywords.Common;

/**
 * The camera object allows you to use the device back facing camera to take pictures and show preview images.
 *Currently the camera orientation is always landscape. Usually you will want to force the application to also be in landscape mode (Project - Supported Orientations).
 *Only one process can access the camera at any time. Therefore it is highly recommended to initialize the camera object in Activity_Resume and release it in Activity_Pause.
 *A working example with explanations is available <link>here|http://www.basic4ppc.com/forum/basic4android-getting-started-tutorials/6891-take-pictures-internal-camera.html</link>.
 */
@ActivityObject
@Permissions(values={"android.permission.CAMERA"})
@ShortName("Camera")
@Version(1.0f)
@Events(values={"Ready (Success As Boolean)", "PictureTaken (Data() As Byte)"})
public class CameraW {
    private static Camera c;
    private SurfaceView sv;
    private String eventName;
    private BA ba;
    private AtomicInteger readyCount = new AtomicInteger();
    /**
     * Initializes the camera.
     *Panel - The preview images will be displayed on the panel.
     *EventName - Events subs prefix.
     *The Ready event will be raised when the camera has finished opening.
     */
    public void Initialize(final BA ba, ViewGroup Panel, String EventName) {
        this.ba = ba;
        readyCount.set(0);
        this.eventName = EventName.toLowerCase(BA.cul);
        sv = new SurfaceView(ba.context);
        anywheresoftware.b4a.BALayout.LayoutParams lp = new anywheresoftware.b4a.BALayout.LayoutParams(0, 0,
                Panel.getLayoutParams().width, Panel.getLayoutParams().height);
        Panel.addView(sv, lp);
        if (c != null) {
            readyCount.set(1);
        }
        sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        sv.getHolder().setFixedSize(Panel.getLayoutParams().width, Panel.getLayoutParams().height);
        sv.getHolder().addCallback(new SurfaceHolder.Callback() {

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format,
                    int width, int height) {
            }

            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (readyCount.addAndGet(1) == 2) {
                    ba.raiseEvent(null, eventName + "_ready", true);
                }
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }

        });
        if (c == null) {
            ba.submitRunnable(new Runnable() {

                @Override
                public void run() {
                    try {
                        c = Camera.open();
                        if (readyCount.addAndGet(1) == 2) {
                            ba.raiseEventFromDifferentThread(null, CameraW.this, -1,eventName +  "_ready", false, new Object[] {true});
                        }
                    }
                    catch (Exception e) {
                        Release();
                        ba.raiseEventFromDifferentThread(null, CameraW.this, -1,eventName +  "_ready", false, new Object[] {false});
                    }
                }

            }, this, -1);
        }
    }
    /**
     * Starts displaying the preview images.
     */
    public void StartPreview() throws IOException {
        c.setPreviewDisplay(sv.getHolder());
        c.startPreview();
    }
    /**
     * Stops displaying the preview images.
     */
    public void StopPreview() {
        if (c != null)
            c.stopPreview();
    }
    /**
     * Releases the camera object and allows other processes to access the camera.
     */
    public void Release() {
        if (sv != null) {
            ViewGroup vg = (ViewGroup) sv.getParent();
            vg.removeView(sv);
            sv = null;
        }
        if (c != null) {
            c.release();
            c = null;
        }
    }
    /**
     * Takes a picture. When the picture is ready, the PictureTaken event will be raised.
     *You should not call TakePicture while another picture is currently taken.
     *The preview images are stopped after calling this method. You can call StartPreview to restart the preview images.
     */
    public void TakePicture() {
        c.takePicture(null , null, new Camera.PictureCallback() {

            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                ba.raiseEvent(null, eventName + "_picturetaken", data);
            }
            
        });
    }



}
 

JogiDroid

Member
Licensed User
I found that preview image is bit odd in my ZTE Blade (updated to 2.2 version), it contains both horizontal and vertical black lines if the preview panel is not filling whole screen. Number and placement of weird lines depends size of the preview panel, sometimes there are lot of lines, sometimes just few.
Mayby just bug in preview image scaling (based on cameralib code I cant see there is much to do.. )
 

droidman

Member
Licensed User
i tested this and worked out just fine, but is it a way or tutorial how to make effects on the picture?
 

swissmade

Well-Known Member
Licensed User
Not all Data Saved

Hello all,
I try this sample and it is working fine.
But when I take a picture not all data is saved.
I have only a part of the image.
The rest of the image is grey.:sign0137:

I use a Sony Xperia Arc with 8mp Camera.
It seems that we lose some data on the way.

Please help,
 
Last edited:

swissmade

Well-Known Member
Licensed User
Hi Erel,

Yes I close the stream.
I use the sample from the Forum to test this.

Sub Camera1_PictureTaken (Data() As Byte)
camera1.StartPreview
Dim out As OutputStream
out = File.OpenOutput(File.DirRootExternal, "1.jpg", False)
out.WriteBytes(data, 0, data.Length)
out.Close 'Closing Stream
ToastMessageShow("Image saved: " & File.Combine(File.DirRootExternal, "1.jpg"), True)
btnTakePicture.Enabled = True
End Sub

I also have the len of data in the image so I think the Camera is not sending all the Data. I check this with msgbox(Data.length,"")

Hope you can help.
Thanks
Also many thanks for your fast respond.

:icon_clap:
 
Status
Not open for further replies.
Top