andbook!.pdf - Learning Android Get an anddev.org - Android-Shirt Back to index
anddev.org Header Logo
FAQ Search Top rated articles Browse Feeds anddev.org - Authors Contact Details Register Log in

Using CameraPreview to save a picture on disk

Goto page 1, 2, 3  Next
 
       anddev.org - Android Development Community | Android Tutorials | Index -> Advanced Tutorials
Author Message
charroch
Freshman
Freshman


Joined: 06 Dec 2007
Posts: 6

PostPosted: Thu Dec 06, 2007 4:04 pm    Post subject: Using CameraPreview to save a picture on disk Reply with quote

Using CameraPreview to save a picture on disk


What is this: I was working on the CameraPreview application and noticed there were no capturing tool to save the image into a PNG or JPG. It is quite simple to enable it but requires a bit of understanding.

Question Problems/Questions: post right below...

Difficulty: 2.5 of 5

Desctription: First off, we create a new project within Eclipse. I named it CameraPreview so I can just copy/paste the cameraPreview application found here.

If you run the application, you should see a square changing color with a black and white background:



Let analyse how the camera application is built:


The CameraPreview does not do much. It is our main class but nothing in there is of any interest.

The Preview class is where everything happens. It will contain the canvas which we will draw onto and then to the screen. The constructor and most methods are used to handle the extra thread that we will use to keep refreshing the output of the camera. For instance we will set the size of the screen (and camera output) in the constructor, ensure that the PreviewThread will be killed when the activity goes onPause and reborn when it hits onResume.

The surfaceView is just a handler on which we will be able to paint the output of the camera.

Lets look at the meat of the application: PreviewThread.

The run method will do several things:

1. Set some options to the camera device and open it:

Java:
                    CameraDevice.CaptureParams param = new CameraDevice.CaptureParams();
                    param.type = 1; // preview
                    param.srcWidth = 1280;
                    param.srcHeight = 960;
                    param.leftPixel = 0;
                    param.topPixel = 0;
                    param.outputWidth = 320;
                    param.outputHeight = 240;
                    param.dataFormat = 2; // RGB_565
                    camera.setCaptureParams(param);


2. Hold the surface handler and lock onto it:
Java:
               SurfaceHolder holder = mHolder;
               while (!mDone) {
                    Canvas canvas = holder.lockCanvas();


3. Print onto the canvas the camera output:
Java:

camera.capture(canvas);


4. Post it to the UI and unlock the canvas (which actually does not make much difference because it will loop undifinitely with the while loop):
Java:

holder.unlockCanvasAndPost(canvas);


It is important to understand how the preview is done before continuing in the capturing part. as you can see we lock onto a canvas that is being updated by the camera. The camera can be open only once.


2. The capturing part:

We will do our capturing in the CameraPreview class.

First off we create a key listener:

Java:

     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
               mPreview.pause();
               takePicture();
               mPreview.resume();       
          return true;
        }
        return false;
     }


I used the center button to be the one that will take the picture. First I pause the Preview class. The onPause will kill the the thread which is looping as explained above and close the connection to the camera device. This will release the device for any other method to use. The takePicture is the method used to take the picture - I will explain it next. The resume will call the onResume so we can have the preview continuing after the picture was taken.

Lets take a look at the takePicture method:

Java:

private int i = 0;
     
     void takePicture(){
          CameraDevice camera = CameraDevice.open();
          if (camera != null) {
               Log.i("MyLog", "inside the camera");
               CameraDevice.CaptureParams param = new CameraDevice.CaptureParams();
               param.type = 1; // preview
               param.srcWidth = 1280;
               param.srcHeight = 960;
               param.leftPixel = 0;
               param.topPixel = 0;
               param.outputWidth = 320;
               param.outputHeight = 240;
               param.dataFormat = 2; // RGB_565
               camera.setCaptureParams(param);
               
               Bitmap myPic = Bitmap.createBitmap(320, 240, false);
               Canvas canvas = new Canvas(myPic);
               try {
                    FileOutputStream stream = super.openFileOutput("picture" + i++ + ".png", MODE_PRIVATE);
                    camera.capture(canvas);
                    myPic.compress(CompressFormat.PNG, 100, stream);
                    stream.flush();
                                 stream.close();
               }catch(Exception e) { Log.e("MyLog", e.toString()); }
               
               // Make sure to release the CameraDevice
               if (camera != null)
                    camera.close();
          }
     }


