Take’s diary

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

YOLOオリジナルデータの学習その2(追加学習)

今回は

 Joseph Chet Redmonさんの本家Darknet  https://pjreddie.com/darknet/yolo/  ではなく、AlexeyAB さんのDarknetを使って追加学習させてみました。前回の記事ではHow to train YOLOv2 to detect custom objectsの解説が全てと書いていますが AlexeyAB さんのDarknetの場合はhttps://github.com/AlexeyAB/darknetが全てです。私が確認した範囲では、認識率は両者ほぼ互角。単精度浮動小数点数指定ができるため、認識スピードがかなり向上できる他に、最近分かったことですが、学習の最適ポイントが掴みやすいなど、かなりの優れものでした(解説を良く読んでみればわかるのですが.....)。

 前回の学習では

takesan.hatenablog.com

 ジャンケンの3クラスのみの学習をさせたのですが、どうしてもグーが人間の顔と勘違いしやすい傾向がありました。元画像が赤外線カメラなので、そのような傾向があるかとは思いますが、新たに顔を追加学習させたら、さらに良い結果になりそうな気配です。じゃー「どうしたら追加学習できるのか?」ということが、今回のテーマです。

                    f:id:TAKEsan:20190111114732p:plain

      顔がGooになってしまうことがある....のですが、顔データを追加学習させると、こうなります

       

この画像はLEPTON3.0はPI3に接続。同一LAN内につないだJetson Xavierで画像表示とジャンケン認識をさせてます。

くどくど説明を書いてもかえって分かりにくくなるので、今回は要点のみとします。

ラベリング作業

  • 顔の動画を撮影して、2000枚程度の画像を作成した。
  • labelimg/data の中にあるpredefined_class.txtに4番目のクラス(今回はFace)を追加する。
  • とりあえず別のフォルダで顔のみをラベリングして作ったデータを、前回のデータフォルダにコピーする。
  • 前回のアノテーション済みの画像の中に顔が写っている場合は、追加指定を行う。
  • 入力ミスで思い当たりのないクラスが増えていることがある。自分で設定したクラスがひとりでにできていたら、前に遡って必ずその部分を消すことが必要。これをしないと、学習中意味不明のエラーまたはメモリオーバーが表示されて、悩まされることになる。

f:id:TAKEsan:20190111114852p:plain

  • 今回は精度の高い最新のyolov3で学習。単精度浮動小数点演算機能を外してビルドし直した。(つまり倍精度演算)

Yolov3_voc.fgの変更点

  • 認識精度を上げる方法はいくつかあって、https://github.com/AlexeyAB/darknetkこの中のHow to improve object detection:  を参照。
  • 一番有効なのは、WEIGHTとHEIGHTの数値を大きくしてトレーニングすること。
  • 一般的には416x416。精度を上げるには608x608または832x832とする。ただしyolov3の場合608x608で学習させると、私の環境ではメモリーオーバーで止まる。今回618x618の場合は subdivisions=16 とした。
  • classesの数値を3箇所変更(今回のクラス追加で4に変更した)
  • filtersの数値は YOLOv3の場合(classes + 5)x3)となる。これも3箇所変更。今回は27。本家Yolo(こちらはYOLOv2で(classes+5)x5)とは、この部分の計算方法が違う
  • cfgファイルは、yolov3.cfgでもyolov3_voc.cfgでもあまり変わらないようだ(ただしパラメーターが微妙に違う)。解説の通りyolov3_voc.cfgを使う方が無難。

obj.namesの変更点

  • obj.namesにクラス名を1つ追加。今回はFaceとした。

obj.dataの変更点

  • obj.data のclassesの数値を変更。今回は4。これが忘れがちで、学習時原因不明エラーの要因となる。

学習

  • AlexeyAB さんのdarknetは状況を確認するためにlossの数値と学習回数を関連付けたグラフが表示される。
  • 最適学習状況の判別機能が備わっている。学習コマンド実行時最後に-map オプションの指定によりmApの現状の数値がグラフに書き込まれる。
  • 学習は8000回目ぐらいが上限で良い様だ。5000前後でmap コマンドを実行して確認しiouとmapの一番高いものを選ぶ
  • Lossは小さい方が良いが、データによりそれ以上下がらない場合あり。0.3以下が望ましい
  • mApが同じでも認識が良好である場合とそうでない場合がある。なるべく7000回ぐらい試してサンプル画像をTESTしてみるのがベターの様だ。
  • Lossが1以下に下がらない場合、クラス数設定が各部で違っている場合が多い=使えない。
  • mapが90%を越すころから使い物になってくる。開始から2.5時間から3時間の間。
  • 最初の1000回の所要時間で学習終了時刻が、ほぼ予想できる(当たり前か...)。

