雑食性雑感雑記

知識の整理場。ため込んだ知識をブログ記事として再構築します。

趣味用PCを新しくした話

家では自作PCを使ってゲームしたり、撮った写真管理したり、スマホに入れる音楽データをリッピングしたりしているのですが、
その環境が古くなってきて、世の中が Windows11 とか言い出してきているので新しくすることにしました。

単なる PC 組み立て記事になってしまいますが、備忘録かねて書いておきます。
( 最近技術記事書けてないのもあって、お茶濁す感じですが… )

要望

  • 大きさ
    • 現在、テレビ棚の下に PC 置いている。スペースは奥行 40 cm。高さ 23 cm くらい (横幅はある)。ここに入るやつ。
    • 現 PC の箱は SilverStone 製の GD06 というもの。
  • そこそこゲームできるやつ。
    • CPU はハイスペックなものが良い。
    • GPU はミドルグレードで良いけどほしい。
  • Windows 機
    • 仕事では Ubuntu 使ってたりもするけど、やっぱり Windows 安定している。
    • Home で十分。

買い物

物欲の消化をかねて、月1回ペースで買い物しました。Amazon のセールとか見ながらなるべく安くなるように。
( ということで実際の時間としては半年かかってます。。組むのは一瞬でしたが。)

※ 以降で、金額 1,000 未満は四捨五入して書いてます。

6月 マザーボード、7月 ケース

SilverStone のケースはアルミでカッコいいし、小さい PC 用も出ているので、引き続き SilverStone のものにしました。
SG12 というもの。高さはぎりぎりですが、GD06 より横幅無くなりコンパクトになりそう。

でマザボは Micro-ATX が必要になりますが、ちょうどセールで出ていたので MSI のものを購入。
今までの PC は Intel しか使ったことが無かったので、気まぐれで AMD を選択してみました。

6月 ... MSI MAG B550M MORTAR WIFI \15,000
7月 ... Silver Stone SG12 \15,000

8月 電源

電源は適当に選定。『すごい CPU & GPU を付けるぞ~』というわけでは無いので、そんなすごい電源はいらない。
ちょうど最近出た機種で、「組み立てやすい」という電源が出ていたので、それを選択。

8月 ... 玄人志向 80PLUS GOLD取得 ゲーミングPC向け ATX電源 750W KRPW-GA750W/90+ \11,000

9月 ストレージ、10月 メモリ

現 PC は HDD2TB x 2 にして写真をミラーリングで保存していたが、
( たまたまかもだけど ) 稼働率高くないからか、不良が発生するようなことは無く――

ミラーリングは不要で、別途、適宜外部 SSD にデータ吸わせて手動バックアップという方針にしました。

メインストレージに 1TB、データストレージに 2TB。
購入したマザボには M.2 が 2本差さるので、それぞれ M.2 で用意。

メモリは一応多めに用意しておくことにしました。16GB x 2。ゲーム用途が主としては十分すぎるかな。

9月 ... キングストン SSD NV1-E M.2 1TB \10,000、2TB \21,000
10月 ... キングストン FURY DDR4 2666MHz 16GB x 2 14,000

それぞれ、Amazon のセールで見つけたときに。

11月 CPU + α

現 PC は Intel i7 が入っているので、同じくらいのグレードらしい AMD の 5700G を選択。CPU クーラーもついているんですね。
あと、グリスとかねじとか足りないもの \2,000。Windows Home \20,000。

GPU も AMD で揃えたかったのですが、色んな要因で高騰しているので、諦めました。。
現 PC についているものは後から購入して取り付けたので、そんなに古くないだろうと再利用することに。
( NVIDIA GeForce 1070 OC。購入時 \54,000 )

11月 ... AMD Ryzen 7 5700G \50,000 + その他 \22,000

これで買い物おしまい。

組み立て

ストレージ、メモリが到着したころからマザボに組み付けていたのですが、CPU来てからが早かった。。
新しいおもちゃがくると気になっちゃいますね…

f:id:kazuki_nagasawa:20211104211746j:plain
上からの図。結構なぎりぎり感。

>

f:id:kazuki_nagasawa:20211104212431j:plain
電源は上にのせる形式。空調的には効率的なのかな?

