MSC2011 D3-303 “Windows Phone/iOS/Android から Windows Azure を利用する” session follow up - Part 2 : Babel.cam ソリューション解説-2

皆様、こんばんは!さて、本記事では、前の記事に続いて、Babel.Cam解説、今度はソースコードについて、いくつか見ていきましょう。

基本的には、Worker Role.cs に、重要な処理が書いてあります。ここで理解して戴きたいのは、まずは Web Role との違いですね。ご存じない方もおられたようですので、改めてご紹介します。次の図をご覧ください。

image

Web Role には IIS があり、Worker Role には IIS はありません。Worker Roleは、main() 関数を持った DLLです。ただ、いずれも RoleEntryPoint   を実装していることは共通点でしょう。サンプル実装は、下記にある通りです。

image

この Run() という箇所で、while (true) の Loop が動いていることに注目してください。Worker Role での処理は、ここで何らかのインプットを待ち続けて、それに対して何らかのアウトプットを返す、というようなイメージでOKだと思います。

これを頭に入れたうえで、Babel.Cam の WorkeRole.cs のソースコードを見てみましょう。順にみていきますが、まずは Run() の箇所です。

Run()

    1: public override void Run()
    2: {
    3:     // This is a sample worker implementation. Replace with your logic.
    4:     Trace.WriteLine("BabelCam.Worker entry point called", "Information");
    5:  
    6:     while (true)
    7:     {
    8:         try
    9:         {
   10:             foreach (var rawMessage in this.requestQueue.GetMessages(this.batchSize))
   11:             {
   12:                 this.requestQueue.DeleteMessage(rawMessage);
   13:                 var request = Extensions.Deserialize<OcrRequest>(rawMessage.AsString);
   14:  
   15:                 if (string.IsNullOrWhiteSpace(request.BlobName) || !request.BlobName.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
   16:                 {
   17:                     continue;
   18:                 }
   19:  
   20:                 var container = this.cloudBlobclient.GetContainerReference(request.ContainerName);
   21:                 var blob = container.GetBlobReference(request.BlobName);
   22:  
   23:                 try
   24:                 {
   25:                     ProcessOutput processOutput = null;
   26:                     using (var stream = new MemoryStream())
   27:                     {
   28:                         blob.DownloadToStream(stream);
   29:  
   30:                         processOutput = this.ProcessImage(stream);
   31:                     }
   32:  
   33:                     var words = string.Empty;
   34:                     var wordCount = 0;
   35:                     var status = string.Empty;
   36:                     if (processOutput != null)
   37:                     {
   38:                         if (!processOutput.Attributes.TryGetValue("Status", out status))
   39:                         {
   40:                             status = string.Empty;
   41:                         }
   42:  
   43:                         if (!processOutput.Attributes.TryGetValue("Words", out words))
   44:                         {
   45:                             words = string.Empty;
   46:                         }
   47:  
   48:                         var wordCountString = string.Empty;
   49:                         processOutput.Attributes.TryGetValue("WordCount", out wordCountString);
   50:                         int.TryParse(wordCountString, out wordCount);
   51:                     }
   52:  
   53:                     var message = string.Empty;
   54:                     var translationRowKey = string.Empty;
   55:                     if (status.Equals(Status.Success.ToString(), StringComparison.OrdinalIgnoreCase))
   56:                     {
   57:                         if (wordCount > 0)
   58:                         {
   59:                             var translated = this.Translate(words, request.FromLanguage, request.ToLanguage);
   60:  
   61:                             var wordEntity = new WordsEntity(translated, blob.Uri.ToString(), request.UserNameIdentifier);
   62:                             this.translatedWordsRepository.AddTranslatedWords(wordEntity);
   63:                             translationRowKey = wordEntity.RowKey;
   64:  
   65:                             this.LogInformation(string.Concat("orig (", request.FromLanguage, ") for ", request.UserNameIdentifier, ": ", words));
   66:                             this.LogInformation(string.Concat("trans (", request.ToLanguage, ") for ", request.UserNameIdentifier, ": ", translated));
   67:  
   68:                             message = translated;
   69:                         }
   70:                         else
   71:                         {
   72:                             message = "[No text was found]";
   73:                             translationRowKey = string.Empty;
   74:                         }
   75:                     }
   76:                     else
   77:                     {
   78:                         message = "[The OCR conversion failed]";
   79:                         translationRowKey = string.Empty;
   80:                     }
   81:  
   82:                     this.SendPushNotification(request.UserNameIdentifier, message, translationRowKey);
   83:                 }
   84:                 catch (Exception exception)
   85:                 {
   86:                     this.SendPushNotification(request.UserNameIdentifier, "There was an error processing your translation request.");
   87:                     this.LogError(exception);
   88:                 }
   89:             }
   90:         }
   91:         catch (Exception exception)
   92:         {
   93:             this.LogError(exception);
   94:         }
   95:  
   96:         Thread.Sleep(this.pollIntervalMiliseconds);
   97:         Trace.WriteLine("Working", "Information");
   98:     }
   99: }

image

もう一度、Babel.Cam の処理の流れを、書き出してみましょう。

Windows Phone のカメラで撮った、英語やフランス語等、外国語の文章を、画像ファイルとしてWindows Azure ストレージにUpload します。

その情報を Web Role が受信し、Web Role により Message として Queue に格納されます。

それを、同じく Windows Azure にいる Worker Role が、常にチェックしていて、画像が入ってきたというQueueの情報をもとに、Microsoft Research が提供するHawaii というOCRのWeb サービスに渡して、テキストファイル化します。

その後、Bing Translator Services という SOAPのWeb サービスにそのテキストファイルを渡して、翻訳を行います。

翻訳が完了すると、MPNS (Microsoft Push Notification Service) を使って、その通知をWindows Phone に送付すると同時に、翻訳された結果のテキストファイルを、Windows Phone に送付します。

Translate()

つぎに、このRun()の中でも呼ばれている Translate() です。これは、Hawaii OCR Service のWeb サービス(SOAP)を参照設定し、あらかじめ取得しておいた Hawaii Application ID を渡すことにより、使用することができます。

ソリューションエクスプローラの中から、BabelCam.Worker プロジェクトを選択し、Service References フォルダを開くと、そこに Hawaii OCR Service が参照設定されています。

  image image

 

    1: private string Translate(string words, string fromLanguage, string toLanguage)
    2: {
    3:     var translated = words;
    4:     if (!fromLanguage.Equals(toLanguage, StringComparison.OrdinalIgnoreCase))
    5:     {
    6:         try
    7:         {
    8:             using (var client = new TranslatorService.LanguageServiceClient(new BasicHttpBinding(), new EndpointAddress(this.translatorBaseAddress)))
    9:             {
   10:                 var options = new TranslatorService.TranslateOptions();
   11:                 var response = client.GetTranslations(this.translatorAppId, words, fromLanguage, toLanguage, 1, options);
   12:                 translated = response.Translations[0].TranslatedText;
   13:             }
   14:         }
   15:         catch (Exception exception)
   16:         {
   17:             this.LogError(exception);
   18:         }
   19:     }
   20:  
   21:     return translated;
   22: }

TranslateService をインスタンス化し、そのメソッドを操作して、この SOAP の Web サービスを利用することができます。このメソッドの中で、FROM : 言語から、TO : 言語に変換しています。

以上、セッションでご紹介したソースコードを簡単に見てみました。興味のある方は、ぜひほかの箇所もWalk through してみてくださいね。

本件に関し、さらに詳しく知りたい方は、C:\WindowsAzure\WATWindowsPhone\Docs にあるReadme.docx の P.19 以降及び P.113 以降をご覧ください。Hawaii や Bing Translator の ID 取得方法も紹介されています。

なお、開発環境の構築や、Windows Azure への展開と端末へのインストールに関しては、セッションでの順番通り、次項でご紹介するスクラッチからのWindows Phone Cloud Application 開発のところで、ご紹介します。

鈴木 章太郎