同一データで、416x416と608x608とした場合の学習内容比較

母艦の性能  CPU:i7 6700K(16GB)GPU:  GTX1080Ti  ハードディスク主体

cfgファイルのWEIGHTとHEIGHTについて416x416指定の場合

  • 以下の結果で1000回あたりほぼ50分必要だった。

      時刻           回数           iou              mAp

       8:48            1000           測定しない    測定しない

      10:26           3000           75.66           90.5

      11:14           4000           75.97           90.64

      12:07           5000           79.16           90.76

      12:57           6000           78.83           90.76

      13:43           7000           78.88           90.77

      14:04           7400           79.26           90.77      この時点でloss=0.273

 

f:id:TAKEsan:20190111114940p:plain

  • なぜかmapが正しく表示されていない。416x416は7000回目のデータを採用した。

cfgファイルのWEIGHTとHEIGHTについて608x608指定の場合

  • メモリーオーバー対策にsubdivisions=16とした。1000回あたり約130分必要。

      時刻           回数           iou              mAp

        17.09           1000           測定しない    測定しない

        22:37           3000           76.77           90.4

      0:29            4000           79.45           90.5

      2:24            5000           79.57           90.71

      4:16            6000           78.55           90.70

      5:59            7000           79.52           90.71

      7:47            7400           78.60           90.72      この時点でloss=0.2766

  • グラフでもわかる様に収束が遅くばらつきがある。

f:id:TAKEsan:20190111115555p:plain

  • 608x608は6000回目のデータを採用した。

考察

  • 画像でテストしてみると、今回はなぜか608x608は、416x416より認識率が落ちる様だ。もともとLEPTON画像は160x120の画像なので、少し無理があるのかもしれない。最終的に採用したデータは416x416で学習させた7000回目のものとした。(最初に貼り付けた動画はこのデータを使ってます)
  • 認識時はcfgファイル内容を608x608に変更すると認識率は上がる。ただしスピードは犠牲となる。
  • テストデータの比率を10%としたが、これを10%以上に上げると多少認識率が上がる可能性はある。
  • 倍精度指定での学習データでも、単精度画像認識が可能だった。
  • 倍精度浮動小数点指定と、単精度浮動小数点指定で学習結果と時間に差が出るのかどうかのテストはまだだが、Jetson Xavierで単精度浮動小数点指定でビルドしたDarknetの学習をさせてみた場合、 YoloV2のジャンケン3クラス(416x416)の場合で1000回あたり約60分だった。これはJetson Xavier単独でも学習作業が可能であることを示す。

 世の中クラウド学習が方々で話題になってますが、色々制約があるのも事実だと思われます。身近な機材で目の前で刻々と変化する学習状態を確認するのも、また、オツなものですよ。(2日も3日も動かすわけじゃないので)

                      ってことで。今回はおしまい。

 

 

 

 

 

Jetson TX2 にインストールした OpenFremeworks でも YOLO その2 (Yolo on Jetson TX2 with OpenFremeworks(Part 2))

  1. 前回の記事で、 In the last article,

 Jetson TX2 にインストールした OpenFremeworks でも 16bit浮動小数点YOLOを動かしてみました。が、アプリを立ち上げた途端止まってしまったり、早そうだけどそうでも無いような感じでした。しかも2つのスレッドで動かしているため、認識スピードの計測が難しそうなので無視していました。

    f:id:TAKEsan:20181007074429j:plain

 YOLO3は今回作成したソフトの場合、TX2ではメモリーオーバーで動かないようですが、本来のYOLO3が遅いながらも実行できるので、もう少しトライしてみる価値がありそうです。

 で、引き続きYOLO2のweightデータを使います。インストール方法は前回の通りです。

Even OpenFremeworks installed on Jetson TX 2 tried running 16 bit floating point YOLO. Although it stopped as soon as the application was launched, it seemed to be fast, but it seemed like it was not so. Moreover, because it moves with two threads, it seems to be difficult to measure recognition speed, so I ignored it.
In the case of software created this time YOLO 3 seems not to work with memory over in TX 2, but since original YOLO 3 can be executed slowly, it seems to be worth a little more trying.
We will continue to use YOLO 2's weight data. The installation method is as last time.

