Vue.jsでそれっぽい何かを作る2(改造)

株式会社イメージ・マジックの技術ブログ、今回の担当のsoenoです。 実務でいろいろ触ってはいるのですが。投稿しやすいコンテンツとなるとどうにも…。 というわけで以前の投稿に乗っかろうと思います。


何をするか

以前の投稿で作成したVueの機能を流用し、改造、改善していきます。

何を変えるか考える

前の機能の印象から変更点を考える

  1. webなのに白黒ってどうなのだろう。
    • 色を増やす
    • 色を増やせるように色の選択機能を追加 
  2. 見本と入力エリアが同じ大きさってどうなのだろう。(比較はしやすい?)
    • サイズを変える
  3. 升目少ない。
    • 増やす。
  4. どこ操作していいかわからない。
    • 入力エリアに枠を付けて目立たせる
  5. そろったかどうかどう確認するかわかりにくい(今は自動で勝手にチェックする)
    • チェックボタンを押すまでは比較しない
  6. 大人よりは子供向けのゲームになるだろうな。
    • ひらがなにする。

結果

できました!
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>vue</title>
    <style type="text/css">
      .wrapStyle{
          margin: 0 50px;
          display: block;
          position: relative;
          float: left;
      }
      .celAreaWrap{
        border: solid 2px #414141;
      }
      .celWrap{
          display: inline-block;
          box-sizing: content-box;
          margin:0;
          display: inline-block;
      }
      .cel{
          border:solid thin #eeeeee; 
          box-sizing: border-box;
          display: inline-block;
          margin:0;
          padding:0;
      }
      .colorCel{
        border: solid 2px #666666;
      }
      .btn{
        margin-top: 50px;
        border: solid 2px #666666;
        padding: 15px;
        min-width: 121px;
        text-align: center;
        background: #ffa500;
      }
      .cel_clr0{
          background-color:#ffffff;
      }
      .cel_clr1{
          background-color:#111111;
      }
      .cel_clr2{
          background-color:#ff0000;
      }
      .cel_clr3{
          background-color:#fc5000;
      }
      .cel_clr4{
          background-color:#f8ab04;
      }
      .cel_clr5{
          background-color:#48ff00;
      }
      .cel_clr6{
          background-color:#09f7ff;
      }
      .cel_clr7{
          background-color:#002fff;
      }
      .cel_clr8{
          background-color:#a304ff;
      }
    </style>
</head>
<body>
<div id="app">
  <div class="wrapStyle" v-bind:style="mihonBoxStyle">
    <p>みほん</p>
    <div v-for="(items, yoko) in resultList" v-bind:style="mihonCelWrap">
      <div v-for="(item, tate) in items" 
            v-bind:style="mihonCelStyle" class="cel" 
            v-bind:class="'cel_clr' + resultList[yoko][tate]">
      </div> 
    </div>
  </div>
  <div class="wrapStyle" v-bind:style="boxStyle">
    <p>みほんをまねしてね</p>
    <div class="celAreaWrap">
      <div v-for="(items, yoko) in userList" v-bind:style="celWrap">
        <div v-for="(item, tate) in items"
            v-on:click="changeCel(userList[yoko][tate])" :data-yoko="yoko" :data-tate="tate"
            v-bind:style="celStyle" class="cel"
            v-bind:class="'cel_clr' + userList[yoko][tate]">
        </div>
      </div>
    </div>
    <div>
      <p>いろをえらぼう</p>
      <div v-bind:style="celWrap">
        <div v-for="id in colorIdList" v-on:click="changeColor(id)" v-bind:style="celStyle" class="cel colorCel" v-bind:class="'cel_clr' + id">
        </div>
      </div>
    </div>
  </div>
  <div class="wrapStyle">
      <div v-on:click="check()" class="btn">そろったかな?</div>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<script language="javascript">
