Outils pour utilisateurs

Outils du site


issue114:inkscape

This month, we're going to look at the Convolve Matrix filter primitive. Convolution is a mathematical term for a process of repeatedly applying one function to the varying output of another function. In the computing world, it's commonly used with discrete values, rather than continuous ones, as you might get when dealing with sampled audio or, indeed, with individual pixels in an image. So, in digital signal processing, convolution generally means using a function to map a series of values to a new series. In SVG filter terms, that means mapping one set of pixels to a new set. The “function” is defined using a matrix of numbers, hence the filter name is “Convolve Matrix” - although “map pixel values using a matrix” would have perhaps made it a little more understandable to the layman. Let's look at how a convolution matrix actually works by picturing its effect on a simple image made up of a small set of pixels. For this demonstration, we'll use black and white pixels with values of 0 and 255 (with numbers in-between being shades of gray). In a real filter there are three color channels, so our single-channel black-and-white image here is merely a model to represent the calculation process. The shape we'll use is just a 9-pixel square inside a larger 25-pixel square.

Ce mois-ci, nous allons examiner la primitive de filtrage Matrice de convolution. La convolution est le terme mathématique pour le mécanisme d'application répétitive d'une fonction à la sortie variable d'une autre fonction. Dans le monde informatique, elle est habituellement utilisée avec des valeurs discrètes (c'est-à-dire séparées), plutôt qu'avec des valeurs continues, telles que vous pouvez en obtenir quand vous traitez de l'audio échantillonné, ou bien sûr, des pixels individuels d'une image. Aussi, en traitement numérique du signal, la convolution signifie généralement qu'on utilise une fonction pour représenter une série de valeurs dans une nouvelle série. En termes de filtres SVG, cela signifie représenter un ensemble de pixels dans un autre ensemble. La « fonction » est définie par l'utilisation d'une matrice de nombres, d'où le nom « Matrice de convolution » du filtre - bien que l'expression « représentation des valeurs des pixels par l'utilisation d'une matrice » l'aurait peut-être rendue un peu plus compréhensible au néophyte.

Regardons comment fonctionne une matrice de convolution en illustrant son effet sur une image simple faite d'un petit ensemble de pixels. Pour cette démonstration, nous utiliserons des pixels noirs et blancs avec les valeurs 0 et 255 (les nombres intermédiaires étant des niveaux de gris). Dans un vrai filtre, il y a trois canaux de couleurs ; aussi notre image mono-canal en noir et blanc est seulement un modèle pour représenter la logique de calcul. La forme que nous utiliserons sera un simple carré de 9 pixels dans un carré plus grand de 25 pixels.

Our first matrix will be a 3×3 array, with each cell containing the number 1.00, and the “target” specified as the center cell of the matrix. Here's how that looks in the Inkscape GUI: The convolution process itself consists of taking our matrix and positioning it so that the target cell in the matrix is positioned over each pixel in the input image, in turn. We're going to look at the calculation that takes place for the first black pixel in our input image – the one with the red outline. The 9 pixels covered by the matrix are all multiplied by the corresponding value in the matrix cell, then added together. The result is clamped so that it doesn't exceed 255 or drop below 0, and is then used as the value for the output pixel. This image might clarify things a little – the green area represents the 3×3 matrix, with each pixel's contribution to the output shown. The value of the output pixel is therefore: (255×1.00) + (255×1.00) + (255×1.00) + (255×1.00) + (0×1.00) + (0×1.00) + (255×1.00) + (0×1.00) + (0×1.00)

Notre première matrice sera un tableau 3×3, avec le nombre 1.00 dans chaque case, et la « cible » spécifiée comme étant la cellule centrale de la matrice. Voici à quoi ça ressemble dans l'interface utilisateur d'Inkscape :

Le procédé de convolution lui-même consiste à prendre notre matrice et à la positionner de sorte que la cellule cible de la matrice soit tour à tour positionnée au-dessus de chaque pixel de l'image d'entrée. Nous allons regarder le calcul qui a lieu pour le premier pixel noir de l'image d'entrée, celui avec un contour rouge. Les 9 pixels recouverts par la matrice sont tous multipliés par la valeur correspondante de la cellule de la matrice, puis additionnés ensemble. Le résultat est borné, de sorte qu'il ne peut pas excéder 255 ou passer sous 0, et il est utilisé ensuite comme valeur du pixel de sortie. Cette image peut clarifier un peu les choses : la zone verte représente la matrice 3×3, avec une représentation de la contribution de chaque pixel à la sortie.

