Take’s diary

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

M1 MacでOpenframeworks for iOS

久々のブログ更新

 M1 MacBook Airを手に入れてから、Openframeworks for iOSがまともに動かなくなったので、諦めて1年近く経ちました。特にOpenframeworks上でOpenCVを使ったものはお手上げ。今回はOpenCVのソースビルドからスタートです。

 今回は以前作ったステレオサーマルカメラのデプス表示部分を実機稼働させてみました。何が原因かわからずエラー出まくりのM1 Mac上でOFでの実機ビルドの他に、全く動く気配もなかったOpenCV for iOSも、contribを含めた最新フルバージョンで動くようになりました。終わってしまえば案外簡単な結末でした。

f:id:TAKEsan:20220102143111p:plain

OpenCV のソースをM1 iOS用にビルド

 このブログを参考にさせていただきました。ただしインストールは簡単にはいかず、試行錯誤の上ビルドしました。

https://www.cathand.app/entry/2020/11/23/214000

 

OpenCVインストールの条件、内容は、

  • M1 MacにはあらかじめHomebrew、git、最新のcmake(3.22.1)をインストール
  • macOSバージョンは現在最新の12.0.1 Monterey Xcodeは13.2.1
  • 現在OpenCV3.4.6ではうまくビルドできず、OpenCV4.5.3ならOK
  • contribも一緒にインストール。

cmakeなどの基本ソフトのインストールは他の方が詳しく書いていますので省略。

まず

以下からOpenCV4.5.3とOpencv Contribをダウンロードします。

https://opencv.org/releases

GitHub - opencv/opencv_contrib: Repository for OpenCV's extra modules

Xcode認識のためターミナルから

   sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

好きなところにダウンロード解凍したOpenCVフォルダを置き、その中に同じくダウンロードしたopencv_contrib フォルダを入れます。

さらにopencv_contribに入り 

   git checkout -b 4.5.3 refs/tags/4.5.3

で、contribのバージョンをOpenCvと合わせ、Opencvフォルダに戻った後

opencv-4.5.3/platforms/ios/build_framework.py を開きます

 

f:id:TAKEsan:20220102132917p:plain

このように560、561行目にコメントを入れ、

python2.7 platforms/ios/build_framework.py  --iphoneos_archs armv7,armv7s,arm64 —iphoneos_deployment_target 9 --dynamic --contrib opencv_contrib iphones

を実行。実機用のopencv2.frameworkをビルドします。

次に今のこのコメントを外し、同じように今度は578,579行の最初にコメントを入れ

python2.7 platforms/ios/build_framework.py --iphonesimulator_archs  i386,x86_64,arm64  --iphoneos_deployment_target 9 --dynamic --contrib opencv_contrib iphonesimulator

でシュミレーション用のopencv2.frameworkをビルド。どちらも20分以上かかります。

最後に

xcodebuild -create-xcframework -output opencv2.xcframework -framework iphones/opencv2.framework -framework iphonesimulator/opencv2.framework

で合体。

opencv2.xcframeworkというフォルダができますので、これをOpenCVを使いたいプロジェクトフォルダにコピーしておきます。605Mという巨大なファイルですが、Xcode上でうっかりミスで消してしまうことが多いのでコピーした方が無難。

M1 Macで

Openframeworks0.11.2の最新バージョンをダウンロード。

myAppsフォルダにプロジェクトを作成し、その中にopencv2.xcframeworkをコピー

プロジェクトを開き「Frameworks,Libraries,and Embeded Content」 へopencv2.xcframeworkをドラッグ。

f:id:TAKEsan:20220102134138p:plain

さらに最初だけは「Runpath Search Paths」にこのフォルダのパスを記入した方が良いようです。

f:id:TAKEsan:20220102134340p:plain

次に ofxOS_Prefix.pchを開き、最初の方にopencv関連のライブラリヘッダーをインクルード。

今回の場合は、以下4行

  #include <opencv2/opencv.hpp>

  #include <opencv2/stereo.hpp>

  #include "opencv2/ximgproc/disparity_filter.hpp"

  using namespace cv::ximgproc;

この部分は使用するopencvのライブラリによります。

f:id:TAKEsan:20220102134705p:plain

エラーが出ても気にせず、Xcodeを一旦終了。

ファインダーでアプリケーションの中からXcodeを探し右クリックでファイル情報を表示して

Xcodeをロゼッタモードにします。(中間の「Rosettaを使用して開く」にチェックを入れる)

      f:id:TAKEsan:20220102134924p:plain

これで、一応実機とiOSシュミレーションが使えるようになります。が、さらにプロジェクト上の設定で切り替えが必要です。

実機、もしくはアプリショップへダウンロードする場合でアーカイブファイルを作成する場合は、

f:id:TAKEsan:20220102135145p:plain

「Excluded Archutectures」を上図のように空白にし、

シュミレーターを使う場合は、ここをarm64にします。

 さらにM1 Macで動かす場合は(My mac(Designed iPad)を選択する場合)Xcodeのロゼッタモードを解除し、Excluded Archutecturesを空白にすれば簡単に動き出します。

 この切り替えがめんどうくさいのですが、慣れればさほど問題はありません。

 ロゼッタモードでは編集時間の切り替えに少し引っかかる感がありますが、Airでも以前のIntel Mac Proよりビルドスピードが速いので特に問題なし。

 OpencvがOpenframeworks上で自由自在に使えるようになります。問題はOFの画像をOpenCV形式に変換する部分ですが、

   ofImage L_img;

     of一般のイメージ処理

   cv::Mat imgL= cv::Mat(L_img.getHeight(),L_img.getWidth(), CV_8UC3, L_img.getPixels().getData());

という感じで最後に1文を加えれば簡単にofImageからMatに変換できます。逆も一般的なofxopencvのaddonを使う場合と同じ。ofxcvアドオンは便利ですがM1では動かないことと、使えるライブラリが少ないので最初から断念しています。

 

自分用の忘備録ということで説明が大雑把ですが、これで画像処理やOpenCV上のAIは自由自在。当分はなんとかなる。 やれやれ......。

 

 

 

 

 

 

 

 

 

 

 

