raspberry pi のカメラモジュールでエフェクト機能実験

raspberry pi のカメラモジュールでエフェクト機能実験

前回カメラモジュールを購入したので
引き続きカメラ機能を実験

使用したのは
ラズベリーパイ カメラモジュール Camera Module for Raspberry Pi

本体は
Raspberry Pi Type B 512MB
を使用

raspistill コマンドを使うことでデジカメみたいに写真をとれる

raspistillコマンドは、さまざまなオプションがあり
目的に応じて様々な撮影条件を設定できる

ユニークなものとしてはエフェクト機能

エフェクトモードは20種類あり
-ifx オプションに続けてエフェクトモードを指定することで設定できる

エフェクトモードを水彩画みたいにして撮影するなら

 raspistill -o image.jpg -ifx watercolour

とする

なお、リモートでログインしているとその端末では見れないけど
HDMIケーブルとかでディスプレイに接続しておけば
プレビューをみることができる

HDMIケーブルについては
Amazon で安いのがあったので、これを利用

なお
raspistillコマンドには
露出モードを設定する -ex オプション
ホワイトバランスを設定する -awb オプション
測光モードを設定する -mm オプション
の設定が可能となっている

Webページの進捗状況の取得

Webページの進捗状況の取得

進捗状況がわからないと
フリーズしたと思われる

進捗状況の取得には
WebChromeClientクラスの
onProgressChangedハンドラを使う 

このメソッドをoverrideすることで進捗状況を取得できる

Webview..setWebChromeClient(new WebChromeClient(){

@Override
public void onProgressChanged(Webview view, int newProgress){
super.onProgressChanged(view, newProgress);

//行う処理

}
});

onProgressChanged()の引数は
WebView viewが
WebChromeClient()をセットしているWebView

int newProgressがWebページの進捗状況を表す0~100の数値
これは、パーセンテージを意味する

これで、進捗状況が取得できたので
画面に表示する

表示方法は2種類あり
進捗バーを使うか
進捗インジケータ、これは回転するアイコン
を使うことになる

まず、進捗バーの場合
この場合、レイアウトファイルに
ProgressBarを追加する


[/java]

次に表示する進捗インジケータの準備

進捗インジケータの表示には
ActivityクラスのsetProgressBardeterminateVisibility()を使う

このメソッドを使うには
Activityクラスの
setContentView()を使う前に
ActivityクラスのrequestWindowFeature()を使う必要がある

もし、それより後に実行すると
RuntimeException requestFeature() must be called before adding content
という例外が発生する

タイトルバーに進捗インジケータを表示するなら

@Override
public void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

//タイトルバーへ進捗インジケータ表示
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

setContentView(R.layout.main);
}

というように、レイアウトファイル読み込みの前に記述する

そして、進捗インジケータや進捗バーは
ページ読み込み開始の時点では表示するけど
読み込んだら非表示にしておく

この方がWebページを広く使えるし見やすい
この表示、非表示を実現するには
WebViewClientの
onPageStartedハンドラと
onPageFinishedハンドラを使う

mWebView.setWebChromeClient(new WebChromeClient(){

@Override
public void onProgressChanged(WebView view, int newProgress){

super.lnProgressChanged(view, newProgress);

//進捗状況の取得
mProgressBar.setProgress(newProgress);
}
});

mWebView.setWebViewClient(new WebViewClient(){

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon){
super.onPageStarted(view, url, favicon);

//ProgressBarの表示
mProgressBar.setVisibility(View.VISIBLE);

//進捗インジケータ表示
setProgressBarIndeterminateVisibility(true);

}


@Override
public void onPageFinished(WebView view, String url){
super.onPageFinished(view,url);

//ProgressBar非表示に
mProgressBar.setVisibility(View.GONE);
//進捗インジケータを非表示に
setProgressBar.IndeterminateVisibility(false);
}
});

ページ履歴の前後へ移動するには

ページ履歴の前後へ移動するには

WebViewは、ページ内のリンクをクリックすると次のページへ遷移する

遷移履歴をたどり、戻るなら
goBack()

進むなら
goForward()
を使う

履歴の取得には
WebViewクラスのcopyBackForwardList()を使う

webview.copyBackForwardList();
WebBackForwardList bfList = webview.copyBackForwardList();
WebHistoryItem item = bfList.getCurrentItem();
String title = item.getTitle();
String url = item
getUrl();

さらに、今見ているページ以外の前後の履歴の削除も可能
これは、 clearHistory()を使う

webview.clearHistory();

というように使う

残念なことに1つ前とかのように特定ページのみの削除ということはできない

AndroidでWebコンテンツの表示

AndroidでWebコンテンツの表示

Webコンテンツを表示するには
WebViewクラスのloadUri()を使う

まず、

private WebView mWebView;

