En traitement d'image , un noyau , une matrice de convolution ou un masque est une petite matrice utilisée pour le floutage, la netteté, le gaufrage, la détection des contours , etc. Cela est accompli en effectuant une convolution entre le noyau et une image . Ou plus simplement, lorsque chaque pixel de l'image de sortie est une fonction des pixels proches (y compris lui-même) de l'image d'entrée, le noyau est cette fonction.
Détails
L'expression générale d'une convolution est
où est l'image filtrée, est l'image originale, est le noyau du filtre. Chaque élément du noyau du filtre est pris en compte par et .
Selon les valeurs des éléments, un noyau peut provoquer une large gamme d'effets :
Ce qui précède ne sont que quelques exemples d’effets réalisables en convoluant des noyaux et des images.
Origine
L'origine est la position du noyau qui se trouve au-dessus (conceptuellement) du pixel de sortie actuel. Elle peut se trouver à l'extérieur du noyau réel, mais correspond généralement à l'un des éléments du noyau. Pour un noyau symétrique, l'origine est généralement l'élément central.
Convolution

La convolution est le processus consistant à ajouter chaque élément de l'image à ses voisins locaux, pondérés par le noyau. Cela est lié à une forme de convolution mathématique . L'opération matricielle effectuée (la convolution) n'est pas une multiplication matricielle traditionnelle, bien qu'elle soit également désignée par *.
Par exemple, si nous avons deux matrices trois par trois, la première étant un noyau et la seconde une image, la convolution est le processus consistant à inverser les lignes et les colonnes du noyau et à multiplier les entrées localement similaires et à les additionner. L'élément aux coordonnées [2, 2] (c'est-à-dire l'élément central) de l'image résultante serait une combinaison pondérée de toutes les entrées de la matrice d'image, avec des poids donnés par le noyau :
Les autres entrées seraient pondérées de la même manière, où nous positionnerions le centre du noyau sur chacun des points limites de l'image et calculerions une somme pondérée.
Les valeurs d'un pixel donné dans l'image de sortie sont calculées en multipliant chaque valeur de noyau par les valeurs de pixels de l'image d'entrée correspondante. Cela peut être décrit algorithmiquement avec le pseudo-code suivant :
pour chaque ligne d'image dans l'image d'entrée : pour chaque pixel dans la ligne d'image : mettre l'accumulateur à zéro pour chaque ligne du noyau : pour chaque élément de la ligne du noyau : si la position de l'élément correspond* à la position du pixel alors multipliez la valeur de l'élément correspondant* à la valeur du pixel ajoutez le résultat à l'accumulateur fin si définir le pixel de l'image de sortie sur l'accumulateur
- *les pixels de l'image d'entrée correspondants sont trouvés par rapport à l'origine du noyau.
Si le noyau est symétrique, placez le centre (origine) du noyau sur le pixel actuel. Le noyau chevauchera les pixels voisins autour de l'origine. Chaque élément du noyau doit être multiplié par la valeur du pixel avec lequel il chevauche et toutes les valeurs obtenues doivent être additionnées. Cette somme résultante sera la nouvelle valeur du pixel actuel actuellement chevauché par le centre du noyau.
Si le noyau n'est pas symétrique, il doit être retourné autour de son axe horizontal et vertical avant de calculer la convolution comme ci-dessus.
La forme générale de la convolution matricielle est
Manipulation des bords