まず、立ち上げた時頻繁に止まる現象は First, the phenomenon that stops frequently when launched

 yolov2.chgの設定が原因のようでした。最初の方のbachとsubdivisionsの設定数値を下記のようにすると、最悪2回程度のアプリ立ち上げ直しで上手く動きます。要はメモリー設定がらみです。

It seemed to be caused by setting yolov2.chg. If you set the setting values ​​of bach and subdivisions of the first one like the following, it will work well with restarting the app about twice the worst. The point is in memory setting.

f:id:TAKEsan:20181006232037p:plain

今回は、This time,

 より正確に複数スレッドを動作させるために修正したソースofApp.cppを最後に付けました。これで、かなり正確に認識スピードが計測できるようになりました。また、認識スピードや認識の正確さに関係のあるyolov2.cfgのweightとheightの値を両方とも試しに352に設定してみます。これで実行した動画が........。

Lastly we added the modified source ofApp.cpp to make multiple threads work more accurately. With this, it is possible to measure recognition speed fairly accurately. Also try setting both the weight and height values ​​of yolov2.cfg, which are related to recognition speed and accuracy of recognition, to 352 for testing. The video that I ran in this .........

    

         画像上左上のターミナル画面が認識スピード

 ここで確認できるのは、後半ターボが効いているように最高30~40fpsくらいで画像の認識をしている?こと。画像で写しているMacBook上の動画は、早回ししているものを利用していますが十分追従しているようです。つまり、エエーホントかよーでした。ここで使っているWEB CAMERAは最高30fpsなので、1コマあたり2回ぐらい認識している場合があるようです。動画前半のように、画像全体が白っぽいと15fpsぐらいに下がってしまうところが興味深いのですが、このあたりはガンマ補正で修正できる範囲です。前回と違って明らかにクラスを表示しているボックスの表示が早いことが確認できると思います。しかも結構正確!!。認識したいクラスを絞っで、独自の学習をさせたデータを利用すると、かなりの物が作れそうです。

※2019/6/20   Jetpack4.2  及び最新のDarknetでは14fpsが上限でした。なぜこの当時早くなったか只今調査中です。

 ちなみに608x608では5.5fps前後、416x416では10.8fps前後でした。こちらの方は画像のホワイトバランスの影響が出ないので、TX2にとって352x352(32の倍数)前後の設定がもっとも効率が良いのかもしれませんね。現場で今回のソースを応用する場合、Xavierは必要ないかもしれません。(安くなったし....Switchで成功してしばらく販売継続しそうだし...)

As you can see here, do you recognize the image as much as 30 to 40 fps as the second half turbo works? about. The movie on the MacBook shot in the image seems to be tracking enough although it is using the one being fast-forwarded. Really ?Since WEB CAMERA used here has a maximum of 30 fps, it seems there are cases where it is recognized about twice per frame. As in the first half of the movie, if the entire image is whitish, it is interesting that it goes down to around 15 fps, but this area is a range that can be corrected by gamma correction. Unlike the last time I think that it is possible to confirm that the display of the box clearly showing the class is fast. And pretty accurate! ! .

By the way it was around 5.5 fps at 608 x 608, around 10.8 fps at 416 x 416. As this case has no influence of the white balance of the image, the setting around 352 x 352 (multiples of 32) for TX 2 may be the most efficient. Xavier may not be necessary when applying this source for the case. (It became cheap and it seems to continue selling for a while after succeeding at Nintendo Switch ...)

以下ofApp.cpp

 ここでのビデオ入力画像の大きさを640x480(setup()内)に設定しています。これを大きくすることでYOLOの認識率がアップするようですが、TX2にとっては結構負担になるので入力画像は小さくしています。今回YOLO側の読み込み画像が352x352なのであまり問題にならないようです。


#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.25;

cv::Mat 		mat;
ofTrueTypeFont cop20,cop50;
Detector detector(cfg_file, weights_file);
std::vector result_vec,result_vec1 ,result_vec2;
image_t xxx,yyy;
std::vector<bbox_t> 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";
    file.close();
    return file_lines;
}
std::vector obj_names;
  