We do the following:

1. Open the camera device and set the options as in the PreviewThread class

2. Now I create a new Bitmap with the same dimension as the output of the camera device:
Java:

param.outputWidth = 320;
param.outputHeight = 240;
...
Bitmap myPic = Bitmap.createBitmap(320, 240, false);


3. I also create a canvas that will print into the Bitmap (instead of the screen)
Java:

Canvas canvas = new Canvas(myPic);


4. Now I open a file using Context.openFileOutput
NOTE: As you can see I use a dynamic name. I noticed that overiding the file with the same name will not work. It will recreate a new file but it seems that you can not open the png afterwards. It might be a bug, need time to investigate a bit further. Now each time you take a picture it will be named picture1.png, picture2.png and so forth. Furthermore, you might want to wait a bit and refreshing the ddms view before saving...

5. I capture the image onto the canvas (onto the bitmap) and then save it by compressing it onto the outputStream:
Java:

                    camera.capture(canvas);
                    myPic.compress(CompressFormat.PNG, 100, stream);

Nothing much to explain here. It is quite straightforward to understand. You have 2 formats: PNG and JPG. The second value is the quality 100 being best and 0 worth. FInally stream is the ouptut stream that were given in 4.

Next I will create a quick viewer. However, you can check the files from:

Open Perspective -> DDMS -> under "data -> data -> <package name> -> files -> picture0.png...". You can save it onto disk with the small disk drive icon on the top right. and check how marvelous the picture is.

Hope it helps somebody.

Comments are welcomed.

./Carl



camera.zip
 Description:
Source code taken from eclipse.

Download
 Filename:  camera.zip
 Filesize:  39.74 KB
 Downloaded:  529 Time(s)



Last edited by charroch on Tue Dec 11, 2007 10:56 am; edited 1 time in total
Back to top
View user's profile Send private message
venkat
Senior Developer
Senior Developer


Joined: 27 Nov 2007
Posts: 152
Location: India

PostPosted: Sat Dec 08, 2007 12:22 pm    Post subject: Reply with quote

Dear plushminus,
first of all i have to say Thank u very much to u , this is what i am looking for long before. Thank u very much once again.Very Happy This site is very very use full to me . Smile

Regads,
venkat
Back to top
View user's profile Send private message
venkat
Senior Developer
Senior Developer


Joined: 27 Nov 2007
Posts: 152
Location: India

PostPosted: Mon Dec 10, 2007 3:11 pm    Post subject: Reply with quote

Dear PlusMinus,
i run your program "Using CameraPreview to save a picture on disk", it's nice work and it will helpful to me also Smile .

I got a one error Sad , I tried to solve, but i can't Sad . could you attach full source code as u did before all your tutorial or can you just tell, what may be the error.

i got a error in below line.
FileOutputStream stream = super.openFileOutput("picture" + i++ + ".png", MODE_PRIVATE);

if i place my cursor on MODE_PRIVATE, it says MODE_PRIVATE can't be resolved Question

if i declared one integer variable "MODE_PRIVATE", I got new error The method openFileOutput(String, int) is undefined for the type SurfaceView

can u help me please , Thanks in advance,

regards,
venkat;)
Back to top
View user's profile Send private message
plusminus
Site Admin
Site Admin


Joined: 14 Nov 2007
Posts: 2439
Location: College Park, MD

PostPosted: Mon Dec 10, 2007 8:22 pm    Post subject: Reply with quote

Hey venkat,

the code/tutorial was not created by me, but by: charroch. ( I pleased him by main, to attach the full source to his post, a minute ago.)

