Androidカメラの露出やオートフォーカスの制御

Androidカメラの露出やオートフォーカスの制御

カメラでオートフォーカスを使うには
autoFocus()
を使う

露出を変更したいのなら
setExposureCompensation()
を使う

これらをやる前に、まずは
カメラプレビューを実装しておくこと

次に、端末がオートフォーカス対応か判定する

public static boolean isSupportFocus(Context context){

return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS);

}

これで、結果がtrueなら使えるし
falseなら使えない

判定ができたらオートフォーカスを使うため
Camera.autoFocus()を使う

private void setCameraAutoFocus(){

if(mCamera !=null && mIsSupportFocus){

mCamera.autoFocus(new AutoFocusCallback(){

@Override
public void onAutoFocus(boolean success, Camera camera){
//AutoFocusのリセット
mCamera.cancelAutoFocus();

//AutoFocus開始
mCamera.autoFocus(null);
}
});
}
}

そして、露出情報の取得
露出を変更する前に
デバイスに搭載されているカメラの
露出情報を取得する

これで取得した値を元に
現在の露出値や
露出値の上限、下限を設定

//下限の設定
mMinExposure = mCamera.getParameters().getMinExposureCompensation();

//上限の設定
mMaxExposure = mCamera.getParameters().getMaxExposureCompensation();

//現在の露出値の算出
mExposureValue = mCamera.getParameters().getExposureCompensation();

そして、事前に取得した露出情報を露出の値を変更する

mExposureValue++;

//露出値の上限以上なら補正
if(mExposureValue > mMaxExposure){
mExposureValues = mMinExposure;
}

Parameters param = mCamera.getParameters();
param.getExposureCompensation(mExposureValue);

mCamera.setParameters(param);

mBtnExposure.setText(getString(R.string.exposure )+ mExposureValue);

次に、システム要件にオートフォーカスが必要なことを明記する
これで、非対応端末はインストールできる

これは、AndroidManifest.xmlへ追記する

<uses-feature android:name="android.hardware.camera.autofocus"/>

オートフォーカス連続使用の注意点として
デバイスによっては強制終了の可能性があるため
事前に
Camera.cancelAutoFocus()を呼び出し
オートフォーカスをリセットすれば安定する

あと、露出情報については
CameraParametersのメソッドをつかうことで
各露出情報を取得できる

getMaxExposureCompensation()
露出の上限

getMinExposureCompensation()
露出の下限

getExposureCompensation()
現在の露出値

Androidの写真をアルバムへ保存

Androidの写真をアルバムへ保存

写真をアルバムへ保存するには
Cameraクラスの
takePicture()を使って写真を撮影し
これで得たbyte配列のデータをJPGへ変換して保存する

まずは、カメラプレビューの実装し
次に、写真を撮影

写真の撮影にはtakePicture()を使う

mSurfaceView.setOnTouchListener(new OnTouchListener(){

@Override
public boolean onTouch(View v, MotionEvent event){

if(event.getAction() == MotionEvent.ACTION_DOWN){

if(mCamera !=null){

//前回撮影した画像を保存できない場合
if(!mIsSave){
mCamera.takePicture(null,null, mPictureCallback);
mIsSave = true;
}
}
}
return true;
}
});

次に、画像の保存先の設定

String savePath = Environment.getExternalStorageDirectory().getPath() + "SAVE_PATH";

File file = new File(saveFile);

if(!file.exists()){
file.mkdir();
}

Calendar cal = Calendar.getInstance();

SimpleDateFormat sdFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");

String imgPath = savePath +"/"+ "IMG_"+sdFormat.format(cal.getTime())+".jpg";

ちなみに、microSDに保存するときには
権限が必要になるため
AndroidManifest.xmlへ権限を追記する

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

次に、設定した保存バスを元に
FileOutputStream の
write()で画像データを書き込む
書き込み後はギャラリーへ反映するため
ContentProviderの更新をする

try{
FileOutputStream fos = new FileOutputStream(imgPath, true);

fos.write(data);
fos.close();

//ContentProvider update
ContentValues values = new ContentValues();
values.put(Images.Media.MIME_TYPE, "image/jpeg");
values.put("_data",imgPath);

getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

}catch(Exception e){

}

takePicture()には
3つのコールバック、つまり行う挙動を指定できる

Camera.ShutterCallback
キャプチャした瞬間、コールバックを返す

Camera.PictureCallback
撮影したrawデータの画像をコールバックとして返す

Camera.PictureCallback
撮影したjpegデータの画像をコールバックとして返す

Raspberry Pi のLED抵抗の値についてメモ

