shuhelohelo’s blog

Xamarin.Forms多めです.

Arduino IDEを使ったM5Stack開発環境を整える

M5Stackの開発をしたいと思い立ち、開発環境を整えてみる

色々なサイトを参考にしながらすすめる。

基本はこちらから。

Getting Started: M5Stack の開発環境を準備する | プロトタイプ向けマイコンモジュール M5Stack と 3G 拡張ボードをセットアップする | ソラコムユーザーサイト - SORACOM Users

Arduino IDEのインストール

こちらからダウンロードする

Software | Arduino

Windowsストアからもインストールできるけれど、一番上のInstaller版を選択する。 ストア版などだと後々VSCodeとの連携で失敗するかもしれないとのことだった。

そのままの設定でインストール。

インストール中に以下のようなダイアログが表示された。 まあ全部インストールする。

インストールが終わったら起動してみる。 こんな感じ。

USBドライバーのインストール

次はUSBドライバーをインストールする。

Download | m5stack-store

CP2104 DriverのWindows用をダウンロードする

ダウンロードしたzipファイルを解凍して、以下のファイルを実行する。

インストーラが起動するので、インストールする。 インストール完了。

PCとM5StackをUSBケーブルで接続する。 デバイスマネージャを開いて以下のように表示されることを確認する。 よし!

M5Stackのボード定義のインストール

Arduino IDEで使えるようにするには、Arduino IDEでボード定義というものをインストールする必要がある。 そのためにライブラリマネージャに追加のライブラリ検索範囲を設定する必要がある。 ファイル > 環境設定

環境設定が開いたら、 追加のボードマネージャのURLの欄に以下のURLを追加する。 https://dl.espressif.com/dl/package_esp32_index.json

OKを押して環境設定を閉じる。

ツール > ボード > ボードマネージャを選択する。

検索欄にESP32と入力して検索すると、esp32というパッケージが見つかるので、これをインストールする。

さて、ボードをインストールするのだけれど、自分の持っているM5Stackはどれかというとこれ。

M5Stack Gray(9軸IMU搭載)--在庫限り - SWITCH-SCIENCE

ツール > ボード > ESP32 Arduinoを選択すると、ずらっとデバイスのリストが表示されるのでその中から対象のM5Stackデバイスを選択する。 M5Stack Grayの場合はどれだ?

こちらの記事によると、Grayの場合はM5Stack-Core-ESP32とのこと。

【M5Stack】ビルド時のボード・オプションの選び方(ArduinoIDE,VSCode+PIO) | M5Stack沼人の日記

Arduino IDEで先程のリストからM5Stack-Core-ESP32を選択する。

シリアルポートを選択

Arduino IDEからM5Stackにプログラムを書き込んだり、通信するためのシリアルポートを選択します。 ツールを開いてみると、シリアルポートにCOM3(環境によって異なる)があるので、これを選択します。デバイスマネージャで確認したポートです。

M5Stackライブラリのインストール

M5Stackの様々な機能にアクセスするためのライブラリをインストールします。

スケッチ > ライブラリをインクルード > ライブラリを管理を選択する。

ライブラリマネージャが表示されるので、M5Stackと入力して検索する。 絞り込んでも山ほどあるので、頑張ってM5Stackというライブラリを探す。真ん中ぐらいかな。

依存するライブラリもインストールするか訊かれるので、全部インストールする。

あれ、途中で失敗した。スクショ取り忘れた。

でも、ファイル > スケッチ例にM5Stackが追加されている。 まあいっか。

Hello Worldしてみる

それじゃHello Worldいってみますか スケッチ例にHello Worldがあるのでそれを選択する。

setup関数でディスプレイに「Hello World」を表示するだけの簡単なサンプルが表示される。

このまま左上の⇒ボタンをクリックすればコンパイルとデプロイが始まる

結構時間かかる…初回だからとのこと。

書き込みが完了した。 以下のように表示されたらOK。

M5Stackに「Hello World」と表示されている やった!

ASP.NET MVCプロジェクトにIdentityユーザー認証機能を追加する

とりあえずチュートリアルに従ってアプリを作る。

そうだ、ユーザー認証機能を追加しよう。

というときのためのメモです。

