難問解決

※三次元復元をするにあたって射影幾何学を勉強してるんですが,なかなかしわの少ないマイブレインでは理解に時間がかかっており,ブログを書くネタがないので今回はこんなあほな感じのエントリです(笑)

2017年度年末に,京都大学の望月教授が2012年に出していたABC予想の証明が,他の数学者の査読・チェックが終わり証明が正しいという結論になった.というニュースが出ました.(査読・チェックに5年かかるというあたり素人には驚きですが.)この日本人の活躍に触発されて,自分も難問を解いてみることにしました.

ちなみに,望月教授が証明した「ABC予想」は下記です.
「互いに素(1以外に共通の約数をもたない)である3つの自然数(正の整数)の組(a、b、c)について考える。a、bの和がcである(a+b=cを満たす)とき、積abcの互いに異なる素因数の積をdとする。任意の実数κ> 1のとき、dをκ乗(累乗)した積は、まれにcよりも小さくなる。すなわち、そのような数の組(a、b、c)は有限個だけしか存在しない」

僕が証明した「牛角食べ放題のほうがお得予想」は,下記です.
「30代前半男性が空腹状態で牛角に食事に行く場合,単品注文より食べ放題を選択したほうがコストパフォーマンスが高い.」

ということで,証明しました.

まず,前提となる食べ放題のコースは下記です.
牛角コース+飲み放題=4960円

土曜日の夜19時に牛角に殴り込み,下記を食べきりました.
・カルビステーキ x 1 = 690円
f:id:rkoichi2001:20180113232353p:plain
・厚切りカルビタレ x 4 = 2200円
f:id:rkoichi2001:20180113232415p:plain
・ロースタレ x 4 = 2360円
f:id:rkoichi2001:20180113232504p:plain
・ロースステーキ x 1 = 890円
f:id:rkoichi2001:20180113232442p:plain
いぶりがっこクリームチーズ x 2 = 780円
f:id:rkoichi2001:20180113232526p:plain
・キムチ x 1 = 290円
f:id:rkoichi2001:20180113232531p:plain
・炙りベーコン x 2 = 780円
f:id:rkoichi2001:20180113232553p:plain
・黒糖おさつバター x 2 = 780円
f:id:rkoichi2001:20180113232600p:plain
チョレギサラダ x 1 = 590円
f:id:rkoichi2001:20180113232611p:plain
・ごはん中 x 1 = 260円
f:id:rkoichi2001:20180113232619p:plain
・ガトーショコラ x 1 = 250円
f:id:rkoichi2001:20180113232858p:plain
アサヒスーパードライ x 5 = 2500円
f:id:rkoichi2001:20180113232726p:plain
・赤ワイングラス x 1 = 390円
写真なし..
合計 12760円   ....証明完
(但し,合コン等目的が異なる場合はこの限りではない.)

ということで,命題「30代前半男性が空腹状態で牛角に食事に行く場合,単品注文より食べ放題を選択したほうがコストパフォーマンスが高い.」を証明しました.証明に当たって,工夫したところは,,,うーんそうですね.白米の消費量を抑えたところでしょうか.

ということで,皆様も牛角に行ったときはぜひ食べ放題を選択してください.
(望月先生,しょーもないエントリに紐づけてしまいごめんなさいm(_ _)m)

沖縄一人旅 3日目

3日目

あっというまの3泊4日でしたが,ついに最終日です.最終日の飛行機が12時前だったので,結局観光というよりは国際通りの写真を撮ることで時間いっぱい使ってしまいました.三次元復元するには,いろんな視点の写真を集めないといけないのですが,国際通りが結構長く(約1.6キロ)朝7時過ぎから取り始めて,ほぼ三時間かかってしまいました.

f:id:rkoichi2001:20180102024119j:plain

ということで,三次元復元ができたらまたアップします.

ガチンコ一人旅を終えて

久々のガチンコ一人旅だったのですが,記念館とか,いろいろ見て回る分には一人旅のほうが融通も利くしいいかなあと.ただ,ご飯&ナイトライフ(笑)は友達がいたほうがやっぱりもりあがりますね.あと,一人旅だと副産物として写真を取る量が俄然増えます(笑).でもなんだかんだで満足度は高かったので,一年に一回くらいはまたどこかに行ければと思います.また旅行した時はブログに上げるので,見てくださいませ!ということで,3日目はなんかスカスカになってしまいましたが,以上,沖縄一人旅でした.

沖縄一人旅 2日目

2日目