Raspberry Pi のLED抵抗の値についてメモ

LEDは
かける電圧をあげていくと
一定の電圧を超えたところで
それまで流れなかった電流が一気に流れる

このときの電圧のことを
順方向電圧
という

例として

高輝度5mm赤色LED 7cd60度 (10個入)
http://akizukidenshi.com/catalog/g/gI-01318/

の画面をみると

VF:2.1V
と書いてあり
これが順方向電圧を示している
VF がそれになる

おそらくこれが耐えれる電圧
これを超えないために抵抗をつける

また
標準電流:20mA
となっているので
この場合、通常は 20mA まで電流をおさえる
ということになる

ちなみに 1A は 1000mA

もし220Ωの抵抗をいれたなら
(電圧 3.3 V – LED の電圧 2.1V) / 220Ω
となるので
0.004 A
つまり
4mAとなる

最大値の 20mA まで電流をながすと
かなりまぶしいけど
Raspberry Pi には電流に制限があり
1つの GPIO に流せる電流量は
16mA
となっているので、これはできない

さらに Raspberry Pi では
ピンヘッダー全体で流せるのが
最大 50mA
しかないので注意

Ubuntu 14.04 LTS インストール

Ubuntu 14.04 LTS インストール

uvuntu 12.10のサポートが切れたため
サポート期間の長いLTSをクリーンインストール

あらかじめ必要なファイルはバックアップをとってあるので
あとは
インストール開始

デスクトップ以外に
12.04LTS のパソコンがあるので
そちらを元に作成

https://www.ubuntulinux.jp/News/ubuntu1404-ja-remix
のページから
ISOファイルをダウンロードする

今回は KDDI研究所のものをつかった
http://ftp.kddilabs.jp/Linux/packages/ubuntu-jp/release-cd/releases/14.04/ubuntu-ja-14.04-desktop-amd64.iso

これでISOファイルは入ったので
ISOファイルをDVDに焼く必要があるので
k3b を起動してISOファイルをDVDーRへ書き込み

インストールするときに、光学ドライブの調子がよくないみたいなので
最小限の項目のみでインストール

インストール完了後、いろいろとメモをとりたいので
Ubuntu ソフトウェアセンターで
tomboy をインストール

次に firefox が終了時にタブを閉じてしまうので
https://support.mozilla.org/ja/kb/restore-previous-session#w_akccaicdchagaeecageggo
を参考に
firefox を起動して
edit > preferemces
で設定画面をひらき
When Firefox stats の項目が
Show my home page
になっているので、これを
show my windows and tabs from last time
に変更する

Ubuntu Gnome 14.04 をインストールしてからやったこと

を元にインストールしていく

私の場合用途は仮想環境の構築と
Androd 関連の開発とかサーバーなどなど

あとは  windows 7 でゲームできればOK
というかんじ

なにはともあれ、まずはアップデートなので

sudo apt-get update
sudo apt-get upgrade

よく使うパッケージをインストールするので

sudo apt-get install git build-essential 

次に Chrome のインストール

sudo apt-get install chromium-browser chromium-browser-l10n

そしてドライバーを入れるため
http://www.howopensource.com/2012/10/install-nvidia-geforce-driver-in-ubuntu-12-10-12-04-using-ppa/
を参考に

sudo apt-add-repository ppa:xorg-edgers/ppa
sudo apt-add-repository ppa:ubuntu-x-swat/x-updates
sudo apt-get update
sudo apt-get install nvidia-current nvidia-settings

これでインストール

次に仮想環境のインストール

https://my.vmware.com/jp/web/vmware/free#desktop_end_user_computing/vmware_player/6_0

から
VMware Player for Linux 64 bit を選んでダウンロード

ダウンロードしたバージョンは 6.0.3

インストールに関しては
Linux(Ubuntu14.04等)にVMwarePlayerをインスコんごwwww

を参考に

cd ダウンロード/
sudo bash VMware-Player-6.0.3-1895310.x86_64.bundle 

でインストール

ちなみにアンインストール方法も調べてみた

LinuxのVMwarePlayer抹殺マン。

によれば

sudo vmware-installer -u vmware-player

でアンインストール可能

次に VMware Player のゲストマシンで 3Dアクセラレーションを有効化したいので
Linux ホスト VMWare 3D アクセラレーション

を参考に追加インストール

まず必要なパッケージをインストール

その前に端末がつかいにくいので、端末を起動して
編集 > キーボードショートカット
を選択して

メニューのアクセスキーを有効にする
のチェックを外します

次にパッケージのインストール

sudo apt-get install driconf 

あと vim を使いたいので