void show_console_result(std::vector<bbox_t> const result_vec, std::vector<std::string> const obj_names) {

    for (auto &i : result_vec) {
        if (obj_names.size() > i.obj_id) 
			ofNoFill();
			ofSetLineWidth(1);
        
			//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);
			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);
			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)
{
	    result_vec = detector.detect_resized(x,640,480,thresh,false);
}
void detect_y(image_t x)
{
	    result_vec1 = detector.detect_resized(x,640,480,thresh,false);
}
//--------------------------------------------------------------
//  DetectNet部分をマルチスレッドにする。No1
class Detect_x: public ofThread {
public:
	void threadedFunction(){
		ok=false;
		detect_x(xxx);
		ok=true;
	}
	bool ok;
};
Detect_x Found_X;
//--------------------------------------------------------------
//  DetectNet部分をマルチスレッドにする。No2
class Detect_y: public ofThread {
public:
	void threadedFunction(){
		ok1=false;
		detect_y(yyy);
		ok1=true;
	}
	bool ok1;
};
Detect_y Found_Y;
int a1=0,b1=0;
//--------------------------------------------------------------
void ofApp::setup(){
    obj_names = objects_names_from_file(names_file);
    cop20.load("cooperBlack.ttf",10,true,true,true);
    cop50.load("cooperBlack.ttf",30,true,true,true);
    img.allocate(640,480,OF_IMAGE_COLOR);
    video.setDeviceID( 0 );
	video.setup(640,480,OF_PIXELS_RGBA);
	Found_X.ok=true;
	Found_Y.ok1=true;
}
//--------------------------------------------------------------
void ofApp::update(){
bool q1,q2;
	video.update();
	if(video.isFrameNew()==true){
    img.setFromPixels(video.getPixels().getData(),video.getWidth(),video.getHeight(),OF_IMAGE_COLOR);
		mat=ofxCv::toCv(img);
        q1=false;
	    q2=false;
	  while(q1==false && q2==false){  //2つのスレッドのどちらかがスタートできるまで待つ-->全体のFPSが下がるが正確な計測ができる。
            if (Found_X.ok){
                cv::Mat imgx;
                cv::cvtColor(mat,imgx, cv::COLOR_RGB2BGR);
                std::shared_ptr<image_t> image_ptr(new image_t, [](image_t *imgx) { detector.free_image(*imgx); delete imgx; });
			    std::shared_ptr<IplImage> ipl_small = std::make_shared<IplImage>(imgx);
                *image_ptr = ipl_to_image(ipl_small.get());
                xxx=*image_ptr;
                       
                Found_X.startThread();
                q1=true;
            }
            else if(Found_Y.ok1)  {      //elseが必要!! これでないとプログラムがハングする。
                cv::Mat imgy;
                cv::cvtColor(mat,imgy, cv::COLOR_RGB2BGR);
                std::shared_ptr<image_t> image_ptr(new image_t, [](image_t *imgy) { detector.free_image(*imgy); delete imgy; });
			    std::shared_ptr<IplImage> ipl_small = std::make_shared<IplImage>(imgy);
                *image_ptr = ipl_to_image(ipl_small.get());
                yyy=*image_ptr;
                Found_Y.startThread();
                q2=true;
            }
      }
    }
}
//--------------------------------------------------------------
void ofApp::draw(){

    ofLog() << ofGetFrameRate();  //zissitu FPS!!
    ofSetColor(255);
    video.draw( 0, 0 );
    bool q1=false;
    bool q2=false;
    while(q1==false && q2==false){
        if (Found_X.ok){
            Found_X.lock();
            show_console_result(result_vec, obj_names);
            result_vec2=result_vec;
            Found_X.unlock();
            q1=true;
        }
        if(Found_Y.ok1) {  //els は必要なし!!スレッドが動いていなければどちらか又は両方表示させる。--->ちらつき防止
            Found_Y.lock();
            show_console_result(result_vec1, obj_names);
            result_vec2=result_vec1;
            Found_Y.unlock();
            q2=true;
        }

    }
}
//--------------------------------------------------------------
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){
}

               では。また.......。 See you !

 

Jetson TX2 にインストールした OpenFremeworks でも YOLOを動かす。

TX2でYOLOがどのくらいのスピードになるか?

f:id:TAKEsan:20181004220526j:plain

      ちょっと埃をかぶってますが、以下の動画は紛れもなくこのTX2で実行してます。

 これは、weightデータがTiny YOLOではなく標準のYOLO V2です。使ったデータは今回の場合80クラスを認識しますが、コンピューターにとって全世界が80項目しかないので誤認識があります。でもハマったものはなかなか素晴らしい結果でした。

     今回TX2でYOLOを実行させた結果です。画像のスピードは問題なし。YOLOの認識速度もなかなか!!(画像がボケているのにこの辺りがAIですかね......)

 前の記事でJetson XvierにインストールしたopenFrameworksYOLOを動かしてみましたが、なかなか良い結果が出たので、じゃーTX2で実行したらどうなるのかってのが今回の実験です。

