Toshusai blog

知識の保管庫

UnityのTransfrom.Find()について

UnityのTransform.Find()について

2018/03/15, Unity2017.3.0f3
子のオブジェクトを文字列で取得するメソッド。
https://docs.unity3d.com/ScriptReference/Transform.Find.html
公式リファレンス(2017.3)では、以下のようにディレクトリのパスのように記述することで深いところにある子まで取得できると書いてあるが、この書き方では取得できなかった。

//Find the child named "ammo" of the gameobject "magazine" (magazine is a child of "gun").
            ammo = gun.transform.Find("magazine/ammo");

下のように親の名前は書かずに最初の子からのパスを書くことでちゃんと動いた。

//parent
//  -child
//    -child2
transform.Find("child/child2")

C#でフィールド名をすべて取得する

C#でフィールド名をすべて取得する

using UnityEngine;
using System.Reflection;

User user = new User();
FieldInfo[] infoArray = user.GetType().GetFields();
foreach (var info in infoArray) {
    Debug.Log(info.Name + ":" + info.GetValue(user));
}

参考

Gitの使い方まとめ

Gitの備忘録

基本的な使い方

初期化

> git init

リモートリポジトリを追加

> git remote add origin https://hogehoge
# 削除
> git remote rm otigin

コミット

> git add [file]
# 新規、変更、削除をすべて
> git add -A
# 変更、削除をすべて
> git add -u
# 新規、変更をすべて
> git add .

> git commit -m ["message"]

コミットの削除

# 作業ツリーを維持(HEADだけ)
> git reset --soft HEAD~
# 作業ツリーを維持(インデックスも)
> git reset HEAD~
# 作業ツリーも削除(全部)
> git reset --hard HEAD~

コミットの打ち消し

> git revert [commit]

プッシュ

> git push origin master

コミットの結合

> git rebase -i HEAD~3

なんとなく使い方が英語で出てくる
HEAD~3はブランチ先頭から3コミットという意味

確認系

# 変更点
> git status
# ログ
> git log
# 便利
> git log --online

参考

PythonでWebスクレイピング

PythonでWebスクレイピング

from urllib.request import urlopen
from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print "Encountered a start tag:", tag

    def handle_endtag(self, tag):
        print "Encountered an end tag :", tag

    def handle_data(self, data):
        print "Encountered some data  :", data

url = "https://hogehoge"
html = urlopen(url).read().decode("utf-8")
parser = MyHTMLParser()
parser.feed(html)

参考

BlenderのシェイプキーをUnityでブレンドシェイプとして使う

BlenderのシェイプキーをUnityでブレンドシェイプとして使う

Blenderのオブジェクトモードでシェイプキーを追加(Basicともう1つ以上) 編集モードでシェイプキーを編集したらFBXで出力してUnityにもっていくと、シェイプキーを付けたメッシュのMeshSkinRendererにBlend Shapesといパラメーターが増えているのでそれを1~100で動かすことで使える。

PHPの備忘録

PHPの備忘録

<?php
//コメントアウト
/*
複数行コメントアウト
*/
define("NAME", "VALUE");

function fizzBuzz(){
    for($i = 1; $i <= 100; $i++){
        if ($i % 3 == 0) {
            echo 'Fizz';
        }
        if ($i % 5 == 0) {
            echo 'Buzz';
        }
        if (!($i % 3 == 0) && !($i % 5 == 0)) {
            echo $i;
        }
        echo "\n";
    }
}

abstract class Vector2{
    protected $x;
    protected $y;

    function __construct($x, $y){
        $this->x = $x;
        $this->y = $y;
    }

    abstract function toString();
}

class Vector3 extends Vector2{
    private $z;

    function __construct($x = 0, $y = 0, $z = 0){
        parent::__construct($x, $y);
        $this->z = $z;
    }

    function toString(){
        //文字列の中に変数をかける
        return "($this->x, $this->y, $this->z)";
    }

    function Hoge(){
        return true;
    }
}

