Ce post est le résultat d’un travail scolaire. Voir le rapport original. Voir la page actuelle des plugins GIMP.

Plugin GIMP : Transformée de Fourier

Rémi Peyronnet – Février 2002

Introduction

Gimp est un logiciel de traitement d’image issu du monde libre, et de plus en plus employé. OpenSource, il profite de nombreuses contributions, en particulier grâce à de nombreux plugins qu’il est possible de développer.

Je me suis posé la question de savoir quels avantages il était possible de tirer en manipulant les images dans une espace fréquentiel, c’est à dire après transformation de Fourier. J’imaginais en effet qu’il serait possible d’exploiter ce mode pour obtenir des effets intéressants, en gommant une partie du spectre, en appliquant les effets classiques au spectre (blur, noise, …) ou en cachant du texte dans le spectre (stéganographie). La façon la plus simple pour répondre à toutes ces questions était d’expérimenter, voila donc pourquoi j’ai entrepris de réaliser ce plugin.

Imaginant que cela serait plus intéressant de travailler sur une transformée de Fourier d’une image représentée en YUV plutôt qu’en RGB, j’ai également développé auparavant un plugin YUV.

I. Elaboration du plug-in

I. 1. Choix de la bibliothèque de FFT

Il existe sur Internet de nombreux codes disponibles pour réaliser une transformée de Fourier, et ceci du plus simple au plus perfectionné. Les programmes les plus simples (qui tiennent souvent en moins d’une centaine de lignes) ont souvent des performances médiocres, et ne savent opérer que sur des images aux dimensions égales à une puissance de deux. Il est évidemment possible pour contourner cette limite de remplir par des zéros de manière à arrondir à la puissance de deux supérieure.

Cependant, une librairie semble s’imposer dans ce domaine : FFTW comme Fastest Fourier Transform in the West. Cette bibliothèque est disponible sur de nombreuses plateformes. Outre ses performances incroyables, elle permet de s’adapter automatiquement à des tailles non puissances de 2. Elle propose aussi de nombreuses options, comme les transformées réelles, en place,… J’ai donc opté pour cette librairie, d’utilisation relativement facile.

I. 2. Programmation d’un Plug-in GIMP

L’intégration dans GIMP n’a pas été la chose la plus facile du projet, car la documentation est assez elliptique sur ce sujet. Heureusement, il existe beaucoup de plugins pour GIMP OpenSource, ce qui m’a permis de comprendre le fonctionnement normal d’un Plugin.

Le plugin possède un code d’initialisation, qui se charge d’enregistrer la fonction dans GIMP, en inscrivant par exemple une entrée dans un menu. Lorsque ce menu est appelé, la fonction principale du plugin est appelée, en donnant en paramètre un pointeur vers l’image. Ce plugin a besoin, pour effectuer la transformée de Fourier, de connaître toute l’image en même temps. La méthode optimisée de traitement par blocs n’est donc pas possible. Pour effectuer ces opérations, l’image est recopiée intégralement en mémoire, puis un deuxième tableau est alloué, pour effectuer la transformée de Fourier.

II. Un problème majeur : la taille d’un octet

Cela parait ridicule à rappeler, mais un octet ne peut prendre que 256 valeurs différentes. Il faut bien remarquer que lorsque nous effectuons la transformation de Fourier, nous travaillons sur un type de données ‘flottant’, c’est à dire capable de stocker des nombres réels avec une précision honorable. Or l’image elle ne peut contenir dans un mode RGB uniquement 256 niveaux par plans de couleurs.

Le problème qui se pose avec la transformée de Fourier est que les nombres en sortie ne sont pas du tout répartis de la même façon en valeur. Nous avons en effet généralement de très importantes valeurs pour les basses fréquences, et des valeurs décroissantes ensuite, et nous devons stocker ces résultats :

  • avec une précision suffisante pour pouvoir reconstituer l’image correctement.
  • organisés d’une façon intelligible pour ensuite pouvoir travailler facilement sur l’image en comprenant ce que l’on fait
  • en tenant compte du fait que les résultats peuvent être positifs ou négatifs : pour ceci, j’ai décidé de toujours centrer sur 128.