TX2へのOpenframeworksのインストールはこの記事を参照して下さい。

takesan.hatenablog.com

 で、YOLOのインストールは

takesan.hatenablog.com

TX2Xavierに勝とうっていうのは所詮無理な話で、

 少しでもXavierに近づけるためにマルチスレッドを利用します(OF上ではofThreadを利用)。Xavierでも裏スレッドを1個使って実現させましたが、今回は2個使いますGPUはマルチスレッド対応していないということが頭に入ってました。でも、「微妙に時間をずらすことでGPUをうまく使えないか」ってことが今回の発想です。ここでもやはりデータ変換が鍵を握ってて、ちょっと時間がかかりましたが、なんとなく狙い通りになってるようです。

まずyolov2.cfgの設定ですが

最初の方を

batch=32

subdivisions=8

weight=416

height=416

にしました。weight、heightとも608にするのが理想的ですが、あまり認識に変化が無いようなのでスピード重視です。yolov2.cfgが何者かは、前の記事を確認して下さい。

 また、Web CameraのopenFrameworks側設定画像は640x480。相当粗い画像ですがYOLOはそれでも満足の行く結果を出してしまいます。これもスピード対策。openFrameworkeのソースは次の通りです。

 ただし、まだバグが残っているようでmake runしてもエラーが出る場合があります。その場合は、何度もmake run してみて下さい(Xavierも同じ)

main.cpp


#include "ofMain.h"
#include "ofApp.h"
//========================================================================
int main( ){

	ofSetupOpenGL(640,480, 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,result_vec1,result_vec2 ;
float ttt,ttt1,ttt2,sss,sss1;  //Time
image_t xxx,yyy,zzz;
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";
    file.close();
    return file_lines;
}

std::vector obj_names;
  
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(1);
        
			//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);
			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);
			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)
{
	    result_vec = detector.detect(x,thresh,false); 
}
void detect_y(image_t x)
{
	    result_vec1 = detector.detect(x,thresh,true); 
}
void detect_z(image_t x)
{
	    result_vec2 = detector.detect(x,thresh,true); 
}
//--------------------------------------------------------------
//  DetectNet部分をマルチスレッドにする。
class Detect_x: public ofThread {
public:
	
	void threadedFunction(){

		ok=false;
		detect_x(xxx);    
                ok2=true  ;       
		ok=true;
	}
	bool ok;
};

Detect_x Found_X;
//--------------------------------------------------------------
//  DetectNet部分をマルチスレッドにする。
class Detect_y: public ofThread {
public:
	
	void threadedFunction(){

		ok1=false;
		
			cv::Mat imgy;
			cv::cvtColor(mat, imgy, cv::COLOR_RGB2BGR);
			std::shared_ptr image_ptr1(new image_t, [](image_t *imgy) { detector.free_image(*imgy); delete imgy; });
			std::shared_ptr ipl_small1 = std::make_shared(imgy);
			*image_ptr1 = ipl_to_image(ipl_small1.get());
			yyy=*image_ptr1;

		detect_y(yyy);    
		ok3=true  ;       
		ok1=true;
	}
	bool ok1;
};

Detect_y Found_Y;

//--------------------------------------------------------------
void ofApp::setup(){
obj_names = objects_names_from_file(names_file);
    cop20.load("cooperBlack.ttf",10,true,true,true);
    cop50.load("cooperBlack.ttf",30,true,true,true);
    img.allocate(640,480,OF_IMAGE_COLOR);
    video.setDeviceID( 0 );
	//video.setDesiredFrameRate( 30 );
	video.setup(640,480,OF_PIXELS_RGBA);
	Found_X.ok=true;
	Found_Y.ok1=true;
	ok2=true;
	ok3=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;
                        
			Found_X.startThread();
                }
		else if(Found_Y.ok1)  Found_Y.startThread();
    }
}
//--------------------------------------------------------------
void ofApp::draw(){
   ofSetColor(255);
   video.draw( 0, 0 );
   Found_X.lock();
   show_console_result(result_vec, obj_names);
   Found_X.unlock();
   Found_Y.lock();
   show_console_result(result_vec1, obj_names);
   Found_Y.unlock();
}
//--------------------------------------------------------------
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){ 
}

