wrongwrongな開発日記

情報系大学生が挑戦したことや日常を書いていきます

たった1文字付け足すだけで、5000兆円が3688.35倍になる、その驚きの方法とは!?

最近流行りの「5000兆円欲しい!」という言葉。
でもちょっと待って下さい!簡単な方法で5000兆円は3688.35倍になります!
どうせなら、お得に沢山のお金を要求してみませんか?
その驚きの方法とは……

#include <iostream>

using namespace std;

int main() {
    unsigned long long Money = 5000000000000000;//5000兆
    cout << ~Money << "円欲しい!" << endl;
    return 0;
}
18441744073709551615円欲しい!

~5000000000000000円欲しい!
たったこれだけのことで、18441744073709551615円要求することができました!

……ホントすいませんでした。

貧困の本質と、日本の社会に蔓延する非効率

突発的に意見を記事にしてみます。

目次

貧困の本質とは

「貧困の問題の本質は、定期券や回数券の方が総合的に安くても、貧乏人はそんなに大きな金額を出せないから、結果毎回切符を買うことになること」
Twitterで、こんなニュアンスのツイートを見かけました(出典は忘れてしまいました)。

 自分はこの、お金が無いせいで非効率な選択を迫られることを貧困の本質として、今回の記事を書きます。

予算不足と二重に非効率な選択

 お金の使い方の話をすれば、どんな場でも投資と回収の効率が語られます。
しかし、その中で予算をたくさん確保することが、効率に対してどれだけ重要かという考えは案外語られることが少ないように感じます。

 よく行われている会議を例に取ります。

resnavi.info

 

「重役を招集して会議をすることは、その参加者や関係者に対する時給を使っているのと同じことだから、その時給の合計を下回る金額の決定に関しては会議をすべきではない」という意見を聞いたことがありますが、これをできない原因こそ、この貧困の本質ではないかと自分は思います。

 つまり、予算が余りにも限られているため、その配分の決定のためには結果として会議をする必要が出て来る。それがどれだけ総合的に見て非効率な事であっても、予算上限がキッチリと区切られている以上、その会議には、会議を行うために掛かる投資分の価値が出てしまうということです。

現在への影響

 やるかやらないかを決めることは、物事の本質ではありません。やると決めたら、それを実現することが必要であり、実現のためには、お金以外にも知識・経験が必要になります。

 この知識と経験は、時間を掛けなければ得ることのできないものです。一万時間の法則などが、時間を掛けることの重要性を語る良い例でしょうか。

 会議のために時間を使う人間には、実現すべき課題があるはずです。しかし、会議は参加者から時間を奪っていくものです。逆に言えば、効率が悪いまま行われる会議は、事業の失敗や、その達成までの時間を遅らせる原因になるということです。

未来への影響

 事業は、行うことで初めて効果が期待できます。また、やるかやらないかという話し合いよりも、実践のために動いた際に得ることのできる知識・経験の方が、遥かに有用な、次に繋がる財産となります。

 次に繋がる財産を得るということは、例えその事業が失敗に終わったとしても、未来における成功が近付いた、と言ってもよいでしょう。

 予算不足から事業開始の決定が遅れたり、事業が立ち消えになってしまえば、未来の成功も、失敗という経験すらも手に入らないままです。

予算が無ければ、非効率な手段を使うしかなくなる

 予算さえあれば、無駄な会議への投資が減り、人員が自らの担当する課題の実現のために割く時間が増え、新たな課題の実現によって将来に繋がるノウハウの蓄積が行われます。

 予算が無いということは、結果として現在の事業と、将来の事業の両方に対して悪影響を出す、非効率な選択を迫られるということです。

 あるいは話し合いが持たれるならまだいい方で、本当に追い詰められてしまえば何か挑戦したいと言っても、「予算が無いから」の一言で、話し合いすら起こらないのかもしれません。

 ここに書いた例のように、予算が無ければ投資を行うことができなくなり、結果として中長期的な大損を選択せざるを得なくなります。

予算と組織の生産性の関係

 自分は、以上ような例から、予算不足は組織の生産性を二重に損なうと考えています。予算が無いから慎重になる必要ができ、慎重になるから時間やコストが掛かり、結果として非効率になってしまう、というスパイラルが見えます。

 また、予算と組織の生産性は、少なくとも線形なものでないとも考えています。つまり、予算を減らせば、生産性はより大きく落ち込むと考えています。

 予算を減らしても生産性が落ちないだとか、この予算でもこれぐらいはできるはずだとか、そんな意見は机上の空論ではないでしょうか。

