2016-09-05 3 views
0

Ich verwende das Renderscript, um die Gaußsche Unschärfe auf einem Bild zu machen. aber egal was ich getan habe. Die ScriptIntrinsicBlur ist mehr schneller. Warum ist das passiert? ScriptIntrinsicBlur verwendet eine andere Methode? diese id meine RS-Code:Warum ist ScriptIntrinsicBlur schneller als meine Methode?

#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. Wie haben Sie Ihre Tests durchgeführt? 2. Auf welcher Hardware/welchem ​​Emulator testen Sie? 3. Wenn auf dem Gerät - berücksichtigen Sie, dass ODM ScriptIntrinsics mit zusätzlichen Hardwareressourcen implementieren kann, die App-Entwicklern nicht zur Verfügung stehen. –

+0

Ich teste in einem echten Telefon nach einem Bild (293x220). meine Methode kostet etwa 120ms –

+0

was ist der Mittelwert von ODM? Ich teste in einem echten Telefon nach einem Bild (293x220), Blur Radius ist 20. Meine Methode kostet etwa 120ms. Die ScriptIntrinsicBlur kostete etwa 25 ms. Ich fand, dass die Methode copyTo() zu viel Zeit kostete (ScriptIntrinsicBlur verwendet die Methode ebenfalls, aber es kostet wenig Zeit). nebenbei, wo finde ich den RS Quellcode über die ScriptIntrinsicBlur? –

Antwort

2

render intrinsics sind sehr unterschiedlich umgesetzt von dem, was Sie mit einem Skript des eigenen erreichen können. Dies ist aus verschiedenen Gründen, aber vor allem, weil sie vom RS-Treiber-Entwickler von einzelnen Geräten auf eine Weise gebaut werden, die diese bestimmte Hardware/SoC-Konfiguration bestmöglich nutzt, und höchstwahrscheinlich Anrufe auf niedriger Ebene an die Hardware macht, die einfach ist nicht verfügbar auf der RS-Programmierschicht.

Android bietet jedoch eine generische Implementierung dieser intrinsics, um "Fallback" zu sortieren, falls keine niedrigere Hardware-Implementierung verfügbar ist. Wenn Sie sehen, wie diese allgemeinen Informationen erstellt werden, erhalten Sie eine bessere Vorstellung davon, wie diese Eigen- schaften funktionieren. Zum Beispiel können Sie den Quellcode der generischen Implementierung der 3x3-Faltung hier intrinsisch sehen rsCpuIntrinsicConvolve3x3.cpp.

Schauen Sie sich den Code ab Zeile 98 dieser Quelldatei sehr genau an und beachten Sie, wie sie no for loops für die Faltung verwenden. Dies wird als entrollte Schleifen bezeichnet, bei denen Sie die 9 entsprechenden Speicherstellen im Code explizit hinzufügen und multiplizieren, wodurch die Notwendigkeit einer for-Schleifenstruktur vermieden wird. Dies ist die erste Regel, die Sie bei der Optimierung von parallelem Code berücksichtigen müssen. Sie müssen alle Verzweigungen in Ihrem Kernel loswerden. Wenn Sie Ihren Code betrachten, haben Sie eine Menge von if 's und for' s, die Verzweigung verursachen - das bedeutet, dass der Kontrollfluss des Programms nicht durchgehend von Anfang bis Ende ist.

Wenn Sie Ihre for-Schleifen ausrollen, sehen Sie sofort eine Leistungssteigerung. Beachten Sie, dass Sie Ihren Kernel nicht mehr für alle möglichen Radiusbeträge verallgemeinern können, wenn Sie Ihre für Strukturen entfernen. In diesem Fall müssten Sie feste Kernel für verschiedene Radien erstellen, und dies ist genau, warum Sie separate 3x3 und 5x5 Faltungs-Intrinsics sehen, weil dies genau das ist, was sie tun. (Siehe Zeile 99 des 5x5-Intrinsic bei rsCpuIntrinsicConvolve5x5.cpp).

Darüber hinaus hilft die Tatsache, dass Sie zwei separate Kernel haben, nicht. Wenn Sie eine Gauß'sche Unschärfe machen, ist der Faltungskern tatsächlich trennbar und Sie können 1xN + Nx1 Faltungen tun, wie Sie es dort getan haben, aber ich würde empfehlen, beide Pässe im selben Kernel zusammen zu setzen.

Bedenken Sie jedoch, dass selbst das Ausführen dieser Tricks Ihnen wahrscheinlich immer noch nicht so schnelle Ergebnisse liefert wie die tatsächlichen Intrinsics, da diese wahrscheinlich für Ihr spezifisches Gerät (en) optimiert wurden.

+0

Vielen Dank. Ihre Antwort gibt mir eine Menge Hilfe.Vielen Dank. Wenn ich den Looper ausrollen würde, würde ich den Radius, den ich wollte, nicht verwischen! Der Radius von 1024 wird nicht abgerollt. oder ich benutze parallel in einer parallelen gibt es irgendeine methode? –

+0

Richtig! Gerollte Schleifen für einen großen Radius zu machen, ist nicht praktikabel. Für einen großen Radius gibt es jedoch noch einen Trick, den Sie ausprobieren können: Mehrere Unschärfen mit einem kleineren Radius entsprechen einer einzelnen Unschärfe mit einem großen Radius. Siehe zum Beispiel: http://computergraphics.stackexchange.com/questions/256/is-doing-multiple-gaussian-blurs-the-same-as-doing-one-larger-blur. So ein Bild verwischt einmal mit Radius R = 4 Mal mit Radius R/2 verwischen. Sie können diese Eigenschaft möglicherweise verwenden, um große Unschärfen aus kleineren effizienteren Unschärfen zu erstellen. Sie müssen einige Tests durchführen, um zu sehen, ob es tatsächlich schneller ist ... – monoeci

+0

es ist eine großartige Idee. ich habe diese Methode vorher mit jni in android.i verwendet, um einen Artikel über zu finden: eine Gaußsche Unschärfe entspricht drei Kastenunschärfe (Netz: http: //blog.ivank.net/fastest-gaussian-blur.html) .es arbeitet in a lineare Berechnung, ich werde es in einer parallelen Berechnung versuchen. –

Verwandte Themen