when sth. cannot be resolved within Eclipse, keep on hitting ORGANIZE-IMPORTS ( CTRL + SHIFT + O ) until Eclipse fixes that for you Wink

MODE_PRIVATE is located in android.content.Context.MODE_PRIVATE, so I'd first say simply add an:
Java:
import android.content.Context;

to your code.

BUT , normally you do not need to do that import, because Activity is a Sub-Sub-Sub-Class of the abstract Context-Class Exclamation

It looks like you are trying to use MODE_PRIVATE the openFileOutput(...) from within an private/anonymous class (SurrfaceView ?) or sth. But probably not in the 'Outer'-Activity itself Exclamation

To achieve what you are trying by now, you need to use the this-Pointer of the surrounding Activity:
Java:
OuterActivity.this.openFileOutput(...);


Tell us if your problem was solved by this Smile (or not Wink ).

Regards,
plusminus

_________________
Please remember, that this board is give & take Smile

| Android Development Community / Tutorials
Back to top
View user's profile Send private message Send e-mail Visit poster's website
venkat
Senior Developer
Senior Developer


Joined: 27 Nov 2007
Posts: 152
Location: India

PostPosted: Tue Dec 11, 2007 7:10 am    Post subject: Reply with quote

Thank u for ur reply PlusMinus, Smile
Still i can't fix the error Sad
Back to top
View user's profile Send private message
plusminus
Site Admin
Site Admin


Joined: 14 Nov 2007
Posts: 2439
Location: College Park, MD

PostPosted: Tue Dec 11, 2007 7:17 am    Post subject: Reply with quote

Hello venkat,

perhaps give us some code, so we can have a look Smile

Regards,
plusminus

_________________
Please remember, that this board is give & take Smile

| Android Development Community / Tutorials
Back to top
View user's profile Send private message Send e-mail Visit poster's website
venkat
Senior Developer
Senior Developer


Joined: 27 Nov 2007
Posts: 152
Location: India

PostPosted: Tue Dec 11, 2007 8:54 am    Post subject: Reply with quote

Sure Plusminus,
i have attached my source code here. take a look and tell me what is may the problem.

regards,
venkat

Java:
package com.android.camerapreview;
import java.io.FileOutputStream;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.CameraDevice;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;


// ----------------------------------------------------------------------

public class CameraPreview extends Activity
{    
    @Override
        protected void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);

        // Hide the window title.
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        // Make sure to create a TRANSLUCENT window. This is recquired
        // for SurfaceView to work. Eventually this'll be done by
        // the system automatically.
        getWindow().setFormat(PixelFormat.TRANSLUCENT);

        // Create our Preview view and set it as the content of our
        // Activity
        mPreview = new Preview(this);
        setContentView(mPreview);
    }

    @Override
        protected boolean isFullscreenOpaque() {
        // Our main window is set to translucent, but we know that we will
        // fill it with opaque data. Tell the system that so it can perform
        // some important optimizations.
        return true;
    }

    @Override
        protected void onResume()
    {
        // Because the CameraDevice object is not a shared resource,
        // it's very important to release it when the activity is paused.
        super.onResume();
        mPreview.resume();
    }

    @Override
        protected void onPause()
    {
        // Start Preview again when we resume.
        super.onPause();
        mPreview.pause();
    }

   private Preview mPreview;
}

// ----------------------------------------------------------------------

class Preview extends SurfaceView implements SurfaceHolder.Callback
{
 
     CameraPreview cp;
     Preview mPreview;
     Preview(Context context) {
        super(context);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
       
        mHolder = getHolder();
        mHolder.setCallback(this);
        mHasSurface = false;
        setFocusable(true);
        // In this example, we hardcode the size of the preview. In a real
        // application this should be more dynamic. This guarantees that
        // the uderlying surface will never change size.
        mHolder.setFixedSize(320, 240);
    }

    public void resume() {
        // We do the actual acquisition in a separate thread. Create it now.
        if (mPreviewThread == null) {
            mPreviewThread = new PreviewThread();
            // If we already have a surface, just start the thread now too.
            if (mHasSurface == true) {
                mPreviewThread.start();
            }
        }
    }

