最初は、SATCHを使って「何か」を検出したらしゃべる...というのが目的でした。

で、「何」を検出するか考えているうちに、ゴッホの「ひまわり」はどうかな?と。

よく似ているものを識別して検出できるのか?

最初は半信半疑で、特に「15本の向日葵」と「12本の向日葵」は、構図も色合いもよく似ているので

7割方無理だろうと思っていました。

で、やってみて驚きました。問題なくすんなりと識別して検出。

SATCHというAR用SDKのエンジンは色認識はやっていない、ということのようなので、特徴点のみでデータを作っているのか?



「しゃべる」という目的は二の次になり、どうなっているのか?...が今はメインです。

これから調べてみます。わかり次第ご報告します。

NEWS

独立行政法人情報通信研究機構(NICT)が多言語に対応した音声翻訳アプリを国際研究チームと開発、無償で一般公開してくれるそうな。

現在は、iPhoneのみの対応だけど、今年中にはAndroidでも使えるようになる....そうです。

VoiceTra4U-M

TOP

対象作品と言語

検出対象は、現存する以下の6点。

(Wikipediaから引用)

1888年8月に制作されたとされる3点

12本の向日葵(ノイエ・ピナコテーク)





15本の向日葵(ナショナル・ギャラリー)





3本の向日葵(個人所蔵)





1889年1月に制作されたとされる3点

15本の向日葵(ゴッホ美術館)





15本の向日葵(損保ジャパン東郷青児美術館)





12本の向日葵(フィラデルフィア美術館)







日・英・仏の3ケ国語で、しゃべってみます。

TOP

SATCHシナリオ作成

SATCHでのシナリオのつくり方は以下を参照。

SATCHを使ってArt_ProjectでAR(マルチ・トラッキング)


ご注意!
個人所蔵の「3本の向日葵」は、検出に手間がかかります。

データ作成のための元絵の解像度が低かったのが原因....だと思います。

こんな画像でデータを作りました。







TOP

Eclipseでアプリ作成

Eclipse側の基本コードはこのページを参照

TextToSpeechのEclipse側のコードはこんな感じ。

vvvv,wwww,xxxx,yyyy,zzzzは適当に読み替えてください。

【抽象クラス】

package com.kddi.satch.zzzz;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.View;
import android.widget.FrameLayout;

import com.kddi.satch.LoadScenarioStatus;
import com.kddi.satch.ARViewer;
import com.kddi.satch.sunflowersspeaks.SunflowersSpeaksActivity;

import android.util.Log;
import java.util.Locale;
import android.speech.tts.TextToSpeech;//追加4tts
import android.speech.tts.TextToSpeech.OnInitListener;//追加4tts

import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.BufferType;
import android.view.WindowManager.LayoutParams;
import android.os.Handler;
import android.widget.Toast;

public abstract class xxxx extends Activity implements TextToSpeech.OnInitListener {
    protected abstract String getSampleScenarioName();
    protected abstract String getSampleLogTag();
    
    public TextToSpeech tts;//追加4tts
    public TextView Text_View;
    public LinearLayout textLayout;
    private Handler mHandler;
    private String textview_content = "";
    public String disp_lang = "en";
    
    
    
    protected boolean _isInitializedCorrectly;
    protected ARViewer _kddiComponent;
    protected FrameLayout _frameLayout;
    
    public void resetMembers(){
        _isInitializedCorrectly = false;
        _frameLayout = null;
        _kddiComponent = null;
    }
    private static final int DIALOG_EXIT = 0;
    
    public void initComponent(){
        _isInitializedCorrectly = false;
        _kddiComponent = new ARViewer(this);
        // This FrameLayout must be empty (but initialized) when you pass it to the kddiComponent.initialize() method.
        _frameLayout = new FrameLayout(this);
        _kddiComponent.initialize(_frameLayout);
        _isInitializedCorrectly = true;
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Add null to AR Viewer Library compornent's reference.
        resetMembers();
        //追加4tts
        tts = new TextToSpeech(getApplicationContext(), this);
        
        mHandler = new Handler();  
        
    }
    
    @Override
    public void onRestart() {
        super.onRestart();
    }
    
    @Override
    public void onStart() {
        super.onStart();
        // Create AR Viewer Library compornent.
        initComponent();
        postInitComponent();
        initContentView();
        if (_isInitializedCorrectly) {
            // Do authorize and madia is loaded.
            // You must call loadScenario() method.
            loadScenario();
        }
    }
    
