タグ : camera

カメラにアクセスする2(CameraUI)

airforandroidではCameraにアクセスする方法が2種類あると以前に紹介しましたが、今回はもうひとつの方法CameraUIについて調べてみたいと思います。

CameraUIでは端末のデフォルトカメラアプリを呼び出し、MediaEventで撮影したファイルを受け取るといった処理を行ないます。
CameraとCameraUIの違いはFlash内のCameraを使うか?外部のカメラアプリ?を使うかという違いだと思いますが、それぞれ以下のように長所短所があると思います。

・Camera

長所: リアルタイムにカメラにエフェクトをかけて撮影できるなどカメラそのものに対する自由度が高い。

短所: カメラをアタッチしたvideoは負荷が高く、高解像度の静止画を保存するのはあまり向いてない。

・CameraUI

長所: 使い慣れたUIで処理の流れが高速に行える。デベロッパーがUIを作る手間が省ける。カメラアプリなので撮影時の解像度、明るさ、フォーカス、顔検出などアプリによって細かい設定が可能。

短所: 突然別アプリが立ち上がったようにみえてしまうのでUIに統一感がなくなる。カメラにアクセスできないので写真を撮るという限定的な機能に絞られてします。

低解像度でもよくてカメラへのアクセスが自由におこないたい場合はCameraを、画質へのこだわりやスムーズな処理を求める場合はCameraUIのように分けるのがベスト?ではないでしょうか。

CameraUIを呼び出す為のサンプルスクリプトは以下になります。

package
{
	//このサンプルはβ20100923版でNexusOneにて動作確認したものです。

	import flash.display.Sprite;
	import flash.media.CameraUI;
	import flash.media.MediaType;
	import flash.media.MediaPromise;
	import flash.events.MediaEvent;
	import flash.text.TextField;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;

	public class Main extends Sprite
	{
		var cameraui:CameraUI;

		public function Main()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			//CameraUIを呼び出す
			callCameraUI();
		}

		//CameraUI呼び出し
		private function callCameraUI():void {
			//インスタンスを作成
			cameraui = new CameraUI();
			//CameraUI終了時のコールバック
			cameraui.addEventListener(MediaEvent.COMPLETE, onLoadImg);
			//CameraUIを静止画モードで起動
			cameraui.launch(MediaType.IMAGE);
			//CameraUIを動画モードに設定すると動画が保存され参照することもできます。
			//cameraui.launch(MediaType.VIDEO);
		}

		//撮影後の処理
		private function onLoadImg(e:MediaEvent):void {

			//MediaPromiseで受渡されたファイルの情報を取得できます。
			var mediapromise:MediaPromise = e.data;

			var txt:TextField = new TextField();
			txt.autoSize = "left";
			txt.appendText("filename : " + mediapromise.file.name + "\n");
			txt.appendText("path : " + mediapromise.file.nativePath + "\n");
			txt.appendText("size : " + mediapromise.file.size + "\n");
			txt.appendText("type : " + mediapromise.file.type + "\n");
			addChild(txt);
			//保存されたデータにアクセスする場合はCameraRollにアクセスします。
			//CameraRollの説明はまた別記事にて。
		}
 	}
}

この記事はairforandroidフォーラムのこの記事を参考にしました。

CameraRollについて

CameraRollを利用すると主に以下の2つのアクションが可能です。

カメラロールに bitmapDataを保存する。

var bmp:BitmapData = new BitmapData(10, 10, false, 0xFFFFFFFF);
var cr:CameraRoll = new CameraRoll();
cr.addBitmapData(bmp);

カメラロールを参照する。

カメラロールを参照する場合は少し複雑になり、CameraUIと同じような挙動をします。

CameraRoll.browseForImage()を叩くと、デバイスの画像ビューワーが呼び出され特定の画像1枚を選ぶような遷移になります。ここで1枚を選ぶと画像ビューワは閉じられairforandroidに戻ります。戻った際にイベントからMediaPromiseに選択した画像の情報が渡されます。

adobeの公式ヘルプにまんまお手本が乗っているので↓に引用部分を転載します。