La valeur du pixel de sortie est donc : (255×1.00) + (255×1.00) + (255×1.00) + (255×1.00) + (0×1.00) + (0×1.00) + (255×1.00) + (0×1.00) + (0×1.00)

It doesn't take much mathematical skill to realise that the five white pixels each contribute a value of 255 to the output, whilst the black pixels contribute nothing. So the value used as the output pixel is just 255×5 = 1,275. Except that the output values are clamped, so the actual output value is just 255 – this matrix has turned the black pixel into a white one. Moving on to the next pixel, we get a similar result. This time there are only three white pixels that contribute to the output, but that's still a value of 765, which gets clamped, so the output is again white. Considering the remaining black pixels in our test image, it's pretty obvious that all the outside ones will turn white. In fact it's only the very center pixel that remains black. So the output from this particular convolution matrix is just a single black pixel at the center of a white square. Some of you may have noticed that I've conveniently started with a pixel that is not on the very edge of the filter area. How does Inkscape calculate the value for the top-left pixel, for example, given that five of the points covered by the matrix simply don't exist? The answer lies in the Edge Mode popup in the filter settings: “Duplicate” copies the pixels from the outer edge to fill any missing values; “Wrap” uses the pixels from the opposite side of the image to fill the gaps as though it were working on a tiled version of the input; “None” just sets the channel values for the missing pixels to zero.

Pas besoin de grandes connaissances mathématiques pour comprendre que les cinq pixels blancs ont apporté une valeur de 255 à la sortie, alors que les pixels noirs ne contribuent en rien. Aussi, la valeur utilisée pour le pixel de sortie est simplement 255 x 5 = 1 275. Sauf que les valeurs de sortie sont limitées, de sorte que la vraie valeur de sortie est 255 seulement, cette matrice change le pixel noir en un pixel blanc.

Sur le pixel suivant, le résultat est identique. Cette fois-ci, seulement trois pixels blancs contribuent à la sortie ; mais la valeur est encore de 765, qui est bornée, et donc la sortie est à nouveau blanche.

En considérant les pixels noirs restants dans notre image, il paraît assez évident que tous ceux de l'extérieur vont virer au blanc. En fait, seul le pixel tout au centre reste noir. Ainsi, la sortie de cette matrice de convolution particulière est un simple pixel noir au centre d'un carré blanc.

Certains d'entre vous auront noté que j'ai commencé astucieusement sur un pixel qui n'est pas au bord de la zone de filtrage. Comment fait Inkscape pour calculer la valeur du pixel en haut à gauche, par exemple, étant donné que cinq des points couverts par la matrice n'existent simplement pas ? La réponse se trouve dans la liste déroulante « Mode bordure » des réglages du filtre : « Dupliquer » copie les pixels du bord extérieur pour remplir toutes les valeurs manquantes ; « Retour à la ligne » utilise les pixels du côté opposé de l'image pour remplir les vides, comme si elle travaillait sur une version tuilée de l'image d'entrée ; « Aucun » met les valeurs des canaux des pixels manquants à zéro.

Or at least that's how it's supposed to work. According to the official Inkscape manual, this parameter is completely ignored by Inkscape, despite being present in the UI. It doesn't specify what method is used to calculate the missing pixels and, as the official manual has not been updated for version 0.91, I'm not sure if this situation has changed with the more recent release. So we'll just ignore the question, and assume that Inkscape does something to populate the missing pixels or omit them from the calculation, so that we don't have to worry too much about it. Because the values we've chosen tend to result in calculations that get clamped, our filter, as it stands, pretty much just creates black and white pixels in the output. Before clamping, we were getting results of 1,275 and 765, but we then proceed to throw away any difference between the values because they're both greater than 255. By using the Divisor control in the filter settings, we're able to scale the output of the calculations prior to any clamping, allowing us to rein in the values to preserve those differences. A good rule of thumb is to set the divisor to the same value as the total of the individual numbers in your matrix. By setting it to 9 in our example image, the outputs of 1,275 and 765 are reduced to 142 (1,275 ÷ 9) and 85 (765 ÷ 9), giving us this result: Now each output pixel is the average of nine pixels from the input image. Although it might not be clear from this small example, the outcome is a simple blurring of the input image. In reality, it would be better to use a Gaussian Blur primitive if you just want to soften your image a bit, but this was, of course, just a demonstration of the mathematics that takes place behind the scenes with the Convolve Matrix.

