7
votes

Traiter avec des conditions de frontière / des régions d'halo à Cuda

Je travaille sur le traitement de l'image avec Cuda et j'ai un doute sur le traitement des pixels.

ce qui est souvent fait avec les pixels limites d'une image lors de l'application d'un fichier m x m filtre de convolution?

dans un 3 x 3 Le noyau de convolution, ignorant la limite 1 de l'image de l'image est plus facile à traiter, en particulier lorsque le code est amélioré avec la mémoire partagée. En effet, dans ce cas, il n'est pas nécessaire de vérifier si un pixel donné a tout le Néighourhood disponible (c'est-à-dire Pixel à Coord (0, 0) N'a pas laissé, gauche-majuscule, voisins supérieurs). Cependant, supprimer la limite de pixel de l'image d'origine pourrait générer des résultats partiels.

opposé à cela, je voudrais traiter tous les pixels de l'image, également lorsque vous utilisez des améliorations de mémoire partagées, c'est-à-dire, par exemple, le chargement 16 x 16 pixels, mais calculer l'intérieur 14 x 14 . Également dans ce cas, ignorer les pixels limites génère un code plus clair.

ce qui est généralement fait dans ce cas?

Est-ce que quelqu'un utilise habituellement mon approche ignorant les pixels de frontière?

Bien sûr, je suis au courant que la réponse dépend du type de problème, c'est-à-dire ajouter deux images pixel-sage n'a pas ce problème.

Merci d'avance.


0 commentaires

3 Réponses :


12
votes

Une approche commune pour traiter les effets de la frontière consiste à poser l'image d'origine avec des lignes et des colonnes supplémentaires en fonction de votre taille de filtre. Certains choix communs pour les valeurs rembourrées sont:

  • une constante (par exemple zéro)
  • Réplique la première et la dernière ligne / colonne autant de fois que nécessaire
  • reflète l'image aux frontières (E.G. colonne [-1] = colonne [1], colonne [-2] = Colonne [2])
  • enveloppez les valeurs d'image (E.g. colonne [-1] = colonne [largeur-1], colonne [-2] = colonne [largeur-2])

4 commentaires

Je vois. Il nécessite un effort supplémentaire dans tous les cas, comme un pré-traitement pour coussiner l'image (il nécessite une mémoire réallant). Il est probablement plus efficace simuler le rembourrage supplémentaire lors du chargement de données dans une mémoire partagée «étendue» (I.E. Chargez 18x18 pixels pour calculer 16x16). Je travaille pour le moment avec cette approche. Merci pour vos suggestions.


PQB Si vous utilisez la mémoire de la texture du GPU fournira et gérera ces conditions de limite pour vous, plus la mémoire de texture est plus rapide.


C'est Rigt Fabriziom. C'est le cas de forcer les données dans la limite. Mais dans d'autres types de problèmes, je pourrais également stocker les résultats et les réutiliser dans un autre appel de noyau. Dans ce cas, je ne pouvais pas utiliser la mémoire de texture pour stocker les résultats. Je lirais quelque chose à propos de la mémoire de la surface mais je ne l'utilise jamais. (La convolution était un exemple de créer un contexte pour la question :). Merci pour votre allusion.


Existe-t-il un résumé pour les propriétés des différents choix?



5
votes

TL; DR: Cela dépend du problème que vous essayez de résoudre - il n'y a pas de solution pour cela qui s'applique à tous les problèmes. En fait, mathématiquement parler, je soupçonne qu'il ne peut y avoir aucune "solution" puisque je crois que c'est un problème mal posé, vous êtes obligé de traiter.

(excuses d'avance pour mon abus imprudent de mathématiques)

Démontrer Considérons une situation où toutes les valeurs de composants de pixels et de noyau sont supposées être positives. Pour avoir une idée de la façon dont certaines de ces réponses pourraient nous égarer, pensons en outre à un filtre simple en moyenne ("boîte"). Si nous définissons des valeurs en dehors de la limite de l'image à zéro, cela glissera clairement la moyenne à chaque pixel au sein du CELL (N / 2) (distance de Manhattan) de la limite. Vous obtiendrez donc une bordure «sombre» sur votre image filtrée (en supposant une composante d'intensité unique ou un espace de courant RVB - vos résultats varieront par Colorspace!). Notez que des arguments similaires peuvent être faits si nous définissons les valeurs en dehors de la limite sur une constante arbitraire - la moyenne tendra vers cette constante. Une constante de zéro peut être appropriée si les bords de votre image typique tendent vers 0 de toute façon. Ceci est également vrai si nous considérons un filtre plus complexe les noyaux comme un gaussien, mais le problème sera moins prononcé, car les valeurs du noyau tendent à diminuer rapidement avec la distance du centre.