package
{
	import flash.media.CameraRoll;
	import flash.media.MediaPromise;
	import flash.media.MediaType;
	import flash.events.MediaEvent;
	import flash.events.Event;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.IOErrorEvent;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;

	public class Main extends Sprite{
		private var mediaSource:CameraRoll = new CameraRoll();

		public function Main() {
			this.stage.align = StageAlign.TOP_LEFT;
			this.stage.scaleMode = StageScaleMode.NO_SCALE;

			if( CameraRoll.supportsBrowseForImage )
			{
				log( "Browsing for image..." );
				mediaSource.addEventListener( MediaEvent.SELECT, imageSelected );
				mediaSource.addEventListener( Event.CANCEL, browseCanceled );

				mediaSource.browseForImage();
			}
			else
			{
				log( "Browsing in camera roll is not supported.");
			}
		}

		private var imageLoader:Loader;
		private function imageSelected( event:MediaEvent ):void
		{
			log( "Image selected..." );

			var imagePromise:MediaPromise = event.data;

			imageLoader = new Loader();
			if( imagePromise.isAsync )
			{
				log( "Asynchronous media promise." );
				imageLoader.contentLoaderInfo.addEventListener( Event.COMPLETE, imageLoaded );
				imageLoader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, imageLoadFailed );
				imageLoader.loadFilePromise( imagePromise );
			}
			else
			{
				//log( "Synchronous media promise." );
				imageLoader.loadFilePromise( imagePromise );
				this.addChild( imageLoader );
			}
		}

		private function browseCanceled( event:Event ):void
		{
			log( "Image browse canceled." );
		}

		private function imageLoaded( event:Event ):void
		{
			log( "Image loaded asynchronously." );
			this.addChild( imageLoader );
		}

		private function imageLoadFailed( event:Event ):void
		{
			log( "Image load failed." );
		}

		private function log( text:String ):void
		{
			//trace( text );
		}
	}
}

カメラにアクセスする1 (Camera)

[この記事はベータ版(2010/09/23版)でしか確認をとれていません。正式リリース後に変更されている可能性もありますので参考程度にご覧ください。]

airforandroidでは、Cameraにアクセスする方法は2種類あります。
・Camera(昔からあるカメラ)
・CameraUI(デバイス備え付けのカメラアプリからデータを受ける)

詳しく知りたい方はこちら(airforandroid forum)

ここではCameraについて説明します。

まず設定から。
自分の持ってるNexusOneではすこし癖がありカメラでとったものが90度回転してしまっています。
なので、書き出し設定を以下のように設定しています。

・パーミッション設定

・書き出し設定

カメラにアクセスするサンプルスクリプト

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.media.Camera;
	import flash.media.Video;

	public class Main extends Sprite
	{

		public function Main()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			if (Camera.isSupported) {
				var w:int = stage.stageWidth;
				var h:int = stage.stageHeight;
				//カメラをゲット
				var camera:Camera = Camera.getCamera();
				//カメラの解像度のキャプチャの更新頻度(fps)を設定(w,h,fps)
				camera.setMode(w, h, 10);
				//表示用のビデオを作成
				var video:Video = new Video(w, h);
				//ビデオとカメラを関連付ける
				video.attachCamera(camera);
				addChild(video);
			}
		}
	}
}

カメラを表示するだけでなく、撮った画像を保存するところまで行います。
保存方法は色々有りますが、ここでは簡単なカメラロールに保存する方法を紹介します。

カメラロールに保存する

デバイスのカメラロールに保存するためにはCameraRollを使います。
下のサンプルでデータを保存している箇所は下の2行です。

var cameraroll:CameraRoll = new CameraRoll();
cameraroll.addBitmapData(bmpd);

package
{
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.media.Camera;
	import flash.media.CameraRoll;
	import flash.media.Video;
	import flash.ui.Multitouch;
	import flash.ui.MultitouchInputMode;
	import flash.events.TouchEvent;

	public class Main extends Sprite
	{
		var w:int;
		var h:int;
		var video:Video;

		public function Main()
		{
			Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;

			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;

			w = stage.stageWidth;
			h = stage.stageHeight;

			if (Camera.isSupported) {

				//カメラをゲット
				var camera:Camera = Camera.getCamera();
				//カメラの解像度のキャプチャの更新頻度(fps)を設定(w,h,fps)
				camera.setMode(w, h, 10);
				//表示用のビデオを作成
				video = new Video(w, h);
				//ビデオとカメラを関連付ける
				video.attachCamera(camera);
				addChild(video);

				stage.addEventListener(TouchEvent.TOUCH_TAP, saveData);
			}
		}
		//渡されたビットマップデータを保存する
		private function saveData(e:TouchEvent):void {
			var bmpd:BitmapData = new BitmapData(w, h, false, 0xFFFFFF);
			bmpd.draw(video);
			//カメラロールのインスタンスを作成する。
			var cameraroll:CameraRoll = new CameraRoll();
			//カメラロールにbitmapdataそのまま放り込む。これで保存完了
			cameraroll.addBitmapData(bmpd);
		}
	}
}

カメラロールはとても簡単ですが、圧縮率が調整できないので、かなり画質部分については不満が出るかもしれません。
これを解消するためにjpgファイルとして保存する方法がありますが、処理が重すぎてアプリ自体が強制終了してしまうので、かなりシビアに調整する必要があります。この方法についても改めて紹介したいと思います。

おまけ1:flatoolkit sample
処理速度がどれくらいになるかflatoolkitで動作確認してみました。↓こんな感じ。
javaで作ったものと比べるとやはりもたつくので複雑なアニメーションとかオブジェクトをかぶせるとしんどいかも?

おまけ2:エフェクトカメラ サンプル
Cameraの上のレイヤーにドラッグできるレイヤーモードを加算にしたMovieClipを配置した、簡単カメラエフェクト。