2020年10月7日水曜日

電流帰還アンプを作る

おじさん、LM380非革命アンプとLM386革命アンプの基板を注文したのですが、到着までに3週間程度かかるみたいで、後に注文したLM380非革命アンプの基板がまだ届きません。

一回も回路のミスをしなくても、基板をつくって、部品を購入して、はんだ付けをし、動作確認するまで1か月以上かかるようです。

回路図をミスってもう一度作ると二か月かかるのかぁ。


LM386の革命アンプしか作ってないけど、革命アンプシリーズは音がよくてびっくりしました。

あまり、大きい音が出せないマンション暮らしのおじさんには1W程度の出力のLM380の子分のLM386で十分です。

いろいろ調べてみると、なんと、革命アンプより音が良いらしい、電流帰還アンプというものがあるらしい。

それはさっそく作ってみなければ。

おじさん、「LM386とLM380さえあればなんでもできる」と思っているので、本日はLM380とLM386で電流帰還アンプを作ってみたいと思います。

http://c3plamo.blog.fc2.com/blog-entry-1868.html


http://settembre21x.blogspot.com/2014/09/lm380.html


世の中にはすごい人がたくさんいまして、もうとっくにLM380とLM386で回路図をシミュレーションしている人がいるので、そのまま基板を作って注文するだけ簡単に高性能アンプが作れます。


本当に革命アンプより音がいいのかなぁ。


LM380電流帰還アンプ回路図

LM380電流帰還アンプ配線図

LM380電流帰還アンプKicadデータ

LM386電流帰還アンプ回路図
LM386電流帰還アンプ回路図




LM386電流帰還アンプKicadデータ

C基板サイズよりちょっと大きめの、50mm*80mmのブレッドボートサイズの基板できりきり入る大きさ。
この大きさで作ればミント缶とかいろいろな小型ケースに収まります。
高さ的にミント缶には絶対入らないのですが、おじさん意地でもこのブレッドボートサイズに詰め込みます。

結果がわかるのは一か月後。

2020年9月23日水曜日

レールスプリッター

おじさん、最近ヘッドフォンアンプを作ろうとしてレールスプリッターではまってしまいました。


オペアンプのICを使おうとするときに最初につまづく負の電圧源。

よくオペアンプの教科書に+5Vと-5Vを作らないといけないなんて書かれています。

おじさんも大学生の時にWebなんてなかったから、どうやって負の電圧を作ればいいのか何か月もなやんだしなぁ。

この負の電圧を仮想的に簡単に作るのがレールスプリッター。


https://nabe.adiary.jp/vgnd


レールスプリッターをつかうと、オペアンプのICがACアダプターなど単一電源で使えます。こんなに便利な回路なのですが、なぜか「定本 opアンプ回路の設計」の本とかにもレールスプリッターのことが書かれていません。なんで?


こんな感じで不安定なレールスプリッターでもよいんですが、

ネットに落ちている回路図はこんなかんじでTIのTLE2426のICを使うものが多いです。

しかしここ数か月、秋月電子でなぜかTIのICが売っていません。


【質問】TI社の半導体製品が生産終了でないのに在庫限りです。どうしてですか?/TIのIC単体を購入できませんがどうしてですか?

[2020/07/21 15:13:44]

【回答】メーカーの方針によりエンドユーザーを特定できない流通経路での販売が禁止されたため、今後の入荷の予定はございません。ご迷惑とご心配をおかけして申し訳ございません。何卒ご理解の程お願い申し上げます。なお、互換品がございましたら是非情報をお寄せください。


ChinaPostから届く、電子工作のキットとかレギュレーターICとかオペアンプとかって結構偽物の部品が届くしなぁ。ダイソーとかで売っている電気小物の回路図も必ずどこか間違えてるし。TIも信頼できるところにしか自分たちの部品を売らないらしい。

米中貿易戦争の影響がこんなところにも来ているのか。


でもレギュレーターもオペアンプもロジックICもオーディオアンプもほとんどのアナログICってTIじゃないか!

なんで正規品が売っている店で正規品が売れなくなるんだよ!

どうやってTIのICを使わずにオペアンプの電源を作ればいいんだよ!

そもそもオペアンプの電源を作るのにオペアンプがいるってなんで??

などいろいろな疑問がたくさんわいてきます。


世の中にはすごい方がいらっしゃいまして、もうとっくにTIのICを使わない、高性能のレールスプリッターを作っている方がいらっしゃいました。しかも、安いし高出力だし、かなりすごい。


http://settembre21x.blogspot.com/2016/05/njm386lm3866v.html


おじさんも大学時代にLM38xがあれば、負の電圧も作れるし、増幅もできるからオペアンプいらないじゃんと思っていたんですよね。

LM386だとTIだけじゃなく新日本無線とか台湾メーカーからも出てるICなので、もうTIのICなんか使わない。



2020年9月10日木曜日

LM380非革命アンプとLM386革命アンプを作る

おじさんみたいなエンジニアは年を取ってくるとアンプを作りたくなるみたいです。

なんでみんなじじいになるとアンプをつくるんだろう?

おじさん、前回のUSB切替機を作ったときに、Kicadの使い方と格安プリント基板の発注方法がわかったので、調子にのってアンプの基板も作ってみました。


以前このブログで紹介した、LM386のICを使った「LM386革命アンプ」とLM380のICを使った「LM380非革命アンプ」の二種類の基板を作ってみました。


LM380非革命アンプ

http://settembre21x.blogspot.com/2013/08/lm380.html


LM386革命アンプ

http://settembre21x.blogspot.com/2013/09/lm386.html


このサイトの情報をもとに基板を作ります。

ユニバーサル基板でアンプを作るととてもめんどくさいので、このプリント基板を欲しいと思っている人、結構いると思うんだよね。

おじさん、人類の資産である回路図と基板データを全部デジタル化。

少しでも世の中のためになればと思っています。


オリジナルと同じように、ユニバーサル基板風に配線するのか、プロっぽくべたグランドにして鋭角にならないように配線するのか迷う。

とりあえず、実験しやすいように、コネクターはACアダプター、3.5mmジャック、ターミナルブロック、配線はユニバーサル基板風にしてみました。


LM380非革命アンプ回路図

LM380非革命アンプ基板

LM380非革命アンプKicadデータ


LM386革命アンプ回路図
LM386革命アンプ基板

いつも、もう少しきちんと作り方をまとめたり、基板の配線すればいいかなぁと思っているのですが、おじさん8割くらい完成するとそこでやる気がなくなっちゃうのです。

秋月電子で革命アンプキットが売れるんじゃないかと思っているのですが、やっぱうれないのかなぁ。


2020年9月8日火曜日

USB切り替え機を作る

 家からのテレワークではどうしてもできない、PCのUSBの抜き差し。

おじさん、このUSBの抜き差しさえどうにかできれば、会社に行かなくてもよいのに・・・・。

USB切り替え機ってちゃんと規格を守って作ると大変なのか、なぜか売っていません。

これ前からずっとほしいのに誰も作ってくれない・・・・・。

なので自分でUSB切り替え基板を作ってみました。


https://akizukidenshi.com/catalog/g/gI-11516/

TLP241というフォトカプラはON時の抵抗が超低抵抗の0.1オームなので、これを使うと小信号系のものは大体何でもON/OFF制御できます。

こんな感じでフォトカプラをアナログスイッチの代わりで使えばよいんですね。



おじさん不器用だし老眼なので、秋月電子で売っているDIPサイズの部品しかはんだ付け付けできません。

知恵を振り絞り、秋月電子で売っている部品だけで回路を作ります。

とりあえず回路図と基板はできた。

kicadのデータはこちら

https://drive.google.com/file/d/1vQ7F435evCeMMdOw_tbZDoyq0ELVTZia/view?usp=sharing


FusionPCBに基板を発注します。

小さい基板ならば10枚5ドルで作れるらしい。

めっちゃ安い。

でも基板は5ドルでできるんですが、送料が15ドルって・・・。


Kicadで秋月電子で売っている部品だけで回路図を作ったり、アートワークする方がはるかに時間がかかりコストが高いので、「秋月電子の部品だけで作るKicadデータ集」みたいなものを公開しているサイトとかないのかなぁ。



2020年8月18日火曜日

C言語の標準ライブラリだけでMNISTをつくる

