Azure 関数で結果を Azure SQL DB に書き込む

データベースができたので、Custom Vision Service を呼び出して、画像にシロクマが含まれる可能性を判断し、Azure SQL データベースに出力を書き込むように、Azure 関数を変更してみましょう。

  1. Azure portal で、前に作成した Azure 関数アプリに切り替えます。

  2. [プラットフォーム機能] を選択して [プラットフォーム機能] タブを開き、[コンソール] をクリックします。

    関数コンソールを開く

  3. 関数コンソールで次のコマンドを実行し、request パッケージ、tedious パッケージ、および Azure Storage SDK for Node.js をインストールして関数でそれらを使用できるようにし、表示される警告メッセージは無視します。 パッケージが正しく読み込まれるよう、それぞれを個別に実行してください。

    npm install request
    
    npm install tedious
    
    npm install azure-storage
    

    ヒント

    JavaScript で記述された Azure 関数は、Node.js 環境で実行されます。 関数コンソールでは、その環境にアクセスし、ローカル環境の場合と同じように、NPM パッケージをインストールすることができます。

  4. 作成した関数の名前を選択して、関数アプリに含まれる Azure 関数を開きます (HttpTrigger1 のようなものです)。

  5. 関数のコードを次のコードに置き換えます。

    module.exports = function (context, req) {
        var predictionUrl = 'PREDICTION_URL';
        var predictionKey = 'PREDICTION_KEY';
        var storageAccountName = 'ACCOUNT_NAME';
        var storageAccountKey = 'ACCOUNT_KEY';
        var databaseServer = 'SERVER_NAME.database.windows.net';
        var databaseUsername = 'ADMIN_USERNAME';
        var databasePassword = 'ADMIN_PASSWORD';
        var databaseName = 'photodb';
    
        // Parse input
        var input = JSON.parse(req.rawBody)[0];
        var id = input.deviceid;
        var latitude = input.latitude;
        var longitude = input.longitude;
        var url = input.url;
        var blobName = url.substr(url.lastIndexOf('/') + 1);
        var timestamp = input.timestamp;
    
        // Generate a SAS
        var azure = require('azure-storage');
        var blobService = azure.createBlobService(storageAccountName, storageAccountKey);
    
        var now = new Date();
        var expiry = new Date(now).setMinutes(now.getMinutes() + 3);
    
        var policy = {
            AccessPolicy: {
                Permissions: azure.BlobUtilities.SharedAccessPermissions.READ,
                Start: now,
                Expiry: expiry
            },
        };
    
        var sas = blobService.generateSharedAccessSignature('photos', blobName, policy);
    
        // Call the Custom Vision Service
        const options = {
            url: predictionUrl,
            method: 'POST',
            headers: {
                'Prediction-Key': predictionKey
            },
            body: {
                'Url': url + '?' + sas
            },
            json: true
        };
    
        var request = require('request');
    
        request(options, (err, res, body) => {
            if (err) {
                context.log(err);
                context.done();
            }
            else {
                var probability =  body.predictions.find(p => p.tagName.toLowerCase() === 'polar bear').probability;
                var isPolarBear = probability > 0.8; // 80% threshhold
    
                // Update the database
                var Connection = require('tedious').Connection;
                var Request = require('tedious').Request;
    
                var config =
                {
                    authentication:
                    {
                        type: 'default',
                        options:
                        {
                            userName: databaseUsername,
                            password: databasePassword
                        }
                    },
                    server: databaseServer,
                    options:
                    {
                        database: databaseName,
                        encrypt: true
                    }
                }
    
                var connection = new Connection(config);
    
                connection.on('connect', (err) => {
                    if (err) {
                        context.log(err)
                        context.done();
                    }
                    else {
                        var query = "INSERT INTO dbo.PolarBears (CameraID, Latitude, Longitude, URL, Timestamp, IsPolarBear) " +
                            "VALUES ('" + id + "', " + latitude + ", " + longitude + ", '" + url + "', '" + timestamp + "', " + (isPolarBear ? "1" : "0") + ")";
    
                        dbRequest = new Request(query, err => {
                            if (err) {
                                context.log(err);
                                context.done();
                            }
                        });
    
                        dbRequest.on('error', err => {
                            context.log(err);
                            context.done();
                        });
    
                        dbRequest.on('requestCompleted', () => {
                            context.done();
                        });
    
                        connection.execSql(dbRequest);
                    }
                });
            }
        });
    };
    
  6. [保存] をクリックして変更を保存します。