J’ai donc tenté plusieurs stratégies. Pour tous les tests qui vont suivre, l’image source utilisée est un simple carré blanc, centré, et l’illustration comporte soit l’image source, soit la transformée de Fourier, et la transformée inverse.

II. 1. Conversion brutale en un octet

Ma première tentative a été de convertir brutalement le résultat en un octet. Le résultat fut vraiment atroce car, arrondi, 255.5 donne 0. En deuxième, j’ai donc arrondi en tronquant tout ce qui était supérieur à 128 à 255, et inférieur à -128 à 0. Le résultat fut meilleur :

Nous voyons alors clairement que les hautes fréquences (de faible amplitude) sont très bien passées, donnant des contours parfaits, et que les basses fréquences dépassant largement 255, sont très mal passées, ne parvenant pas à reconstituer le centre. Cela donne alors une première idée d’effet possible, trouvée grâce à ce problème : un effet de pseudo détection de bords, simplement en effaçant les basses variations de couleurs, donnant un résultat relativement artistique. Regardons en effet le résultat appliqué sur une image un peu plus quelconque :

image source

II. 2. Mise à l’échelle avant conversion

L’idée logique venant à la suite de cela est de mettre à l’échelle en prenant la valeur maximum et en divisant toutes les valeurs par ce nombre avant de les stocker. Cette méthode donne des résultats abominables, principalement car elle ne conserve que les très basses fréquences. Les fréquences plus élevées sont ensuite toutes éliminées car le résultat de la division est proche de zéro et arrondi à zéro. Cette méthode est donc à abandonner. Voici un résultat :

image source Division fixe

II. 3. Echelle logarithmique

La troisième étape est de considérer une échelle logarithmique. Pour tenir compte des valeurs négatives, nous utilisons la fonction suivante :

inline guchar get_glog (double d)
{
  int i;
  i = (int) ( ((d>0)?log(d+1):-log(-d+1))*128 );
  return (guchar) (i>128)?255:((i<-128)?0:i+128);
}

Cette fonction s’arrange pour donner les valeurs 0 à 127 aux valeurs négatives et 128 à 255 aux valeurs positives, et d’avoir un résultat de log toujours positif (d’où le +1)

Les résultats sont très satisfaisants :

Voici un autre exemple avec deux images moins quelconques :

Pour pouvoir utiliser de façon optimale la plage 0-255 disponible, nous devons mettre à l’échelle les valeurs logarithmiques, en divisant par la valeur la plus élevée. Cette valeur doit donc être stockée pour pouvoir permettre le traitement inverse. N’ayant pas trouvé de méthode satisfaisante, la version actuelle enregistre cette information dans le dernier pixel de l’image de la transformée de Fourier.

III. Utilisation

III. 1. Installation

Ce plugin est distribué soit sous forme de fichier source à compiler, soit en un binaire pour windows, à placer dans le répertoire ‘plugins’ de GIMP. Il faut alors relancer GIMP, et les entrées ‘FFT Directe’ et ‘FFT Inverse’ sont ajoutées dans le menu Filters/Rendu. Il suffit juste de cliquer sur une de ces entrées pour lancer le plugin.

En sortie, l’image contient le résultat de la transformation.

III. 2. Ordonnancement des composantes

Aucun réordonnancement des composantes n’est effectué. L’ordre est donc conforme à celui de la transformée utilisée. D’autre part, les parties imaginaires sont actuellement traitées de la même manière que les parties réelles. Une grande partie des progrès à faire pour une prochaine version se situera donc dans ce domaine, pour rendre plus compréhensible le résultat (de même que le centrage en 0).

L’ordre des composantes utilisé par FFTW est décrit sur le schéma suivant :

ordre des composantes

Comme vous pouvez le constater la sortie de cette transformée est plus importante (de deux pixels) que l’image. Ces deux derniers pixels sont ignorés pour l’instant. Pour représenter le résultat d’une transformation de Fourier 2D, les quatre quadrants sont souvent inversés, comme décrit dans le schéma qui suit :