また、Identityユーザー認証機能を後付するとプロジェクト内にIdentityで使われるクラスやページのソースコードが作成されるので、いろいろアレンジしやすいです。

Identityユーザー認証機能の追加

追加自体はとても簡単です。 スキャフォールディングによって作成されます。

ソリューションエクスプローラで右クリックしてコンテキストメニューから追加 > 新規スキャフォールディングアイテムを選択します。

f:id:shuhelohelo:20190612122308p:plain

スキャフォールディングを追加ウィンドウが開くので、左ペインからIDを選択して右下の追加ボタンを押します。

f:id:shuhelohelo:20190612122451p:plain

IDの追加ウィンドウが開く。

f:id:shuhelohelo:20190612122900p:plain

とりあえずやること。

Startup.csの編集

認証機能を使うことを指定する。

Configureメソッドに以下を追加

これはapp.UseMVCよりも前に置くこと。

app.UseAuthentication();

_Layout.cshtmlの編集

Identityのスキャフォールディングで_LoginPartial.cshtmlが作成されているので、これがサイトのヘッダ部分に表示されるようにする。

<partial name="_LoginPartial" />を埋め込む。

    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
・・・
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <partial name="_LoginPartial" />
・・・
                </div>
            </div>
        </nav>
    </header>

IdentityのLoginページやRegisterページはライブラリ内に保持されているので、直接編集などはできない。 困ったな。

わかった!!!

IDの追加画面で、オーバーライドする項目でチェックをつけたページがAreas/Identity/Pages/Account内に作成されて、オーバーライドという形で編集できるようになる。

OpenCvSharpでHSVで特定の色を取り出すときの簡易確認ツール

OpenCVで特定の色だけにしぼり込むときに、HSV(Hue,Saturation,Value)で範囲を指定するとします。

ある画像をサンプルとして、自分が絞り込みたい色の範囲はHSVでどの値の範囲なのかを確認したいです。

そんなちょっとした確認に便利なツールがあると便利です。ここでは、そんなちょっとしたツールをOpenCvSharpで作ります。

OpenCVでは独自のウィンドウに加え、スライダーをウィンドウに設置することができるため、こういった値を変えながら結果を確認したい、という用途にピッタリのツールを簡単に作ることができます。

f:id:shuhelohelo:20201123124148p:plain

参考

OpenCVのサンプルコードはPythonC++のものがたくさん見つかりますが、OpenCvSharpでも書き方はほとんど同じなので、他言語のサンプルを簡単な脳内変換でOpenCvSharpで使うことができます。

今回参考にさせてもらったのは以下の記事です。

qiita.com

ありがとうございました。

実装

流れとしては、

  1. 名前付きのウィンドウを作成する
  2. そのウィンドウにスライダー(trackbar)を配置する
  3. スライダーのOnChangedイベントにイベントハンドラを設定する
  4. イベントハンドラの中でスライダーの値をもとに画像を生成してウィンドウを更新

です。これがほぼすべてです。

using OpenCvSharp;
using System;

namespace OpenCvHsvChecker
{
    class Program
    {
        private static int _h_min = 0;
        private static int _h_max = 0;
        private static int _s_min = 0;
        private static int _s_max = 0;
        private static int _v_min = 0;
        private static int _v_max = 0;

        private static Mat src = new Mat();
        private static Mat hsv = new Mat();
        //private static Mat dst = new Mat();

        private const string WINDOW_NAME = "HSV Checker";
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            string inputImagePath = "Images/gauge-1.jpg";

            src = Cv2.ImRead(inputImagePath);
            if (src is null)
                return;
            Cv2.ImShow("src", src);

            Cv2.CvtColor(src, hsv, ColorConversionCodes.BGR2HSV, 3);
            Cv2.ImShow("hsv", hsv);

            //名前つきウィンドウを作成
            Cv2.NamedWindow(WINDOW_NAME);

            //ウィンドウ名を指定してスライダーを配置
            Cv2.CreateTrackbar("H_Min", WINDOW_NAME, 179, onChange: H_Min_Changed);
            Cv2.CreateTrackbar("H_Max", WINDOW_NAME, 179, onChange: H_Max_Changed);
            Cv2.CreateTrackbar("S_Min", WINDOW_NAME, 255, onChange: S_Min_Changed);
            Cv2.CreateTrackbar("S_Max", WINDOW_NAME, 255, onChange: S_Max_Changed);
            Cv2.CreateTrackbar("V_Min", WINDOW_NAME, 255, onChange: V_Min_Changed);
            Cv2.CreateTrackbar("V_Max", WINDOW_NAME, 255, onChange: V_Max_Changed);