Ou, du moins, c'est ainsi que c'est censé fonctionner. D'après le manuel officiel Inkscape, ce paramètre est complètement ignoré par Inkscape, bien qu'il soit présent dans l'interface. La méthode pour calculer les pixels manquants n'est pas indiquée, et, comme le manuel officiel n'a pas été remis à jour pour la version 0.91, je ne sais pas si la situation a changé avec la version la plus récente. Aussi, nous ignorerons cette question et considérerons qu'Inkscape remplit les pixels manquants ou les néglige lors du calcul, de sorte de ne pas trop nous en faire à ce propos.

Parce que les valeurs que nous avons choisies entraînent une troncature des résultats de calcul, notre filtre, tel qu'il est, crée simplement en sortie des pixels noirs et blancs. Avant la troncature, les résultats étaient 1 275 et 765, mais notre action suivante supprime toute différence de valeur parce qu'ils sont tous au-dessus de 255. En utilisant le contrôle Diviseur dans les paramètres du filtre, nous pouvons adapter la sortie des calculs avant troncature, nous permettant de restreindre les valeurs pour préserver ces différences. Une bonne règle empirique est de mettre le diviseur à la même valeur que la somme de chacun des nombres de la matrice. En le réglant à 9 dans notre exemple, les sorties 1 275 et 765 sont réduites à 142 (1 275 ÷ 9) et 85 (765 ÷ 9), nous donnant ce résultat :

Ici, chaque pixel de sortie est la moyenne de neuf pixels de l'image d'entrée. Bien que cela puisse ne pas être clair dans ce petit exemple, le résultat est un simple floutage de l'image d'entrée. En réalité, il serait mieux d'utiliser une primitive Flou gaussien si vous voulez juste adoucir un peu votre image, mais c'était, bien sûr, juste une démonstration des mathématiques qui se déroulent en arrière-plan de la Matrice de convolution.

Now let's move on to some more interesting matrices. I'm going to stop with the pixel-by-pixel approach, and the mathematical explanations – it's all just an extension of the examples I've shown so far, but with larger images and multiple color channels. We'll use a different classical image to demonstrate these because Mona, quite honestly, isn't that interesting when you apply a convolution matrix. So we'll switch to Michelangelo's “Creation of Adam”, with each image showing the unfiltered version at the top left, and the filtered one at the bottom right. We'll start with a “Sobel” matrix: A Sobel operator emphasises the differences between adjacent pixels in one direction or another. The result is essentially a map with bright colors where there is a sharp transition between pixels, and dark colors where there is little or no difference between adjacent pixels. In practical terms, therefore, it acts as an edge detection filter, in this case highlighting vertical edges (note, particularly, the coving at the right of the image).

Maintenant, passons à des matrices plus intéressantes. J'arrête l'approche pixel par pixel et les explications mathématiques, c'est tout simplement une extension des exemples que je vous ai montré jusqu'à présent, mais avec des images plus grandes et plusieurs canaux de couleur. Nous utiliserons une image classique différente pour les présenter car la Joconde, très honnêtement, n'est pas intéressante pour l'application d'une matrice de convolution. Aussi, nous prendrons à la place la Création d'Adam de Michel-Ange, avec, pour chaque image, la version non filtrée en haut à gauche et la version filtrée en bas à droite. Nous commencerons par une matrice « Sobel » :