inversion des quadrants

Cette opération reste à effectuer.

IV. Réflexions diverses

IV. 1. FFT et JPEG

Impossible de faire une transformée de Fourier et de ne pas penser au JPEG. Le JPEG utilise en effet une DCT sur des blocs de 8×8 pixels, puis multiplie le résultat par une matrice dont le but est d’atténuer les faibles fréquences, pour que cette matrice contienne le plus de zéros possibles et puisse se comprimer le mieux possible.

Un des freins majeurs à appliquer une transformation de Fourier de manière globale à l’image était la puissance requise. Maintenant que les processeurs sont beaucoup plus performants, et que des librairies puissantes et gratuites existent, cet argument est à revoir. Cependant l’approche par blocs est intéressante, car dans les images des blocs sont souvent composés de couleurs proches. Etant donné la taille des images actuelles, des blocs de 8×8 sont peut-être un peu petits, et il serait intéressant de pouvoir augmenter leur taille, ou même de pouvoir prévoir une taille variable selon les réglages de l’utilisateur, ou selon la taille de l’image.

IV. 2. Compression

Essayez donc de compresser la transformée de Fourier par ce plug-in d’une image dans un format non destructif. Vous pourrez constater que la réduction est impressionnante. (11ko pour un fichier source de 300ko en jpeg) Cependant cette différence s’explique très simplement par l’importante altération de la qualité d’image qu’impose la quantification aux valeurs 0-255 d’un octet.

IV. 3. Stéganographie

Une des questions initiale m’ayant poussé à faire ce plug-in était de savoir s’il était possible d’utiliser l’espace fréquentiel pour y intégrer des informations, et de tester leur résistance aux altérations de l’image.

Testons donc ceci sur une image. Une fois dans l’espace fréquentiel, nous pouvons utiliser n’importe quel effet à notre disposition pour y intégrer l’information que nous voulons. Pour notre test, nous prendrons du texte, que nous inclurons avec un calque, en mettant par exemple la transparence à 6%, juste pour que le texte reste lisible, mais pour affecter le moins possible. Cependant lors de la transformée inverse les dégâts sont considérables :

Stéganographie

Il semblerait donc que la stéganographie ne soit pas envisageable par cette méthode. Cependant il existe sûrement des moyens plus efficaces et discrets de représenter l’information que comme nous l’avons fait, avec du texte qui affecte beaucoup de fréquences d’un coup.

IV. 4. Retouche d’image

Il est tout à fait envisageable d’utiliser ce plugin pour retoucher des images, et gommer des défauts. Le plus classique est par exemple un filtre passe-bas, qui coupe les moyennes et hautes fréquences. En pratique, il s’agit de gommer toutes ces fréquences (c’est à dire toute l’image sauf les coins) avec du gris neutre (RGB=128,128,128).

IV. 5. Visions Artistiques

Ce plugin peut être utilisé pour appliquer des effets spéciaux aux images, soit pour modifier une image, soit pour en créer une depuis une page blanche. Tout d’abord il faut considérer que l’on est dans l’espace de Fourier, c’est à dire que le 0 est constitué par du gris (RVB = 128,128,128). Les valeurs inférieures seront considérées comme négatives, et supérieures comme positives. Il faut faire très attention au pixel sensible permettant la normalisation pour la transformée inverse. Celui-ci est situé environ à droite de l’image à mi-hauteur. Si par hasard vous modifiez cette valeur le résultat peut être amusant. Par exemple, voici ce que donne la transformée inverse après avoir enregistré la transformée de Fourier en jpeg (qui a donc “lissé” le pixel, et lui a attribué une valeur supérieure, surexposant l’image) :

ne pas toucher au pixel de normalisation

IV. 5. a. Effets spéciaux

IV. 5. a. a. Détection de bords

Il est possible de faire une détection de bord relativement artistique. Il s’agit en fait de profiter de la saturation des valeurs au dessus de 256 pour éliminer les basses fréquences (de valeur plus élevée). Un plugin est dédié à cette action. Voici deux résultats possibles :