$origin = new Vector3();
echo $origin->toString();
//(0, 0, 0)

//文字列の変数からクラスのインスタンスを作ったり、関数を呼び出すこともできる
$vector3String = "Vector3";
$toStringString = "toString";
$one = new $vector3String(1, 1, 1);
echo $one->$toStringString();
//(1, 1, 1)


?>

WindowsでPHPをローカルで動かす

WindowsPHPをローカル環境で動かす方法

このサイトでXAMPPをダウンロードする。 www.apachefriends.org XAMPPとは最も人気のある開発環境(Apacheディストリビューション)だそうです。

ダウンロードしたら起動して ApacheのActionsのStartを押すだけ、あとはlocalhostにアクセスするとデフォルトのページが見えるはず。 位置はC\:/xampp/htdocs

参考

https://haniwaman.com/local-apache/

3SATがNP完全であることの証明

はじめに

CNF論理式とは

リテラル(論理変数またはその否定)の論理和である節の論理積からなる論理式
CNF論理式: { \displaystyle
(x_1 \lor x_2 \lor …) \land (x_1 \lor \lnot x_2 \lor …) \land …
}
リテラル: { \displaystyle
x_1, x_2, \lnot x_2
}
節: { \displaystyle
(x_1 \lor x_2 \lor ...)
}

SAT問題とは

あるCNF論理式を真にするようなリテラルの組み合わせは存在するかどうかの充足可能性問題。 { \displaystyle
(x_1 \lor x_2) \land (\lnot x_1 \lor \lnot x_2)
}
x1を真にx2を偽にすればこのCNF論理式は真になるので回答はYes。

3-SAT問題

SAT問題の中で節のリテラル数が高々3つのもの。 { \displaystyle
(\lnot x_1 \lor x_2 \lor x_3) \land (x_1 \lor \lnot x_2 \lor \lnot x_3) \land …
}

3SATがNP完全であることの証明

SATがNP完全であることはCook Levinの定理より自明である。 3SAT問題に関して、リテラルへの値の代入は多項式時間で検証できるため、3SATはNPである。
SATが多項式時間で3SATに変換できるならば3SATがNP完全であるので、これを示す。

以下のようにして3つ以上のリテラルを含むSATの各節を3SATのいくつかの新しい節の連接節に縮小する。
SATの1つの節を例とする
{\displaystyle
(x_1 \lor \lnot x_2 \lor x_3 \lor \lnot x_4 \lor x_5 \lor \lnot x_6)
}

1.対応するSATの節の最初の2つのリテラルと自由変数z1との論理和である3SATの新しい節を作る。
{\displaystyle
(x_1 \lor \lnot x_2 \lor z_1)
}

2.SATの節の次のリテラルとz1の否定と自由変数z2の論理和の節を作る。
{\displaystyle
(x_1 \lor \lnot x_2 \lor z_1) \land (x_1 \lor \lnot z_1 \lor z_2)
}

3.これをSATの節のリテラルが残り2つになるまで繰り返し、最後の2つのリテラルと最後の自由変数の否定で論理和の節を作る。
{\displaystyle
(x_1 \lor \lnot x_2 \lor z_1) \land (x_3 \lor \lnot z_1 \lor z_2) \land (\lnot x_4 \lor \lnot z_2 \lor z_3) \land (x_5 \lor \lnot x_6 \lor \land \lnot z_3)
}

4.1から3ををSATの各節に対して行い新しい3SAT問題を作成する。

このSAT3への変換は元のSATがm個(m>3)のリテラルを含む場合、変換されたSAT3はm-2の節を持ち、m-3の自由変数を持つ。 したがってこの変換は多項式時間、多項式空間でできる。

次に、この変換を行った際、問題の真偽が変わらないことを示す。
{\displaystyle I} あるSATが充足可能のとき、対応する3SATも充足可能である。

SATはすべての節が同時に真と評価されるときに充足可能である。SATの節が真のとき、少なくとも1つのリテラルは真である。したがって、対応する3SATでは、その真であるリテラルを含む節は真であり、3SARTのその節の自由変数を偽にすることで隣の3CNF節を真にすることができる。よってすべての3CNF節を真にするように変数を変えることができる。