2日目は沖縄中部 ~ 北部を攻めました.国際通りの三次元復元のための写真を撮ろうと思っていたのですが,寝坊してしまい,,,10時から撮影するために国際通りに向かったものの,交通量が多すぎてあきらめました.国際通りの写真は最終日の朝に持ち越しです.

道の駅かでな&米軍嘉手納基地

米軍基地シリーズ第二弾です.Wikipediaによると,日本にある米軍基地のなかでも最大の規模を誇るのがこの嘉手納基地で,面積にして約東京ドーム420個分,3700mの滑走路が二本あり,成田と同じ規模らしいです.道の駅かでなはこの基地を見渡せる位置にあります.

道の駅かでな
f:id:rkoichi2001:20180102014855j:plain

道の駅かでなの名物,ジャンボチーズバーガー
(でっかいパンにはさんでるだけやん.という突っ込みはなしでお願いします.)
f:id:rkoichi2001:20180102015041j:plain

嘉手納基地
f:id:rkoichi2001:20180102015231j:plain

万座毛

この景色とっている最中に気付いたんですが,写真とるのって難しいですね.まあまあいいカメラ買ったはずなのに,どの写真もあんまイケてない感じに見えて...
なんでかな?と思ってたのですが,おそらく自分のセンスがないのと,使いこなせてない感じかと.
f:id:rkoichi2001:20180102015448j:plain

古宇利大橋