La convolution du noyau nécessite généralement des valeurs provenant de pixels situés en dehors des limites de l'image. Il existe plusieurs méthodes pour gérer les contours de l'image.
- Étendre
- Les pixels de bordure les plus proches sont étendus conceptuellement autant que nécessaire pour fournir des valeurs pour la convolution. Les pixels d'angle sont étendus en segments de 90°. Les autres pixels de bord sont étendus en lignes.
- Envelopper
- L'image est conceptuellement enveloppée (ou carrelée) et les valeurs sont extraites du bord ou du coin opposé.
- Miroir
- L'image est conceptuellement reflétée sur les bords. Par exemple, si vous essayez de lire un pixel situé à 3 unités à l'extérieur d'un bord, vous en lirez un situé à 3 unités à l'intérieur du bord.
- Recadrer / Éviter les chevauchements
- Tout pixel de l'image de sortie qui nécessiterait des valeurs au-delà du bord est ignoré. Cette méthode peut entraîner une image de sortie légèrement plus petite, les bords ayant été rognés. Déplacez le noyau de sorte que les valeurs extérieures à l'image ne soient jamais requises. L'apprentissage automatique utilise principalement cette approche. Exemple : taille du noyau 10x10, taille de l'image 32x32, image résultante 23x23.
- Récolte de noyaux
- Tout pixel du noyau qui s'étend au-delà de l'image d'entrée n'est pas utilisé et la normalisation est ajustée pour compenser.
- Constante
- Utiliser une valeur constante pour les pixels situés à l'extérieur de l'image. On utilise généralement du noir ou parfois du gris. En général, cela dépend de l'application.
Normalisation
La normalisation est définie comme la division de chaque élément du noyau par la somme de tous les éléments du noyau, de sorte que la somme des éléments d'un noyau normalisé soit égale à un. Cela garantit que le pixel moyen de l'image modifiée est aussi brillant que le pixel moyen de l'image d'origine.
Optimisation
Les algorithmes de convolution rapides incluent :
- convolution séparable
Convolution séparable
La convolution 2D avec un noyau M × N nécessite M × N multiplications pour chaque échantillon (pixel). Si le noyau est séparable, le calcul peut être réduit à M + N multiplications. L'utilisation de convolutions séparables peut réduire considérablement le calcul en effectuant deux fois la convolution 1D au lieu d'une seule convolution 2D.
Mise en œuvre
Voici une implémentation concrète de convolution réalisée avec le langage de shading GLSL :
// auteur : csblo // Travail réalisé simplement en consultant : // https://en.wikipedia.org/wiki/Kernel_(image_processing) // Définir les noyaux #définir l'identité mat3(0, 0, 0, 0, 1, 0, 0 , 0, 0) #définir le bord0 mat3(1, 0, -1, 0, 0, 0, -1, 0 , 1) #définir le bord1 mat3(0, 1, 0, 1, -4, 1, 0, 1, 0) #définir le bord2 mat3(-1, -1, -1, -1, -1, 8, -1, -1, -1, -1) #définir la netteté mat3(0, -1, 0, -1, 5, -1, 0, -1, 0) #définir le flou de boîte mat3(1, 1, 1, 1, 1, 1, 1, 1) * 0.1111 #définir le flou gaussien mat3(1, 2, 1, 2, 4, 2, 1, 2, 1) * 0,0625 #define emboss mat3(-2, -1, 0, -1, 1, 1, 0, 1, 2) // Trouver les coordonnées de l'élément de matrice à partir de l'index vec2 kpos ( int index ) { return vec2 [ 9 ] ( vec2 ( - 1 , - 1 ), vec2 ( 0 , - 1 ) , vec2 ( 1 , - 1 ) , vec2 ( - 1 , 0 ) , vec2 ( 0 , 0 ) , vec2 ( 1 , 0 ) , vec2 ( - 1 , 1 ) , vec2 ( 0 , 1 ) , vec2 ( 1 , 1 ) )[ index ] / iResolution . xy ; } // Extraire la région de dimension 3x3 de l'échantillonneur centré sur uv // échantillonneur : échantillonneur de texture // uv : coordonnées actuelles sur l'échantillonneur // retour : un tableau de mat3, chaque index correspondant à un canal de couleur mat3 [ 3 ] region3x3 ( sampler2D sampler , vec2 uv ) { // Créer chaque pixel pour la région vec4 [ 9 ] region ; for ( int i = 0 ; i < 9 ; i ++ ) region [ i ] = texture ( sampler , uv + kpos ( i )); // Créer une région 3x3 avec 3 canaux de couleur (rouge, vert, bleu) mat3 [ 3 ] mRegion ; for ( int i = 0 ; i < 3 ; i ++ ) mRegion [ i ] = mat3 ( region [ 0 ][ i ], region [ 1 ][ i ], region [ 2 ][ i ], region [ 3 ][ i ], region [ 4 ][ i ], region [ 5 ][ i ], region [ 6 ][ i ], region [ 7 ][ i ], region [ 8 ][ i ] ); return mRegion ; } // Convolution d'une texture avec le noyau // noyau : noyau utilisé pour la convolution // échantillonneur : échantillonneur de texture // uv : coordonnées actuelles sur l'échantillonneur vec3 convolution ( noyau mat3 , sampler2D échantillonneur , vec2 uv ) { vec3 fragment ; // Extraire une région 3x3 centrée sur uv mat3 [ 3 ] region = region3x3 ( sampler , uv ); // pour chaque canal de couleur de la région for ( int i = 0 ; i < 3 ; i ++ ) { // obtenir le canal de région mat3 rc = region [ i ]; // multiplication composant par composant du noyau par le canal de région mat3 c = matrixCompMult ( noyau , rc ); // ajouter chaque composant de la matrice float r = c [ 0 ][ 0 ] + c [ 1 ][ 0 ] + c [ 2 ][ 0 ] + c [ 0 ][ 1 ] + c [ 1 ][ 1 ] + c [ 2 ] [ 1 ] + c [ 0 ][ 2 ] + c [ 1 ][ 2 ] + c [ 2 ][ 2 ]; // pour le fragment sur le canal i, définir le résultat fragment [ i ] = r ; } return fragment ; } void mainImage ( out vec4 fragColor , in vec2 fragCoord ) { // Coordonnées de pixels normalisées (de 0 à 1) vec2 uv = fragCoord / iResolution . xy ; // Convolution du noyau avec la texture vec3 col = convolution ( emboss , iChannel0 , uv ); // Sortie sur l'écran fragColor = vec4 ( col , 1.0 ); }