détection de bords détection de bords

Il est aussi possible de le faire à la main. Pour cela il suffit d’effacer (colorier par du gris RVB=128,128,128) les zones de basses fréquences, c’est à dire en haut à gauche et en haut à droite.

IV. 5. a. b. Boursouflures

Essayons d’appliquer un très léger flou à l’image. Le décalage entre les parties réelles et imaginaires va être estompé, et donc les phases modifiées. Il en résulte un effet de “boursouflrues” de l’image :

Boursouflures après l'application d'un leger flou

Le flou doit réellement être très léger, sans quoi l’image deviendra méconnaissable.

IV. 5. a. c. Plissé

En sélectionnant l’outil doigt (qui correspond à “étaler et mélanger les couleurs existantes, un peu comme on étale la peinture fraîche sur un tableau avec un doigt”) et en traçant un trait oblique rapide en haut à droite, nous obtenons :

Plissé

IV. 5. a. d. Gondolé

En ajoutant un petit rond dans le coin supérieur gauche, nous obtenons un effet de “gondolé” :

Gondolé

IV. 5. a. e. Tremblant

En éliminant les fréquences moyennes et intermédiaires, c’est à dire en sélectionnant l’ensemble de l’image sauf deux triangles en haut à gauche et en haut à droite, puis en remplissant cette zone de gris neutre (RGB=128,128,128), nous obtenons l’effet suivant :

Tremblant

IV. 5. b. Génération d’images

Il est également possible de générer des images complètement nouvelles, et ceci est d’ailleurs un fonctionnement assez intéressant du plugin. Pour ceci, il est recommandé de commencer avec l’image remplie de la couleur gris neutre (RGB=128,128,128), puis de travailler à partir de cela, en ajoutant des points en appliquant des effets,…

Voici un exemple tout simple, composé de peu de points (obtenus soit avec l’outil pinceau, soit avec l’outil accentuer) :

Ceci peut être utile par exemple pour générer des motifs qui pourront être répétés, et servir de base à l’application d’autres effets.

V. Améliorations possibles

Comme vous ave pu vous en rendre compte, ce plug-in est actuellement très basique, et les méthodes utilisées massacrent beaucoup le résultat. Il sera donc bon, de procéder à quelques améliorations possibles :

  • trouver un moyen plus intelligent pour stocker le facteur de mise à l’échelle pour la méthode logarithmique,
  • ordonner les composantes de manières plus parlantes,
  • tenir compte des deux dernières données de la transformée de Fourrier, et éventuellement supprimer une colonne de données moins importante pour pouvoir stocker celle-ci,
  • pouvoir choisir un mode de visualisation ou l’on décomposerait l’image en le module et l’argument de la transformée de Fourier (nous aurions ainsi deux images) ou les dissocier (actuellement elles sont côte à côte),
  • ainsi que toutes les améliorations relatives à l’optimisation du traitement, qui est actuellement lent, non pas du fait de la FFT, mais de tout le traitement qu’il y a autour.
  • supporter le mode 48 bits (16 bits pour les 3 composantes R,V,B) dès que GIMP le permettra, permettant ainsi d’éliminer la contrainte des 256 niveaux.
  • Optimiser les traitements, en effet, la FFT elle-même ne prend environ moins que 30% du temps. L’essentiel du temps est perdu dans la mise en place des tableaux, les recopies, les recherches de maxima, et toutes les conversions (exponentiation,…). Pour ce dernier point en particulier il serait très intéressant de créer des tables précalculées, particulièrement pour le décodage.

A propos du support 48bits (16bits par canal de couleur pour un pixel), je me suis posé la question de savoir à quel point cela améliorerait les choses. J’ai donc créé un plugin (FFT 16b Simulation) qui simule une telle transformation en 16bits. Pour cela, j’effectue successivement la transformée directe puis la transformée inverse, en intercalant entre les deux le système de codage qui simule la déperdition liée au stockage sur un mot (deux octets), soit l’utilisation d’une plage entière de 0 à 65535. Le résultat a été à la hauteur de mes espérances, puisque je ne note aucune différence visible entre l’orignal et le résultat de la simulation :

