カメラアプリでautofocusで実装する。

前回のカメラアプリデータを利用します。

カメラで撮影をして保存してみる。

 

autoFocusの実装は既に機能として、用意されています。

public void autoFocus(){
 if( myCamera != null ){
 // オートフォーカスのあと撮影に行くようにコールバック設定
 myCamera.autoFocus( new Camera.AutoFocusCallback() {
 @Override
 public void onAutoFocus(boolean success, Camera camera) {
 camera.autoFocus( null );
 myCamera.takePicture(null, null, mPictureListener);
 });
 }
}

カメラにauto focusを実装にはautoFucusを実装するとフォーカスをした瞬間にtakePictureを呼び出すので、綺麗な写真を取れます。

 

しかし、実機で落ちます。。(galaxy s)

 

多くのサイトでマニフェストの設定と書いてあったので、

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="in.andante.camerapre"
 android:versionCode="1"
 android:versionName="1.0">
 <uses-sdk android:minSdkVersion="7" />
 <uses-permission android:name="android.permission.CAMERA"></uses-permission>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
 <uses-feature android:name="android.hardware.camera" />
 <uses-feature android:name="android.hardware.camera.autofocus" />
 <application android:icon="@drawable/icon" android:label="@string/app_name">
 <activity android:name=".CameraPre"
 android:label="@string/app_name"
 android:screenOrientation="landscape"
 android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>

 </application>
</manifest>

で、変更したのですがuser-featureは使える機種を限定するだけで使用の際につける義務はなかった気がしたのですが、これでも解決はしませんでした。(エミュレーターで動いていたので。。)

 

なぜ落ちるのか。と下記の仮定をしました。

「実際にautofocusとカメラの取得を同時に行う事で、メモリ過多になり、落ちているのかな。。と」

 

takePictureのcallback関数が実行されていないようです。

なので、autofocusが完了後に時間を置いて写真を撮ってみます。

Thread trd = new Thread(new Runnable(){
 public void run() {
 try{
 Thread.sleep(1000);
 }catch(Exception e){}
 myCamera.takePicture(null, null, mPictureListener);
 };
});
trd.start();

で1秒後に実行されますが、これでは、フォーカスがあった状態の写真じゃなく、フォーカスがあった「一秒後」の写真になってしまいます。

なので、フォーカスがあった段階で、プレビューの更新をとめます。

camera.stopPreview();

上記をいれる事で遅延させてカメラの画像取得、及び保存を実行しています。

public void autoFocus(){
 if( myCamera != null ){
 myCamera.autoFocus( new Camera.AutoFocusCallback() {
 @Override
 public void onAutoFocus(boolean success, Camera camera) {
 camera.autoFocus( null );
 camera.stopPreview();
 //myCamera.takePicture(null, null, mPictureListener);
 Thread trd = new Thread(new Runnable(){
 public void run() {
 try{
 Thread.sleep(1000);
 }catch(Exception e){}
 myCamera.takePicture(null, null, mPictureListener);
 };
 });
 trd.start();
 }
 });
 }
}

これを画面に触れた段階で実行するので

public boolean onTouchEvent(MotionEvent event) {
 if (event.getAction() == MotionEvent.ACTION_DOWN) {
 if (myCamera != null && bool) {
 bool = false;
 autoFocus();
 //myCamera.takePicture(mShutterListener, null, mPictureListener);
 }
 }
 return true;
}

今回に関しては、1秒後の実行ですが、この秒数をもっと短くしても問題ありません。

しかし、秒数を短くする事で負荷を上げてしまう可能性があります。

実機でのテストを重ねて最適の負荷の時間を探すのがベストです。

 

多くの写真アプリでもフォーカス後に時間がかかっているものが多いので、この仕様なのかと推定しています。

ソース全体は下記です。

package in.andante.camerapre;

import java.io.*;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.hardware.Camera;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback{
 private Camera myCamera;
 private Context context;
 private Boolean bool = true;
 public CameraView(Context context){
 super(context);
 this.context = context;
 getHolder().addCallback(this);
 getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
 }

 public void surfaceCreated(SurfaceHolder holder){
 myCamera = Camera.open();
 try {
 myCamera.setPreviewDisplay(holder);
 } catch (Exception e) {
 e.printStackTrace();
 }
 }

 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
 Camera.Parameters parameters = myCamera.getParameters();
 //parameters.setPreviewSize(width, height);不要でした。すいません。
 myCamera.setParameters(parameters);
 myCamera.startPreview();
 }

 public void surfaceDestroyed(SurfaceHolder holder){
 myCamera.release();
 myCamera = null;
 }

 // JPEGイメージ生成後に呼ばれるコールバック
 private Camera.PictureCallback mPictureListener =new Camera.PictureCallback() {
 public void onPictureTaken(byte[] data, Camera camera) {
 if (data != null) {
 if(!sdcardWriteReady()){
 Toast.makeText(context, "SDCARDが認識されません。", Toast.LENGTH_SHORT).show();
 bool = true;
 camera.startPreview();
 return;
 }
 FileOutputStream foStream = null;
 //フォルダのパスを表示します。。
 File file = new File(Environment.getExternalStorageDirectory().getPath() + "/cmr/");
 //フォルダが存在しなかった場合にフォルダを作成します。
 if(!file.exists()){
 file.mkdir();
 }
 //これで他のとかぶらない名前の設定ができました。
 String imgName = Environment.getExternalStorageDirectory().getPath() + "/cmr/" + System.currentTimeMillis() +".jpg";

 try {
 foStream = new FileOutputStream(imgName);
 foStream.write(data);
 foStream.close();

 ContentValues values = new ContentValues();
 ContentResolver contentResolver = context.getContentResolver();
 values.put(Images.Media.MIME_TYPE, "image/jpeg");
 values.put("_data", imgName);
 try {
 contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
 }catch(Exception e){
 Toast.makeText(context, "再起動後に画像が認識されます。", Toast.LENGTH_SHORT).show();
 e.printStackTrace();
 }
 } catch (Exception e) {
 Toast.makeText(context, "ファイルの保存中にエラーが発生しました。", Toast.LENGTH_SHORT).show();
 e.printStackTrace();
 }
 bool = true;
 camera.startPreview();
 }else{
 Toast.makeText(context, "データが取得できませんでした。", Toast.LENGTH_SHORT).show();
 bool = true;
 camera.startPreview();
 }
 }
 };

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 if (event.getAction() == MotionEvent.ACTION_DOWN) {
 if (myCamera != null && bool) {
 bool = false;
 autoFocus();
 //myCamera.takePicture(mShutterListener, null, mPictureListener);
 }
 }
 return true;
 }

 //書き込みができるかどうかを判別する関数
 public boolean sdcardWriteReady(){
 String state = Environment.getExternalStorageState();
 return (Environment.MEDIA_MOUNTED.equals(state));
 }

 // オートフォーカス
 public void autoFocus(){
 if( myCamera != null ){
 // オートフォーカスのあと撮影に行くようにコールバック設定
 myCamera.autoFocus( new Camera.AutoFocusCallback() {
 @Override
 public void onAutoFocus(boolean success, Camera camera) {
 camera.autoFocus( null );
 camera.stopPreview();
 //myCamera.takePicture(null, null, mPictureListener);
 Thread trd = new Thread(new Runnable(){
 public void run() {
 try{
 Thread.sleep(1000);
 }catch(Exception e){}
 myCamera.takePicture(null, null, mPictureListener);
 };
 });
 trd.start();
 }
 });
 }
 }
}

以上です。

前後の記事

前の記事:

次の記事:

関連の記事

コメント

:D らぼ☆ろぐ » Android カメラプレビューよりサイズを指定して画像取得

[…] アンドロイドのカメラアプリを作成するにあたっての雑感。 カメラアプリでautofocusで実装する。 Android で タイマー処理 を考えてみた BitmapFactory.Options […]

コメントの投稿

  • サイト内検索

新作アプリの紹介

関連サイトの紹介

アンドロイドアプリ開発TIPS
きぐるみカメラ
ふらいぱん
アンドロイドのデザイン集
Page top↑