    @Override
    public void onResume(){
        super.onResume();
        if (_isInitializedCorrectly) {
            // GL context is recreated and media is reloaded.
            _kddiComponent.onResume();
            reservePlayScenario();
        }
    }
    
    @Override
    public void onPause() {
        // When the activity is paused the GL context is destroyed, so all media is unloaded.
        if (_isInitializedCorrectly) {
            cancelReservePlayScenario();
        if (_kddiComponent.checkLoadScenarioStatus() == LoadScenarioStatus.COMPLETE) {
            _kddiComponent.pauseScenario();
        }
            _kddiComponent.onPause();
        }
        super.onPause();
    }
    
    @Override
    public void onStop() {
        releaseContentView();
        // Destroy AR Viewer Library Objects.
        if (_isInitializedCorrectly){
            _kddiComponent.terminate();
            _kddiComponent = null;
            _frameLayout = null;
        }
        super.onStop();
    }
    
    //追加4tts
    @Override
    public void onInit(int status) {
        //空
        //
        
    }
    
    @Override
    public void onDestroy() {
        // Destroy AR Viewer Library Objects.
        if (_isInitializedCorrectly){
            _frameLayout = null;
            _kddiComponent = null;
        }
        super.onDestroy();
        resetMembers(); // forced clean
        
        //追加4tts
        tts.shutdown();
    }
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg){
        switch(keyCode){
            case android.view.KeyEvent.KEYCODE_BACK :
            showDialog( DIALOG_EXIT );
            return true;
        }
        return false;
    }
    
    public void postInitComponent(){
        // override this if you need to do some special handling on the component after standard initialization
        if (_isInitializedCorrectly) {
            _kddiComponent.activateAutoFocusOnDownEvent(true);
        }
    }
    
    public void initContentView(){
        // override this if you need to do some special handling on the component after standard initialization
        if (_isInitializedCorrectly) {
            // you'll probably use some other UI object as the content view that itself will embed the component's frame layout -- here you can change all this
            // by default, the frame layout containing DFusion will be the activity content view
            setContentView(_frameLayout);
        }
        //
        
        textLayout = new LinearLayout(this);
        Text_View = new TextView(this);
        Text_View.setTextColor(Color.YELLOW);
        Text_View.setText("");
        
        
        textLayout.addView(Text_View, new LinearLayout.LayoutParams(200,200));
        //textLayout.setVisibility(View.VISIBLE);
        addContentView(textLayout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
        
        
    }
    
    public void set_text(String str_content){
        if (str_content.equals("private")) {
            
            if (disp_lang.equals("ja")) {
                textview_content = "3本の向日葵(個人所蔵)";
                
            }else if (disp_lang.equals("en")) {
                textview_content = "First version(Private collection)";
                
            }else if (disp_lang.equals("fr")) {
                textview_content = "Vase avec trois tournesols(Collection privée)";
                
            }
            
            
            
        
        }else if (str_content.equals("gogh")){
            
            if (disp_lang.equals("ja")) {
                textview_content = "15本の向日葵(ゴッホ美術館)";
                
            }else if (disp_lang.equals("en")) {
                textview_content = "Repetition of the 4th version(Van Gogh Museum)";
                
            }else if (disp_lang.equals("fr")) {
                textview_content = "Vase avec quinze tournesols(Musée van Gogh)";
                
            }
            
            
            
        
        }else if (str_content.equals("sompo")){
            
            if (disp_lang.equals("ja")) {
                textview_content = "15本の向日葵(損保ジャパン東郷青児美術館)";
                
            }else if (disp_lang.equals("en")) {
                textview_content = "Replica of the 4th version(Sompo Japan Museum of Art)";
                
            }else if (disp_lang.equals("fr")) {
                textview_content = "Vase avec quinze tournesols(Musée d'art Sompo)";
                
            }
            
            
            
        
        }else if (str_content.equals("national")){
            
            if (disp_lang.equals("ja")) {
                textview_content = "15本の向日葵(ナショナルギャラリー)";
                
            }else if (disp_lang.equals("en")) {
                textview_content = "Fourth version(National Gallery)";
                
            }else if (disp_lang.equals("fr")) {
                textview_content = "Vase avec quinze tournesols(National Gallery)";
                
            }
            
            
            
        
        }else if (str_content.equals("neue")){
            
            if (disp_lang.equals("ja")) {
                textview_content = "12本の向日葵(ノイエ・ピナコテーク)";
                
            }else if (disp_lang.equals("en")) {
                textview_content = "Third version(Neue Pinakothek)";
                
            }else if (disp_lang.equals("fr")) {
                textview_content = "Vase avec douze tournesols(Neue Pinakothek)";
                
            }
            
            
            
        
        }else if (str_content.equals("philadelphia")){
            
            if (disp_lang.equals("ja")) {
                textview_content = "12本の向日葵(フィラディルフィア美術館)";
                
            }else if (disp_lang.equals("en")) {
                textview_content = "Repetition of the 3rd version(Philadelphia Museum of Art)";
                
            }else if (disp_lang.equals("fr")) {
                textview_content = "Vase avec douze tournesols(Musée d'art de Philadelphie)";
                
            }
            
            
        
        }else{
            textview_content = "";
        }
        
        
        (new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //
                        Text_View.setText(textview_content);
                    }
                });
            }
        })).start();
        
        
    }
    
    public void releaseContentView(){
        // override this if you need to do some special handling on the component after standard initialization
        if (_isInitializedCorrectly) {
            // do here the release of the your UI instances (if customized)
        }
    }
    
    public void loadScenario(){
        ApplicationInfo appInfo = null;
        PackageManager packMgmr = getApplicationContext().getPackageManager();
        try {
            appInfo = packMgmr.getApplicationInfo(getPackageName(), 0);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException("Unable to locate assets, aborting...");
        }
        String dpdfile = appInfo.sourceDir + getSampleScenarioName();
        _kddiComponent.loadScenario(dpdfile);
    }
    
    // Set polling rate for loading the media.
    private final int REPEAT_INTERVAL = 100;
    private Handler handler = new Handler();
    private Runnable runnable = null;
    
    
    //
    private void reservePlayScenario(){
        if (runnable == null){
            runnable = new Runnable(){
                @Override
                public void run(){
                    LoadScenarioStatus status = _kddiComponent.checkLoadScenarioStatus();
                    if (status == LoadScenarioStatus.CANCEL){
                        // cancel(appli suspend)
                    }else if (status == LoadScenarioStatus.COMPLETE){
                        // Ready to play scenario
                        _frameLayout.setVisibility(View.VISIBLE);
                        _kddiComponent.playScenario();
                        
                        Toast.makeText(xxxx.this, "Preparing........", Toast.LENGTH_LONG).show();
                        
                    }else if (
                        status == LoadScenarioStatus.ERROR_NETWORK_UNUSABLE ||
                        // faild to load a media becase of no network connection.
                        status == LoadScenarioStatus.ERROR_NETWORK ||
                        // faild to load a media becase of network error.
                        status == LoadScenarioStatus.ERROR_SOFTWAREKEY ||
                        // faild to load a media becase software key has not be found on server.
                        status == LoadScenarioStatus.ERROR_CONTENT_STOPPED ||
                        // faild to load a media becase content has stopped.
                        status == LoadScenarioStatus.ERROR_SERVER ||
                        // faild to load a media becase of server error.
                        status == LoadScenarioStatus.ERROR_ETC
                        // faild to load a media becase of another error.
                        ){
                    // error
                    }else{
                        handler.postDelayed(this, REPEAT_INTERVAL);
                    }
                }
            };
            handler.postDelayed(runnable, REPEAT_INTERVAL);
        }
    }
    
    //
    
    private void cancelReservePlayScenario(){
        if (handler != null && runnable != null){
            handler.removeCallbacks(runnable);
            runnable = null;
        }
    }
    
    protected Dialog onCreateDialog(int id) {
        
        String end_mes = "Really want to quit the application?";
        String yes_str = "Yes";
        String no_str = "No";
        
        Dialog dialog;
        switch(id) {
            case DIALOG_EXIT:
            {
                //
                
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(
                        end_mes
                )
                .setCancelable(true)
                .setPositiveButton(yes_str, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        finish();
                    }})
                    .setNegativeButton(no_str, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }});
                    dialog = builder.create();
                    break;
            }
            default:
            dialog = null;
        }
        return dialog;
    }
    
    
    
}