でメンバ変数にする

次に、onCreate()の中で

mWebView = new WebView(this);
setContentView(mWebView);

でWebViewが使えるようにする

ただし、このままだとJavascriptが無効なので

mWebView.getSettings().setJavascriptEnabled(true);

でJavascriptを有効にする
そのぶんセキュリティーは落ちるので注意

あとは、表示するWebページを設定

mWebView.setWebViewClient(new WebViewClient());
mWebView.loadUri("http://www.google.co.jp");

あと、WebViewを扱うときには、終了したら破棄する処理も必要

[@Override]
public void onDestroy(){

super.onDestroy();

mWebView.stopLoading();
ViewGroup webParent = (ViewGroup)mWebView.getParent();

if(webParent != null){

webParent.removeView(mWebView);
}
mWebView.destroy();
}
[/java]

これは、AndroidのWebViewは
Javaプログラムだけでなく
ネイティブのCのコードも使っているかららしい

このため、WebViewを明示的に破棄しないと
メモリの解放ができない

このため、Activity.onDestroyハンドラでWebViewクラスの
destroy()を呼んでいる

また、このときに
removeView()でActivityから切り離してから
destroy()を呼ぶこと

これをやらないと
Error: WebViewdestroy() called while still attached
とエラーが発生する

アプリ内でYouTube再生

アプリ内でYouTube再生

Androidアプリ内でYouTubeを再生するには
VideoViewを使う
もしくは
Android YouTube Player API
を使う

Android YouTube Player APIを使う場合
Google Map のAPIを使うときみたいに
Google APIs ConsoleでAPIを使えるようにしておく必要がある

そして、
Simple API Accessの keyをメモしておく

ここまでできたら
Android YouTube Player APIをダウンロードする

これをダウンロードしたら解凍して
libsから
YouTubeAndroidPlayerApi.jar

プロジェクトへ追加する

Eclipseを使っているなら
libs以下へ配置すれび自動認識される

ここまでできたら
レイアウトファイルへ
YouTubeを再生するためのViewを追加する

<com.google.android.youtube.player.YouTubePlayerView
android:id="@+id/youtube"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

次に、Javaでの操作だけど
YouTubeを再生するためには
YouTubeBaseActivityを継承し
OnInitializedListenerを実装する必要がある

さらに
レイアウトファイルで追加したYouTubePlayerViewインスタンスへ
API key の設定も必要になる

まずは、継承から

extends YouTubeBaseActivity implements OnInitializedListener

を追加

次に、API キーなどを変数へ格納

private static final String DEVELOPER_KEY ="取得したAPIキー";
private static final int RECOVERY_DIALOG_REQUEST = 1;

次に、onCreate()の中で
YouTubePlayerViewへDeveloperキーを設定

YouTubePlayerView youtubeView = (YoutubePlayerView)findViewById(R.id.youtube);

youtubeView.Initialize(DEVELOPER_KEY, this);

次に、初期化失敗のときの処理

@Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult errorReason){

//初期化失敗の処理
if(error.Reason.isUserRecoverableError()){
//エラー回避可能ならダイアログで表示
errorReason.getErrorDialog(this, RECOVERY_DIALOG_REQUEST).show();

}else{
//エラー回避不可能ならトーストで表示
String errorMessage = String.format(getString(R.string.error_player),
errorReason.toString());
Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show();
}
}

そして、初期化成功のときの処理

@Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player, boolean wasRestored){

//YouTubeの動画Idを設定
if(!wasRestored){
player.cueVideo("再生したい動画のID");
}
}

また、YouTubeにアクセスするということで
AndroidManifest.xmlへ
インターネットにアクセスする権限を追加する

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

このように、YouTubePlayerViewを使うことで
動画再生のためのボタンやフルスクリーン機能なども自動で実装される

このあたりの制御には
YouTubePlayerViewクラスに実装されているメソッドを使う

APIや制御方法などは、Google Map APIに近い

あと、YouTubeの動画IDは
YouTubeのURLの中の
https://www.youtube.com/watch?v=
の後の部分になる

raspberry pi でカメラモジュール

raspberry pi でカメラモジュール

カメラモジュールを raspberry Pi 本体に追加し
電源を入れる

今回もamazon で購入してみた
使用したのは
ラズベリーパイ カメラモジュール Camera Module for Raspberry Pi

今回もリモートで実行

まず
SSH でログインして
次に

sudo raspi-config

で設定画面を開く

raspi_camera

設定で
Enable Camera を選択し

Enable support for Raspberry Pi camera
とカメラをサポートするか聞かれるので
Enable にして有効化

raspi_camera2

そして設定画面を終了すると再起動するか聞かれるので再起動

これで設定が反映されるので
撮影はコマンドからできる

しっかり接続できていないと

