1 pièce(s) jointe(s)
Comment fonctionnent les filtres de rééchantillonnage trouvés sur le web ?
et utilisés parfois dans des logiciels répandus, comme DoubleCommander (gestionnaire de fichiers écrit en Lazarus) et son "viewer" de fichiers image ?
Bonsoir,
Suite à la lecture intéressante d'un petit bout du "Fundamentals of Computer Graphics" que je vous résume ainsi (traduction Google) :
- Lorsqu'on rétrécit l'image, on dimensionne le filtre en fonction de l'espacement des échantillons de sortie (rayon 3 sur la figure 9.39).
- Lorsqu'on agrandit l'image, la taille du filtre est déterminée par l'espacement des échantillons d'entrée (rayon 1 de la figure 9.39).
et la figure 9.39 c'est elle :
Pièce jointe 278932
je me suis donc dit que j'allais tester le fonctionnement d'un module écrit en Pascal, qui embarque 7 filtres :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
| const
ResampleFilters: array[0..6] of record
Name : string; // Filter name
Filter: TFilterProc;// Filter implementation
Width : Single; // Suggested sampling width/radius
end = (
(Name: 'Box'; Filter: BoxFilter; Width: 0.5),
(Name: 'Triangle'; Filter: TriangleFilter; Width: 1.0),
(Name: 'Hermite'; Filter: HermiteFilter; Width: 1.0),
(Name: 'Bell'; Filter: BellFilter; Width: 1.5),
(Name: 'B-Spline'; Filter: SplineFilter; Width: 2.0),
(Name: 'Lanczos3'; Filter: Lanczos3Filter; Width: 3.0),
(Name: 'Mitchell'; Filter: MitchellFilter; Width: 2.0)); |
La seule aide dont on dispose, c'est 4 lignes de commentaires juste avant le prototype de la méthode :
Code:
1 2 3 4 5 6
| // Interpolator:
// Src: Source bitmap
// Dst: Destination bitmap
// filter: Weight calculation filter
// fwidth: Relative sample radius
procedure Stretch(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single); |
J'ai regardé comment les autres utilisent cette procédure Stretch, ça a l'air un peu pifométrique : j'ai trouvé
Code:
Stretch(src, dst, ResampleFilters[5].Filter, ResampleFilters[5].Width);
et aussi
Code:
Stretch(src, dst, ResampleFilters[2].Filter, ResampleFilters[2].Width);
ce qui laisse supposer que le codeur de l'appel a estimé que le mieux était d'utiliser la valeur définie par défaut et basta.
Et comme ce comportement me chagrinait, j'ai essayé de tester avec différentes valeurs pour chaque filtre, et c'est là que les ennuis commencent :
1- pour me simplifier la vie, j'ai d'abord tenté avec une bête boucle genre
Code:
1 2 3 4 5
| for i := 0 to 6 do begin // parcours de la liste des filtres
for j := 0 to 4 do begin // 5 valeurs de radius
Stretch(Src, Dst, ResampleFilters[i].Filter, j);
end; // for j
end; // for i |
mais ça partait tout de suite en "Access Violation"... :aie:
Je vous la fais courte (car j'y ai passé un certain temps...) : certains filtres ne supportent pas de commencer avec un radius à 0, et c'est même plus vicieux que ça, regardez comment je m'en suis sorti :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| for i := 0 to 6 do begin
for j := 0 to 4 do begin // 5 valeurs de radius
for k := 1 to 2 do begin
// j=0 radius=0.5 & 1 - j=1 radius=1.5 & 2 - j=2 radius=2.5 & 3 - etc.
radius := (k * 0.5) + j;
// les 3 premiers filtres fonctionnent à partir de 0.5
if i = 3 then radius := radius + 1.0; // démarre à 1.5 et continue
if i = 4 then radius := radius + 1.5; // démarre à 2.0 et continue
if i = 5 then radius := radius + 2.5; // démarre à 3.0 et continue
if i = 6 then radius := radius + 1.5; // démarre à 2.0 et continue
Stretch(Src, Dst, ResampleFilters[i].Filter, radius);
end; // for k
end; // for j
end; // for i |
On dirait que ce qui est noté dans le code comme "Suggested sampling width/radius" est en fait la valeur minimum à employer. Admettons. Mais alors, si on regarde un filtre en détail,
Code:
1 2 3 4 5
| function TriangleFilter(Value: Single): Single;
begin
if (Value < 0.0) then Value := -Value; // si la valeur est négative on la transforme en positive
if (Value < 1.0) then Result := 1.0 - Value else Result := 0.0;
end; |
pourquoi ne puis-je pas lui passer 0 (zéro) ?
Mais ce n'est pas le plus important. Ce qui l'est,
2- c'est que une fois ma boucle au point, je l'ai exécutée en générant un fichier pour chaque valeur de chaque filtre, et là, patatras, pour chaque filtre il n'y a aucune différence entre la dizaine de fichiers générés, en contradiction totale avec les "Fondamentals" et la zolie image que je vous ai proposée...
À partir de là, ça dépasse mon entendement et mes compétences, d'où *ce* post.
La seule piste que je vois, c'est une incompréhension de ma part de ce commentaire : Suggested sampling width/radius, le signe "/" n'indiquant pas un mot ou l'autre mais vraiment une division de width (quelle width ? De qui de quoi ?) par radius ? Est-ce que ça colle avec l'autre commentaire : Relative sample radius ?
Un dernier mot : les essais ont été faits en UpSampling et en DownSampling, résultats identiques. :?
À vos claviers ! ;)
Comment fonctionnent les filtres de rééchantillonnage trouvés sur le web ?
Bonjour, :salut:
S'emballer sur un sujet à la lecture d'un document, c'est très bien ...
Citation:
Envoyé par
Jipété
... Suite à la lecture intéressante d'un petit bout du "Fundamentals of Computer Graphics" ... je me suis donc dit que j'allais tester le fonctionnement d'un module écrit en Pascal ...
... encore faut-il au départ se limiter à un projet raisonnable, afin d'identifier et de franchir les obstacles qui ne manqueront pas de se présenter.
Et là, tu n'y va pas de main morte:
Citation:
Envoyé par
Jipété
... je me suis donc dit que j'allais tester le fonctionnement d'un module écrit en Pascal, qui embarque 7 filtres ...
Connais-tu véritablement chacune des procédures mentionnées (Filter: TFilterProc) ? As-tu toi-même codé leur implémentation, disposes-tu du texte source, ou les as-tu au moins testées sur des images appropriées ?
Je m'en tiendrais pour commencer à un filtre unique, avant d'introduire en second lieu une liste d'enregistrements de longueur croissante:
Code:
ResampleFilters: array[0..N_Filtre] of record
Et ce que tu laisses transparaître plus loin de tes errements laisse craindre le pire:
Citation:
Envoyé par
Jipété
... La seule aide dont on dispose, c'est 4 lignes de commentaires ...
... J'ai regardé comment les autres utilisent cette procédure Stretch, ça a l'air un peu pifométrique ...
... ce qui laisse supposer que le codeur de l'appel a estimé que le mieux ...
... Et comme ce comportement me chagrinait ...
L'essentiel est qu'après toutes ces tribulations, tu aies gardé le moral. :D
Quelques questions, de la part de quelqu'un qui observe tout cela de Sirius:
1°) L'allusion à une procédure d'étirement (procedure Stretch(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single)) n'implique-t-elle pas une modification du rapport (Largeur/Hauteur), donc de la forme de l'image ?
2°) Le rayon (radius) associé à un échantillonnage n'est-il pas obligatoirement positif ? Il faut en revenir à l'algorithme utilisé.
3°) Peut-on prétendre maîtriser un projet en arrangeant des boîtes noires dont on ne connaît pas le contenu ? C'est bien ce que tu cherches, à moins que je ne me trompe complètement ...
4°) À quel titre le bricolage d'autrui, dont tu sais peu de choses sinon rien, pourrait-il servir de référence ?
Citation:
... J'ai regardé comment les autres utilisent cette procédure Stretch, ça a l'air un peu pifométrique ...
Au royaume des aveugles, les borgnes sont rois.
Et je suis sûr que tu as déjà eu l'occasion de découvrir des algorithmes ineptes.
Il te faut prendre connaissance des codes sources des divers filtres en cause, pour comprendre véritablement ce qui se passe.
Donc une meilleure confiance en soi, et un retour à l'excellente documentation disponible sur developpez.com, site pour développeurs-programmeurs pro injustement méconnu :mrgreen:.
1 pièce(s) jointe(s)
Comment fonctionnent les filtres de rééchantillonnage trouvés sur le web ?
1°) Mettons les mains dans le cambouis.
Citation:
Envoyé par
Jipété
... Est-ce que le fait de ne rien comprendre à la pignonnerie d'une boîte de vitesse empêche de conduire un véhicule ? ...
Non bien sûr ... cependant si un bricoleur aussi motivé que toi entreprend de construire un véhicule à partir de pièces détachées, il lui faudra bien les examiner toutes avec soin pour comprendre leur fonctionnement, et rassembler une bonne documentation technique afin de s'assurer de la sécurité et de la viabilité de l'ensemble ...
Il ne lui sera cependant pas nécessaire - je le reconnais - d'aborder la théorie des engrenages.
2°) Voilà donc la mire idéale :fleur: :heart: :rose2::rose:, à laquelle tu faisais allusion au mois d'avril ...
Citation:
Envoyé par
Jipété
Connais-tu véritablement chacune des procédures mentionnées ? ... les as-tu au moins testées sur des images appropriées ?
... Oui, la preuve, avec à gauche réduction à 80 %, à droite agrandissement à 150 % ...
Tests d'images très soignés, comme toujours.
3°) Abordons la question du "rayon" :weird: (traduction hâtive et probablement incorrecte de radius dans le cas présent): Pièce jointe 279363
lors d'une reproduction d'image avec changement de taille, on transfère la moyenne pondérée du pixel central et d'un certain nombre de ses voisins, les coefficients affectés à ces derniers étant d'autant plus faibles qu'ils sont plus éloignés; la distribution des valeurs est représentée par une courbe paire, maximale à l'origine, nulle au-delà d'un certain seuil (±1) et dont le contour influe sur l'aspect de l'image obtenue.
Tu en as toi-même rapporté l'exemple du pic triangulaire:
Citation:
Envoyé par
Jipété
... Mais alors, si on regarde un filtre en détail,
Code:
1 2 3 4 5
| function TriangleFilter(Value: Single): Single;
begin
if (Value < 0.0) then Value := -Value; // si la valeur est négative on la transforme en positive
if (Value < 1.0) then Result := 1.0 - Value else Result := 0.0;
end; |
pourquoi ne puis-je pas lui passer 0 (zéro) ?
qui correspond très simplement à la fonction linéaire par morceaux: F(x) = 0 si (│x│>1) sinon F(x) = 1 - │x│ :furax:.
Le commentaire que tu a laissé, ainsi que l'une de tes réponses ultérieures
Citation:
Envoyé par
Jipété
Le rayon (radius) associé à un échantillonnage n'est-il pas obligatoirement positif ?
Peu importe : s'il est négatif il est converti.
révèle l'ambiguïté qui s'est glissée dans ton esprit.
Dans le graphe de la courbe précédente, l'abscisse (x) représente une distance sans unité; l'écart de position correspondant, exprimé en pixels, est donné par le produit D = R * x , où (R) représente la demi-largeur à la base du pic et mesure l'étalement de ce dernier sur l'image initiale.
C'est une grandeur strictement positive, qui ne peut s'annuler parce qu'alors le filtre, infiniment sélectif, ne laisserait plus rien passer.
Elle s'évalue sur l'axe horizontal, et non sur celui des ordonnées.
Désolé de rester dans le vague, mais je ne peux être plus précis au sujet du terme radius, faute de documents mentionnant la notation employée.
Si je trouve de nouvelles sources, je reviendrai sur ce point.
Comment fonctionnent les filtres de rééchantillonnage trouvés sur le web ?
Je tente de répondre à deux de tes remarques.
1°)
Citation:
Envoyé par
Jipété
... Abordons la question du "rayon" (traduction hâtive et probablement incorrecte de radius dans le cas présent) ...
C'est pourtant la première qui tombe des traducteurs du web ...
C'est effectivement la traduction immédiate; mais ce terme représente ici la "demi-largeur" du pic, et son "étalement" ... d'autres synonymes ne sont pas exclus.
Un document en français traitant à fond de ce sujet permettra de trancher.
2°)
Citation:
Envoyé par
Jipété
... Le commentaire que tu as laissé, ainsi que l'une de tes réponses ultérieures révèle l'ambiguïté qui s'est glissée dans ton esprit.
Explique, parce que quand j'écris "si la valeur est négative on la transforme en positive" c'est juste écrire en français ce que je lis là : if (Value < 0.0) then Value := -Value; ...
Tu parais confondre deux termes, comme le montrent deux remarques étroitement apparentées (#4 , texte en rouge) que tu as faites:
a) l'une concernant l'abscisse (Value) de la fonction filtre triangulaire (function TriangleFilter(Value: Single): Single;) évoquée dans le message (#1), et que j'ai commentée sous la forme simplifiée F(x): il s'agit d'une variable réelle, admettant pour valeurs critiques (0) et (±1), et qui correspond au quotient (Dpixels/Rseuil), rapport de l'écart (horizontal ou vertical) entre deux pixels de l'image de départ au seuil choisi (lui aussi exprimé en pixels);
b) l'autre concernant le rayon (radius) associé à l'échantillonnage; il s'agit d'une constante strictement positive, probablement assimilable au Rseuil précédent, sous réserve de confirmation sur documents.
La confusion est confirmée par l'un de tes commentaires:
Citation:
pourquoi ne puis-je pas lui passer 0 (zéro) ?
Ne vois pas un procès d'intention dans ces remarques appuyées. :)
# Où trouve-t-on le code des filtres utilisés dans ton arsenal, ainsi que celui de la procédure concernée ?
Code:
1 2 3 4 5 6 7 8 9 10
|
(Name: 'Box'; Filter: BoxFilter; Width: 0.5),
(Name: 'Triangle'; Filter: TriangleFilter; Width: 1.0),
(Name: 'Hermite'; Filter: HermiteFilter; Width: 1.0),
(Name: 'Bell'; Filter: BellFilter; Width: 1.5),
(Name: 'B-Spline'; Filter: SplineFilter; Width: 2.0),
(Name: 'Lanczos3'; Filter: Lanczos3Filter; Width: 3.0),
(Name: 'Mitchell'; Filter: MitchellFilter; Width: 2.0));
procedure Stretch(Src, Dst: TBitmap; filter: TFilterProc; fwidth: single); |
Cela me permettra peut-être de mieux comprendre la documentation.
S'agit-il du même code pour Gimp ?
Citation:
... quand par exemple je trafique la netteté avec The Gimp, je peux jouer sur le rayon, la quantité et le seuil, ça doit donc être possible (mais je n'ose pas publier le code qui utilise les filtres, c'est une abomination de prise de tête ...
Comment fonctionnent les filtres de rééchantillonnage trouvés sur le web ?
Citation:
Envoyé par
Jipété
... Mais où as-tu trouvé ça ? Le mot Mitchel, même avec deux "L" (comme un oiseau ou mieux, un ange :mrgreen:), n'existe pas dans ton lien !
Il se trouve au moins dans celui que tu as donné
La perte d'une aile n'invalide pas l'information fournie par anapurna.
Merci pour tes infos, et le lien qui conduit à l'un des meilleurs articles sur le sujet. J'ai moi-même cherché, et la documentation triée est rassemblée ci-dessous.
Je l'ai envoyée à une autre personne intéressée par le traitement des images.
Je n'ai pas compris la présence simultanée du paysage et de l'avion sur ton image-test: peut-être souhaitais-tu mettre en comparaison des objets naturels et artificiels ?
# Généralités, initiation
Images / généralités
http://rea.ccdmd.qc.ca/ri/numerixel/
Le traitement numérique des images
http://images.math.cnrs.fr/Le-traite...es-images.html
Traitement d'images
https://fr.wikipedia.org/wiki/Traitement_d%27images
# Calculs bien expliqués
Les filtres usuels en traitement d'images
http://xphilipp.developpez.com/articles/filtres/
Tutoriel : Le redimensionnement d'images HHHHHHHHHHHHH (il s'agit de ton lien)
https://clouard.users.greyc.fr/Panth.../index-fr.html
Quelques méthodes de filtrage en Traitement d'Image
https://hal.archives-ouvertes.fr/hal...280v1/document
# Logiciels
PhotoFiltre
http://www.photofiltre-studio.com/doc/
Manipulation d'images
https://docs.gimp.org/fr/
Matrices de convolution
https://docs.gimp.org/fr/plug-in-convmatrix.html
Logiciels gratuits
http://bastas.pagesperso-orange.fr/d...aire.htm#photo
# Théorie ***** calculs difficiles
Quelques méthodes de filtrage en Traitement d'Image
https://hal.archives-ouvertes.fr/hal...280v1/document
Détection des contours
http://www.optique-ingenieur.org/fr/...ontenu_02.html
Comment fonctionnent les filtres de rééchantillonnage trouvés sur le web ?
Citation:
Envoyé par
Jipété
... Oui, mais pour voler ça devient tout de suite plus compliqué, :ptdr:
Oh, mais on n'arrête pas le progrès ! Voir le lien récemment proposé sur un autre forum ...
Citation:
Envoyé par
Jipété
... Quant à moi, je suis face à un problème hallucinant ... mais depuis ce matin il m'est impossible de recréer les fichiers que j'ai générés hier soir en testant les filtres : les résultats sont systématiquement pourris ... un truc de malade. :marteau:
Bah, je vais lâcher l'affaire quelques heures, histoire que la tempête se calme ...
Rien de tel qu'un peu de détente pour y voir plus clair ! :D (1)
Citation:
Envoyé par
Jipété
...
J'ai trouvé ça, c'est sympathique même si très compliqué pour moi, d'autant plus que je n'y trouve pas la réponse à ma question (comment appliquer radius 1, 2, 3 tel que montré dans le "Fundamentals"), mais c'est peut-être une mauvaise question ...
Voilà comment je vois le problème, d'un point de vue qualitatif et tout à fait extérieur: les matheux spécialistes apporteront les précisions (ou les rectifications) nécessaires.
Le filtre fait le lien entre deux ensembles discrets, les images de départ (A) et d'arrivée (B) par l'intermédiaire d'une fonction réelle d'une variable réelle, continue, paire, maximale au centre (x = 0) et nulle au-delà d'un seuil (x = ± 1); par exemple la fonction triangle F(x) définie par
Code:
IF Abs(x)<1 THEN F:= 1 - Abs(x) ELSE F:= 0;
Soit par ailleurs une image (A) unidimensionnelle, réduite à une ligne de 100 pixels [0 <= i < 100] et sa transformée (B) trois fois plus grande, et comportant par conséquent 300 pixels [0 <= j < 300].
Une correspondance rudimentaire entre les 2 indices (i:= j DIV 3) conduirait à un résultat grossier, ensemble de 100 agrégats unicolores de largeur (3); il faut donc, dans la nouvelle image, calculer 200 nouveaux pixels dont la couleur s'accorde à celle de leurs voisins exactement définis, et dont les rangs sont multiples de 3: (j = 3*i).
On peut s'en sortir par l'introduction des conventions suivantes:
Code:
1 2
| CONST R = 3; // valeur du rayon (radius ?) définie en constante
a:= Round(j/3); i:= a + k; x:= (j - 3*i)/R; |
et envisager la couleur obtenue (provisoirement réelle, puis ramenée à une entier de format Byte): Cb = Sk=-2+2(Ca[a+k]*F(xk)) ;
on obtient dans ces conditions, pour 4 valeurs consécutives de l'indice (j):
Code:
1 2 3 4 5 6
|
j a Cb
99 33 Ca[31]*F(2) + Ca[32]*F(1) + Ca[33]*F(0) + Ca[34]*F(-1) + Ca[35]*F(-2) = Ca[33]
100 33 Ca[31]*F(7/3) + Ca[32]*F(4/3) + Ca[33]*F(1/3) + Ca[34]*F(-2/3) + Ca[35]*F(-5/3) = (2/3)*Ca[33] + (1/3)*Ca[34]
101 34 Ca[32]*F(5/3) + Ca[33]*F(2/3) + Ca[34]*F(-1/3) + Ca[35]*F(-4/3) + Ca[36]*F(-7/3) = (1/3)*Ca[33] + (2/3)*Ca[34]
102 34 Ca[32]*F(2) + Ca[33]*F(1) + Ca[34]*F(0) + Ca[35]*F(-1) + Ca[36]*F(-2) = Ca[34] |
Ainsi lorsque (j) varie de (99) à (102), la couleur (Cb) des pixels passe progressivement de celle du premier pixel antécédent (33) à la suivante.
Un plus grand rayon (R>3) ferait apparaître un plus grand nombre de termes non-nuls, donc un flou plus marqué par lissage de couleur sur une zone plus étendue de l'image initiale (A).
Un rayon plus faible estomperait aussi les détails par création d'irrégularités quasi-périodiques, la somme pouvant alors présenter des valeurs plus dispersées; un rayon très faible (R<0.1) conduirait dans le cas présent à une image lacunaire:
Code:
IF (j MOD 3 = 0) THEN Cb[j]:= Ca[j DIV 3] ELSE Cb[j]:= 0;
Il existe donc bien, pour un filtre donné, une valeur optimale du rayon (R = g ?) pour laquelle la résolution de l'image agrandie est maximale.
Voilà le principe simple (ou simpliste ?) du calcul, pour un agrandissement entier supérieur à l'unité; une valeur quelconque (g = LB / LA > 1) du rapport conduirait à des résultats analogues, mais plus lourds à formaliser.
La notation est volontairement personnelle, afin d'éviter toute interférence avec celle de ton code.
(1) J'ai connu mésaventure semblable :arf: : tu as sans doute oublié de reporter une modification dans l'une des unités, procédures ou fonctions concernées. Cafouillage assuré :aie: !
J'en suis venu à indexer tous les versions successives des mes unités encore en évolution: Bitmap_01, Bitmap_02, ... etc. afin de connaître laquelle est appelée par un programme donné.
Il faut même détruire toute ancienne version devenue inutile, incompatible avec les programmes conservés.
Comment fonctionnent les filtres de rééchantillonnage trouvés sur le web ?
Citation:
Envoyé par
Jipété
... en outre, tant que je ne peux pas comparer pixel à pixel l'original avec sa copie filtrée, il est illusoire d'espérer avancer ici ...
Vu la complexité des fonctions en cause, cela n'a rien d'évident ! :D
Tu peux cependant voir ce qui se passe qualitativement dans le cas:
- du filtre le plus simple (triangle),
- d'une image élémentaire (rangée verticale isolée de pixels blancs sur fond noir),
après agrandissement horizontal de l'image (sa dimension étant multipliée par un facteur entier).
Les documents consultés sur ce forum m'ont donné une idée sur un tout autre sujet: le tracé des graphes définis par des équations du type F(x, y), sans possibilité d'exprimer d'une façon explicite l'une des coordonnées en fonction de l'autre.