            //初期画像を表示
            Cv2.ImShow(WINDOW_NAME, src);

            Cv2.WaitKey();
        }

        private static void V_Max_Changed(int pos, IntPtr userData)
        {
            _v_max = pos;
            Update();
        }

        private static void V_Min_Changed(int pos, IntPtr userData)
        {
            _v_min = pos;
            Update();
        }

        private static void S_Max_Changed(int pos, IntPtr userData)
        {
            _s_max = pos;
            Update();
        }

        private static void S_Min_Changed(int pos, IntPtr userData)
        {
            _s_min = pos;
            Update();
        }

        private static void H_Max_Changed(int pos, IntPtr userData)
        {
            _h_max = pos;
            Update();
        }

        private static void H_Min_Changed(int pos, IntPtr userData)
        {
            _h_min = pos;
            Console.WriteLine(_h_min);
            Update();
        }


        static void Update()
        {
            //HSV画像とスライダーの値からマスクを生成
            var scalar_min = new Scalar(_h_min, _s_min, _v_min);
            var scalar_max = new Scalar(_h_max, _s_max, _v_max);
            Mat mask = new Mat();
            Cv2.InRange(hsv, scalar_min, scalar_max, mask);

            //マスク画像を使って元画像にフィルタをかける
            Mat dst = new Mat();
            src.CopyTo(dst, mask);

            //ウィンドウの画像を更新
            Cv2.ImShow(WINDOW_NAME, dst);
        }
    }
}

OpenCvSharpのインストールについては以下の記事を。

shuhelohelo.hatenablog.com

ソースコード

github.com

OpenCvSharp4で特定の色だけを抜き出す

以下の記事をOpenCvSharpを使ってやった内容になります。

tecsingularity.com

RGBで指定

RGBで色の範囲を指定して抜き出すには以下のようにします。

using OpenCvSharp;
using System;

namespace OpenCVColorExtraction
{
    class Program
    {
        //抽出したい色の範囲をRGBで指定
        const int B_MAX = 40;
        const int B_MIN = 0;
        const int G_MAX = 50;
        const int G_MIN = 0;
        const int R_MAX = 170;
        const int R_MIN = 100;

        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            string inputFilePath = "Images/gauge-1.jpg";

            //画像を読み込み
            var src = Cv2.ImRead(inputFilePath);
            if (src is null)
                Console.WriteLine("fail to read file.");

            //マスクを作成
            Scalar s_min = new Scalar(B_MIN, G_MIN, R_MIN);
            Scalar s_max = new Scalar(B_MAX, G_MAX, R_MAX);
            Mat maskImage = new Mat();
            Cv2.InRange(src, s_min, s_max, maskImage);


            //マスクを使ってフィルタリング
            Mat masked = new Mat();
            src.CopyTo(masked, maskImage);

            //表示
            using (new Window("src", src))
            using (new Window("maskImage", maskImage))
            using (new Window("masked", masked))
                Cv2.WaitKey();//何かキーが押されるまで待つ

        }
    }
}

概ねOKですね。

元画像 f:id:shuhelohelo:20201123094350p:plain

マスク f:id:shuhelohelo:20201123094404p:plain

抽出 f:id:shuhelohelo:20201123095126p:plain

HSVで指定

(追記:HSVの値を変えながらデバッグを繰り返すのは面倒なので簡易確認ツールを作成しました。

shuhelohelo.hatenablog.com )

using OpenCvSharp;
using System;

namespace OpenCVColorExtraction
{
    class Program
    {
        //抽出したい色の範囲をHSVで指定
        const int H_MAX = 120;
        const int H_MIN = 111;
        const int S_MAX = 255;
        const int S_MIN = 50;
        const int V_MAX = 255;
        const int V_MIN = 50;
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            string inputFilePath = "Images/gauge-1.jpg";

