オンプレ系インフラエンジニアがAzureを勉強する

いつか誰かの何かの役に立つと嬉しいな

【JavaScript】サンプルボタン作成 - その3(React利用 - 関数コンポーネントver.)

はじめに

JavaScriptを勉強し始めました。
Reactのありがたみを知るために、簡単なサンプルボタンを下記パターンで書いて比較してみます。
それぞれの書き方の差を確認するために書いたので、実装としては良い書き方じゃないところもあります。

題材

下記のようなボタンをサンプルにします。
【要件】
OKボタンをクリックするとNGボタンに変わる
OKボタン、NGボタンともにマウスオーバーで色が変わる
ボタンにカーソルを合わせるとマウスカーソルが変化する
f:id:mitsunooon:20201020222502g:plain
 

共通部分(CSS)

CSSは一律共通にしています。

/* ボタンの基本にするスタイル */
/* OKの時 */
.sampleButton-ok {
    width: 128px;
    height: 40px;
    margin: 8px;
    background-color: #ff3535;
    border: solid 1px #ff3535;
    border-radius: 4px;
    color: #fff;
    font-size:12px;
    line-height: 40px;
    text-align: center;
    cursor: pointer;
}

/* OKの時のマウスオーバー */
.sampleButton-ok:hover {
    background-color: #da3232;
}

/* NGの時の基本スタイル(※OKボタンからの差分のみ) */
.sampleButton-ng {
    background-color: #fff;
    border: solid 1px #3553ff;
    color: #3553ff;
}

/* NGの時のマウスオーバー */
.sampleButton-ng:hover{
    background-color: #eee;
}

 

HTML/CSS/JavaScript-React利用有り-関数コンポーネント使用ver.

HTML/CSS/JavaScriptに加えてReactを使います。
Reactのコンポーネントにも関数コンポーネントとクラスコンポーネントがあるので、今回は関数コンポーネントを使います。
ja.reactjs.org

プロジェクトの作成

Reactのプロジェクトを作成するところは備忘録として記載します。
Reactを使うための初期構築についてはいろいろわかりやすい記事があるのでそちらをご覧ください。
[手順]

  • Visual Studio Codeを開く。
  • 新しいターミナルを開く。
  • npx create-react-app [プロジェクト名]でプロジェクトを新規作成。
  • cdでプロジェクトのディレクトリに移動。
  • yarn startで実行。

不要なファイルもたくさんあるため、必要に応じて削除します。

index.html

タイトルの部分を変更します。
不要な部分も削除します。(残しておいてもボタンの挙動には影響ないです。)

index.js

App.jsというメインになるページを呼び出しています。
ここで直接ボタンのコンポーネントを呼ぶこともできますが、複雑なUIを作っていくことを考えると、ここでボタンのような一機能のコンポーネントを直接呼び出すことはあんまりないかなと思います。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

App.js

ここでSampleButtonというボタンのコンポーネントを呼び出します。
クラスを適用するためのidの指定をしておきます。

import React from 'react';
import SampleButton from './SampleButton';

function App() {
  return (
    <div >
      <SampleButton id="samplebutton"/>
    </div>
  );
}

export default App;//他の関数から呼び出せるように

SampleButton.js

ボタンの本体部分になります。

import './SampleButton.css';
import React from 'react';

function SampleButton(props) {
  // クリックイベントで実行する処理
  function handleClick(e) {
    const samplebutton = document.getElementById(props.id);

    // sampleButton-ngのクラスが適用されているかで動作を分ける
    if (!samplebutton.classList.contains('sampleButton-ng')) {
      //クラスの追加
      samplebutton.classList.add('sampleButton-ng');
      //テキストの書き換え
      samplebutton.textContent = "NG";
    } else {
      //クラスの削除
      samplebutton.classList.remove('sampleButton-ng');
      //テキストの書き換え
      samplebutton.textContent = "OK";
    }
  }

  return (
    // idは上位から受け取ったものを利用する
    <div onClick={handleClick} className="sampleButton-ok" id={props.id}>
      OK
    </div >
  );
}

export default SampleButton;

ボタンを増やす場合

コンポーネント呼び出し時のidを分けるだけで同じボタンが量産できます。

App.js