sudo apt-get install vim

GPIO 14 でLチカ

GPIO 14 でLチカ

GPIO 4 ではなぜかLチカできなくなったため
GPIO 14 で実験する

一般的なPCボードとか Ardunio などでは
スイッチがONであることを
3.3V か 5V で表す

ちなみに Raspberry Pi は 3.3 V
GPIO は 1 か 0 のデジタルで
1 が high
0 が low

とりあえずブレッドボードへ配線してみた

必要部品に関しては
ハック ラズベリーパイ Raspberry Pi 電子工作入門キット。
で揃うので、これを使用

配線は
GPIO 14
から
220Ωの抵抗につなぎ、これから
LEDの+、つまり足の長いほうへつなぐ

次にLEDのー
足の短いほうを
GNDにつなげる

GPIO 14 はGNDのとなりにある左上3番め

GNDは左上4番めになる

これで Raspberry PI を起動すると
LEDが点灯する

今回はこれを制御してみる
なお、LED操作には root 権限で動かすので

sudo su

で動かすことになる

まずは GPIO 14 を有効化する

echo "14" > /sys/class/gpio/export 

次にモードを出力モードに指定

echo "out" > /sys/class/gpio/gpio14/direction 

これで消灯しているので
LED点灯するには

echo "1" > /sys/class/gpio/gpio14/value 
echo "0" > /sys/class/gpio/gpio14/value 

で消灯になる

シェルスクリプトでも制御できるけど
実行する時には root 権限が必要なため
sudo をつけて実行する必要がある

今回、実験したけど

./led.sh: 3 行: echo: 書き込みエラー: デバイスもしくはリソースがビジー状態です

とエラーがでるもののLEDの点滅には成功

Androidカメラプレビューの利用

Androidカメラプレビューの利用

Androidのカメラプレビューを使うには
SurfaceViewを設置してプレビュー領域を作成

そして、Cameraクラスの
startPreview()を使ってプレビューを開始する

まずは、SurfaceViewの設置

これは、レイアウトファイルとなるXMLファイルへ追加する

<SurfaceView
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

次に、SurfaceHolderを取得して
コールバック処理を追加する

これは、設置した SurfaceView から
SurfaceHolder を取得し
SurfaceViewの生成時
変更時、破棄時のときの処理を追加する

コールバック処理は、そのときに行う処理のこと

まずは、SurfaceHolderの取得

mSurfaceHolder = mSurfaceView.getHolder();

次に、処理の追加
処理の追加は、addCallback()で追加できる
とりあえず、プロトタイプ

mSurfaceHolder.addCallback(

@Override
public void surfaceDestroyed(SurfaceHolder holder){
//破棄時の処理
}

@Override
public void surfaceCreated(SurfaceHolder holder){
//生成時の処理
}

@Override
public void surfaceChanged(SurfaceHolder holder int format, int width, int height){
//変更時の処理
}

)

これで、だいたいの骨格ができたので
次に、生成時の処理になる
surfaceCreated()の中身を書いていく

ここでは、カメラの初期化
そして、デバイスで利用できる
カメラプレビュー一覧も取得する

なお、取得失敗も考慮し、try catchで処理する

@Override
public void onCreated(SurfaceHolder holder){

//カメラの初期化処理
mCamera = Camera.open();
if(mCamera !=null){

try{
mCamera.setPreviewDisplay(mSurfaceHolder);

}catch(IOException e){
e.printStackTrace();
}

//利用可能なプレビューサイズの取得
mSupportedPreviewSize = mCamera.getParameters().getSupportedPreviewSizes();
}
}

次に、SurfaceChanged()の中身を書いていく
ここでは、ディスプレイサイズから
カメラプレビューに最適なサイズを選択し
カメラプレビューを開始する

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

if(mCamera !=null){
Parameters param = mCamera.getParameters();

if(mSupportedPreviewSizes !=null){

//ディスプレイサイズに最適なサイズを設定
mPreviewSize = CameraUtil.getOptimalPreviewSize(mSupportedPreviewSizes, width, height);

//カメラのプレビューサイズをセット
param.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(param);
}

//プレビュー開始
mCamera.startPreview();
}

}

そして、
surfaceDestroyed()へ
SurfaceView破棄時のコールバック処理を書く
ここでは、カメラプレビューを使わなくなったときの処理を書く

@Override
public void surfaceDestroyed(SurfaceHolder holder){

//カメラの終了処理
mCamera.stopPreview();
mCamera.release();
mCamera =null;
}

あとは、カメラを扱うため
AndroidManifest.xmlへ権限を追記する

