【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matchingharmonylab
公開URL:https://arxiv.org/pdf/2404.19174
出典:Guilherme Potje, Felipe Cadar, Andre Araujo, Renato Martins, Erickson R. ascimento: XFeat: Accelerated Features for Lightweight Image Matching, Proceedings of the 2024 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR) (2023)
概要:リソース効率に優れた特徴点マッチングのための軽量なアーキテクチャ「XFeat(Accelerated Features)」を提案します。手法は、局所的な特徴点の検出、抽出、マッチングのための畳み込みニューラルネットワークの基本的な設計を再検討します。特に、リソースが限られたデバイス向けに迅速かつ堅牢なアルゴリズムが必要とされるため、解像度を可能な限り高く保ちながら、ネットワークのチャネル数を制限します。さらに、スパース下でのマッチングを選択できる設計となっており、ナビゲーションやARなどのアプリケーションに適しています。XFeatは、高速かつ同等以上の精度を実現し、一般的なラップトップのCPU上でリアルタイムで動作します。
セル生産方式におけるロボットの活用には様々な問題があるが,その一つとして 3 体以上の物体の組み立てが挙げられる.一般に,複数物体を同時に組み立てる際は,対象の部品をそれぞれロボットアームまたは治具でそれぞれ独立に保持することで組み立てを遂行すると考えられる.ただし,この方法ではロボットアームや治具を部品数と同じ数だけ必要とし,部品数が多いほどコスト面や設置スペースの関係で無駄が多くなる.この課題に対して音𣷓らは組み立て対象物に働く接触力等の解析により,治具等で固定されていない対象物が組み立て作業中に運動しにくい状態となる条件を求めた.すなわち,環境中の非把持対象物のロバスト性を考慮して,組み立て作業条件を検討している.本研究ではこの方策に基づいて,複数物体の組み立て作業を単腕マニピュレータで実行することを目的とする.このとき,対象物のロバスト性を考慮することで,仮組状態の複数物体を同時に扱う手法を提案する.作業対象としてパイプジョイントの組み立てを挙げ,簡易な道具を用いることで単腕マニピュレータで複数物体を同時に把持できることを示す.さらに,作業成功率の向上のために RGB-D カメラを用いた物体の位置検出に基づくロボット制御及び動作計画を実装する.
This paper discusses assembly operations using a single manipulator and a parallel gripper to simultaneously
grasp multiple objects and hold the group of temporarily assembled objects. Multiple robots and jigs generally operate
assembly tasks by constraining the target objects mechanically or geometrically to prevent them from moving. It is
necessary to analyze the physical interaction between the objects for such constraints to achieve the tasks with a single
gripper. In this paper, we focus on assembling pipe joints as an example and discuss constraining the motion of the
objects. Our demonstration shows that a simple tool can facilitate holding multiple objects with a single gripper.
20. Intrinsicによる加算コードの最適化Intrinsicによる加算コードの最適化
id l d dd t SSEvoid loadaddstoreSSE
(uchar* src1, uchar* src2, uchar* dest, const int size)
{{
for(int i=0;i<size;i+=16)
{
//データをレジスタa,bに128bit(8bit16個)ロードする
__m128i a = _mm_load_si128((__m128i*)(src1+i));
128i b l d i128(( 128i*)( 2 i))__m128i b = _mm_load_si128((__m128i*)(src2+i));
//aとbのデータを同時に16個足し算し,aに結果を書きこむ
a = mm add epi8(a b);a _mm_add_epi8(a,b);
//レジスタaのデータをメモリのdestに128bit(8bit16個)ストアする
_mm_store_si128((__m128i*)(dest+i),a);
}
}
23. Intrinsicによる加算コードの最適化Intrinsicによる加算コードの最適化
id l d dd t SSEvoid loadaddstoreSSE
(short* src1, short* src2, short* dest, const int size)
{{
for(int i=0;i<size;i+=8)
{
//データをレジスタa,bに128bit(16bit8個)ロードする
__m128i a = _mm_load_si128((__m128i*)(src1+i));
128i b l d i128(( 128i*)( 2 i))__m128i b = _mm_load_si128((__m128i*)(src2+i));
//aとbのデータを同時に8個足し算し,aに結果を書きこむ
a = mm add epi16(a b);a _mm_add_epi16(a,b);
//レジスタaのデータをメモリのdestに128bit(16bit8個)ストアする
_mm_store_si128((__m128i*)(dest+i),a);
}
}
37. uchar → short (アライメント無視)uchar → short (アライメント無視)
void cvtuchar2short(uchar* src, short* dest, int size)
{
for(int i=0;i<size;i+=8)for(int i=0;i<size;i+=8)
{
//8バイト刻みでロードする(下半分は無視)//8 イト刻みで ドする(下半分は無視)
__m128i a = _mm_loadu_si128((__m128i*)(src+i));
//下位ビットをshortに変換する命令
a =_mm_cvtepu8_epi16(a);
//shortのデータは16ごとに書きこめる
mm store si128(( m128i*)(dest+i) a);_mm_store_si128((__m128i*)(dest+i),a);
}
}}
38. uchar → short (アライメント考慮)uchar → short (アライメント考慮)
void cvtuchar2shortAligned(uchar* src, short* dest, int size)
{{
for(int i=0;i<size;i+=16)
{{
//16バイト刻みでロードする
__m128i a = _mm_load_si128((__m128i*)(src+i));
//レジスタ全体を8バイト右シフトする
__m128i b = _mm_srli_si128(a,8);
//下位ビ トを h tに変換する命令とストアを2回づつ//下位ビットをshortに変換する命令とストアを2回づつ
a =_mm_cvtepu8_epi16(a);
b = mm cvtepu8 epi16(b);b _mm_cvtepu8_epi16(b);
_mm_store_si128((__m128i*)(dest+i),a);
_mm_store_si128((__m128i*)(dest+i+8),b);
}
}
39. uchar → short (アライメント考慮)uchar → short (アライメント考慮)
void cvtuchar2shortAligned2(uchar* src, short* dest, int size)
{{
//全て0のレジスタを作る
const m128i zero = mm setzero si128();const __m128i zero = _mm_setzero_si128();
for(int i=0;i<size;i+=16){
//16バイト刻みでロードする
__m128i a = _mm_load_si128((__m128i*)(src+i));
//ゼロで入力のHigh Lowをアンパック
128i b khi i8( )__m128i b = _mm_unpackhi_epi8(a,zero);
a = _mm_unpacklo_epi8(a,zero);
//ストア//ストア
_mm_store_si128((__m128i*)(dest+i),a);
_mm_store_si128((__m128i*)(dest+i+8),b);
}
}
40. 比較比較
• C++: 355.772 ms
• アライメント無視: 64 8431 msアライメント無視: 64.8431 ms
• アライメント考慮1: 44.4918 ms
• アライメント考慮2: 37.3332 ms
最大9.5倍の高速化
41. uchar → int (アライメント無視)uchar → int (アライメント無視)
void cvtuchar2int(uchar* src, int* dest, int size)
{
for(int i=0;i<size;i+=4)for(int i=0;i<size;i+=4)
{
//4バイト刻みでロードする(12バイト分無視)//4 イト刻みで ドする( イト分無視)
__m128i a = _mm_loadu_si128((__m128i*)(src+i));
//下位ビットをintに変換する命令
a =_mm_cvtepu8_epi32(a);
_mm_store_si128((__m128i*)(dest+i),a);
}}
}
42. uchar → short (アライメント考慮)uchar → short (アライメント考慮)
void cvtuchar2intAligned(uchar* src, int* dest, int size)
{{
for(int i=0;i<size;i+=16)
{
//16バイト刻みでロードする// イ 刻 する
__m128i a = _mm_load_si128((__m128i*)(src+i));
//intにしてストア
__m128i b =_mm_cvtepu8_epi32(a);
_mm_store_si128((__m128i*)(dest+i),b);
//レジスタ全体を4バイト右シフトして変換してストアを4回繰り返す
a = _mm_srli_si128(a,4);
b =_mm_cvtepu8_epi32(a);
_mm_store_si128((__m128i*)(dest+i+4),b);
a = _mm_srli_si128(a,4);
b ( )b =_mm_cvtepu8_epi32(a);
_mm_store_si128((__m128i*)(dest+i+8),b);
a = _mm_srli_si128(a,4);
b t 8 i32( )b =_mm_cvtepu8_epi32(a);
_mm_store_si128((__m128i*)(dest+i+12),b);
}
}
43. 比較比較
• C++: 365.9 ms
• アライメント無視: 125 0 msアライメント無視: 125.0 ms
• アライメント考慮: 65.6 ms
最大5.6倍の高速化
44. 変換 mm cvt (SSE4 1)変換_mm_cvt (SSE4.1)
0 0
_mm_cvtepu8_epi16 _mm_cvtepu8_epi32
下位ビットを任意の整数に変換する命令下位ビットを任意の整数に変換する命令
__m128i _mm_cvtepi#_epi#(__m128i a)
m128i mm cvtepu# epi#( m128i a)__m128i _mm_cvtepu#_epi#(__m128i a)
#8,16,32 #16,32,64
45. シフト mm slli si128 (SSE2)シフト_mm_slli_si128 (SSE2)
mm slli si128( m128i 4)
0 0
mm srli si128( m128i 4)_mm_slli_si128(__m128i, 4) _mm_srli_si128(__m128i, 4)
レジスタ全体を右 左にシフトする関数レジスタ全体を右,左にシフトする関数
右シフトで任意のビットを下位に持ってこれる
51. short からucharshort からuchar
void cvtshort2ucharAligned (short* src, uchar* dest, int size)
{
for(int i=0;i<size;i+=16)for(int i=0;i<size;i+=16)
{
//shortのデータ16個を2つのレジスタにロード//shortのデ タ 6個を のレジスタに ド
__m128i a = _mm_load_si128((__m128i*)(src+i));
__m128i b = _mm_load_si128((__m128i*)(src+i+8));
ジ タをパ デ タ 変換//二つのレジスタをパックしてcharのデータに変換してストア
a = _mm_packs_epi16(a,b);
mm store si128(( m128i*)(dest+i) a);_mm_store_si128((__m128i*)(dest+i),a);
}
}}
52. int からucharint からuchar
void cvtint2ucharAligned(int* src, uchar* dest, int size)
{
for(int i=0;i<size;i+=16)
{
//intのデータ16個を4つのレジスタにロード
m128i a = mm load si128(( m128i*)(src+i));__m128i a _mm_load_si128((__m128i )(src i));
__m128i b = _mm_load_si128((__m128i*)(src+i+4));
__m128i c = _mm_load_si128((__m128i*)(src+i+8));
m128i d = mm load si128(( m128i*)(src+i+12));__m128i d = _mm_load_si128((__m128i )(src+i+12));
//上位下位二つのレジスタをパックして2つのshortのデータに変換
a = _mm_packs_epi32(a,b);
k i32( d)c = _mm_packs_epi32(c,d);
//shortをパックしてストア
a = _mm_packs_epi16(a,c);
_mm_store_si128((__m128i*)(dest+i),a);
}
}
53. 実験結果実験結果
Short to uchar
• C++: 341.8 mC++: 341.8 m
• SSE: 35.4 ms
9 7倍の高速化
Int to ucahr
9.7倍の高速化
Int to ucahr
• C++: 372.1 ms
• SSE: 45.3 ms
8.2倍の高速化8.2倍の高速化
64. コピー a copyTo(b)コピー a.copyTo(b)
void copy8SSE(const Mat& src, Mat& dest)void copy8SSE(const Mat& src, Mat& dest)
{
const uchar* s = src.data;
uchar* d = dest data;uchar d = dest.data;
const int size = src.size().area()*src.channels();
for(int i=0;i<size;i+=16)
{{
__m128i a = _mm_load_si128((__m128i*)(s+i));
_mm_store_si128((__m128i*)(d+i),a);
}
}
16バイト単位でロードとストアを
繰り返すだけ!繰り返すだけ!
65. 結果結果
• OpenCV 68.2 ms
• C++ 308.4ms
• SSE 62.8 ms
コメント
– さすがにコピーは変わらない
– SSEを使ってでコピーしているところをmemcpyでpy
書いても同じ速度
– 自前でループ展開して代入するよりは速い.
66. 変換 a convertTo(b CV 16U)変換 a.convertTo(b,CV_16U)
void cvtuchar2ushortMatSSE(Mat& a,Mat& b)
{{
const int size = a.size().area()*a.channels();
const uchar* s = a.ptr<uchar>(0);
i d h * d b i d h (0)unsigned short* d = b.ptr<unsigned short>(0);
const __m128i zero = _mm_setzero_si128();
for(int i=0;i<size;i+=16)
{
__m128i a = _mm_load_si128((__m128i*)(s+i));
__m128i b = _mm_unpackhi_epi8(a,zero);__ _ _ p _ p ( , );
a = _mm_unpacklo_epi8(a,zero);
_mm_store_si128((__m128i*)(d+i),a);
mm store si128(( m128i*)(d+i+8),b);_mm_store_si128((__m128i )(d+i+8),b);
}
}
必要な型変換を実装するだけ必要な型変換を実装するだけ
(本当はax+bが出来る関数だけど今回は無視)
67. 結果結果
• OpenCV 159.2 ms
• C++ 206.5 msC++ 206.5 ms
• SSE 100.7 ms
コメントコメント
– 型変換はOpenCVに実装されてないため高速化さ
れるれる.
68. 加算 c = a + b cv::add(a b c)加算 c = a + b, cv::add(a,b,c)
void add8SSE(Mat& src1, Mat& src2, Mat& dest)
{
uchar* pa = src1.data;uchar* pb = src2.data;uchar* pc =
dest datadest.data;
const int size = src.size().area()*src.channels();
for(int i=0;i<size;i+=16)for(int i 0;i size;i 16)
{
__m128i a = _mm_load_si128((__m128i*)(pa+i));
__m128i b = _mm_load_si128((__m128i*)(pb+i));
//飽和演算つきunsignedの加算
a mm adds epu8(a b);a = _mm_adds_epu8(a,b);
_mm_store_si128((__m128i*)(pc+i),a);
}}
}
69. 結果結果
• OpenCV 101.1 ms
• C++ 524.9 msC++ 524.9 ms
• SSE 71.4 ms
コメントコメント
– 例外処理が無いことやアライメントをそろえている
分O CVの関数より高速分OpenCVの関数より高速
70. 乗算(1/2) c = a mul(b) cv::multiply(a b c)乗算(1/2) c = a.mul(b) ,cv::multiply(a,b,c)
void multiply8SSE(const Mat& src1, const Mat& src2,Mat& dest)
{
//ロードしたりする前処理
uchar* s1 src1 data; uchar* s2 src2 data;uchar* duchar* s1 = src1.data; uchar* s2 = src2.data;uchar* d =
dest.data;
const int size = src1.size().area()*src1.channels();const int si e src .si e().area() src .channels();
const __m128i zero = _mm_setzero_si128();
for(int i=0;i<size;i+=16)
{
//ロードデータを2本ストア
m128i a = mm load si128(( m128i*)(s1+i));__m128i a = _mm_load_si128((__m128i*)(s1+i));
__m128i b = _mm_load_si128((__m128i*)(s2+i));
71. 乗算(2/2) c = a mul(b) cv::multiply(a b c)乗算(2/2) c = a.mul(b) ,cv::multiply(a,b,c)
//入力をhigh,lowにアンパック
__m128i a1 = _mm_unpackhi_epi8(a,zero);
__m128i a2 = _mm_unpacklo_epi8(a,zero);
m128i b1 mm unpackhi epi8(b zero);__m128i b1 = _mm_unpackhi_epi8(b,zero);
__m128i b2 = _mm_unpacklo_epi8(b,zero);
//High,Lowごとに乗算(8bit乗算命令はない)して下位ビットをとりだ//High,Lowごとに乗算(8bit乗算命令はない)して下位ビットをとりだ
す
a = _mm_mullo_epi16(a1,b1);
b = _mm_mullo_epi16(a2,b2);
//2つの計算結果をアンパックしてストア
a = mm packus epi16(a b);a = _mm_packus_epi16(a,b);
_mm_store_si128((__m128i*)(d+i),a);
}}
}
72. 結果結果
• OpenCV 405.8 ms
• C++ 409.3 msC++ 409.3 ms
• SSE 106.1 ms
コメントコメント
– OpenCVには実装されていない命令のためかなり
高速化に成功高速化に成功
73. 変換2(1/2) a convertTo(b CV 16U 5 10)変換2(1/2) a.convertTo(b,CV_16U,5,10)
void convertTouchar2ushortMatSSE(Mat& src,Mat& dest, const int
l h i b )alpha, const int beta)
{
const int size = src size() area()*src channels();const int size = src.size().area() src.channels();
const uchar* s = src.data;
unsigned short* d = dest.ptr<unsigned short>(0);
//0をセットするのと16ビットで8つの定数alpha.betaをセット
const __m128i zero = _mm_setzero_si128();
t 128i A t1 i16( l h )const __m128i A = _mm_set1_epi16(alpha);
const __m128i B = _mm_set1_epi16(beta);
for(int i=0;i<size;i+=16)for(int i 0;i<size;i+ 16)
{
__m128i a = _mm_load_si128((__m128i*)(s+i));
74. 変換2(2/2) a convertTo(b CV 16U 5 10)変換2(2/2) a.convertTo(b,CV_16U,5,10)
//ロードしたレジスタを上位でアンパックして乗算
128i b khi i8( )__m128i b = _mm_unpackhi_epi8(a,zero);
b = _mm_mullo_epi16(b,A);
//ロードしたレジスタを下位でアンパックして乗算//ロ ドしたレジスタを下位でアンパックして乗算
a = _mm_unpacklo_epi8(a,zero);
a = _mm_mullo_epi16(a,A);
//計算結果を加算(shotrでの計算結果を得る)
a = _mm_adds_epi16(a,B);
b dd i16(b B)b = _mm_adds_epi16(b,B);
//shortは8ごとでも16境界なので2つストア
mm store si128(( m128i*)(d+i) a);_mm_store_si128((__m128i )(d+i),a);
_mm_store_si128((__m128i*)(d+i+8),b);
}
}
75. 結果結果
• OpenCV 1067.5 ms
• C++ 307.6 msC++ 307.6 ms
• SSE 109.9 ms
• コメントコメント
– convertToとかかなり使うのに,OpenCVはどんな
実装してるんだか実装してるんだか...
– 乗算と加算,型変換のSSE化なのでかなり速い
76. 反転 cv::flip(a b 1)反転 cv::flip(a,b,‐1)
void flipSSE__(Mat& src, Mat& dest) //上下左右反転のみ
{
//反転用のマスク生成
const m128i mask =const __m128i mask =
_mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
const int size =src.size().area()*src.channels();const int si e src.si e().area() src.channels();
uchar* s = src.data+size‐16;
uchar* d = dest.data;
逆 ド 転//入力を逆からロードして反転してストア
for(int i=0;i<size;i+=16)
{{
__m128i a = _mm_load_si128((__m128i*)(s));
a = mm shuffle epi8(a,mask);_ _ _ p ( , );
_mm_store_si128((__m128i*)(d+i),a);
s‐=16;
77. 反転 cv::flip(a b code)全対応反転 cv::flip(a,b,code)全対応
void flip8UC1SSE(Mat& src, Mat& dest, int flipCode)
{
if(flipCode==0)
{
t i t i l /16const int size = src.cols/16;
uchar* ss = src.data;
uchar* dd = dest.data+(src.rows‐1)*src.cols;
for(int j=src.rows;j‐‐;)
{
__m128i* s = (__m128i*)(ss);
__m128i* d = (__m128i*)(dd);
for(int i=size;i‐‐;)
{
m128i a = mm load si128(s++);__m128i a _mm_load_si128(s );
_mm_store_si128((d++),a);
}
ss+=src.cols;
dd‐=src.cols;
}
}
else if(flipCode==1)
{
const int size = src.cols/16;
const __m128i mask = _mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
uchar* ss = src.data + src.cols ‐16;
uchar* dd = dest.data;
for(int j=src.rows;j‐‐;)
{
__m128i* s = (__m128i*)(ss);
__m128i* d = (__m128i*)(dd);
for(int i=size;i‐‐;)
{
m128i a = mm load si128(s );__m128i a = _mm_load_si128(s‐‐);
a = _mm_shuffle_epi8(a,mask);
_mm_store_si128((d++),a);
}
ss+=src.cols;
dd+=src.cols;
}
}
else if(flipCode==‐1)
{{
const __m128i mask = _mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0);
const int size =src.size().area()*src.channels();
uchar* s = src.data+size‐16;
uchar* d = dest.data;
for(int i=0;i<size;i+=16)
{
__m128i a = _mm_load_si128((__m128i*)(s));
a = _mm_shuffle_epi8(a,mask);
_mm_store_si128((__m128i*)(d+i),a);
6s‐=16;
}
}
}
78. 結果結果
フリップには3種類(左右反転 上下反転 上下左右反転)• フリップには3種類(左右反転,上下反転,上下左右反転)
• 左右 :OpenCV 375.6 ms C++ 313.4 ms SSE 68.8 ms
• 上下 :OpenCV 88 30 ms C++ 59 59 ms SSE 64 0 ms上下 :OpenCV 88.30 ms C++ 59.59 ms SSE 64.0 ms
• 上下左右 :OpenCV 467.3 ms C++ 306.5 ms SSE 65.8 ms
コメント
– 上下反転はSSEでもほとんど変わらない(逆順にmemcpyするだ
け)
– 上下左右反転 左右反転の結果はかなりいい上下左右反転,左右反転の結果はかなりいい
– Flipはビデオ入力とかでも良く使うので有望?
– ただしRGBのカラー処理ではないので,ちゃんとRGBように作ら
ないと使えないかなないと使えないかな
80. 色変換 cv::cvtColor(a b CV BGR2RGB)色変換 cv::cvtColor(a,b,CV_BGR2RGB)
void cvtBGR2RGB_SSE_nonal(Mat& src,Mat& dest){
i i i () ()* h l ()const int size = src.size().area()*src.channels();
const uchar* s = src.data; uchar* d = dest.data;
m128i mask =__m128i mask =
_mm_setr_epi8(2,1,0,5,4,3,8,7,6,11,10,9,14,13,12,
15);//ここを捨てる.//BGRBGRBGRBGRBGR B
for(int i=0;i<size;i+=15)//画素5x3づつ処理する
{
//境界をそろ てなくても使えるロ ド//境界をそろってなくても使えるロード
__m128i a = _mm_loadu_si128((__m128i*)(s+i));
//シャッフル//シャッフル
a = _mm_shuffle_epi8(a,mask);
//境界をそろってなくても使えるストア
_mm_storeu_si128((__m128i*)(d+i),a);
}
}
82. 色変換高速版(1/3) cv::cvtColor(a b CV BGR2RGB)色変換高速版(1/3) cv::cvtColor(a,b,CV_BGR2RGB)
//16画素x3個3つのレジスタに分けてロード
128i l d i128(( 128i*)( i))__m128i a = _mm_load_si128((__m128i*)(s+i));
__m128i b = _mm_load_si128((__m128i*)(s+i+16));
m128i c = mm load si128(( m128i*)(s+i+32));__m128i c = _mm_load_si128((__m128i )(s+i+32));
//レジスタをまたぐ値を抽出
int Ba = _mm_extract_epi8(a,15);
int Rb = _mm_extract_epi8(b,1);
int Bb = _mm_extract_epi8(b,14);
i t R t t i8( 0)int Rc = _mm_extract_epi8(c,0);
//シャッフルしてRGBRGBRGBRGBRっぽい値にスワップ
a = mm shuffle epi8(a mask1);a _mm_shuffle_epi8(a,mask1);
b = _mm_shuffle_epi8(b,mask2);
c = _mm_shuffle_epi8(c,mask3);
83. 色変換高速版(1/3) cv::cvtColor(a b CV BGR2RGB)色変換高速版(1/3) cv::cvtColor(a,b,CV_BGR2RGB)
//レジスタ間でまたぐ値を挿入して
i i8( Rb 15)a=_mm_insert_epi8(a,Rb,15);
b=_mm_insert_epi8(b,Ba,1);
b= mm insert epi8(b Rc 14);b=_mm_insert_epi8(b,Rc,14);
c=_mm_insert_epi8(c,Bb,0);
//16画素x3個ストア
_mm_store_si128((__m128i*)(d+i),a);
_mm_store_si128((__m128i*)(d+i+16),b);
t i128(( 128i*)(d i 32) )_mm_store_si128((__m128i*)(d+i+32),c);
}
}}
84. 結果結果
• OpenCV 392.3 ms
• C++ 265.2 msC++ 265.2 ms
• SSE 99.3 ms
• SSE 68.8 ms (アライメントまで考慮)
コメントコメント
– 良く使う割にOpenCVの関数は遅い
92. 分離(7/8) cv::split(a v)分離(7/8) cv::split(a,v)
//16個づつa,b,cにロード
a = _mm_load_si128((__m128i*)(s+i));
b = _mm_load_si128((__m128i*)(s+i+16));
c = mm load si128(( m128i*)(s+i+32));c = _mm_load_si128((__m128i )(s+i+32));
//ブレンドを使ってBBBBBBBBBBBBBBBBに変換
m128i v = mm blendv epi8(b,a,bmask1);__ _ _ _ p ( , , );
v = _mm_blendv_epi8(c,v,bmask2);
_mm_stream_si128((__m128i*)(B),v);
//シ とブ ドを使 に変換//シャッフルとブレンドを使ってGGGGGGGGGGGGGGGGに変換
a = _mm_shuffle_epi8(a,smask1);
b = mm shuffle epi8(b smask1);b = _mm_shuffle_epi8(b,smask1);
c = _mm_shuffle_epi8(c,smask1);
v = _mm_blendv_epi8(b,a,bmask3);_ _ _ p ( , , );
v = _mm_blendv_epi8(c,v,bmask2);
_mm_stream_si128((__m128i*)(G),v);
93. 分離(8/8) cv::split(a v)分離(8/8) cv::split(a,v)
//シャッフルとブレンドを使ってRRRRRRRRRRRRRRRRに変換
a = _mm_shuffle_epi8(a,ssmask1);
c = _mm_shuffle_epi8(c,ssmask1);
b = mm shuffle epi8(b ssmask2);b = _mm_shuffle_epi8(b,ssmask2);
v = _mm_blendv_epi8(b,a,bmask3);
v = mm blendv epi8(c,v,bmask4);_ _ _ p ( , , );
_mm_stream_si128((__m128i*)(R),v);
//16画素分進める
B+=16;G+=16;R+=16;
}
}}
94. 結果結果
• OpenCV 697.1 ms
• C++ 468.7 msC++ 468.7 ms
• SSE 171.4 ms
コメント
– RGBを並べ替えるのは結構めんどくさい...RGBを並 替えるのは結構めんどくさい...
– これベースにカラー画像のSSE化が可能
RRRRGGGGBBBBに並べ替えるのは 「速度的にこ– RRRRGGGGBBBBに並べ替えるのは,「速度的にこ
れってベストなの?」という疑問は残るが...
109. 乗算 mm mullo epi乗算_mm_mullo_epi
• _mm_mullo_epi(__m128i a, __m128i b)
• A,bの16bit同士の積を8つ同時に行う.桁の
あふれは無視あふれは無視
• _mm_mulhi_epi(__m128i a, __m128i b)
が上位ビ 演算• が上位ビットの演算
118. 使っているCPUがどこまでSSEをサ
ポートしているか
void checkCPU(void)
{
int CPUInfo[4];
int InfoType = 1;int InfoType = 1;
__cpuid(CPUInfo, InfoType);
if (CPUInfo[3] & 0x00800000) printf("support MMX¥n");
else printf("DO NOT support MMX¥n");else printf( DO NOT support MMX¥n );
if (CPUInfo[3] & 0x02000000) printf("support SSE¥n");
else printf("DO NOT support SSE¥n");
if (CPUInfo[3] & 0x04000000) printf("support SSE2¥n");
else printf("DO NOT support SSE2¥n");else printf( DO NOT support SSE2¥n );
if (CPUInfo[2] & 0x00000001) printf("support SSE3¥n");
else printf("DO NOT support SSE3¥n");
if (CPUInfo[2] & 0x00080000) printf("support SSE4.1¥n");
else printf("DO NOTsupport SSE4 1¥n");else printf( DO NOTsupport SSE4.1¥n );
if (CPUInfo[2] & 0x00100000) printf("support SSE4.2¥n");
else printf("DO NOT support SSE4.2¥n");
return ;
}
OpenCV.jpクックブックの「CPUがサポートする機能(SSEなど)をチェックする」より
http://opencv.jp/cookbook/opencv_utility.html#cpu‐sse
}