節約・効率化のための予算

 こういった場合に、「じゃあ節約・効率化すればいいじゃないか」という意見があるかもしれませんが、この場合節約・効率化のための予算すらもつかないのです。

 つまり、組織的に動くことはできず、節約・効率化を個々人の努力のみに頼ることになります。良くあるブラック企業がこれでしょう。

 その効果も、ノウハウ無し、予算無しでは高が知れています。

 例えば効率化のために人を動かしたとして、掛かるコストと、それによって得られる効果はどれほどでしょうか。

 新しい機材を導入したり、新しいシステムを導入したり、本気で効率化を図ろうというなら、掛かる金額はそれこそただの切符と定期券位には金額が違います。勿論、最終的な投資効率も、切符と定期券位には違います。

 予算を付けずに人だけ動かしたとして、大規模な投資をした場合と比べ、どれだけの効果があるのでしょうか。自分は、まさにこの部分が、貧困の本質と重なって見えます。

銀の弾丸なんて無い

 組織の余裕の無さを、個々人の細々とした努力によって打開できると考えている人間は、一種の銀の弾丸、万能薬を求めていると言って良いでしょう。

 もしかすると、それをできる可能性はあるのかもしれません。個々人の努力によって完璧に調整された、完璧に無駄の無い予算配分というものがあるのかもしれません。

 しかし、その完璧な予算配分も、何か一つのトラブルで簡単に狂ってしまうでしょう。また、その完璧な予算配分のためには、より多くの情報を集める必要が有り、現実では実現のためには非効率な会議がより多く行われるだけでしょう。

 無駄を嫌うあまりに、より完璧な予算配分、より完璧な節約という方向にリソースを注ぎ込み、どんどん痩せていくというのは本末転倒ではないでしょうか。そもそも、予算は使うために確保する物です。

 しっかりと予算を使って、活動をしていかなければ、痩せ死にのスパイラルに陥るだけでしょう。

教育や公共の組織への影響

 今予算不足が最も大きく影響しているのは、教育や公共の組織だと、自分は感じています。

 予算は増やさないがちゃんとやれ、予算は減らすがちゃんとやれ、もう限界近い所まで削っているのに、更にやれ、もっとやれと本当に強い圧力が掛かっていることをひしひしと感じます。その予算に関しても、クーラーの設定温度をどうこうしろだとか、そんな小手先の手段だけで十分に確保できるだろうという文句をよく聞きます。

 その声が受け入れられてしまうのは、公共の組織はそれを聞かない訳にはいかないからでしょうし、その上に立つのは、やはりその声に応える必要のある人間だからでしょう。

今の現場の非効率さ

 よく神エクセルだとか、自動化の進まなさだとかで、様々な実例と共に公共の組織は揶揄されます。

  一方で、その改善のための予算は付きません。

 教育現場も、小・中・高と全てが予算不足によって悩まされています。特に教師という仕事の余りにもブラックな様は社会問題として周知されつつあります。

toyokeizai.net

 共通して言えることは、時代に取り残された部分に大規模な予算を当て、大幅な効率化を図る必要が有るという点です。

 システムの導入と、それに合わせた人員の教育、はっきり言って膨大な予算が必要でしょう。

 また、大学も同じように、研究費の不足からくる研究力不足が問題となりつつあります。

blog.goo.ne.jp

予算を上乗せし、集中的に使うべきではないか

ここは何の具体性も無い、個人的な意見です。

 自分は、現在の予算に更に上乗せする形で、一度ある場所、ある分野に集中して予算を投入し、一年を目途に徹底的な効率化を図ってみるべきだと考えています。つまり、新しいシステムを導入し、それに人員が習熟するまで徹底的に予算を付けるということです。

 役所でも学校でも構いませんが、それによってどれだけ投資とそのリターンがあるかを確認することができます。また、そのノウハウは、次の投資の際の効率化に役立つでしょう。

 ここまでの意見で最大の問題は、予算不足による悪影響はある程度説明していても、予算を投入することによるメリットを説明できていない点です。だからこそ、自分はこれを実践して、確認して欲しいと思います。

 勿論、一度に全ての場所で同じことをするだけの金がどこにも無いということは分かっています。だからこそ、それを解決するための第一歩を、自分は望みます。

 その一歩目にこそ、五年や十年掛かってでも、しっかりとした投資が必要だと、自分は思います。