Beethoven Baby Grandのツイター交換

電子工作リハビリの一環として

 購入してから13年ほど経過しているスピーカー「Beethoven Baby Grand」のツイターを交換してみました。ミッドレンジ、ウーファーともゴムエッジは問題なし。コーンは樹脂製ですが、弾力は買った時のままつまり全く交換の必要がありません。現状問題ないのですが、以前から音質向上目的でツイターを変更したいと思っていたので思い切って交換を決意。結果的には長い道のりでした。

 メーカーはウィーンアコースティクスで、この頃の機種にはScan Speak製のツイターが使われていました。ネットで調べると、「Beethoven Baby Grand」の下位機種「Mozart Grand Symphony Edition」ではソフトドームツイターD2965/930004 が使われているとか。このシリーズは90000、930000、950000、970000の4機種で6Ω 90dBとなっておりソフトドームとしては能率が高く、型番が上がるほど性能が良くなっている様です。ところが私のBeethoven Baby Grandを調べるとD2964/930004  となっていてこちらは多分4Ωバージョン。

f:id:TAKEsan:20210503115223j:plain

 スピーカーシステム全体としての公称値が4Ω 91.5dBとなっていました。現在D2904シリーズは中古品も流通していない様です。インピーダンスが違う様ですが、最悪元に戻せば良いことなので、今回は比較的手に入りやすくグレードが1ランク高いD2965/950000を手に入れることにしました。

 このツイターはメーカー出荷時は2個ペアで販売しており、現在は3万円前後。過去にヨドバシで販売していた様で当時の価格は5万円くらいといった高級スピーカーユニットです。一応Amazonで国内販売していたものが4.5万。思い立ったらすぐ欲しくなるもので、水準より1.5万円も高いものでしたが早々に手配しました。

 注文確定後に届いたメールに「Dash Button/Dash Replenishmentサービスによるご注文については、Dash Button/サービス対応デバイスでの対象商品の設定時とご注文時の提供条件(たとえば、商品、価格、税金、入手可能性、送料及び売主)が一部変更されている場合があります。上記「注文内容」を十分にご確認ください。」との注意書きがありました。嫌な予感がしましたが、注文書には新品1セットとなっていたので、とりあえず待つことに....。で、届いたものが

f:id:TAKEsan:20210503115254j:plain

 1個だけで、しかも1ペアの箱をわざわざ半分に切って乱暴にテープ貼り。おまけにレターパックプラスに入れただけのあんちょこ配送の上に納品代行会社から届いたものでした。1セットとは1個という意味だったんですね!。結局3倍の金額で売っていたことになります。早々に返品処理をして他の方法を探すことにしました。

 次に買いやすいところはセカイモン。こちらは米国から新品2個セット送料税込約3.4万の落札金額と、かなり良心的な金額設定なのでセカイモンは初めてでしたが思い切って購入。4月11日に注文して到着が4月28日なのでまずまずの日数で到着しました。こちらは入札商品を現地で確認するシステムのためかなり安心ですが、到着まではなんとも言えません。で、到着したのが

f:id:TAKEsan:20210503120817j:plain

 ネット表示通り新品で、おまけに3重のダンボール包装でした。結果的に国内のなんちゃら出店者よりアメリカの方が数倍良心的といった皮肉な結果。

ここからが本番

 今度は単純にユニットを載せ替えただけでうまく音がバランスするかです。もともとのツイターは4Ωらしいので、今回6Ωのものに取り替えると高音のレベルが低くなると予想されます。まーこの辺りは試聴してアンプ側でレベル補正すれば良いかということで、ひとまず載せ替え作業を実施。

 ところが、ツイターの保護プレートが同じ形状同様寸法にもかかわらず、ボックス側の彫り込みにほんの1ミリ以下の違いで入ってくれません。工作精度の差なんでしょうね。ボックス側を加工したくないのでどうするか考えましたが、ビス位置が同じなので保護プレートを交換すれば良いんじゃないかってことで、

f:id:TAKEsan:20210503122257j:plainf:id:TAKEsan:20210503122216j:plain

f:id:TAKEsan:20210503122220j:plain

無事おさめることができました。

f:id:TAKEsan:20210503122520j:plain

見た目は元のままです。東日本大震災で転げ回ったにも関わらず無傷だった頑丈なスピーカーでしたが、去年掃除のとき引っ掛けて倒してしまった時に上部左角にダメージを受けてます。少し残念ですが、愛着の方が上なので一生ものになって行くでしょうね。

肝心の音出し

 始終耳鳴りが止まらないおじいさんですから、当てにはなりませんが10年以上聞き続けてきたスピーカーです。うれしいことに特定の音域でのピークギャップは感じられませんでした。相変わらず魅力的な音で鳴ってくれます。しかも製品のレベルを930000から950000に上げているので、意図していた透明感が増している気配があります。また、大音量でも他のスピーカーユニットに異常が見られないのでまずは成功。最近はスマホで周波数特性が測定できる様で、試してみたいところですが、測ったら測ったで気になってしょうがなくなることはわかっているので、今回は結果が良かったということでひとまず終了。あとはエージングでどうなるかです。

ひとつ楽しみが増えました。

長期休止していたお詫びとAirPods Maxまで

もう1年以上もブログを更新していませんでした。

 この間にいろいろなことがありました。相変わらずAI寄りのプログラムを作り続けていて、赤外線カメラを使い、おもにiPhoneアプリを作っていました。このアプリは赤外線カメラを持っていなくてもiPhone端末のキャプチャー動画でYOLO3の学習済みデータを30FPS以上という実用的なスピードで試せます。自分で作った学習済みデータを入れ替え可能で、iPhone11以上そしてM1 Macで試せるんです!!。時代はコロナ禍。Flir LEPTON3.5を持ってる方はマスク有無の顔を認識して、できるだけ精度の高い顔の体温を測ることもできます。書く体力があれば、折を見てこれまでためてたものを公開しようと思ってます。

         f:id:TAKEsan:20210115123455p:plain