{\displaystyle I\hspace{-1pt}I} 3SATが充足可能なとき、対応するSATも充足可能である。

3SATのすべての節は真になる。これはSATのすべての節に少なくとも1つ真であるリテラルが存在する場合である。SATのある節のすべてのリテラルが偽の場合、3SATの自由変数に関わらず節を真にすることができないため。よってSATの節のリテラルは少なくとも1つ真であり、対応するSAT節を真にする。

{\displaystyle I}, {\displaystyle I\hspace{-1pt}I}より、3SAT問題はNP完全である。

【JavaScript】MNISTデータベース(バイナリファイル)を扱う

はじめに

JavaScriptでバイナリデータを扱いたい。 MNISTデータベースの教師用画像をJavaScriptcanvasを用いて表示する。 Qiitaから移行。

MNISTデータセットの入手

よく手書き文字認識に使われるデータベース http://yann.lecun.com/exdb/mnist/ ここからダウンロードできる。 今回は教師画像データの"train-images-idx3-ubyte.gz"をダウンロードして使う。 Windowsコマンドプロンプトgzip -d train-images-idx3-ubyte.gz で展開するとtrain-images-idx3-ubyteが出てくる。

上記のサイトに中身のフォーマットが書いてある

[offset] [type] [value] [description]
0000 32 bit integer 0x00000803(2051) magic number
0004 32 bit integer 60000 number of images
0008 32 bit integer 28 number of rows
0012 32 bit integer 28 number of columns
0016 unsigned byte ?? pixel
0017 unsigned byte ?? pixel
........
xxxx unsigned byte ?? pixel
Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).

HTMLを用意する

ファイルを読み込むためにinputを用意しておく。 今回はcanvasを使うのであらかじめhtmlにcanvasも用意しておく。 bodyは最低限でこんな感じ。

<body>
    <input type="file" id="imgfile">
    <br/>
    <canvas id="canvas"></canvas>
    <script type="text/javascript" src="main.js"></script>
</body>

バイナリデータを扱いやすく変換する

バイナリファイルを読み込むときに使うのがDataViewオブジェクト

new DataView(buffer [, byteOffset [, byteLength]]) DataView.prototype.getUint8() ビューの開始位置から指定されたバイト単位のオフセットで符号無し8ビット整数値(unsigned byte) を取得します。。 DataView.prototype.getInt32() ビューの開始位置からの指定されたバイト単位のオフセットで符号あり32ビット整数値(long)を取得します。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/DataView

画像の枚数、高さ、幅をフォーマットに従って取得してみる。