前回、TensorFlowのC++APIでMNISTを作ったときに気づいたのですが、MNISTってほとんどがPythonで書かれていて、C言語で書かれたもはほとんどみあたりません。

MNISTって簡単だからC言語の標準ライブラリ関数だけでかけるんじゃない?

おじさん、世の中にないものは作りたくなってしまう・・・。


大学生とかが課題ででそうだけどなぁ。

数時間でできそうなので、C言語で書かれたシンプルなMNISTを作ってみました。


コードはこちら。

https://github.com/yomei-o/Tiny-MNIST


---------------------------

[train 100] 0.610000

[train 200] 0.720000

[train 300] 0.810000

[train 400] 0.860000

[train 500] 0.900000

[train 600] 0.890000

[train 700] 0.830000

[train 800] 0.900000

[train 900] 0.880000

[train 1000] 0.930000

[train 1100] 0.920000

[train 1200] 0.920000

[train 1300] 0.890000

[train 1400] 0.910000

[train 1500] 0.840000

[train 1600] 0.860000

[train 1700] 0.930000

[train 1800] 0.930000

[train 1900] 0.870000

[train 2000] 0.960000

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------11111111----------

----------------------------------111111111111----------

--------------------------1111111111111111--------------

----------------111111111111111111111111----------------

--------------111111111111111111111111------------------

--------------111111111111----11111111------------------

----------------------------11111111--------------------

----------------------------11111111--------------------

--------------------------11111111----------------------

--------------------------111111------------------------

------------------------111111--11----------------------

----------------11111111111111111111--------------------

----------------11111111111111111111--------------------

------------------1111111111----------------------------

--------------------111111------------------------------

--------------------111111------------------------------

--------------------111111------------------------------

------------------111111--------------------------------

------------------111111--------------------------------

------------------1111----------------------------------

--------------------------------------------------------

--------------------------------------------------------


0: 0.000501

1: 0.160337

2: 0.023977

3: 0.000703

4: 0.005110

5: 0.005704

6: 0.002674

7: 0.715114

8: 0.043781

9: 0.042101

------------------------


なんかできた。

学習がめんどくさいので2000回くらいしかやっていませんが、10000回くらいやればもっと正解率が上がるはず。

C言語でTensorflowとか使わなくてもMNISTって簡単に作れんだね。



2020年8月15日土曜日

TensorflowのC++APIで学習をする

ここ数日暑い、暑すぎる。

あまりにも外が暑いので家でプログラムをします。


TensorflowのC++APIはあまりマニュアルが整備されていないため、ソースコードがほとんどありません。

その中でも特にC++APIを使った学習に関するサンプルはGoogleが作った簡単なもの以外まったくない。

一番ありそうなMNISTの学習サンプルもないのです。

なんで?


世の中でまだ誰もTensorflowのC++APIを使ったMNISTの学習サンプルがないので作ってみました。


作っている途中でわかったのですが、TensorflowのC++APIにはオプティマイザーとか最急降下法の便利な関数がありません。

手動でネットワークの微分とかバックプロパゲーションをしないといけないみたいです。

だからMNISTのC++の学習サンプルがないのか!


微分とか言っても高校生でできる程度の簡単なものだし、行列の演算も内積と四則演算と転置くらいしかありません。

バックプロパゲーションって結果を使って計算するだけならばそんな難しくなです。


バックプロパゲーションを理解する

https://postd.cc/2015-08-backprop/


いまさらMNIST分類器を自前実装する

https://qiita.com/soramimi_jp/items/d557d2d7adf8cc555d8b


上記のサイトを参考にしてTensorflowのC++APIでMNISTを実装してみました。


全ソースコードはこちら

https://drive.google.com/file/d/1bqBK5B8xTR0K_0C1z7yOg5yKk-teq-9I/view?usp=sharing

-----------------------------

#define NOMINMAX

#define WIN32_LEAN_AND_MEAN


#include <iostream>

#include <fstream>

#include <vector>

#include "mnistutil.h"


#include <tensorflow_cc_client_client_session.h>

#include <tensorflow_cc_ops_standard_ops.h>

#include <tensorflow_core_framework_tensor.h>

#include <tensorflow_cc_framework_gradients.h>

#include <tensorflow_core_platform_init_main.h>


#ifdef _MSC_VER

#pragma comment(lib,"ws2_32.lib")

#endif


#define tf tensorflow



void print_tensor(const char* tt,tf::Tensor& out)

{

int sz,i,w=1,h;

printf("%s\n",tt);

sz = out.dims();

printf("dims()=%d\n", sz);

for (i = 0; i < sz; i++) {

w *= out.dim_size(i);

printf("dim_size(%d)=%d\n",i,out.dim_size(i));

}

if (w > 10)w = 10;

for (i = 0; i < w; i++) {

float* p = (float*)out.data();

printf("data(%d)=%f \n", i, p[i]);

}

if (sz > 1) {

h = out.dim_size(0);

if (h > 10)h = 10;


for (i = 1; i < h; i++) {

float* p = (float*)out.data();

printf("data(%d)=%f \n", i * out.dim_size(1), p[i * out.dim_size(1)]);

}

}

printf("\n");

}



int main(int argc, char** argv)

{

int batch_num = 100;

int epoch_num = 10000;

float rate = 0.1;

int i;


tf::Scope root=tf::Scope::NewRootScope();

tf::ClientSession session{ root };


tf::Output x = tf::ops::Placeholder(root, tf::DataType::DT_FLOAT);

tf::Output t = tf::ops::Placeholder(root, tf::DataType::DT_FLOAT);


tf::Output w1 = tf::ops::Variable(root, {784,50}, tf::DataType::DT_FLOAT);

auto assign_w1 = tf::ops::Assign(root, w1, tf::ops::RandomNormal(root, { 784,50 }, tf::DataType::DT_FLOAT));


tf::Output b1 = tf::ops::Variable(root, { 50 }, tf::DataType::DT_FLOAT);

auto assign_b1 = tf::ops::Assign(root, b1, tf::ops::RandomNormal(root, { 50 }, tf::DataType::DT_FLOAT));


tf::Output w2 = tf::ops::Variable(root, { 50,10 }, tf::DataType::DT_FLOAT);

auto assign_w2 = tf::ops::Assign(root, w2, tf::ops::RandomNormal(root, { 50,10 }, tf::DataType::DT_FLOAT));


tf::Output b2 = tf::ops::Variable(root, { 10 }, tf::DataType::DT_FLOAT);

auto assign_b2 = tf::ops::Assign(root, b2, tf::ops::RandomNormal(root, { 10 }, tf::DataType::DT_FLOAT));


auto a1=tf::ops::Add(root,tf::ops::MatMul(root, x, w1),b1);


auto z1 = tf::ops::Sigmoid(root,a1);

auto a2 = tf::ops::Add(root, tf::ops::MatMul(root, z1, w2), b2);

auto y = tf::ops::Softmax(root,a2);


tf::Tensor x_data;

tf::Tensor t_data;


auto dy = tf::ops::Div(root, tf::ops::Sub(root, y, t), {(float)batch_num});


auto z1t = tf::ops::Transpose(root, z1, {1,0});

auto w2_ = tf::ops::MatMul(root, z1t, dy);

auto b2_ = tf::ops::Sum(root, dy, {0});


auto w2t = tf::ops::Transpose(root, w2, { 1,0 });

auto dz1 = tf::ops::MatMul(root,dy,w2t);

auto dsg = tf::ops::Mul(root, tf::ops::Sub(root, {(float)1}, tf::ops::Sigmoid(root, a1)), tf::ops::Sigmoid(root, a1));

auto da1 = tf::ops::Mul(root,dsg,dz1);


auto xt = tf::ops::Transpose(root, x, { 1,0 });

auto w1_= tf::ops::MatMul(root,xt,da1);

auto b1_ = tf::ops::Sum(root, da1, { 0 });

 

auto w1_g = tf::ops::Mul(root, w1_, {(float)rate});

auto w2_g = tf::ops::Mul(root, w2_, { (float)rate });

auto b1_g = tf::ops::Mul(root, b1_, { (float)rate });

auto b2_g = tf::ops::Mul(root, b2_, { (float)rate });


auto w1_n = tf::ops::Sub(root, w1, w1_g);

auto w2_n = tf::ops::Sub(root, w2, w2_g);

auto b1_n = tf::ops::Sub(root, b1, b1_g);

auto b2_n = tf::ops::Sub(root, b2, b2_g);


auto a_w1 = tf::ops::Assign(root, w1, w1_n);

auto a_w2 = tf::ops::Assign(root, w2, w2_n);

auto a_b1 = tf::ops::Assign(root, b1, b1_n);

auto a_b2 = tf::ops::Assign(root, b2, b2_n);


std::vector<tf::Tensor> outputs {};


//init

auto ret1 = session.Run({ assign_w1,assign_w2,assign_b1,assign_b2 }, nullptr);


std::vector<float> train_img, test_img, train_lbl, test_lbl,bat_img,bat_lbl;

load_image_file(TRAIN_IMAGE, train_img);

load_label_file(TRAIN_LABEL, train_lbl);

load_image_file(TEST_IMAGE, test_img);

load_label_file(TEST_LABEL, test_lbl);




//train

for (i = 0; i < epoch_num; i++) {


create_batch(batch_num, train_img, train_lbl, bat_img, bat_lbl);


tf::Tensor x_data(tf::DT_FLOAT, tf::TensorShape({ batch_num,784 }));

auto dst_x_data = x_data.flat<float>().data();

memcpy(dst_x_data, bat_img.data(), sizeof(float)*bat_img.size());


tf::Tensor t_data(tf::DT_FLOAT, tf::TensorShape({ batch_num,10 }));

auto dst_t_data = t_data.flat<float>().data();

memcpy(dst_t_data, bat_lbl.data(), sizeof(float) * bat_lbl.size());


//ok  auto ret0 = session.Run({ { x, x_data },{ t, t_data } }, { y }, &outputs);

//print_tensor("y", outputs[0]);

//break;


auto ret2 = session.Run({ { x, x_data },{ t, t_data } }, { a_w1, a_b1, a_w2, a_b2 }, nullptr); 

}


// predict

create_batch(1, test_img, test_lbl, bat_img, bat_lbl);


tf::Tensor xt_data(tf::DT_FLOAT, tf::TensorShape({ 1,784 }));

auto dst_xt_data = xt_data.flat<float>().data();

memcpy(dst_xt_data, bat_img.data(), sizeof(float) * bat_img.size());


tf::Tensor tt_data(tf::DT_FLOAT, tf::TensorShape({ 1,10 }));

auto dst_tt_data = tt_data.flat<float>().data();

memcpy(dst_tt_data, bat_lbl.data(), sizeof(float) * bat_lbl.size());


auto ret3 = session.Run({ { x, xt_data },{ t, tt_data } }, { y }, &outputs);


float* pc = (float*)(outputs[0].data());

print_image(bat_img.data());

print_label(pc);


return 0;

}

-----------------------------


結果はこちら

-----------------------------

2020-08-14 14:07:44.312280: I C:\trash\mnist\tensorflow_2_2_mnist_001\src_core\tensorflow_core_platform_cpu_feature_guard.cc:143] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

--------------------------------------------------------

----------------------------------------11111111--------

--------------111111------------------1111111111--------

--------------111111----------------111111111111--------

------------11111111----------------1111111111----------

------------11111111----------------1111111111----------

----------11111111--------------111111111111------------

----------11111111--------------1111111111--------------

----------11111111--------------1111111111--------------

----------1111111111------------11111111----------------

------------1111111111111111111111111111----------------

----------------111111111111111111111111----------------

------------------1111111111111111111111----------------

--------------------111111111111111111------------------

----------------------------1111111111------------------

------------------------------11111111------------------

----------------------------11111111--------------------

----------------------------11111111--------------------

----------------------------11111111--------------------

----------------------------111111----------------------

----------------------------111111----------------------

--------------------------------------------------------

--------------------------------------------------------


0: 0.000000

1: 0.000000

2: 0.000014

3: 0.000000

4: 0.997556

5: 0.000003

6: 0.000017

7: 0.000059

8: 0.000143

9: 0.002207


-----------------------------


できた!

バックプロパゲーションとかも誤差関数固定ならば数時間で作れるんですね。


2020年8月4日火曜日

Variational Autoencoder(VAE)をやってみた。

おじさんの本日のディープラーニングはオートエンコーダーです。
いつも認識系ばっかりやっているので、たまには生成系もやってみます。

論文はこれ。
https://arxiv.org/pdf/1312.6114.pdf

MNISTでいうと、通常は画像から数値に変換していました。
オートエンコーダーでは逆で数値から画像を作ります。

日本語の解説はこの辺に載ってます。
https://qiita.com/kenmatsu4/items/b029d697e9995d93aa24

このVAEですが、C++言語のサンプルが世の中にない!
なんでだよー。

なのでC++でVAEができるか試してみました。

まずこのKerasのVAEのサンプルコードを改良して.pbファイルを出力するようにします。
https://github.com/keras-team/keras/blob/master/examples/variational_autoencoder_deconv.py

vae.save_weights()の行を以下のように修正すれば、.pbファイルを出力してくれるので、そのファイルをfreezeすればC++で学習済みデータを利用できます。
--------------------
        #vae.save_weights('vae_mlp_mnist.h5')
        vae.save('vae_mlp_mnist.h5')
        decoder.save('vae_mlp_mnist_decoder.h5')
--------------------


いろいろな数字の認識が二次元空間にマップされます。


これをもとに2次元空間から画像を生成してみます。
z[0] = 1;z[1] = 0.4;
2次元空間のこのポイントが「5」と「8」の中間くらいのはずなので、
このポイントから画像を生成すると「5」と「8」の中間くらいの画像ができるはず。

-----------------------
#include <iostream>
#include <cstdio>

#include "opencv2_core.hpp"
#include "opencv2_imgproc.hpp"
#include "opencv2_imgproc_imgproc_c.h"
#include "opencv2_imgcodecs.hpp"

#include <tensorflow_c_c_api.h>
#include "tf_utils.hpp"

#ifdef _MSC_VER
#pragma comment(lib,"ws2_32.lib")
#endif

#define MODEL_FILENAME "vae_mlp_mnist_decoder.pb"

static int displayGraphInfo()
{
TF_Graph *graph = tf_utils::LoadGraphDef(MODEL_FILENAME);
if (graph == nullptr) {
std::cout << "Can't load graph" << std::endl;
return 1;
}

size_t pos = 0;
TF_Operation* oper;
printf("--- graph info ---\n");
while ((oper = TF_GraphNextOperation(graph, &pos)) != nullptr) {
printf("%s\n", TF_OperationName(oper));
}
printf("--- graph info ---\n");

TF_DeleteGraph(graph);
return 0;
}

int main()
{
printf("Hello from TensorFlow C library version %s\n", TF_Version());

#if 0
/* read input image data */
cv::Mat image = cv::imread("4.jpg");
//cv::imshow("InputImage", image);
/* convert to 28 x 28 grayscale image (normalized: 0 ~ 1.0) */
cv::cvtColor(image, image, CV_BGR2GRAY);
cv::resize(image, image, cv::Size(28, 28));
image = ~image;
//cv::imshow("InputImage for CNN", image);
image.convertTo(image, CV_32FC1, 1.0 / 255);
#endif

/* get graph info */
displayGraphInfo();

TF_Graph *graph = tf_utils::LoadGraphDef(MODEL_FILENAME);
if (graph == nullptr) {
std::cout << "Can't load graph" << std::endl;
return 1;
}

/* prepare input tensor */
TF_Output input_op = { TF_GraphOperationByName(graph, "z_sampling"), 0 };
if (input_op.oper == nullptr) {
std::cout << "Can't init input_op" << std::endl;
return 2;
}

const std::vector<std::int64_t> input_dims = { 1,2 };
std::vector<float> input_vals;

input_vals.resize(2);
input_vals[0] = 1;
input_vals[1] = 0.4;

TF_Tensor* input_tensor = tf_utils::CreateTensor(TF_FLOAT,
input_dims.data(), input_dims.size(),
input_vals.data(), input_vals.size() * sizeof(float));

/* prepare output tensor */
TF_Output out_op = { TF_GraphOperationByName(graph, "dense_3/Sigmoid"), 0 };
if (out_op.oper == nullptr) {
std::cout << "Can't init out_op" << std::endl;
return 3;
}

TF_Tensor* output_tensor = nullptr;

/* prepare session */
TF_Status* status = TF_NewStatus();
TF_SessionOptions* options = TF_NewSessionOptions();
TF_Session* sess = TF_NewSession(graph, options, status);
TF_DeleteSessionOptions(options);

if (TF_GetCode(status) != TF_OK) {
TF_DeleteStatus(status);
return 4;
}

/* run session */
TF_SessionRun(sess,
nullptr, // Run options.
&input_op, &input_tensor, 1, // Input tensors, input tensor values, number of inputs.
&out_op, &output_tensor, 1, // Output tensors, output tensor values, number of outputs.
nullptr, 0, // Target operations, number of targets.
nullptr, // Run metadata.
status // Output status.
);

if (TF_GetCode(status) != TF_OK) {
std::cout << "Error run session";
TF_DeleteStatus(status);
return 5;
}

TF_CloseSession(sess, status);
if (TF_GetCode(status) != TF_OK) {
std::cout << "Error close session";
TF_DeleteStatus(status);
return 6;
}

TF_DeleteSession(sess, status);
if (TF_GetCode(status) != TF_OK) {
std::cout << "Error delete session";
TF_DeleteStatus(status);
return 7;
}

const auto probs = static_cast<float*>(TF_TensorData(output_tensor));

//for (int i = 0; i < 28 * 28; i++) {
// printf("prob of %d: %.3f\n", i, probs[i]);
//}

cv::Mat out = cv::Mat::zeros(28, 28, CV_32F);
memcpy(out.data, probs, sizeof(float) * 28 * 28);
out *= 256;
cv::imwrite("out.png", out);

TF_DeleteTensor(input_tensor);
TF_DeleteTensor(output_tensor);
TF_DeleteGraph(graph);
TF_DeleteStatus(status);

return 0;
}
-----------------------




なんかできた。
これを人の顔とか物でやると二つの顔の間の顔とかが生成できるのね。
それにしてもオートエンコーダって地味だね。


2020年7月30日木曜日

AlphaGoをやってみた。

AlphaGoは、数年前にGoogleの子会社のDeepMindが作った最強の囲碁プログラムです。
http://augmentingcognition.com/assets/Silver2017a.pdf



オリジナルはPythonで書かれているのですが、なんとC++版を実装してくれた人がいます。

https://github.com/leela-zero/leela-zero

おじさん、前からAlphaGoで遊んでみたかったんだよねぇ。
C++のコードはそんな大きくないので、頑張って,GPUがないおじさんのマシンでも動くように改良してビルドしてみました。
bootstを使っているのですが、おじさんboost嫌いなので、booastをとってこなくても動くようにしました。

ソースコードはこちら
https://drive.google.com/file/d/1JYpTM4r9i-KihoK_tVxzQoTNCrC7K4gr/view?usp=sharing


学習をするのに1700年かかるそうです。
おじさん暇なんですが、そんなに待ってる余裕ねーよ。
ということで学習済みデータを取ってきます。

学習済みデータが300Mバイトもある。
びょえー。


leelazを起動して

genmove w
genmove b

などのコマンドを打てば、AIが考えてくれるらしい。

-----------------------------
Leela: genmove w
Thinking at most 36.0 seconds...
NN eval=0.969207
Playouts: 12, Win: 81.57%, PV: Q4 D16 pass
Playouts: 83, Win: 61.85%, PV: pass Q4 D16 D4 Q16 R14 C3
Playouts: 187, Win: 59.55%, PV: pass Q4 D16 Q16 D4 F17 R3 R4
Playouts: 289, Win: 59.46%, PV: pass Q4 D16 Q16 D4 F17 R3 R4 Q3
Playouts: 410, Win: 59.52%, PV: pass Q4 D16 Q16 D4 F17 R3 R4 Q3
Playouts: 557, Win: 59.33%, PV: pass Q4 D16 D4 Q16 R14 C3 D3 C4
Playouts: 724, Win: 58.85%, PV: pass Q4 D16 Q16 D4 F17 R3 R4 Q3 O3
Playouts: 899, Win: 58.53%, PV: pass Q4 D16 Q16 D4 F17 R3 R4 Q3 O3
Playouts: 1036, Win: 58.33%, PV: pass Q4 D16 D4 Q16 C17 C16 D17 E16
Playouts: 1252, Win: 58.11%, PV: pass Q4 D16 Q16 D4 F17 R3 R4 Q3 O3
Playouts: 1405, Win: 58.07%, PV: pass Q4 D16 Q16 D4 F17 R3 R4 Q3 O3
Playouts: 1683, Win: 57.91%, PV: pass Q4 D16 Q16 D4 C3 D3 C4 D5 B6
Playouts: 1878, Win: 57.85%, PV: pass Q4 D16 Q16 D4 C3 D3 C4 D5 B6 O17
Playouts: 1989, Win: 57.84%, PV: pass Q4 D16 C17 D17 C16 D15 B14 R16 C4 E3 D5

  Q4 ->     269 (V: 59.61%) (LCB: 57.13%) (N:  1.29%) PV: Q4 D4 pass R3 Q3 R4 Q5 S6 Q16 D16 F17 R7 C14
pass ->    1440 (V: 57.18%) (LCB: 57.12%) (N: 89.81%) PV: pass Q4 D16 Q16 D4 C3 D3 C4 D5 B6 O17
 D16 ->     214 (V: 59.35%) (LCB: 56.67%) (N:  1.04%) PV: D16 Q16 pass C17 D17 C16 D15 B14 D4 Q4 O3 C13
  D4 ->     101 (V: 59.02%) (LCB: 55.24%) (N:  1.26%) PV: D4 Q16 pass Q4 D16 C3 D3 C4 D5 B6 O17 C7
8.2 average depth, 14 max depth
1586 non leaf nodes, 1.28 average children
2026 visits, 718331 nodes, 2024 playouts, 56 n/s

= Q4

NN eval=0.094774

  D3 ->    1707 (V: 21.96%) (LCB: 19.96%) (N:  3.07%) PV: D3 C5 D16 Q16 F4 C17 C16 D17 F17 E17 E16 F18
 R16 ->    1459 (V: 21.94%) (LCB: 19.78%) (N:  2.55%) PV: R16 D17 D3 P17 C15 Q15 R15 Q14 R13 D5 C5 C6 C4 D6 F4 H3
  C4 ->     982 (V: 22.09%) (LCB: 19.45%) (N:  0.49%) PV: C4 Q16 D17 E3 D5 D15 C15 C14 C16 D14 F17 D10 R3 R4
 Q17 ->     946 (V: 22.03%) (LCB: 19.35%) (N:  0.71%) PV: Q17 D4 C16 R15 P16 E16 E17 F17 D17 F16 C14 K16 R3 Q3
 C16 ->     461 (V: 21.73%) (LCB: 17.87%) (N:  1.71%) PV: C16 Q16 D3 C5 F4 E16 E17 F17 D17 F16 D14
 D16 ->    1420 (V: 19.99%) (LCB: 17.78%) (N: 29.75%) PV: D16 C17 D17 C16 C14 C15 D15 B14 D4 C13 D14 Q16 C18 B12 B17 B16 A16 B15 R3 R4 Q3
 D17 ->     346 (V: 21.57%) (LCB: 17.10%) (N:  1.74%) PV: D17 D4 R16 P16 P17 O17 Q17 O16 Q14 R12 R13 Q12 O14
  D4 ->     482 (V: 19.87%) (LCB: 16.06%) (N: 10.57%) PV: D4 D16 R16 P17 Q15 M17 R3 R4 Q3 P3 P2 O2 O3 P4 N2 Q2
 Q16 ->     401 (V: 18.65%) (LCB: 14.50%) (N: 13.88%) PV: Q16 D16 C4 E3 D5 H3 C17 C16 D17 E17 E18 F18 F17 E16 G18 D18
  R3 ->     233 (V: 18.36%) (LCB: 12.87%) (N:  8.57%) PV: R3 D16 Q3 P4 P3 O4 S5 D4 Q16 O3
  R6 ->      16 (V: 10.38%) (LCB:  0.00%) (N:  1.93%) PV: R6 Q16 D4 D16 C17 C16 D17 E17
  O3 ->       7 (V:  8.99%) (LCB:  0.00%) (N:  0.99%) PV: O3 D4 Q16 D16 C17 D17
 R17 ->       1 (V:  2.32%) (LCB:  0.00%) (N:  0.28%) PV: R17
  C3 ->       1 (V:  2.32%) (LCB:  0.00%) (N:  0.24%) PV: C3
 Q15 ->       1 (V:  2.25%) (LCB:  0.00%) (N:  0.29%) PV: Q15
 D15 ->       1 (V:  2.16%) (LCB:  0.00%) (N:  0.29%) PV: D15
 E16 ->       1 (V:  2.16%) (LCB:  0.00%) (N:  0.24%) PV: E16
 C17 ->       1 (V:  2.10%) (LCB:  0.00%) (N:  0.36%) PV: C17
10.0 average depth, 29 max depth
6794 non leaf nodes, 1.25 average children

8468 visits, 2977365 nodes


Passes: 0            Black (X) Prisoners: 0
Black (X) to move    White (O) Prisoners: 0

   a b c d e f g h j k l m n o p q r s t
19 . . . . . . . . . . . . . . . . . . . 19
18 . . . . . . . . . . . . . . . . . . . 18
17 . . . . . . . . . . . . . . . . . . . 17
16 . . . + . . . . . + . . . . . + . . . 16
15 . . . . . . . . . . . . . . . . . . . 15
14 . . . . . . . . . . . . . . . . . . . 14
13 . . . . . . . . . . . . . . . . . . . 13
12 . . . . . . . . . . . . . . . . . . . 12
11 . . . . . . . . . . . . . . . . . . . 11
10 . . . + . . . . . + . . . . . + . . . 10
 9 . . . . . . . . . . . . . . . . . . .  9
 8 . . . . . . . . . . . . . . . . . . .  8
 7 . . . . . . . . . . . . . . . . . . .  7
 6 . . . . . . . . . . . . . . . . . . .  6
 5 . . . . . . . . . . . . . . . . . . .  5
 4 . . . + . . . . . + . . . . .(O). . .  4
 3 . . . . . . . . . . . . . . . . . . .  3
 2 . . . . . . . . . . . . . . . . . . .  2
 1 . . . . . . . . . . . . . . . . . . .  1
   a b c d e f g h j k l m n o p q r s t

Hash: 4764FDB76CFE8FAC Ko-Hash: 7CBB65687BFB484F

Black time: 01:00:00
White time: 00:59:23
-----------------------------

できたけど、なんか一手打つのに10分くらいかかる・・・・・。
こういうのもGPUがないマシンでもいちおう動かせるんですね。


2020年7月28日火曜日

monodepthをやってみる

前回、最小のTensorflow2をつくることができたました。
そこで今回はこれを使って深度推定のmododepthをやってみたいと思います。

https://arxiv.org/pdf/1609.03677.pdf

monodepthは、深度推定のアルゴリズムの中では高速で正確らしいです。
最近はCPPのコードも充実してきています。
https://github.com/yan99033/monodepth-cpp
これはさっそく試さなくては。

python版のtensorflowはバージョンがちょっとでも違うと動かないですが、c++版の方は多少バージョンが違ってもきちんと動きます。
なので、おじさんはc++で頑張ります。

さくっと最小のtensorflow2で動くように改良。
ソースコードはこちら
https://drive.google.com/file/d/1_pxE6jmjXABKvtZgHkiJTu6aIzuJRTPv/view?usp=sharing

-----------------------
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN

#include <monodepth.h>

//#include <opencv2/opencv.hpp>
#include "opencv2_core.hpp"
#include "opencv2_imgproc.hpp"
#include "opencv2_imgproc_imgproc_c.h"
#include "opencv2_imgcodecs.hpp"

#ifdef _MSC_VER
#pragma comment(lib,"ws2_32.lib")
#endif

int main()
{
  int image_width = 1241;
  int image_height = 376;

  image_width = 1242;
  image_height = 375;

  monodepth::MonoDepth m(image_width, image_height, "model_city2kitti_vgg.pb");
  cv::Mat depth;

  std::string img_path = "/path/to/image/folder/";

  int number_of_images = 1000;

  //for (int i=0; i<number_of_images; i++)
  {
    //char index_buf[7]; sprintf(index_buf, "%06d", i); std::string index_s(index_buf);
    //cv::Mat image(cv::imread(img_path + index_s + ".png"));
    // std::cout << "read image in test monodepth" << std::endl;
    cv::Mat image(cv::imread("000027.png"));
      assert(!image.empty());

    m.inference(image, depth);

    // convert disparity to depth
    depth = 0.3128f / (depth + 0.00001f);

    double min_val, max_val;
    cv::Mat depthmap_visual;
    cv::threshold(depth, depthmap_visual, 50.0, 50.0, cv::THRESH_TRUNC); // apply threshold
    cv::minMaxLoc(depthmap_visual, &min_val, &max_val);
    depthmap_visual = 255 * (depthmap_visual - min_val) / (max_val - min_val);
    depthmap_visual.convertTo(depthmap_visual, CV_8U);
    cv::applyColorMap(depthmap_visual, depthmap_visual, 2); //COLORMAP_JET

    cv::Size img_size = image.size();
    int height = img_size.height * 2;
    int width = img_size.width;
    cv::Mat full(height, width, CV_8UC3);
    cv::Mat top(full, cv::Rect(0, 0, img_size.width, img_size.height));
    image.copyTo(top);
    cv::Mat bottom(full, cv::Rect(0, img_size.height, img_size.width, img_size.height));
    depthmap_visual.copyTo(bottom);

    cv::imwrite("top.png", top);
    cv::imwrite("bottom.png", bottom);
    cv::imwrite("full.png", full);
    //cv::namedWindow("FULL", CV_WINDOW_AUTOSIZE);
    //cv::imshow("FULL", full);
    //cv::waitKey(1);
  }

  return 0;
}
-----------------------

vggとresnetの学習済みデータが合わせて500Mバイトもあるけど、それ以外はなんか何事もなく簡単に作れてしまった。
結果はいかのとおり。


GPUがあると13fpsでると書いてあるけど、おじさんのCPUだけマシンだと1枚の画像を解析するだけでも数分間かかる・・・。
monodepthはCPUだけでも遅いけどどうにかできそうってことがわかった。
monodepthの日本語の記事がひとつしかない。日本の自動車メーカーとか大丈夫なんでしょうか?

Tensorflow2をビルドする

いつのまにかTensorflowのバージョンが2に上がっている。
以前はVisualStudioでビルドできなかったのですが、バージョンアップに伴いWindowsでも簡単にビルドできるようになったっぽいのでおじさんもビルドしてみました。

bazelでビルドするとビルド途中でエラーが・・・
なんでだよ。

なのでbazelを使わないでビルドしてみました。
VisualStudio2019に以下のファイル追加すればbazelを使わないほかのビルドシステムでビルドできるっぽいですね。

・tensorflow/core以下の全ファイル
・tensorflow/core/kernelsは必要なものだけをビルド
・double-conversion
・farmhash
・protocolbuffer
・eigen
・giflib
・turbojpeg
・libpng
・sqlite3
・grpc
・nsync
・absl
・re2
・zlib
・snappy
・json-cpp
・bazelによって自動生成されるops_xxxxx.cpp
・gentoolによって自動生成されるxxxxx.pb_text.cc、xxxxx.pb_text.h、xxxxx_pb_text-impl.h
・protocによって自動生成されるxxxxx.pb.cc、xxxxx.pb.h

基本的にはtensorflow/coreをどうにかビルドできればtensorflowは動きます。
gpuとかtpuとかrpcとかがファイル名に入っているものは、gpuとかtpuとかrpcを使わないのであればいらないっぽいです。
これらのファイルを全部VisualStudio2019に追加すれば、bazelを使わなくてもtensorflowをビルドすることができます。

tensorflow/core/kerners内のファイルをたくさんビルドしようとすると、コンパイル時にヒープ領域が足りなくなるようで、
その時は以下の設定をするとよいそうです。
https://kenkyu-note.hatenablog.com/entry/2019/11/01/193301

ファイル名を「tensorflow/core/hoge.cc」から「tensorflow_core_hoge.cc」のように名前を変えれば、全部のファイルを一つのディレクトリに入れてビルドすることもできます。

tensorflowはopencvとよく併用されるので、ついでにopencvのソースも一部入れます。

いつものようにtensorflow2でmnistをやってみます。
https://qiita.com/iwatake2222/items/d7cb2bd10a8bbb81cd8b
mnistはこのサイトを参考にします。

これをベースに最小のtensorflow2を作ってみました。
https://drive.google.com/file/d/1UzCRcFLTe-VF8LDn1jP2hZumXNOYfmFR/view?usp=sharing


いつものようにこの画像を認識させてみます。


-----------------------
prob of 0: 0.000
prob of 1: 0.001
prob of 2: 0.013
prob of 3: 0.043
prob of 4: 0.929
prob of 5: 0.003
prob of 6: 0.001
prob of 7: 0.006
prob of 8: 0.001
prob of 9: 0.003
-----------------------

できた!


2020年6月30日火曜日

算数の問題を解く

おじさん、子供の公文式の分数の計算問題があまりにめんどくさくてできません。
帯分数を仮分数に直して通分して計算して約分して仮分数を帯分数に直さないといけないのです。
もうなんなんだこのめんどくささ。

小学生の子供にどうやって問題を解いているのか聞いたら、Photomathというアプリをタブレットに入れて、写真を撮れば答えがわかるよと言われました。

どうも最近はスマホで算数の問題の写真を撮ると答えが出るらしい。
しかもきちんと計算の順序まで出る。
おじさん、最近の小学生のタブレット学習についていけない・・・。


これ、どういう仕組みになってるの?
調べてみると、im2markupというもので、数式の写真を文字列に変換し、計算しているらしい。

im2markup
http://arxiv.org/pdf/1609.04938v1.pdf

日本語のページが一つもヒットしないのは、きっと日本人は誰もやってないのね。
さみしいねぇ。これだけディープラーニングと言われている中、おじさんしかいないのかよ。

最近はなんでもLSTMですね。
LSTMって完全チューリングマシンなので、人間ができてLSTMでできないことなどないはず。
でもTensorFlowもTorchもLSTMはGPUがないと動かない・・・・

これをおじさんのパソコンでもうごかしたいので、本日はim2markupをWindowsのGPUがないマシンでも動くように移植します。
LSTMでできないことはないはずだけど、おじさんもC言語で実装できないものはない!


ディープラーニングって、論文や公開されているソースコードを動かそうとしても、だいたいそのままじゃ動きません。
使っているライブラリのバージョンが合わないとか、論文が間違っているとか、本当に大変な作業です。
これを自分のところで動くようになるまでにだいたい一か月くらいかかります。

よくディープラーニングのエンジニア募集のところにも書いてあるもんね、論文を読んで実装できることと。
これをさくさくできる人、日本にいるんだろうか?

論文とソースコードをにらめっこして、ひたすらおかしいところを直し、img2markupをWindowsのGPUなしでも動くC言語のコードにしてみました。

オリジナルはLuaJitコンパイラでLua言語で書かれている部分をコンパイルしないと動かないのですが、インタープリタで動くFFIライブラリも移植して、C言語とLuaのスクリプトファイルだけで動きます。
これでどんなマシンでもim2markupが動く。たぶんほかのLSTMのものも動く。

ここでいつもは学習済みデータをとってくるのですが、torchで書かれたものはGPUがあるマシンで学習させると、GPUがあるマシンでないと認識のテストができません。
CPU版の学習済みデータがない・・・。
なので本日は頑張ってGPUがないマシンで、im2markupの学習も行います。

おじさん、学習もできるんです。
サンプルの学習は数式が1000個、GPUなしで1エポック8時間かかります。
1日に3エポックしか進まないじゃないか。
15エポック学習するには5日間もかかるので、とりあえず3エポック学習させてみました。

ソースコードと学習済みデータはこちら。
https://drive.google.com/file/d/1VF_hckbuAH28UjtoAjMeUK2OW7hfCtvk/view?usp=sharing

この画像を文字列に変換してみます。


----------------
\Gamma ( z + 1 ) = \int _ { 0 } ^ { \infty } \, \, d x \, \, e ^ { - x } x ^ { z }
----------------

おー、認識に数分かかるけどなんかあってる。
3エポックしか学習させていないのに。

これを全データで120時間かけて学習させるときっとかなりの確率で数式を認識するんだと思います。このあとテキストに変換された数式を数式の計算ソフトに食わせればよいのね。

LSTMほんとすごいね。
本日はLSTMは時間かかるけどGPUがないマシンでもできるよっていう話でした。


2020年6月15日月曜日

深度推定をする

カメラの画像から物体までの距離を推定する、単眼深度推定(mono depth estimation)をしてみました。
この分野は盛んに研究されているらしく、vid2depth、monodepth、fcrndepthなど、いろいろなアルゴリズムがあります。
最新の研究だと、Tensorflowにデフォルトにないオペレーターとかを使うから、動かすのがとってもめんどくさい。
さらに、精度の良いアルゴリズムは、学習済みデータでも100Mくらい、元データは何百GバイトもあってとてもおじさんのPCでは実験できません。
この人たちどういうPCで研究しているんだろね。
そもそもおじさんのPCはGPUないし。

一年位前から手軽にさくっとできる単眼深度推定ないかなぁと論文を探していたところ、以下の論文を見つけました。
"Unsupervised CNN for single view depth estimation: Geometry to the rescue"
https://arxiv.org/pdf/1603.04992.pdf

学習はステレオカメラだけど、推定は一枚の画像でできるようです。

https://github.com/Ravi-Garg/Unsupervised_Depth_Estimation
学習済みデータも数十メガバイトしかないし。
なんかResNet50ベースって書いてあるから、これならば、いつもの最小のOpenCVでもさくっと実行できるかも。

ということで、github上の学習済みデータとテストコードを見ながら頑張って数時間かけて上記の論文のプログラムをC++で作ってみました。

以前作った最小限のOpenCVもとってきます。
http://yomeiotani.blogspot.com/2019/07/pose-estimation.html




なんとなくそれっぽい結果が。
-------------------------------
#include<stdio.h>
#include<string>
#include<vector>
#include<iostream>

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION


#include "opencv2_core.hpp"
#include "opencv2_imgproc.hpp"
#include "opencv2_imgproc_imgproc_c.h"
#include "opencv2_dnn.hpp"

#include "stb_image.h"
#include "stb_image_write.h"

void changeb_g(unsigned char* p, int x, int y, int c)
{
int ct = x*y;
int i;
unsigned char t;
for (i = 0; i < ct; i++) {
t = p[0];
p[0] = p[2];
p[2] = t;
p[3] = 255;
p += c;
}
}



#if 1

int main()
{
unsigned char* p;
int x=-1, y=-1, n=-1;

// Specify the paths for the 2 files
std::string protoFile = "deploy_resnet50by2_pool.prototxt";
std::string weightsFile = "train_iter_40000.caffemodel";


// Read the network into Memory
cv::dnn::Net net = cv::dnn::readNetFromCaffe(protoFile, weightsFile);

//
p = stbi_load("1.png", &x, &y, &n, 4);
//p = stbi_load("mask5.jpg", &x, &y, &n, 4);
if (p == NULL || x < 1 || y < 1)return 1;
changeb_g(p, x, y, 4);
cv::Mat color = cv::Mat(y, x, CV_8UC4);
memcpy(color.data, p, x * 4 * y);
stbi_image_free(p);

cv::Mat img;
cv::cvtColor(color, img, CV_BGRA2BGR);
cv::resize(img, img, cv::Size(608,160));

cv::Mat inpBlob = cv::dnn::blobFromImage(img, 1.0);

// Set the prepared object as the input blob of the network
net.setInput(inpBlob);


cv::Mat output = net.forward();
std::vector<cv::Mat> outImg;
cv::dnn::imagesFromBlob(output, outImg);
cv::Mat out = outImg[0];
unsigned char* pp = new unsigned char[608 * 160 * 4];
p = pp;
float* ff = (float*)out.data;

for (int i = 0; i < 160 * 608; i++) {
//if(i%608==0)printf("i=%d ff=%f\n",i,*ff);
float a = *ff++*5;
if (a > 255)a = 255;
if (a <0)a = 0;
*p++ = a;
*p++ = a;
*p++ = a;
*p++ = 255;
}

stbi_write_png("result.png", 608, 160, 4, pp, 4 * 608);

delete[] pp;
return 0;
}

#endif
-------------------------------

おじさん、次はLSTMをやりたいんだよなぁ。
機械翻訳のseq2seqとか、数式の写真をとると答えを計算してくれるim2latexとかをやってみたい。
opencvだとオペレーターが足りないんだよなぁ。


2020年6月12日金曜日

マスク着用を判定する。

おじさんの会社では、入り口のゲートのところにカメラが置いてあって、マスクをしているかチェックされます。
間違ってマスクを会社に置いたまま外に出ると、二度と社内に入れなくなります。

AIがマスク着用判定、注意も 東大発ベンチャーがシステム開発
https://www.sankeibiz.jp/business/news/200317/bsc2003170500006-n1.htm

いいなぁ、東大生が作ると新聞に載るけど、おじさんが一時間くらいで作っても誰も褒めてくれない・・・。おじさんさみしい。

世の中にあるマスク着用判定プログラムは、いろいろなライブラリが必要で、なかなか軽量なものがない。
そこで、他のライブラリを必要としない単体で動くコンパクトなものを作ってみました。


まず、いつもの通り学習済みデータを取ってきます。

https://github.com/didi/maskdetection
ここのmodelのフォルダにある学習済みデータを取ってきます。
100Mバイトもある。
そう思っていたけど、ResNet50でできているのね。

次に、以前作った最小限のOpenCVもとってきます。
http://yomeiotani.blogspot.com/2019/07/pose-estimation.html

ソースコードはこんな感じ。
一時間くらいでサクッと作れます。
----------------------------------------------------
#include<stdio.h>
#include<string>
#include<vector>
#include<iostream>

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION


#include "opencv2_core.hpp"
#include "opencv2_imgproc.hpp"
#include "opencv2_imgproc_imgproc_c.h"
#include "opencv2_dnn.hpp"

#include "stb_image.h"
#include "stb_image_write.h"

void changeb_g(unsigned char* p, int x, int y, int c)
{
int ct = x*y;
int i;
unsigned char t;
for (i = 0; i < ct; i++) {
t = p[0];
p[0] = p[2];
p[2] = t;
p[3] = 255;
p += c;
}
}

int main()
{
unsigned char* p;
int x=-1, y=-1, n=-1;

// Specify the paths for the 2 files
std::string protoFile = "deploy.prototxt";
std::string weightsFile = "face_mask.caffemodel";


// Read the network into Memory
cv::dnn::Net net = cv::dnn::readNetFromCaffe(protoFile, weightsFile);

//
p = stbi_load("nomask5.jpg", &x, &y, &n, 4);
//p = stbi_load("mask5.jpg", &x, &y, &n, 4);
if (p == NULL || x < 1 || y < 1)return 1;
changeb_g(p, x, y, 4);
cv::Mat color = cv::Mat(y, x, CV_8UC4);
memcpy(color.data, p, x * 4 * y);
stbi_image_free(p);

cv::Mat img;
cv::cvtColor(color, img, CV_BGRA2BGR);
cv::resize(img, img, cv::Size(224, 224));

cv::Mat inpBlob = cv::dnn::blobFromImage(img, 1.0, cv::Size(224, 224), cv::Scalar(104, 117, 123));

// Set the prepared object as the input blob of the network
net.setInput(inpBlob);


cv::Mat output = net.forward();
std::cout << output<<std::endl;

float result = 0.0;
result = output.at<float>(0);

if (result > 0)printf("mask\n");
else printf("nomask\n");

return 0;
}

----------------------------------------------------

結果は以下の通り。
10枚くらいためしましたが全部正解。

----------------------------------------------------
Attempting to upgrade batch norm layers using deprecated params: face_mask.caffemodel
Successfully upgraded batch norm layers using deprecated params.
[-0.98020822]
nomask

Attempting to upgrade batch norm layers using deprecated params: face_mask.caffemodel
Successfully upgraded batch norm layers using deprecated params.
[1.4664154]
mask
----------------------------------------------------

なんだぁマスク装着判定ってこんな簡単に作れるのか。
これはいい商売になるねぇ。



2020年6月11日木曜日

会社のパソコンの電源をリモートでいれる。

おじさんも最近テレワークしています。
家から会社のパソコンにアクセスしているのですが、たまにその会社のパソコンがハングして固まってしまいます。
その固まったパソコンを再起動するために会社にいかないといけないのです。

とってもめんどくさいので、スマートプラグを使用して家から会社のパソコンの電源をリモートでON/OFFできるようにしてみました。


1.機材の準備

broadlinkという会社から発売されているスマートプラグ、「eplug」というものを買います。

amazonで購入

この機材を買うとスマホからコンセントのON/OFFを行うことができます。
日本でもLinkJapanという会社から同じものが発売されています。
日本だと少し割高ですが、3000円くらいで買えるようです。
おじさんは秋葉原のヨドバシカメラで買ってきました。

このスマートプラグは同じアカウントでログインすれば複数のスマホから同じ機材を操作できます。
なれればとても便利なのですが、中国製だけあって、最初のうちはアプリがバグだらけでとても使いづらいです。
どうやらiphoneとAndroidでアクセスするサーバが違うらしく、両方の端末から使いたい場合、二回アカウントをつくらないといけないみたいです。

だいたいこういう製品は、ネットの接続が一時的に切れると通電もOFFになったりして実用的に使えないことが多いのですが、この商品はネットの接続が切れても通電状態をOFFにすることなく、現在の状態をそのまま保持します。
また、通電状態で停電になると、復帰時にも通電状態になるようです。
この辺はしっかりできてますね。

あと、コンセントにさすものは発火すると危ないので、PSEマークという認証を取らないといけないのですが、この会社の製品はきちんとPSEマークを取っているようです。

2.パソコンの設定

通常パソコンはコンセントに電源をさしただけでは動かない設定になっています。
大手メーカーのパソコンはデータセンターなどで停電時したときに自動で電源が入るように、BIOSや環境設定で、コンセントの電源ONと同時にOSがブートして起動するような設定ができます。僕の使っている、HP、Dell、Macどれもこの設定を行うことができました。
たぶんほかのパソコンでもできるものと思います。


3.アプリのダウンロードと設定

何種類かアプリがあって、どれを使えばよいのかよくわからないのですが、
iphoneは「ehome」、Androidは「home link」というアプリをダウンロードすれば、上記のスマートプラグをスマホからコントロールすることができるようになります。
アプリからデバイスを登録すればスマートプラグが使えるようになるのですが、なぜか2/3くらいの確率で登録が失敗します。
なので新しいデバイスを登録するときはいつも3回くらいデバイスのリセットボタンを押して登録をします。
先ほども書いたように、iphoneとAndroid両方の端末で使いたい場合、この作業を2回行わないといけないので、6回くらい?デバイスリセットを繰り返して両方のスマホに登録します。

4. Alexaの設定

これはやらなくてもよいのですが、alexaアプリをいれてスマートスピーカーの設定をすると、おうちで「アレクサ、PCの電源をいれて」というと、会社のスマートプラグのPCの電源が入るようになります。
子供の前でやると、子供が何回もPCを再起動して大変なことになります。


これでスマホからスマートプラグを操作して会社のパソコンの電源をON/OFFできるようになります。
ハマるポイントさえわかればとっても便利。

世の中の大部分の家電はコンセントにさしただけでは待機状態で電源がONにはなりません。この場合、同じメーカーから発売されている「eRemote」という商品を使えばリモコンコードを送信することができ、家電を操作することもできます。






2020年5月26日火曜日

実効再生産数を計算する。

おじさん、おうちでやることがない。
とっても暇なのです。

コードを書かないと頭が鈍ってしまう。
なので、今日は頭の体操に、最近よくニュースで発表されている「実効再生産数」を自分で計算してみたいと思います。

まず、計算アルゴリズム。

有名な西浦先生のやつみてみます。
http://statmodeling.hatenablog.com/entry/covid19-estimate-effective-reproduction-number

難しすぎる・・・。
予備知識がないと何を言っているのかわからない。
正確に計算しようとするとベイズ統計とか回帰解析とか最尤推定とかかなりめんどくさい。
おじさん、この「犬」みたいな漢字が何十年間も読めない・・・。
統計と推定なので結構ディープラーニングの知識と近い気がする。

しかし、いつものごとく、このブログはC言語限定。
プログラムコンテストみたいに数時間でできる簡単なコードでないとだめなのです。
ということで、別のアルゴリズムを探します。