AirPods Maxについて。

 前から興味があったのですが、なかなか手に入らない。たまたまAmazonで在庫があったので注文したら2日で届きました。私にはオーディオ熱の波があって、ピークになると新品に交換する癖があります。その最終が15年前でした。サラリーマンとしてはギリギリの出費で、McIntosh MA6900とVienna Acoustics Beethoven Concert Grandの組み合わせ。

f:id:TAKEsan:20210217210915j:plain

 スピーカーはMcIntoshと相性がいいと言われてるJBLを使ってましたが、高音がいまいちだったのでBeethoven Concert Grandに変えてます。McIntoshはこの機種の場合、外観には似合わず比較的おとなしい音を奏でるアンプになっていて、自分的にはステキな組み合わせと思っています。それにしてもこのスピーカー。かなりのお気に入りです。ジャズピアノ、ボーカルが主なのですが、ボーカルを聞いたらこれに並ぶものがあるんだろうかというくらい。今ではスピーカユニットが変更されて、なんとペア70万以上にもなり、初老のおじいさんが買える金額ではありません。東日本大震災で転げ回ってもびくともせず、いまだにその上品で艶があって絹のような。この透明なユニットのような音を奏でてくれます。

f:id:TAKEsan:20210217213559j:plain

 過去、ハイレゾオーディオやSACD等など色々試しましたが、それに見合った音を再現するとなると、ケーブルや電源etcで、オーディオ的に細かな分解能が気になって、当然機材も高くなっていき、音楽を楽しむどころではなくなって来ます。なので、気に入った音に出会った15年前に区切りをつけています。このクラスでは音はスピーカーの周りにまとわりつくのではなく音場として聞こえてきますし、低音の質や量感、全体のバランスもかなり満足してます。

 今回はさすがに資金は限られているし、そこそこの金額で買えるヘッドホンであるiPods Maxの購入に踏み切りました。それにしてもApple製品だけに見事に評価が分かれてますね。悪い評価では、重いだのカッコ悪いだの、ソニーやボーズと比較してどうのこうの。高いから買わないだの....。

 私の場合はApple製品を多く持っているので、家庭の事情からノイズキャンセル付きで、極端に高くないものが欲しかったのと、店頭での印象が良かったので一択でした。ヘッドホンをスピーカーと比較しても、音像も低音感もまったく違うのでどうしょうもありません。

f:id:TAKEsan:20210217213509j:plain

 AirPos Maxはというと、初めて聞いた時のSACDの音とでも言いましょうか。元来ヘッドホン派ではないので、静粛感や音像、分解能の高さに驚いてしまいます。圧縮音源オンリー、デジタル送信でアンプ内蔵であることを考慮すると、本体の質感を含めてなかなか素晴らしいものでした。声の方は最初は粒子感があって細かな霧が集まって出力されるみたいな雰囲気です。これが不思議なことにしばらくすると粒子感がなくなって、解像度が増し、素晴らしい。ヘッドホンとスピーカーとでは音源からの距離が違うので分解能は比べようがありません。つまり分解能では断然このヘッドホンが良い。音の分離が優れてるからといっても、手放しにコレとは言えないのが嗜好品のおもしろいところ。Vienna Acoustics スピーカーのように艶を伴ったボーカルは聞こえてきません。 空間オーディオはすばらしいですね。このヘッドホンの凄さを120%引き出すようです。おまけ的なものではなくこれ専用としてもいい。

 まーとにかくここが良いここが悪いと言ってもしょうがない。どうせ手に入れるなら後で後悔しないようないいものを手に入れること。で、それには今の自分の知識と経験、モノの質感も含めて最良と言えれば自分の人生的にお得な買い物です。どうやらこのヘッドホンはその条件にあってるようです。

 

Jetson Nano で AI応用ソフトを作る

今回は 

Jetson nanoにインストールしたOpenFrameworksから、OpecCVDarknet(YOLO)を動かす方法を書きます。

    f:id:TAKEsan:20190613150516j:plain

 Jetson nanoでAI系のソフトをインストールして動かしてみたけれど、これを利用して自分の目標とする「何か」を作るとき、その先膨大な解説と格闘しなければならず、大概行き詰まってしまいます。また、nanoはPI3に比べれば早いといってもIntel の汎用CPUに比べると1/4位のスピード。AIエンジンを利用して応用ソフトを組む場合、インタープリタ型言語であるPython等を使うと、応用部分であきらかに遅くなってしまう傾向がある点は否めません。CythonやSWIGを使えば早くなりますが、結局C言語に戻ってしまうことになります。やはり最初からCやC++等を使って、なるべくCPU処理部分のスピードを上げるのがnanoでは得策と思われます。AIの研究者でもないので基本的な構造学習作業をワープして、即応用に繋がる方法はないものなのでしょうか?。今回は一例としてOpenframeworksとDarknet(yolo)を使って、簡単に応用ソフトを作るためのきっかけがつかめたら良いと考えて記事を書きます。

                 

                                             今回作成したYOLOv2-tinyの動画です。

                 
YOLOv2の動画。認識スピードは遅いものの結構正確でした。認識部分を別スレッドで動かしているので、動画に後から追いつく感じ...

なぜOFとDarknet(YOLO)を結びつけるのか?

 C++はコンパイラ型言語ですから、できあがったアプリの全体スピードが速いことは常識です。ところがプログラム自体も、コンパイル作業も、一般的に非常に煩雑で、専門外の人間にはに取り付き難いことが欠点でもあります。これらの欠点を大きく改善したものがOF(OpenframeWorks)だと私は認識しています。

 OFの優れている点は、オープンソースであることと、文法構成の工夫で初級段階でも高度なグラフィックソフトが出来てしまうこと。さらに先人の作った様々な画像系・サウンド系・その他のライブラリがAdoonという形で簡単に利用できることです。様々なディバイスでアプリが作れますが、私の経験ではMacのXcode環境よりLinuxの方が構造が簡単でプログラムし易いように思います。初心者にとってはコンパイルが時として上手くいかなかったりすることがありますが、日本では2人の先生が初歩から応用までわかりやすく解説していますので参考にしてください。

前橋工科大学 田所先生 

               yoppa org – 第2回 : クリエイティブコーディング基本 – openFrameworks 1

