2016-09-05 8 views
0

저는 Renderscript를 사용하여 이미지에 가우시안 블러를 적용합니다. 하지만 내가 한 일과 상관 없습니다. ScriptIntrinsicBlur가 더 빠릅니다. 왜 이런 일이 일어 났습니까? ScriptIntrinsicBlur가 다른 방법을 사용하고 있습니까? 이 ID 내 RS 코드 :왜 ScriptIntrinsicBlur가 내 방법보다 빠릅니까?

#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; 
} 
+0

1. 시험을 어떻게하고 있었습니까? 2. 어떤 하드웨어/에뮬레이터를 테스트하고 있습니까? 3. 장치에있는 경우 - ODM이 응용 프로그램 개발자가 사용할 수없는 추가 하드웨어 리소스로 ScriptIntrinsics를 구현할 수 있다고 생각하십시오. –

+0

나는 진짜 전화로 이미지 (293x220)로 테스트한다. 내 방법 비용은 약 120ms –

+0

ODM의 평균은 무엇입니까? 나는 진짜 전화로 이미지 (293x220)로 테스트하고, 블러 반경은 20이다. 나의 방법 비용은 약 120ms이다. ScriptIntrinsicBlur 약 25ms .i는 copyTo() 메서드가 너무 많은 시간을 소비한다는 것을 알았습니다 (ScriptIntrinsicBlur도이 메서드를 사용하지만 시간이 거의 들지 않았습니다). 그런데 ScriptIntrinsicBlur에 대한 RS 소스 코드를 어디에서 찾을 수 있습니까? –

답변

2

Renderscript 내장 함수가 자신의 스크립트를 달성 할 수있는 것과 매우 다르게 구현됩니다. 이는 여러 가지 이유가 있지만 주로 특정 하드웨어/SoC 구성을 최대한 활용할 수있는 방법으로 개별 장치의 RS 드라이버 개발자가 작성하고 간단히 하드웨어에 대한 낮은 수준의 호출을 수행하기 때문에 가능합니다 RS 프로그래밍 계층에서는 사용할 수 없습니다.

안드로이드는 이러한 내장 함수의 일반적인 구현을 제공하지만 더 낮은 하드웨어 구현을 사용할 수없는 경우에는 "폴백"을 정렬합니다. 이러한 일반적인 것들이 어떻게 수행되는지 보는 것은 이러한 내장 함수가 어떻게 작동하는지 더 잘 이해할 수있게 해줍니다. 예를 들어, 여기에 3x3 컨볼 루션 내장 함수의 일반적인 구현 코드 인 소스 코드 rsCpuIntrinsicConvolve3x3.cpp을 볼 수 있습니다.

해당 소스 파일의 98 번째 줄부터 시작하여 코드를 자세히 살펴보고 루프에 을 사용하는 방법에주의하십시오. 무엇이 발생합니까? 이것은 풀린 루프 (unrolled loop)로 알려져 있습니다. 코드에서 9 개의 해당 메모리 위치를 명시 적으로 추가하고 곱하여 for 루프 구조가 필요 없습니다. 이것은 병렬 코드를 최적화 할 때 고려해야 할 첫 번째 규칙입니다. 커널에서 모든 분기를 제거해야합니다. 코드를 보면 분기가 발생하는 iffor이 많이 있습니다. 즉, 프로그램의 제어 흐름이 처음부터 끝까지 똑바로 진행되지 않음을 의미합니다.

for 루프를 실행하면 즉시 성능이 향상됩니다. for 구조체를 제거하면 더 이상 모든 반경 값에 대해 커널을 일반화 할 수 없습니다. 이 경우 서로 다른 반지름에 대해 고정 된 커널을 만들어야하는데, 이것은 정확히입니다. 왜 3x3 및 5x5 회선 내장 함수가 개별적으로 표시되는지 이해해야합니다. (의 5x5 내장 함수 99 행 참조).

또한 두 개의 별도 커널이 있다는 사실은 도움이되지 않습니다. 가우스 블러를하고 있다면, 컨볼 루션 커널은 실제로 분리가 가능하며 거기에서했던 것처럼 1xN + Nx1 컨볼 루션을 할 수 있지만, 두 커널을 같은 커널에 둘 것을 권장합니다.

이러한 트릭을 수행하더라도 실제로는 특정 장치에 최적화 된 것이므로 실제 내장 함수와 같은 빠른 결과를 제공하지는 않습니다.

+0

정말 고마워요. 당신의 대답은 저에게 많은 도움을줍니다. 감사합니다. 내가 루퍼를 풀면, 내가 원하는 반지름을 흐리게 할 것입니다! 1024 반경은 펼쳐질 수 없습니다. 또는 병렬로 병렬로 사용하는 방법이 있습니까? –

+0

오른쪽! 반경이 큰 루프를 푸는 것은 실용적이지 않습니다. 그러나 큰 반경의 경우 시도해 볼 수있는 또 다른 트릭이 있습니다. 작은 반경의 여러 번 흐리게 처리하면 큰 반경의 단일 흐림 효과와 동일합니다. 예를 들어 http://computergraphics.stackexchange.com/questions/256/is-doing-multiple-gaussian-blurs-the-same-as-doing-one-large-blur를 참조하십시오. 그래서 반경 R = 1 번으로 흐려지는 이미지는 반경 R/2로 4 번 번짐. 더 작은보다 효율적인 블러로부터 큰 블러를 구성하기 위해이 속성을 사용할 수 있습니다. 실제로 테스트가 더 빠르지 만 테스트가 필요합니다. – monoeci

+0

좋은 생각입니다. 나는이 방법을 android.i의 jni와 함께 사용하기 전에 다음에 관한 기사를 찾는다 : 가우스 흐림은 3 개의 상자 흐림 (웹 : http : //blog.ivank.net/fastest-gaussian-blur.html)과 동일하다. 선형 계산, 나는 그것을 병렬 계산에서 시도 할 것이다. –