サービスのコードをカスタマイズする

コードでは、request パッケージを使って、作成した Custom Vision Service を呼び出し、分析する画像の URL を渡します。 結果を解析し、画像にシロクマが含まれる確率を示す値を取得します。

その後、tedious パッケージを使って、データベースにレコードを書き込みます。 そのレコードには、カメラの ID、カメラの緯度と経度、画像の URL、写真が撮影されたときを示すタイムスタンプ、および画像にシロクマが含まれるかどうかを示す IsPolarBar の値が含まれます。 画像にシロクマが含まれるかどうかを判定するしきい値は、このコード行では 80% になっています。

var isPolarBear = probability > 0.8; // 80% threshhold

このコードのもう 1 つの注目すべき側面は、Shared Access Signature (SAS) が使われていることです。 前に作成した photos コンテナーはプライベートです。 そこに格納されている BLOB にアクセスするには、ストレージ アカウントへのアクセス権または、ストレージ アカウントのアクセス キーを持っている必要があります。 Shared Access Signature を使用すると、匿名ユーザーでも個々の BLOB にアクセスできるようになりますが、指定された時間に限られ、必要に応じて読み取り専用アクセスにできます。

コードでは、Azure Storage SDK for Node.js を使って、Custom Vision Service に渡される BLOB に対する読み取り専用の SAS を生成し、クエリ文字列として BLOB の URL に追加しています。 SAS の有効期間は 3 分で、読み取りアクセスのみが許可されます。 これにより、コードでは、すべてのユーザーがダウンロードできるパブリック コンテナーに BLOB を置くことなく、分析のために Custom Vision Service にプライベート BLOB を送信できます。

作成した Azure リソースでこれを動作させるには、いくつかの変数を指定する必要があります。 関数エディターでコードを次のように編集します。

  1. 関数コードの次のプレースホルダーを、次の値に置き換えます。 その後、変更を保存します。

    • 2 行目の PREDICTION_URL を、Custom Vision Service からの予測 URL に置き換えます。
    • 3 行目の PREDICTION_KEY を、Custom Vision Service からの予測キーに置き換えます。
    • 4 行目の ACCOUNT_NAME を、ストレージ アカウントの名前に置き換えます。
    • 5 行目の ACCOUNT_KEY を、ストレージ アカウントのアクセス キーに置き換えます。
    • 6 行目の SERVER_NAME を、データベース サーバーに割り当てた名前に置き換えます。.database.windows.net サフィックスはそのままにします。
    • 7 行目の ADMIN_USERNAME を、指定したデータベース ユーザー名に置き換えます。
    • 8 行目の ADMIN_PASSWORD を、指定したデータベース パスワードに置き換えます。

接続をテストする

最後のステップとして、エンド ツー エンドの接続をテストしましょう。

  1. Azure portal で Stream Analytics ジョブに戻ります。

  2. 前と同じようにして、Stream Analytics ジョブを開始します。

  3. Stream Analytics ジョブが実行状態になったら、Azure Cloud Shell に切り替えて、プロジェクト フォルダーにいることを確認します。

  4. 次のコマンドで、カメラ配列の実行を開始します。

    node run.js
    
  5. カメラ配列と Stream Analytics ジョブを、3 から 5 分間実行させたままにします。 その後、Stream Analytics ジョブを停止し、run.js を停止します。

  6. Azure portal でデータベースに戻り、クエリ エディターを使って次のクエリを実行します。

    SELECT * FROM dbo.PolarBears
    
  7. 分析のために Custom Vision Service に送信された画像を表す行がいくつか、テーブルに含まれていることを確認します。 各行の IsPolarBear 列を見ます。 シロクマが含まれると分析された画像の数はいくつですか。

    Azure 関数によってデータベースに書き込まれた行

次に、Power BI でデータを視覚化し、さらに説得力があるグラフィカルなデータを作成します。