var input = document.getElementById("imgfile");           //input要素を取得
input.addEventListener("change", function(event){         //input要素に変更があったら発火
    var file = event.target.files;                        //FileListオブジェクトを取得
    var binaryReader = new FileReader();                  //FileReaderオブジェクトを生成
    binaryReader.readAsArrayBuffer(file[0]);              //ファイルをArrayBufferオブジェクトとして格納
    
    binaryReader.onload = function(){                     //読み込みが成功したら発火
        var dataView = new DataView(binaryReader.result); //DataViewオブジェクトを生成
        var number_of_images = dataView.getInt32(4);      //画像の枚数を取得
        var row = dataView.getInt32(8);                   //画像の行数(高さ)を取得
        var column = dataView.getInt32(12);               //画像の列数(幅)を取得
        console.log(number_of_images, row, column);       //確認用に出力
    }
}

無事60000 28 28と出力された。 それでは画像1枚1枚のデータを取得していきたい。 1枚の画像は28x28の784個のunsigned byte(0-255)で構成されるので、 要素が784個の1次元配列を60000個の、[60000, 784]の2次元配列で6万の画像を扱う。 DataViewからその配列に変換する関数を作成した。

var mnistToImageArray = function(dataView, number_of_images, row, column){
    var images = [];
    var offset = 0;
    for(var i = 0; i < number_of_images; i++){
        images.push([]);                                        //画像データを入れる1次元配列を挿入
        for(var j = 0; j < row; j++){
            for(var k = 0; k < column; k++){
                images[i].push(dataView.getUint8(16 + offset)); //Uint8を入れていく
                offset++;                                       //offsetを次へ
            }
        }
    }
    return images;
}

画像をcanvasに描画する

まずはcanvas、contextを取得してImageDataオブジェクトを作る。 ImageDataとは

ImageData インターフェイスは、 要素の領域の基礎をなすピクセルデータを表します。ImageData() コンストラクターや、canvas に関連付けられた CanvasRenderingContext2D オブジェクトの createImageData() メソッドおよび getImageData() メソッドによって生成されます。ImageData は putImageData() メソッドの第 1 引数として利用可能です。

ImageData.data 読取専用 RGBA の順で 0 から 255 の間の整数 (両端の値を含む) を並べたデータを持つ 1 次元配列を表す Uint8ClampedArray です。

https://developer.mozilla.org/ja/docs/Web/API/ImageData

canvasを28x28に変えてからImageDataオブジェクトを作る。

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.width = column;
canvas.height = row;
var dstData = ctx.createImageData(canvas.width, canvas.height);

作ったImageDataを先ほど1次元配列にした画像データを入れる関数を作る。 1次元配列なので2重ループでのインデックスに注意。 MNISTのデータは0を黒、255を白にしているので注意。

var dataArrayToImageData = function(dataArray, imageData){  //dataArray->独自に変換した画像データ
    for(var i = 0; i < imageData.height; i++){
        for(var j = 0; j < imageData.width; j++){
            var idx = i + j * imageData.width;               //1次元配列の適したインデックスを指すように変数を作る
            var idx2 = idx * 4;                              //ImageData.dataはRGBAの情報があるのでインデックスを4倍する
            imageData.data[idx2] = 255 - dataArray[idx];     //白黒反転させる
            imageData.data[idx2 + 1] = 255 - dataArray[idx]; //RGBに同じ値を入れる
            imageData.data[idx2 + 2] = 255 - dataArray[idx];
            imageData.data[idx2 + 3] = 255;                  //不透明度は最大
        }
    }
}

ここまできたら今までのを全部まとめて、ctx.putImageData()でImageDataをcanvasに入れるだけ。 最終的なソースがこれ。

var mnistToImageArray = function(dataView, number_of_images, row, column){
    var images = [];
    var offset = 0;
    for(var i = 0; i < number_of_images; i++){
        images.push([]);                                        //画像データを入れる1次元配列を挿入
        for(var j = 0; j < row; j++){
            for(var k = 0; k < column; k++){
                images[i].push(dataView.getUint8(16 + offset)); //Uint8を入れていく
                offset++;                                       //offsetを次へ
            }
        }
    }
    return images;
}

var dataArrayToImageData = function(dataArray, imageData){  //dataArray->独自に変換した画像データ
    for(var i = 0; i < imageData.height; i++){
        for(var j = 0; j < imageData.width; j++){
            var idx = i + j * imageData.width;               //1次元配列の適したインデックスを指すように変数を作る
            var idx2 = idx * 4;                              //ImageData.dataはRGBAの情報があるのでインデックスを4倍する
            imageData.data[idx2] = 255 - dataArray[idx];     //白黒反転させる
            imageData.data[idx2 + 1] = 255 - dataArray[idx]; //RGBに同じ値を入れる
            imageData.data[idx2 + 2] = 255 - dataArray[idx];
            imageData.data[idx2 + 3] = 255;                  //不透明度は最大
        }
    }
}

var input = document.getElementById("imgfile");           //input要素を取得
input.addEventListener("change", function(event){         //input要素に変更があったら発火
    var file = event.target.files;                        //FileListオブジェクトを取得
    var binaryReader = new FileReader();                  //FileReaderオブジェクトを生成
    binaryReader.readAsArrayBuffer(file[0]);              //ファイルをArrayBufferオブジェクトとして格納
    
    binaryReader.onload = function(){                     //読み込みが成功したら発火
        var dataView = new DataView(binaryReader.result); //DataViewオブジェクトを生成
        var number_of_images = dataView.getInt32(4);      //画像の枚数を取得
        var row = dataView.getInt32(8);                   //画像の行数(高さ)を取得
        var column = dataView.getInt32(12);               //画像の列数(幅)を取得
      
        var canvas = document.getElementById("canvas");
        var ctx = canvas.getContext("2d");
        canvas.width = column;
        canvas.height = row;
        var dstData = ctx.createImageData(canvas.width, canvas.height);
        
        var images = mnistToImageArray(dataView, number_of_images, row, column);
        dataArrayToImageData(images[0], dstData);
        ctx.putImageData(dstData, 0, 0);
    }
}, false);

最後に

Deep Learningを勉強していてMNISTデータベースを使った手書き文字認識というのが最初にでてきたので、やってみようと思った。 Deep Learningの主流はpythonで、pythonでモジュールを使って楽にMINSTデータベースを扱えるらしいが、なんとなくJavaScriptで書いてみることにした。。

【AtCoder】ABC072C解説

時間制限 : 2sec / メモリ制限 : 256MB

配点 : 300 点

問題文 長さ N の整数列 a1,a2,…,aN が与えられます。

各 1≤i≤N に対し、ai に 1 足すか、1 引くか、なにもしないかの三つの操作からどれか一つを選んで行います。

この操作の後、ある整数 X を選んで、ai=X となる i の個数を数えます。

うまく操作を行い、X を選ぶことで、この個数を最大化してください。

制約
1≤N≤105
0≤ai<105(1≤i≤N)
ai は整数
入力
入力は以下の形式で標準入力から与えられる。

N a1 a2 .. aN 出力 うまく操作を行い、X を選んだ時の ai=X なる i の個数の最大値を出力せよ。

自分の考え

1降順ソートしてから先頭から見る
2今のai+2までのaiの個数を数える
3aiの連続が途切れる度2を繰り返す
これで2の最大が出力になる。 ちょっと上手く説明できないのでコードで。

n = int(input())
a_n = list(map(int, input().split()))
a_n.sort()
a_n.reverse()
def check(idx):
    global a_n
    base = a_n[idx]
    count = 0
    dif = 0
    while dif <= 2:
        idx += 1
        count += 1
        if(idx == n):
            return count
        dif = base - a_n[idx]
    return count
 
ans = 1
i = 0
c = check(i)
tmp = a_n[0]
while(i != n):
    if(tmp != a_n[i]):
        tmp = a_n[i]
        c = check(i)
    if(ans < c):
        ans = c
    i += 1
print(ans)

なんとも冗長なコードになってしまった。 ここでコード長最短ACの方のコードを見てみよう。

c=[0]*10**5;input()
for i in map(int,input().split()):
    c[i]+=1
print(max([sum(c[i:i+3]) for i in range(10**5)]))

たった4行である。 入力の最大個数である105の長さの配列を宣言し、入力のそれぞれの値に対応するインデックスにその値がいくつあるのかカウントしている。 最後に3つごとにスライスし、その合計の最大を出力する。 非常に美しいコードだ。

メモ

Pythonでセミコロンを使うと1行に2行分書けることを初めて知った。最初の入力Nは使う必要がないので、最初の行の後ろに書いたのだろう。

Deep Learning入門概略

はじめに

オライリー・ジャパンのゼロから作るDeep Learning」を読んで忘れないうちに備忘録的にディープラーニングの教師ありの分類問題についてまとめていきます。 隠れ層が1層の2層ニューラルネットワークを数値微分を使って勾配を求めて学習する部分までまとめます。

パーセプトロン

パーセプトロンとはニューラルネットワークディープラーニング)の起源となるアルゴリズム。 複数の入力を受け、1つの出力をする。 出力は0か1だけ、例えば、二つの入力の合計がθ(閾値)以下なら0、それ以外なら1を返す関数。