Un opérateur Sobel amplifie les différences entre les pixels voisins dans une direction ou une autre. Le résultat est essentiellement une configuration avec des couleurs lumineuses, où la transition entre pixels est franche, et des couleurs sombres où il y a peu ou pas de différences entre les pixels adjacents. Par conséquent, dans la pratique, il agit comme un filtre de détection des contours, dans ce cas, soulignant les contours verticaux (notez particulièrement la moulure à droite de l'image).

Rotating the values of the matrix through 90° (so that the top row contains 1, 2, 1 and the bottom row is -1, -2, -1) turns it into an edge detection filter for horizontal edges. In this case the coving vanishes, but any near-horizontal shapes are accentuated: A more general form of edge detection, which highlights both vertical and horizontal lines resulting in an “outline” version of the original image, can be achieved with the following matrix: It's easy to imagine this, followed by a Color Matrix primitive, forming the basis of a “pencil sketch” filter chain, but you can achieve a similar result by using the Bias parameter in the filter preferences. This lets you add a fixed offset to the result of each calculation, and acts to brighten or darken the output image. Setting this parameter to 1.0 with the previous filter gives this result: A variation on edge detection is edge enhancement. This matrix will emphasise edges but still allow the original colors to show through, resulting in a sharpened appearance to the image: Here's another matrix that darkens some edges whilst lightening others, giving rise to an embossed appearance. As you can see, there are a variety of effects that can be produced with this primitive, although it's far from intuitive to work out what values you need to enter into the matrix to get a particular output. Although the matrix approach allows for a vast number of possibilities, there are really only a few well-known matrices that are commonly used. A search online will provide you with some more examples, but they all fall into the same basic themes I've covered here.

La rotation des valeurs de la matrice de 90 ° (de telle sorte que la ligne du haut contienne 1, 2, 1 et celle du bas -1, -2, -1) la transforme en un filtre de détection des contours horizontaux. Dans ce cas, la moulure disparaît, mais les formes presque horizontales sont accentuées :

Une forme plus générale de détection des contours, qui souligne les lignes verticales et horizontales donnant une version « schématique » de l'image d'origine, peut être réalisée avec la matrice suivante :

Il est facile d'imaginer ceci, suivi par une primitive Matrice des couleurs, formant la base d'une chaîne de filtrage d'« esquisse au crayon », mais vous pouvez obtenir un résultat similaire en utilisant le paramètre Déviation dans les préférences de filtrage. Celui-ci vous permet d'ajouter un décalage fixe au résultat de chaque calcul et agit pour assombrir ou éclaircir l'image de sortie. Le réglage de ce paramètre à 1.0 avec le filtre précédent donne ce résultat :

Une variation de la détection des contours est l'amélioration des contours. Cette matrice amplifie les contours tout en permettant aux couleurs d'origine de transparaître, donnant une apparence accentuée de l'image :

Voici une autre matrice qui assombrit certains contours tout en en éclaircissant d'autres, donnant vie à une sorte d'embossage. Comme vous pouvez le voir, il y a une large palette d'effets qui peuvent être produits avec cette primitive, bien que déterminer quelles valeurs saisir pour obtenir une sortie particulière soit peu intuitif. Bien que l'approche par matrice permette beaucoup de possibilités, il n'y a en réalité que peu de matrices bien connues qui sont utilisées habituellement. Une recherche en ligne vous fournira quelques autres exemples, mais ils rejoignent tous les thèmes de base que je vous ai présentés ici.

Before concluding, there are a few more controls in the filter UI that need to be explained. The Size parameter, as you might expect, sets the size of the matrix. I've only used 3×3 matrices in this article, but 5×5 is also a common size, and you could go higher still, to consider a wider area around each source pixel. Just remember that the size of the matrix defines how many pixels need to be read and calculated for each output pixel, so increasing this parameter can quickly impose a much larger processing burden on Inkscape. In the examples here, I've assumed that the center of the matrix is positioned over the target pixel for each calculation. It's possible to change that using the Target fields in the UI, where 0,0 would set the top-left cell of the matrix as the target. All this does is shift the output a little, so there's little reason to worry about it too much. Finally, the Preserve Alpha checkbox determines whether the alpha of the original pixel is transferred to the output unchanged (checked), or if the alpha channel is also subject to the convolution process (unchecked). I tend to leave this checked, as it's one less channel of calculations for Inkscape to perform, and I haven't yet found myself needing to convolve the alpha channel.

Avant de conclure, il y a quelques contrôles supplémentaires dans l'interface du filtre, que je dois expliquer. Le paramètre Taille, comme vous pouvez vous y attendre, détermine la taille de la matrice. Je n'ai utilisé que des matrices 3×3 dans cet article, mais 5×5 est aussi une taille classique, et vous pouvez augmenter encore plus, pour prendre en compte une zone plus large autour de chaque pixel de la source. Simplement, gardez en tête que la taille de la matrice définit le nombre de pixels qui doivent être lus et calculés pour chaque pixel de sortie ; aussi, l'augmentation de ce paramètre peut rapidement imposer une charge de traitement beaucoup plus grande à Inkscape.

Ici, dans ces exemples, j'ai pris comme base que le centre de la matrice est positionné sur le pixel cible de chaque calcul. Il est possible de changer cela en utilisant le champ Cible de l'interface, où 0,0 placera comme cible la cellule en haut à gauche de la matrice. Tout ceci décale un peu la sortie ; aussi, il n'y a pas de raison d'en faire grand cas.

Enfin, la case à cocher Préserver l'opacité détermine si l'alpha du pixel d'origine est transféré sans changement à la sortie (coché), ou si le canal alpha est aussi traité par le processus de convolution (décoché). J'ai tendance à le garder coché, car ça fait un canal de moins à calculer pour Inkscape et je n'ai pas encore ressenti le besoin de convoluer le canal alpha.

Image Credits “The Creation of Adam” by Michelangelo https://en.wikipedia.org

Crédits images La création d'Adam par Michel-Ange https://fr.wikipedia.org

issue114/inkscape.txt · Dernière modification : 2016/11/09 15:43 de andre_domenech