mmal: mmal_vc_component_create: failed to create component 'vc.ril.camera' (1:ENOMEM)
mmal: mmal_component_create_core: could not create component 'vc.ril.camera' (1)
mmal: Failed to create camera component
mmal: main: Failed to create camera component
mmal: Camera is not detected. Please check carefully the camera module is installed correctly

と表示されてしまう

接続方法については
http://www.raspberrypi.org/help/camera-module-setup/
の動画を参考にさせていただきました

raspberry pi カメラモジュール
で検索するともっとでるかも

接続できたらログインして

raspistill -o image.png

で撮影できる

ちなみに、このときに raspberry pi とディスプレイを
HDMI ケーブルでつないでおけば
画面にカメラの画像がでる
基本的には5秒プレビューしてから撮影だけど

-t オプションを指定してミリ秒単位で指定もできる

1秒は1000ミリ秒
なので1秒後とかなら

raspistill -o image.png -t 1000

とすればいい

プレビュー画面なんていらない
というなら
-n オプションをつけるとできるけど
この場合、画面が綺麗に写らないことがあるようなので注意

また解像度の指定もできる
デフォルトでは
2592 x 1944 ピクセル

これも
-w オプションで幅
-h オプションで高さ
を指定できる

1秒間のプレビューと、 640x 480 の画像で撮影するなら

raspistill -o image.png -t 1000 -w 640 -h 480

とすればいい

オプションはたくさんあるので

raspistill 

でオプションヘルプをみるとわかりやすい

なお、ディスプレイではなく
端末画面に表示するなら

sudo apt-get install fbi

で fbi をインストールし、これを使う

fbi -a image.jpg 

で実行

ただし、リモートではできず本体の場合のみできた

Androidでビデオ再生

Androidでビデオ再生

ビデオなどの動画再生は、VideoViewを使う

VideoViewは、MediaPlayerに似てるけど
こっちは、あらかじめレイアウトファイルへ組み込むことができる

まず、レイアウトファイルへ

<VideoView
android:id="@+id/video"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

そして、Javaで
VideoViewのインスタンスを取得し
動画のURIを設定してから
VideoView.start()で再生する

VideoView video = (VideoView)findViewById(R.id.video);

//res/raw/sampleを取得
video.setVideoUri(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.sample));

//動画の再生開始
video.start();

なお、VideoViewに設定できる動画は
ローカルファイルを直接指定するか
もしくは
ネットワークやリソースフォルダのファイルを指定することになる

ローカルファイルを設定するなら
VideoView.setVideoPath(String)
を使う

ネットワークやリソースフォルダを指定する場合は
Uriでパスを作成して
VideoView.setVideoUri(Uri)
で設定する

そして、設定してから
VideoView.start()で再生

MediaPlayerと違うのは、あらかじめ読み込む処理がいらないこと

ちなみに、再生の停止は
VideoView.pause()

再生位置を指定するなら
VideoView.seekTo(long)
を使う

音楽情報を通知領域へ表示

音楽情報を通知領域へ表示

通知領域へ音楽情報を表示するには
独自にNotificationのレイアウトを作成し
音楽の再生、停止、のタイミングでNotificationに設定する

ServiceへNotificationを設定
この処理のタイミングは
音声データの再生のタイミングにする

まず、Notificationの登録

startForeground(1, generateNotification());

generateNotification()の中身は以下のようになる

//通知領域タップ時のPendingIntent作成
Intent actionIntent = new Intent(getApplicationContext(), MainActivity.class);

PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, actionIntent, PendingIntent.FLG_UPDATE_CURRENT);

//独自レイアウトのRemoteView作成
RemoteViews notificationView = new RemoteViews(getPackageName(), R.layout.status_bar);

//Notification生成
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());

//アイコン設定
builder.setSmallIcon(R.drawable.ic_start_media);

//独自レイアウトをNotificationへ設定
builder.setContent(notificationView);

//設定をtrueにして常時表示
builder.setOngoing(true);

//通知領域に初期表示のときの設定
builder.setTicker("sample title を設定");
builder.setContentIntent(pi);

//statusbar のアイコン設定
notificationView.setImageResource(R.id.imageIcon, R.drawable.ic_launcher);

//status barへタイトル設定
notificationView.setTextViewText(R.id.textTitle, "Sample title");

//status barへアーティスト名表示
notificationView.setTextViewText(R.id.textArtist, "Sample Artist");

//イメージアイコンボタンを押したときのIntentの設定
PendingIntent contentIntent   = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), Intent.FLAG_ACTIVITY_NEW_TASK);

notificationView.setOnClickPendingIntent(R.id.imageIcon, contentIntent);

//再生、一時停止を押したときのIntentの設定
notificationView.setOnClickPendingIntent(R.id.play, createPendingIntent("playpause"));