快晴でなかったのがちょっと残念ですが,景色は最高でした.ちなみに持参したガイドブックによると,”古宇利島”は恋の島とよばれ島全体がパワースポットなんだとか.7人の神様がいて,ご利益は縁結びらしいですぞ.グヘヘ( *´艸`)
f:id:rkoichi2001:20180102020137j:plain

辺戸岬

沖縄本島最北端までやってきました!沖縄は中北部から北部にかけて自然がいっぱい残っているみたいで,北部全域が国立公園(やんばる国立公園)に指定されているとのこと.
f:id:rkoichi2001:20180102020645j:plain

f:id:rkoichi2001:20180102020612j:plain

焼肉88

最後の晩餐は焼肉です!沖縄らしいものにしようか悩みましたが,肉が食べたく!
ただ,沖縄はステーキ,焼肉,鉄板焼きとか,アメリカ文化が残っているせいか肉料理のお店が至る所にあります.
このお店も食べ放題3500円の割にはとても肉の質が高く,おいしかったです.3500円だとこっちの安々くらいの値段ですが,根本的に仕入れ値が安いんですかね?

(食べ放題のお肉には見えなくないですか??)
f:id:rkoichi2001:20180102021043j:plain

2日目の成果

f:id:rkoichi2001:20180102014042p:plain

沖縄一人旅 1日目

1日目

1日目は沖縄南部を攻めました.始めはバス・モノレールで行ける範囲でいろいろと見ようと思っていたのですが,平和祈念公園ひめゆりの塔に行くにはレンタカーを借りる必要があり,急遽借りることにしました.今回の旅で”歴史の勉強”もしたいと思っていたのですが,全日程を通してひめゆりの塔が一番良かった場所だったと思います.(良かったという表現を使っていいのかどうか微妙ですが.)戦争の悲惨さはもちろんですが,日米両軍の思惑,実際の地上戦の様子,3カ月間にわたる攻防が客観的な情報として示されていて,とても勉強になりました.残念なことに平和祈念公園の資料館は既に年末休みに入っていて見学ができなかったのですが,これはまた別の機会に行ければと思います.

首里城記念公園

首里城全体のレベルで三次元復元を企んでいたのですが,城壁が高くてまんべんなく写真をいろんな視点から取ることができずあきらめました.一番立派なとこ(下記写真)の周囲だけで三次元復元をしてみます.
f:id:rkoichi2001:20180102000233j:plain

平和通り商店街&花笠食堂

昨日のゆうなんぎいより少し沖縄度が増す定食屋さんでした.”ゴーヤチャンプル定食”もあったのですが,ここでヒヨってはいけないと思い”てびち定食”を注文.う~ん.やっぱりまだまだ沖縄初心者ですね(笑)てびちの甘い感じがちょっと自分には慣れない感じでした.
国際通りー>平和通り商店街を進んでいくと,花笠食堂があります.
f:id:rkoichi2001:20180102000302j:plain

花笠食堂
f:id:rkoichi2001:20180102000333j:plain
f:id:rkoichi2001:20180102000355j:plain

てびち定食
f:id:rkoichi2001:20180102011516j:plain

ひめゆりの塔

恥ずかしいことに,歴史をちゃんと勉強してこなかったので太平洋戦争のこともあまり知らずに行ったのですが,とても分かりやすく説明がなされていました.真珠湾攻撃で開戦し,ミッドウェーでの敗戦から徐々に負けペースになって,硫黄島が陥落し,沖縄地上戦が始まり...という感じで,3カ月の地上戦に突入していくわけですが,想像を絶する状態だったことは資料からひしひしと伝わってきました.ちょうど憲法改正が議論に上がりつつある今,歴史をきちんと勉強しておくことは大事なのかなと思います.ということで,下記をちょっと読んでみようと思います.

失敗の本質―日本軍の組織論的研究 (中公文庫)

失敗の本質―日本軍の組織論的研究 (中公文庫)

平和祈念公園

平和祈念公園は資料館がお休みだったので,施設を見て回るくらいしかできませんでした.
f:id:rkoichi2001:20180102000717j:plain

嘉数高台公園

こちらの公園は,沖縄地上戦の激戦地の一つなのですが,米軍の普天間基地が一望できる展望台があります.
展望台の写真
f:id:rkoichi2001:20180102000750j:plain

普天間基地(右に隊列を組んで駐機しているのがオスプレイです.)
f:id:rkoichi2001:20180102000820j:plain

三丁目の島そば屋

嘉数高台公園のすぐそばにある沖縄そば屋さんです.食べログでの評価も高く食べたかったんですが,売り切れ次第営業終了ということで,16時の段階ですでに終了してました...
f:id:rkoichi2001:20180102000848j:plain

A&Wバーガー

そばが食えなかったので代わりのA&Wバーガーです.中のチーズが普通のハンバーガーみたいに薄くとろけている感じでなく,これが絶妙です.
f:id:rkoichi2001:20180102000909j:plain

牧志公設市場

市場です.もうちょっと沖縄通になれば,市場で買ったものを二階の食堂にもっていって調理してもらうことも可能みたいなんですが,そこまでの勇気はなく(笑)
f:id:rkoichi2001:20180102000928j:plain

ジャッキーステーキハウス

食いすぎかな...と思いましたが,我慢できずに23時にステーキ食いに行きました.
写真,23時の様子ですが,30分くらい待ちました.
f:id:rkoichi2001:20180102000955j:plain
f:id:rkoichi2001:20180102001040j:plain

1日目の成果

f:id:rkoichi2001:20180102002959p:plain

沖縄一人旅 0日目

下記の投稿の最後のほうで,来年のつくばチャレンジまでの空いた時間で 「”沖縄三次元復元プロジェクト” を実施します!」と宣言したと思うのですが,宣言通り行ってきました.
とは言っても,宣言通り三次元復元できたわけでなく,旅行に行ってたんまり写真を取ってきた段階ですが(笑)国際通り首里城の写真はいっぱい取ってきたので,これから二月末までかけて勉強&三次元復元します.

daily-tech.hatenablog.com

で,せっかく沖縄に行ったのでいろいろと「自分の知らない沖縄」を見てこようと,いろんなとこに行ってきました.本当はもっといろいろと勉強して,ローカルの人しか知らないとこにも行ければよかったのですが,勉強する時間も滞在時間も十分でなく有名処に行くのみになってしまいましたが...

下記,今回使った"沖縄の教科書"です

おとな旅プレミアム 沖縄 (おとな旅PREMIUM)

おとな旅プレミアム 沖縄 (おとな旅PREMIUM)

沖縄 オトナの社会見学 R18

沖縄 オトナの社会見学 R18

0日目

初日は現地に到着したのが18時を過ぎていたので,夕食で終わってしまいましたが,”ゆうなんぎい”という定食屋に行ってきました.結構有名どころのようで,平日(28)の夕方だったにも関わらず30分以上待ちました.が,評判通りおいしかったです.初心者は”ゆうなんぎい定食A”を頼んでおけば一通りまんべんなく沖縄料理が食べられるとのことで,頼みました.チャンプルーとか,沖縄初心者でもおいしく食べられるメニューが中心でした.

那覇空港

めんそ~れ!一年ぶりに帰ってきました.
f:id:rkoichi2001:20180101232314j:plain

お店の写真

きらきら国際通りのすぐそばにある割には,割と地味な風貌です.
f:id:rkoichi2001:20180101232054j:plain

ゆうなんぎい定食A

すみません...全部一気に出てくるわけもなく,かといってお店も混んでて箸をつけないわけにもいかず,食いかけです(笑)
f:id:rkoichi2001:20180101232123j:plain

ホテル Kariyushi LCH. Izumizaki 県庁前

一泊4000円前後と結構安く,国際通りまで歩いて5分かかりません.が,お部屋が狭いです(笑)
f:id:rkoichi2001:20180101232621j:plain

0日目の成果

f:id:rkoichi2001:20180101233834p:plain

Open MVG & Open MVS 2 三次元復元!!

ということで,このエントリでは自前の一眼レフでとった仙台駅の写真を三次元復元するとこまで行きます.
前回のエントリまでできていることを前提にしてます.

daily-tech.hatenablog.com

で,OpenMVG と OpenMVSですが,割と処理がたくさんあるので実行ファイルがいっぱいできます.これを一つ一つ正しい引数を渡して実行していけばいいのですが,なかなか骨が折れます.ということで,サンプル実行してくれるスクリプトが落ちてます.

github.com

上記 git の MvgMvs_Pipeline.py というスクリプトファイルを実行すれば,サンプル実行が可能になります.
なのですが,ちょっとゴチャっとしてしまっているので,簡単化した Python スクリプトを作りました.よければご利用下さい.

#!/usr/bin/python
#! -*- encoding: utf-8 -*-

import os
import subprocess
import sys

# Indicate the openMVG binary directory
OPENMVG_BIN_DIR = "~/workspace/reps/openmvg/install/bin"
OPENMVS_BIN_DIR = "~/workspace/reps/openmvs/install/bin/OpenMVS"
OPENCV_SO_DIR = "~/workspace/reps/opencv-3.1.0/install/lib"

def SetEnvVars():
  if 'LD_LIBRARY_PATH' not in os.environ:
    LD_LIBRARY_PATH_ORG = os.environ['LD_LIBRARY_PATH']
    os.environ['LD_LIBRARY_PATH'] = OPENCV_SO_DIR
    try:
      os.execv(sys.argv[0], sys.argv)
    except Exception, exc:
      print 'Failed re-exec:', exc
      sys.exit(1)

  print 'Success:', os.environ['LD_LIBRARY_PATH']

class SfMPipeLine(object):

  def __init__(self, datadir, camera_file_path):
    self.__datadir = datadir
    self.__image_dir = os.path.join(self.__datadir, "images")
    self.__output_dir = os.path.join(self.__datadir, "result")
    self.__matches_dir = os.path.join(self.__output_dir, "matches")
    self.__reconstruction_dir = os.path.join(self.__output_dir,"reconstruction_global")
    self.__mvs_dir = os.path.join(self.__output_dir,"mvs_dir")
    self.__camera_file_params = camera_file_path
    self.__create_folder_structure(self.__output_dir)

  def start_sfm_pipeline(self):
    self.__intrinsics_analysis(self.__camera_file_params)
    self.__compute_features()
    self.__compute_matches()
    self.__reconstruct_incremental()
    self.__colorize_structure()
    self.__triangulate_robust()
    self.__convert_openmvg2openmvs()
    self.__densify_pointcloud()
    self.__reconstruct_mesh()
    self.__refine_mesh()
    self.__texture_mesh()


  def __create_folder_structure(self, output_dir):
    print("Create Folder Structure")
    
    if not os.path.exists(output_dir):
      os.mkdir(output_dir)  

    if not os.path.exists(self.__matches_dir):
      os.mkdir(self.__matches_dir)

    if not os.path.exists(self.__reconstruction_dir):
      os.mkdir(self.__reconstruction_dir)

    if not os.path.exists(self.__mvs_dir):
      os.mkdir(self.__mvs_dir)


  def __intrinsics_analysis(self, camera_file_params):
    print("Intrinsics Analysis")
    exepath = os.path.join(OPENMVG_BIN_DIR, "openMVG_main_SfMInit_ImageListing")
    pIntrisics = \
            subprocess.Popen( [exepath, "-i", self.__image_dir, "-o", self.__matches_dir, \
                              "-d", camera_file_params, "-c", "3"] )
    pIntrisics.wait()

  def __compute_features(self):
    print("Compute Features")
    exepath = os.path.join(OPENMVG_BIN_DIR, "openMVG_main_ComputeFeatures")
    jsonpath = self.__matches_dir+"/sfm_data.json"
    pFeatures = \
            subprocess.Popen( [exepath, "-i", jsonpath, "-o", self.__matches_dir, \
                              "-m", "SIFT", "-f" , "1"] )
    pFeatures.wait()


  def __compute_matches(self):
    print("Compute Matches for Incremental SfM PipeLine")
    exepath = os.path.join(OPENMVG_BIN_DIR, "openMVG_main_ComputeMatches")
    jsonpath = self.__matches_dir+"/sfm_data.json"
    pMatches = \
            subprocess.Popen( [exepath, "-i", jsonpath, "-o", self.__matches_dir, \
                               "-f", "1", "-n", "ANNL2"] )
    pMatches.wait()

  def __reconstruct_incremental(self):
    print("Incremental Reconstruction")
    exepath = os.path.join(OPENMVG_BIN_DIR, "openMVG_main_IncrementalSfM")
    jsonpath = self.__matches_dir+"/sfm_data.json"
    pRecons = subprocess.Popen( [exepath, "-i", jsonpath, "-m", self.__matches_dir, \
                                "-o", self.__reconstruction_dir] )
    pRecons.wait()


  def __colorize_structure(self):
    print("Colorize Structure")
    exepath = os.path.join(OPENMVG_BIN_DIR, "openMVG_main_ComputeSfM_DataColor")
    plypath = os.path.join(self.__reconstruction_dir,"colorized.ply")
    binpath = self.__reconstruction_dir+"/sfm_data.bin"
    pRecons = subprocess.Popen( [exepath, "-i", binpath, "-o", plypath] )
    pRecons.wait()

  def __triangulate_robust(self):
    print("Robust Triangulation")
    exepath = os.path.join(OPENMVG_BIN_DIR, "openMVG_main_ComputeStructureFromKnownPoses")
    plypath = os.path.join(self.__reconstruction_dir,"robust.ply")
    binpath = self.__reconstruction_dir+"/sfm_data.bin"
    pRecons = \
          subprocess.Popen( [exepath, "-i", binpath, "-m", self.__matches_dir, "-o", plypath] )
    pRecons.wait()

  def __convert_openmvg2openmvs(self):
    print("OpenMVG 2 OpenMVS")
    exepath = os.path.join(OPENMVG_BIN_DIR, "openMVG_main_openMVG2openMVS")
    binpath = self.__reconstruction_dir+"/sfm_data.bin"
    mvspath = self.__mvs_dir+"/scene.mvs"
    pRecons = \
          subprocess.Popen( [exepath, "-i", binpath, "-o", mvspath, "-d", self.__mvs_dir] )
    pRecons.wait()

  def __densify_pointcloud(self):
    print("Densify Point Cloud")
    exepath = os.path.join(OPENMVS_BIN_DIR, "DensifyPointCloud")
    mvspath = self.__mvs_dir + "/scene.mvs"
    pRecons = \
          subprocess.Popen( [exepath, mvspath, "-w", self.__mvs_dir] )
    pRecons.wait()


  def __reconstruct_mesh(self):
    print("Reconstruct Mesh")
    exepath = os.path.join(OPENMVS_BIN_DIR, "ReconstructMesh")
    mvspath = self.__mvs_dir + "/scene_dense.mvs"
    pRecons = \
          subprocess.Popen( [exepath, mvspath, "-w", self.__mvs_dir] )
    pRecons.wait()

  def __refine_mesh(self):
    print("Refine Mesh")
    exepath = os.path.join(OPENMVS_BIN_DIR, "RefineMesh")
    mvspath = self.__mvs_dir + "/scene_dense_mesh.mvs"
    pRefin = \
          subprocess.Popen( [exepath, \
                            mvspath, "-w", self.__mvs_dir] )
    pRefin.wait()

  def __texture_mesh(self):
    print("Texture Mesh")
    exepath = os.path.join(OPENMVS_BIN_DIR, "TextureMesh")
    mvspath = self.__mvs_dir + "/scene_dense_mesh_refine.mvs"
    pRecons = \
          subprocess.Popen( [exepath, \
                            mvspath, "-w", self.__mvs_dir] )
    pRecons.wait()

if __name__=='__main__':

  SetEnvVars()

  camera_file_params = \
        "~/workspace/reps/openmvg/openMVG/src/openMVG/exif/" + \
        "sensor_width_database/sensor_width_camera_database.txt"
  data_dir = "~/Desktop/SFM/SFM_Sample/ImageDataset_SceauxCastle"
  #data_dir = "~/Desktop/SFM/SFM_Sample/Sendai_Station"
  #data_dir = "~/Desktop/SFM/SFM_Sample/Mansion"
  #data_dir = "~/Desktop/SFM/SFM_Sample/ET"

  print("SfM Pipeline Started")
  pl = SfMPipeLine(data_dir, camera_file_params)
  pl.start_sfm_pipeline()

スクリプトの最初にある下記の3つの変数は,それぞれ OpenMVG, OpenMVSの実行ファイルが存在する箇所.OpenCVの共有ライブラリが存在する箇所です.それぞれの環境に合わせて設定してください.
OPENMVG_BIN_DIR =
OPENMVS_BIN_DIR =
OPENCV_SO_DIR =

あと,ディレクトリの構成としては,mainでいくつか例を載せていますが,下記のような構成で回せばそのまま回るかと.imagesの配下に撮影した写真を入れてください.

XXXX_Sample
|
|-images

最後に,カメラのイメージャ情報を渡す必要があります.カメラの画像素子名を画像データのプロパティからアプリが拾ってきてくれるので,その画像素子名に対応するサイズを下記のファイルに記述する必要があります.

~/workspace/reps/openmvg/openMVG/src/openMVG/exif/sensor_width_database/sensor_width_camera_database.txt"

自分の場合はSonyのAlpha2で,撮影した写真のプロパティ情報に"ILCE-7M2"という画像素子名が表示されていたので,上記のテキストファイルに下記の一行を追記しました.

ILCE-7M2;35.8


で,SI2017参加時に取得した仙台駅の写真5枚程度を用いて三次元復元してみました.完璧とは言わないまでも,そこそこうまく出来たかなと思います.
ただ,写真の数が増えてくると,Cuda Memory Errorとかが出てきてうまく実行できないのでこの辺は中身を覗いて修正するなり改変するなりしないとつらそうです.

f:id:rkoichi2001:20180101133358p:plain

ということで,新年一発目の投稿でした!!

Open MVG & Open MVS 1 ビルドを通すまで...

あけましておめでとうございます!今年もよろしくお願いします.
2018年の抱負から始めるべきだったのですが,昨年やり残してたアイテムの備忘録も兼ねて,新年一発目の投稿はSFMで行きます.

で,以前の投稿

daily-tech.hatenablog.com

では,Mastering OpenCVのサンプルコードを使ってSFMを実施しようとしたのですが,これだとアルゴリズムの実装がストレートすぎて,自前のカメラで撮影した写真の対応は難しそうです...何回やっても計算がいい感じに収束しませんでした.ただ,シンプルな作りになっているので,アルゴリズムの基本的な流れの理解にはとても役立つと思います.

で,自前のカメラで実用に耐えられるSFMのコードを探しました.いくつか候補はあったのですが,コードがメンテされているとのことで,下記のサイトを参考にして OpenMVG を題材に今後の作業を進めていくことにしました.

画像から3次元形状を復元!OpenMVGでSfMを試してみた - 株式会社CFlatの明後日スタイルのブログ

0. 目論見

今年のつくばチャレンジでは,SFM (Structure From Motion) とMVS (Multi View Stereo) の技術を自律走行に生かせないかなあと思っています.具体的には,
1. つくばチャレンジのコースを一眼レフカメラでいっぱい撮影して三次元マップをオフライン生成.
2. オフライン生成したマップ情報を元に,ロボットのカメラ(低解像度)で撮影した画像を合わせこみ.オンラインで自己位置推定.
という感じで,攻めたいと思っています.が,いまやっていることより難易度が上がるので,頭がついて行けばいいのですが,,,

1. コードのダウンロード

落としてきたコードは下記のものです.どちらのコードも整理されていますが,結構大きい&難しいことをやっているので,中身を理解するまでには時間が掛かりそうですが,,

OpenMVG

github.com

OpenMVS

github.com

Eigen-3.2.10

http://bitbucket.org/eigen/eigen/get/3.2.10.zip
※Eigenのバージョンは3.2.xでないとダメです.3.3.xだと,OpenMVSのメッシュ化のところでこけます.

VCGLib

github.com

Ceres

github.com

2. コードのビルド

コードのビルドは下記の要領でできました.
(フォルダはホームフォルダから始める前提で書いてます.)

2-0 必要モジュールのインストール

(ちなみに,自分はROS Kineticを入れているので,ROSを入れてない人は他にもいくらかインストールしないと行けないかもしれません.)

cd
sudo apt-get install libpng-dev libjpeg-dev
sudo apt-get install libcgal-dev libcgal-qt5-dev
sudo apt-get install meshlab
sudo apt-get -y install freeglut3-dev libglew-dev libglfw3-dev

2-1. OpenMVG のビルド

OpenMVGは割とストレートにビルド完了まで行きました.

cd
mkdir ~/workspace/reps/openmvg ~/workspace/reps/openmvg/build ~/workspace/reps/openmvg/install
git clone https://github.com/openMVG/openMVG ~/workspace/reps/openmvg/dist
cd ~/workspace/reps/openmvg/dist
git submodule update -i
cd ~/workspace/reps/openmvg/build
cmake -DCMAKE_INSTALL_PREFIX=~/workspace/reps/openmvg/install ../dist/src/
make install -j8

3. OpenMVS のビルド

3-1 Eigen のビルド
cd
hg clone https://bitbucket.org/eigen/eigen#3.2.10 workspace/reps/eigen-3.2.10/dist
mkdir workspace/reps/eigen-3.2.10/build workspace/reps/eigen-3.2.10/install
cd workspace/reps/eigen-3.2.10/build
cmake -DCMAKE_INSTALL_PREFIX=~/workspace/reps/eigen-3.2.10/install/ ../dist/
make install

ここからはちょっと正しいかどうか怪しいのですが,Eigenの3.2.x系にEigen3Config.cmakeファイルがなかったので,Eigenの3.3.4をmake install した時に生成されるshare/eigen3のフォルダをコピーしてeigen-3.2.10/install/shareに持ってきました.つまり,,,

cd
cp -r ~/workspace/reps/eigen-3.3.4/install/share/ ~/workspace/reps/eigen-3.2.10/install/

で,このままだとcmakeに認識されるバージョン情報が違ってしまうので,下記の変更を実施しました.

Eigen3Config.cmake

Before

set (EIGEN3_FOUND 1)
set (EIGEN3_USE_FILE    "${CMAKE_CURRENT_LIST_DIR}/UseEigen3.cmake")

set (EIGEN3_DEFINITIONS  "")
set (EIGEN3_INCLUDE_DIR  "${PACKAGE_PREFIX_DIR}/include/eigen3")
set (EIGEN3_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/include/eigen3")
set (EIGEN3_ROOT_DIR     "${PACKAGE_PREFIX_DIR}")

set (EIGEN3_VERSION_STRING "3.3.4")
set (EIGEN3_VERSION_MAJOR  "3")
set (EIGEN3_VERSION_MINOR  "3")
set (EIGEN3_VERSION_PATCH  "4")

After

set (EIGEN3_FOUND 1)
set (EIGEN3_USE_FILE    "${CMAKE_CURRENT_LIST_DIR}/UseEigen3.cmake")

set (EIGEN3_DEFINITIONS  "")
set (EIGEN3_INCLUDE_DIR  "${PACKAGE_PREFIX_DIR}/include/eigen3")
set (EIGEN3_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/include/eigen3")
set (EIGEN3_ROOT_DIR     "${PACKAGE_PREFIX_DIR}")

set (EIGEN3_VERSION_STRING "3.2.10")
set (EIGEN3_VERSION "3.2.10")
set (EIGEN3_VERSION_MAJOR  "3")
set (EIGEN3_VERSION_MINOR  "2")
set (EIGEN3_VERSION_PATCH  "10")

set (EIGEN_FOUND ${EIGEN3_FOUND})
set (EIGEN_USE_FILE    ${EIGEN3_USE_FILE})

set (EIGEN_DEFINITIONS  ${EIGEN3_DEFINITIONS})
set (EIGEN_INCLUDE_DIR  ${EIGEN3_INCLUDE_DIR})
set (EIGEN_INCLUDE_DIRS ${EIGEN3_INCLUDE_DIRS})
set (EIGEN_ROOT_DIR     ${EIGEN3_ROOT_DIR})

set (EIGEN_VERSION_STRING ${EIGEN3_VERSION_STRING})
set (EIGEN_VERSION ${EIGEN3_VERSION})
set (EIGEN_VERSION_MAJOR  ${EIGEN3_VERSION_MAJOR})
set (EIGEN_VERSION_MINOR  ${EIGEN3_VERSION_MINOR})
set (EIGEN_VERSION_PATCH  ${EIGEN3_VERSION_PATCH})
Eigen3ConfigVersion.cmake

Before

set(PACKAGE_VERSION "3.3.4")

if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
  set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()

  if("3.3.4" MATCHES "^([0-9]+)\\.")
    set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
  else()
    set(CVF_VERSION_MAJOR "3.3.4")
  endif()

After

set(PACKAGE_VERSION "3.2.10")

if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
  set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()

  if("3.2.10" MATCHES "^([0-9]+)\\.")
    set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
  else()
    set(CVF_VERSION_MAJOR "3.2.10")
  endif()
3-2 VCG のビルド

こいつはCMake&ビルド不要です.

cd
git clone https://github.com/cnr-isti-vclab/vcglib.git ~/workspace/reps/vcg
3-3 Ceres Solver のビルド
cd
git clone https://github.com/ceres-solver/ceres-solver.git ~/workspace/reps/ceres/dist
mkdir ~/workspace/reps/ceres/build ~/workspace/reps/ceres/install
cd ~/workspace/reps/ceres/build

で,せっかくEigenをインストールしたので,Ceresが参照するEigenのバージョンも下記のように変更してあげます.
変更する箇所は,ceres/dist/CMakeLists.txt以下の下記の部分です.
Before

find_package(Eigen REQUIRED)

After

set(Eigen3_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../eigen-3.2.10/install/share/eigen3/cmake")
find_package(Eigen3 REQUIRED CONFIG PATHS ${Eigen3_DIR} NO_DEFAULT_PATH)

そしてビルドします.

cmake -DCMAKE_INSTALL_PREFIX=~/workspace/reps/ceres/install/ -DMINIGLOG=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=ON ../dist/
make install -j8
3-4 OpenMVSのビルド

ここまでくれば,ようやくOpenMVSのビルドです.

cd
git clone https://github.com/cdcseacave/openMVS ~/workspace/reps/openmvs/dist
mkdir ~/workspace/reps/openmvs/build ~/workspace/reps/openmvs/install

で,同じくCMakeLists.txtを変更してやる必要があります.
一つ目のCMakeLists.txtは~/workspace/reps/openmvs/dist/CMakeLists.txtです.変更内容はCeresの時と同じで,自分のローカルでビルドしたEigenとOpenCVを使うように設定します.

openmvs/dist/CMakeLists.txt

Before

FIND_PACKAGE(Eigen ${SYSTEM_PACKAGE_REQUIRED})
if(EIGEN_FOUND)
       INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIRS})
        ADD_DEFINITIONS(${EIGEN_DEFINITIONS} -D_USE_EIGEN)
        SET(_USE_EIGEN TRUE)
 endif()
 
FIND_PACKAGE(OpenCV ${SYSTEM_PACKAGE_REQUIRED})

After

SET(Eigen3_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../eigen-3.2.10/install/share/eigen3/cmake")
FIND_PACKAGE(Eigen3 ${SYSTEM_PACKAGE_REQUIRED} CONFIG PATHS Eigen3_DIR NO_DEFAULT_PATH)
if(EIGEN3_FOUND)
       INCLUDE_DIRECTORIES(${EIGEN3_INCLUDE_DIRS})
        ADD_DEFINITIONS(${EIGEN_DEFINITIONS} -D_USE_EIGEN)
        SET(_USE_EIGEN TRUE)
 endif()
 
SET(OpenCV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../opencv-3.1.0/install/share/OpenCV")
FIND_PACKAGE(OpenCV ${SYSTEM_PACKAGE_REQUIRED} CONFIG PATHS ${OpenCV_DIR} NO_DEFAULT_PATH)

二つ目ののCMakeLists.txtは~/workspace/reps/openmvs/dist/libs/MVS/CMakeLists.txtです.gitからダウンロードした VCG と Ceres のパス設定をします.

dist/libs/MVS/CMakeLists.txt

VCG,Ceresのパス設定.
Before

FIND_PACKAGE(VCG ${SYSTEM_PACKAGE_REQUIRED})
if(VCG_FOUND)
       include_directories(${VCG_INCLUDE_DIRS})
       add_definitions(${VCG_DEFINITIONS})
endif()
 
FIND_PACKAGE(CERES ${SYSTEM_PACKAGE_REQUIRED})
 if(CERES_FOUND)
        include_directories(${CERES_INCLUDE_DIRS})
        add_definitions(${CERES_DEFINITIONS})

After

SET(VCG_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../../vcg/")
include_directories(${VCG_INCLUDE_DIRS})
add_definitions(${VCG_DEFINITIONS})
 
SET(CERES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../ceres/install/lib/cmake/Ceres")
FIND_PACKAGE(Ceres ${SYSTEM_PACKAGE_REQUIRED} CONFIG PATHS ${CERES_DIR} NO_DEFAULT_PATH)
 if(CERES_FOUND)
        include_directories(${CERES_INCLUDE_DIRS})
        add_definitions(${CERES_DEFINITIONS})

あと,Ceres-Solverのリンクディレクトリ周りも少し修正が必要でした.
Before

TARGET_LINK_LIBRARIES(MVS Common Math IO ${CERES_LIBS} ${CGAL_LIBS} ${CUDA_CUDA_LIBRARY})

After

LINK_DIRECTORIES("${CERES_DIR}/../../")
TARGET_LINK_LIBRARIES(MVS Common Math IO ceres ${CGAL_LIBS} ${CUDA_CUDA_LIBRARY})

で,ビルドが通ります.

cd
cd ~/workspace/reps/openmvs/build
cmake -DCMAKE_INSTALL_PREFIX=~/workspace/reps/openmvs/install ../dist/
make install -j8

ふーーーー.記事にまとめようとすると,時間かかりますね...次は実際に自分の一眼レフでとった写真を復元します.