薄く広げることはしないで欲しい

 ここで強く主張したいのは、薄く広げるような投資だけはして欲しくないということです。

 貧困の本質で言うなら、投資を薄く広げることは、切符を多く買えることには繋がっても、定期券を多く買うことに繋げることはできません。

 やるなら徹底的に、予算食いの効率化が毒だと言うなら、皿まで食べつくすような投資を望みます。

おわりに

 自分がこの記事で主張したかったことは、お金を使わないということ、お金を使えないということがどれだけ非効率か、ということです。

 特に公共の組織が陥っている、予算不足から非効率が改善できないというスパイラルは、早急に解決すべき問題でしょう。社会問題として語られるようになってから大分時間が経っています。

 自分は、今の日本が急速に貧困へと落ちようとしているように感じています。その理由としては、どこを見ても、やれ節約だ、やれ効率化だと叫ぶばかりで、誰も大きく予算を使わないように見えることが大きいです。

 しかし、その効率化は果たして本当に正しいのでしょうか。それこそ、必要とされているのはインフラ規模のものなのに、手作業を効率化したことを誇っているだけのように見える場面が多々あります。それが切符と定期券の話に重なって見えます。

 挙句の果てに求めるのは、低コストで効果の高い銀の弾丸です。最近ではAI・人工知能がブームだからと言って、それなら何とかなるだろうという妄想を基にした発言を上が行っている、あるいはそういった話を振られた、という話をよく耳にします。

 それ以前にできることは多々あるはずです。自動化できる部分が多くあるはずです。時間も金も掛けずにできることなんて無いのではないかと思います。

 自分は、予算は投入すればするほど、効率的な選択肢が見えて来ると考えています。この社会はお金を使えばもっと良くなるはずだ、という願望を込めて、記事を公開します。

初心者でも分かる、自作PC DDR4メモリーの選び方(2017年度)

PCを自作したいけれど、メモリの選び方が分からないという人向けに、DDR4メモリーの選び方を書きました。
一応、玄人が読んでも楽しめる記事に仕上がったかなと思います。
質問などあれば、記事に関係のないことでも気軽に書き込んで頂けるとありがたいです。

モリーの基礎知識

まずメモリーを選ぶ上で押さえておきたい基礎知識から紹介します。

モリーの基礎的な情報

モリーを選ぶ上で重要な3つのスペックは、動作周波数・レイテンシ・動作電圧です。
基本的に、動作周波数が高く、レイテンシが低いものが有利とされます。
レイテンシは、キャスレイテンシ(CL)や、タイミングなど別の呼び方もあります。

www.ark-pc.co.jp
例えば上のページでは、以下のような記載があります。

XMP2.0: DDR4-3200 CL15-15-15-35 1.35Volt / SPD: DDR4-2133 CL15-15-15-36 1.20Volt

この記載からは、以下のことが読み取れます。

  • XMPに対応している
  • 動作周波数3200MHz レイテンシ15-15-15-35 動作電圧1.35V
  • 動作周波数2133MHz レイテンシ15-15-15-36 動作電圧1.20V

XMPと、動作周波数などの情報が複数記載されている点は後述します。

XMP

XMPとは、メモリーの動作速度に関する規格です。
モリーのOCは、最近ではメモリの持つXMPのプロファイル情報をマザーボードに読み込ませるというやり方が一般的になっています。
先ほど、動作周波数などの情報が複数記載されていたのは、メモリーがこのプロファイル情報を複数持っているということを表しています。
最近ではどのマザーボード/メモリーXMPに対応しているものがほとんどなので、マザーボードXMPに対応しているかどうかを確認する必要は特に無いです。
また、基本的なDDR4メモリーはDDR4 2133のプロファイルを持っており、マザーボードはDDR4 2133に対応しているので、どの組み合わせでも全く動作しないということはありません。

プロファイルについて

DDR4メモリーには複数のプロファイルを持つものがありますが、注意したいことは、高い周波数に対するプロファイルを持っている製品だからと言って、2133との間のプロファイルを持っているとは限らない点です。
例えば、DDR4 2666対応のメモリーだからといって、2400のプロファイルを持っているとは限りません。
これが問題になるのは、IntelのH270など、OCには対応していないが、XMPには対応しているというマザーボードを買う時です。
2666や、それを越える高周波数のメモリーを買っても、プロファイルが無いせいで2133でしか動かせないということになります。
一応、そのようなマザーボード向けの高性能メモリーというものもあります(後述)。

ヒートシンクの必要性