【継承クラス】

package com.kddi.satch.wwww;


import com.kddi.satch.zzzz.xxxx;


import android.os.Bundle;
import android.speech.tts.TextToSpeech;//4tts
import android.util.Log;
import android.widget.Toast;

import java.util.Locale;
import android.content.Intent;


import android.view.Menu;
import android.view.MenuItem;

public class yyyy extends xxxx {
    
    private static final String THIS_CLASS_SHORT_NAME = yyyy.class.getName().replace("com.kddi.satch.wwww.", "");
    private static final String THIS_LOGTAG  = THIS_CLASS_SHORT_NAME;
    // Set AR scenario file path.
    private static final String SCENARIO_NAME = "/assets/Scenario/Scenario_sunflower/vvvv.dpd";
    protected String getSampleScenarioName() { return SCENARIO_NAME; }
    protected String getSampleLogTag() { return THIS_LOGTAG;   }
    
    private String prev_val = "";
    
    //private static final int CHECK_TTS = 682730935;

    //menu
    private static final int    MENU_JA  = 0;
    private static final int    MENU_EN   = 1;
    private static final int    MENU_FR      = 2;
    
    
    @Override
    public void postInitComponent(){
        super.postInitComponent();
        
        //4tts
        if (_isInitializedCorrectly) {
            _kddiComponent.activateAutoFocusOnDownEvent(true);
            _kddiComponent.registerCommunicationCallback("setTrackingStatus", this, "setTrackingStatus");
        }
    }
    