import React from 'react';
import SampleButton from './SampleButton';

function App() {
  return (
    <div>
      <SampleButton id="samplebutton1"/>
      <SampleButton id="samplebutton2"/>
      <SampleButton id="samplebutton3"/>
    </div>
  );
}

export default App;//他の関数から呼び出せるように

SampleButton.js
変更なし。

コード全体

 コード全体は下記Githubにあげています。
github.com

おわりに

ボタンの使いまわしがぐっと楽になったような気がします。
関数コンポーネントでの値の保持はhooksを使う方法などもあるので、もう少し違う書き方ができそうです。

次はReactのクラスコンポーネントを使ってみます。
 

【JavaScript】サンプルボタン作成 - その2(クラス利用)

はじめに

JavaScriptを勉強し始めました。
Reactのありがたみを知るために、簡単なサンプルボタンを下記パターンで書いて比較してみます。
それぞれの書き方の差を確認するために書いたので、実装としては良い書き方じゃないところもあります。

題材

下記のようなボタンをサンプルにします。
【要件】
OKボタンをクリックするとNGボタンに変わる
OKボタン、NGボタンともにマウスオーバーで色が変わる
ボタンにカーソルを合わせるとマウスカーソルが変化する
f:id:mitsunooon:20201020222502g:plain
 

共通部分(CSS)

CSSは一律共通にしています。

/* ボタンの基本にするスタイル */
/* OKの時 */
.sampleButton-ok {
    width: 128px;
    height: 40px;
    margin: 8px;
    background-color: #ff3535;
    border: solid 1px #ff3535;
    border-radius: 4px;
    color: #fff;
    font-size:12px;
    line-height: 40px;
    text-align: center;
    cursor: pointer;
}

/* OKの時のマウスオーバー */
.sampleButton-ok:hover {
    background-color: #da3232;
}

/* NGの時の基本スタイル(※OKボタンからの差分のみ) */
.sampleButton-ng {
    background-color: #fff;
    border: solid 1px #3553ff;
    color: #3553ff;
}

/* NGの時のマウスオーバー */
.sampleButton-ng:hover{
    background-color: #eee;
}

 

HTML/CSS/JavaScript-クラス利用有り

使っているのはHTML/CSS/JavaScriptだけですが、クラスの形にしました。
クラスの形にすると同じボタンの量産が少し楽になります。
developer.mozilla.org

index.html

ボタンをクリックしたときの挙動についてはここでは書かず、後からindex.jsを読み込むようにします。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./index.css">
    <title>SampleButtonテスト</title>
</head>
<body>
    <!-- Sampleボタン -->
    <div id="samplebutton" class="sampleButton-ok">OK</div>
    <script type="text/javascript" src="./index.js"></script>
</body>
</html>

index.js

クリックしたときの具体的な挙動はindex.jsに書いてます。
OKNGの切り替え基準は同じです。

class SampleButton {
    // コンストラクター
    constructor(_button) {
        // ボタンのクリックイベントを設定する
        _button.addEventListener('click', this.click);
    }

    // クリックイベントで実行する処理
    click() {
        // NGボタンのCSSが適用されているかを判断する
        if (!this.classList.contains('sampleButton-ng')) {
            //クラスの追加
            this.classList.add('sampleButton-ng');
            //テキストの書き換え
            this.textContent = "NG";
        } else {
            //クラスの削除
            this.classList.remove('sampleButton-ng');
            //テキストの書き換え
            this.textContent = "OK";
        }
    }
}
// ボタンインスタンスを作成する。
//指定したidにSampleButtonを適用している
let samplebutton = new SampleButton(document.getElementById('samplebutton'));

ボタンを増やす場合

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./index.css">
    <title>SampleButtonテスト</title>
</head>
<body>
    <!-- Sampleボタン -->
    <div id="samplebutton" class="sampleButton-ok">OK</div>
    <div id="samplebutton2" class="sampleButton-ok">OK</div>
    <div id="samplebutton3" class="sampleButton-ok">OK</div>
    <div id="samplebutton4" class="sampleButton-ok">OK</div>
    <div id="samplebutton5" class="sampleButton-ok">OK</div>
    <script type="text/javascript" src="./index.js"></script>
</body>
</html>

index.js