ヒートシンクがあるメモリーの方がいいのか、という話は耳にします。
個人的な意見としては、「無くてもいいけどあった方がカッコいい」です。
また、メモリークーラーやトップフロークーラーを用いる場合には、ヒートシンクがあった方が当然冷えやすいです。

マザーボードとの相性

相性問題という話は割と有名ですが、全く動作しないというような場合は減っています。
最近のメモリ・マザーボードの相性問題は、XMPのプロファイルを読み込んだ際に起動できなかったり、額面通りのスペックで動作させられないような状況を指す場合が多いです。
これは特に3000MHzを越えるような高速・低レイテンシ・高電圧なメモリーを、4枚以上差したような場面で発生しやすいです。
解決策としては、メモリーを買い直すか、レイテンシ・周波数の設定を緩めるなどがあります。
ただし、メモリーの相性問題は、マザーボードBIOSの更新によって改善する場合が多くあります。

相性問題への対策

少し本題から外れますが、相性問題を嫌うなら、以下のような対策があります。

  • マザーボードのQVLに記載されたメモリを選ぶ
  • メモリの枚数をできるだけ減らす
  • セットで動作確認が済んでいるメモリーを選ぶ
  • 発売から時間の経った(=BIOSが成熟した)マザーボードを選ぶ

最後の対策に関しては、新しさとの兼ね合いもありますが、基本的に発売から半年位が価格・性能などの点で買い時かなと、個人的に思っています。

モリーの選び方

ここまでで紹介した知識があれば、一通りのメモリーを選ぶことができます。
性能を重視しないなら、規格に関係なく安めのメモリーを買い、DDR4 2133で動かすのが良いでしょう。
記事の執筆時点では、DDR4メモリーはCorsairのDDR4 2666メモリーが安いらしく、選ぶ人が多い印象があります。
性能を求めるなら、予算内で、QVLに載っていて、できる限り高周波数低レイテンシなメモリーを選ぶ、です。
もちろん、見栄えを気にするなら、ヒートシンクの形やLEDなんかにも注目です。
この基本はほぼ変わりません。
ここから書くのは、もう一歩踏み込んだ内容です。

プラットフォームに最適化された製品

DDR4メモリーの中には、特定のプラットフォームに最適化された製品があります。
例えば、G.SkillやGeILといったメーカーは、最近話題のRyzen向けに最適化したメモリーを発売しています。
www.ark-pc.co.jp
www.geil.com.tw

Intelでは

特にX99やX299のような環境Intelのプラットフォームについてもメモリーの相性があるようで、踏み込むならプラットフォームに合わせたメモリーを選んだ方が良いようです。
ただし、Intelアーキテクチャとメモリーの話は、前述の通りガッチガチに調整された高周波数・低レイテンシなメモリーを多く差して完璧に動作させたい、という時に必要な話で、そうでもない場合には特に意識する必要は無いでしょう。
少なくとも、まず参考にすべきなのはマザーボードのQVLです。

レイテンシの違い

モリーの中には、同一メーカー・同一ヒートシンクでレイテンシの違う製品があります。
例えば、以下の2つです。
www.ark-pc.co.jp
www.ark-pc.co.jp
これらを取り違えてしまうと、少しとはいえ違った製品を購入することになるので、こだわる際には注意が必要でしょう。
モリーの型番には、基本的に末尾にCXXやCLXX(XXはレイテンシ)と付くので、型番を読むことで、レイテンシの違いを見分けることもできます。

「安く」買いたい

モリーは、枚数が少ない方が容量単価が低い傾向があり、同じ16GBでも、4GBx4枚より8GBx2枚の方が安い場合が多いです。
これ以外には、保証やサポートなど、リスクに対する部分でしょうか。
例えばG.Skillでは、BIOSからマニュアル/XMPでメモリOCをして、結果メモリーが故障したとしても、無期限に新品と交換という保証があります。
www.mustardseed.co.jp
転んだ先の杖ということで、多少は保証も気にしてもいいかもしれません。
また、メモリーのセットでの買い方として、2枚セットを2つ買うという買い方を聞いたこともあります。
これはメモリーが故障した際に、4枚セットで買っていると、規約の問題で4枚全てを修理に送る必要が有り、PCを使えない時間が出るため、その対策ということでした。
個人的には、メモリーの故障のリスクはそこまで高くなく、できる限りセットで購入した方が、テスト済みであることから上手く動く確率が高いため、できるだけ纏めて買った方が良いと考えています。

CPUクーラーとの干渉

