ARM NEONの使い方 ロード・ストア編
ロード
vld1[q]_<type>(ptr)はptrから64bit(qが付く場合は128bit)のベクタをロードします
サンプル
符号付き16bit整数のベクタをロードしてみます
ロードしたベクタの各レーンをvget_lane_s16()で取得し、表示してみます
#include <stdio.h> #include <stdint.h> #include <arm_neon.h> int main() { int16_t a[4] = { 0, 1, 2, 3 }; int16x4_t va = vld1_s16(a); printf("lane[0]: %d\n", vget_lane_s16(va, 0)); printf("lane[1]: %d\n", vget_lane_s16(va, 1)); printf("lane[2]: %d\n", vget_lane_s16(va, 2)); printf("lane[3]: %d\n", vget_lane_s16(va, 3)); return 0; }
実行結果
lane[0]: 0 lane[1]: 1 lane[2]: 2 lane[3]: 3
ストア
vst1[q]_<type>(ptr, val)は64bit(qが付く場合は128bit)のベクタvalをptrにストアします
サンプル
vld1_s16()でロードしたベクタをもう一度ストアして表示してみます
#include <stdio.h> #include <stdint.h> #include <arm_neon.h> int main() { int16_t a[4] = { 0, 1, 2, 3 }; int16x4_t va = vld1_s16(a); int16_t b[4]; vst1_s16(b, va); for (int i = 0; i < 4; i++) printf("b[%d]: %d\n", i, b[i]); return 0; }
実行結果
b[0]: 0 b[1]: 1 b[2]: 2 b[3]: 3
逆インターリーブロード
先ほどはvld1を紹介しました
実はこの他にvld2、vld3、vld4というものもあります
どんな処理になるのか、サンプルを見てみましょう
サンプル
vld2_s16()を使って、符号付き16bit整数のベクタをロードしてみます
vld2_s16()の戻り値はint16x4x2_tになります
これはデータ型編でも紹介した通り、int16x4_t を2つ要素に持つ型です
各要素はvalというメンバに格納されています
val[0]とval[1]それぞれのレーンを表示してみます
#include <stdio.h> #include <stdint.h> #include <arm_neon.h> int main() { int16_t a[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; int16x4x2_t va = vld2_s16(a); printf("val[0]:\n"); printf("%d\n", vget_lane_s16(va.val[0], 0)); printf("%d\n", vget_lane_s16(va.val[0], 1)); printf("%d\n", vget_lane_s16(va.val[0], 2)); printf("%d\n", vget_lane_s16(va.val[0], 3)); printf("val[1]:\n"); printf("%d\n", vget_lane_s16(va.val[1], 0)); printf("%d\n", vget_lane_s16(va.val[1], 1)); printf("%d\n", vget_lane_s16(va.val[1], 2)); printf("%d\n", vget_lane_s16(va.val[1], 3)); return 0; }
実行結果
val[0]: 0 2 4 6 val[1]: 1 3 5 7
val[0]にはa[8]の偶数番目の要素が、val[1]にはa[8]の奇数番目の要素がロードされました
このサンプルの偶数と奇数のように、交互に配置されたものを分離することから
vld2(およびvld3、vld4)を逆インターリーブ(deinterleave)とも呼びます
(インターリーブには「交互配置する」という意味合いがあります)
逆インターリーブの実用例としては、以下のものがあります
- XYの2次元座標の配列から、それぞれのチャンネル(X,Y)を分離する
- RGBの画素値の配列から、それぞれのチャンネル(R,G,B)を分離する
インターリーブストア
こちらはデータをインターリーブ(交互配置)してストアする処理になります
サンプル
偶数の配列と奇数の配列をそれぞれint16x4x2_tのval[0]とval[1]にロードして
vst2_s16()でインターリーブしてみます
#include <stdio.h> #include <stdint.h> #include <arm_neon.h> int main() { int16_t a[4] = { 0, 2, 4, 6 }; //even int16_t b[4] = { 1, 3, 5, 7 }; //odd int16x4x2_t vc; vc.val[0] = vld1_s16(a); vc.val[1] = vld1_s16(b); int16_t c[8]; vst2_s16(c, vc); for (int i = 0; i < 8; i++) printf("c[%d]: %d\n", i, c[i]); return 0; }
実行結果
c[0]: 0 c[1]: 1 c[2]: 2 c[3]: 3 c[4]: 4 c[5]: 5 c[6]: 6 c[7]: 7
偶数と奇数を交互に配置することができました
参考
その他、インターリーブの解説がある記事・資料を載せておきます
- Coding for NEON - Part 1: Load and Stores
- NEONを使用してZynq-7000 AP SoCでのソフトウェア性能を向上
- ARM NEON SIMD
- (注)クリックするとpdfの資料がダウンロードされます
- インターリブを使用してBGRをRGBに変換する例が紹介されています
次回
次回は加算編です
ARM NEONの使い方 加算編
CUDA 8がVisual Studio 2015 Update 3に対応した
今年の6月頃からCUDA 8.0 RC(Release Candidate)は出てたのですが
このたび正式なCUDA 8.0がリリースされたようです
CUDA Toolkit | NVIDIA Developer
Parallel Forall Blog - Nvidiaでも紹介されていて
個人的に嬉しかったのがVisual Studio 2015 Update 2以降に対応したこと(RCの時は未対応だった)
Expanded developer platform support including Microsoft Visual Studio 2015 (updates 2 and 3) and GCC 5.4 (Ubuntu 16.04).
これで最新のVisual StudioでCUDA8を試すことができますね!
Pascal GPUが欲しくなるなあ
ARM NEONの使い方 組み込み関数一覧編
組み込み関数一覧
こちらのページにNEONの組み込み関数一覧が載ってます
慣れてきたらここから必要な演算を探すと良いでしょう
infocenter.arm.com
命名規則
NEONの組み込み関数は大抵のものが以下の規則で名前が付けられています
<opname>[q]_<type>
<opname>はvaddやvmul等、演算の内容を表します(必ず頭に'v'が付きます)
[q]はオプションフラグで、qが付く場合は128bit型に対する演算を表します
<type>はオペランドの型を表します
例えば、 vadd_s16 は int16x4_t(64bit型)同士の加算を、
vaddq_s16 は int16x8_t(128bit型)同士の加算を表します
次回
次回からは個別の演算に対して解説していきます
ARM NEONの使い方 ロード・ストア編
ARM NEONの使い方 データ型編
NEONデータ型の概要
NEONのデータ型は以下の規則で名前が付けられています
<type><size>x<number of lanes>_t
例えば、 int16x4_t は符号付き16bit整数を4個保持する型となります
それぞれの要素を「レーン」と呼びます
NEONのデータ型は64bitのものと128bitのものがあります
NEONデータ型一覧
NEONのデータ型一覧を示します
64bit型 | 128bit型 |
---|---|
int8x8_t | int8x16_t |
int16x4_t | int16x8_t |
int32x2_t | int32x4_t |
int64x1_t | int64x2_t |
uint8x8_t | uint8x16_t |
uint16x4_t | uint16x8_t |
uint32x2_t | uint32x4_t |
uint64x1_t | uint64x2_t |
float16x4_t | float16x8_t |
float32x2_t | float32x4_t |
poly8x8_t | poly8x16_t |
poly16x4_t | poly16x8_t |
以上をさらに配列にした型もあり、以下の規則で名前が付けられています
<type><size>x<number of lanes>x<length of array>_t
例えば、 int16x4x2_t は int16x4_t を2つ要素に持つ型です
各要素はvalというメンバに格納されています
struct int16x4x2_t { int16x4_t val[2]; };
これらの配列型は一部のNEON命令で使用されます
使いどころについては今後の記事で解説したいと思います
polyって何?
データ型一覧の中に、見慣れないpolyという型がありました
気になってググったところ、以下の投稿を発見しました
どうやら多項式(polynomial)算術演算というものをするための型らしく
誤り検出符号や暗号化に利用されているようです
次回
個別の組み込み関数について説明する前に、全体像に触れておきたいと思います
ARM NEONの使い方 組み込み関数一覧編
ARM NEONの使い方 予告編
今使ってるvimプラグイン
最近の私はIDE(Visual Studio、Eclipse)を中心に開発することが多かったのですが
休日は久々にvimで遊びました
というわけで(?)、現在私が使っているvimプラグインをご紹介します
私は普段c++を使うことが多いので、c++向けの設定も入ってます
目次
プラグイン管理ツール
まずは管理ツールを入れなきゃねってことで
これはNeoBundleで決まりでしょ!
…って思ってたら気になる記事がいくつか
このvim-plug、導入が楽なので気に入った!
NeoBundleも十分楽だけどこっちはもっと楽でした
それでは本編
scrooloose/nerdtree
GitHub - scrooloose/nerdtree: A tree explorer plugin for vim.
言わずと知れたファイルエクスプローラ
私はF2でエクスプローラを開閉するように設定しています
nnoremap <F2> :NERDTreeToggle<CR>
tomasr/molokai
GitHub - tomasr/molokai: Molokai color scheme for Vim
カラースキーマ
Sublime Textで使われてるやつ
molokai_originalの方が柔らかくて好き
colorscheme molokai let g:molokai_original = 1
tyru/caw.vim
自動コメント/コメント解除
ノーマルモード + <C-k>で現在行をコメント
ビジュアルモード + <C-k>で複数行を一括コメント!
nmap <C-k> <plug>(caw:i:toggle) vmap <C-k> <plug>(caw:i:toggle)
itchyny/lightline.vim
GitHub - itchyny/lightline.vim: A light and configurable statusline/tabline for Vim
ステータスライン
現在の編集モードなどをカラフルに表示してくれる
octol/vim-cpp-enhanced-highlight
junegunn/vim-easy-align
GitHub - junegunn/vim-easy-align: A Vim alignment plugin
選択範囲を指定した文字(<Space>, =, :, etc.)で揃えてくれる
これが決まると超絶気持ちがいい
vimを使ってない人にドヤりたくなる(やらないけどw)プラグイン
justmao945/vim-clang
GitHub - justmao945/vim-clang: Clang completion plugin for vim
こっからは補完系プラグインの紹介
vim-clangはclangを使ったc++の補完プラグイン
標準ライブラリの補完ができるのは強い
Shougo/neocomplete
GitHub - Shougo/neocomplete.vim: Next generation completion framework after neocomplcache
言わずと知れた補完プラグイン
タイプしたその場で補完候補が出るので一気にIDEっぽくなる
ファイルパスを保管してくれるのも地味に便利
Shougo/neosnippet
GitHub - Shougo/neosnippet.vim: neo-snippet plugin contains neocomplcache snippets source
コードスニペット補完
今日入れましたが、なにこれ超便利
挿入モードでp<C-k>を打つと↓が挿入されて感動!
std::cout << << std::endl;
Shougo/neosnippet-snippets
GitHub - Shougo/neosnippet-snippets: The standard snippets repository for neosnippet
スニペットの辞書
neosnippetと一緒にいれておく
Free Space Computation
以前の記事で紹介したStixelのアルゴリズムに登場する
Free Space Computationを実装しようと頑張ってます
Free Space Computationとは
視差画像をもとにシーンのFree Space(移動可能な領域、路面)を推定する手法です
推定結果のイメージです(赤く塗った領域がFree Space)
より具体的には、Free Space Computationは
画像の列ごとに「路面と物体の下端との境界」となる行を見つける問題となります
参考文献
[1]Efficient Representation of Traffic Scenes by Means of Dynamic Stixels
[2]Free Space Computation Using Stochastic Occupancy Grids and Dynamic Programming
考え方
下図に、画像のある列におけるステレオ観測値(=奥行き)の分布を示します
緑線が路面、赤線が物体、青線が背景の領域です
この図から、
- 「路面と物体の下端との境界」から手前側(カメラ側)は路面の領域
- 物体領域の奥行きはほぼ一定(物体は大抵の場合直立しているため)
ということがわかると思います
これをより数学的に書くと次のようになります
- を「路面と物体の下端との境界」となる行、を画像下端の行とすると、]を満たすは路面領域内にあり、その視差はで表される
- を物体の高さ(画素単位)とすると、]を満たすは物体領域内にあり、その視差はで一定である
ここではある行に対応する路面上の視差を返す関数であり
論文では観測した路面の視差をB-Splineでフィッティングすることでを求めています
この考えをもとに、境界の位置で最小となるようなコスト関数を定義します
コスト関数
コスト関数は以下のように、2つのサブコスト と の重み付き和となります
は路面(Road)のコストで、以下のように表されます
は物体(Object)のコストで、以下のように表されます
これを各画素について計算します
コードで書くとこんな感じです
for (int u = 0; u < disp.cols; ++u) { for (int vb = 0; vb < disp.rows; ++vb) { float objectCost = 0, roadCost = 0; for (int v = vb; v < disp.rows; ++v) roadCost += fabsf(disp(v, u) - roadDisp[v]); for (int v = max(vb - hv, 0); v < vb; ++v) objectCost += fabsf(disp(v, u) - roadDisp[vb]); score(vb, u) = alpha1 * roadCost + alpha2 * objectCost; } }
DPによる境界の計算
最終的な出力である「路面と物体の下端との境界」を計算します
これは先ほど求めたスコアの値だけではなく、境界の空間的な連続性も考慮する必要があります
そこで画像の各画素をノードとして、左端と右端を結ぶ最適な経路を求める問題を考えます
これは動的計画法的に以下のように書けます
for (int v = 0; v < score.rows; v++) { dpcost(v, 0) = score(v, 0) } for (int u = 1; u < score.cols; u++) { for (int v = 0; v < score.rows; v++) { float mincost = FLT_MAX; int minpath = 0; for (int vv = 0; vv < score.rows; vv++) { int jump = abs(disp(v, u) - disp(vv, u - 1)); int penalty = std::min(P1 * jump, P1 * P2); float cost = score(v, u) + dpcost(vv, u - 1) + penalty; if (cost < mincost) { mincost = cost; minpath = vv; } } dpcost(v, u) = mincost; dppath(v, u) = minpath; } } // back track float mincost = FLT_MAX; int minv = 0; for (int v = 0; v < dpcost.rows; v++) { if (dpcost(v, dpcost.cols - 1) < mincost) { mincost = dpcost(v, dpcost.cols - 1); minv = v; } } for (int u = dppath.cols - 1; u >= 0; u--) { path[u] = minv; minv = dppath(minv, u); }
上記コードで境界の空間的な連続性を与えるのが、以下の部分です
int jump = abs(disp(v, u) - disp(vv, u - 1)); int penalty = std::min(P1 * jump, P1 * P2);
隣接ノードとの視差の絶対差分に比例したペナルティを与えます
(ただし、ある程度の不連続性を許容するため差分がP2以上であればペナルティは一定とする)
ただこの計算、オーダーがとなって結構重いです
これであってるんかな…
実装結果
前回紹介したデータセットを使って実験した結果が最初に見せた図です
入力の視差画像はデータセットから与えられます
路面上の視差はB-Splineではなく、直線で近似してます
まあ何となくあってそうに見える…
おわりに
Free Space Computationについて理解したこと、実装したことを紹介しました
今後はB-splineによる路面モデルの推定を実装したいです