\begin{cases}
    y = 0 (x_1 + x_2 \leq \theta)\\
    y = 1 (x_1 + x_2 > \theta)
\end{cases}

ニューラルネットワーク

ニューラルネットワークでは複数の入力にそれぞれ重みをかけて、その上にバイアスを足して、活性化関数によって変換したものを出力します。 文字にすると分かりにくいので数式で表すと。


y = h(w_1x_1 + w_2x_2 + b)\\
h(x) =
\begin{cases}
0 \, (x \leq 0)\\
1 \, (x > 0)
\end{cases}
w1, w2は重み、bがバイアス、hが活性化関数です。

これを層にすることでニューラルネットワークを構築します。 入力層、中間層、出力層に分けられて、それぞれにいくつものノードが存在します。上記の入力、出力を繰り返して信号を伝達していきます。

分類問題を学習するには、入力と出力のデータ(教師データ)を用意します。 ニューラルネットワークに入力し、その出力と教師データの正解との誤差を求め、重み、バイアスを調整して、ニューラルネットワークの出力を正解に近づける。 これを繰り返して、分類問題を学習します。

行列

ニューラルネットワークの計算は行列を使ってまとめて行うことができます。


\mathbb{Y} = \mathbb{X}\mathbb{W} + \mathbb{B}
内積の計算を行うので、重みWの行数は入力Xの列数である必要があり、バイアスBは重みの列数と同じ次元である必要があります。