f:id:kazuki_nagasawa:20211104213640j:plain
GPU 付けた。2本目 M.2 に SilverStone の M.2 用放熱板を付けてみたらぎりぎりに…。
f:id:kazuki_nagasawa:20211104235020j:plain
完成!!詰め込みました。

電源投入 → 起動しない!?

電源投入したが起動しなくて焦る orz..

はめ込み確認したがきちんとはまっているし、CPU まで外して確認したくないし…。

色々調べた結果、BIOS が古いのが原因。BIOS 更新したら直った。
おそらく、マザボが元々 3000 系の Ryzen に対応していたのが原因で、箱に「BIOS更新済み」とあったけど、それは 3000 系として最新なのかと。

購入したマザボはデータ入れた USB メモリさして、リセットボタン押下で直接更新できるタイプで、そういった点ではすごく助かりました!!
https://jp.msi.com/blog/flash-bios-button-sop

( この辺りも実は選定ポイントに入るかもしれない。)

反省とか感想とか

  • 仕事でも使っているので知ってたんですが、最近はマザボに M.2 直接差さるから選択によっては HDD スペース不要なんですよね。。
    • ――というところに頭が回らず、HDD いっぱい差さる箱を買ってしまった。。省スペース型ではあるけど。
    • 1/4 くらいのスペースは何も入ってないエリアになってますね。
    • とはいえ Mini-ITX は GPU 選んだり制約かかってくるからやっぱり Micro-ATX ベースかなぁ。むー。。
  • 静音になった!!
    • 単に HDD 分?ファン数減った?すごく静かです。
  • Windows 11 は PC 作成後 3 日で落ちてきました。11 化したからどうかということもないけど、まあいいんじゃないでしょうか。
  • 金額的には普通に買った方が安いのかな?まあ気にしない。。
  • 旧 PC は内部をクリーンし、HDD 等余計なものを省いたうえで Zwift 専用機になりました。
    • Zwift スマホアプリで使ってて電源の制約があったのですが、それが無くなったのが嬉しい!!

『滑らか』なセンサデータの極大・極小を取得してみる

最近は Python でセンサデータを確認したり、特徴を取ったりな仕事をしています。
その中で最近「データの極大値・極小値を取る」ことをやりました。

Python で行う方法を調べると、よく出てくるのは scipy の signal モジュールで、

import numpy as np
from scipy import signal

a = np.array([1, 2, 3, 4, 5, 4, 3, 2, 1])
b = signal.argrelmax(a, order = 1)[0]

print(b)
# [4]

と、確かに極大値となっているところのインデックスを取得できるのですが、

最大値が連続しているような、以下の形式だと極大値を取ってくることができませんでした。

c = np.array([1, 2, 3, 4, 5, 5, 4, 3, 2, 1])
d = signal.argrelmax(c, order = 1)[0]

print(d)
# []

おそらく、signal は信号処理で argrelmax が極大値ではなくピークを取ってくる処理なので、
このような『なめらか』なのは極大値とみなされないのかと。


――あんまりやっている人が見つからなかったので、書いてみました。
やってることは大したことではなく、結局は傾き = 変化量が 0 を含めて符号変わるところを見るだけなので。。
後々の処理の都合で『後優先』になっています。

def get_peaks(data) :
    """ 
    データの変化量を順にみて、
    極大 (変化量が + から - に変わる地点)、極小 (- から +) のインデックスをそれぞれ取得 
    """

    if data.size < 2 :
        raise Exception("Data size is too few.")

    # 変化量算出
    diffs = np.zeros((data.size - 1))
    for i in range(data.size - 1) :
        idx = i + 1
        diffs[i] = data[idx] - data[idx - 1]

    # 符号
    def get_flag(diff) :
        if diff > 0 : return 1
        if diff < 0 : return -1
        return 0

    peak_max = []
    peak_min = []

    pre_flag = get_flag(diffs[0])

    for i in range(1, diffs.size) :

        flag = get_flag(diffs[i])

        # 今の変化量が 0 なら無視
        if flag == 0 : continue

        # 前の変化量が 0 or 今の変化量が 0 以外 → 設定
        # NOTE: 0 番目の変化量から 0 で続いていたときのみ
        if pre_flag == 0 :
            if flag > 0 : 
                peak_min.append(i)
            else :
                peak_max.append(i)
            pre_flag = flag
            continue

        # 変化量が前と違う場合は設定
        if pre_flag != flag :
            if flag > 0 :
                peak_min.append(i)
            else :
                peak_max.append(i)
            pre_flag = flag

    return peak_max, peak_min