    public void pause() {
        // Stop Preview.
        if (mPreviewThread != null) {
            mPreviewThread.requestExitAndWait();
            mPreviewThread = null;
        }
    }

    public boolean surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, start our main acquisition thread.
        mHasSurface = true;
        if (mPreviewThread != null) {
            mPreviewThread.start();
        }
        // Tell the system that we filled the surface in this call.
        // This is a lie to preven the system to fill the surface for us
        // automatically.
        // THIS IS REQUIRED because other wise we'll access the Surface object
        // from 2 different threads which is not allowd (And will crash
        // currently).
        return true;
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return. Stop the preview.
        mHasSurface = false;
        pause();
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // Surface size or format has changed. This should not happen in this
        // example.
    }
 

   
   
    private int i = 0;
   
    void takePicture(){
         CameraDevice camera = CameraDevice.open();
         if (camera != null) {
              Log.i("MyLog", "inside the camera");
              CameraDevice.CaptureParams param = new CameraDevice.CaptureParams();
              param.type = 1; // preview
              param.srcWidth = 1280;
              param.srcHeight = 960;
              param.leftPixel = 0;
              param.topPixel = 0;
              param.outputWidth = 320;
              param.outputHeight = 240;
              param.dataFormat = 2; // RGB_565
              camera.setCaptureParams(param);
             
              Bitmap myPic = Bitmap.createBitmap(320, 240, false);
              Canvas canvas = new Canvas(myPic);
              try {                                                          
      //          MODE_PRIVATE can't be resolved    
                 FileOutputStream stream =cp.openFileOutput("picture" + i++ + ".png", 1);
                    camera.capture(canvas);
                   myPic.compress(CompressFormat.PNG, 100, stream);
                   stream.flush();
                                stream.close();
              }catch(Exception e) { Log.e("MyLog", e.toString()); }
             
              // Make sure to release the CameraDevice
              if (camera != null)
                   camera.close();
         }
         
         Log.i("MyLog","inside the Capture");
    }
   
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
       if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
        Log.i("MyLog","inside the key Event");
           
               mPreview.pause();
              takePicture();
            mPreview.resume();      
         return true;
       }
       return false;
    }
   
   
    // ----------------------------------------------------------------------

    class PreviewThread extends Thread
    {
        PreviewThread() {
            super();
            mDone = false;
        }

        @Override
                public void run() {
            // We first open the CameraDevice and configure it.
            CameraDevice camera = CameraDevice.open();
            if (camera != null) {
                CameraDevice.CaptureParams param = new CameraDevice.CaptureParams();
                    param.type = 1; // preview
                    param.srcWidth      = 1280;
                    param.srcHeight     = 960;
                    param.leftPixel     = 0;
                    param.topPixel      = 0;
                    param.outputWidth   = 320;
                    param.outputHeight  = 240;
                    param.dataFormat    = 2; // RGB_565
                camera.setCaptureParams(param);
            }

            // This is our main acquisition thread's loop, we go until
            // asked to quit.
            SurfaceHolder holder = mHolder;
            while (!mDone) {
                // Lock the surface, this returns a Canvas that can
                // be used to render into.
                Canvas canvas = holder.lockCanvas();

                // Capture directly into the Surface
                if (camera != null) {
                    camera.capture(canvas);
                }

                // And finally unlock and post the surface.
                holder.unlockCanvasAndPost(canvas);
            }

            // Make sure to release the CameraDevice
            if (camera != null)
                camera.close();
        }

        public void requestExitAndWait() {
            // don't call this from PreviewThread thread or it a guaranteed
            // deadlock!
            mDone = true;
            try {
                join();
            } catch (InterruptedException ex) { }
        }
        private boolean mDone;
    }

            SurfaceHolder   mHolder;
    private PreviewThread   mPreviewThread;
    private boolean         mHasSurface;
}
Back to top
View user's profile Send private message
charroch
Freshman
Freshman