活性化関数

活性化関数が実際にどういった関数か、今回使う活性化関数を紹介します。

シグモイド関数

h(x) = \frac{1}{1 + e^{-x}}
グラフと書くと分かりますが、0から1の実数を出力する単調増加関数です。

ソフトマックス関数

y_k = \frac{e^{a_k}}{\sum_{i=1}^n e^{a_i}}

シグモイド関数と同様に、0から1の実数を出力します。この値は確率として解釈でき、多クラス分類に使うことができます。 例えば出力が(0.1, 0.1, 0.2, 0.6)であれば4番目のクラスy4の確率は60%と言えます。

損失関数

損失関数とは簡単に言えば誤差を求める関数です。 yはニューラルネットワークの出力、tは教師データとします。
2乗和誤差

E=\frac{1}{2}\sum {(y_k-t_k)}^2

交差エントロピー誤差

E=-\sum t_k\log{y_k}

勾配

勾配とは、ある関数のすべての変数の偏微分をベクトルとしてまとめたものです。 関数f(x, y)なら勾配は


(\frac{\partial f}{\partial x},
\frac{\partial f}{\partial y})

です。 損失関数の勾配を求めれば、勾配はその点において、最も変化量の多い方向を指します。 これを用いて、誤差が最も少なくなる方向に重み、バイアスを変更します。 ※勾配はあくまでその点において、極小値、鞍点を探すだけで、必ずその関数の最小値を指すわけではない。

数値微分

勾配を求めるために偏微分を使いたいが、微分はxの小さい変化(限りなく0に近い)によって関数f(x)がどれだけ変化するというものです。 プログラムでは限りなく0に近い値は表せないので、実際に0近い値だけxを変化させて微分を求めることを数値微分と言います。 丸め誤差で省略されない程度に、例えば変化量を0.0001として

\frac{f(x + 0.0001) - f(x)}{0.0001}
のように求めます。

【Python】音声を離散フーリエ変換して周波数帯ごとにビジュアライズする

はじめに

toshusai.hatenablog.com

この記事の時点で音声に関してあやふやだったことが多いので改めて書くことにした。今回はwavファイルを読み込んでフーリエ変換し、周波数ごとに振幅を表示したい。

wavファイルを読み込む

import wave
import numpy as np
#Wave_readオブジェクトとして得る
wf = wave.open("440Hz.wav", "rb")
#チャンネルの番号、1:モノラル、2:ステレオ
channels_no = wf.getnchannels()
#サンプルサイズ、2ならバイト?
sample_width = wf.getsampwidth()
#サンプリング周波数
sampling_frequency = wf.getframerate()
#全体のフレーム数
frames = wf.getnframes()