面倒なので極大極小まとめて作っちゃいましたが、先ほどの配列 c に対してかけると

pmax, pmin = get_peaks(c)

print(pmax, pmin)
# [5] []

と、欲しかった結果を得ることができました。


…実は signal じゃないモジュールで取得してくれる処理があったりするのかも。と思いつつ。。。

Ubuntu で動作する .NET 系な GUI フレームワークお試し (Mono、AvaloniaUI)

仕事で、Ubuntu で動く GUI フレームワークについてアレコレ調べたのですが、思いの外深掘りになったので、改めて私的にまとめてみることにしました。

経緯

以前、Ubuntu を使って Qt を用いた GUI アプリケーションを作ったのですが、データのやり取りが絡んでくると結構複雑な構成となり、ちょっとトラウマ。。

改めて今回、同じく Ubuntu で動く GUI アプリケーションとなったので、使えるものを調査することにしました。
最近 WPF を使うことが増えてきたので、その互換のものがあればスキル的にも楽かと――

作るもの

実際作るものを簡易的にした構成の『動画ビューア』を作ります。
動画の読み込みは OpenCV を使った動的ライブラリ (.so ファイル) で行い、それを GUI から呼び出すようにします。

完成イメージはこんな感じ。
動画の読み込みダイアログ使ったファイルパス指定と、動画のスタート/ストップボタン。

f:id:kazuki_nagasawa:20201021121106p:plain
完成イメージ

作ったもの

今回作ったものは github に置いたので、興味ある方はどうぞ。
github.com

環境

Ubuntu 環境は WSL で試してます。

  • Windows 10
    • Visual Studio 2017
    • Ubuntu 18.04 (on WSL)
      • OpenCV 4.2.0
      • .NET Core SDK 2.1

以下、作ったものの説明です。

動的ライブラリ

これは普通に Linux C++ で動くものを作るだけだから特記事項はないですね。
忘れがちなのは 「extern "C"」 でエントリポイント作らないと外部呼出しで関数が見えないことですね。

extern "C" {
    int initialize(char* videoPath, int width, int height);
    int getFrame(byte* data);
}

使い方としては、
・initialize で読み込む動画パス、映像表示ウィンドウの幅、高さを指定。
・getFrame で 1 フレーム分の動画データを映像表示ウィンドウに合わせてリサイズ後、byte 配列として取得。
というのを想定しています。

Mono で Windows Forms アプリケーションを Linux 化

調べていてまず初めに出てきたのが Mono。オープンソースで .NET Framework ベースの GUI を使えるらしい。( そのうち .NET Framework に吸収される? )
https://www.mono-project.com/

いくつか使える中の Windows Forms アプリケーションを試してみます。

Visual Studio で GUI 構築

( どこまで新しいバージョンに対応できているか不明ですが、)
作成はそのまま Visual Studio でできるので、開発は楽で良いですね。

so ライブラリ読み込み部は Linux 側じゃないと確認できないので、表示・操作部分のみ VS 側で実装してしまいます。

f:id:kazuki_nagasawa:20201021123056p:plain
Visual Studio で表示部作成中

Linux 側に移してビルド

表示部が出来たら、C# ファイル (.cs) だけを Linux 側にコピー。今回は
・Program.cs ... コード全体のエントリポイント
・MainWindow.cs ... ウィンドウの処理実装部
・MainWindow.Designer.cs ... ウィンドウのコントロールやデザイン。大抵自動生成。
の3ファイル。
MainWindow.cs には so ライブラリとの結合処理も追加で作ります。

