0

Renderscript を使用して、画像にガウス ブラーを適用します。しかし、私が何をしたとしても。ScriptIntrinsicBlur の方が高速です。なぜこれが起こったのですか?ScriptIntrinsicBlur は別の方法を使用していますか? このIDは私のRSコードです:

#pragma version(1)
#pragma rs java_package_name(top.deepcolor.rsimage.utils)

//aussian blur algorithm.

//the max radius of gaussian blur
static const int MAX_BLUR_RADIUS = 1024;

//the ratio of pixels when blur
float blurRatio[(MAX_BLUR_RADIUS << 2) + 1];

//the acquiescent blur radius
int blurRadius = 0;

//the width and height of bitmap
uint32_t width;
uint32_t height;

//bind to the input bitmap
rs_allocation input;
//the temp alloction
rs_allocation temp;

//set the radius
void setBlurRadius(int radius)
{
    if(1 > radius)
        radius = 1;
    else if(MAX_BLUR_RADIUS < radius)
        radius = MAX_BLUR_RADIUS;

    blurRadius = radius;


    /**
    calculate the blurRadius by Gaussian function
    when the pixel is far way from the center, the pixel will not contribute to the center
    so take the sigma is blurRadius / 2.57
    */
    float sigma = 1.0f * blurRadius / 2.57f;
    float deno  = 1.0f / (sigma * sqrt(2.0f * M_PI));
    float nume  = -1.0 / (2.0f * sigma * sigma);

    //calculate the gaussian function
    float sum = 0.0f;
    for(int i = 0, r = -blurRadius; r <= blurRadius; ++i, ++r)
    {
        blurRatio[i] = deno * exp(nume * r * r);
        sum += blurRatio[i];
    }

    //normalization to 1
    int len = radius + radius + 1;
    for(int i = 0; i < len; ++i)
    {
        blurRatio[i] /= sum;
    }

}

/**
the gaussian blur is decomposed two steps:1
1.blur in the horizontal
2.blur in the vertical
*/
uchar4 RS_KERNEL horizontal(uint32_t x, uint32_t y)
{
    float a, r, g, b;

    for(int k = -blurRadius; k <= blurRadius; ++k)
    {
        int horizontalIndex = x + k;

        if(0 > horizontalIndex) horizontalIndex = 0;
        if(width <= horizontalIndex) horizontalIndex = width - 1;

        uchar4 inputPixel = rsGetElementAt_uchar4(input, horizontalIndex, y);

        int blurRatioIndex = k + blurRadius;
        a += inputPixel.a * blurRatio[blurRatioIndex];
        r += inputPixel.r * blurRatio[blurRatioIndex];
        g += inputPixel.g * blurRatio[blurRatioIndex];
        b += inputPixel.b * blurRatio[blurRatioIndex];
    }

    uchar4 out;

    out.a = (uchar) a;
    out.r = (uchar) r;
    out.g = (uchar) g;
    out.b = (uchar) b;

    return out;
}

uchar4 RS_KERNEL vertical(uint32_t x, uint32_t y)
{
    float a, r, g, b;

    for(int k = -blurRadius; k <= blurRadius; ++k)
    {
        int verticalIndex = y + k;

        if(0 > verticalIndex) verticalIndex = 0;
        if(height <= verticalIndex) verticalIndex = height - 1;

        uchar4 inputPixel = rsGetElementAt_uchar4(temp, x, verticalIndex);

        int blurRatioIndex = k + blurRadius;
        a += inputPixel.a * blurRatio[blurRatioIndex];
        r += inputPixel.r * blurRatio[blurRatioIndex];
        g += inputPixel.g * blurRatio[blurRatioIndex];
        b += inputPixel.b * blurRatio[blurRatioIndex];
    }

    uchar4 out;

    out.a = (uchar) a;
    out.r = (uchar) r;
    out.g = (uchar) g;
    out.b = (uchar) b;

    return out;
}
4

1 に答える 1

2

Renderscript 組み込み関数は、独自のスクリプトで実現できるものとは大きく異なります。これにはいくつかの理由がありますが、主な理由は、特定のハードウェア/SoC 構成を最大限に活用する方法で、個々のデバイスの RS ドライバー開発者によって構築されているためです。 RS プログラミング層では使用できません。

ただし、Android はこれらの組み込み関数の一般的な実装を提供しており、より下位のハードウェア実装が利用できない場合に備えて、一種の「フォールバック」を行います。これらの一般的なものがどのように行われるかを見ると、これらの組み込み関数がどのように機能するかについてのより良いアイデアが得られます。たとえば、3x3 畳み込み組み込み関数の一般的な実装のソース コードは、rsCpuIntrinsicConvolve3x3.cppにあります。

そのソース ファイルの 98 行目から始まるコードをよく見てください。畳み込みを行うために for ループがまったく使用されていないことに注意してください。これはアンロール ループと呼ばれ、コード内の 9 つの対応するメモリ ロケーションを明示的に加算および乗算することで、for ループ構造が不要になります。これは、並列コードを最適化するときに考慮しなければならない最初のルールです。カーネル内のすべての分岐を取り除く必要があります。コードを見ると、分岐を引き起こす多数の と があります。ifこれforは、プログラムの制御フローが最初から最後までまっすぐではないことを意味します。

for ループを展開すると、すぐにパフォーマンスが向上することがわかります。for 構造を削除すると、考えられるすべての半径量に対してカーネルを一般化できなくなることに注意してください。その場合、さまざまな半径に対して固定カーネルを作成する必要があります。これがまさに、3x3 と 5x5 の畳み込み組み込み関数が別々に表示される理由です。( rsCpuIntrinsicConvolve5x5.cppの 5x5 組み込み関数の 99 行目を参照してください)。

さらに、2 つの別個のカーネルがあるという事実は役に立ちません。ガウスぼかしを行っている場合、畳み込みカーネルは実際に分離可能であり、そこで行ったように 1xN + Nx1 畳み込みを行うことができますが、両方のパスを同じカーネルにまとめることをお勧めします。

ただし、これらのトリックを実行しても、実際の組み込み関数ほど高速な結果が得られない可能性があることに注意してください。これらはおそらく特定のデバイス用に高度に最適化されているためです。

于 2016-09-05T09:50:27.760 に答える