#bytesオブジェクトとして最大[引数]フレーム取得する。
buf = wf.readframes(wf.getnframes())
data = np.frombuffer(buf, dtype = "int16")
#ステレオだとdataには[左, 右, 左, 右, ...]のように入っているので、スライスして右だけ取り出す。別に左でもいいけどなんとなく。
if(channels_no == 2):
    data = data[::2]

print("Channel No:", channels_no)
print("Sample Width:", sample_width)
print("Sampling Frequency:", sampling_frequency)
print("Frames:", frames)
"""Standart output
Channel No: 1
Sample Width: 2
Sampling Frequency: 44100
Frames: 220500
"""

周波数ごとに表示する

import os
import time
#コンソールクリアのラムダ式
cclear = lambda : os.system('cls' if os.name=='nt' else 'clear')
#表示する行数
line = 50
#離散フーリエするデータの幅(1/24秒)
delta = int(sampling_frequency / 24)
fft_data = []
for i in range(int(len(data) / delta)):
    #deltaでスライスして高速離散フーリエ変換
    fft_data.append(np.fft.fft(data[i * delta:(i + 1) * delta]))
    #周波数帯ごとに振幅を表示する。
    for j in range(line):
        #まとめる周波数帯をスライスで取得
        width = fft_data[i][j * int(delta / line):(j + 1) * int(delta / line)]
        #振幅の絶対値の平均をとる
        s = 0
        for w in width:
            s += abs(w)
        y = s / len(width)
        #大きすぎるので小さくするため定数で割る
        length = int(y / 30000)
        #それでも大きい場合制限する
        if(length > 100):
            length = 100
        #その周波数帯の先頭と振幅を視覚的に表示する
        print({0:05d}.format(int(j * delta / line * 24)) + "*" * length))
    #0.2秒待機
    time.sleep(0.2)
    #コンソールクリア
    cclear()

実行結果

440Hzが流れるwavファイルを読み込んだ様子
f:id:toshusai:20171114234952g:plain 10kHzが流れるwavファイルを読み込んだ様子
f:id:toshusai:20171114235007g:plain 2つ目のfor文の中のdeltaを100とかにすれば100区切りで表示できる。

参考

PythonでWAVファイルを読み込む - 音楽プログラミングの超入門(仮)

22.4. wave — Read and write WAV files — Python 3.6.3 documentation

Blenderで音に合わせてキーフレームを打つ(Blender API)