            //画像を読み込み
            var src = Cv2.ImRead(inputFilePath);
            if (src is null)
                Console.WriteLine("fail to read file.");

            //HSVに変換
            Mat hsv = new Mat();
            Cv2.CvtColor(src, hsv, ColorConversionCodes.RGB2HSV);

            //マスクを作成
            Scalar s_min = new Scalar(H_MIN, S_MIN, V_MIN);
            Scalar s_max = new Scalar(H_MAX, S_MAX, V_MAX);
            Mat maskImage = new Mat();
            Cv2.InRange(hsv, s_min, s_max, maskImage);

            //マスクを使ってフィルタリング
            Mat masked = new Mat();
            src.CopyTo(masked, maskImage);

            //表示
            using (new Window("src", src))
            using (new Window("hsv", hsv))
            using (new Window("maskImage", maskImage))
            using (new Window("masked", masked))
                Cv2.WaitKey();//何かキーが押されるまで待つ
        }

でも、色相の指定がよくわからないな。 なぜこの色がこの範囲になるのか。

f:id:shuhelohelo:20201123103926p:plain

f:id:shuhelohelo:20201123103948p:plain

f:id:shuhelohelo:20201123104000p:plain

ソースコード

github.com

C#でOpenCVを使うOpenCVSharp

OpenCVC#ラッパーとしてOpenCvSharpというNugetパッケージがあります。

github.com

これを使うとC#OpenCVによる画像処理や色々ができます。

例えば、画像を読み込んで表示させたり、画像の色空間を変更したりというのは以下のようなコードを書きます。

using OpenCvSharp;
using System;

namespace OpenCVColorExtraction
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            string inputFilePath = "Images/gauge-1.jpg";

            var src = Cv2.ImRead(inputFilePath);
            if (src is null)
                Console.WriteLine("fail to read file.");

            Mat hsv = new Mat();
            Cv2.CvtColor(src, hsv, ColorConversionCodes.RGB2HSV);

            using (new Window("src", src))
            using (new Window("hsv", hsv))
                Cv2.WaitKey();

        }
    }
}

C++Pythonでおなじみだと思います。

これを実行すると以下のように元画像とHSV色空間に変換された画像が表示されます。 f:id:shuhelohelo:20201123091140p:plain

インストール

インストールは簡単で、OpenCvSharpのReadMeにも書かれているとおり、Nugetパッケージからインストールするだけです。

ここではWindowsの場合のみを紹介しますが、他にもUWPの場合やUbuntuの場合などが書いてありますので、自分の環境に合わせてインストールするパッケージを選択します。

Windowsの場合は2とおりあります。

  • OpenCVSharp4 と OpenCVSharp4.runtime.winの組み合わせ
  • OpenCVSharp4.Windowsのみ

特段理由がなければ後者のOpenCvSharp.Windowsをインストールすればよいでしょう。

Dell Latitude 7300でFnキーロック

Latitude 7300ではファンクションキーを使う場合にはFnキーをあわせて押す必要があります。 例えば、Visual Studioデバッグを実行したいとき(F5)などですね。

Fnキーを一緒に押さない場合は、画面の明るさや音量を変更する機能が割り当てられています。

Surfaceとは真逆なので、結構戸惑ってしまいますが、当然それを解消する設定が用意されており、以下のように公式で紹介されています。

Fn+EscでFnキーをロックすることができ、Surfaceなどと同じ操作にすることができます。 音量などを変更したい場合は逆にFnキーと併用することになります。

www.dell.com

正規表現でマッチした部分を同じ文字数の記号で置換する

実装の一例としてメモ

例えば,連続する半角スペースに対して2文字目以降をnbspで置き換える場合は以下のようにする.

        public static string ConsecutiveBlanksToNBSP(string sentense)
        {
            Regex re = new Regex(@"(?<= )( )+");
            MatchCollection matches = re.Matches(sentense);

            if (matches.Count == 0)
            {
                Console.WriteLine("nothing");
                return sentense;
            }

            var strBuilder = new StringBuilder(sentense);
            foreach (Match m in matches)
            {
                strBuilder.Remove(m.Index, m.Length);//対象部分を削除
                strBuilder.Insert(m.Index, new string('\u00a0', m.Length));//挿入
            }

            return strBuilder.ToString();
        }