<uses-permission android:name="android.permission.CAMERA"/>

あと、surfaceHolder へ
addCallback()
で、コールバックを追加すると
以下のメソッドかわ追加される

surfaceDestroyed(SurfaceHolder holder)
これは、SurfaceViewが破棄されたときに呼ばれる

surfaceCreated(SurfaceHolder holder)
これは、SurfaceViewが生成されるときに呼ばれる

surfaceChanged(SurfaceHolder holderint format, int width, int height)
これは、SurfaceViewが変更されたときに呼ばれる

カメラプレビューでこれらのメソッドを使う場合

onSurfaceCreated()で
カメラを初期化

onSurfaceChanged()で
プレビューの設定と開始処理

onSurfaceDestroyed()で
カメラの終了処理をする

次に、カメラパラメーターの設定

カメラは、プレビューを開始する前に
カメラに様々なパラメーターを追加できる

プレビューサイズもパラメーターに該当するため
パラメーター扱いで変更できる

カメラパラメーターは
Camera.getParameters()で取得し
setParameters()でセットする

あと、Androidはデバイスごとに画面のプレビューサイズが代わるため
CameraUtilクラスの
getOptimalPreviewSize()を使う

Android近接センサーの値取得

Android近接センサーの値取得

近接センサーは、端末に物体が接近しているときの距離を検出するセンサー

これも、SensorManagerをつかえば値を取得できる

まず、SensorManagerのインストールを取得

mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

次に、近接センサーの登録
これも
SensorManagerクラスの
registerListener()で登録

private void registerSensor(){

if(mSensorManager ==null){

mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

mSensorManager.registerListener(
this, 
mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
mSensorManager.SENSOR_DELAY_UI
);
}
}

次に、バッテリー消費対策のため
センサー解除の実装

SensorManagerクラスの
unregisterListener()により解除可能になる

private void unregisterSensor(){

if(mSensorManager !=null){

mSensorManager.unregisterListener(this);
mSensorManager = null;

}
mvibrator.cancel();
}

これで登録と解除はできるので
近接センサーの値取得

近接センサーの値は、
onServiceChanged()
のeventオブジェクトに格納されているので
そこから取得する

@Override
public void onSensorChanged(SensorEvent event){

//近接センサーの値を取得
if(event.sensor.getType() == Sensor.TYPE_PROXIMITY){

if(event.values[0] ==0){
mVibrator.vibrate(new long[] {
100, 100
}, 0);

}else{

mVibrator.cancel();
}
}
}

今回は、近接センサーの値が 0 なら
バイブで振動するようにしている

Android気圧センサーの値取得

Android気圧センサーの値取得

Androidデバイスに気圧センサーが内蔵されているなら
SensorManagerを使い
気圧値の取得ができる

まずは、SensorManagerのインスタンス取得

Context.getSystemService()

SensorManagerのインスタンス取得する

mSensorManager= (SensorManager)getSystemService(SENSOR_SERVICE);

次に、気圧センサーの登録
これは、SensorManagerクラスの
registerListener()
を使い、気圧センサーを登録する

@Override
protected void onResume(){

super.onResume();

//センサー登録
mSensorManager.registerListener(
this,
mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE),
SensorManager.SENSOR_DELAY_UI
);

}

これだけだと、バッテリー消費が激しくなるので
解除方法も記述

解除は
SensorManagerクラスの
unregisterListener()をつかうことで
気圧センサーの登録を解除できる

@Override
protected void onPause(){

super.onPause();

//センサー登録解除
mSensorManager.unregisterListener(this);
}

これで、登録と解除はokなので、
次は、センサーから気圧値を取得し表示する

@Override
public void onSensorChanged(SensorEvent event){

//気圧値の取得
float[] values = event.values;
mTvPressure.setText(" "+values[0]);
}

とりあえず、これで取得や表示などはできるけど
気圧センサーは搭載されてない端末が多いため
搭載されてないことを前提に実装すること

方法は2つ
Google Play へ公開するのなら
uses-featureの設定により、Google Playからのインストールができないようにできる

もう一つは、プログラムでチェックする方法
こっちは、サードパーティーアプリとか
自作で入れるときに使う

まずは、uses-featureの場合
この場合、AndroidManifest.xmlへ設定する

<uses-feature
android:name="android.hardware.sensor.pressure"
android:require="true"/>

を追加する

次に、プログラムでの判定

public static boolean hasFeaturePressure(Context context){

return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_BAROMETER);
}

Androidデバイスの方位を調べる

Androidデバイスの方位を調べる

デバイスの方位を調べるには
加速度センサー、磁気センサーを使い
包囲角度を計算する