Joined: 06 Dec 2007
Posts: 6

PostPosted: Tue Dec 11, 2007 11:00 am    Post subject: Reply with quote

Hello Venkat,

I attached the source code of the application. You can import it directly into eclipse and run it. I think the problem relates, as plusminus says, to the scope. openFileOutput is a method taken from the class Context which is being extended by Activity. so instead of cp.openFileOutput("picture" + i++ + ".png", 1); you can write (or should be able to) this.openFileOutput.

Anyway it might be better if you check the source and compare it to your code.

TC,
./Carl
Back to top
View user's profile Send private message
venkat
Senior Developer
Senior Developer


Joined: 27 Nov 2007
Posts: 152
Location: India

PostPosted: Tue Dec 11, 2007 12:32 pm    Post subject: Reply with quote

Thank you very much charroch and Plusminu, mistake was my side only Sad . your code is working perfectly.

thanks again.Smile Very Happy

regards,
venkat
Back to top
View user's profile Send private message
venkat
Senior Developer
Senior Developer


Joined: 27 Nov 2007
Posts: 152
Location: India

PostPosted: Sat Dec 15, 2007 10:14 am    Post subject: Reply with quote

Hi charroch,
Can you tell me how to draw Image or anything on the Camera Preview. While taking picture that picture should be combination of image and Camera Preview. Do u have any idea about this problem.


Thanks and regards,
venkat. Smile
Back to top
View user's profile Send private message
charroch
Freshman
Freshman


Joined: 06 Dec 2007
Posts: 6

PostPosted: Tue Dec 18, 2007 7:20 pm    Post subject: Reply with quote

Hello Venkat,

You can call me carl btw Wink. I am on vacation for the next week and I don't have my plateform at hand. However you should be able to use the canvas class to draw something on top of the current image. I guess you can capture an image and then draw whatever you want. If you want to draw an image it might be a bit more difficult. You could use a BitmapDrawable or Bitmap and then setBound function and draw it onto the canvas.

I don't have the code but I remember I did something of the above.

Hope it helps,
Carl
Back to top
View user's profile Send private message
venkat
Senior Developer
Senior Developer


Joined: 27 Nov 2007
Posts: 152
Location: India

PostPosted: Wed Dec 19, 2007 4:53 am    Post subject: Reply with quote

HI carl, Smile
i am very happy to see ur reply. i will do that what ever u said. if i have any query i will inform to u .

Regards,
venkat.
Back to top
View user's profile Send private message
Katharnavas
Senior Developer
Senior Developer


Joined: 04 Dec 2007
Posts: 100
Location: India

PostPosted: Wed Dec 19, 2007 7:17 am    Post subject: Reply with quote

Hi,
Thanks for ur code carl its work nice.. But im finding some problems whatever i do its not getting install on my simulator.. Only during the launch time it works if i close it and try to locate there is no such application ... I even tried using adb install. Same problem it shows the installation size but no use.

And one more thing i could not close the application the only way to exit is using the Home Button.

Any Suggestions please.
Back to top
View user's profile Send private message Yahoo Messenger
automatonx
Freshman
Freshman


Joined: 29 Dec 2007
Posts: 3

PostPosted: Sat Dec 29, 2007 12:57 am    Post subject: Reply with quote

Add
XML:
<category android:value="android.intent.category.LAUNCHER" />

in AndroidManifest.xml. It will install the application on your simulator.

I think, you need to add another KeyEvent in onKeyDown method to stop the camera.

just my 2 cents.
Back to top
View user's profile Send private message
Katharnavas
Senior Developer
Senior Developer


Joined: 04 Dec 2007
Posts: 100
Location: India

PostPosted: Sat Dec 29, 2007 6:13 am    Post subject: Reply with quote

Hi,
instead of creating a file in the package directory is it possible to save the file to the some other system directory or to the SDCard..
If so what to be done..
Back to top
View user's profile Send private message Yahoo Messenger
Display posts from previous: