Take’s diary

Macとマイコンに関すること--ワクワクの製作日記

Jetson Xavier にインストールした OpenFremeworks で YOLOを動かす。 (Yolo on Jetson Xavier with OpenFremeworks.)

ボクの実力ではもうできないと思ってました。I thought that I can no do this.

f:id:TAKEsan:20180923130424j:plain

  今回の完成形。Zavierにインストールしたopenframeworksでyoloを実行させているところです。This completion form. I am running yolo with openframeworks installed in Xavier.

 YOLOopenFrameworks(以下OF)で実行できるofxDarknetというaddonが存在します。ただしWindowsとMacのみ。他にLinux用のofxDarknetもあるのですが、母艦でもXavierでも上手くインストールできません。1週間以上トライしてなんとか動かしたらimagenetでも10fpsまで行きませんでした。しかも肝心のYOLOが動かない。いろいろやってるうちに分かったのは、このaddonTinyYOLOしか動かない(yolo9000も動くらしいが9000クラスもあるため。スピード的にリアルタイムは不可能)ことが判明。そして、GPU演算に16bit浮動小数点が指定できないので、Xavierには全く向かないことが分かってきました。こちらはYOLOを利用して、何か作りたいわけですから、これジャーなーと言うことで、10日目で諦めました。でもDarknetのファイル構成など少しずつ分かってきたので、いくらかスキルアップにはなった模様。 OFOpenCV3.4をリンクして、これに入っているYOLOを使う方法もありますが、Darknetと比較すればかなり遅くなると思われます。

There is an addon called ofxDarknet that can run YOLO with openFrameworks (OF) below. However, Windows and Mac only. There is also ofxDarknet for Linux, but you can not install it successfully on both the mother ship and Xavier. I tried it for more than a week and managed somehow but imagenet did not go up to 10 fps. Moreover, the important YOLO does not work. While I was doing various things, this addon works only TinyYOLO (yolo 9000 also works, but because there are 9000 classes, it is impossible to realtime speed). And since we can not specify 16 bit floating point for GPU operation,  it is not suitable for Xavier at all. Because I want to make something using YOLO, I gave up on the tenth day alter all. But as I learned about Darknet's file structure little more than before, it seems that I got some skill up. There is a way to link OpenCV 3.4 to OF and use YOLO contained in it, but it seems to be considerably slow compared to Darknet.

さーて、どうするか?。Well, what do I do? .

 以前TX1でJetson InferrenceをOFに組み込んだときのことを思い出しました。一度ビルドした標準のDarknet(前回紹介したやつ)のコンパイル済みライブラリ(.soファイル=Shared Object)を取り込む方法。これだと多少手間は必要ですが、本来のスピードと性能は維持されることになります。標準Darknetでは16bit浮動小数点指定と、.soファイルが作れませんが、これだと作れます。ぜったいにできる!!

I remembered what happened when Jetson Inferrence was incorporated into OF before on TX 1.

How to import the compiled library (.so File =Shared Object) of the standard Darknet (introduced last time) that was built once before. Although this requires some trouble, the original speed and performance will be maintained. Standard Darknet does not make 16 bit floating point specification and .so file, but it can be created with this. You can do it by all means! !

 ヒントはダウンロードしたDarknetフォルダにあるMakefileと、2つのyoloサンプルsrc/console_dll.cppsrc/yolo_v2_class.hppにありました。これらのファイル中でOpenCVに関連する部分を取り除いていくと、画像認識に必要な関数は4つだけである事が分かります。つまりweightcfgクラス名ファイルを読み込む3つの関数。それに画像と、認識基準係数を指定して見つかっただけのバウンディングボックス座標とクラス番号等が戻ってくる関数です。OFの動画データをDarknet画像入力データimage_tに変換する部分が最大の山で、今回OpenCVを使っているのはこの部分だけとなります。cv::Mat ---> image_t 変換に作者の書いたソースを使いましたが、OFのデータからわざわざcv::Matに変換しなくても(つまりOpenCVを使わなくても)良さそうな感じ。余計なことはこのくらいにして、

Hints were found in the Makefile in the downloaded Darknet folder and two yolo samples src / console_dll.cpp, src / yolo_v2_class.hpp. If you remove the part related to OpenCV in these files, you can see that there are only 4 functions necessary for image recognition. That is, weight and cfg, 3 functions to read the class name file.

And a function that returns images and bounding box coordinates just found by specifying the recognition criterion coefficient and class numbers . The big issue to convert OF moves data into Darknet image input data image_t, and this time it is only this part that uses OpenCV. I used the source written by the author for cv :: Mat ---> image_t transformation, but it does not bother to convert from OF data to cv :: Mat (that is, without using OpenCV?) feel good . Do not care,

横取り作戦開始。 Mission start.

 Xavierの場合、内部eMMCは容量が少ないので、必ず外付けHDDかSSDまたは内蔵SDカードでインストール作業を行う必要があります。

 まず、上記サイトからダウンロードした圧縮ファイルを展開して、フォルダ名をとりあえずdarknet-XXXとし、Darknetをビルドします。ソースを見るとOpenCVを使ってるのは画像処理部分ですが、OpenCVに関わる余計なライブラリの指定や、OFと競合が発生する可能性(ofxDarknetの問題点)があります。OFにはofxCVというOpenCVのaddonがあるのでこれが利用できます。なのでOpenCVを使わない設定にすることが必要。darknet-XXXに入り、Makefileの中身を以下のように修正します。で、make  !!

In the case of Xavier, the capacity of the internal eMMC is small, so you must install it with an external HDD, SSD or internal SD card.

First, unzip the compressed file downloaded from the above site, set the folder name as darknet - XXX for the time, and build Darknet. Looking at the source, using OpenCV is the image processing part, but there is the possibility of specifying extra libraries related to OpenCV and possibility of conflict with OF (the problem of ofxDarknet). There is an OpenCV addon named ofxCV in OF, so we can use it. So it is necessary to make settings that do not use OpenCV. Enter darknet - XXX, and modify the contents of Makefile as follows. So, make !!

f:id:TAKEsan:20180923110951p:plain

1〜7行目まで上のように修正、36行目ARCのコメントを外し、数値3カ所を修正Xabierは62、TX2は61にします  Modify 1 ~ 7 lines as above, remove comment on line 36 ARC, fix 3 numerical values Xabier will be 62, TX 2 will be 61

 最終的にdarknet.soが出来ていることを確認します。今回は/usr等に移動せず、この場所のまま利用することにします。

Make sure that darknet.so is finally created. I will not move to / usr etc this time and will use this place as it is.

     f:id:TAKEsan:20180923111448p:plain

 openFrameworks0.10.0をインストール(以下記事参照)

 ofxCV addonをインストール Install ofxCV addon

https://github.com/kylemcdonald/ofxCv/

プロジェクトフォルダを作成 Create project folder

of/app/myapp/enptyExample を同じ場所にコピーしてここではDarknet_TESTとしました。

I copied   of / app / myapp / enptyExample to the same place and set it here as Darknet_TEST.

addons .makeファイルを作成  Create addons .make file

Darknet_TESTに入り、nanoとかで

     ofxCv

     ofxOpenCv

と書いて保存 します。

 

Go into Darknet_TEST using nano editor or so.

      ofxCv

      ofxOpenCv

Write and save.

f:id:TAKEsan:20180923111917p:plain

config.makeファイルを修正 Fixed config.make file

上図のように81行目付近と110行目付近(書き込みはどこでも良いのですが)2カ所付け足します。

以下2行です。

 

 PROJECT_LDFLAGS= -lm -pthread -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand -lstdc++  -L ./ /media/nvidia/TX2/XAVIEL/darknet-XXX/darknet.so

  ※SOファイルの場所は自分の環境に合わせて下さい。

 

 PROJECT_CFLAGS = -DGPU -I/usr/local/cuda/include/ -DCUDNN -DCUDNN_HALF -Wall -Wfatal-errors -Wno-unused-result -Wno-unknown-pragmas -DGPU -DCUDNN_HALF

 

As shown in the above figure, we add two places near the 81st line and the 110th line (writing is OK anywhere).

It is two lines below.

  PROJECT_LDFLAGS = -lm -pthread - L / usr / local / cuda / lib64 - lcuda - lcudart - lcublas - lcurand - lstdc ++ - L. / /Media/nvidia/TX 2 / XAVIEL / darknet - XXX / darknet.so

  ※ Please match the SO file location to your own environment.

  PROJECT_CFLAGS = - DGPU - I / usr / local / cuda / include / - DCUDNN - DCUDNN - HALF - Wall - Wfatal - errors - Wno - unused - result - Wno - unknown - pragmas - DGPU - DCUDNN - HALF

    

srcフォルダの中にリンクフォルダを作る  Create link folder in src folder

Darknet-TEST/src にリンクファイルを作ります。今回は下図のようにsrc1としました。

リンク先はDarknetフォルダにあるsrcフォルダです。

Create a link file in Darknet-TEST / src. This time it is src1 as shown below. The link destination is the src folder in the Darknet folder.

   f:id:TAKEsan:20180923112731p:plain

binフォルダの中にデータを追加 Add data to bin folder

darknet-XXX/cfg  darknet-XXX/data  の2つのフォルダをコピー(Darknetフォルダにあるcfgとdataフォルダです)

さらにweightファイルをダウンロードしてコピーします。今回はyolov2.weightsとyolov3.weightsの2つを入れておけば良いと思います。ダウンロード方法は上記Darknet ホームページに書いてあります。(確かDarknetインストール時に自動でダウンロードされたような....)

 また、dataフォルダの中にcooperBlac.ttfフォントファイルをコピーしておきます。(of/examples/graphics/fontShapesExample/bin/dataに入ってる)

最終的にbinの中身は以下のようになります。余計なweightsファイルが入ってますが気にしない。

Copy two folders darknet-XXX / cfg darknet-XXX / data (cfg and data folder in the Darknet folder)

Download the weight file and copy it. In this time I think that it is good to put in two of yolov2.weights and yolov3.weights. The download method is written on the above Darknet homepage. (It seems like it was automatically downloaded at Darknet installation ....)

Also copy cooperBlac.ttf font file into the data folder. (In of/examples/graphics/fontShapesExample/bin/data)

Eventually the contents of the bin will be as follows. There is an extra weights file but not mind.

f:id:TAKEsan:20180923113339p:plain

srcの中にプログラムを入れる Put the program in src

 以下よりmain.cpp、ofApp.h、ofApp.cppを上書きして下さい。ソースは雑ですがご勘弁を....。

Overwrite main.cpp, ofApp.h, ofApp.cpp from below. The source is sloppy but I am sorry ....

以下 main.cpph


#include "ofMain.h"
#include "ofApp.h"

//========================================================================
int main( ){

	ofSetupOpenGL(1024,768, OF_WINDOW);			// <-------- setup the GL context

	// this kicks off the running of my app
	// can be OF_WINDOW or OF_FULLSCREEN
	// pass in width and height too:
	ofRunApp( new ofApp());

}

以下 ofApp.h


#pragma once

#include "ofMain.h"
#include "ofxCv.h"

class ofApp : public ofBaseApp{
	public:
		void setup();
		void update();
		void draw();
		
		void keyPressed(int key);
		void keyReleased(int key);
		void mouseMoved(int x, int y);
		void mouseDragged(int x, int y, int button);
		void mousePressed(int x, int y, int button);
		void mouseReleased(int x, int y, int button);
		void mouseEntered(int x, int y);
		void mouseExited(int x, int y);
		void windowResized(int w, int h);
		void dragEvent(ofDragInfo dragInfo);
		void gotMessage(ofMessage msg);
		
		ofVideoGrabber video;
		ofImage img;
};

以下 ofApp.cpp


#include "ofApp.h"
#include "src1/yolo_v2_class.hpp"    // imported functions from DLL

std::string  names_file = 	"data/coco.names";
std::string  cfg_file = 	"cfg/yolov2.cfg";
std::string  weights_file = "yolov2.weights";

float const thresh = 0.20;

cv::Mat 		mat;
ofTrueTypeFont cop20,cop50;
Detector detector(cfg_file, weights_file);
std::vector result_vec ;
float ttt,ttt1;  //Time
image_t xxx;
std::vector objects_names_from_file(std::string const filename) {
    std::ifstream file(filename);
    std::vector file_lines;
    if (!file.is_open()) return file_lines;
    for(std::string line; getline(file, line);) file_lines.push_back(line);
    std::cout << "object names loaded \n";
    return file_lines;
}

auto obj_names = objects_names_from_file(names_file);
  
void show_console_result(std::vector const result_vec, std::vector const obj_names) {
    for (auto &i : result_vec) {
        if (obj_names.size() > i.obj_id) 
			ofNoFill();
			ofSetLineWidth(3);
        
			//Color Set!!
			int const colors[6][3] = { { 1,0,1 },{ 0,0,1 },{ 0,1,1 },{ 0,1,0 },{ 1,1,0 },{ 1,0,0 } };
			int const offset = i.obj_id * 123457 % 6;
			int const color_scale = 150 + (i.obj_id * 123457) % 100;
			ofSetColor(colors[offset][0]*color_scale, colors[offset][1]*color_scale, colors[offset][2]*color_scale,80);
			ofDrawRectRounded(i.x,i.y,i.w,i.h,5);
			//Mozi Draw!!
			string ss;
			ss=" "+ obj_names[i.obj_id]+" "+ofToString(i.prob*100,1);
			ofFill();
			ofDrawRectangle(i.x,i.y,i.w,20);
			ofSetColor(255,255,255,95);
			cop20.drawString(ss, i.x,i.y+15);
    }
}
static image_t make_empty_image(int w, int h, int c)
    {
        image_t out;
        out.data = 0;
        out.h = h;
        out.w = w;
        out.c = c;
        return out;
    }
static image_t make_image_custom(int w, int h, int c)
    {
        image_t out = make_empty_image(w, h, c);
        out.data = (float *)calloc(h*w*c, sizeof(float));
        return out;
    }
static image_t ipl_to_image(IplImage* src)
    {
        unsigned char *data = (unsigned char *)src->imageData;
        int h = src->height;
        int w = src->width;
        int c = src->nChannels;
        int step = src->widthStep;
        image_t out = make_image_custom(w, h, c);
        int count = 0;

        for (int k = 0; k < c; ++k) {
            for (int i = 0; i < h; ++i) {
                int i_step = i*step;
                for (int j = 0; j < w; ++j) {
                    out.data[count++] = data[i_step + j*c + k] / 255.;
                }
            }
        }
        return out;
    }

void detect_x(image_t x)
{
ttt=ofGetElapsedTimef();
	    result_vec = detector.detect(x,thresh,false); 
ttt=ofGetElapsedTimef()-ttt;
		ttt1=ttt;	    
}
//--------------------------------------------------------------
//  DetectNet部分をマルチスレッドにする。
class Detect_x: public ofThread {
public:
	
	void threadedFunction(){
		ok=false;
		detect_x(xxx);
		ok=true;
	}
	bool ok;
};
Detect_x Found_X;
//--------------------------------------------------------------
void ofApp::setup(){

    cop20.load("cooperBlack.ttf",15,true,true,true);
    cop50.load("cooperBlack.ttf",30,true,true,true);
    img.allocate(960,720,OF_IMAGE_COLOR);

    video.setDeviceID( 0 );
	video.setup(960,720,OF_PIXELS_RGBA);
	Found_X.ok=true;
}
//--------------------------------------------------------------
void ofApp::update(){
ofLog() << ofGetFrameRate();
	video.update();
	if(video.isFrameNew()==true){
		img.setFromPixels(video.getPixels().getData(),video.getWidth(),video.getHeight(),OF_IMAGE_COLOR);
		mat=ofxCv::toCv(img);
		if (Found_X.ok){
            cv::Mat imgx;
			cv::cvtColor(mat, imgx, cv::COLOR_RGB2BGR);
			std::shared_ptr image_ptr(new image_t, [](image_t *imgx) { detector.free_image(*imgx); delete imgx; });
			std::shared_ptr ipl_small = std::make_shared(imgx);
			*image_ptr = ipl_to_image(ipl_small.get());
			xxx=*image_ptr;
        if (Found_X.ok){
			Found_X.ok=false;
			Found_X.startThread();
	    }
    }
 }

}
//--------------------------------------------------------------
void ofApp::draw(){
	ofSetColor(255);
    video.draw( 0, 0 );
    
    Found_X.lock();
    show_console_result(result_vec, obj_names);
    Found_X.unlock();

    string ss1;
    ss1 =ofToString(ttt1,3)+ " sec";
    ofSetColor(255);
    cop50.drawString(ss1, 50,700);
}
//--------------------------------------------------------------
void ofApp::keyPressed(int key){
}
//--------------------------------------------------------------
void ofApp::keyReleased(int key){
}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y){
}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){
}
//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){
}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){
}
//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){
}
//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 
}

bin/cfg/yolov2.cfgを修正 Fix bin/cfg/yolov2.cfg

 bin/yolov3.weightを使用する場合はyolov3.cfgの同じ箇所を修正

 最初の方を以下のように変更

[net]

# Testing

#batch=1         コメントを入れる

#subdivisions=1   コメントを入れる

# Training

 batch=64                   コメントを外す

 subdivisions=8         コメントを外す

width=608       608に変更 スピード重視の場合は416(32の倍数なら動く)

height=608      608に変更    スピード重視の場合は416(32の倍数なら動く)

channels=3

 

Fix bin/cfg/yolov2.cfg

When using bin/yolov3.weight fix the same place in yolov3.cfg

Change the first one as follows

[net]

# Testing

# batch = 1                Add comment

# subdivisions = 1     Add comments

# Training

 batch = 64               Remove comments

 subdivisions = 8      Remove comments

width = 608              Changed to 608 for better speed it is 416 (it works if it is a multiple of 32)

height = 608             Changed to 608 for better speed it is 416 (it works if it is a multiple of 32)

channels = 3

ビルド&実行 Build & run

Darknet-TESTフォルダの先頭に戻って、カメラを接続し、

      make

      make run 

で実行です。

 最初の手順は面倒ですが、2回目からはフォルダ毎コピーすれば、新しいプログラムが作成できます。まさかこれだけでハイスピードYOLOが動くとは思いませんでした。

 認識部分は別スレッドで動かしているので、画像そのもののスピードは落ちず、OF上60fpsを維持しています(画像スピードはカメラに依存)。認識スピードは前回の記事に書いたモノとほぼ同じです。つまり実用レベルのスピードでした!!。

Return to the beginning of the Darknet-TEST folder, connect the camera,

      make

      make run

It is executed in.

The first step is troublesome, but from the second time you can copy new folders and create new programs. I did not think that high speed YOLO would work just by this.

Since the recognition part is moved by another thread, the speed of the image itself does not decrease and it keeps 60 fps on OF (image speed depends on the camera). The recognition speed is almost the same as the one written in the last article. That was a practical level of speed! ! .

実際にXavierで実行してみると....... Actually running it with Xavier .......

                   

今回はyolov2.weightを使用。yolov2.cfg 中width=608 height=608にした場合。画像は30fpsのままで認識にかかった時間は左下に表示この場合は1/0.069=14.49fps!!  We used yolov2.weight this time. When yolov2.cfg width = 608 height = 608. The image remains at 30 fps and the recognition time is displayed in the lower left. In this case 1 / 0.069 = 14.49 fps !!  

                   

今回はyolov2.weightを使用。yolov2.cfg 中width=416 height=416にした場合。この場合は1/0.039=25.64 fps!! Tiny-yoloだとここまで認識しません。 We used yolov2.weight this time. When yolov2.cfg  width = 416 height = 416. In this case 1 / 0.039 = 25.64 fps !! Tiny-yolo does not recognize so far.

 メインスレッドがほぼ60fpsなので認識内容を元にまだまだ別の複雑な処理が可能になります。これはCフレームワークに乗せることの醍醐味ではないでしょうか?OFのほとんどのaddonが実行できることになります。YOLOの学習作業は、過去の記事で試していますがこれは、あくまでも母艦Ubuntuを使った場合です。

Since the main thread is almost 60 fps, another complicated processing can still be performed still more based on the recognition content. Is not this the real pleasure of putting it on the C framework? Most addons of OF can be executed. YOLO's learning work is tried in the past articles, but this is the case when using the mother ship Ubuntu.

これがXavier内で簡単な学習ができるとなると、アマチュアでも同一機種内で、どこでも学習と認識の応用が出来ることになります。また、今回はYOLOの実現だけですが、Darknetにはまだまだ実用的なexampleがたくさんあります。もうワクワクどころじゃ無くなってきました。

                           では、また.........。

If this makes it easy to learn in Xavier, amateurs will be able to apply learning and recognition anywhere within the same model. Also, this time only YOLO realization, Darknet still has many practical examples. It is no longer a thrilling excuse.

 

  see you..........

Jetson Xavier を動かしてみた。

JETSON XAVIER(ザビエル)が来た

今回は発売間もないザビエルを手に入れたので、簡単なテストやインストール結果などを書くことにします。若くは無いので開封の儀は、止めておきます。

 本体は、プレゼン写真で見る限りエンジニアリングプラスチックかと思っていましたが、アルミダイキャスト製でずっしりと重い立派なものでした(ノートパソコン1個分ですからトーゼン)。性能に関してはメーカーの資料を見て下さい。内部eMMC容量以外は現状不満の無い内容です。CPUは最大8個すべて2.2Gで動きます。

f:id:TAKEsan:20180913141710j:plain

f:id:TAKEsan:20180913141710j:plain   f:id:TAKEsan:20180913141759j:plain

 USBCが2カ所付いているのでいろいろなものが接続できそうです。TX2と違ってWIFIが付いていないのですがUSBドングルでなんとかなりそう。

 直ぐに始められるというキャッチが頭に入ってたので、ケーブル類をつないで、パワースイッチを入ればなんとかなると思ってました。しばらくすると何やらコマンド画面が出てきて、インストーラを実行しないとダメとか....。表示通りコマンドを入力して、

f:id:TAKEsan:20180913142257j:plain

 

少し待つと、UBUNTU18.04画面が現れます。反応が異様に鈍い...。

 中をよく調べると、初期状態ではCUDAも何もかも入っていない。スイッチオンではUbuntu18.04が動くだけでした。やっぱり...と思いながら、気持ちを入れ直して、いつものJetpacインストールを行います。経験上、OSもすべて上乗せし直したした方が良いようです。jetpacのバージョンは4です。

Jetson Download Center | NVIDIA Developer

 ここからJetpack4.0とインストールガイドをダウンロードしますが、NVIDIAのユーザー登録が必要です。

 基本は今まで書いたのとほぼ同じ。

NVIDIA JETSON TX1 を動かしてみた!! - Take’s diary

 インストール用には、母艦接続用USBケーブルを差して(一番上の写真が接続場所です)母艦Ubuntu(今回はIntel i7 6700k+GTX1080ti)と接続します。母艦は16.04でもOKでしたが、OpenCV3.3.1でないとエラーになります。3.4の方はダウングレードする必要があります(他に方法があると思いますが)。フォースリカバリーモードにする方法も前と同じです。

.............30分前後時間が必要。

Jetpacインストールが終わったらまず最初にやっておくこと。

 ザビエルもTX2も、省エネ型とかMAXパワーとかいろいろなCPUモードで動きます。モード切替方法は大切だと思うのですが、マニュアルには詳細な説明が無いようです。最強モードにするにもこつが必要。

まずCPUとGPUのクロックを最大にします。

home へ行き

sudo nvpmodel -m 0

sudo ./jetson_clocks.sh

sudo ./jetson_clocks.sh -show    で変更内容が確認できます。

--showは別ですが、前2行を続けて実行しないとダメみたいでした。続けないと上手く変更できません。マニュアルに書いてあるjetson_clocksを実行しただけでは、最大になりません。

 sudo nvpmodel コマンドは -m 0 ~ -m 6 まで指定できて、クロックの最大設定やコアの優先が決まります。./jetson_clocks.sh -showで「自分の目で」現在の設定内容を確認しましょう。表示内容を見れば納得です。

 jetson_clocks.shを実行した後はファンが比較的大きな音で強力に回り出します。静音化対策はしていないよう....。

CPUはあまり早くない。

 早速いつものメインCPUの体感速度がわかるPythonを実施すると、6秒程度。これはTX2から30%程度のアップにとどまっています。ちなみに母艦Intel i7+GTX1080tiでは3.2秒前後なので、私の母艦の半分程度のスピードです。まーTX2のクロックアップ分と考えた方が良さそうです。それにしてもUnuntu18.04はとろい。まだ公開したてだからしょうがないっちゃーしょうが無い。みんなで寄付が必要ですねー。

 経験上以下のソースだけでCPUメインコアの速度が分かります(私には...。OSの体感スピードが、出てきた数値とほぼ比例。)。test.pyかなんかで以下のソースを保存して下さい。その場合実行はpython test.pyです。

************************************************************************************************

def fib(n):

    if n <= 1: return n

    else: return fib(n-2) + fib(n-1)

 

def entry_point(argv):

    a = time.time()

 

    result = fib(36)

    timespan = time.time() - a

    print (result)

    print (timespan)

 

    return 0

 

def target(*args):

    return entry_point, None

 

if __name__ == "__main__":

    import sys

    entry_point(sys.argv)

*************************************************************************************************

ザビエルの売りはGPU

 16ビット浮動小数点と、整数演算が小型の割にとても早いとのことですが、発表している数値は理論値に近いと思われます。これを直感的に体感するにはどうしたら良いか?。ありました。Darknetです。ただしこの方の公開しているものです。

github.com

 ここでインストールするDarknetは以前のV2.0も最新のV3.0も簡単に変更できる上に、ザビエルの売りである16ビット浮動小数点演算の指定や、soファイルの作成も可能で、標準のDarknetより早くなるようです。

 キモはMakefileの変更で、以下のようにします。CPU、CUDNN、CUDNN_HALF、

OPENCV、LIBSOはすべて1、CUDAアーキテクチャはsm62に変更します。で、普通にmake -j8であっという間にコンパイル完了。ザビエルはCPUコアが8つなので、-j8が指定できます。

f:id:TAKEsan:20180913202530p:plain

    上の画像では ARCH= の部分コメントの#マークを外してませんが、外すことを忘れずに!!

 でスピードはどうなの?

 以下のDarknet設定は、すべて16bit浮動小数点です。つまりCUDNN_HALF=1。

yoloは入力画像の大きさ設定で、認識スピードや認識率が大きく変化します。設定はyolov2.cfg又はyolov3.cfgの最初の方を変更します。

f:id:TAKEsan:20180913202023p:plain

上図で(この場合はyolov3.weight)widthとheightの数値を変えます。この数値は32の倍数ならなんとかなるようですが、自分のWEBカメラの設定以下である必要ありです。

コマンドは、

./darknet detector demo cfg/coco.data cfg/yolov3.cfg yolov3.weights -c 0

です。yolov2の場合は3を2に変更するだけでした。yolov2.weightsとyolov3.weightsファイルが必要ですが、ファイルダウンロード方法は

YOLO: Real-Time Object Detection

を参考にして下さい。ちなみにcoco.dataとcfgファイルはDarknetに入ってます。

まず母艦でyoloV3から。

 私の母艦環境はIntel i7 6700k+GTX1080ti+Ubuntu16.04です。yolov3.cfgの入力画像の面大きさ設定が416x416、608x608の場合は、どちらも30fps出てます。やっぱ早いですね。ちなみに608x608の方は認識率がかなり高くなります。以下左416x416が、右が608x608です。

f:id:TAKEsan:20180913151037p:plain   f:id:TAKEsan:20180913151111p:plain

        608x608の認識が良いことが分かると思います。

次にザビエル

f:id:TAKEsan:20180913152016p:plain f:id:TAKEsan:20180913152940p:plain

 ザビエルではyolov3.weightを使って入力画像416x416が16.1fps、608x608が9.1fpsでした。ちなみにTX2では4.4fpsと2.3fpsだったので、ザビエルはTX2の3倍以上早いことになります。右上のターミナル画面はCPUとGPUの現在設定画像です(sudo ./jetson_clock.sh --show)。

 ザビエルはここでも私の母艦の丁度半分くらいのスピードになってます。

ソフト起動時は認識画像が最大になり、ザビエルやTX2は、表示スピードがかなり鈍ります。一旦画像を消すと上写真の様な小さな画像に切り替わり、本来のスピードで表示するようになります。表示ルーチンの改良が必要のようです。

調子に乗ってyolov2ではどうなのかですが....。

f:id:TAKEsan:20180913152728p:plain  f:id:TAKEsan:20180913152754p:plain

 なんとザビエルは416x416の方は29.5fps 、608x608で15.5 fpsでした。TX2ではおのおの9.5fps、 5.2fps なのでここでも約3倍速くなりました。入力画像を608x608にするとYolo2でも認識しているものはyolo3とあまり変わらなくなるのが上画像で分かると思います。ただし細かい部分はやはりyolo3でしょうか。でもyolo3の15fps前後っていうのは魅力的ですねー。以下の動画でも確認出来ると思いますがほぼパーフェクト!!。実際ザビエルでリアルタイム画像認識している様子です。(HARF_CUDNN,yolov3,入力画像412x412)

               www.youtube.com

 AI画像認識は、いままで試して見て、デカいコンピューターを使っても現実的ではありません。気軽に持ち運びできないからです。適度に小さいものはそれなりに遅くなる。TX2がそうでした。これは新しい時代が来たって感じです。

さらにダメ押し

 CUDAのサンプルでsmoke paticlesってのがあるんですが、驚くなかれザビエルでは60fps!! 母艦でも60fps。TX2では30FPSでした。以下ザビエルの動画です。左上の60fpsって表示見えます?

           youtu.be

openFrameworksは動くか?

 ザビエルはROSに焦点を絞ってる気配がありますが、私の場合はいつもここから始まります。openFremeworks0.10.0が動きました!!。インストール方法は前の記事と同じ

takesan.hatenablog.com

 

ですが、Tessだけは再コンパイルが必要でした。tess2.aのダウンロードは以下のアドレス、

Takesan - Google ドライブ 

 この中でkiss&tess.zipを選択して下さい。openFramworks/libの中のkisstessを入れ替えます。openFrameworksでビルドしたものは60fpsが最大ですが、特に負荷の高いものなどは確実に早くなりましたし、マルチで動かしてもスピードが落ちません。ネッ。

    youtu.be

結論

 問題はGPUの使い方に尽きると思います。なので一般向けではありません。つまり普通にUbuntuマシンとして使ったら、TX2と変化があまり感じられないと言うこと。ザビエルのGPUを使った今回のAI体感では、i7+GTX1080tiよりは50%スピードダウンするがTX2の3倍速いってことが結論。いろいろなデータを調べるとGTX1060クラスメモリー容量の多いグラフィックボードを積んだ消費電力の小さなパソコンが、手のひらに乗ったというような事になるのですが、そうするとTX2では最初から諦めていた学習も可能なわけで....。

 今回は、あくまでも他人様のソフトで検証してます。これからさらに最適化すればすごいことになりそう....!!。あとはopenFrameworksにDarknetを入れるだけですが、これがなかなかできない。

--->9/23 63才の誕生日直前に成功しました!!次の記事をご覧下さい。この時と同じような精度と認識スピードで、OFでもYOLOで画像認識できました。画像スピードも落ちないので、上出来だと思ってます。

--->10/7   TX2でも9/23と同じことをやってみました。認識率の低いtinyではなくyolov2.weightを使い、マルチスレッドを効率的に使い、入力画像関連の変更をすると、認識率はほとんどそのままで、TX2で最大30fps以上というトンデモナイスピードが出ることがわかりました。

 

 

                          では、また.....。

 

 

 

 

JETSON TX2 に OpenFrameworks 0.10.0 をインストール

TX2にJETPACK3.3をインストールしたので、最新のOF(Openframeworks0.10.0)をインストールしてみました。前にも書きましたがTX2には簡単にOFがインストールできません。OFを動かすには内部の変更作業が必要です。今回はソース変更等大きく分けて3つの作業だけで済みました。今回のOFバージョンアップはかなりTX2に向いているようで、0.9.8のようなキーボード入力時の不具合などは解消されています。またサンプルプログラムについてはgles(Pi専用のサンプル)以外は、高負荷のものでも問題なく動くようです。

f:id:TAKEsan:20180827130658p:plain

OFのダウンロード

ここからlinux armv7 をダウンロードします。

https://openframeworks.cc/download/

解凍してフォルダ名をof_10.0に変更したとします。

次に必要ライブラリをインストールします。

sudo apt install cmake

of_10.0/scripts/linux/ubuntu   に行き

sudo ./install_dependencies.sh 

を実行します。前処理が必要だったのですが、10.0の場合はすんなりインストールスクリプトが実行できました。

修正点1

of_10.0/libs/openFrameworksCompiled/project/makefileCommon/config.shared.mk

を開き79行目

else ifeq ($(PLATFORM_ARCH),armv7l)

else ifeq ($(PLATFORM_ARCH),aarch64)

に変更。

修正点2

of_10.0/libs/openFrameworksCompiled/project/linuxarmv7l/config.linuxarmv7l.default.mk

を開き41〜48行目付近

#PLATFORM_CFLAGS += -march=armv7

#PLATFORM_CFLAGS += -mtune=cortex-a8

#PLATFORM_CFLAGS += -mfpu=neon

#PLATFORM_CFLAGS += -mfloat-abi=hard

PLATFORM_CFLAGS += -fPIC

PLATFORM_CFLAGS += -ftree-vectorize

PLATFORM_CFLAGS += -Wno-psabi

PLATFORM_CFLAGS += -pipe