    //
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //4tts
        //tts = new TextToSpeech(getApplicationContext(), this);
        
        

    }
    
  //4tts
    public void onInit(int status) {
         //
        if (TextToSpeech.SUCCESS == status) {
            //
        } else {
            Log.d("", "Error Init");
        }
    }
    
    
    
    
    //add
    public void setTrackingStatus(String[] arrayOfString) {
        Log.i("STAT",arrayOfString[0]);
        
        if (arrayOfString[0].equals(prev_val)) {
            Log.i("STAT","repeat");
            return;
        }
        
        if (arrayOfString[0].equals("out")) {
            prev_val = "";
            set_text("");
            return;
        }
        
        
        set_text(arrayOfString[0]);
        
        
        if (arrayOfString[0].equals("private")) {
            if (disp_lang.equals("ja")){
                tts.speak("3本のひまわり、アメリカ", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("en")){
                tts.speak("Sunflowers, first version,United States", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("fr")){
                tts.speak("Vase avec trois tournesols, États-Unis", TextToSpeech.QUEUE_FLUSH, null);
            }
            
            
        }else if (arrayOfString[0].equals("gogh")){
            if (disp_lang.equals("ja")){
                tts.speak("15本のひまわり、アムステルダム", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("en")){
                tts.speak("Sunflowers, repetition of the 4th version ,Amsterdam, Netherlands", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("fr")){
                tts.speak("Vase avec quinze tournesols,Amsterdam, Pays-Bas", TextToSpeech.QUEUE_FLUSH, null);
            }
            
            //
        }else if (arrayOfString[0].equals("sompo")){
            if (disp_lang.equals("ja")){
                tts.speak("15本のひまわり、東京", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("en")){
                tts.speak("Sunflowers, replica of the 4th version,Tokyo, Japan", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("fr")){
                tts.speak("Vase avec quinze tournesols,Tokyo, Japon", TextToSpeech.QUEUE_FLUSH, null);
            }
            
            
        }else if (arrayOfString[0].equals("national")){
            if (disp_lang.equals("ja")){
                tts.speak("15本のひまわり、ロンドン", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("en")){
                tts.speak("Sunflowers, fourth version,London, England", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("fr")){
                tts.speak("Vase avec quinze tournesols ,Londres, Angleterre", TextToSpeech.QUEUE_FLUSH, null);
            }
            
            
        }else if (arrayOfString[0].equals("neue")){
            if (disp_lang.equals("ja")){
                tts.speak("12本のひまわり、ミュンヘン", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("en")){
                tts.speak("Sunflowers, third version,Munich, Germany", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("fr")){
                tts.speak("Vase avec douze tournesols,Munich, Allemagne", TextToSpeech.QUEUE_FLUSH, null);
            }
            
            
        }else if (arrayOfString[0].equals("philadelphia")){
            if (disp_lang.equals("ja")){
                tts.speak("12本のひまわり、フィラデルフィア", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("en")){
                tts.speak("Sunflowers, repetition of the 3rd version,Philadelphia, United States", TextToSpeech.QUEUE_FLUSH, null);
            }else if (disp_lang.equals("fr")){
                tts.speak("Vase avec douze tournesols ,Philadelphie, États-Unis", TextToSpeech.QUEUE_FLUSH, null);
            }
            
            
        }
    
        
        prev_val = arrayOfString[0];
        
    }
    
    //
    public boolean onCreateOptionsMenu(Menu menu) {
          
          
         menu.add(0, MENU_JA,0, "日本語").setIcon(R.drawable.ja);
         menu.add(0, MENU_EN,0, "English").setIcon(R.drawable.en);
         menu.add(0, MENU_FR,0, "Français").setIcon(R.drawable.fr);
         

         return true;
      }
    
    public boolean onOptionsItemSelected(MenuItem item) 
    {
        
        
    
        switch ( item.getItemId() )
        {
            case MENU_JA:
                disp_lang = "ja";
                check_lang("ja");
                
                return true;
            case MENU_EN:
                disp_lang = "en";
                check_lang("en");
                
                return true;
            case MENU_FR:
                disp_lang = "fr";
                check_lang("fr");
                
                return true;
            
                
        }
        
        return false;
    }
    //
    public void check_lang(String lang){
        if (lang.equals("ja")) {
            if (tts.isLanguageAvailable(Locale.JAPAN) >= TextToSpeech.LANG_AVAILABLE) {
                Toast.makeText(yyyy.this, "JP", Toast.LENGTH_LONG).show();
                //setLanguage
                 tts.setLanguage(Locale.JAPAN);
                 
                 
                 
                 
                 
                 
            }else{
                
                if (tts.isLanguageAvailable(Locale.JAPAN) >= TextToSpeech.LANG_MISSING_DATA) {
                    Toast.makeText(yyyy.this, "日本語データをインストールしてください。", Toast.LENGTH_LONG).show();
                }else{
                    Toast.makeText(yyyy.this, "TextToSpeechのエンジンを日本語用に切り替えてください。", Toast.LENGTH_LONG).show();
                    
                    
                }

            }
        }else if (lang.equals("en")) {
            if (tts.isLanguageAvailable(Locale.UK) >= TextToSpeech.LANG_AVAILABLE) {
                Toast.makeText(yyyy.this, "UK", Toast.LENGTH_LONG).show();
                //setLanguage
                tts.setLanguage(Locale.UK);
                
            }else{
                
                if (tts.isLanguageAvailable(Locale.UK) >= TextToSpeech.LANG_MISSING_DATA) {
                    Toast.makeText(yyyy.this, "Install TTS English data.", Toast.LENGTH_LONG).show();
                }else{
                    Toast.makeText(yyyy.this, "Change TTS Engine to PicoTTS.", Toast.LENGTH_LONG).show();
                }
            }
        }else if (lang.equals("fr")) {
            if (tts.isLanguageAvailable(Locale.FRENCH) >= TextToSpeech.LANG_AVAILABLE) {
                Toast.makeText(yyyy.this, "FR", Toast.LENGTH_LONG).show();
                //setLanguage
                tts.setLanguage(Locale.FRENCH);
            }else{
                
                if (tts.isLanguageAvailable(Locale.FRENCH) >= TextToSpeech.LANG_MISSING_DATA) {
                    Toast.makeText(yyyy.this, "Installe les françaises données.", Toast.LENGTH_LONG).show();
                }else{
                    Toast.makeText(yyyy.this, "Changez le Moteur TTS à PicoTTS.", Toast.LENGTH_LONG).show();
                }
            }
        }
        
    }
    
    
    
    //
}


SATCHのシナリオから検出時データを取得して、N2 TTSとPico TTSに喋らせてます。



TOP

インストールと起動

日本語をしゃべるには、日本語の音声合成エンジンが必要です。

KDDIの音声合成エンジン「N2 TTS」のインストールについては、以下を参照

アンドロイドで日本語音声出力(TextToSpeech):音声読み上げ


英・仏語は日本語とは別のTTSエンジンを使います(Pico TTS)。









切り替えは、現状、マニュアルで行います。





N2 TTSがインストールされていれば、N2のアイコンをタップしてメニュから行えます。
設定->音声入出力->テキスト読み上げの設定->エンジン、からも行えます。


英・仏言語データのインストール

Pico TTSの項をタップして、言語データが未インストールなら、タップしてインストールを実行してください。


アプリ(apk)をインストール



ダウンロード後、通知パネルを開いて、完了メッセージの項をタップ。
起動

初回起動時には以下のようなメッセージが出ますが、気にしないでください。「はい」でも「いいえ」でも、どちらでもいいです。









起動後、7~8秒、真っ暗ですが問題ないです。







カメラを絵に向けると、文字を表示して、「しゃべります」。

絵のサイズはある程度大きなものにしてください。あまりに小さいと検出に失敗します。













TOP





トップページ| サイトマップ|