2009-06-21 5 views
4

J'essaye d'effectuer un filtre Médian sur une image en Java mais c'est terriblement lent. Premièrement, si l'un d'entre vous connaît une implémentation autonome, je pourrais l'utiliser serait fantastique si vous pouviez me le faire savoir. J'applique sur Android, en essayant de reproduire une petite partie de la JAI.Obtenir des octets à partir d'un int pour éviter les changements de bit - Java (Filtrage Médian)

Dans ma méthode, je prends chaque pixel, extraire la R, G & valeurs B à l'aide

r = pixel >> 16 & 0xFF 

ou similaire, trouver la médiane pour le noyau et terminer avec

pixel = a | r <<16 | g << 8 | b 

Y at-il façon que je peux saisir les octets d'un int de telle sorte que ce serait plus rapide?

Cordialement,

Gavin


EDIT: code complet pour aider à diagnostiquer mon manque de performance sur demande

Pour le fichier source réelle s'il vous plaît aller here c'est où ma mise en œuvre de filtre médian peut être trouvé.

largeur et hauteur variables sont de la taille de dest et sont disponibles en tant que variables membres de classe. Les pixels sont linéarisés en un tableau à une dimension.

private void medianFilterSquare(int[] source, int[] dest, int rWidth, 
     int rHeight, int radius) { 
    // Source has been reflected into a border of size radius 
    // This makes it radius * 2 pixels wider and taller than the dest 
    int r,g,b; 
    int destOffset, rOffset, kOffset; 

    // The first offset into the source to calculate a median for 
    // This corresponds to the first pixel in dest 
    int rFirst = radius + (rWidth*radius); 

    // We use a square kernel with the radius passed 
    int neighbours = (radius+radius+1)*(radius+radius+1); 

    int index; 

    // Arrays to accumulate the values for median calculation 
    int[] rs = new int[neighbours]; 
    int[] gs = new int[neighbours]; 
    int[] bs = new int[neighbours]; 

    // Declaring outside the loop helps speed? I'm sure this is done for me 
    // by the compiler 
    int pixel; 

    // Iterate over the destination pixels 
    for(int x = 0; x < height; x++){ 
     for(int y = 0; y < width; y++){ 
      // Offset into destination 
      destOffset = x + (y * width); 
      // Offset into source with border size radius 
      rOffset = destOffset + rFirst + (y * (radius *2)); 

      index = 0; 

      // Iterate over kernel 
      for(int xk = -radius; xk < radius ; xk ++){ 
       for(int yk = -radius; yk < radius ; yk ++){ 
        kOffset = rOffset + (xk + (rWidth*yk)); 
        pixel = source[kOffset]; 
        // Color.red is equivalent to (pixel>>16) & 0xFF 
        rs[index] = Color.red(pixel); 
        gs[index] = Color.green(pixel); 
        bs[index] = Color.blue(pixel); 
        index++; 
       } 
      } 
      r = medianFilter(rs); 
      g = medianFilter(gs); 
      b = medianFilter(bs); 

      dest[destOffset] = Color.rgb(r, g, b); 
     }   
    }  
} 
+0

Le décalage de bits et des opérations logiques devraient être relativement rapide. Êtes-vous sûr que ce n'est pas le code entre ces deux lignes qui est le goulot d'étranglement? – colithium

+0

Comment "trouvez-vous la médiane pour le noyau". Pouvons-nous voir le code? – Nosredna

+0

En fait, vous n'êtes même pas nous montrer comment vous obtenez g, b, ou a. Pourquoi ne pas poster tout le code? – Nosredna

Répondre

5

Comme d'autres l'ont dit, il est possible que c'est le bit entre ce qui est à l'origine du problème. Une chose que je dirais (qui peut être évidente, mais de toute façon) - ne pas simplement profiler l'application sur une machine virtuelle de bureau et supposer que le goulot d'étranglement sera au même endroit. Je ne serais pas du tout surpris de trouver des goulots d'étranglement entièrement différents chez Dalvik.

Est-il possible pour vous de travailler avec les valeurs encore décalées? Par exemple, si vous deviez juste masque pour différentes couleurs:

int r = pixel & 0xff0000; 
int g = pixel & 0xff00; 
int b = pixel & 0xff; 

vous pouvez modifier votre algorithme de traitement en conséquence? Une dernière réflexion: je trouve toujours la préséance des opérateurs de décalage déroutante.Je serais fortement recommandons que d'un point de vue de la lisibilité, vous les crochets:

r = (pixel >> 16) & 0xFF; 
pixel = a | (r <<16) | (g << 8) | b; 

Irrelevant à la performance, mais si je mainteneur je serais certainement reconnaissant :)

+2

+1 pour le bracketing – akarnokd

0

En plus de mon commentaire, j'ai trouvé le code qui effectue un filtre médian en java.

Link Download

1

Le fastet façon d'obtenir vos valeurs r, g, b devrait être

new byte[] { 
    (byte)(value >>> 24), 
    (byte)(value >>> 16), 
    (byte)(value >>> 8), 
    (byte)value 
}; 
+1

Si le temps le plus court dépend de toutes sortes de choses, y compris si le processeur a un shifter de baril ou non. Pour quel processeur est-ce? x86 ou ARM ou autre chose? – Nosredna

1

Se concentrer sur la façon de faire les opérations sur les bits est une distraction. Cela n'a d'importance que si vous effectuez ces opérations car vous traitez inutilement le même pixel encore et encore.

Vous appelez le filtre médian pour chaque pixel trois fois, et vous obtenez plusieurs pixels autour du pixel par pixel. Ce qui signifie que vous faites tout ce travail pour le même pixel plusieurs fois. Vous avez for boucles imbriquées quatre en profondeur!

Si votre rayon est de 5, vous traitez 121 pixels. Ensuite, vous descendez de un et traitez 121 pixels à nouveau, et vous avez déjà converti tous sauf 11 d'entre eux! Vous faites la même chose pour chaque pixel, puis déplacez-vous vers le bon pixel. Pour un rayon de cinq, vous faites deux ordres de grandeur autant de conversions RGB que nécessaire.

Je vous suggère de garder votre image dans ou de convertir votre image pour séparer les tableaux rouges, bleus et verts d'abord.


Si le rayon est grand, vous pouvez garder les sommes rouges, bleus et verts que vous vous déplacez le long, en soustrayant les pixels du haut et en ajoutant les pixels du fond que vous rampez sur le bitmap, mais cela rendrait le code un peu plus compliqué. Que vous ajoutiez du code pour optimiser davantage dépend de vos besoins.


De plus, vous avez un tas de petites choses qui pourraient être optimisées. Je ne suis pas sûr que le compilateur s'en occupe ou non. Par exemple, vous pourriez faire une certaine réduction de la force. Vous n'avez pas besoin des multiplications que vous avez dans les lignes qui calculent neighbors, destOffset, rOffset ou kOffset. L'addition est tout ce dont vous avez besoin pour ceux qui refactorisent un peu le code.

+0

médiocre nommage de ma part, medianFilter devrait juste être appelé médian, il trouve la médiane d'un tableau d'entiers. – gav

+0

Ça n'a pas vraiment d'importance. c'est le fait que vous faites le même traitement sur les mêmes pixels encore et encore. Bien qu'il y ait beaucoup plus de place pour l'amélioration, séparer l'image entière en parties rouges, vertes et bleues est le plus gros poisson ici. – Nosredna

+0

Je suis sûr que vous avez raison, mais j'ai besoin de trouver la médiane des rouges, des verts et des bleus dans l'image pour le noyau de pixels autour de chaque pixel. Je ne vois pas comment je pourrais appeler moins médian, il faut l'appeler 3 fois pour chaque pixel. À moins que je ne manque quelque chose d'évident que j'espère sincèrement que je suis. – gav

0

Vous pouvez de temps en temps sortir avec effectuer des opérations arithmétiques sur les composants bleus & rouges simultanément dans un seul int:

int average(int rgb1, int rgb2) 
{ 
    int rb = (((rgb1 & 0xFF00FF) + (rgb2 & 0xFF00FF)) >> 1) & 0xFF00FF; 
    int g = (((rgb1 & 0xFF00) + (rgb2 & 0xFF00)) >> 1) & 0xFF00; 
    return (rb | g); 
} 

parce que les composants rouges et bleus sont séparés par 8 bits, ils ne nuisent pas à chaque autre.

Je ne l'ai jamais vu une augmentation significative (plus de 5-10%) de ce même speedup.

+1

Même avec 8 bits de séparation, il pourrait commencer à avoir des ennuis si son rayon dépasse 7. Avec un rayon de 8, il aurait 289 voisins à la moyenne. – Nosredna

Questions connexes