過去の教授のひとりごと
http://www.med.u-toyama.ac.jp/biostat/hitori.html

実効再生産数の論文
https://www.medrxiv.org/content/10.1101/2020.01.27.20018952v1.full.pdf

こっちは簡単そう。
とりあえず上記のアルゴリズムでプログラムを組んでみます。


次にデータ。
おじさんの住んでいる東京都のデータを取ってきます。
https://stopcovid19.metro.tokyo.lg.jp/

最近は東京都のホームページからオープンデータとして感染者のデータが取れるのね。
すごいねぇ。
なんかこの前やったKaggleみたい。

これらのデータとアルゴリズムで実効再生産数を求めて、エクセルでグラフにてみました。
一時間くらいで作った割には、なんかそれっぽいね。
できたのかな????
発症日じゃなくて発表日であるとか、配列の日にちをすこしずらさないといけないとか細かいところはたくさん違うけど、まぁしょうがない。

コードは以下の通りです。
-------------------------------------
#include <stdio.h>
#include <string.h>
#include <math.h>
#define MAX_LIST 200

struct infected {
char date[16];
int n;
double rt;
};

static int mon_day[] = { 31,29,31,30,31,30,31,31,30,31,30,31,0 };
struct infected infe[MAX_LIST];

static void create_infe_base()
{
int i, j,c=0;
for (i = 0; i < 100; i++) {
if (mon_day[i] == 0)break;
for (j = 0; j < mon_day[i]; j++) {
if (c >= MAX_LIST)break;
sprintf(infe[c].date, "2020-%02d-%02d", i+1, j+1);
c++;
}
}
}

char *mystrtok(char *s1, const char *s2) {
static char *str = 0;
register int i, j;

if (s1)str = s1;
else s1 = str;
if (!s1) { return(0); }
j = strlen(s2);
while (1) {
if (!*str) {
str = 0;
return(s1);
}
for (i = 0; i < j; i++) {
if (*str == s2[i]) {
*str++ = 0;
return(s1);
}
}
str++;
}
}

static int input_infe_data()
{
int ret = -1,id,ct,i;
FILE* fp;
char buf[256],date[256];
char* ptr;
fp = fopen("130001_tokyo_covid19_patients.csv","rb");
if (fp == NULL)goto end;
while (1) {
id = 0;ct = 0;buf[0] = 0;date[0] = 0;
fgets(buf, sizeof(buf), fp);
if (buf[0] == 0)break;
ptr = mystrtok(buf, ",");
if (ptr == NULL)break;
sscanf(ptr,"%d", &id);
if (id < 1)continue;
while (ptr != NULL) {
ct++;
ptr = mystrtok(NULL, ",");
if (ptr != NULL && ct==4) {
strcpy(date, ptr);
break;
}
}
if (date[0] == 0)continue;
for (i = 0; i < MAX_LIST; i++) {
if (strcmp(infe[i].date, date))continue;
infe[i].n++;
break;
}
}
ret = 0;
end:
if (fp)fclose(fp);
return ret;
}
static void print_infe()
{
int i;
for (i = 10; i < MAX_LIST; i++) {
//printf("%4d  %s  %5d   %.2f\n",i, infe[i].date, infe[i].n, infe[i].rt);
printf("%d,%s,%d,%.2f\n", i, infe[i].date, infe[i].n, infe[i].rt);
}

}
static void calc_r()
{
int i,j;
double R, K, L = 7, D = 9, A, B;
for (i = 9; i < MAX_LIST;i++) {
A = 0,B=0;
for (j = 0; j < 5; j++) {
A += infe[i - j].n;
B += infe[i - 5-j].n;
}
if (A == 0 || B == 0)continue;
K = (log10(A) - log10(B))/5;
R = K * K*(L*D) + K * (L + D) + 1;
if (R < 0)continue;
infe[i].rt = R;
}
}
int main()
{
create_infe_base();
input_infe_data();
calc_r();
print_infe();
return 0;
}
-------------------------------------

今日は実効再生産数を求めるでした。


2020年2月27日木曜日

C++のヘッダーファイルだけでシングルトンを作る

おじさん、この年になってもいまだにC++で上手にシングルトンが作れません。
スレッドセーフとか考えるとあれ結構難しくない?
やっぱシングルトンは良質な水と原料からでないと作れないっぽいです。

だれでも簡単に任意のクラスをシングルトンにできるクラスとか作れないんだろうか。
よく、シングルトンを作るために.cppファイルでstatic変数を

static MyClass*pmyclass=NULL;

とか宣言するのも意味不明でたまに忘れてコンパイルエラーになってめんどくさいし。
シングルトンのクラスをシングルトンじゃなくしたり意外とめんどくさいじゃん。

.cppファイルがいらない、C++のヘッダーファイルだけで簡単にシングルトンを作れるのか考えてみました。

とりあえず getInstance()の関数の中で、

getInstance(){
Singleton<MyClass> a;
return a.a;
}

このように返せばどんなクラスでもシングルトン化できるテンプレートを作成。
これで数行追加するだけでどのクラスもシングルトン化できてだいぶ便利な気がする。

template<typename A> A Singleton<A>::a;

この一行がポイントですね。
ヘッダーファイル内でグローバル変数宣言できるし。
しかもヘッダーファイルを使わないときは、メモリ使われないし。
やっぱテンプレート機能をつかわないとできないのかなぁ。

----------------------------------
#include <cstdio>

//ここからヘッダーファイルのつもり
template<typename A> class Singleton {
public:
static A a;
};
template<typename A> A Singleton<A>::a;
//ここまでヘッダーファイルのつもり

class ClassA{
public:
ClassA() {
printf("ClassA()\n");
}
virtual ~ClassA(){
printf("~ClassA()\n");
}
static ClassA& getInstance()
{
Singleton<ClassA> a;
return a.a;
}
void func(){
printf("func()\n");
}
};

int main(int argc, char* argv[])
{
ClassA& ca = ClassA::getInstance();
ca.func();
return 0;
}

----------------------------------


2020年2月26日水曜日

オペアンプで遊ぶ その2

オペアンプのブログが意外とうけが良いので、調子に乗って本日も真のオペアンプではないですが、オペアンプ系です。


おじさん今から30年以上前の小学4年生の時にLM386というICではじめてインターフォンを作りました。IC一つで作れるんで、小学生でも作れるんですね。
音系の電子工作ってどれもとても音が悪かった記憶が残っています。

しかし、最近、インターネットでいろいろな人が回路図を公開することが増えて、回路図をみんなでレビューしていることと、電子回路のシミュレーションが誰でもソフトで簡単できるようになったことで、何十年も定石と言われていた電子回路が実は違っていたことがわかってきました。

いままで地球は平らで地球の周りを太陽が回っていると思っていたのに、実は地球は丸く太陽の周りを地球が回っているくらい、アナログ電子回路も地動説と天動説くらい常識が変わっています。

http://settembre21x.blogspot.com/2013/09/lm386.html
このLM386革命アンプもそうなのです。
おうちのインターフォンとか給湯器のスピーカーを鳴らすために使われている50円くらいの定番のIC。音が悪いけど値段が安いのでありとあらゆるところに使われています。
このICがちょっと回路を直すだけで、10万円相当のハイレゾコンポになるらしいのです。

おじさんも先日のA47アンプのオペアンプの技術解説をみていてLM386のアンプも同じなんじゃないって思っていたんだよね。
もう何年も前にそのことに気づいている人がいるのね。
最初に気づいたこの人ほんとすごいねー。

先日のオペアンプもそうですが、おじさん、いままで音が悪いのはICの性能が悪いからだったと思っていたんですが、IC周辺のリファレンス回路が間違っているのね。

いままで安かろう悪かろうだと思われていた電子回路が、ほんのちょっと頑張って改良すれば、高級オーディオ並みにまで音が良くなるっぽいです。
とりあえず回路図は複製できた。

http://symphonical-rain.main.jp/?p=13205
去年末ごろからダイソーで見かける300円のイヤホンも市販の1500円くらいのイヤホンと同じくらい音質がいいらしいし、最近は十万円くらいのオーディオ機器は自分で千円くらいで作れるんですね。
前回のヘッドフォンアンプにつなげればもう高級オーディオいらない??

これ作って実験してみようっと。