class SampleButton {
    // コンストラクター
    constructor(_button) {
        // ボタンのクリックイベントを設定する
        _button.addEventListener('click', this.click);
    }

    // クリックイベントで実行する処理
    click() {
        // NGボタンのCSSが適用されているかを判断する
        if (!this.classList.contains('sampleButton-ng')) {
            //クラスの追加
            this.classList.add('sampleButton-ng');
            //テキストの書き換え
            this.textContent = "NG";
        } else {
            //クラスの削除
            this.classList.remove('sampleButton-ng');
            //テキストの書き換え
            this.textContent = "OK";
        }
    }
}

// ボタンインスタンスを作成する。
//指定したidにSampleButtonを適用している
let samplebutton = new SampleButton(document.getElementById('samplebutton'));
let samplebutton2 = new SampleButton(document.getElementById('samplebutton2'));
let samplebutton3 = new SampleButton(document.getElementById('samplebutton3'));
let samplebutton4 = new SampleButton(document.getElementById('samplebutton4'));
let samplebutton5 = new SampleButton(document.getElementById('samplebutton5'));

コード全体

 コード全体は下記Githubにあげています。
github.com

おわりに

次はReactを使ってみます。
 

【JavaScript】サンプルボタン作成 - その1

はじめに

JavaScriptを勉強し始めました。
Reactのありがたみを知るために、簡単なサンプルボタンを下記パターンで書いて比較してみます。
それぞれの書き方の差を確認するために書いたので、実装としては良い書き方じゃないところもあります。

題材

下記のようなボタンをサンプルにします。
【要件】
OKボタンをクリックするとNGボタンに変わる
OKボタン、NGボタンともにマウスオーバーで色が変わる
ボタンにカーソルを合わせるとマウスカーソルが変化する
f:id:mitsunooon:20201020222502g:plain
 

共通部分(CSS)

CSSは一律共通にしています。

/* ボタンの基本にするスタイル */
/* OKの時 */
.sampleButton-ok {
    width: 128px;
    height: 40px;
    margin: 8px;
    background-color: #ff3535;
    border: solid 1px #ff3535;
    border-radius: 4px;
    color: #fff;
    font-size:12px;
    line-height: 40px;
    text-align: center;
    cursor: pointer;
}

/* OKの時のマウスオーバー */
.sampleButton-ok:hover {
    background-color: #da3232;
}

/* NGの時の基本スタイル(※OKボタンからの差分のみ) */
.sampleButton-ng {
    background-color: #fff;
    border: solid 1px #3553ff;
    color: #3553ff;
}

/* NGの時のマウスオーバー */
.sampleButton-ng:hover{
    background-color: #eee;
}

 

HTML/CSS/JavaScript-クラス利用無し

何のひねりもなく、HTML/CSS/JavaScriptだけを使い、素直に書いた場合です。

index.html

 index.htmlでボタンをクリックしたときにJavaScriptの関数を呼び出すようにしています。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./index.css">
    <title>SampleButtonテスト</title>
</head>
<body>
    <script type="text/javascript" src="./index.js"></script>
    <!-- Sampleボタン -->
    <div id="samplebutton" class="sampleButton-ok" onclick="OnSampleButtonClick()">OK</div>
</body>
</html>

index.js

クリックしたときの具体的な挙動はindex.jsに書いてます。
CSSの適用状態で場合分けしてます。

//SampleButton
function OnSampleButtonClick() {
    const samplebutton = document.getElementById("samplebutton");

    // sampleButton-ngクラスが適用されているか確認
    if (!samplebutton.classList.contains('sampleButton-ng')) {
        //クラスの追加
        samplebutton.classList.add('sampleButton-ng');
        //テキストの書き換え
        samplebutton.textContent = "NG";
    } else {
        //クラスの削除
        samplebutton.classList.remove('sampleButton-ng');
        samplebutton.textContent = "OK";
    }
}

コード全体

 コード全体は下記Githubにあげています。
github.com

おわりに

次は上記をクラス利用した形に書き直してみます。
 

Bot Framework Composerで作る簡単FAQチャットボット

はじめに