特に大型のCPUクーラーや、メモリーに覆いかぶさる大きさのトップフロークーラーは、背の高いメモリーと干渉する場合があります。
まずはCPUクーラーの許容メモリ高と、メモリーヒートシンクの高さを調べた方が良いでしょう。
モリーヒートシンク高に関しては、自分がまとめた記事がありますので、良ければ参考にどうぞ。
wrongwrong163377.hatenablog.com

どうやって探すか

モリーを探す際に落とし穴になりがちなのは、価格.comのようなサイトでは、掲載されているメモリーの種類が少なかったり、レイテンシといった情報が深く書かれていないため、ベストなメモリーを探しにくかったりする点です。
自分は、メモリーを探す場合には、掲載情報の多さや良質さから、PC Shop Arkと、OverClockWorksという2つの通販サイトをお勧めします。
www.ark-pc.co.jp
www.ocworks.com
まずはこれらのサイトで購入したいメモリーを決定し、その上で自分の好みの店で買うと良いでしょう。

もっと踏み込んだ話

ここからは、更に具体的で踏み込んだ話として、特徴のある面白い製品を紹介していきます。

最速のメモリーメーカー

自分が知る限り、XMPの設定で最速のメモリーを作っているメーカーを紹介します。
もちろん、ここに紹介したメーカー以外にも、魅力的な製品を作っているメーカーは沢山あります。

Corsair

DDR4 2133, DDR4 2400のメモリーでは、後述の低周波数・超低レイテンシメモリーを提供しているメーカーです。
特にDDR4 2400C10を見た時には何かの間違いじゃないかと思いました。
デザインを意識した製品や、価格の安い製品などもあり、ファンの多いメーカーです。
個人的には特にDDR4 2800以下のメモリーで強い印象があります。

G.Skill

DDR4 3000を越える周波数のメモリーとしては、最速のメモリーを提供しているメーカーです。
前述の通り、OCに関する保証などもあり、非常に完成度の高い高周波数・低レイテンシメモリーを生産しています。
個人的なオススメは、DDR4 3200C14と、DDR4 3600C15ですね。

LED付きの製品

モリーの中には、LEDを搭載した、光るメモリーがあります。
更にその中には、温度によって点滅速度が変わったり、RGB LEDを搭載し、マザーボードなどと合わせてライティングに対応したモデルもあります。
先程も紹介した記事ですが、こちらにはLEDの有無も記載しています。
wrongwrong163377.hatenablog.com

GeIL SuperLuceシリーズ

www.ark-pc.co.jp
GeIL SuperLuceシリーズは、温度によって点滅速度が変わるメモリーです。
ただ差すだけで温度によって点滅が変わる様子を楽しむことができるのが魅力です。

RGB LEDに対応した製品

wrongwrong163377.hatenablog.com
RGB LEDに対応した製品に関しては、こちらの記事をどうぞ。
2017/7/2現在情報のあるRGB LEDに対応したメモリーを全て記載しています。
現状ではマザーボードメーカー各社で制御できるメモリーが違っていて、消費者としては面倒くさい状況になっていますが、それでもPC全体でライティング制御している様子はとてもカッコいいです。
温度情報に合わせたライティングも可能なようなので、実用面でもメリットがあります。

OC非対応マザー向け高性能メモリー

IntelのH270やB250などのマザーボードでは、最大メモリー周波数などのスペックに制限があり、高周波数なメモリーは使えない状況があります。
しかし、そのような環境向けに、低周波数ながらも超低レイテンシな高性能メモリーが提供されています。

Corsair CMD16GX4M2B2400C10

www.ark-pc.co.jp
CorsairのDominator Platinumシリーズのメモリーで、H270プラットフォームで利用できるメモリーとしては、自分の知っている限り最速の製品です。

低周波数・超低レイテンシメモリーの探し方

こういったメモリーは、マザーボードのQVLから探すと見つけやすいです。
基本的にDDR4 2133/2400の動作電圧は1.20Vですが、こういったメモリーでは1.35Vになっています。

終わりに

自分の持っている限りの知識を纏めましたが、いかがでしたでしょうか。
分かりやすさを意識して書きましたが、分かりにくい表現等あれば気軽にコメントして下さい。
記事に関係のないコメントもお待ちしています。

Androidでc++と連携してOpenCVを動かすサンプルプロジェクト解説

wrongwrong163377.hatenablog.com
github.com
こちらの記事で紹介したサンプルプロジェクトの解説記事です。
このプログラムは、以下のページを参考に作成しました。
OpenCV for Android入門 – カメラ編 « Rest Term

プログラムの概要