Avant simulation Après simulation 16bits

VI. Code

VI. 1. gpplugin.c

/**
 *  (c) 2002 Rémi Peyronnet
 *  Plugin GIMP : Fourier Transform
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include "gtk/gtk.h"
#include "libgimp/gimp.h"

// Uses the brillant rfftw lib
#include <rfftw.h>

/** defines ***********************************************************/

#define PLUG_IN_NAME "plug_in_fft"
#define PLUG_IN_VERSION "Februar 2002, 1.0"


/** Plugin interface *********************************************************/

void query(void);
void run(char *name, int nparams, GimpParam *param, int *nreturn_vals, GimpParam **return_vals);

inline guchar get_guchar (int i)
{
  return (guchar) (i>=255)?255:((i<0)?0:i);
}

inline guchar get_gchar128 (int i)
{
  return (guchar) (i>=(int)128)?255:((i<=(int)-128)?0:i+128);
}

inline guchar get_glog (double d)
{
  int i;
  i = (int) ( ((d>0)?log(d+1):-log(-d+1))*128 );
  return (guchar) (i>128)?255:((i<-128)?0:i+128);
}

inline double get_flog (int i)
{
  double d;
  if (i > 128)
  {
    d = exp((i-128.0)/128.0)-1;
  }
  else
  {
    d = -exp(i/128.0)+1;
  }
  return d;
}


inline double abslog(double d)
{
  if (d > 0)
  {
    return log(d+1);
  }
  else
  {
    return -log(-d+1);
  }
}


inline double absexp(double d)
{
  if (d > 0)
  {
    return exp(d)-1;
  }
  else
  {
    return -exp(-d)+1;
  }
}


GimpPlugInInfo PLUG_IN_INFO = {
  NULL, /* init_proc */
  NULL, /* quit_proc */
  query,        /* query_proc */
  run   /* run_proc */
};


MAIN()