Bot Framework Composer がv1.1.1になり、QnA Makerのナレッジベース作成がComposer上でできるようになりました。
これにより簡単なFAQチャットボットであればかなり素早く簡単にできるのではないかと思いますので、使い勝手を見ながらFAQチャットボットの作り方を書いていきます。


Bot Framework Composerとは

Bot FrameworkをベースとしたチャットボットがGUIで作れるツールです。
ノンコーディング、ローコーディングでチャットボットが作れます。
LUISやQnA Maker、Power Virtual Agentsとの連携ができます。

Introduction to Bot Framework Composer - Bot Composer | Microsoft Docs


作るもの

Azureの料金に関するFAQチャットボット

Azure の料金に関する FAQ | Microsoft Azure


使うもの

  • Azure上のQnA Makerリソース
  • Bot Framework Composer v1.1.1


参考

Create a QnA Maker knowledge base in Composer - Bot Composer | Microsoft Docs

 

手順

AzureにQnA Makerのリソースを作成する

Azureに[QnA Maker]というリソースを作成します。

価格レベルについて、Freeレベルで発行できるナレッジベースは2つまでのようです。

Azure リソース - QnA Maker - Azure Cognitive Services | Microsoft Docs

f:id:mitsunooon:20201014000305j:plain

f:id:mitsunooon:20201014000928p:plain

 

リソースの作成が完了したら、キーを取得します。
作成したQnA Makerのリソース内の[キーとエンドポイント]から、[キー 1][キー 2]のいずれかをコピーして控えておきます。
Bot Framework Composerからボットを作成するときに必要になります。

f:id:mitsunooon:20201014001048p:plain

 

Bot Framework Composerでボットを作成する

Bot Framework Composer(v1.1.1)を下記よりインストールします。

Release 1.1.1 · microsoft/BotFramework-Composer · GitHub

 

Bot Framework Composerを起動します。
起動したら、ホーム画面で[New]をクリックします。

f:id:mitsunooon:20201014001237j:plain


[Create from knowledge base(QnA Maker)]を選択し、[Next]をクリックします。

f:id:mitsunooon:20201014001302j:plain

 

[Name]にプロジェクトの名前を入力します。
チャットボットの名前になるものではないです。

プロジェクトの保存場所も指定します。

f:id:mitsunooon:20201014001343j:plain

 

[URL]に参考にするFAQサイトのURLを入力します。
今回はAzureの料金に関するFAQのサイトを使います。
[Create knowledge base]をクリックします。

f:id:mitsunooon:20201014001404j:plain


ナレッジベースの作成が終わるとQnAの項目に入力したURLから読み取った質問と回答のペアが一覧で表示されます。
ここでさらにナレッジベースを追加することや質問回答のペアを追加することもできます。
マルチターンの設定はまだQnA Makerウェブサイトのようにはできないのかなと思います。

f:id:mitsunooon:20201014001449j:plain


せっかくv1.1.1から多言語対応になったので日本語に設定します。
左下の歯車マークをクリックします。
[Bot Settings]>[Create copy to translate bot content]をクリックします。

f:id:mitsunooon:20201014001722j:plain

 

[Japanese(Japan)]を選択します。
下の部分にもチェックを入れておきます。
[Done]をクリックします。

f:id:mitsunooon:20201014001751j:plain

 

[Bot language (active)]と[Default language]を[Japanese(Japan)]に変更します。

f:id:mitsunooon:20201014001814j:plain


ボットの作成をします。
右上の[Start Bot]をクリックします。
[QNA Subscription key]にAzureのQnA Makerリソース作成時に控えておいたキーを入力します。

f:id:mitsunooon:20201014001935p:plain

 

作成が完了すると、[Bot Framework Emulator]で動作確認ができます。

f:id:mitsunooon:20201014002024j:plain


TeamsやLINEなどのチャットボットとして使う場合には、今まで同様の方法でAzureにリソースを作成します。
過去記事などご参考いただければと思います。

Bot Framework ComposerでLINEボットを作ったときにつまづいたところ - オンプレ系インフラエンジニアがAzureを勉強する

 

注意

[Start Bot]をした後にQnA Makerのホームを見に行くと、こちらにもナレッジベースが作成されています。

f:id:mitsunooon:20201014002250p:plain