東北大学 小嶋先生 こじ研(openFrameworks)

 YOLOC言語を使って開発されていますから、OFを使ってYOLOが簡単に使えたら、どんなこともできそうな気がします。nanoでAI応用プログラムを作ってとても感無量ってことを前回の記事で書きました。

SWAPを作成する

以降のコンパイル作業時にメモリが不足して、止まってしまう可能性がありますので、SWIPファイルを最初に作成するのがベターです。nanoでの設定方法は、Jetsonの世界では有名な方(私はサメのおじさんと呼んでます)以下で説明しています。

Jetson Nano - Use More Memory! - JetsonHacks

nanoにOFをインストールする

 前回の記事でも少しヒントを書いたのですが、以前私が書いた記事を参考にして、nanoへのインストール手順をまとめた方がいます。

openframeworks jetson nano instructions · GitHub

 実はこの部分の説明をどうしたものか迷っていたので大変助かりました。

 この記事はOFのコンパイル環境を作る説明だけなので、さらに手順が必要です。 以降はOFのディレクトリに入りINSTALL.mdを確認して下さい。前回実行したサンプルを含めてOFのexampleが殆どすべて実行できます。サンプルの内容はmigizoさんが紹介しています。nanoはGPU(OpenGL)の性能がなかなかなので、最新のMac Book Proと比較しても遜色ないスピードで動きます。

openFrameworks(v0.9.8)Examples一覧 - Qiita

OFでOpenCVを使う

 YOLOを使うためにはOpenCVライブラリが必要です。機能や性能が限られます(contribやGPUが使えない)が、OFにはofxOpenCvofxCvという有名なaddonがあります。ただしこれらのaddonを使わなくともnanoに元々インストールされているOpenCVライブラリや、最新のOpenCVがOFから簡単に導入できます。nanoに入っているのはバージョン3.3ですが、それ以降のOpenCVを使う場合は別途インストールが必要です。

  LinuxでOFアプリを作る場合は、myAppsディレクトリの中のemptyExampleをコピーして作って行くことになります。以下の作業でOFからOpenCVが使えるようになります。

1.コピーしたemptyExampleディレクトリの名称を変える。

      ディレクトリ名が最終的なアプリ名称になります

     中に入っているconfig.makeに次の2行を入れます(79行目と107行目を修正)

       PROJECT_LDFLAGS=-DOPENCV `pkg-config --libs opencv`

       PROJECT_CFLAGS = -DOPENCV `pkg-config --cflags opencv`

2.ヘッダーファイルを指定する

     src/ofApp.h  を開き #include "ofMain.h" の後に

     #include "opencv2/core/utility.hpp"

    等、OpenCVに必要なヘッダーファイルを追加します。後はofApp.cppのsetup()、update()、draw()にOpenCVの様々な関数を書いて実行することができます。さらにusiing namespace cv; を宣言すればもっと使いやすくなります。

反則ですが慣れて来れば、ofApp.cppの先頭に書き込んでもOKです。ofApp.cppに自分で用意した関数を使う場合はそちらの方が簡単かもしれません(今回はofApp.cppにヘッダー宣言を入れています)

3.OFとOpenCVの画像データの受け渡し

 一番問題になるのはこの辺りで、OFとOpenCVでは画像データの構造が違うので、変換処理が必要です。この部分を簡単にしたのがofxCVアドオンですが、今回はこのアドオンを使用しない(使えない)ので、基本は次の様な感じで変換します。

 OF image形式からOpenCV mat形式 に変換

  cv::Mat mat;

  ofImage img;

  mat=cv::Mat(img.getHeight(),img.getWidth(),CV_8UC3,img.getPixels().getData());

      ※ CV_8UC3の部分はOpenCVの使用する関数によって変更する必要あり

 OpenCV mat形式からOF image形式 に変換

  img.setFromPixels( mat.ptr(),mat.cols,  mat.rows, OF_IMAGE_COLOR, false);

この2行の変換処理に関して、全体のスピードには殆ど影響が無いようです。

これさえできれば、最新OpenCVの画像処理をOFから自在に利用できるようになります。

Darknetのインストール

 このソフトは全体がC言語で開発されていることと、導入が非常に簡単な点が大きな利点です。Nvidiaではnano用に簡単に導入できるAIエンジン(Tensorflowやtorch)も公開していますが、短縮版です。一般的に著名な標準版のCaffe、Tensorflow、torch等をnanoに移植するには、非常な煩雑なインストール手順と長大なコンパイル時間を否応にも経験しなければなりません。しかも最終的にはGPUメモリ容量の問題でアウト!。その点Darknetは、Makefileを少し修正すればmakeコマンドだけで標準版がインストールできてしまい、コンパイル時間もnanoの場合5〜6分で終わってしまいます。コンパイル後はコマンド一発で動画や静止画の認識テストや学習まで出来て、画像認識性能や認識スピードも現在の最新のモノと遜色ないと言ったらどんな不満が出てくるんでしょうか?しかもnanoのメモリ4GBでギリギリセーフ。

 前から紹介してきたDarknetは、16ビット小数点演算指定ができるので、スピードではまさにnano向きなのです。一方NvidiaではJetson infarencceというjetsonシリーズで非常に有効なTensorRTを利用した3種類の画像認識が出来るソースを公開しています。(よく知られているオレンジやバナナの認識....)でも、前から思っていたのですがWebカメラを使って実行したところ、さほど認識スピードが速くないのです。特にSegNetは顕著。これはTX1,TX2,Xavierでも同じ印象を受けます。また自分で作ったデータを学習させる場合もDIGITSを使った場合、DIGITS本体のインストール作業や実行上の独特の手順が要求されます。でも最終的に出来たモノはスピードや認識精度共YOLOと互角又は性能が落ちる様に思えます。みなさんも是非試してみることをおすすめます。ある意味これがメーカー水準と言うことになることになると思います。

 ではYOLOは完全にnano向きかといえばそうでもありません。今回のDarknetは過去のバージョン(V2)も試せますが、標準データではやはり遅くなるので、アプリを作るのであればTiny yolo v3やTiny yolo v2を使うことになると思います。認識結果もさほど悪く無いことが分かっています。インストールは前回の記事を参考にしてください。