そして、東西南北どの方位に該当するかを計算し、方位を求めることになる

まず、SensorManagerのインスタンス取得

mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

次に、センサーの登録
これは、
SensorManagerクラスの
registerListener()を使い加速度センサー、および磁気センサーを登録する

@Override
protected void onResume(){

super.onResume();

mSensorManager.registerListener(
this,
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_UI);

mSensorManager.registerListener(
this,
SensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
SensorManager.SENSOR_DELAY_UI);

}

次に、センサーの解除について

これは、SensorManagerの
unregisterListener()で
加速度センサー、および磁気センサーを解除する

@Override
public void onPause(){

//センサー解除
mSensorManager.unregisterListener(this);
super.onPause();
}

次に、方位角度を求める

これは、センサーで取得した値から方位角度を計算することで求められる

@Override
public void onSensorChanged(SensorEvent event){

switch(event.sensor.getType()){

//加速度センサーの値取得
case Sensor.TYPE_ACCELEROMETER:
mAcMatrix = event.values.clone();
break;

//磁気センサーの値取得
case Sensor.TYPE_MAGNETIC_FIELD:
mMgMatrix = event.values.clone();
break;
}

if(mMgMatrix !=null && mAcMatrix !=null){

float[] orientation = new float[3];
float R[] = new float[16];
float I[] = new float[16];

//加速度センサー、磁気センサーの値を元に、回転行列を計算する
SensorManager.getRotationMatrix(R, I, mAcMatrix, mMgMatrix);

//デバイスの向きに応じて回転行列を計算する
SensorManager.getOrientation(R, orientation);

//ラジアンから角度へ変換
float angle = (float)Math.floor(Math.toDegrees(orientation[0]));

//角度の範囲を0~360度へ調整
if(angle >=0){

orientation[0]=angle;
}else if(angle < 0){
orientation[0]= 360 + angle;
}

//得られた角度を画面へ表示
mTVAzimuth.setText(String.valueOf(orientation[0]));
}

このように、Androidでは
方位角を求めるには、
加速度センサーと磁気センサーを併用し
加速度の値
磁気センサーの値
デバイスの向きを元に回転行列を作成する

その後で、該当するラジアン値を角度へ変換することで
方位角を求めることができる

注意点として、センサーは一度登録すると解除するまで作動するため
バッテリー消費が激しくなる

このため、登録にはonPause()
解除には onResume()で行うようにする

WebIOPi の REST API

WebIOPi の REST API

REST API は
httpプロトコルで
URLにパラメータをつけて相手先サーバーと通信する方式のことで
Ajax ではお馴染みの通信方法

JavaScript から WebIOPi のAPI に
httpリクエストを送信することで
Raspberry Pi のGPIOポートの制御ができる

詳しくは
https://code.google.com/p/webiopi/wiki/RESTAPI
を参考に

主なAPIとしては

Set GPIO function
これは GPIO ポートの入出力モードの設定

構文は
POST /GPIO/GPIO番号/function/方向

GPIO番号が設定したいGPIO番号
方向は in で入力 out で出力
pwm なら PWM出力になる
戻り値は設定した内容になる

例としては
POST GPIO/4/function/in

Set GPIO value
これは GPIO ポートに出力する

構文は
GET /GPIO/GPIO番号/value/値

GPIO番号が出力したいGPIO番号
値は
0なら low 出力
1なら high 出力
戻り値は設定した値

例としては
GET /GPIO/4/value/1

Get GPIO value
これはGPIOポートの状態を取得
構文は
GET /GPIO/GPIO番号/value
GPIO番号は取得したいGPIO番号
戻り値は設定したポートの値で 0 か 1 になる

例としては
GET /GPIO/25/value

Output PWM with a duty cycle ratio
これはGPIOポートにPWM出力をする
構文は
POST /GPIO/GPIO番号/pulseRatio/比率
GPIO番号は、出力したいGPIO番号
比率は0~1になり
50%なら 0.5 になる
例としては
POST /GPIO/18/pulseRation/0.5

Output PWM with a angle for servos
これはGPIOポートに接続したサーボモータを制御する
構文は
POST /GPIO/GPIO番号/pulseAngle/角度
GPIO番号は、出力したいGPIOポート番号
角度は0度から指定可能で
もし45度なら 45 で指定
例としては
POST /GPIO/18/pulseAngle/45

Get Full GPIO state/configuration
これは、すべての状態を取得する
構文は
GET /*
戻り値は JSON 形式になる
使用例もそのままで
GET /*

となる

基本的に、
取得ならGET
送信とか出力などは POST
となる