new Vue({
  el: '#app',
  data: {
    cellSize:60,
    nowColor:1,
    resultList: [
      [0,0,1,1,1,1,1,0,0],
      [0,1,4,4,4,4,4,1,0],
      [1,4,4,1,4,1,4,4,1],
      [1,4,4,4,4,4,4,4,1],
      [1,4,1,4,4,4,1,4,1],
      [1,4,1,4,4,4,1,4,1],
      [1,4,4,1,1,1,4,4,1],
      [0,1,4,4,4,4,4,1,0],
      [0,0,1,1,1,1,1,0,0],
    ],
    userList: [
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
    ],
    
    colorIdList:[0,1,2,3,4,5,6,7,8],
    
    mihonBoxStyle:{
      width:0,
    },
    mihonCelWrap:{
      height:0,
    },
    mihonCelStyle:{
      width:0,
      height:0,
    },

    boxStyle:{
      width:0,
    },
    celWrap:{
      height:0,
    },
    celStyle:{
      width:0,
      height:0,
    },
  },
  created() {
    this.mihonCelStyle.width = this.cellSize/3+ 'px';
    this.mihonCelStyle.height = this.cellSize/3+ 'px';
    this.mihonCelWrap.height = this.cellSize/3+ 'px';
    this.mihonBoxStyle.width = this.mihonCelWrap * this.resultList[0].length + 'px';

    this.celStyle.width = this.cellSize+ 'px';
    this.celStyle.height = this.cellSize+ 'px';
    this.celWrap.height = this.cellSize+ 'px';
    this.boxStyle.width = this.cellSize * this.resultList[0].length + 4 + 'px';//4はボーダー
  },
  
  methods: {
    changeColor(id) {
      this.nowColor = id;
    },
    changeCel() {
      const yoko = parseInt(event.currentTarget.dataset.yoko);
      const tate = parseInt(event.currentTarget.dataset.tate);
      Vue.set(this.userList[yoko], tate, this.nowColor);
    },
    //確認ボタンが押されたら揃ったか確認する。
    check() {
      for(let i=0,len = this.resultList.length; i<len; i++){
        for(let ii=0,len2 = this.resultList[i].length; ii<len2; ii++){
          if(this.userList[i][ii] !== this.resultList[i][ii]){
            //不一致があったら処理を抜ける
            alert("まだそろってません。");
            return;
          }
        }
      }
      setTimeout(function () {
        alert("そろいました。おめでとうございます!");
      }, 100);
    },
  },
})
</script>
</body>
</html>

キーマクロを作ってみた話

こんにちは。イメージマジック三浦です。昼食のために外に出たある日、風から感じる香りが変わったなと感じます。もうすぐ春ですね。
今回はnginxのログを解析するために、サクラエディタのキーマクロを作った話です。

キーマクロを作った経緯

システムのパフォーマンスが悪い時期があり、ボトルネック調査の一環でnginxのログを解析していました。 解析時はExcelやGoogleスプレッドシートでフィルタリングできるように、テキスト置換を繰り返してきましたが、次第にパターンが決まってきて、置換自体を手動リプレイできる状態になってきました。そしてはたと気づきました。
 

ここまでパターン化できているならキーマクロで自動化しよう!

キーマクロの記録方法

ここで、キーマクロの記録について触れておきます。
キーマクロは「ツール」>「キーマクロの記録開始」とクリックすることで、記録開始できます。タイトルバーに「キーマクロの記録中」と出てきます。
上の状態で「ツール」>「キーマクロの記録終了」とクリックすると、キーマクロ記録を終了できます。

簡単なキーマクロを作ってみる

まずは簡単なキーマクロを作ってみます。

(1,2,3,4,5)
を正規表現置換で
(1)
(2)
(3)
(4)
(5)
のようにする操作をキーマクロで保存してみます。 正規表現は以下の通りです。
「ツール」>「キーマクロの読込」とすると、以下のファイルが出てきます。
このパス内にある「RecKey.mac」が、先ほど記録したキーマクロの実体ファイルです。このファイルをデスクトップなどにコピーします(元の場所におくと次回のキーマクロ記録で消えてしまうため)。

キーマクロ機能拡張

先のキーマクロの中身はこのようになっています。
S_ReplaceAll(',', '\\)\\r\\n\\(', 1068);  // すべて置換
S_ReDraw(0);    // 再描画
Excelできれいに貼り付けられるようにしたいという目的を踏まえ、キーマクロを拡張していきます。今回は以下のような方針で拡張しました。
  • S_ReplaceAll のパターンを増やして、異なる方法での置換を実行する
  • S_ReDrow(0)を最後に呼び出すことで、画面再描画は最小限にする
  • 1行をタブで区切られた時の要素数が均一かつ、Excelに貼り付けた時に情報がきれいに出る。