Jetson Nano を使ってみる!! - Take’s diary

今回はMakefileのLIBSO=1 がミソです。

ジャーこれを使ってOFでプログラムを書くには

どうすればいいんでしょうか?Darknetは頻繁に修正を行っているので、いつも最新版を利用したいのですが、addonにするとそういうわけにはいかなくなります。そこでDarknetのsoファイル(Shared Objectファイル)を直接リンクすることにしました。

以下の様にします。

Darknetフォルダが ~/darknet であることを想定しています

1.config.makeに次の2行を入れます(79行目と107行目を修正)

PROJECT_LDFLAGS=-DOPENCV `pkg-config --libs opencv` -lm -pthread -L/usr/local/cuda/lib64 -lcuda -lcudart -lcublas -lcurand -lstdc++  -L ./ ~/darknet/libdarknet.so

 PROJECT_CFLAGS = -DOPENCV `pkg-config --cflags opencv` -DGPU -I/usr/local/cuda/include/ -DCUDNN -DCUDNN_HALF -Wall -Wfatal-errors -Wno-unused-result -Wno-unknown-pragmas -DGPU -DCUDNN_HALF

2. OFのプロジェクトフォルダにリンクファイルを作る

プロジェクトフォルダ/src の中に以下の様にsrc1という名称のリンクフォルダを作成します。リンク先はdarknet/src です。

srcフォルダの中でターミナルを起動して、以下のコマンドを実行します

ln -s ~/darknet/src ./src1   

f:id:TAKEsan:20190613131906p:plain

3.ofApp.cppにヘッダーファイルを指定する

   以下のWebCameraを使った例のofApp.cppを参照して下さい。ここではOpenCVのヘッダーも入れています。

#include "ofApp.h"

#include "opencv2/core/utility.hpp"

//using namespace std;

//using namespace cv;

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

4.bashrc に以下の1行を追加

多分これでコンパイルが通るはずですが、yolo_v2class.hppが無いという様なエラーが出た場合は、.bashrc に LD_LIBRARY_PATHにyolo_v2class.hpp の入っているディレクトリを追加して下さい。

LD_LIBRARY_PATH=/home/????/darknet/include:$(LD_LIBRARY_PATH}

5.必要ファイルを所定のフォルダにコピーする。

OFで動いたYOLOのweightsサンプルデータは以下の3種類です。一応テスト用にダウンロードしてbinフォルダにコピーします。

https://pjreddie.com/media/files/yolov3-tiny.weights

https://pjreddie.com/media/files/yolov2.weights

https://pjreddie.com/media/files/yolov2-tiny.weights

さらに以下のファイルをdarknet/cfgから

yolov3-tiny.cfg

yolov2.cfg

yolov2-tiny.cfg

darknet/dataから以下のファイルをコピーします

coco.names

また、テスト用にbin/dataの中にフォントファイルcooperBlack.ttfをコピーして下さい。

cooperBlack.ttfは、of/examples/graphics/fontShapesExample/bin/dataに入っています。 

f:id:TAKEsan:20190613135204p:plain

    余計なファイルも入っていますが、binフォルダの中はこんな感じになります。

 DarknetをOFからそっくりそのまま使うので、認識スピードがDarknet本体より遅くなることがありません。以下は最も単純なWebCameraを使った画像認識の例です。これを発展させれば音声や画像、GPIOを含めた様々な応用ソフトが作れることになります。ビデオデータをmapに変換している部分もありますし、何より画像認識部分はOFで簡単に書けるマルチスレッドを利用してます。カメラの表示スピードは殆ど落ちないし、2つの認識スレッドを空きを見ながら処理してるので、条件が良ければ標準のものよりスピードがかなり上がります。(v2とv3に関してはtiny版が5fps位上がるようだ。なぜか標準版は変わりなし)

以下src/ofApp.h

#pragma once

#include "ofMain.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;

};

以下src/main.h

#include "ofMain.h"

#include "ofApp.h"

 

//========================================================================

int main( ){

usleep(2000000); //YOLOがGPUの競合で止まるのを防ぐため2秒のDELAYを設ける

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.cpp          Xcodeからそのままコピーしたのでインデントがおかしいですがご勘弁。OFでは実質update()とdraw()を繰り返しているだけですから、AIを使うと言っても下記のようにこの部分のソース自体は非常に簡潔になります。

#include "ofApp.h"

#include "opencv2/core/utility.hpp"

//using namespace std;

//using namespace cv;

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

 

//std::string  names_file = "coco.names"; //yolov2を動かす場合

//std::string  cfg_file = "yolov2.cfg";

//std::string  weights_file = "yolov2.weights";

std::string  names_file = "coco.names"; //yolov3-tinyを動かす場合

std::string  cfg_file = "yolov3-tiny.cfg";

std::string  weights_file = "yolov3-tiny.weights";

//std::string  names_file = "coco.names";  //yolov2-tinyを動かす場合

//std::string  cfg_file = "yolov2-tiny.cfg";

//std::string  weights_file = "yolov2-tiny.weights";

float const thresh = 0.20;//この数値を変えることで認識の閾値を調整する

cv::Mat mat;

ofTrueTypeFont cop20,cop50;

 

Detector detector(cfg_file, weights_file);//yoloの初期設定

 

std::vector<bbox_t> result_vec; //認識した結果のバウンディングボックスの座標

float ttt,ttt1;  //Time測定で使用

//以下オブジェクト(クラス)名称を読み込むための関数

std::vector<std::string> objects_names_from_file(std::string const filename) {

    std::ifstream file(filename);

    std::vector<std::string> 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<std::string> 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) {

ofNoFill();

ofSetLineWidth(2);

//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);

 

string ss;

ss=" "+ obj_names[i.obj_id]+" "+ofToString(i.prob*100,1);

ofSetColor(255);

cop20.drawString(ss, i.x,i.y+15);

    }

}

//--------------------------------------------------------------

//  DetectNet部分をマルチスレッドにする。