4行にコメント記号を入れ(上記#)、

さらに69〜71行目付近

#PLATFORM_PKG_CONFIG_LIBRARIES += glesv1_cm

#PLATFORM_PKG_CONFIG_LIBRARIES += glesv2

#PLATFORM_PKG_CONFIG_LIBRARIES += eg

3行にコメント記号を入れて保存。

修正点3

OF0.9.8まではapothecaryというライブラリインストーラがあったのですが、最新バージョンでは入ってないので、以前TX2用のOF0.9.8用にコンパイルしたkisstess2ライブラリを所定の場所にコピーします。pocoは0.10.0から標準addonになったようです。

一応以前のものをダウンロードできるようにしておきました。この中からOF10.0lib.zipを選択して下さい。

Takesan - Google ドライブ

解凍するとフォルダの中にlibkiss.a及びlibtess2.aが入っているので

libkiss.aは

of_10.0/libs/kiss/lib/linuxarmv7l

へ重ね書きし、

libtess2.aは

of_10.0/libs/tess2/lib/linuxarmv7l

へ重ね書きします。

最終スクリプトの実行

of_10.0/scripts/linux

へ行き

./compileOF.sh -j4

でOFのコンパイルが始まります。

今回はこれだけでOF0.10.0が使えるようになりました。

f:id:TAKEsan:20180827132942p:plain

       ZEDステレオカメラを接続してVideoGrabberのサンプルソースを実行中

 

 

 

 

 

FLIR ONE Proを分解したら、簡単にLEPTON3.5が取り出せる!!

YOLO学習済みデータをiPhoneで利用する方法は次に回して、今回は最新耳寄り情報です。

FLIR ONE Proって?

 iPhoneやAndroidに取り付けてスマホをサーモグラフィー化する商品で、昨年までの同名商品(FLIR ONE)は、赤外線センサーとして同社のLEPTON3が入っていました。

         f:id:TAKEsan:20180816104920p:plain

 このカメラは、WEBカメラも取り付けてあり、2つのカメラ画像を合成して相互の弱点を補うというものです。しかし、LEPTON3は160X120画素の横長、スマホに表示される画像は縦長です。画像の比率を考慮するとLEPTON3画像のほんの1部しか使っていないことになります。ある意味つまんないですね。

 私たち(渡辺、小野)の作ったThermal Cam3はWEBカメラ未搭載ですが、無線格安、しかもLEPTON3の画像をすべて使い切っています。

 また、この記事でも紹介していますが、以前のFLIR ONEは簡単に分解できることもわかっています。

 果たして新しく、価格が高くなったFLIR ONE Proは簡単に分解できるか?、そしてセンサーは何を使っているかが疑問でした。買うといっても個人ではなかなか........。

今回

 Maker Faire Tokyoで知り合った大澤さんがFLIR ONE Proを持っていると言うことで、早々に分解して送っていただいた写真がこれです。

f:id:TAKEsan:20180816111300j:plain  f:id:TAKEsan:20180816111311j:plain

f:id:TAKEsan:20180816111412j:plain

f:id:TAKEsan:20180816115325j:plain

 本体両側のネジカバーをマイナスドライバーのようなもので外し、ネジ2本を外します。ネジを外す工具は一般的なヘックスローブドライバT5サイズだそうです。面白いことに、以前の製品より構造が簡単になっていることが分かります。LEPTONセンサーはソケットにはまっているだけですから、この段階で簡単に外すことができますし、元に戻すことも以前よりさらに簡単です。

 以下渡辺さん情報。Andoroid版はGPDでも使えるそうです。(FLIR ONEアプリをネットからapk fileダウンロードして入れ替えるとのこと)

f:id:TAKEsan:20180816120247j:plain

f:id:TAKEsan:20180816115523j:plain f:id:TAKEsan:20180816115542j:plain

f:id:TAKEsan:20180816115600j:plain f:id:TAKEsan:20180816115624j:plain

中に入っていたのはLEPTON3.5!!

 いろいろ試していただいた結果、中に入っている赤外線センサーはLEPTON3.5である事がわかりました。この部品はDegiKeyで購入することができて価格は30,631円。

 FLIR ONE Proを持っていて、私たちのThermal Camに興味を持っている方なら、バラさない手はありませんぞ!!

LEPTON3と3.5の違い

 両者とも画素に変化は無く160X120ドットのままですが、出力されたデータから簡単にすべての画素の測定温度を計算できるところが大きく違います。今までのLEPTON3は温度を計算するために、i2cを使ってチップ温度を取得する必要がありました。しかも温度計算方法についてメーカーは非公開。

 LEPTON3.5は通常の使い方をする限りi2cが必要なくなります。i2cのライブラリや余計な計算が必要ないので、私たちの使っているメモリの少ないESP8266には断然有利になります。

 LEPTON3.5用のARDUINO IDEソースも上記記事でダウンロード可能です。

 

 

YOLOオリジナルデータの学習

今回は今年のMaker faire tokyoで使ったAIジャンケンのデータ作成方法を書くことにします。AIの専門家では無いので、固有名詞の間違いはご容赦願います。

         

       今回のデータを使ってジャンケンの手をリアルタイム認識させています。

 YOLOを使った画像認識が早いのは分かりました。ただし練習でおおーっと思った画像認識に使うデータは、他人が用意したものです。自分の必要なデータをどんな用途で使うかで価値が決まって来ますよね。

 用意する学習データは1クラス数千枚単位ですから、アマチュアではチョットって感じです。せっかく苦労して用意しても、使えなかったら何にもならないわけで、今回は「データー作成はそれほどでもなかった」的な記事です。メイカーフェア出展に向けて、サーモグラフィーを利用したジャンケン認識用画像の種類は3クラスとなりますが、これ以上のクラス数でも作成方法の基本は同じです。

 次回は学習済みデータをiPhoneへ移植するまでを取り上げますが、今回の学習済みデータはYOLOが動く環境なら問題なく動作することを確認しています。作成時疑問に思っていたことについても、併せて説明して行きます。

参考資料は1つだけ

 Yoloを使った学習方法の説明は、ネット上でいろいろ見つけることができますが、基本は How to train YOLOv2 to detect custom objects

のようです。疑問に思ったときのヒントは、この記事のどこかに書いてありますので、あまり日本語の記事に惑わされず熟読することが大切。今回は動画から必要画像を取り出して、ただひたすらにクラス分け作業を行う、という方法です。

必要機器

 Mac      : iMacでもMacBookでも何でもOK

 Ubuntuマシン  :必ずNVIDIAのGPUが付いているマシン(今回は1080 Ti 1個)

 iPhone    :  iPhone7以上 (認識に今回はiPhoneを使ったのでUbuntuマシン

         でもOK)

 ソフト    :特に購入ソフトは無し

1.動画を用意

 今回はジャンケンですから、iPhoneで録画したこんな感じの動画を用意します。

f:id:TAKEsan:20180815114234p:plain

 赤外線カメラ画像なのでグレースケールデータだけです。後で説明しますが、クラス分けが大変なので1クラスのみで、あらゆる方向から動画を撮影しました。今回はグー、チョキ、パー それぞれの動画ファイルを名前を変えて用意しています。

 YOLOは同一画像で複数のクラスを認識するので、最初は複数のクラスが写っている必要があるかと思いましたが、そんなことは関係ないようです。また、撮影画像のピクセル数はあまり問題にはなりませんでした。結果的に、720pや1080p、540pが混在しても認識への影響は感じられません。

2.MacのiMoveを使って編集

 iMoveを使いましたが動画編集ソフトなら何でもOKです。上の画像のままでは右の方に余計な部分が写っているので、トリムすることと、対象クラスが写っていない不必要な画像をある程度削除しておくことが目的です。(iMoveではトリムでは無くクロップという単語を使います)最終的に720p、MP4で保存しました。

f:id:TAKEsan:20180815115732p:plain

3.動画を複数のjpg画像に変換

 ここからはUbuntuマシンを使います。まず画像変換ソフトをインストール。動画から画像に変換できるソフトなら何でも良いのですが、ffmpegを使いました。ffmpegはMacでもWindowsでも使えますので、特にUbuntuマシンで無くてもOKです。インストール方法は他の記事を参考にして下さい。

 新しいフォルダを作って、その中にmp4ファイルをコピーします。そのフォルダで以下のコマンドを実行。

   ffmpeg -i input.mp4 -vcodec mjpeg -r 5 image_%03d.jpg

    input.mp4:入力動画ファイル名

    image_%03d.jpg:出力ファイル名 

 image001.jpeg.....と名称の付いたファイルがたくさん作成されますが、この例で「image」部分はクラス毎に名称を変更した方が、後からデータを追加するときに便利です。今回のデータは基本8fpsくらいで、iMoveを使って30fpsで保存しています。そのままでは同じアングルの画像がたくさんできてしまうので、1/6ぐらいに間引いて作成しています。なので作った動画に合わせて-r 5部分を変更します。

 コマンドを実行すると、フォルダ内に連番の付いた大量のjpegファイルが作成されます。今回の場合はクラスが3つなので3回実行。当然ですが連番前の名称を変えていれば、同一フォルダ内に作成してもOKです

4.2回目の間引き

 できた画像を確認して、不必要な画像を削除します。よく注意しながら目標のクラスが写っていない画像や、重複があるものはドンドン削除してしまいます。重複が多い場合は、すべて削除してffmpeg の-r以下の数値を減らして再実行した方が早いかもしれません。ここでの削除作業は後の作業効率や認識結果に関わってくるので、重要です。

5.YOLOをインストール

 以下に沿ってインストール。今回はiPhoneとの連携のためV2.0を使いましたが、最新のものでOKです。

Installing Darknet

 GPUを使わなければ意味がありませんのでmakefaileの中のGPU=1 を有効にすることと、OpenCVはあらかじめインストールしておいて OPENCV=1にしておくことも忘れずに!!。OpenCVをインストールしないと、動画系のテストプログラムが実行できません。Yoloはmakeにさほど時間がかかりません。Caffeとは雲泥の差です。

6.ラベリングする(アノテーション)

 Yoloは上記で作ったイメージファイルの他に、これらに対応したTXTファイルが必要です。(イメージファイルのどの部分に対応するクラス画像があるかを示すテキストファイルで、イメージファイルの枚数分必要です)ここら辺が一番単調で、手作業では時間のかかる部分となります。今回ラベリングソフトはlabelimgを使いました。以下インストール方法ですが、すでにpython3をインストールしている場合は3,4行目は必要ありません。

labelimgと言うフォルダができるので、この中に自分の好きな名称のフォルダを作り、先ほど作成したイメージデータを全部コピーします。最初は3つのフォルダが必要かなとも思いましたが、クラス毎にimgデータのファイル郡の名称を変えていればごちゃ混ぜにしてOK。

 次にlabelimg/data の中にあるpredefined_class.txtを修正します(これが一番手っ取り早い)。最初は20クラスぐらいあらかじめ書き込んでありますが、今回は3クラスだけなので、既に書き込んであるクラス名を全部消し、自分の好きな名称でクラス名を改行して書き込みます。今回はguu、tyoki、paaにしました。これは必ず修正が必要です。

labelimgフォルダの中でプログラムを実行します。

 python3 labelimg.py

以下のような画面が表示されるので、次のような設定を行います。

f:id:TAKEsan:20180815230111p:plain

 左側 OpenDir で先ほど作ったイメージデータが入っているディレクトリを指定

    Change Save Dir で同じディレクトリを選択。イメージもアノテーション

    ファイルも同じディレクトリに入れちゃいます。

    さらにSaveの下にある PscalVOC を選択して、必ずYOLOに変更します。

    これを忘れると、最初からやり直しになるので注意

 そして上部のメニューバーからView->Single Class Modeにチェックを入れます。

Single Class Modeとはクラス名が同一の場合、連続でデータを入力できるので、1つのクラスに対応したjpeg画像がここで生きることになります。但しSingle Class Modeにすると最初だけClass名選択をする必要があります。

 今回は連番以外の名称をクラス別に変えているので途中クラス選択の必要がありますが、その場合は一時的にSingle Class Modeを外して違うクラスを選択後、またSingle Class Modeにします。とにかく少ないデータで一度使ってみて、コツをつかんで下さい。操作は直感でできちゃいます。

 画像中のクラス画像選択はD、 選択した画面を保存して次の画面に移る場合はWキーを押します。

 D-->マウスで対角指示-->W-->D-->マウスで対角指示-->W と連続してクラス指定が可能で、慣れると1枚の画像のアノテーション所要時間は2秒程度。上手く行けば1時間で1500枚以上のクラス分け(アノテーション)が可能です。但ししばらく作業をしていると、キーボードを操作している左手がしびれてしまいますのでご注意を!!。

 ここで1枚の画像に複数のクラス画像が入っている場合、いちいちクラス名を指定しなければ無いのでグンと効率が落ちます。

ってことをマスターしてしまえば、基本データ作りは一応完成です。ここで作ったデータはtiny_Yoloでも標準Yoloでも使用できます。

 Yoloのアノテーションのファイルは以下のようになっています。内容が違っている場合や、クラス番号が正常で無い場合はlabelimgの設定を疑って下さい。

f:id:TAKEsan:20180816102939p:plain

最初の数値がクラス番号で、今回は0~2の3種類。この場合は画像に存在するクラスがTyoki=1で1個のみ。その次の4つの数値はTyokiの範囲を示します(yoloは比率で表すようです)

f:id:TAKEsan:20180816103351p:plain

       この場合は1つの画像にGuu=0とPaa=2が存在していることを示します。

7. またまたダメ押しのチェック

 これで終了とも思えるのですが、必ず間違いがあるので、もう一度ダメ押しのチェックを行います。イメージとアノテションファイルをごちゃ混ぜにした成果がここに出ます。以下のように同一名のjpgとtxtファイルができますが、たまにjpgファイルに対応しているtxtファイルが作られていない場合があるので、それを見つけ出してダブっているjpgファイルを消してしまいます。例えば普通は以下のようにjpgとtxtが交互に並びますが、txtファイルが作成されていないと、jpgファイルが続くことになります。

f:id:TAKEsan:20180816002346p:plain

8.yoloの下準備

 Darknetをインストールするとdarknetディレクトリができます。

darknet/data の中に先ほど作ったjpgとtxtの入ったごちゃ混ぜデータをディレクトリごと移動またはコピー。

 その中にprocess.pyの名称で、以下の内容のファイルを作成

import glob, os

# Current directory
current_dir = os.path.dirname(os.path.abspath(__file__))

# Directory where the data will reside, relative to 'darknet.exe'
path_data = 'data/obj/'

# Percentage of images to be used for the test set
percentage_test = 10;

# Create and/or truncate train.txt and test.txt
file_train = open('train.txt', 'w')  
file_test = open('test.txt', 'w')

# Populate train.txt and test.txt
counter = 1  
index_test = round(100 / percentage_test)  
for pathAndFilename in glob.iglob(os.path.join(current_dir, "*.jpg")):  
    title, ext = os.path.splitext(os.path.basename(pathAndFilename))

    if counter == index_test:
        counter = 1
        file_test.write(path_data + title + '.jpg' + "\n")
    else:
        file_train.write(path_data + title + '.jpg' + "\n")
        counter = counter + 1

これは

How to train YOLOv2 to detect custom objects

に掲載されてます。何をするのかというと、ディレクトリの中のデータを教師ファイルと学習用ファイルに分けてそれぞれリストファイルを作ります(train.txtとtest.txt)標準では教師ファイルは全体の10%ですが、これを変える場合はpercentage_test = 10 のところを15とか30とかにします。この数字の変更はかなりシビアで、最終的にできた学習済みファイルの結果を試して最適値を探します。最終的にと簡単には言いますがGTX1080 Tiで学習させても数時間必要ですので、容易ではありません。今回の場合30%で一番良い結果が得られました。実行方法は、

   python process.py

 実行環境はPython2である事に注意です。画像が不足していると感じた場合は、追加画像群を他の場所でアノテーションして、すべてこの場所に放り込み、再度python process.pyを実行するだけです(その場合は画像名称を変えることを忘れずに!!)。

9.cfgファイルを変更

 tinyYOLO標準YOLOでは変更内容が違うことに注意して下さい。変更ファイルは、darknet/cfgの中に入っています。

tinyYOLOの場合(今回のiPhoneを使う場合)  

  tiny-yolo-voc.cfgを変更します

   2行目 batchを64に変更(初めから書いてあったと思う)

   3行目 subdivisions=8に変更(これも初めから書いてあったと思う)

  120行目 classes=20を3に変更 (クラスの数です)

  1.   114行目 filters=512 を 40に変更 (class+5)*5の計算値 (3+5)*5=40

                クラスの数が変わればfiltersの数値も変わることに注意!!

標準yoloの場合

    How to train YOLOv2 to detect custom objects

  に沿って、yolo-voc.cfg内容を変更します

10.あらかじめトレーニングされたデータをダウンロード

 一応 darknet/ 内に必要ファイルをダウンロードします。このファイルは学習を収束させるために必要なファイルだそうです。  

YOLO V2とV3とではデータが違います。

  YOLO V2.0の場合は

     downloaded

    darknet19_448.conv.23がダウンロードされます。

  YOLO V3.0の場合は

    wget https://pjreddie.com/media/files/darknet53.conv.74

    darknet53.conv.74がダウンロードされます。

11.さらに2つのファイルを作ります

  darknet/cfgの中にobj.namesファイルを作成

  obj.names :先ほどのpredefined_class.txtと内容が同じファイル。

        クラス名を改行しながら書きます。今回はGoo、Tyoki、Paa

f:id:TAKEsan:20180815230202p:plain

  obj.data    :必要ファイルのリンクを書き込んだファイル。以下のような感じ。

      class   必ず指定。今回の場合3

      train ディレクトリとファイル名を指定 先ほどのtrain.txt

      valid ディレクトリとファイル名を指定 先ほどのtest.txt

      names   クラスファイルの場所とファイル名 obj.names

      backup  学習済みデータの保存先 このままでOK

f:id:TAKEsan:20180815230221p:plain

12.いよいよ学習

darknet/  ディレクトリ内で以下を実行。今までの作業はこのコマンドを実行させるために必要なことだったと言うことですね。

 tiny Yoloでyolo v2.0の場合

./darknet detector train cfg/obj.data cfg/tiny-yolo-voc.cfg darknet19_448.conv.23

 

 標準YoloでYolo v3.0の場合は

./darknet detector train cfg/obj.data cfg/yolo-voc.cfg darknet53.conv.74

となります。

 今までのリンク先などの設定で、データの置き場所などが変更可能だと言うことが分かると思います。ある程度慣れてきたら自分の環境に合わせて変更してしまえば、もっと使い安くなります。

13.すんなり行けば良いのですが

 ここまでの過程が複雑でデータ量が多いことから、普通は簡単には動いてくれません!!

 実行してから、まず最初の1〜2分の段階でjpegに対応したtxtファイルがありませんのようなエラーメッセージが出たら、アノテーションが上手くいってません。上記7.をもう一度確認してjpegファイルがダブってないかチェック後、必ず 

python proxess.pyを再実行。同様のエラーが無くなるまで同じことを繰り返します。上手くいくと以下のような画像になります。

f:id:TAKEsan:20180815230846p:plain

 この場合はtiny-yolo-voc.cfgを使ってsubdivisions=8にした場合で、これを40,000回繰り返し(tiny-yolo-voc.cfgの15行目 max_batchの最後の数値を40,000にした)、100回毎にでデータがdarknet/dataの中に自動保存されます。つまりこの例では400回上書き保存されます。  

 subdivisionsの値を少なくすると計算がメモリー上に展開されるようで、全体のスピードが多少速くなりますが、GPUメモリーが不足してエラーになる可能性があります。

 上の画像のように、8回毎に平均値が出ます。avgの手前の数値が0に近づくほど良好とのこと。今回のデータでは1.455029になっていますが、これ以上続けてもほとんど変わらないことと、次で説明する認識テストもほぼ良好なので。ここで打ち切っています。

 ただし標準Yoloでの学習は確実にゼロに近づきます。また白黒で学習させているのにカラー画像でも認識するのには驚いてしまいました。Tiny Yoloではそうはいきません。

 このほか、Classがゼロだったり他の数値が表示されていない場合は、クラス分けやクラスの数の指定が各段階で上手くいっていない可能性があります。再度確認となりますが、「6.クラス分けをする」だけは慎重に作業する必要があります。ここで設定を間違うとアノテーションを最初からやり直ししなければなりません。

 今回約4,500枚のデータを使い4万回の繰り返しで、大体6時間(GTX 1080 Ti)ぐらい必要でした。学習時間はGPU性能に依存します。

14.学習結果の確認

 学習結果は、darknet/backupに保存されます。tiny-yolo-voc.backup又はyolo-voc.backupがそのデータで、

./darknet detector test cfg/obj.data cfg/yolo-voc.cfg backup/tiny-yolo-voc.backup test.jpg

(標準学習の場合はtiny-を消します)

と打ち込み認識したいサンプル写真を指定(test.jpg)すると現在の学習状況が確認できます。満足できる認識内容なら、どの段階でもCtrl+Cで打ち切り可能。その場合は.backupが学習済みデータになります。その他に各段階でデータが作成されていますので、それらを利用することも可能です。

 学習実行内容をよく見ていると、数値にかなり波があるので、avgの手前の数値が0に近付いたとしても、少なくとも1万回以上は繰り返さないと満足な結果は得られないようです。 ここら辺はデータ量や質によりなんとも言えないので、ヒーター付き集塵機(GPUの付いたUbuntuマシンを私はこう呼びます。夏場はさらに冷却用の扇風機が必要...)の電気料金が許す限り自分で試して見るしかありません。

f:id:TAKEsan:20180815235143p:plain

以上の作業は慣れるとスイスイなのですが、伝わったかなー、伝わんねーだろーなー。

 2ヶ月前の作業を思い出しながら書いたので、結構疲れた....。

 

                             では、また。

この後追加学習を試してみました。以下参照

takesan.hatenablog.com

 

 

今年もMaker Faire Tokyo 2018に出ます!!

 今年もメイカーフェア東京(Maker Faire Tokyo 2018)に出展することになりました。

    This year we will also exhibit at Maker Faire Tokyo (Maker Faire Tokyo 2018).

f:id:TAKEsan:20180704201130j:plain

 今まで、格安WIFI付きプロセッサ(ESP8266)とFLIR LEPTON赤外線センサーで、どれだけ本格的・実用的なiPhoneアプリが作れるかどうか追求してきました。

  Until now, We have been trying more practical iPhone apps with cheap WIFI core (ESP 8266) and a FLIR LEPTON infrared sensor.

f:id:TAKEsan:20180705100857p:plain

 昨年に引き続き今年も赤外線カメラ(FLIR LEPTON1,3,3.5)を使います。もちろん小型ドローンへの搭載も着々とバージョンアップが進んでいます。

 今年は大きく分けて3つの内容になります。今までは、開発時間に追われてブログ更新をしてませんでしたが、公開できる面白い材料がずいぶんそろってきましたので、頑張って今後紹介して行きたいと思ってます。

  We are still using FLIR LEPTON 1, 3, 3.5 this year as well. Of course small drone test has steadily been upgraded.

  This year we expanded three main items. Until now, we are too busy to update our blogs, but a lot of awesome idea we will introduce you, so we will do our best and we want to introduce it in the future.

今年はメンバーが3人になりました    This year three members have become

 東北と関西の共同作品です。こんな感じのロゴにしました。WT&Dに名称変更。

  It is a collaboration work between Miyagi, Hyogo and Kyoto. We made it as a logo like this. Changed name into WT & D this time.

              f:id:TAKEsan:20180705101320p:plain

 

1.小型ドローンへのLEPTON搭載?    LEPTON installed in a small drone?

 確実に進化しています。成果のお話は会場で!!

 It evolves reliably. Talk about achievements at the venue! !

f:id:TAKEsan:20180705102508p:plain          えっドビー軽量化?       weight reduction?

2.小型サーモグラフィ画像2個で立体表示可能か?   Can 3D display be possible with two small thermographic images?

 前回の記事で最後の方に動画を付けましたが、こんな感じです。

  We attached a movie in the last article. it is like this.

           www.youtube.com

 ステレオメガネやスマホゴーグルで立体画像が確認できます。サーモグラフィー画像2画面同時表示、しかもWIFIでリアルタイムってのはありそうで無かった未知の領域です。これはコンピューターで遠近を認識できることを意味しますが、この動画のDepth画像や3Dメッシュ画像で確実に距離を認識していることが分かると思います。

  You can check stereoscopic images with stereo glasses or smartphone goggles. It is an unknown area which was not likely to be real-time with WIFI simultaneous display of thermographic picture 2 screens at the same time. This means that you can recognize the perspective in the computer, but you can see that the distance is definitely recognized by Depth image and 3D mesh image of this movie.

f:id:TAKEsan:20180705104206p:plain

3.小型サーモグラフィ画像で画像認識は可能か?    Can image recognition be possible with a small thermographic image?

 画素の少ないサーモグラフィで、ジャンケンの手の形がどれだけ正確に認識できるかどうかトライしてみました。初めはLEPTON3.5で作ったデータで学習させた結果をLEPTON3.5で認識させていましたが、試しに画素の荒いLEPTON1(たった80x60画素)で認識させたところ、まともに動くことが分かりました。会場ではWIFIの混線が予想されるので、妨害電波に強いLEPTON1系のアプリを使う予定です。日本国内でもこのセンサーをお持ちの方が多いと思いますので、今後ブログ上で学習データの作成方法やソースを公開して行こうと思っています。

 今回のソフトは、熱を発する人間の手のひらだけを画像認識できます。一種の温度フィルターですね。これができたってことは応用が無限に広がります。それもTX2じゃなくてiPhoneで!!(画像はiPhone7 plusです)どう思います?

 With a thermography with few pixels, I tried to see how accurately the hand shape of Rock-paper-scissors could be recognized. Initially we was letting LEPTON 3.5 recognize the result of learning with data created with LEPTON 3.5, but when I tried to recognize it with a rough LEPTON 1 (only 80 × 60 pixels), we found it to work properly It was. At the venue, it seems that WIFI 's crosstalk is expected, so we plan to use LEPTON 1 series applications that are stronger against jamming waves. We think that there are many people who have this sensor even in Japan, so I'm thinking about going to introduce how to create learning data and sources on our blog in the future.
 The software of this time can recognize only the palm of the human's fingers image. Is it a kind of temperature filter? The fact that this can be done spreads the application infinitely. It's not TX 2 but on the iPhone! ! (The image is iPhone 7 plus) What do you think?

    

  f:id:TAKEsan:20180705105004p:plain

 そして、応用。2種類のジャンケン画像を読み取ってf:id:TAKEsan:20180524102209j:plain (Juneチャン)が、歌います(簡単な手話の解読)。高齢者の脳トレに最適!!。

 And application. two kinds of Rock-paper-scissors images (June Chan), but my dog sings (easy decoding of sign language). Ideal for the brain train of elderly people! ! .

    

今回のアプリは、    This time,

  すべてアプリ登録しているThermal Cam3、1を利用して作りましたが、マルチスレッド化しているので申請条件に合わないため、アプリストアから配布ができません。ただしテストフライトという形なら配布が可能ですので、希望する方はコメントして下さい。

 I made it using Thermal Cam 3 & 1 which all the applications are registered, but because it is multi-threaded, it can not be distributed from the apps store because it does not meet the apple application requirements. However, since distribution is possible in the form of Test Flight, please comment if you wish.

      f:id:TAKEsan:20180704191549j:plain

Thermal Cam、Thermal Cam3のインストール方法は、       How to install Thermal Cam, Thermal Cam 3,

 この記事をご覧下さい。性能を上げるため来月中を目標にバージョンアップする予定です。
 Please see this article. We plan to upgrade to the target next month to improve performance.

takesan.hatenablog.com

 

 

 

 

 

 

ジュンちゃん手術をする!!

 我が家のアイドルトイプードルのJUNEですが、1才にも満たない頃フローリングですってんころりん。右後ろ足の膝のお皿が外れ安くなりました。グルコサミンを飲ませたりしてだましだまし散歩を続けましたが、今年獣医さんに連れて行ったら、外れた膝のお皿がずれたままくっついてしまったとか....。膝蓋骨脱臼のグレード4だそうです。道理で散歩の途中で座り込んでしまうことが多くなったわけだ。

 このままでは左足もおかしくなってしまうと言うことで即手術命令!!。最悪歩けなくなるかもしれないと思い。いくらか歩けているうちに毎日少しずつお散歩をさせ(今から思えば考えすぎでした)、

f:id:TAKEsan:20180524143650j:plain

 我が家から車で2.5時間。盛岡の岩手大学動物病院を紹介してもらって連れて行くことに..。

 前日は心配して友人から送ってもらったお守りを付けて、夕ご飯以降絶食絶水。僕の作ったご飯しか食べないというような悪いしつけをしたので、冷凍にして持参。ついでに事前にトリミングして、狂犬病やワクチンの注射をしてから手術に挑ませることにしました。

f:id:TAKEsan:20180524102209j:plain

岩手山がとても素晴らしかった!!

f:id:TAKEsan:20180524102350j:plain

後ろ姿が泣ける。

f:id:TAKEsan:20180524102430j:plain

 手術内容は、右膝の皮膚をタテに5cmくらい切って、お皿がずれないように足膝部分の骨を削る。横にくっついてしまった膝のお皿を外し、所定の部分まで引っ張ってピンで固定。なんていう飼い主としては身の毛もよだつような内容。

 5/8入院、5/9夕方手術、5/10病院で安静、5/11退院という強行スケジュールでした。5/9の手術後、先生から無事終了の連絡が来て一安心して見たものの、こんなに早く退院して良いのと思いながら迎えに行きました。

 うちのワンコは病院が大嫌いなのですが、岩手大学動物病院では先生を初め皆さんに優しくしていただいたようで、別れ際に先生へお礼(ペロペロ)をしてました。でも帰ってきたときは、こんな痛々しい姿に....。 飼い主としては不安です。

f:id:TAKEsan:20180524103914j:plain

  家の中では飛び跳ねないようにケージ入り命令が出たのですが、なにせしつけが悪く野放し状態で育てたので、ケージに入る訳もなく、

f:id:TAKEsan:20180524103308j:plain

 入れると暴れるだけなので、かえって悪くなると思って、部屋を小さく仕切り様子を見ることにしました。寝るときも一緒でないと本人は気が済まないので、ベッドから飛び降りないようリードを付けて一緒にご就寝することに..。

 1週間目でこちらのお医者さんに行って包帯を取っ替えて、2週間目で再びこちらのお医者さんで抜糸。

f:id:TAKEsan:20180524105221j:plain

 平気で歩いてます。まだ、かさぶたを噛む可能性があるので、エリザベスカラーを付けたままです。術後1ヶ月でCTを撮りに又岩手大学へ行くことになりますが、飛び跳ねないような環境を作れば、なんとか大丈夫そうです。やれやれ。