画像トラッキングして地図やWeb・ストリートビューと連携するAR(拡張現実)
今回は、ゴッホの「ひまわり」のみをトラッキングします。
検出したら、情報表示サイトを開いて、その絵を所蔵している美術館をGoogle Mapsで見てみましょう。
さらに、StreetViewを開いて、美術館の中をウォーキング。
Art_Projectのindoor StreetViewみたいに、フロア・プランなんぞはありませんが、雰囲気は分かります。
こんな感じです
絵画の検出には、SATCHを使ってArt_ProjectでARで使ったやり方を使用。
絵画は、ゴッホの「ひまわり」を使います。
絵画を検出していない場合
メニュの「Map View」をタップすると、GPSで取得した現在地を表示。
GPSでの位置情報が取得できていない場合、とりあえず「富士山」にロケーションされています。
「Web View」をタップすると、ただのGoogleの検索画面を表示。
「StreetView」をタップした場合。
GPSで位置情報が取得できていない場合やご近所にStreetViewのポイントがない場合、単に真っ黒な画面。
絵画を検出した場合
緑色の文字が表示されれば、検出OKです。
緑色の文字が表示された状態のままで、以下を実行。
メニュの「Map View」をタップすると、この絵画を所蔵するゴッホ美術館の場所を表示。
「Web View」をタップすると、絵画などについての情報を表示。
「StreetView」をタップした場合、ゴッホ美術館をストーリートビュー。
美術館の中に入っていきましょう。
「コンパスモード」を使えば、スマホを傾けたり周囲に向けることでストリートビューの方向が変わります。
我が家のボロ部屋が美術館になったみたいで、結構、臨場感があります。
以上
TOP
SATCHのtracking.luaのコード
絵画の検出については、SATCHを使ってArt_ProjectでARを参照。
【tracking.lua】
local text = Text2D(getCurrentScene():getObjectByName("text")) local componentInterface = getComponentInterface() -- GLOBAL VARIABLES -- tracking statuts: if 1 : tracking, if 0 : no tracking gtrackingStatus = 0 -- get the keyframe index from a tracked object with auto-initialization gtrackingKeyFrameIndex = -1 -- scene local scene = getCurrentScene() -- get the virtual camera, will be used to send to the MLT local camera = Camera(scene:getCurrentCamera()) -- get the videocapture, will be used to send to the MLT local videoCapture_live = VideoCapture(scene:getObjectByName("videocapture_live")) -- Tracking local MLTPlugin = getMLTPluginManager() -- Error status local errorStatus = eOk -- tracking index : the index of the tracker.xml (because we can open more than 1 tracking.xml file). local trackingIndex = -1 -- the fps of the Tracking engine local trackingRate = 0 -- vector to put the tracking position local trackingPosition = Vector3() -- quaternion to put the tracking orientation local trackingOrientation = Quaternion() -- 3D object receiving tracking pose --local trackingObject = Object3D(scene:getObjectByName("Tracking_Object")) -- object index from the tracking scenario (0 : first object, 1 : second object...) (this is the index in the "Objects" panel of the CV GUI) local trackingObjectIndex = 0 local found_sendflag = false local lost_sendflag = false -- this is how to start a tracking. the function needs the path to the tracker.xml file, the videocapture id and the camera object. errorStatus, trackingIndex = MLTPlugin:startTracking("tracker/tracker.xml", videoCapture_live:getVidCapID(), camera) -- if the tracking has correctly started, we can proceed to an infinite loop if errorStatus == eOk then repeat errorStatus, gtrackingStatus = MLTPlugin:getTargetStatus(trackingIndex, trackingObjectIndex) errorStatus, gtrackingKeyFrameIndex = MLTPlugin:getRecognizedKeyFrameIndex(trackingIndex, trackingObjectIndex) -- if our object is detected... if (gtrackingStatus == 1) then if found_sendflag == false then --LOG("found") -- call java method componentInterface:executeAppFunc("setTrackingStatus","true") text:setText("The Sunflowers(Vincent van Gogh)") text:setVisible(true) found_sendflag = true lost_sendflag = false end -- if the tracking is lost else if lost_sendflag == false then --LOG("lost") -- call java method componentInterface:executeAppFunc("setTrackingStatus","false") text:setVisible(false) found_sendflag = false lost_sendflag = true end end until coroutine.yield() end
SATCHのシナリオを「com.kddi.satch.xxxxxx」で保存。
xxxxxxのところは、ご自分で適当に設定してください。
このアプリケーションIDは、EclipseのAndroidManifest.xmlのパッケージ名と一致させます。
でないと、アプリをデプロイする際に、ライセンスエラーになります。
TOP
Eclipseのプロジェクト
ソースはSATCHのViewFlipperを参照。
ダウンロードできるようにしておきましたので、なんかの参考にしていただければ...。
ViewFlipper
このままでは、素直に使えません(^^)。
これをベースにして、プロジェクトを再作成してください。
その際必要なのは、
com.kddi.satch.sampleviewflipperのプロジェクト名をSATCHのシナリオ作成で使ったものに変更。
res/layout/map.xmlのGoogleのキーを自分のものに変更(この辺はSATCHのDevelopers siteのViewFlipperを参照)
GPSを使うので、AndroidManifest.xmlに以下を追加。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
javaファイルで基本的に変更がかかるのは、SampleViewFlipper.javaだけです。
参考になるかどうか分かりませんが例です。SATCHのオリジナルと異なるのは、GPSの位置情報取得用のコードを入れています。
StreetViewは、本来ViewFlipperで実装したかったのですが、MapViewでsetStreetView()関数が非推奨になって、使い方がわからなかったのでintentで画面遷移して表示しています。
ぜんぜんユーザーフレンドリーじゃないので、コードが分かったら、差し替えます。
package com.kddi.satch.sunflowersviewflipper; import java.util.List; import com.google.android.maps.*; import android.location.*; import com.kddi.satch.sunflowersactivity.SunflowersActivity_simpleMap; import android.app.Activity; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ViewFlipper; import android.net.Uri;//add import android.content.Intent;//add public class SunflowersViewFlipper extends SunflowersActivity_simpleMap { private static final String THIS_CLASS_SHORT_NAME = SunflowersViewFlipper.class.getName().replace("com.kddi.satch.sunflowersviewflipper.", ""); private static final String THIS_LOGTAG = THIS_CLASS_SHORT_NAME; private static final String SCENARIO_NAME = "/assets/Scenario/Scenario_a/art_project_sunflowers.dpd"; protected String getSampleScenarioName() { return SCENARIO_NAME; } protected String getSampleLogTag() { return THIS_LOGTAG; } private ViewFlipper _viewFlipper; private MapView _mapView; private GoogleMapsItemizedOverlay _itemizedoverlay1; private GoogleMapsItemizedOverlay _itemizedoverlay2; private OverlayItem _overlayitem1; private OverlayItem _overlayitem2; private GeoPoint _point1; private GeoPoint _point2; private WebView _webView; private String _urlTargetPlace; private boolean _trackingStatus; private LocationManager mLocationManager; private double my_latitude = 0; private double my_longitude = 0; private static final int MENU_VIEW_DFUSION = 0; private static final int MENU_VIEW_WEB = 1; private static final int MENU_VIEW_MAP = 2; private static final int MENU_STREET_VIEW = 3; private void resetMembers() { _viewFlipper = null; _mapView = null; _itemizedoverlay1 = null; _itemizedoverlay2 = null; _overlayitem1 = null; _overlayitem2 = null; _point1 = null; _point2 = null; _webView = null; _trackingStatus = false; _urlTargetPlace = "http://www.google.com"; } @Override protected boolean isRouteDisplayed() { // from MapActivity return false; } @Override public void onCreate(Bundle savedInstanceState) { Log.i( getSampleLogTag(), ".... onCreate" ); super.onCreate(savedInstanceState); // initialize values resetMembers(); // initialize inflated views {// Web view LayoutInflater li = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); _webView = (WebView) li.inflate(R.layout.web, null); initWebView(); } {// Map view LayoutInflater li = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); _mapView = (MapView) li.inflate(R.layout.map, null); initMapView(); } mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setAltitudeRequired(false); criteria.setBearingRequired(false); criteria.setCostAllowed(false); criteria.setPowerRequirement(Criteria.POWER_LOW); criteria.setSpeedRequired(false); //指定する取得条件でプロバイダーを取得 String provider = mLocationManager.getBestProvider(criteria, true); mLocationManager.requestLocationUpdates(provider, 1000, 1, locationListener); } @Override public void onDestroy() { Log.i( getSampleLogTag(), ".... onDestroy" ); // release what we inflated _mapView = null; _webView = null; super.onDestroy(); // final clean-up resetMembers(); } @Override public void postInitComponent() { Log.i( getSampleLogTag(), "postInitComponent" ); _kddiComponent.activateAutoFocusOnDownEvent(true); _kddiComponent.registerCommunicationCallback("setTrackingStatus", this, "setTrackingStatus"); } @Override public void initContentView() { Log.i( getSampleLogTag(), "initContentView" ); if (_isInitializedCorrectly) { _viewFlipper = new ViewFlipper(this); // Child 0 : Gogh _viewFlipper.addView(_frameLayout); // Child 1 : Web View _viewFlipper.addView(_webView); // Child 2 : Map View _viewFlipper.addView(_mapView); // Activity content view will be the view flipper setContentView(_viewFlipper); new Handler().postDelayed(new Runnable() { public void run() { openOptionsMenu(); } }, 1000 ); } } @Override public void releaseContentView() { Log.i( getSampleLogTag(), "releaseContentView" ); if (_isInitializedCorrectly) { if (_viewFlipper != null) { _viewFlipper.removeAllViews(); } _viewFlipper = null; } } public void setTrackingStatus(String [] arrayOfString) { Log.i("STATUS",arrayOfString[0]); if(arrayOfString.length ==1){ if(arrayOfString[0].equals("true")){ setTrackingStatus(true); }else{ setTrackingStatus(false); } } } public void setTrackingStatus(boolean trackingStatus) { _trackingStatus = trackingStatus; } public void setUrlTargetPlace(int target_num) { MapController ctrl = _mapView.getController(); if(ctrl != null){ if(target_num == 1){ List<Overlay> mapOverlays = _mapView.getOverlays(); mapOverlays.clear(); initMapView(); ctrl.setCenter(_point1); _urlTargetPlace = "http://www.google.fr"; }else if(target_num == 2){ ctrl.setCenter(_point2); _urlTargetPlace = "http://wisteriahill.sakura.ne.jp/jqm_viewflipper/sunflowers.html"; } } _webView.loadUrl(_urlTargetPlace); } public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, MENU_VIEW_DFUSION, 0, "Gogh's Sunflowers").setIcon(R.drawable.sunflowers); menu.add(0, MENU_VIEW_WEB, 0, "Web view").setIcon(R.drawable.web); menu.add(0, MENU_VIEW_MAP, 0, "Map view").setIcon(R.drawable.map); menu.add(0, MENU_STREET_VIEW, 0, "StreetView").setIcon(R.drawable.pegman); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch ( item.getItemId() ) { case MENU_VIEW_DFUSION: _viewFlipper.setDisplayedChild(0); return true; case MENU_VIEW_WEB: _viewFlipper.setDisplayedChild(1); return true; case MENU_VIEW_MAP: _viewFlipper.setDisplayedChild(2); if (_trackingStatus == true){ setUrlTargetPlace(2); } else{ setUrlTargetPlace(1); } return true; case MENU_STREET_VIEW: double sv_latitude = 0; double sv_longitude = 0; if (_trackingStatus == true){ sv_latitude = 52.358417; sv_longitude = 4.881083; } else{ sv_latitude = my_latitude; sv_longitude = my_longitude; } String uri = "google.streetview:cbll="+ sv_latitude +"," + sv_longitude; Intent streetView = new Intent(android.content.Intent.ACTION_VIEW,Uri.parse(uri)); startActivity(streetView); return true; } return false; } private class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } } public void initMapView() { _mapView.setBuiltInZoomControls(true); //overlay2 Gogh Museum List<Overlay> mapOverlays = _mapView.getOverlays(); Drawable drawableMuseum = this.getResources().getDrawable(R.drawable.van_gogh_museum); _itemizedoverlay1 = new GoogleMapsItemizedOverlay(drawableMuseum, this.getApplicationContext()); double latitude2 = 52.358417; double longitude2 = 4.881083; int lat2 = (int)(latitude2*1E6); int lon2 = (int)(longitude2*1E6); _point2 = new GeoPoint(lat2,lon2); _overlayitem2 = new OverlayItem(_point2, "Hello!", "I'm 2"); _itemizedoverlay1.addOverlay(_overlayitem2); mapOverlays.add(_itemizedoverlay1); // overlay1 MyLoc Drawable drawableMyLoc = this.getResources().getDrawable(R.drawable.home); _itemizedoverlay2 = new GoogleMapsItemizedOverlay(drawableMyLoc, this.getApplicationContext()); if (_point1 == null) { double latitude1 = 35.36286726458948; double longitude1 = 138.73157501220703; int lat1 = (int)(latitude1*1E6); int lon1 = (int)(longitude1*1E6); _point1 = new GeoPoint(lat1,lon1); } _overlayitem1 = new OverlayItem(_point1, "Hello!", "I'm 1"); _itemizedoverlay2.addOverlay(_overlayitem1); mapOverlays.add(_itemizedoverlay2); } public void initWebView() { _webView.getSettings().setJavaScriptEnabled(true); _webView.getSettings().setBuiltInZoomControls(true); _webView.getSettings().setSupportZoom(true); final SunflowersViewFlipper activity = this; _webView.setWebChromeClient(new WebChromeClient() { public void onProgressChanged(WebView view, int progress) { // Activities and WebViews measure progress with different scales. // The progress meter will automatically disappear when we reach 100% ((Activity) activity).setProgress(progress * 1000); } }); // to handle its own URL requests : _webView.setWebViewClient(new MyWebViewClient()); _webView.loadUrl("http://www.google.fr"); } //------------------------------- private LocationListener locationListener = new LocationListener() { @Override public void onLocationChanged(Location location) { my_latitude = location.getLatitude(); my_longitude = location.getLongitude(); int lat = (int)(my_latitude*1E6); int lon = (int)(my_longitude*1E6); _point1 = new GeoPoint(lat,lon); } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { switch (status) { case LocationProvider.AVAILABLE: //Log.v("Status", "AVAILABLE"); break; case LocationProvider.OUT_OF_SERVICE: //Log.v("Status", "OUT_OF_SERVICE"); break; case LocationProvider.TEMPORARILY_UNAVAILABLE: //Log.v("Status", "TEMPORARILY_UNAVAILABLE"); break; } } }; } //-- end of file --
コード内のパッケージ名やファイル名、SCENARIO_NAMEのパスは修正してください。
TOP
公開用Google ApiKey取得
ViewFlipperサンプルでは、MapViewを使ってGoogle Mapsを表示しています。
このサンプルを使って新しくプロジェクトを作成し
apkを公開する場合、公開用のGoogle Apiキーを取得する必要があります。
ここでは、既にキーストアが作成されているものとします。
キーストアの作成やデジタル署名したapkの作成方法は、
PhoneGapでネイティブアプリ
の、「ネイティブアプリを作って、ダウンロード・インストール」の項を参照。
まず、DOS窓を開きます。
以下のコマンドを発行。
keytool -list -keystore <作成したキーストアのフルパス>
パスワードを聞いてきますので、これもキーストア作成時のパスワードを使用。
証明書のフィンガープリントが表示されます。
Sign Up for the Android Maps API
のページを開きます。
ページの下の、I have read and agree ...にチェックを入れ、My certificate's MD5 fingerprint:の欄に先ほどのフィンガープリントを入力して、Generate API Keyのボタンをクリック。
表示されたキーが公開用のキーになります。
これを、res->layout->map.xmlのandroid:apiKeyにセットします。
これで、MapViewでGoogle Mapsが表示されるようになります。
TOP
トップページ| サイトマップ|