class matmat: public ofThread {

public:

void threadedFunction(){

 

ttt=ofGetElapsedTimef();

//ok=false;

cv::Mat imgx;

cv::cvtColor(mat, imgx, cv::COLOR_RGB2BGR);//OpenCV画像用Mat配列を認識用にRGBからBGRに変換

ok=false;

result_vec = detector.detect(imgx,thresh,false);//一番肝心な認識関数バウンディングボックスの座標列をresult_vecに格納

ok=true;

ttt1=1.0f/(ofGetElapsedTimef()-ttt); //以下FPS表示

std::stringstream stm;

stm<<"Framerate : "<< ofToString(ofGetFrameRate(),2)<<" FPS      YOLO : "<<ofToString(ttt1,2)<<" FPS";

ofSetWindowTitle(stm.str());

 

stopThread();

}

bool ok;

};

matmat Found_X,Found_Y;//2つのタスクを宣言

//--------------------------------------------------------------

void ofApp::setup(){

obj_names = objects_names_from_file(names_file);//オブジェクト名称をファイルから読み込む

    cop20.load("cooperBlack.ttf",10,true,true,true);//字体の初期設定

    cop50.load("cooperBlack.ttf",20,true,true,true);//字体の初期設定

    video.setDeviceID( 0 );//WebCamera ディバイス番号 通常は0

video.setup(960,720,OF_PIXELS_RGBA);//WebCameraの初期設定

Found_X.ok=true;//マルチスレッドの前処理

Found_Y.ok=true;//マルチスレッドの前処理

 

}

//--------------------------------------------------------------

void ofApp::update(){

video.update();

if(video.isFrameNew()==true){

        //ビデオフレームが更新されたら2つのタスクの空いている方でAI認識させる

if (Found_X.ok ){

            mat=cv::Mat(video.getHeight(),video.getWidth(),CV_8UC3,video.getPixels().getData());//認識用の画像をOpencv Mat形式に変更

Found_X.stopThread();Found_X.startThread();;

}

    else   if (Found_Y.ok ){

            mat=cv::Mat(video.getHeight(),video.getWidth(),CV_8UC3,video.getPixels().getData());

Found_Y.stopThread();Found_Y.startThread();

}

    }

}

//--------------------------------------------------------------

void ofApp::draw(){

   ofSetColor(255);

   video.draw( 0, 0 );//WebCamera画像を表示する

   Found_X.lock();//ここで他のタスクをロックしないとプログラムがダウンする

   Found_Y.lock();

    show_console_result(result_vec, obj_names);//バウンディングボックスを描画する

   Found_X.unlock();

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

 

}

OFでYOLOを使う場合の条件

  • yolov3はメモリが不足して使えない。
  • yolov2は設定の変更でなんとか動くが、途中で止まる場合は何回か再実行を試みる。

   yolov2.cfg の最初の方のパラメーターを4箇所変更してみて下さい

     batch=1

                  subdivisions=64

                  width=320

                  height=320

      ※width,heightは32の倍数。nanoでは416が限度

  • yolo v3 tinyは応用できるが現行のサンプルデータはなんか変。プログラム上のバグがある模様。yolov2 tinyより認識率がかなり落ちるようだが、データ数か学習が失敗してる可能性もある。(私の学習させたジャンケンではそんなことはなかった)
  • yolov2 tiny が良いみたい。ただし誤認識が多い

ということを考慮するとでtinyを使った場合、nanoで問題なくアプリが作れます。それもyolov2-tinyが良い様です。

 これ以上を望むならTX2やXavierということになりますが、nanoでも殆ど問題ないことが分かりますでしょうか。

以下画像は、このアプリを使って実際に実行させた結果です。

f:id:TAKEsan:20190613142223p:plain

  yolov2を使った画像認識 yolov2.cfgは上記設定。treash=0.2  かなり良いが遅い->5fps程度。ただし動画は60fps。本来のカメラ性能は30fpsなので、まだまだCPU処理部分に余裕がある=凝ったアプリが作れる

f:id:TAKEsan:20190613142254p:plain

yolov3-tinyを使った画像認識 cfg内容は変更なし。treash=0.1  18~25fpsくらいで認識するがバウンディングボックスの範囲が実際より小さく多重認識がある。前にオリジナルデーターで学習させた時の経験上、使ったcfgファイルが今回のサンプルデータと合っていない可能性がある。(自分で学習させた時は全く問題なかった)

f:id:TAKEsan:20190613142310p:plain

yolov2-tinyを使った画像認識 cfg内容は変更なし。treash=0.5  この数値以下にするとかなり小さい対象物も認識するが誤認識も多くなる。これも18~25fpsくらいで認識する。

最後に外付けSSDに開発環境を移行する。

  SDカードでも単独開発できますが、寿命や信頼性の問題、さらにリードライトスピードが遅いので、開発には向きません。SSDにそっくりそのまま移行した方が、OSのレスポンスも早くなります。

 以下「サメおじさんのブログ」を参考にして、全く問題なく全ての環境がSSDに移行できました。

Jetson Nano - Run on USB Drive - JetsonHacks

 SDカードに最終環境が残っているので、外部で使う時などは、またSD環境に戻してSSD無しで使えることが利点です。SDブートに戻すには、

SD側の/boot/extlinux/extlinux.confの中身

APPEND ${cbootargs} rootfstype=ext4 root=/dev/sda1 rw rootwait

の sda1をmmcblk0p1に変更

SSDブートの場合はsda1に変更します。ただしスペルを間違うと2度と起動しなくなるので注意が必要です。(その場合でも他のUbuntuマシンを使って修正は可能)

 この環境ではブート時に一旦SDを見に行ってからSSDにOSを移行する設定なので、SDは取り付けたままにします。SSDを外した場合起動しなくなるので注意が必要。再度SSDを接続してリブートすれば問題なく動きます。

 Jetsonフォーラムで、M.2スロットに変換ボードを接続すればSSDが接続できるかも....とメーカー側の投稿があり、試してみたら全くディスクとして認識されませんでした。 (ダメ元でもう少しトライしてみますけど)

 f:id:TAKEsan:20190614072135j:plain   f:id:TAKEsan:20190614073157j:plain

アーくやしい!!

 

この頃気候のせいか体調があまり芳しくなく、頭がボーとしていて文章がまとまりません。分かりにくいところはコメントください。

                            ではでは。

 