Blenderで音に合わせてキーフレームを打つ(Blender API

Pythonで音声ファイル(wav)を読み込んで、それに合わせてBlenderにあるオブジェクトの大きさを変えてアニメーションさせたらきっとかっこいいと思ったので。(あとから知ったのだが、Blenderは音声からキーフレームに変える機能がついているらしい)

Blender APIとは

Blenderの操作をスクリプトから操作することができるもの。Pythonで使うことができる。いつか入門の記事を書きたい。 https://docs.blender.org/api/current/

Pythonからキーフレームを打つ

1フレームに位置のキーフレームを打ちたかったら、下のように書く。

import bpy
obj = bpy.context.object
# set the keyframe at frame 1
obj.location = 3.0, 4.0, 10.0
obj.keyframe_insert(data_path="location", frame=1)

メソッドの説明

keyframe_insert(data_path, index=-1, frame=bpy.context.scene.frame_current, group="") Insert a keyframe on the property given, adding fcurves and animation data when necessary.
Parameters:
data_path (string) – path to the property to key, analogous to the fcurve’s data path.
index (int) – array index of the property to key. Defaults to -1 which will key all indices or a single channel if the property is not an array.
frame (float) – The frame on which the keyframe is inserted, defaulting to the current frame.
group (str) – The name of the group the F-Curve should be added to if it doesn’t exist yet.
options –
Optional flags:
INSERTKEY_NEEDED Only insert keyframes where they’re needed in the relevant F-Curves.
INSERTKEY_VISUAL Insert keyframes based on ‘visual transforms’.
INSERTKEY_XYZ_TO_RGB Color for newly added transformation F-Curves (Location, Rotation, Scale)
and also Color is based on the transform axis.
Returns:
Success of keyframe insertion.
Return type:
boolean

Blenderにオブジェクトを追加する

Blender起動時にあるキューブを使ってもいいが、今回は新しくキューブを作って、それにアニメーションを適用させる。
1行目で生成し、2行目でシーンでアクティブにする。

bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
obj = bpy.context.scene.objects.active

wavから波形データをアニメーションに移す

以前の記事でやった配列を使ってキーフレームを打っていくだけ。取れるデータは大きいので、スケールの倍率は配列の中の最大値で割って正規化する。今回使った音声データは3分ほどあるので、最初の250フレームだけキーを打った。

import wave
import numpy as np
import logging
file = "絶対パス"
wf = wave.open(file, "r")
buf = wf.readframes(wf.getnframes())
data = np.frombuffer(buf, dtype = "int16")
frame = wf.getnframes()
rate = wf.getframerate()
vframe = int(rate / 24)
vdata = []

for i in range(int(frame / vframe)):
    vdata.append(data[i * vframe * 2])
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
obj = bpy.context.scene.objects.active
vdata_max = max(vdata)
for i in range(250):
    obj.keyframe_insert( data_path='scale', frame=i)
    tmp = vdata[i] / vdata_max
    obj.scale = (tmp, tmp, tmp)

実行するとキーフレームが自動的に打たれる。

参考

【C#】ファイルの読み書き

はじめに

ただの簡単なファイルへの書き込み、読み込みのやり方

System.IO.StreamReader, StreamWriterを使う

StreamWriterのコンストラクタの第2引数はtrueで追記、falseで上書き。コンストラクタはいっぱいオーバーロードされてるので詳しくはリファレンスを。

append
Type: System.Boolean
true to append data to the file; false to overwrite the file. If the specified file does not exist, this parameter has no effect, and the constructor creates a new file.

using System.IO;
public class Test {
    static string FILE_NAME = "test.txt";
    public static void Main () {
        using (StreamWriter sw = new StreamWriter(FILE_NAME, false)){
            sw.WriteLine("Test");
        }

        if (File.Exists (Application.dataPath + FILE_NAME)) {
            using (StreamReader sr = File.OpenText (FILE_NAME)) {
                                Console.WriteLine(sr.ReadLine ());//1行
                    //Console.WriteLine(sr.ReadToEnd ());//全部
                                
            }
        }
    }
}

1行づつ扱いたかったら

while( (line = sr.ReadLine()) != null) {
        Console.WriteLine(line);
}

usingについて

ファイルストリームは開いているときロックされて他からアクセスできないので、閉じる必要がある。基本的に関数の中の変数などが参照されなくなったら自動的にガベージ・コレクションが行われるが、そのタイミングは参照されなくなった時点ではないので、ファイルストリームなどの場合は明示的に解放する必要がある。StreamReaderにはClose()メソッドがあり、使い終わったらClose()すればいいのだが、リソースを解放するためにDispose()というメソッドが実装されている(Close()は破棄するわけではない、詳しく参考を。)。そこで、Dispose()を自動的にやってくれるのがusing構文という訳だ。以下の2つのプログラムは同じことだ。

using (Font font1 = new Font("Arial", 10.0f)) 
{
    byte charset = font1.GdiCharSet;
}
{
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}

参考

File クラス (System.IO)

using Statement (C# Reference) | Microsoft Docs

IDisposable インターフェイス (System)

StreamWriter コンストラクター (String, Boolean) (System.IO)

.NET TIPS ガベージ・コレクタを明示的に動作させるには? - C# VB.NET - @IT

ファイル操作 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C

C# Tips −usingを使え、使えったら使え(^^)−

c# - close or dispose - Stack Overflow