FLIR LEPTON のホームページに私たちのThermal Cam Depthが掲載された!!

Thermal Camは、ドローンへ搭載するために作ったモノです。

f:id:TAKEsan:20180925133820p:plain

f:id:TAKEsan:20180924212322p:plain

 全国に大規模な太陽電池発電所が建設されています。いろいろ批判もあるでしょうが現実的に多く存在しています。

 ヒトの作ったモノは、点検が不可欠。万一故障するとパネルが他の部分より高温になるそうです。規模が大きければ不良箇所を発見することが困難。直流電流が漏電した場合、遮断が難しいため点検員の逃げ場が無くなり、かなり危険な人命を掛けた作業でもあります。そこで高解像度サーモグラフィカメラを取り付けたドローンが登場。専属の操縦士と数百万円にも及ぶ機材が必要でした。機体が大きいため万が一落下した場合、太陽電池や高価な機体の破損にもつながる非常にリスクのある作業です。

f:id:TAKEsan:20180925134543p:plain

        現在改良中の、ソフト・ハードを搭載したドローンからの画像と、途中からiPhon で受信したLEPtON3.5の合成画像が白黒で挿入されています。LEPTON3.5の160x120画素でも、この用途ではほとんど問題ない事が理解できると思います。しかもドローンは自動飛行!!(大澤さん撮影)

 これが市販の軽量ドローンで可能であれば、そして安価であれば大幅なリスク回避が可能となります。そう考えた方がいらっしゃって(W&TのW=渡辺さん)、たまたま私のブログを見て「こりゃー行けるかも」と思ったそうです。そのころ私も赤外線カメラLEPTON1での試行結果から、すぐに実現可能だと思っていました。ただし、LEPTON1では解像度が不足し、最低でもLEPTON3と、省電力軽量、安価なESP8266を使うことが条件。しかし、LEPTON3の特殊性やESP8266の限界、SPIの安定性、2.4Ghz帯の電波干渉、そして経験の無いAppleのアプリ審査という厳しい現実が待っていました。私たちは、専門家のいないアマチュア集団です。ブログやYOUTUBEで知り合った、仙台(小野)、大阪(渡辺)、東京(大澤)、京都の4名がそれぞれ得意分野で知恵を出し合うことになりました。全員がそろうのは1年に1回。Maker Faire Tokyo !!。

            f:id:TAKEsan:20170613224819j:plainf:id:TAKEsan:20180704191549j:plain

 会場の電波状況の悪さに、去年も今年も落胆と改良の日々が続きました。今までは地上では電波が伸びても上空ではダメ。家電量販店や駐車場ではESP8266が止まってしまう。渡辺さんの操縦するドローンがESP8266 WIFIとの混線で落ちたのも2度や3度ではありません。開発からもう1年半が経過し、最新の基盤とソフトができあがったころ、つい先日東京の大澤さんから朗報が来ました。

f:id:TAKEsan:20180925134753p:plain

     

        ドローンからiPodに送ってきた画像です。(この動画は20m付近)

 いままで地上だけでしか実現できなかった長距離接続が可能になった...!?。しかもドローンとの電波干渉が無い?。はたして当初目標としていた到達点に来たのだろうか?

 こんな時、今年のメーカフェアに出展した内容が、サーモグラフィーの世界的企業であるFLIRのホームページに掲載される事になったんです。今年はドローンを切り離してLEPtON3をiPhoneに2個パラレルWIFI接続。赤外線画像でのステレオ(Depth)とAIへの挑戦でした。https://lepton.flir.com/community-showcase-items/thermal-stereo-cam/

f:id:TAKEsan:20180925135101p:plain

f:id:TAKEsan:20180924211357p:plain

 私たちにとってはこの2つの事件に後押しをされて、さらなる改良を続けることにしました。Thermal Cam専用のブレないジンバルを渡辺さんが作っています。大澤さんはThermal Camドローン搭載に向けて現場実証とカメラの取り付け方法を模索しています。私は赤外線画像とAIが結びつかないかを検証しています。私たちの目標は。現場で終結できるコンパクトで安価なモノを作る!!です。

※Thermal Cam (LEPTON1専用)、Thermal Cam 3(LEPTON3、3.5専用)はAppleアプリストアでダウンドードできます。Thermal Cam 専用基盤はW&T Thermal Cam PCB - スイッチサイエンスで手に入れることが可能。ジンバルやドローン機体は今後の経過をこのブログでご期待下さい。

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のサンプルソースを実行中