事前に mcs (Mono C# Compiler) をインストールした上でビルドすれば、アプリケーション (exe) がビルドできます。

実行

あとは mono コマンドで実行すれば OK 。
( Windows 上で実行する場合は X Window System 対応アプリケーションを事前に起動しておくこと。)

f:id:kazuki_nagasawa:20201022083215p:plain
WSL 上から起動した Mono 版アプリケーション

ファイル選択ウィンドウのフィルタ部の日本語は文字化けしてますね。。
この辺り正しく使うなら追加設定必要そう。

f:id:kazuki_nagasawa:20201021123835p:plain
ファイル選択ウィンドウ (Mono)

AvaloniaUI で WPF (に近い) アプリケーションを Linux 化

Windows Forms + Mono を使えばとりあえず Linux で作りやすい GUI ができることが分かったのですが、
・Windows Forms はアンカー設定等、画面サイズがコロコロ変わるものを作りにくい。
・配置には WPF の Grid layout が便利だったなー。
ということでやっぱり WPF 使いたい!!

「Mono で WPF も対応してくれないのー?」→ 「AvaloniaUI なるものがあるよ。」
https://www.mono-project.com/docs/gui/wpf/

ということだったので、それならと AvaloniaUI でも同じものを作って試してみることにしました。

Visual Studio に AvaloniaUI 適用

AvaloniaUI を Visual Studio で使うには、プラグインが配布されているのでそれを入れれば OK 。
https://avaloniaui.net/docs/quickstart/create-new-project

↑のチュートリアルの通り、Avalonia Application を作って進めていけば作れます。

axaml ? xaml ?

現状の AvaloniaUI では、xaml も対応しているらしいけど、axaml が標準らしい。( Avalonia の a がついている xaml ? )

Visual Studio の Solution Explorer で axaml を認識してくれなかったので、全部 xaml にリネームしたらビルド通らなくなった orz..
結局、フォルダ形式で閲覧することで axaml 見えるので、このままで開発を進めました。

コントロール

大体使うコントロールは docs に載ってます
https://avaloniaui.net/docs/controls/
が、やっぱり足りなくなると検索かける必要が。ドキュメントは全体的に少な目。。

また、厳密に WPF と互換性があるわけではなく、結構いろんなところに違いがあります。
例えば、OpenFileDialog だと、WPF 標準は

private void ButtonOpenVideo_Click(object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
    dialog.Filter = "動画ファイル (*.avi,*.mov,*.mp4)|*.avi;*.mov;*.mp4|全てのファイル (*.*)|*.*";
    if (this.TextboxVideoFilePath.Text.Length > 0 && System.IO.File.Exists(this.TextboxVideoFilePath.Text))
    {
        dialog.InitialDirectory = System.IO.Path.GetDirectoryName(this.TextboxVideoFilePath.Text);
        dialog.FileName = System.IO.Path.GetFileName(this.TextboxVideoFilePath.Text);
    }
    else
    {
        dialog.InitialDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
    }
    if (dialog.ShowDialog() == true)
    {
        this.TextboxVideoFilePath.Text = dialog.FileName;
    }
}

なのに対して、AvaloniaUI だと

private async void ButtonOpenVideo_Clicked(object sender, RoutedEventArgs e)
{
    OpenFileDialog dialog = new OpenFileDialog();
    FileDialogFilter f1 = new FileDialogFilter();
    f1.Name = "動画ファイル (*.avi,*.mov,*.mp4)";
    f1.Extensions = new System.Collections.Generic.List<string>();
    f1.Extensions.Add("avi");
    f1.Extensions.Add("mov");
    f1.Extensions.Add("mp4");
    FileDialogFilter f2 = new FileDialogFilter();
    f2.Name = "すべてのファイル (*.*)";
    f2.Extensions.Add("*.*");
    dialog.Filters.Add(f1);
    dialog.Filters.Add(f2);
    try {
        string[] pathes = await dialog.ShowAsync(this);
        if (pathes.Length > 0) {
           TextBoxVideoPath.Text = pathes[0];
        }
    } catch {};
}

な感じ。非同期になってますね。

また、WriteableBitmap も Avalonia.Media.Imaging にあるものを。

Linux 側に移してビルド

表示部が出来たら Linux 側に移してビルド。AvaloniaUI の場合、.NET Core でビルドするので、ソリューションファイル (.sln) 以外すべて必要です。
dotnet build でビルド。

実行

dotnet run で実行。

f:id:kazuki_nagasawa:20201022083324p:plain
WSL 上から起動した AvaloniaUI 版アプリケーション

こっちはファイル選択ウィンドウのフィルタ部の日本語も大丈夫。

f:id:kazuki_nagasawa:20201021125832p:plain
ファイル選択ウィンドウ (AvaloniaUI)

まとめ

.NET 系な GUI フレームワークである Mono と AvaloniaUI それぞれで動画再生 GUI を作ってみました。
ドキュメントが少ないですが、自分としては WPF に近く、レイアウトしやすい AvaloniaUI にとりあえず軍配でしょうか。

開発する側としては、早く OS を垣根を越えて簡単に作れるものが出てくれると嬉しいのですが。。

OpenCV + Matplotlib (Seaborn) で 2 画面の動画 + グラフ表示

前に「OpenCV映像とMatplotlibグラフを、Matplotlibの1つの画面で表示させる」というのをやりました。
OpenCV画像 + matplotlibグラフを合わせて表示する - 雑食性雑感雑記

今回は、綺麗に表示ができる「Seaborn」を使ってみたくなったのに合わせて、
OpenCV 側で2画面を出す方法が分かったので、それをやってみます。

完成形としては、こんな感じで動画のウィンドウとグラフのウィンドウが出る形です。

f:id:kazuki_nagasawa:20200519091112p:plain
完成形

※ 動画はフリーサイト Pexels より。https://www.pexels.com/video/river-with-strong-current-2019791/

コード

そこまで長くないので、全体のコードを貼っておきます。
グレースケール化した色値のヒストグラムを取ってます。
要 cv2, matplotlib, numpy, seaborn。

# for CLI
import matplotlib
matplotlib.use('TkAgg')

import cv2
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

INPUT_MOVIE_PATH = "sample.mp4"


def hist(frame, fig) :
    """ Seaborn histgram """

    # Grayscale
    frame_g = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # Graph
    plt.clf()
    sns.distplot(frame_g.flatten())
    fig.canvas.draw()

    # Graph to np img
    frame_graph = np.fromstring(fig.canvas.tostring_rgb(), dtype = np.uint8, sep = '')
    frame_graph = frame_graph.reshape(fig.canvas.get_width_height()[::-1] + (3,))
    frame_graph = cv2.cvtColor(frame_graph, cv2.COLOR_RGB2BGR)
    return frame_graph


if __name__ == '__main__' :

    # Seaborn initialize
    sns.set()
    fig = plt.figure()

    # Start video capture
    cap = cv2.VideoCapture(INPUT_MOVIE_PATH)

    while True :
        ret, frame = cap.read()
        if not ret :
            break

        # Resize ... 大きすぎる場合に
        # size = (int(frame.shape[1] / 2), int(frame.shape[0] / 2))
        # frame = cv2.resize(frame, size)

        # Histogram
        frame_graph = hist(frame, fig)

        cv2.imshow("Window", frame)
        cv2.imshow("Graph", frame_graph)

        key = cv2.waitKey(30) & 0xFF
        if key == ord('q') :
            break

説明

OpenCV で動画表示

―― はどこでも書かれていると思うので略。

グラフの numpy 画像化

この↓

    # Graph
    plt.clf()
    sns.distplot(frame_g.flatten())
    fig.canvas.draw()

    # Graph to np img
    frame_graph = np.fromstring(fig.canvas.tostring_rgb(), dtype = np.uint8, sep = '')
    frame_graph = frame_graph.reshape(fig.canvas.get_width_height()[::-1] + (3,))
    frame_graph = cv2.cvtColor(frame_graph, cv2.COLOR_RGB2BGR)
    return frame_graph

あたり。

plt.clf() で canvas に書かれたグラフのクリア。
ここ入れないと同じ figure 上に描かれ続けてカラフルなグラフになる。(特定の場合には不要かもだけど…?)

sns.distplot() で描画し、それを fig.canvas.draw() で Matplotlib 内描画エリアに描画しているらしい。
その canvas エリアを numpy array として持ってきて reshape して cv2 用に色空間変換しているのが後半部分。

その他

今回、WSL 上の Python3 から MobaXTerm を使って実行しました。
CLI 実行の場合は python-tk が入ってないとダメとか、

import matplotlib
matplotlib.use('TkAgg')

の設定が無いとダメとかあるとのこと。

メモ

  • とりあえずやりたいことはできました。
  • ただし、遅い。。描画方法等要検討。
    • グラフ描画のための計算 (ヒストグラム) 自体は遅くないハズ。
    • fig.canvas.draw() 自体が遅いという話もある。特定の部分だけ描画更新すると速くなるらしい?
  • グラフも nparray の画像化できてるので、元画像上に重ねて貼るような表現にしてもよいかも。

参照

ProcedualMeshComponent で波打つメッシュを作ってみた

最近はお仕事で UE4 を扱ってます。
その中で ProcedualMeshComponent を扱う機会があったので、その習作。波打つメッシュを作ってみました。

ProcedualMeshComponent

ProceduralMeshComponent | Unreal Engine Documentation
簡単には動的にメッシュを生成したり動かしたりする仕組みです。
作るためにはメッシュの約束事を覚える必要がありますが、フラグ一つで collision つけたりもできるので覚えると便利そう。

検索すると簡単な使い方紹介しているブログがいくつか見つかるので、詳細は省略。

こんなの作った。

作ったサンプルプロジェクトは github に置きました。( C++ プロジェクトです )
github.com

f:id:kazuki_nagasawa:20200409154923g:plain
sin カーブで波打つサンプルです。色をくるくる変えてます。

f:id:kazuki_nagasawa:20200409155033g:plain
Collision つけてオブジェクト乗せたりも可能。
( トランポリン的なものも作れるかも? )

技術的要点

( 以下添付のコードは説明用。一部が省略されています。詳しいコードは github 見てください。)

メッシュ構造

Initialize() でメッシュ頂点の定義をまとめて行っていますが、Triangle の通り、正方形 (0 -> 1 -> 2, 0 -> 2 -> 3) を一つの単位としています。

void AWaveActor::Initialize()
{
	int MeshIndexSize = (this->XMax - 1) * (this->YMax - 1) * 4;

	int midx;
	for (int my = 0; my < this->YMax - 1; my++) {
		for (int mx = 0; mx < this->XMax - 1; mx++) {
			midx = this->GetMeshIndex(mx, my);

			this->Triangles.Add(0 + midx * 4);
			this->Triangles.Add(1 + midx * 4);
			this->Triangles.Add(2 + midx * 4);
			this->Triangles.Add(0 + midx * 4);
			this->Triangles.Add(2 + midx * 4);
			this->Triangles.Add(3 + midx * 4);
		}
	}
}

各頂点は Tick() で動く UpdateMesh() で。

void AWaveActor::UpdateMesh(float DeltaTime)
{
	this->ElapsedTime += DeltaTime;

	TArray<FVector> Vertices;
	TArray<FLinearColor> VertexColors;

	float OffsetX = -this->SizeBase * this->cx;
	float OffsetY = -this->SizeBase * this->cy;

	// Calc per mesh
	FLinearColor Color = GetColor(this->ElapsedTime);
	float SizeBaseHalf = this->SizeBase / 2.0;
	float x0, x1, y0, y1;
	for (int my = 0; my < this->YMax - 1; my++) {
		for (int mx = 0; mx < this->XMax - 1; mx++) {
			x0 = this->SizeBase * (mx - this->cx) + SizeBaseHalf;
			x1 = x0 + this->SizeBase;
			y0 = this->SizeBase * (my - this->cy) + SizeBaseHalf;
			y1 = y0 + this->SizeBase;

			Vertices.Add(FVector(x0, y0, this->GetHeight(mx + 0, my + 0)));
			Vertices.Add(FVector(x0, y1, this->GetHeight(mx + 0, my + 1)));
			Vertices.Add(FVector(x1, y1, this->GetHeight(mx + 1, my + 1)));
			Vertices.Add(FVector(x1, y0, this->GetHeight(mx + 1, my + 0)));

			for (int c = 0; c < 4; c++) {
				VertexColors.Add(Color);
			}
		}
	}
}

と、正方形の各頂点を DeltaTime に従った値で更新しています。

メッシュの作成、更新

メッシュの作成は CreateMeshSection_LinearColor で行っています。第一引数の 0 はメッシュのインデックスの様で、これを増やしていけば複数レイヤに書き込みができるっぽいです (未検証 )。
更新は UpdateMeshSection_LinearColor の方で、対応するインデックスのメッシュの更新を行ってます。
( 全替えしてるのに処理的に重くないのかな…?いちおうサクサク動いてます。 )

void AWaveActor::UpdateMesh(float DeltaTime)
{
	( 中略 )

	if (this->IsCreated) {
		mesh->UpdateMeshSection_LinearColor(0, Vertices, Normals, UV0, VertexColors, Tangents);
	}
	else {
		this->IsCreated = true;
		mesh->CreateMeshSection_LinearColor(0, Vertices, Triangles, Normals, UV0, VertexColors, Tangents, this->IsCollision);
		mesh->ContainsPhysicsTriMeshData(this->IsCollision);
		mesh->CreateDynamicMaterialInstance(0, this->MaterialInterface);
	}
}

メッシュ可変色

ただ色を変えて VertexColors に入れ込むだけでは色は変わりませんでした。
VertexColor を入力にもつ Material を作成し、それを CreateDynamicMaterialInstance() で設定してあげる必要があります。
f:id:kazuki_nagasawa:20200409155405p:plain

まとめ

  • ProcedualMeshComponent で遊んでみました。
    • プログラマブルに動きのあるものを作ってみるのは面白そうです。
    • 応用例を考えるのが大変そうですが…。

OpenCV画像 + matplotlibグラフを合わせて表示する

OpenCV映像とMatplotlibグラフを一つの画面で表示させてみたい。
というのをやってみました。

経緯

OpenCVで動画を解析し、その結果をMatplotlibで表示してみたものの、
更にそれを一画面で表示してみたくなったので、それをやってみました。

OpenCVでMatplotlibのグラフの表示は出来なさそうなので、
逆で、MatplotlibでOpenCVの映像を表示する形になりました。

技術要素と参照

サンプル

サンプルとして、猫画像集めてきて、画像のヒストグラムを取ってみるものを作成してみました。
ソースコードは github。
github.com

猫画像はVisual Geometry Group - University of Oxfordを使用しています。

実行するとこんな感じ。アニメーションで順次グラフが更新されます。
あとはパディングとかオフセット設定すればきれいに見えるかと。
f:id:kazuki_nagasawa:20181212200205p:plain

まとめとか

  • GridSpec便利。
    • 今までテキトーにMatplotlib使っていたけど、しっかり使うと凄いのが (片鱗だけ) 分かった。
  • 描画コストはそれなりにかかるっぽいので、重い映像の描画には向かないっぽい。
    • そういう意味では、わざわざ表示をくっ付けなくても良いかもしれない。。

Boost.numpy 活用 〜ライフゲーム作って高速化してみる〜

Boost.numpy が使えるようになってきたので、ちょっと遊んでみました。
まず、Only Python でライフゲームを作成し、それを Boost.numpy に置き換えて速くなるかどうか試してみました。

( お遊びなので、本格的な速度評価はしてないです。 )

一応、前記事は以下。
Boost.numpy ことはじめ - 雑食性雑感雑記
Boost.numpy ことはじめ その2 (戻り値) - 雑食性雑感雑記

ライフゲームについては――

特定のルールに従ってマス目を塗り替えていくアレです。ALifeとかに詳しい内容いっぱい載ってるので割愛。

今回は、一番よく使われているであろうルールで実装しています。
・ 自分が生きているとき → 周囲生存数が 2 or 3 なら生存。それ以外は死亡。
・ 自分が死んでいるとき → 周囲生存数が 3 なら復活。

あと、オリジナルルールとして、ランダムで問答無用に復活するようになっています。
まあ、ライフゲームのルール自体は本筋じゃないので。

コード

Github 上に置いたので、詳しくはそちらを参照してください。
https://github.com/KazukiNagasawa/boostnumpy_lifegame
( ブログ書くの忘れて3ヵ月放置していたから github の投稿日時と乖離があるの… )

なお、以下 Ubuntu OS、Intel I7 マシンで実行しています。

Python 版

numpy 使ってライフゲームを実行し、OpenCV で描画したものを動画キャプチャしてみました。それをアニメーション gif 表示。
やっぱり遅いです ( gif 作成時に fps 調整している訳ではないですよ。 )

( only python )
f:id:kazuki_nagasawa:20180719130533g:plain

Boost.numpy 版

ライフゲーム実装部をすべて C++ (Boost.numpy) に移してみました。処理に違いが出ないよう、できるだけ Python 実装に寄せた形で作成しています。
同サイズの実行ですが、爆速になりました!!

( boost numpy )
f:id:kazuki_nagasawa:20180719130549g:plain

おまけ

Boost.numpy での実行が爆速になったので、調子に乗って Color 版を作ってみました。
RGB 間のつながりがなく、3つのライフゲームが RGB レイヤで重なって表示されているだけなのであまり面白くないですが…。
( 繋がり意識して3次元的なライフゲーム作ってみても面白いかも。 )

f:id:kazuki_nagasawa:20180719134249g:plain