OpenCVのどのバージョンに対応するか一瞬迷った。
2.4.10に対応するか、3.0Betaなのか・・・
やっぱり、3.0Betaだよなぁ。
まず、H/Wアクセラレータ化するのに簡単な算術系演算を実装して、OpenCV 3.0Betaからアクセスできるところまで進めていきます。
最初に対応する算術系演算は以下のもの。
そのうち、addの実装から紐解いてみる。
ドキュメントは以下を参照して、ここに載っているように動作しなければいけない。
(制限事項は付くかもしれないが・・・)
title=""の関数は以下のように定義されている。
C++: void add(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray(), int dtype=-1)
これの本体はどこにいるかというと、modules/core/src/arithm.cppである。
ここに以下のように実装されている。
void cv::add( InputArray src1, InputArray src2, OutputArray dst,
InputArray mask, int dtype )
{
arithm_op(src1, src2, dst, mask, dtype, getAddTab() ),
}
addを実行するとこれが呼び出されるはず。
ここから、どこへ飛んでいくかというと、getAddTab()に書かれている関数に飛んでいく。
更に追いかけてみると、以下に進む。
static BinaryFunc* getAddTab()
{
static BinaryFunc addTab[] =
{
(BinaryFunc)GET_OPTIMIZED(add8u), (BinaryFunc)GET_OPTIMIZED(add8s),
(BinaryFunc)GET_OPTIMIZED(add16u), (BinaryFunc)GET_OPTIMIZED(add16s),
(BinaryFunc)GET_OPTIMIZED(add32s),
(BinaryFunc)GET_OPTIMIZED(add32f), (BinaryFunc)add64f,
0
};
return addTab;
}
あぁ、バイト単位での演算をできるようにしているんだね。
例えば、ARGBのデータを各色ごとに演算するのはどのように分けているんだろうかと思ったら、こういうことだったのね。
このうち、add8uのみH/Wアクセラレータで対応することにしよう。
俺の中で処理したいのは32bitのARGBで十分だから、つまり、各色8bit(add8u)ということで・・・
じゃぁ、add8uってどこにあるのというと・・・
static void add8u( const uchar* src1, size_t step1,
const uchar* src2, size_t step2,
uchar dst, size_t step, Size sz, void )
{
IF_IPP(fixSteps(sz, sizeof(dst[0]), step1, step2, step),
ippiAdd_8u_C1RSfs(src1, (int)step1, src2, (int)step2, dst, (int)step, ippiSize(sz), 0),
(vBinOp8<uchar, OpAdd<uchar>, IF_SIMD(_VAdd8u)>(src1, step1, src2, step2, dst, step, sz))),
}
IF_IPPって何よ!
precomp.hppに次のようにあります。
ARITHM_USE_IPPってなんですかぁ?
IPP=Intel(R) Integrated Performance Primitives (IPP)
つまり、俺はIntelライブラリ使わないから後者で良いわけですな。
add8u = vBinOp8
と、いうことになる。
それでやっと本体に辿り着いた。
void vBinOp8(const T src1, size_t step1, const T src2, size_t step2, T* dst, size_t step, Size sz)
{
Op8 op8;
Op op;
for( ; sz.height--; src1 += step1/sizeof(src1[0]),
src2 += step2/sizeof(src2[0]),
dst += step/sizeof(dst[0]) )
{
int x = 0;
#if CV_SSE2
if( USE_SSE2 )
{
for( ; x <= sz.width - 32; x += 32 )
{
__m128i r0 = _mm_loadu_si128((const __m128i*)(src1 + x)),
__m128i r1 = _mm_loadu_si128((const __m128i*)(src1 + x + 16)),
r0 = op8(r0,_mm_loadu_si128((const __m128i*)(src2 + x))),
r1 = op8(r1,_mm_loadu_si128((const __m128i*)(src2 + x + 16))),
_mm_storeu_si128((__m128i*)(dst + x), r0),
_mm_storeu_si128((__m128i*)(dst + x + 16), r1),
}
for( ; x <= sz.width - 8; x += 8 )
{
__m128i r0 = _mm_loadl_epi64((const __m128i*)(src1 + x)),
r0 = op8(r0,_mm_loadl_epi64((const __m128i*)(src2 + x))),
_mm_storel_epi64((__m128i*)(dst + x), r0),
}
}
#endif
for( ; x <= sz.width - 4; x += 4 )
{
T v0 = op(src1[x], src2[x]),
T v1 = op(src1[x+1], src2[x+1]),
dst[x] = v0; dst[x+1] = v1;
v0 = op(src1[x+2], src2[x+2]),
v1 = op(src1[x+3], src2[x+3]),
dst[x+2] = v0; dst[x+3] = v1;
}
for( ; x < sz.width; x++ )
dst[x] = op(src1[x], src2[x]),
}
}
えーっと、こうなってるから・・・
俺、ARMだし・・・
add8uの演算本体は以下で良いのかな?
for( ; x < sz.width; x++ )
dst[x] = op(src1[x], src2[x]),
}
まぁ、add関数だからねぇ。
ここまでは単にOpenCVの関数の実体を見つけ出しただけ、H/Wアクセラレータが実装されている時にどう処理するかを考えなければいけない。
各色を演算する場合、これを4回実行されるのかな?