Jetson Nano を使ってみる!!

Jetson nanoが発売されました。

f:id:TAKEsan:20190408231608j:plain

 一応NvidiaですからAI分野に特化したボードってことになりますが、Pi3 B+にMobidiusを追加した価格より、機能面を考慮すると大幅に安いというような衝撃的な仕様でもあります。

 実際はどうなのかってのが今回の内容。NvidiaのJetson関連ボードはTK1から始まってTX1、TX2、Xavierとなってますが、今回のボードはTK1とTX1の中間ぐらいの構成になってます。

 いつものように、メーカーの宣伝が派手なので本当のところは?って、誰でも思うところだと思います。私の理想とする環境はかなり偏ってるので、そのつもりで読んでいただければと思います。

OSのインストールは超簡単。

 今までのTXシリーズは内部eMMc起動のために煩わしい手順が必要でした。今回はそれをやめてSSDブートに変更したため、インストールで引っかかりにくくなりました。メーカーのインストール手順で何の問題もありませんが、最初にUbuntuが立ち上がるまではPi3より遙かに早かったとだけは言えます。インストール完了時点でCUDAもCUDNNもOPENCV3.3もインストールされてるので文句の付け様がありません。

 標準ではWIFIやBlutoothは付いていません。CPUを取り外すとM2コネクタが付いているので汎用のWIFIカードを付けるか、USBドングルってことになります。今回は手っ取り早く余ってるWIFIドングルを使ってみました。

 Jetsonシリーズの常で、日本語環境には向かないので、インストール時は注意してください。ある程度環境を作ってから日本語化する方が無難。マーSDカード(32GB以上)枚数が経済的に許す限りOSをたくさん作っておけば良いことですから、そんなに心配する必要も無いとは思います。

CPUのスピードは?

  いつもの簡易テストでメインコアのCPUテストをしてみるとPI3(Pi3B+1200hzsでオーバークロック)が56.30secに対して 、Nanoは最大能力にすると17.48secでした。そんなに早くはありませんが、それでもPI3B+の約3.2倍!!

 Nanoのオーバークロック方法は、今までのJetsonシリーズとはとは違い、コマンド化されたようです。USB電源から起動可能ですがオーバークロックさせると、システムが止まってしまいます。事前に5V 4A電源(これが上限のようです)とジャンパーピンを取り付ける必要があります。さらにFANを付け足すと一応安心。

 実際動かしてみると、普通に使うんであればこれ以上のスピードはいらないんじゃないかって思いました。でもそこはLunux。汎用にするには少し問題もあることは使ってる方なら分かると思います。

f:id:TAKEsan:20190409083836p:plain

最大の能力を引き出すには、以下2つのコマンドを続けて実行します。

sudo nvpmodel -m 0

sudo jetson_clocks

nvpmodelはNanoの場合0か1の2者選択のみのようでした。0が最大で1が最小です。また、現在の設定値(CPU,GPUの周波数)を確認するには  sudo jetson_clocks --show  を実行します、

 以下の動画はプレインストールされているCUDA Example(GPUを使ったサンプル)を実行させたものですが、この段階でスピード的にはTX2でした(思っていたよりかなり早かった)。

      

Openframeworksが動くか?

 動きました。過去に紹介したTX2の手順で基本的に10.01のインストールが可能です。ただし公式のものは、Ubuntu 18.04でコンパイルエラーになるので、今のところnightly buildsを使う必要があります。また、tess2及びkissのライブラリも再コンパイルが必要でした。すぐにバージョンアップされる可能性があるので、インストール方法は時期を改めて紹介します。OFのnightly builds内容を見ると次期バージョンでは標準でOpenCV4(注目すべきはdnn!!)が使えるようです。この画像は実際にNanoで実行させたものですが、今持ってる最新のMacBook Proとスピードが変わらない....。早っ!!

     

DARKNET(YOLO)のインストール

 今までの経験上インストールにはさほど問題なかったのですが、今回も、これを使わせていただきました。ソース内容が時間単位で変更されています、この方は一体どのくらいの能力を秘めてるんでしょう。DARKNETを利用したC++でのソフト開発はなかなかハードルが高いのですが、最後の説明のように本来のスピードを保ったままOpenframeworksに移植できています。

Nanoで使うには、Makefileの修正が必要です。以下最初の方で赤文字部分の修正が必要(6行の修正)。NanoはTX1とCUDAアーキテクチャが同じのようです(ここでちょっと手こずりました)。修正後 make コマンド一発で、一応DARKNETが動く環境ができます。

4/27:追記----------------------------------------------------------------------------

.bashにCUDAのパスを追加しないとnvccが使えないのでエラーが出ます。なので以下2行の追加必要。

export PATH=/usr/local/cuda/bin:${PATH}

export LD_LIBRARY_PATH=/usr/local/cuda/lib64:${LD_LIBRARY_PATH}

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

 

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

GPU=1

CUDNN=1

CUDNN_HALF=1

OPENCV=1

AVX=0

OPENMP=1

LIBSO=1

ZED_CAMERA=0

 

# set GPU=1 and CUDNN=1 to speedup on GPU

# set CUDNN_HALF=1 to further speedup 3 x times (Mixed-precision on Tensor Cores) GPU: Volta, Xavier, Turing and higher

# set AVX=1 and OPENMP=1 to speedup on CPU (if error occurs then set AVX=0)

 

DEBUG=0

 

ARCH= -gencode arch=compute_30,code=sm_30 \

      -gencode arch=compute_35,code=sm_35 \

      -gencode arch=compute_50,code=[sm_50,compute_50] \

      -gencode arch=compute_52,code=[sm_52,compute_52] \

#   -gencode arch=compute_61,code=[sm_61,compute_61]

 

OS := $(shell uname)

 

# Tesla V100

# ARCH= -gencode arch=compute_70,code=[sm_70,compute_70]

 

# GeForce RTX 2080 Ti, RTX 2080, RTX 2070, Quadro RTX 8000, Quadro RTX 6000, Quadro RTX 5000, Tesla T4, XNOR Tensor Cores