//次へボタンを押したときのIntent設定
notificationView.setOnClickPendingIntent(R.id.next, createPendingIntent("next"));

return builder.build();

サービスが終了すると
自動的にNotificationは終了するけど
明示的に通知領域から解除するには

stopForeground(true);

とする

startForeground()を使うと
Serviceは、システムから強制停止できなくなる

音楽の再生や動画再生みたいに長時間バックグラウンドで動作するサービスの場合
Notificationなどを使って通知領域へサービスの有無を表示するのが一般的

残念ながら、現時点では、表示領域からのアイコンによる操作は
標準ではサポートされてないため
Notificationの独自レイアウトを作成し
そこへ各機能のボタンなどを配置して
RemoteViews経由でIntentを設定していく

音楽情報をロックスクリーンへ表示する

音楽情報をロックスクリーンへ表示する

ロックスクリーンに音楽情報を表示するには
RemoteControlClient

LockScreenに表示する状態を設定し
AudioManagerクラスの
registerRemoteControlClient()
で登録すれば使える

まず LockScreenで押されたボタンのイベントを受け取るレシーバーのPendingIntentを生成

Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(mComponentName);

PendingIntent mediaPendingIntent = PendingIntent.getBroadCast(getApplicationContext(), 0, mediaButtonIntent);

次に、RemoteControlClientを生成、PendingIntentを設定

remoteControlClient = new RemoteControlClient(mediaPendingIntent);
remoteControlClient.setTransportControlFlags(
  RemoteControlClient.FLAG_KEY_MEDIA_PLAY | 
  RemoteControlClient.FLAG_KEY_MEDIA_PAUSE |
  RemoteControlClient.FLAG_KEY_MEDIA_NEXT | 
  RemoteControlClient.FLAG_KEY_MEDIA_STOP
);

そして、RemoteControlClientの登録

mAudioManager.registerRemoteControlClient(remoteControlClient);

そして、AudioFocusの取得
mAudioManager.registerAudioFocus(new AudioFocusChangeListener(){

@Override
public void onAudioFocusChange(int focusChange){

Log.d(TAG,”Focus changed: ” + focusChange);
}
},
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
[/java]

次に、ロックスクリーンに表示する曲情報やボタン状態を設定

//ロックスクリーンの状態を再生に設定
remoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);

//ロックスクリーンに表示する音楽情報を設定
remoteControlClient
.editMetadata(true)
.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, "Sample Artist")
.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, "Sample Album")
.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, "Sample Music")
.apply();

そして、この設定を解除するには
AudioManagerクラスの
unregisterRemoteControlClient()
で解除する

mAudioManager.unregisterRemoteControlClient(remoteControlClient);
remoteControlClient = null;

ロックスクリーンに曲情報を表示するときの注意点は
registerMediaButtonEventReceiver()
によるBroadCastReceiverの登録
そして
AudioFocusの取得
を行うこと

これをやらないと、表示されないので注意

また、ロックスクリーンのボタンの状態は
MediaPlayerの再生とは
自動では連動しないので
状態に合わせて、プログラム側で
RemoteControlClientクラスの
setPlaybackState()で値を設定する必要がある

音声の録音

音声の録音

Androidで音声の録音をするには
MediaRecorderを使う

これを使うことで録音や録画ができる

以下は初期化から録音まで

//MediaRecorder初期化
if(mMediaRecorder == null){
 mMediaRecorder = new MediaRecorder();
}

//入力をマイクへ設定
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

//保存フォーマットを3gpに設定
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

//Audioエンコードをデフォルトへ
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);

//microSDへ名前を指定して保存
String fileName = "recorder.3gp";
mFilePath = Environment.getExternalStorageDirectory() + "/" +fileName;
mMediaRecorder.setOutputFile(mFilePath);

//録音準備完了なら録音開始
try{

mMediaRecorder.prepare();
}cash(Exception e){
 e.printStackTrace();
Log.e(TAG, e.toString(), e);
}

なお、録音停止は、MediaRecorder.stop()を使う

if(mMediaRecorder != null){

mMediaRecorder.stop();
mMediaRecorder.release();
}

また
録音機能には権限が必要になるため
AndroidManifest.xmlへ

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

が必要になる

また、microSDへの保存についても権限が必要になる

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

を追加する

MediaRecorderは、初期化設定して
MediaRecorder.prepare()を読んで準備し
MediaRecorder.start()で録音開始

初期化のときに音声なのか
映像なのかにより初期化方法は異なる

そして、録音のポイントは
オーディオソースの設定
オーディオ出力の設定になる

一般的な録音でいいけど
もし録音した音声が音割れするなら
MediaRecorder.setAudioSource()の引数へ
MediaRecorder.AudioSource.VOICE_RECOGNITION
を設定すると改善させることがある