※このアプリではPermission関連のコードを含めていないため、アプリの実行にはAndroidの設定からカメラのパーミッションを有効にする必要が有ります

このプログラムでは、以下のことをやっています。カッコ内はMainActivity.java内での行数です。

  1. カメラから画像を取り込む(61~)
  2. 取り込んだ画像をrgba形式のMatに変換する(65)
  3. Matをc++のコードに渡し、typeをStringとして受け取り出力(70)
  4. Matをc++のコードに渡し、rgbaの内rを消去(71)
  5. 操作し終わった画像を画面に表示(73)

解説

MatTest1、MatTest2で共通しているのは、longで受け取ったNativeObjAddrからMatを操作している部分です。

Mat &input = *(Mat *) inputAddr;

後はメンバ関数を普通に呼び出しているだけですね。

cv::Mat::type

Mat::typeで帰ってくる値の読み方は以下のページを参考にしました。
ninghang.blogspot.jp
プログラム内のコメントでも少し触れた通り、幾つかの環境で試した結果、MainActivityの65行目のようにrgbaで取得した場合、帰ってくる値は24(CV8UC4)となるようです。
66行目のように、grayでも受け取ることができますが、この場合帰ってくる値は0(CV8UC1)でした。

cv::Mat::forEach

しっかりと理解して書いた訳ではないのですが、ここで利用しているforEach文は、全画素に対してラムダ式の内容を勝手に並列実行してくれるらしいです。

AndroidでOpenCV + NDKでc++と連携してOpenCVを動かす

タイトル通り、AndroidOpenCVを導入し、c++と連携してOpenCVを動かす所まで書きます。
実現する状況は以下の通りです。

最後は個人的な都合でやってます。
OpenCV4Android SDKOpenCV Managerを介して利用することもできますが、今回はポータビリティに重点を置き、プロジェクトの共有ライブラリに.apkを含める形で進めます。

完全にネット上の知識のみの独学でやっているので、間違っている点やおかしな点があるかもしれません。
特にファイルの配置に関しては色々と無駄なことをやっていると思います。
アドバイスやコメント大歓迎です。

この記事の内容と、多少のサンプルコードを加えたプロジェクトをGitHub上で公開しています。
github.com

実行環境

環境は以下の通りです。

AndroidStudio 2.3.3
OpenCV4Android SDK 3.2.0
Gradle version 3.3
NDK version b06

プロジェクトの作成

今回の記事で実行する手順です。

  • (c++のコードを導入するよう設定しプロジェクトを開始)
  • OpenCV4Android SDKの導入
  • c++OpenCVを触れるように設定

プロジェクトの開始

今回はc++との連携を行うため、プロジェクト開始時にInclude C++ supportにチェックを入れます。f:id:wrongwrongwrongwrong163377:20170701165649p:plain
ついでにc++11を有効に。
f:id:wrongwrongwrongwrong163377:20170701165847p:plain
後は適当に選んでプロジェクトを開始します。

NDKのインストー

写真がありませんが、Include C++ supportにチェックを入れてプロジェクトを開始した場合、NDKのインストールが必要になります。
これはAndroidStudioからのメッセージに従ってインストールしていけば何も問題ありません。
以下の画面までたどり着いたら完了です。
f:id:wrongwrongwrongwrong163377:20170701165850p:plain

OpenCV4Android SDKの導入

qiita.com
この項目は、こちらの記事を参考に作成しました。

ダウンロード・解凍・配置

以下のページからバージョンを選びSDKをダウンロードしてきます。
sourceforge.net
今回はopencv-3.2.0-android-sdk.zipをダウンロードしてきました。
zipファイルは解凍したあとでできるだけ動かさない場所に置くと良いでしょう。

プロジェクトへモジュールを導入

AndroidStudioで、File > new > Import Moduleを選択します。f:id:wrongwrongwrongwrong163377:20170701181452p:plain
以下の画面に、「[解凍したものを配置した場所]/OpenCV-android-sdk/sdk/java」を入力し、ライブラリの追加を行います。
f:id:wrongwrongwrongwrong163377:20170701181631p:plain
この画面の後にももうひとつ画面が出ますが、特に弄る必要はありません。
実行後には、[プロジェクトディレクトリ]/import-summary.txtが開きます。
内容(抜粋)は以下のようになります。

ECLIPSE ANDROID PROJECT IMPORT SUMMARY
======================================

Ignored Files:
--------------
The following files were *not* copied into the new Gradle project; you
should evaluate whether these are still needed in your project and if
so manually move them:

* javadoc/
* javadoc/allclasses-frame.html
* javadoc/allclasses-noframe.html
* javadoc/constant-values.html
* javadoc/help-doc.html
* javadoc/index-all.html
* javadoc/index.html
* javadoc/org/
* javadoc/org/opencv/
* javadoc/org/opencv/android/
* javadoc/org/opencv/android/BaseLoaderCallback.html
* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewFrame.html
* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewListener.html
* javadoc/org/opencv/android/CameraBridgeViewBase.CvCameraViewListener2.html
︙

インポートしたものが確認できます。

ライブラリをプロジェクトに追加

プロジェクトのapp/src/mainに、jniLibsという名前のディレクトリを作成し、[解凍したものを配置した場所]/sdk/native/libs内のファイルを全てコピーペーストします。
以下のようになります(※opencv320というディレクトリが追加されているのは一旦無視して下さい)。
f:id:wrongwrongwrongwrong163377:20170701182926p:plain

モジュールの依存関係を設定

AndroidStudioで、File > Project Structureからappを選択し、Dependenciesタブの緑の'+'マークからModule depencencyを選択します。
f:id:wrongwrongwrongwrong163377:20170701183802p:plain
f:id:wrongwrongwrongwrong163377:20170701183808p:plain
すると先ほど取り込んだモジュールが表示されるので、それを選択しOKを押します。
f:id:wrongwrongwrongwrong163377:20170701183827p:plain
以下のようになります。f:id:wrongwrongwrongwrong163377:20170701183950p:plain

gradleの設定

素の状態では、OpenCVコンパイルに関する設定のAPIレベル等が低い状態です。
例えばcamera2 APIを利用しようと思った場合、これはAPIレベル21以降の実装であるため、build.gradleの設定無しではビルドができなくなります。
これを解消するため、build.gradleの設定を変更します。

app/build.gradleと、openCVLibrary320/build.gradleを開きます(320はバージョン名)。
f:id:wrongwrongwrongwrong163377:20170701184419p:plain
編集前の内容は、それぞれ以下のように(抜粋)なっています。

app/build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.wrongwrong.opencv4androidsample"
        minSdkVersion 22
        targetSdkVersion 25
        versionCode 1

openCVLibrary320/build.gradle(編集前)

apply plugin: 'com.android.library'

android {
    compileSdkVersion 14
    buildToolsVersion "25.0.0"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 21
    }
︙

このうちopenCVLibrary320/build.gradleのcompileSdkVersion, buildToolsVersion, minSdkVersion, targetSdkVersionの4項目を、app/build.gradleに合わせて書き換えます。

openCVLibrary320/build.gradle(編集後)

apply plugin: 'com.android.library'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"

    defaultConfig {
        minSdkVersion 22
        targetSdkVersion 25
    }
︙

ここまでの設定で、JavaからOpenCVを利用することが可能になります。

c++からOpenCVを利用する

次に、Androidc++からOpenCVを利用するための設定を行います。
正直この辺りのことは全く理解できていません。
以下のリポジトリから、設定をコピペする形で設定を行いました。
github.com

([解凍したものを配置した場所]/OpenCV-android-sdk/sdkの配置)

持ち運びのためにやっていることなので、やらなくても良い手順です。
app直下にopencv320という名前のフォルダを作成し、[解凍したものを配置した場所]/OpenCV-android-sdk/sdkをコピペします。

app/build.gradleの設定

app/build.gradleに設定を追加します。

app/build.gradle(編集前)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.wrongwrong.opencv4androidsample"
        minSdkVersion 22
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
︙

追加した内容は、cmake内と、buildTypesの手前の内容です。

追加した部分1

cmake {
    cppFlags "-std=c++11"
    abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
}

追加した部分2

sourceSets {
    main {
        jniLibs.srcDirs = ['src/main/jniLibs']
    }
}

app/build.gradle(編集後)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"
    defaultConfig {
        applicationId "com.wrongwrong.opencv4androidsample"
        minSdkVersion 22
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
                abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
            }
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jniLibs']
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
︙
app/CMakeLists.txtの設定

初期状態CMakeLists.txtは以下の通りです(最初から入ってるコメントは邪魔なので消しました)。
app/CMakeLists.txt(編集前)

cmake_minimum_required(VERSION 3.4.1)

add_library( native-lib SHARED src/main/cpp/native-lib.cpp )

find_library( log-lib log )

target_link_libraries( native-lib ${log-lib} )

これを以下のように編集します

app/CMakeLists.txt(編集後)

cmake_minimum_required(VERSION 3.4.1)