# ARCH= -gencode arch=compute_75,code=[sm_75,compute_75]

 

# Jetson XAVIER

# ARCH= -gencode arch=compute_72,code=[sm_72,compute_72]

 

# GTX 1080, GTX 1070, GTX 1060, GTX 1050, GTX 1030, Titan Xp, Tesla P40, Tesla P4

# ARCH= -gencode arch=compute_61,code=sm_61 -gencode arch=compute_61,code=compute_61

 

# GP100/Tesla P100 - DGX-1

# ARCH= -gencode arch=compute_60,code=sm_60

 

# For Jetson TX1, Tegra X1, DRIVE CX, DRIVE PX - uncomment:

 ARCH= -gencode arch=compute_53,code=[sm_53,compute_53]

 

# For Jetson Tx2 or Drive-PX2 uncomment:

# ARCH= -gencode arch=compute_62,code=[sm_62,compute_62]

 

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

ジャー肝心のAI認識です。

 結論的にはTX2とかXavier並の性能に追いつかせることは無理のようです、いつものようにコツが必要。YOLO3とかYOLO2では満足なスピードは得られません。つまりかなり遅いし、YOLO3に至ってはアプリに組み込むとメモリが完全に不足する雰囲気です。でもNvidiaでの公式発表ではTiny YOLO3がかなりのスピードで実行できているのが気になるところ。

 Tiny YOLO3の学習済みデータの大きさは、標準(大体200MB前後)のものより1/6(35MB)程度なので認識率は正直?です。今回は今まで作ったジャンケンのアノテーション済みデータを元にTiny YOLO3を試してみました

f:id:TAKEsan:20190409072556p:plain

    データ:16,115枚

    クラス数:4 (サーモグラフィー白黒画像を使ったグーチョキパーと顔の認識)

    母艦:intel i7 6700K、 GPU GTX 1080Ti

lossが最小値から上がってきているの8000回目で止めています。ちなみに採用したデータは6000回目のもので、この時の計算時間はなんとわずか45分でした。ただしグラフが大きく振れているので認識のばらつきがある気配...。以下はYolov2の学習状況ですが、比べてみるとLossの下限を含めてずいぶん違うことが分かると思います。v2でも6000回がベターで学習時間は2時間程度でした。今回はCUDNN_HALF設定(16ビット浮動小数点)なので前回より早かった可能性があります。でも、今回のように本格的な学習作業(画像の大きなモノ)は、nanoではトライするだけ無駄と思われますのでご注意を.....。多分メモリーが不足して初期段階で止まってしまうと思われます。

f:id:TAKEsan:20190409073929p:plain

 今回nanoテスト用に使ったサンプルソース(以下の動画)は、元々XavierでYOLO3を動かすために自分で作ったものです。PI3で赤外線センサーカメラ表示処理をした後(左側のディスプレイ)、WIFIで画像データをNanoに送り、認識が確実になるよう画像拡大補間やノイズ処理などを施した上で、画像認識をさせた後(右側のディスプレイ)、その認識座標他を再度PI3に送って認識結果を表示させています。「おうちクラウドAI」と名付けました。つまり、離れたところにPI3を置けば、PI3でリアルタイム画像認識をしているような感覚です。  

 赤外線センサーは、Nano直付けでも良い(ただしSPI設定はかなり難しいので今後に期待)のですが、使用用途から考えてWiFiの方が断然有利なので、あえて手間のかかる処理を加えて画像と各種データの送受信をしています。この程度のAI認識ではTinyでも特に問題が無いことが分かります。PI3、Nano双方でGPIOが使えるので応用は無限大!!

      

何よりスピードが早い。今回はLEPTON3(FLIRの赤外線センサーカメラ)の画像読み込みにPIを使ってますが、ESP8266でも可能。3年間積み上げてきたものです。たった1万円強のJetson nanoで動くとなると感慨もひとしおです。

つまりJetson Nanoって....。

 判断は皆さんにお任せします。

 

Jetson Nanoの記事を追加しました。 

takesan.hatenablog.com

 

追伸

おかげさまで、今まで作ったすべてのモノをさらに進化させて展示することができました。いろいろな意見をいただいて、とても有意義な2日間となりました。みなさんありがとうございました。

f:id:TAKEsan:20190510223431p:plain

 様々なディバイスで独立した処理を実行しています。BosonとLeptonは今僕にできるサイコウの詳細な表示が実現できました。XavierもnanoもiPhoneやm5Stackでも!!。この画像に写ってるだけでも「おうちクラウドAI」は2系統で同時実行してます。6種類のディスプレイで違った処理をしてるのですが判別できるでしょうか?(設定と起動までが大変な作業でしたが....)

 

 

 

素敵な花が咲きました。

20年ほど前東京で仕事をしていた頃

 世界洋ラン展で買ってきたパフィオペディラムです。本当はすべてが小豆色のはずだったのですが、2年間育ててやっと咲いた花が「点花」と呼ばれる斑点の付いた花びらでした。パフィオペディラムには原種と整形花があって、この花は整形花に属します。要するに人の手によって改良が加えられた花です。

 愛好家では必衰の掛け合わせによる親株の名前も全く分からなくなりました。すなわち価値はゼロです。

 この種の花は独特の人工的な表情があって、好き嫌いがあります。私もどちらかというとあまり好きではありませんでした。

 購入してから2年目に咲いた花は、少し小さめでしたが、透き通るような白と清楚な緑。そして鮮やかで全体にちりばめられた小豆色の点と形が、派手と言うよりも清々しくとても印象的でした。

f:id:TAKEsan:20190319182721j:plain

 月日がたち、毎年咲いていた株も少しずつ小さくなって、5年ぐらい前から花が全く咲かなくなりました。夏場は毎年玄関先の日陰に置いて、冬はいつもの窓際。私にとってはあまりにも素晴らしかったこの花の印象が忘れられなかったんですね。その間に株がどんどん大きくなりました。

 去年の11月花芽が付いてとても喜んでから5ヶ月。こんな花が咲きました。縦は10cm。今までで一番大きな花です。

f:id:TAKEsan:20190319182900j:plain

乾杯!!

 

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日も動かすわけじゃないので)

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