maintenant Supposons qu'au lieu d'utiliser une constante, nous choisissons de répéter les valeurs de bord. C'est la même chose que de faire une frontière autour de l'image et de copier des lignes, des colonnes ou des angles suffisamment de temps pour assurer que le filtre reste "à l'intérieur" de la nouvelle image. Vous pouvez également y penser comme serrant / saturation des coordonnées des échantillons. Cela pose des problèmes avec notre filtre de boîte simple car il dépassait les valeurs des pixels de bord. Un ensemble de pixels de bord apparaîtra plus d'une fois qu'ils recevront tous le même poids w = (1 / (n * n)) . Supposons que nous échantillonnons un pixel de bord avec une valeur K 3 fois. Cela signifie que sa contribution à la moyenne est la suivante: xxx

si efficacement qu'un pixel a un poids plus élevé dans la moyenne. Notez que, étant donné qu'il s'agit d'un filtre moyen, le poids est une constante sur le noyau. Cependant, cet argument s'applique aux noyaux avec des poids qui varient également en position également (à nouveau: Pensez au noyau gaussien ..).

Supposons que nous envisageons ou reflètent les coordonnées d'échantillonnage afin que nous utilisions toujours des valeurs de l'intérieur la limite de l'image. Cela a des avantages précieux sur l'utilisation d'une constante mais n'est pas nécessairement "correcte" non plus. Par exemple, combien de photos prenez-vous où les objets à la bordure supérieure sont similaires à ceux situés en bas? Sauf si vous prenez des photos de lacs lisses miroirs, je doute que cela soit vrai. Si vous prenez des photos de roches à utiliser, car les textures dans les jeux d'emballage ou de réflexion peuvent être appropriées. Je suis sûr qu'il y a des points significatifs à faire ici sur la manière dont l'emballage et le réflexion réduiront probablement tous les artefacts résultant d'une transformation de Fourier. Cependant, cela revient à la même idée: que vous avez un signal périodique que vous ne souhaitez pas déformer en introduisant de nouvelles fréquences parasites ou en surestimant l'amplitude des fréquences existantes.

Alors que pouvez-vous faire si vous " Réfliger des photos de roches rouges brillantes sous un ciel bleu? Il est clair que vous ne voulez pas ajouter de brume d'orange-ish dans le ciel bleu et le fuzz bleu-ish sur les rochers rouges. Reflétant les échantillons de coordonnées fonctionne parce que nous nous attendons à des couleurs similaires à ces pixels trouvés dans les coordonnées réfléchies ... À moins que, juste pour l'argument, nous imaginons que le noyau du filtre est si gros que la coordonnée réfléchie dépasserait l'horizon.

Revenons à l'exemple du filtre de la boîte. Une alternative avec ce filtre consiste à arrêter de penser à utiliser un noyau statique et à repenser à ce que ce noyau était censé faire. Un filtre à la moyenne / boîte est conçu pour résumer les composants de pixels puis se diviser par le nombre de pixels résumés. L'idée est que cela lisse le bruit. Si nous sommes prêts à négocier une efficacité réduite dans la suppression du bruit près de la limite, nous pouvons simplement résumer moins de pixels et se diviser par un nombre plus petit correspondant. Cela peut être étendu aux filtres avec des termes similaires de termes de normalisation «normalisation» qui sont liés à la zone ou au volume du filtre. Pour les termes «zone», vous comptez le nombre de poids de noyau situés dans la limite et ignorez ces poids qui ne sont pas. Ensuite, utilisez ce nombre comme «zone» (ce qui pourrait impliquer une multiplication supplémentaire). Pour le volume (à nouveau: en supposant des poids positifs!) Sumez simplement les poids du noyau. Cette idée est probablement affreuse pour les filtres dérivés car il y a moins de pixels pour concurrencer les pixels bruyants et les différentiels sont notoirement sensibles au bruit. En outre, certains filtres ont été dérivés d'optimisation numérique et / ou de données empiriques plutôt que des méthodes d'AB-initio / analytique et peuvent donc manquer un facteur "normalisation" "normalisé" facilement apparent.


