| Author |
Message |
charroch Freshman

Joined: 06 Dec 2007 Posts: 6
|
Posted: Thu Dec 06, 2007 4:04 pm Post subject: Using CameraPreview to save a picture on disk |
|
|
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.
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
| 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 |
|
 |
venkat Senior Developer

Joined: 27 Nov 2007 Posts: 152 Location: India
|
Posted: Sat Dec 08, 2007 12:22 pm Post subject: |
|
|
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. This site is very very use full to me .
Regads,
venkat
|
|
| Back to top |
|
 |
venkat Senior Developer

Joined: 27 Nov 2007 Posts: 152 Location: India
|
Posted: Mon Dec 10, 2007 3:11 pm Post subject: |
|
|
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 .
I got a one error , I tried to solve, but i can't . 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
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 |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2439 Location: College Park, MD
|
Posted: Mon Dec 10, 2007 8:22 pm Post subject: |
|
|
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
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
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
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 (or not ).
Regards,
plusminus
_________________
Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
venkat Senior Developer

Joined: 27 Nov 2007 Posts: 152 Location: India
|
Posted: Tue Dec 11, 2007 7:10 am Post subject: |
|
|
Thank u for ur reply PlusMinus,
Still i can't fix the error
|
|
| Back to top |
|
 |
plusminus Site Admin


Joined: 14 Nov 2007 Posts: 2439 Location: College Park, MD
|
Posted: Tue Dec 11, 2007 7:17 am Post subject: |
|
|
Hello venkat,
perhaps give us some code, so we can have a look
Regards,
plusminus
_________________
Please remember, that this board is give & take 
| Android Development Community / Tutorials |
|
| Back to top |
|
 |
venkat Senior Developer

Joined: 27 Nov 2007 Posts: 152 Location: India
|
Posted: Tue Dec 11, 2007 8:54 am Post subject: |
|
|
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 |
|
 |
charroch Freshman

Joined: 06 Dec 2007 Posts: 6
|
Posted: Tue Dec 11, 2007 11:00 am Post subject: |
|
|
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 |
|
 |
venkat Senior Developer

Joined: 27 Nov 2007 Posts: 152 Location: India
|
Posted: Tue Dec 11, 2007 12:32 pm Post subject: |
|
|
Thank you very much charroch and Plusminu, mistake was my side only . your code is working perfectly.
thanks again.
regards,
venkat
|
|
| Back to top |
|
 |
venkat Senior Developer

Joined: 27 Nov 2007 Posts: 152 Location: India
|
Posted: Sat Dec 15, 2007 10:14 am Post subject: |
|
|
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.
|
|
| Back to top |
|
 |
charroch Freshman

Joined: 06 Dec 2007 Posts: 6
|
Posted: Tue Dec 18, 2007 7:20 pm Post subject: |
|
|
Hello Venkat,
You can call me carl btw . 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 |
|
 |
venkat Senior Developer

Joined: 27 Nov 2007 Posts: 152 Location: India
|
Posted: Wed Dec 19, 2007 4:53 am Post subject: |
|
|
HI carl,
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 |
|
 |
Katharnavas Senior Developer

Joined: 04 Dec 2007 Posts: 100 Location: India
|
Posted: Wed Dec 19, 2007 7:17 am Post subject: |
|
|
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 |
|
 |
automatonx Freshman

Joined: 29 Dec 2007 Posts: 3
|
Posted: Sat Dec 29, 2007 12:57 am Post subject: |
|
|
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 |
|
 |
Katharnavas Senior Developer

Joined: 04 Dec 2007 Posts: 100 Location: India
|
Posted: Sat Dec 29, 2007 6:13 am Post subject: |
|
|
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 |
|
 |
|
 | |