OpenCV H/Wアクセラレータ(算術演算系の実装)

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に次のようにあります。

define ARITHM_USE_IPP 1

define IF_IPP(then_call, else_call) then_call

else

define ARITHM_USE_IPP 0

define IF_IPP(then_call, else_call) else_call

endif

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)

{

if CV_SSE2

    Op8 op8;

endif

    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

if CV_ENABLE_UNROLLED

        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;

        }

endif

        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回実行されるのかな?