void
query(void)
{
  /* Definition of parameters */
  static GimpParamDef args[] = {
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
    { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
    { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
  };

  static GimpParamDef *return_vals  = NULL;
  static int        nargs = sizeof(args) / sizeof(args[0]);
  static int        nreturn_vals = 0;

  gimp_install_procedure(
    "plug_in_fft_dir",
    "Transform the image with the FFT",
    "This plug-in applies a FFT to the image, for educationnal or effects purpose.",
    "Rémi Peyronnet",
    "Rémi Peyronnet",
    PLUG_IN_VERSION,
    "<Image>/Filters/Render/FFT Directe",
    "RGB*",
    GIMP_PLUGIN,
    nargs,
    nreturn_vals,
    args,
    return_vals);
  gimp_install_procedure(
    "plug_in_fft_inv",
    "Transform the image with the FFT",
    "This plug-in applies a FFT to the image, for educationnal or effects purpose.",
    "Rémi Peyronnet",
    "Rémi Peyronnet",
    PLUG_IN_VERSION,
    "<Image>/Filters/Render/FFT Inverse",
    "RGB*",
    GIMP_PLUGIN,
    nargs,
    nreturn_vals,
    args,
    return_vals);
}


void
run(char *name, int nparams, GimpParam *param,
    int *nreturn_vals, GimpParam **return_vals)
{
  /* Return values */
  static GimpParam values[1];

  gint sel_x1, sel_y1, sel_x2, sel_y2, w, h;
  gint img_height, img_width, img_bpp, cur_bpp, img_has_alpha;

  GimpDrawable     *drawable;
  GimpPixelRgn region;
  GimpRunModeType  run_mode;
  GimpPDBStatusType   status;

  gint progress, max_progress;

  gint row, col;

  rfftwnd_plan p;
  fftw_real * fft_real, v, m, mean, norm;
  fftw_complex * fft_complex;

  int fft_inv=0;

  if (strcmp(name,"plug_in_fft_inv") == 0) { fft_inv = 1; }

  *nreturn_vals = 1;
  *return_vals  = values;

  status = GIMP_PDB_SUCCESS;

  if (param[0].type!= GIMP_PDB_INT32)  status=GIMP_PDB_CALLING_ERROR;
  if (param[2].type!=GIMP_PDB_DRAWABLE)   status=GIMP_PDB_CALLING_ERROR;

  run_mode = param[0].data.d_int32;

  drawable = gimp_drawable_get(param[2].data.d_drawable);

  img_width     = gimp_drawable_width(drawable->id);
  img_height    = gimp_drawable_height(drawable->id);
  img_bpp       = gimp_drawable_bpp(drawable->id);
  img_has_alpha = gimp_drawable_has_alpha(drawable->id);
  gimp_drawable_mask_bounds(drawable->id, &sel_x1, &sel_y1, &sel_x2, &sel_y2);

  w = sel_x2 - sel_x1;
  h = sel_y2 - sel_y1;


  if (status == GIMP_PDB_SUCCESS)
  {
    guchar buf[]={128,128,128,128};
    guchar * img_pixels;

    gimp_tile_cache_ntiles((drawable->width + gimp_tile_width() - 1) / gimp_tile_width());

    gimp_progress_init("Apply Fourier transform...");

    // Process
    gimp_pixel_rgn_init (&region, drawable, sel_x1, sel_y1, w, h, FALSE, FALSE);
    img_pixels = g_new (guchar, w * h * img_bpp );
    //printf("%d %d %pn", w * h * (img_bpp / 8),img_bpp,img_pixels);
    gimp_pixel_rgn_get_rect(&region, img_pixels, sel_x1, sel_y1, w, h);

    gimp_pixel_rgn_init (&region, drawable, sel_x1, sel_y1, w, h, TRUE, TRUE);


    // FFT !

  fft_real = g_new(fftw_real, (h+2) * w);

  //norm = w*h;
  norm = sqrt((double)w*h);

  if (fft_inv == 0)
  {
    max_progress = /*w*h*/img_bpp*4;
  }
  else
  {
    max_progress = /*w*h*/img_bpp*3;
  }
  progress = 0;
  for(cur_bpp=0;cur_bpp<img_bpp;cur_bpp++)
  {

    if (fft_inv == 0)
    {
      p = rfftw2d_create_plan(w, h, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE | FFTW_IN_PLACE);
      for(col=0;col<w;col++)
      {
        for(row=0;row<h;row++)
        {
          fft_real[col*(h+2)+row]=
                 (fftw_real) (double)img_pixels[(row*w+col)*img_bpp+cur_bpp] / 256;
        }
        //progress += h;
        //gimp_progress_update((double) progress / max_progress);
      }
      progress += 1;
      gimp_progress_update((double) progress / max_progress);
      rfftwnd_one_real_to_complex(p, fft_real, NULL);
      progress += 1;
      //progress += h*w;
      gimp_progress_update((double) progress / max_progress);

      m = 0;
      for(col=0;col<w;col++)
      {
        for(row=0;row<h;row++)
        {
          v = abslog(fft_real[col*(h+2)+row] / norm);
          if ( fabs(v) > m) { m = fabs(v); }
        }
        //progress += h;
        //gimp_progress_update((double) progress / max_progress);
      }
      progress += 1;
      gimp_progress_update((double) progress / max_progress);
      for(col=0;col<w;col++)
      {
        for(row=0;row<h;row++)
        {
          v = abslog(fft_real[col*(h+2)+row] / norm) / m;
          //v = fft_real[col*(h+2)+row] / norm / m;
          //img_pixels[(row*w+col)*img_bpp+cur_bpp] =  get_gchar128( (int) ((v/m)*128) );
          //img_pixels[(row*w+col)*img_bpp] =  get_glog( (int)(v) ); 
          img_pixels[(row*w+col)*img_bpp+cur_bpp] =  get_gchar128( (int)(v*128.0) );
          //printf ("%f %f %d ; ",fft_real[col*(h+2)+row] / norm,
                     v, get_gchar128( (int)(v*128.0)));
        }
        //progress += h;
        //gimp_progress_update((double) progress / max_progress);
      }
      progress += 1;
      gimp_progress_update((double) progress / max_progress);
      img_pixels[(h*w/2-1)*img_bpp+cur_bpp] = get_guchar( m * 10 );

    }
    else
    {
      p = rfftw2d_create_plan(w, h, FFTW_COMPLEX_TO_REAL, FFTW_ESTIMATE | FFTW_IN_PLACE);
      m = (float)img_pixels[(h*w/2-1)*img_bpp+cur_bpp] / 10;
      img_pixels[(h*w/2-1)*img_bpp+cur_bpp] = 128; // Elimine _grosse_ perturbation.
      //printf("%f n",m); //getchar();
      for(col=0;col<w;col++)
      {
        for(row=0;row<h;row++)
        {
          fft_real[col*(h+2)+row]= (fftw_real) 
                 absexp( ((float)img_pixels[(row*w+col)*img_bpp+cur_bpp]-128.0) / 128.0 * m);
          //printf ("%d %f ; ",img_pixels[(row*w+col)*img_bpp+cur_bpp], 
                            fft_real[col*(h+2)+row] / norm);
          //fft_real[col*(h+2)+row]=(fftw_real) 
                            (img_pixels[(row*w+col)*img_bpp+cur_bpp]-128) / 128.0 * m;
          //fft_real[col*(h+2)+row]=(fftw_real) 
                             get_flog(img_pixels[(row*w+col)*img_bpp]);
        }
        //fft_real[col*(h+2)+h]=0;
        //fft_real[col*(h+2)+h+1]=0;
        //progress += h;
        //gimp_progress_update((double) progress / max_progress);
      }
      progress += 1;
      gimp_progress_update((double) progress / max_progress);
      //fft_real[(w/2-1)*(h+2)+h-1]=0;
      rfftwnd_one_complex_to_real(p, (struct fftw_complex *) fft_real, NULL);
      //progress += h*w;
      progress += 1;
      gimp_progress_update((double) progress / max_progress);
      for(col=0;col<w;col++)
      {
        for(row=0;row<h;row++)
        {
          v = fft_real[col*(h+2)+row] / norm;
          img_pixels[(row*w+col)*img_bpp+cur_bpp] = get_guchar((int)( (double)v*256.0));
        }
        //printf("%f ",v*256);
        //progress += h;
        //gimp_progress_update((double) progress / max_progress);
      }
      progress += 1;
      gimp_progress_update((double) progress / max_progress);
    }
  }
  rfftwnd_destroy_plan(p);
  g_free(fft_real);

    // Flush

    gimp_pixel_rgn_set_rect(&region, img_pixels, sel_x1, sel_y1, 
                            (sel_x2-sel_x1), (sel_y2-sel_y1));
    g_free (img_pixels);

    gimp_drawable_flush(drawable);
    gimp_drawable_merge_shadow(drawable->id, TRUE);
    gimp_drawable_update (drawable->id, sel_x1, sel_y1, (sel_x2-sel_x1), (sel_y2-sel_y1));
    gimp_displays_flush();
  }

  values[0].type = GIMP_PDB_STATUS;
  values[0].data.d_status = status;
  gimp_drawable_detach(drawable);
}



Conclusion

Voici donc un plugin Gimp qui permet d’avoir un premier aperçu des possibilités d’une transformée de Fourier d’une image. Il est encore très loin d’être complètement fonctionnel, et ne le sera sans doute pas avant longtemps. Il peut cependant servir simplement pour mener quelques expériences sans prétention sur la manipulation et la transformation d’images, et l’application d’une FFT globalement à l’image et non localement comme dans le JPEG.

Pour terminer sur une note d’espoir, notons que l’écueil actuel principal est la limitation des résultats à 256 valeurs, ce qui est très destructif et handicapant pour la qualité de l’image restituée. Cependant, avec le support prochain des pixels de 48 bits (3×16 bits) par GIMP, cette conversion ne sera plus limitante.

Références