しかし、ここでナレッジベースを編集しても、Bot Framework Composer側に反映されません。
反映されるどころか、[Start Bot]をするとBot Framework Composer上のナレッジベースがQnA Makerウェブサイトに上書き反映されるので、QnA Makerウェブサイト上で編集した内容は消えます。
このあたりのやりとりやナレッジベースを増やす場合についてはまた改めて試行錯誤したいと思います。

 

おわりに

QnA MakerとBot Framework Composerを行き来しながらしていた諸々の設定がBot Framework Composerだけで済むようになった、
手で設定してたappsettings.jsonの設定が不要になった、
あたりが個人的に便利になったなと感じたところでした。
マルチターンの作成やQnA MakerウェブサイトのKBとの連携がもう少しよくなるといいなと思いました。

 

Bot Framework Composer v1.1.1 キャッチアップ

はじめに

2020/09/22~2020/09/25の期間でMicrosoft Ignite 2020という大きなカンファレンスがオンラインで開催されました。

今ならまだオンデマンドでセッションが見れますので、ぜひ気になる技術のセッションを見てみてください。

myignite.microsoft.com


さらに近いタイミング(2020/08)でBot Framework Composer v1.1.1がリリースされましたので、アップデート内容を自分なりにキャッチアップしていきます。(2020/10/07時点)
公式ドキュメントによると大きなトピックは6つあるようなので、それに沿っていきます。

What's new - Bot Framework Composer | Microsoft Docs

リリースノート

BotFramework-Composer/1.1.1.md at main · microsoft/BotFramework-Composer · GitHub

 

QnA Makerのナレッジベース作成

QnA Makerのナレッジベース作成がBot Framework Composer上でできるようになりました!
個人的には一番アツいアップデートです。
機能的にも本家のQnA Makerと比べて遜色ないです。
このあたりについては改めて別記事にします。

f:id:mitsunooon:20201007221230j:plain

 

多言語対応

多言語対応になりました。
今までは基本は英語だけでした。
今までも単純な入出力やQnA Makerとの接続では日本語が使えていましたが、今回はLUISの日本語ロケール対応というのが大きそうです。
詳細は下記のサイト様や動画をご覧いただくのが良いと思います。

Bot Framework Composer v1.1.1 で日本語ロケール (ja-jp) を選択できるようになりました #BotFramework – 技術との戯れ

https://youtu.be/WQnEpVyayAw

 

欲を言えば、Bot Framework Composer自体の表示言語も多言語対応にしてほしかったです…

 

JavaScriptのランタイムコード

プレビュー版ですが、JavaScriptのランタイムコードが取得可能になりました。
今まではC#だけでした。

ランタイムコードを使うとカスタムアクションを使うことができます。
サンプルも公開されていますので是非参考までに。(サンプルはまだC#だけかもです…

BotFramework-Composer/runtime/dotnet/customaction at main · microsoft/BotFramework-Composer · GitHub

 

スキルマニフェストの生成が詳細に

スキルマニフェストの生成自体は前のバージョンからできていましたが、より詳細な項目をユーザーで指定できるようになりました。
例)マニフェスト名、エンドポイント、ダイアログ、トリガーなど

 

【補足】
スキルとは?
別のボットに対して一連のタスクが実行できるボットのことです。
Microsoftが公開してるスキルのサンプル例では、カレンダースキルやToDoスキルなどがあります。

What is a Bot Framework Skill?

 

スキルマニフェストとは?
スキルが実行できるアクション、入力パラメーターと出力パラメーター、およびスキルのエンドポイントを記述するJSONファイルのことです。

 

f:id:mitsunooon:20201007221724j:plain

f:id:mitsunooon:20201007221736j:plain

 

Azure プラットフォームとのより深い統合

これは大きなアップデートというより、
AzureにPublishするときに起きてたバグを修正したというような印象です。


Power Virtual Agentsとの連携

Power PlatformのひとつであるPower Virtual Agentsと連携できるようになりました!
これも個人的に激熱なアップデートです。
Power Virtual Agentsで作ったbotBot Framework Composerで開いて編集できますし、その逆もできます。
個人的な想定としては、基本的な応答はPVAで作って、細かい変数とかを設定したいときはBot Framework Composerを使う感じかな~と考えています。

