2016-09-05 5 views
0

Я использую Renderscript, чтобы сделать гауссовское размытие на изображении. , но независимо от того, что я сделал. ScriptIntrinsicBlur работает быстрее. почему это произошло? ScriptIntrinsicBlur использует другой метод? этот идентификатор мой код 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

i тест в реальном телефоне по изображению (293x220). мой метод стоит около 120 мс –

+0

Что такое среднее ODM? Я тестирую на реальном телефоне изображение (293x220), радиус размытия - 20. Мой метод стоит около 120 мс. ScriptIntrinsicBlur стоит около 25 мс. i обнаружил, что метод copyTo() стоит слишком много времени (ScriptIntrinsicBlur тоже использует этот метод, но это требует немного времени). кстати, где я могу найти исходный код RS о ScriptIntrinsicBlur? –

ответ

2

Renderscript реализованы встроенные функции очень отличается от того, что вы можете достичь с помощью сценария самостоятельно. Это происходит по нескольким причинам, но главным образом потому, что они созданы разработчиком драйверов RS для отдельных устройств таким образом, который наилучшим образом использует эту конкретную конфигурацию оборудования/SoC и, скорее всего, вызывает низкоуровневые вызовы на аппаратное обеспечение, которое просто недоступны на уровне программирования RS.

Android действительно обеспечивает общую реализацию этих свойств, хотя, чтобы отчасти «отступить», если не будет доступной более низкая аппаратная реализация. Увидев, как эти общие действия будут сделаны, вы получите некоторое представление о том, как работают эти внутренние функции. Например, вы можете увидеть исходный код общей реализации встроенной свертки 3x3 здесь rsCpuIntrinsicConvolve3x3.cpp.

Посмотрите на код, начинающийся с строки 98 этого исходного файла, и обратите внимание, как они используют no для циклов, что бы сделать свертку. Это известно как развернутые циклы, где вы добавляете и умножаете явно 9 соответствующих мест памяти в коде, тем самым избегая необходимости в структуре цикла цикла. Это первое правило, которое вы должны учитывать при оптимизации параллельного кода. Вам нужно избавиться от всех ветвлений в вашем ядре. Глядя на ваш код, у вас много if и for, которые вызывают ветвление - это означает, что поток управления программой не проходит прямо от начала до конца.

Если вы разворачиваете свои петли, вы сразу увидите повышение производительности. Обратите внимание, что, удалив ваши структуры, вы больше не сможете обобщать свое ядро ​​для всех возможных размеров радиуса. В этом случае вам нужно будет создавать фиксированные ядра для разных радиусов, и это точно, почему вы видите отдельные встроенные функции свертки 3x3 и 5x5, потому что это именно то, что они делают. (См. Строку 99 5x5, относящуюся к rsCpuIntrinsicConvolve5x5.cpp).

Кроме того, тот факт, что у вас есть два отдельных ядра, не помогает. Если вы делаете гауссовское размытие, сверточное ядро ​​действительно разделяется, и вы можете сделать свертки 1xN + Nx1, как вы это делали, но я бы рекомендовал объединить оба прохода в одном ядре.

Имейте в виду, что даже выполнение этих трюков, вероятно, еще не даст вам столь же быстрых результатов, как и фактические внутренние функции, поскольку они, вероятно, были оптимизированы для вашего конкретного устройства (ов).

+0

Большое вам спасибо. ваш ответ даст мне много помощи. Спасибо. Если я разберу петлитель, тогда я бы размыл радиус, который мне нужен! радиус 1024 будет развернут. или я использую параллельную параллель, есть ли какой-нибудь метод? –

+0

Правильно! Выполнение развернутых петель для большого радиуса нецелесообразно. Однако для большого радиуса есть еще один трюк, который вы можете попробовать: Несколько размывов меньшего радиуса эквивалентны одному размытию большого радиуса. См. Например: http://computergraphics.stackexchange.com/questions/256/is-doing-multiple-gaussian-blurs-the-same-as-doing-one-larger-blur. Таким образом, изображение размыто один раз с радиусом R = размытие 4 раза с радиусом R/2. Возможно, вы сможете использовать это свойство, чтобы создавать большие размытия при меньших более эффективных размытиях. Вам нужно будет сделать некоторые тесты, чтобы убедиться, что это действительно быстрее, хотя ... – monoeci

+0

это отличная идея. Я использую этот метод до того, как jni в android.i найду статью о: гауссовское размытие равно трем размытиям (web: http: //blog.ivank.net/fastest-gaussian-blur.html) .it работает в линейный вычислять, я попробую его в параллельном вычислении. –

 Смежные вопросы

  • Нет связанных вопросов^_^