0 commentaires

1
votes

Votre question est quelque peu large et je crois qu'il mélange deux problèmes:

  1. traiter avec conditions limites forte>; li>
  2. traiter avec des régions halo fortes>. li> OL>

    Le premier problème ( Conditions de limites strong>) est rencontrée, par exemple, lorsqu'il est calculé lors de l'informatique de la convolution entre et l'image et l'image 3 x 3 code> noyau. Lorsque la fenêtre de convolution apparaît sur la frontière, on a le problème de l'extension de l'image en dehors de ses limites. P>

    Le deuxième problème ( Les régions halo forte>) sont rencontrées, par exemple, lorsque Chargement d'un 16 x 16 CODE> DIREL dans la mémoire partagée et il faut traiter la tuile interne 14 x 14 code> pour calculer des dérivés de second ordre. p>

    pour la seconde question, je pense que je pense qu'une question utile est la suivante: analysement de la mémoire de la mémoire de la mémoire de mon noyau de Cuda . p>

    concernant l'extension d'un signal en dehors de ses limites, un outil utile est fourni dans ce cas par la mémoire de texture em> grâce à l'adressage différent fourni Modes, voir Les différents modes d'adressage des textures CUDA . P >

    ci-dessous, je fournis un exemple sur ho W Un filtre médian peut être mis en oeuvre avec des conditions de limites périodiques à l'aide de la mémoire de texture. P>

    #include <stdio.h>
    
    #include "TimingGPU.cuh"
    #include "Utilities.cuh"
    
    texture<float, 1, cudaReadModeElementType> signal_texture;
    
    #define BLOCKSIZE 32
    
    /*************************************************/
    /* KERNEL FUNCTION FOR MEDIAN FILTER CALCULATION */
    /*************************************************/
    __global__ void median_filter_periodic_boundary(float * __restrict__ d_vec, const unsigned int N){
    
        unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
    
        if (tid < N) {
    
            float signal_center = tex1D(signal_texture, tid - 0);
            float signal_before = tex1D(signal_texture, tid - 1);
            float signal_after  = tex1D(signal_texture, tid + 1);
    
            printf("%i %f %f %f\n", tid, signal_before, signal_center, signal_after);
    
            d_vec[tid] = (signal_center + signal_before + signal_after) / 3.f;
    
        }
    }
    
    
    /********/
    /* MAIN */
    /********/
    int main() {
    
        const int N = 10;
    
        // --- Input host array declaration and initialization
        float *h_arr = (float *)malloc(N * sizeof(float));
        for (int i = 0; i < N; i++) h_arr[i] = (float)i;
    
        // --- Output host and device array vectors
        float *h_vec = (float *)malloc(N * sizeof(float));
        float *d_vec;   gpuErrchk(cudaMalloc(&d_vec, N * sizeof(float)));
    
        // --- CUDA array declaration and texture memory binding; CUDA array initialization
        cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
        //Alternatively
        //cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);
    
        cudaArray *d_arr;   gpuErrchk(cudaMallocArray(&d_arr, &channelDesc, N, 1));
        gpuErrchk(cudaMemcpyToArray(d_arr, 0, 0, h_arr, N * sizeof(float), cudaMemcpyHostToDevice));
    
        cudaBindTextureToArray(signal_texture, d_arr); 
        signal_texture.normalized = false; 
        signal_texture.addressMode[0] = cudaAddressModeWrap;
    
        // --- Kernel execution
        median_filter_periodic_boundary<<<iDivUp(N, BLOCKSIZE), BLOCKSIZE>>>(d_vec, N);
        gpuErrchk(cudaPeekAtLastError());
        gpuErrchk(cudaDeviceSynchronize());
    
        gpuErrchk(cudaMemcpy(h_vec, d_vec, N * sizeof(float), cudaMemcpyDeviceToHost));
    
        for (int i=0; i<N; i++) printf("h_vec[%i] = %f\n", i, h_vec[i]);
    
        printf("Test finished\n");
    
        return 0;
    }
    


0 commentaires