f:id:mitsunooon:20201007221630j:plain

f:id:mitsunooon:20201007221647j:plain

 

その他の改善点

少しずつUIの項目とかが変わっていて、使いやすさが上がってるような気もします(気のせいかもしれない


おわりに

今回初めて1つの製品のアップデート内容をがっつり確認しました。
リリースノートをまじまじと読んでみると、一般のユーザーからのIssueに応えて改善されているものも多くてちょっと感動しました。
Bot Framework Composerに限らず、Cognitive ServiceでもPower Platform系でも面白いアップデートがたくさんあったので、いろいろ試してみたいと思います。

 

 

 

 

【C#】指定したフォルダ配下でtxtファイルを探す方法

はじめに

新人さんのプログラミング指導で、課題を出してもただサンプルをコピペしてくるだけということはよくあると思います。

その段階から一歩先へはどう教育したらいいのかという問題をちらほら耳にするので、

自分がしてもらった教育の中でよかったと思う題材について書きます。

 

題材

下記の動作をするコンソールアプリの作成。

指定したフォルダの中にtxtファイルがあるかを確認する。

サブディレクトリがある場合は最下層まで確認する。

txtファイルが存在する場合はそのファイル名と作成日をJSON形式で出力する。

【実行結果イメージ】

f:id:mitsunooon:20200930012604p:plain

 

1回目に作ったもの

SearchOption.AllDirectoriesを使用する方法。

SearchOption.AllDirectoriesを利用すれば、現在のディレクトリとすべてのサブディレクトリを検索してくれます。

SearchOption 列挙型 (System.IO) | Microsoft Docs

これを使うとコードもシンプルで簡単になります。

 

抜粋

// SearchOption.AllDirectoriesを利用する方法
string[] names = Directory.GetFiles(args[0], "*.txt", SearchOption.AllDirectories);
if (names[0] != null)
{
foreach (string subnames in names)
{
var date = new FileInfo(subnames); // JSON化前にリスト化する
List<Items> texts = new List<Items>
{
new Items{ file = subnames , create_time = date.CreationTime.ToString()}
}; //JSON化
string json = JsonSerializer.Serialize(texts); //コンソール出力
Console.WriteLine(json);
}
}

全文

github.com

 

上記に対してもらったフィードバック

「上記の課題の成果からわかるのは、私が目的(やりたいこと)のための調べ物ができるということ。調べた内容を実装することができるということ。

ここまでではプログラミングとしての能力が鍛えられているとは言えないです。

できるだけ、SearchOptionのようなものは使わないで、ロジックを考えてください。」

 

これは私自身ずっと疑問だった指摘でした。

わからないことがあっても、大抵のことは調べればすぐに答えが得られます。

業務上はそれで十分な場合も多いと思います。

しかし、コードをコピペして使えるようにすることが「プログラミングをしている」ということなのか、このままの勉強方法で果たして業務で実装できるプログラマーになれるのか。

 

2回目に作ったもの

SearchOption.AllDirectoriesを使用しない方法。

いわゆる再帰処理です。

私はSearchOption.AllDirectoriesを使用しない方法を考え始めてから、再帰処理という言葉に行きつくまで時間がかかりました。

初めは探索アルゴリズムの考え方を調べたりしてました。

いまいちしっくりこなくて、求める挙動を整理するところから思考しました。

ひとつ目のディレクトリの中を検索し、txtファイル探す。

ディレクトリがあれば、その中に入りまたtxtファイルを探す…

ここで繰り返しが使えそうだ、その繰り返しはまとめられそうだ、回数の制限がわからないからどうしようか…みたいな考えを文字に起こしたり図に描いてみたりしました。

試行錯誤を繰り返した結果、再帰という言葉にたどり着きました。

 

抜粋

static void scan(string dir)
        {
            string[] subdirs = Directory.GetFiles(dir, "*.txt");//dirの中のtxtを探す
            try
            {
                foreach (string files in subdirs)
                {
                    //作成日を取得するための処理
                    var date = new FileInfo(files);

                    //取得した情報をAddItemに渡す
                    Texts txts = new Texts();
                    txts.AddItem(files, date.CreationTime.ToString());

                    // JSON化前にリスト化する
                    List texts = new List
                        {
                            new Items{ file = files , create_time = date.CreationTime.ToString()}
                        };

                    //JSON化
                    string json = JsonSerializer.Serialize(texts);
                    Console.WriteLine(json);

                }

                //ファイルを探し終わったらディレクトリを探して、filesに入れる。はじめのscan(args[0]);と同じ処理ルートになる
                foreach (string files in Directory.GetDirectories(dir))
                {
                    scan(files);
                }
            }
            catch
            {
                Console.WriteLine("探索中にエラーが発生しました。");
            }
        }

 

 全体

github.com

 

2回目に作ったものも、結果的には調べたことの実装に変わりないのですが、

コピペコーディングから脱する考え方の一端がつかめたような気がしました。

 

おわりに

ありがたいことに、世の中には便利な技術や情報が溢れています。

車輪の再発明のようなことは業務では求められないと思いますが、自分が納得して学習していく上では必要なことでもあるのかなと思います。

世の情報の恩恵にあずかりながらも、ちゃんと自分で思考する力を付けて、人並みのコーディング、プログラミングができるようになりたいです。

 

画像内の特定の文字をぼかすLogic Appsを作る

はじめに

ブログや登壇資料で毎回Azureのアカウント情報やサブスク情報をマスクするのが結構気を遣う作業で大変だな~画像投げたら自動で大事なところをマスクしてくれるbot作れないかな~と思ったので、挑戦してみました。
しかし、現時点では完璧に情報を認識してマスクするというところまでは至っておらず…
何ができてなくて、何をできるようにしないといけないのか整理するために今できているところまでを書き起こします。

 

 

参考サイト様

ASCII.jp:文字入り画像を送るとテキストに書き起こすLINEボットを作ろう (1/3)

ASCII.jp:人物写真の顔をAIが検出し、ぼかしてツイートするLogic Appsを作ろう (1/3)

メモ:Cloudmersive Image コネクターを利用して、画像の顔に自動でぼかしを入れる - MoreBeerMorePower

 

作りたいもの

Azure Portalの画面キャプチャを送ると、アカウント情報などにぼかし処理を施して画像を返信してくれるBot

 

今できていること

  • LINE BotにAzure Portalのキャプチャを送る。
  • Azure Portalの右上のアカウント情報をぼかす。
  • ぼかし処理をした画像をDropboxの指定のフォルダに格納する。

 

今できていないこと

  • 画像のサイズや範囲に寄らず、指定した情報を画像内で特定してぼかし処理ができる。
  • ぼかし処理した画像をbotの返信として送信できる。
  • botの媒体をLINEだけでなく他のツールでも使えるようにする。

 

Azure以外の事前準備

  • LINE Messaging APIの利用登録
  • Cloudmersiveの利用登録
  • Dropboxの利用登録

このあたりについては参考サイト様に丁寧な説明があります。

 

作成手順

全体図

f:id:mitsunooon:20200919154125j:plain

 

トリガーの作成

参考サイト様そのままです。
トリガーを[HTTP要求の受信時]にし、LINE botに画像を送信することをトリガーとします。

f:id:mitsunooon:20200919154504p:plain

 

透明画像の準備

事前にDropboxに1200×1200サイズの透明画像を格納しておきます。

透明画像はiPhoneアプリのibisPaintで用意しました。無料で使えますし、サイズ指定もわかりやすいです。

透明画像のサイズは大きければ大きいほうがいいだろうと思い、初めは無駄に大きいものにして失敗したので、ひとまず1200×1200で統一しておいたほうが無難そうです。

f:id:mitsunooon:20200919154555p:plain

 

コンテンツの取得

以降、[For each]内での処理になります。

LINEで送られてきた画像情報を取得します。

URI:concat('https://api.line.me/v2/bot/message/',items('For_each')?['message']?['id'],'/content')

f:id:mitsunooon:20200919154737p:plain

 

ぼかし処理をするための情報収集

ぼかし処理をするためには、ぼかしたい対象の範囲を座標で指定する必要があります。

アカウント情報の文字列の座標を取得するために、[Computer Vision API]の[Optical Character Recognition (OCR) to JSON]を使います。

f:id:mitsunooon:20200919155105j:plain

f:id:mitsunooon:20200919173826j:plain

 

このアクションを使うと、画像から認識した文字ごと位置エリアごとにJSONグループ、階層に分けてくれます。

[boundingBox]で対象の座標がわかります。今回はメールアドレスの部分の座標を使います。

座標の意味はこんな感じだと思います。

"boundingBox": "左上端のX座標, 左上端のY座標, 幅, 高さ"

f:id:mitsunooon:20200919155129p:plain

 

似たようなアクションで、[Computer Vision API]の[Optical Character Recognition (OCR) to Text]というものがあります。

これは認識した文字をテキストにしてくれます。

単純な画像の文字起こしならこの機能を使えば十分そうです。(やや日本語が怪しそうなところはありますが…

f:id:mitsunooon:20200919155828p:plain

 

ぼかしたい情報が画像内に入っているか判定する

今回はメールアドレスが入っていればぼかし処理をするようにしたいので、[Optical Character Recognition (OCR) to Text]で取得したテキスト内にメールアドレスのドメインがあるかを確認します。

f:id:mitsunooon:20200919155953j:plain

 

ぼかす範囲を切り抜くための座標指定

アクション名:Crop an image to a rectangular area

ぼかす範囲を切り抜くために座標指定します。

この座標は[Optical Character Recognition (OCR) to JSON]で取得したものを使います。アイコンの部分までぼかしにいれたいので、横幅を少し大きくしました。

今回は完全に座標を固定で入れていますが、将来的にはここを変数化したいです。

f:id:mitsunooon:20200919160257j:plain

 

対象範囲のぼかし処理

アクション名:Perform a guassian blur on the input image

ぼかし具合を設定します。

ぼかす大きさとかにじみ具合とか指定できます。参考サイト様のこの比率が滑らかでちょうどいいなと思っています。

f:id:mitsunooon:20200919160411j:plain

 

透明画像と合成する

アクション名:Composite two images together

ぼかし処理済みの画像と透明画像を合成します。

f:id:mitsunooon:20200919160555j:plain

 

合成するとこんな感じになります。

右側の真ん中に載るように指定しました。

f:id:mitsunooon:20200919160608j:plain

 

元画像と合成するための切り抜き

アクション名:Crop an image to a rectangular area

上記のぼかし処理済みの画像と元画像を合成したときに、対象位置がちゃんと重なるように切り抜いて調整する必要があります。

重ねる位置が極端に右上端であることと切り抜き範囲を座標で固定していることから、この辺りは参考サイト様そのままではなく少し調整しました。

(考え方ややってることはそんなに変わらないはず…

sub(div(variables('ciwidth'),2),div(158,2))
sub(div(variables('cihight'),2),div(25,2))
div(add(variables('ciwidth'),add(1650,1808)),2)
div(add(variables('cihight'),add(6,19)),2)

f:id:mitsunooon:20200919160815j:plain

 

切り抜いた結果がこんな感じです。

f:id:mitsunooon:20200919173138j:plain

 

元画像と合成する

アクション名:Composite two images together

元画像に先ほど切り抜いた画像を合成します。

結果、こんな感じの画像ができます。

狙い通りの位置がぼかされています。

f:id:mitsunooon:20200919161817p:plain

 

ぼかし処理済みの画像をDropboxに保存する

完成した画像はDropboxに保存します。

フロー上のCloudmersiveのアクション名が英語で分かりづらいので、画像処理の合間合間にこの処理(ファイルの作成)を挟むと、途中経過の画像が保存されて段階がわかりやすいです。

f:id:mitsunooon:20200919162006p:plain

 

おまけ:文字起こしした内容を返す

画像から文字起こしした内容をbotに変えさせたい場合は、任意の場所にこのアクションを入れるとできます。[Optical Character Recognition (OCR) to Text]で取得した内容を返すような形です。

f:id:mitsunooon:20200919162128p:plain

 

おわりに

「今できていないこと」にも記載したように、現状のフローだと、対象箇所をぼかすためにはいろいろと制限があります。(元の画像にブラウザのタブが入っていてはだめとか…

もう少し利便性があげられるように改修していきます。

正直、コードで書いてしまえば早いような気もしますが、Logic Appsを使いこなすために始めた題材なので、できるところまで粘ってみます。