# 以下3行追加
include_directories(./opencv320/sdk/native/jni/include) #置いた場所で変わる
add_library( lib_opencv SHARED IMPORTED )
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so)

add_library( native-lib SHARED src/main/cpp/native-lib.cpp )

find_library( log-lib log )

# lib_opencv追加
target_link_libraries( native-lib lib_opencv ${log-lib} )

注意点として、include_directories(./opencv320/sdk/native/jni/include)の部分では、自分がsdk/jni/includeを置いた場所を指定する必要があります。
ここまでの設定で、Androidc++からOpenCVを利用することができるようになります。

疑問

以下の点が疑問として残っていますので、追々調べていきます。

  • 例えばg++などに渡す最適化のフラグはどのようにして渡すのか(そもそも渡せるのか)
  • 無駄な手順が含まれていないか(全部やってから動くことは確認したが、途中でどう動かないか記録していない)

おまけ

掲載したサンプルプロジェクトの解説を書きました。
wrongwrong163377.hatenablog.com

c++で、if文と%(剰余)のどちらが速いかを少しだけ比較

ある一定数で折り返して用いたいような数があった場合、ぱっと思いつくのは、if文を用いて初期化するやり方と、%で剰余を取る方法でしょうか。
例えば以下のようなコードです。

//if文を用いたコード
while(i < MAX) {
    if((j+=1) == 100) j = 0;
    i++;
}

//%を用いたコード
while(i < MAX){
    j = (j+1)%100;
    i++;
}

どちらのコードでも、jが100になったら0に戻す、という処理をしています。
このif文を用いて初期化する方法と、%で剰余を取るコード、どちらが高速なのか気になったので、検証を行ってみました。

実行環境

実行環境は以下の通りです。

OS Windows10 Pro
CPU Core i7 6700
MEM DDR4 2133CL13 8GBx2
コンパイラ(コンパイルオプション) g++5.3.0-3(-Wall -O3)

※CLion上のRunから実行しました
c++11でコンパイルしました

実行したコード

プロジェクト全体はGitHubに上げました。
github.com

#include <iostream>
#include <chrono>

using namespace std;

constexpr int MAX = 1000000000;

int If(){
    int i = 0;
    int j = 0;

    chrono::system_clock::time_point start, end;
    start = chrono::system_clock::now();
    while(i < MAX) {
        if((j+=1) == 100) j = 0;
        i++;
    }
    end = chrono::system_clock::now();

    cout << "if time\t\t:" << chrono::duration_cast<chrono::milliseconds>(end-start).count() <<endl;

    return i + j;
}

int Mod(){
    int i = 0;
    int j = 0;

    chrono::system_clock::time_point start, end;
    start = chrono::system_clock::now();
    while(i < MAX){
        j = (j+1)%100;
        i++;
    }
    end = chrono::system_clock::now();

    cout << "mod time\t:" << chrono::duration_cast<chrono::milliseconds>(end-start).count() <<endl;

    return i + j;
}

int main() {
    int Ifres, Modres;
    Ifres = If();
    Modres = Mod();

    cout << "if result\t:" << Ifres << '\n'
         << "mod result\t:" << Modres << endl;
    return 0;
}

実行結果

実行結果は以下の通りです。
10回ほど実行して最速だったものを選びました。

Mod If
3088[ms] 1403[ms]

結論

検証はガバガバですが、一目でわかる通りif文方が倍以上高速ですね。
大体の平均では実行時間は半分程度でしたが、iの加算処理が等しく入っていることを考えると、倍以上速いという結論は揺らがないでしょう。
あまり差が出なかったらちゃんと検証するためのコードを書くつもりでしたが、その必要も無かったです。
実験前は、加算と保存の後で更に処理を行うということで、if文を用いた処理の方が遅いのかなと思っていましたが、最適化レベルなどを弄ってみても、やはりif文を用いた方が速かったです。
恐らくif文の方が%演算子よりも分岐予測がしっかりしているためなのでしょうね。

おまけ

javaでもやってみました。
wrongwrong163377.hatenablog.com

参考にしたサイト

時間計測はこちらのサイトを参考にさせて頂きました。
qiita.com

CMakeでコンパイルオプションを複数指定する

set(CMAKE_C_FLAGS "-Wall -O3") #cの場合
set(CMAKE_CXX_FLAGS "-Wall -O3") #c++の場合

というように、指定したいオプションをスペースで区切る。
cとc++で違いがあるようなので注意。

参考にしたページ

cmake の使い方 - PukiWiki