拡張したキーマクロの一部を紹介します。
// HTTPメソッドの分割
S_ReplaceAll('\"(GET|POST|HEAD) ', '\t$1\t', 1068);
// HTTP Status の分割
S_ReplaceAll(' ([2-5][0-9][0-9]) ', '\t$1\t', 1068);
// 再描画
S_ReDraw(0);
拡張したキーマクロは、共通設定の「マクロ」タブで登録し、メニューから実行できるようにします。そこから先は置換結果やExcelに貼りつけた結果を確認しながら、正規表現を調整していきます。

仕上げ

Excelへの貼り付けを繰り返すうちに、以下の点も入れたいという気持ちが出てきたので、拡張中のキーマクロに組み込みます。
  • 先頭に項目行を入れたい
  • 置換後は保存しておいて開いたらすぐに貼り付けられるようにしたい
コード自体はキーマクロの記録で生成されたものをコピペします。
仕上げまで実施し、最終的には以下の通りになりました。
// ファイルの先頭に移動
S_GoFileTop(0);
// 項目行を追加
S_InsText(′項目1\t項目2\…');

// HTTPメソッドの分割
S_ReplaceAll('\"(GET|POST|HEAD) ', '\t$1\t', 1068);
// HTTP Status の分割
S_ReplaceAll(' ([2-5][0-9][0-9]) ', '\t$1\t', 1068);
(他いろいろ)
// 再描画
S_ReDraw(0);

// 保存
FileSave();
これで、アクセスログを整形しExcelに貼り付ける直前まで整形する操作のキーマクロができました。作ってみると非常に便利で、心の中でドヤ顔しています。

最後に

冒頭に記載したボトルネックについては、部員の皆様の活躍により、現在は改善されています。

UWPアプリからOpenCVしてみる(その2)

矢野です。UWPアプリからOpenCVを使う話の決着をつけておきます。 Visual Studioを起動して、メインページにボタンを追加します。 
NuGetを使って、OpenCvSharp(OpenCvSharp4, OpenCvSharp4.runtime.uwp)をインストールします。 ボタンのクリックイベントハンドラ(find_Click)に、カメラからのキャプチャ、データ変換、ARマーカーの検出、結果を表示という流れでコードを書いていきます。
private async void find_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    ImageEncodingProperties encProp = ImageEncodingProperties.CreateBmp();

    var source = captureElement.Source;

    SoftwareBitmap bitmap = null;
    using (var captureStream = new InMemoryRandomAccessStream())
    {
        await source.CapturePhotoToStreamAsync(encProp, captureStream);
        await captureStream.FlushAsync();
        captureStream.Seek(0);

        var decoder = await BitmapDecoder.CreateAsync(captureStream);
        bitmap = await decoder.GetSoftwareBitmapAsync(
            BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
    }

    Mat mat = null;
    using (var stream = new InMemoryRandomAccessStream())
    {
        var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);
        encoder.SetSoftwareBitmap(bitmap);
        await encoder.FlushAsync();

        mat = Mat.FromStream(stream.AsStreamForRead(), ImreadModes.Color);
    }

    var grayMat = new Mat();
    Cv2.CvtColor(mat, grayMat, ColorConversionCodes.BGR2GRAY);

    CvAruco.DetectMarkers(grayMat,
        CvAruco.GetPredefinedDictionary(PredefinedDictionaryName.Dict4X4_50),
        out Point2f[][] corners, out int[] ids, DetectorParameters.Create(), out Point2f[][] rejectedImgPoints);

    CvAruco.DrawDetectedMarkers(mat, corners, ids);

    using (var stream = new MemoryStream())
    {
        mat.WriteToStream(stream);
        using (var raStream = stream.AsRandomAccessStream())
        {
            var decoder = await BitmapDecoder.CreateAsync(raStream);
            bitmap = await decoder.GetSoftwareBitmapAsync(
                BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
        }
    }

    var imageSource = new SoftwareBitmapSource();
    await imageSource.SetBitmapAsync(bitmap);
    image.Source = imageSource;
}
OpenCvSharpは優れたライブラリで、.NETアプリケーションからOpenCVを使いやすくラップしてくれています。おかげでOpenCVでのARマーカー検出、描画は難しくありませんでした。 ところが、それ以前にUWPのSoftwareBitmapとOpenCVのMat型の相互の変換についてはなかなか消化することができず、あちこちのオンラインドキュメントやブログやフォーラムを何度も何度も巡回しつつ、試行錯誤しつつ行ったり来たりを繰り返してようやくそれらしい形にたどり着きました。 あまりそんなことばっかりやっているわけにはいきませんが、ある程度余裕を持て、藻掻いて、悩む時間も時には大事ですね。
これでもって改めて前回の記事をカメラで撮ってみる(横着)と、無事にARマーカーとそのID(3)が認識されていることが確認できました。

Electronを触ってみた件について

●はじめに

こんにちは、イメージマジックのSuzukiです。
最近かなり寒くなってきました。
電気毛布を2枚フル活用しながら寒さを凌いでいます。
最近は「electron」を使っています。
その為、今回は「electron」について書いていこうと思います。

●electronとは

・特徴
HTML + CSS + Node.js でアプリが作れる。
これ1つで Windows, Mac, Linux 向けのアプリが作れる。
他のものに比べて敷居が低い。

●インストール

1.nodeのインストール
Windowsの場合、 https://nodejs.org/download/ からmsiをダウンロードしてインストール。
2.Electronのインストール
$ npm -g install electron-prebuilt
3.プロジェクトの作成
アプリケーション用のディレクトリを作成し、その下で npm init します。
アプリケーションのエントリポイントは「index.js」になっています。

●参考

https://qiita.com/nyanchu/items/15d514d9b9f87e5c0a29

●最後に

electronの特徴や簡単な使い方について紹介しました。
Webの技術(HTML5やJavaScript)を使っているので、習得しやすいメリットがあります。

UWPアプリからOpenCVしてみる(その1)

矢野です。

プロトタイプ的にWindows用のアプリケーションを新たに作成するにあたり、ちょっと考えました。Windows Formsなら自分もそれなり多少は経験もあるけど、新規でつくるのに今さらFormsというのもどうなのかしら? でもWPFやUWPも結局のところあまり普及してきている感じがしないし、枯れたFormsに比べれば、情報はかなり少ない。XAML Islands? 何それ?
このプロトタイプは、PCに接続したWebカメラで撮影した画像をOpenCVでちょちょいと弄れるだけでいいのですが、でもFormsからカメラなどメディア関連の制御をするのは結構大変です。DirectShowとかMedia Foundationなどを使ってC++でゴリゴリ書く気になれば色々細かいところまで自由になるけれど、プロトタイプなのにそのへんをめちゃくちゃ頑張る気にはなれないし、とはいっても入手が容易なC#向けのラッパーだとちょっと古かったり、ちょっと不便だったり……。
で、けっきょくUWPアプリをC#で書くことにしました。それでも周回遅れなのかもしれませんが、せっかくの機会ですから少しでも新しい仕組みに追いついておきたいということで。
  • UWPアプリ上でMediaCaptureを使ってWebカメラにアクセス
  • Webカメラから取り込んだ画像をOpenCVで処理
  • 処理した画像を表示
これらを実現する簡単なアプリをつくってみたいと思います。 Visual Studio 2019を起動し、C#/Windows/UWPの「空白のアプリ(ユニバーサルWindows)」を新しいプロジェクトとして作成。
ソリューションエクスプローラーからPackage.appxmanifestをダブルクリック→機能タブの[Webカメラ]と「マイク]にチェックを入れて保存。
MainPage上にツールボックスからCaptureElementを配置し、適当な名前(captureElement)を付ける。StretchプロパティをUniformToFillにしておく。 MainPageのOnNavigatedTo(NavigationEventArgs)メソッドをオーバーライドする(awaitな呼び出しを行うので、メソッドにasync修飾子を追加します)。
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    var capture = new MediaCapture();

    await capture.InitializeAsync();

    var props = capture.VideoDeviceController
        .GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;

    await capture.VideoDeviceController
        .SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, props);

    captureElement.Source = capture;
    await capture.StartPreviewAsync();
}
プロジェクトをビルドして起動します。

アクセス許可を確認するダイアログが表示された場合は、許可します。
  • マイクへのアクセスを許可しますか?→[はい]
  • カメラにアクセスすることを許可しますか?→[はい]
たったこれだけで、リアルタイムにカメラの画像が表示されました! すごい! 泣きそうなりながらC++でMedia Foundationで書いた時とは大違い!(大げさ)
その2に続きます。