2016-09-05 1 views
0

J'utilise Renderscript pour faire le flou gaussien sur une image. mais peu importe ce que j'ai fait. le ScriptIntrinsicBlur est plus rapide. pourquoi cela est-il arrivé? ScriptIntrinsicBlur utilise une autre méthode? cet identifiant mon code RS:pourquoi ScriptIntrinsicBlur est plus rapide que ma méthode?

#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. Comment faisiez-vous vos tests. 2. Sur quel matériel/émulateur testez-vous. 3. Si sur un périphérique - pensez que ODM peut implémenter ScriptIntrinsics avec des ressources matérielles supplémentaires non disponibles pour les développeurs d'applications. –

+0

je teste dans un vrai téléphone par une image (293x220). ma méthode a coûté environ 120ms –

+0

quelle est la moyenne de ODM? je teste dans un vrai téléphone par une image (293x220), le rayon de flou est 20. ma méthode a coûté environ 120ms. le ScriptIntrinsicBlur coûtait environ 25 ms. J'ai trouvé que la méthode copyTo() coûtait trop de temps (ScriptIntrinsicBlur utilise aussi la méthode, mais cela ne coûte que peu de temps). par la façon où puis-je trouver le code source RS sur le ScriptIntrinsicBlur? –

Répondre

2

intrinsics renderScript sont mises en œuvre de façon très différente de ce que vous pouvez obtenir avec un script de votre propre. C'est pour plusieurs raisons, mais principalement parce qu'ils sont construits par le développeur de pilotes RS de périphériques individuels d'une manière qui permet la meilleure utilisation possible de cette configuration matérielle/SoC particulière, et fait probablement des appels de bas niveau au matériel qui est simplement non disponible sur la couche de programmation RS.

Cependant, Android fournit une implémentation générique de ces intrinsèques, pour faire un peu de "retombée" dans le cas où aucune implémentation matérielle inférieure n'est disponible. Voir comment ces génériques sont faits vous donnera une meilleure idée de la façon dont ces intrinsèques fonctionnent. Par exemple, vous pouvez voir le code source de l'implémentation générique de la convolution 3x3 intrinsèque ici rsCpuIntrinsicConvolve3x3.cpp. Regardez de plus près le code à partir de la ligne 98 de ce fichier source, et notez comment ils utilisent non pour les boucles que ce soit pour faire la convolution. Ceci est connu sous le nom de boucles déroulées, où vous ajoutez et multipliez explicitement les 9 emplacements de mémoire correspondants dans le code, évitant ainsi la nécessité d'une structure de boucle for. C'est la première règle à prendre en compte lors de l'optimisation du code parallèle. Vous devez vous débarrasser de toutes les branches dans votre noyau. En regardant votre code, vous avez beaucoup de if et for qui causent des ramifications - cela signifie que le flux de contrôle du programme n'est pas direct du début à la fin.

Si vous développez vos boucles for, vous verrez immédiatement une amélioration des performances. Notez qu'en supprimant les structures for, vous ne pourrez plus généraliser votre noyau pour tous les rayons possibles. Dans ce cas, vous devrez créer des noyaux fixes pour des rayons différents, et c'est exactement pourquoi vous voyez des intrinsèques de convolution séparées 3x3 et 5x5, parce que c'est exactement ce qu'ils font. (Voir ligne 99 de l'intrinsèque 5x5 à rsCpuIntrinsicConvolve5x5.cpp).

En outre, le fait que vous ayez deux noyaux distincts n'aide pas. Si vous faites un flou gaussien, le noyau convolutif est en effet séparable et vous pouvez faire des circonvolutions 1xN + Nx1 comme vous l'avez fait, mais je vous recommande de mettre les deux passes ensemble dans le même noyau. Gardez à l'esprit cependant que même ces astuces ne vous donneront probablement pas des résultats aussi rapides que les intrinsèques réels, car ils ont probablement été hautement optimisés pour votre appareil spécifique.

+0

merci beaucoup. Votre réponse me donne beaucoup d'aide. Merci. En passant, si je dérouler le looper alors je flouterais le rayon que je voulais! le rayon de 1024 n'est pas déroulé. ou je utilise parallèle dans un parallèle est-il une méthode? –

+0

Droit! Faire des boucles déroulées pour un grand rayon n'est pas pratique. Cependant, pour un grand rayon, il existe une autre astuce que vous pouvez essayer: Les flous multiples d'un rayon plus petit sont équivalents à un seul flou de grand rayon. Voir par exemple: http://computergraphics.stackexchange.com/questions/256/is-doing-multiple-gaussian-blurs-the-same-as-doing-one-larger-blur. Donc une image est floue une fois avec le rayon R = flou 4 fois avec le rayon R/2. Vous pourriez être en mesure d'utiliser cette propriété pour composer de grands flous à partir de petits flous plus efficaces. Vous devrez faire quelques tests pour voir si c'est en fait plus rapide ... – monoeci

+0

c'est une super idée. J'ai utilisé cette méthode avant avec jni dans android.i trouver un article sur: un flou gaussien est égal à trois boîte de flou (web: http://blog.ivank.net/fastest-gaussian-blur.html) .il fonctionne dans un calcul linéaire, je vais l'essayer dans un calcul parallèle. –