Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

BINARISATION D'IMAGES


Information sur la source

Catégorie :Graphique Classé sous : traitement, images, extraction, binarisation Niveau : Initié Date de création : 27/08/2006 Vu / téléchargé: 8 787 / 866

Note :
10 / 10 - par 2 personnes
10,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (20)
Ajouter un commentaire et/ou une note

Description

Cliquez pour voir la capture en taille normale
IMPORTANT: Avant d'ouvrir les fichiers qui se terminent par ...FrameUnit.pas, BIEN LIRE LA REMARQUE FINALE!

C'est suite à une suggestion de Caribensila (voir http://www.delphifr.com/codes/RECHERCHE-SUPPRESSION-IMAGES-DOUBLE-BASEE-SUR-COMPARAISON-INTELLIGENTE_38711.aspx) que j'ai programmé ça. C'est une application simple de traitement d'images. Elle permet de faire subir à une image originale une série de transformations successives et d'afficher l'image résultante. Le but à l'origine était de trouver une méthode pour extraire un grain de beauté d'une photographie pour étudier son aspect au cours du temps.

Pour utiliser le programme, il faut choisir une image initiale, puis ajoutter des filtres, et enfin cliquer sur le bouton "Lancer le calcul". Il y a une fonction de zoom, et on peut faire s'afficher les images initiale et finale en surimpression avec une transparence paramètrable. Avec le menu principal, on peut importer et exporter des chaines de filtres (fichiers *.filter , il y en a 3 fournis).

Pour l'instant il y a 8 filtres disponibles:
+ Filtre médian: un filtre statistique qui remplace chaque pixel par sa valeur médiane à l'intérieur d'une boîte carrée de taille paramètrable (c'est à dire la valeur du milieu lorsqu'on classe les intensités des pixels voisins dans la boite par ordre croissant)
+ Extracteur de canaux: ce filtre extrait des canaux d'une image. Par exemple, on peut extraire le canal vert d'une image en RGB
+ RGB -> HSV: transformation RGB vers HSV (Hue, Saturation, Value) c'est à dire en français la teinte, la saturation et l'intensité lumineuse. C'est un autre mode de représentation des couleurs, très utile en traitement d'images.
+ HSV -> RGB: la transformation réciproque
+ RGB -> Luminance: extraction de la luminance
+ Binarisation: une fonction qui détermine, par une méthode mathématique basée sur l'étude d'ensembles aléatoires, un niveau statistiquement représentatif qui détermine 2 zones dans l'image: l'intérieur et l'extérieur. L'intérieur est transformé en blanc, l'extérieur en noir. Cette fonction est très lente, car elle met en jeu des tas de calculs.
+ Norme du gradient: calcule la norme du gradient de l'image. Utile par exemple pour la détection de bords.
+ Seuillage: met en blanc les pixels dont la valeur est plus grande qu'un certain seuil, en noir sinon.

Sauf dans le cas de RGB->HSV, HSV->RGB et "Extraction de canaux", les filtres travaillent séparément sur tous les canaux de l'image. Je n'ai pas mis de vérification du nombre de canaux, par exemple si vous essayez de transformer une image en HSV qlors qu'elle n'a qu'un seul canal, il se produira une erreur pas très explicite (access violation vraisemblablement), donc il ne faut pas faire n'importe quoi!

Les filtres sont "enchainés", c'est à dire qu'ils s'appliquent l'un après l'autre. Par exemple "RGB -> Luminance" suivi de "Norme du gradient" extraira la norme du gradient de la luminance.

Il y a des exemples de chaines de filtres fournis dans le dossier Exemples/, ainsi que des images pour tester. Dans la plupart des cas, en utilisant le filtre Luminance.filter on arrive à extraire le grain de beauté de l'image (c'est à dire que l'image est bien séparée en 2 parties, l'une étant le grain de beauté). Mais ça ne marche pas à tous les coups... Je suis en train de travailler sur une méthode plus précise, mais ça risque de prendre des temps de calcul prohibitifs...

Il y a une unité de traitement d'images fournie avec le projet: ImgUtils.pas. Elle définit une classe TBitmapData utile pour gérer les images et faire des calculs dessus. J'ai conscience que ça manque de commentaires, je les mettrai si quelqu'un les demande.
 

Source

  • unit MainFormUnit;
  • interface
  • uses
  • Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  • Dialogs, ImgUtils, ExtDlgs, JPEG, ExtCtrls, Buttons, StdCtrls, ComCtrls,
  • MedianBoxFilterFrameUnit, ChannelExtractFrameUnit, RGB2HSVFrameUnit,
  • HSV2RGBFrameUnit, RGB2LuminanceFrameUnit, BinarizeFrameUnit,
  • GradientNormFrameUnit, TresholdFrameUnit, FilterFrameUnit,
  • Menus, ToolWin, ImgList;
  • type
  • TMainForm = class(TForm)
  • OpenPictureDialog1: TOpenPictureDialog;
  • Panel1: TPanel;
  • Edit1: TEdit;
  • SpeedButton1: TSpeedButton;
  • GroupBox1: TGroupBox;
  • ListBox1: TListBox;
  • PopupMenu1: TPopupMenu;
  • ToolBar1: TToolBar;
  • ToolButton1: TToolButton;
  • ToolButton2: TToolButton;
  • Panel2: TPanel;
  • GroupBox2: TGroupBox;
  • ToolButton3: TToolButton;
  • ToolButton4: TToolButton;
  • ImageList1: TImageList;
  • ToolButton5: TToolButton;
  • Panel3: TPanel;
  • PageControl1: TPageControl;
  • TabSheet1: TTabSheet;
  • ScrollBox1: TScrollBox;
  • PaintBox1: TPaintBox;
  • TabSheet2: TTabSheet;
  • ScrollBox2: TScrollBox;
  • PaintBox2: TPaintBox;
  • TabSheet3: TTabSheet;
  • ScrollBox3: TScrollBox;
  • PaintBox3: TPaintBox;
  • GroupBox3: TGroupBox;
  • TrackBar1: TTrackBar;
  • GroupBox4: TGroupBox;
  • TrackBar2: TTrackBar;
  • MainMenu1: TMainMenu;
  • Fichier1: TMenuItem;
  • Exporterlachanedefiltres1: TMenuItem;
  • Importerunechanedefiltres1: TMenuItem;
  • Lancerlachane1: TMenuItem;
  • OpenDialog1: TOpenDialog;
  • SaveDialog1: TSaveDialog;
  • procedure FormCreate(Sender: TObject);
  • procedure SpeedButton1Click(Sender: TObject);
  • procedure FormDestroy(Sender: TObject);
  • procedure ListBox1Click(Sender: TObject);
  • procedure ToolButton2Click(Sender: TObject);
  • procedure ToolButton4Click(Sender: TObject);
  • procedure ToolButton5Click(Sender: TObject);
  • procedure FormShow(Sender: TObject);
  • procedure TrackBar1Change(Sender: TObject);
  • procedure PaintBox1Paint(Sender: TObject);
  • procedure PaintBox2Paint(Sender: TObject);
  • procedure PaintBox3Paint(Sender: TObject);
  • procedure Lancerlachane1Click(Sender: TObject);
  • procedure TrackBar2Change(Sender: TObject);
  • procedure Exporterlachanedefiltres1Click(Sender: TObject);
  • procedure Importerunechanedefiltres1Click(Sender: TObject);
  • private
  • FSrcBitmap,FDstBitmap,FPreviewBitmap:TBitmap;
  • public
  • procedure RegisterFilter(FilterClass:TFilterFrameClass);
  • procedure FilterMenuItemClick(Sender:TObject);
  • procedure UpdateGUI;
  • procedure UpdatePreview;
  • function GetZoomRect:TRect;
  • procedure ResetProcess;
  • procedure Process;
  • end;
  • var
  • MainForm: TMainForm;
  • implementation
  • {$R *.dfm}
  • procedure TMainForm.FormCreate(Sender: TObject);
  • begin
  • SpeedButton1.Align:=alRight;
  • Edit1.Align:=alRight;
  • FSrcBitmap:=TBitmap.Create;
  • FDstBitmap:=TBitmap.Create;
  • FPreviewBitmap:=TBitmap.Create;
  • RegisterFilter(TMedianBoxFilterFrame);
  • RegisterFilter(TChannelExtractFrame);
  • RegisterFilter(TRGB2HSVFrame);
  • RegisterFilter(THSV2RGBFrame);
  • RegisterFilter(TRGB2LuminanceFrame);
  • RegisterFilter(TBinarizeFrame);
  • RegisterFilter(TGradientNormFrame);
  • RegisterFilter(TTresholdFrame);
  • end;
  • procedure TMainForm.SpeedButton1Click(Sender: TObject);
  • var
  • p:TBitmap;
  • begin
  • if OpenPictureDialog1.Execute then begin
  • Edit1.Text:=OpenPictureDialog1.FileName;
  • p:=LoadBitmapFromFile(OpenPictureDialog1.FileName);
  • FSrcBitmap.Destroy;
  • FSrcBitmap:=p;
  • UpdatePreview;
  • ResetProcess;
  • Lancerlachane1.Enabled:=(FSrcBitmap.Width>0) and (FSrcBitmap.Height>0);
  • end;
  • end;
  • procedure TMainForm.FormDestroy(Sender: TObject);
  • begin
  • FSrcBitmap.Destroy;
  • FDstBitmap.Destroy;
  • FPreviewBitmap.Destroy;
  • end;
  • procedure TMainForm.RegisterFilter(FilterClass: TFilterFrameClass);
  • var
  • m:TMenuItem;
  • begin
  • m:=TMenuItem.Create(Self);
  • m.Caption:=FilterClass.Caption;
  • m.Tag:=Integer(FilterClass);
  • m.OnClick:=FilterMenuItemClick;
  • PopupMenu1.Items.Add(m);
  • RegisterClass(FilterClass);
  • end;
  • procedure TMainForm.UpdateGUI;
  • begin
  • if ListBox1.ItemIndex>-1 then begin
  • while GroupBox2.ControlCount>0 do
  • GroupBox2.Controls[0].Parent:=nil;
  • with TFilterFrame(ListBox1.Items.Objects[ListBox1.ItemIndex]) do begin
  • Align:=alClient;
  • Parent:=GroupBox2;
  • end;
  • ToolButton2.Enabled:=True;
  • end else
  • ToolButton2.Enabled:=False;
  • ToolButton4.Enabled:=ListBox1.ItemIndex>0;
  • ToolButton5.Enabled:=(ListBox1.ItemIndex>-1) and (ListBox1.ItemIndex<ListBox1.Count-1);
  • UpdatePreview;
  • end;
  • procedure TMainForm.FilterMenuItemClick(Sender: TObject);
  • var
  • f:TFrame;
  • begin
  • with TFilterFrameClass(TMenuItem(Sender).Tag) do begin
  • f:=Create(ListBox1);
  • f.Name:='';
  • ListBox1.ItemIndex:=ListBox1.Items.AddObject(Caption,f);
  • end;
  • UpdateGUI;
  • end;
  • procedure TMainForm.ListBox1Click(Sender: TObject);
  • begin
  • UpdateGUI;
  • end;
  • procedure TMainForm.ToolButton2Click(Sender: TObject);
  • var
  • a:Integer;
  • begin
  • a:=ListBox1.ItemIndex;
  • ListBox1.Items.Objects[a].Destroy;
  • ListBox1.Items.Delete(a);
  • if a>=ListBox1.Items.Count then
  • Dec(a);
  • ListBox1.ItemIndex:=a;
  • UpdateGUI;
  • end;
  • procedure TMainForm.ToolButton4Click(Sender: TObject);
  • begin
  • ListBox1.Items.Exchange(ListBox1.ItemIndex,ListBox1.ItemIndex-1);
  • UpdateGUI;
  • end;
  • procedure TMainForm.ToolButton5Click(Sender: TObject);
  • begin
  • ListBox1.Items.Exchange(ListBox1.ItemIndex,ListBox1.ItemIndex+1);
  • UpdateGUI;
  • end;
  • procedure TMainForm.FormShow(Sender: TObject);
  • begin
  • TrackBar1.SetTick(0);
  • Lancerlachane1.ImageIndex:=4;
  • end;
  • procedure TMainForm.TrackBar1Change(Sender: TObject);
  • begin
  • UpdatePreview;
  • end;
  • procedure TMainForm.UpdatePreview;
  • var
  • r:TRect;
  • begin
  • r:=GetZoomRect;
  • PaintBox1.BoundsRect:=r;
  • PaintBox1.Invalidate;
  • PaintBox2.BoundsRect:=r;
  • PaintBox2.Invalidate;
  • PaintBox3.BoundsRect:=r;
  • PaintBox3.Invalidate;
  • end;
  • function TMainForm.GetZoomRect: TRect;
  • const
  • T:array[-5..5] of Single=(0.1,0.2,0.3,0.5,0.75,1,1.5,2,3,5,10);
  • begin
  • Result.TopLeft:=Point(0,0);
  • Result.Right:=Round(FSrcBitmap.Width*T[TrackBar1.Position]);
  • Result.Bottom:=Round(FSrcBitmap.Height*T[TrackBar1.Position]);
  • end;
  • procedure TMainForm.PaintBox1Paint(Sender: TObject);
  • begin
  • PaintBox1.Canvas.StretchDraw(GetZoomRect,FSrcBitmap);
  • end;
  • procedure TMainForm.PaintBox2Paint(Sender: TObject);
  • begin
  • PaintBox2.Canvas.StretchDraw(GetZoomRect,FDstBitmap);
  • end;
  • procedure TMainForm.PaintBox3Paint(Sender: TObject);
  • var
  • BF:TBlendFunction;
  • begin
  • FPreviewBitmap.Width:=FSrcBitmap.Width;
  • FPreviewBitmap.Height:=FSrcBitmap.Height;
  • FPreviewBitmap.Canvas.Draw(0,0,FSrcBitmap);
  • with BF do begin
  • BlendOp:=AC_SRC_OVER;
  • BlendFlags:=0;
  • SourceConstantAlpha:=TrackBar2.Position;
  • AlphaFormat:=0;
  • end;
  • with FDstBitmap do
  • Windows.AlphaBlend(FPreviewBitmap.Canvas.Handle,0,0,FPreviewBitmap.Width,FPreviewBitmap.Height,Canvas.Handle,0,0,Width,Height,BF);
  • PaintBox3.Canvas.StretchDraw(GetZoomRect,FPreviewBitmap);
  • end;
  • procedure TMainForm.Process;
  • var
  • p:TBitmapData;
  • a:Integer;
  • begin
  • p:=TBitmapData.CreateAsRGB(FSrcBitmap);
  • try
  • for a:=0 to ListBox1.Items.Count-1 do
  • with TFilterFrame(ListBox1.Items.Objects[a]) do
  • try
  • Filter(p);
  • except
  • on e:Exception do begin
  • Messagebox(0,PChar('Le filtre "'+Caption+'" n°'+IntToStr(a)+' a provoqué une erreur: '#13+e.Message),PChar('Exception '+e.ClassName),MB_ICONERROR);
  • raise;
  • end;
  • else
  • Messagebox(0,PChar('Le filtre "'+Caption+'" n°'+IntToStr(a)+' a provoqué une erreur non spécifiée'),PChar('Exception inconnue'),MB_ICONERROR);
  • raise;
  • end;
  • p.WriteToBitmap(FDstBitmap);
  • finally
  • p.Destroy;
  • UpdatePreview;
  • end;
  • end;
  • procedure TMainForm.ResetProcess;
  • begin
  • FDstBitmap.Width:=0;
  • FDstBitmap.Height:=0;
  • UpdatePreview;
  • end;
  • procedure TMainForm.Lancerlachane1Click(Sender: TObject);
  • begin
  • Process;
  • end;
  • procedure TMainForm.TrackBar2Change(Sender: TObject);
  • begin
  • PaintBox3Paint(nil);
  • end;
  • procedure TMainForm.Exporterlachanedefiltres1Click(Sender: TObject);
  • var
  • f:TFileStream;
  • a,b:Integer;
  • s:string;
  • begin
  • if SaveDialog1.Execute then begin
  • f:=TFileStream.Create(SaveDialog1.FileName,fmOpenWrite or fmCreate);
  • try
  • f.Seek(0,soFromBeginning);
  • for a:=0 to ListBox1.Items.Count-1 do begin
  • s:=ListBox1.Items.Objects[a].ClassName;
  • b:=Length(s);
  • f.Write(b,SizeOf(b));
  • f.Write(s[1],b);
  • f.WriteComponent(ListBox1.Items.Objects[a] as TComponent);
  • end;
  • finally
  • f.Destroy;
  • end;
  • end;
  • end;
  • procedure TMainForm.Importerunechanedefiltres1Click(Sender: TObject);
  • var
  • f:TFileStream;
  • g:TFilterFrame;
  • a:Integer;
  • s:string;
  • begin
  • if OpenDialog1.Execute then begin
  • f:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
  • try
  • for a:=0 to ListBox1.Items.Count-1 do
  • ListBox1.Items.Objects[a].Destroy;
  • ListBox1.Items.Clear;
  • ListBox1.ItemIndex:=-1;
  • f.Seek(0,soFromBeginning);
  • while f.Position<f.Size do begin
  • f.Read(a,SizeOf(a));
  • SetLength(s,a);
  • f.Read(s[1],a);
  • g:=TFilterFrameClass(GetClass(s)).Create(nil);
  • g.DestroyComponents;
  • g.Name:='';
  • f.ReadComponent(g);
  • ListBox1.Items.AddObject(g.Caption,g);
  • end;
  • finally
  • f.Destroy;
  • UpdateGUI;
  • end;
  • end;
  • end;
  • end.
unit MainFormUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ImgUtils, ExtDlgs, JPEG, ExtCtrls, Buttons, StdCtrls, ComCtrls,
  MedianBoxFilterFrameUnit, ChannelExtractFrameUnit, RGB2HSVFrameUnit,
  HSV2RGBFrameUnit, RGB2LuminanceFrameUnit, BinarizeFrameUnit,
  GradientNormFrameUnit, TresholdFrameUnit, FilterFrameUnit,
  Menus, ToolWin, ImgList;

type
  TMainForm = class(TForm)
    OpenPictureDialog1: TOpenPictureDialog;
    Panel1: TPanel;
    Edit1: TEdit;
    SpeedButton1: TSpeedButton;
    GroupBox1: TGroupBox;
    ListBox1: TListBox;
    PopupMenu1: TPopupMenu;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    Panel2: TPanel;
    GroupBox2: TGroupBox;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ImageList1: TImageList;
    ToolButton5: TToolButton;
    Panel3: TPanel;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    ScrollBox1: TScrollBox;
    PaintBox1: TPaintBox;
    TabSheet2: TTabSheet;
    ScrollBox2: TScrollBox;
    PaintBox2: TPaintBox;
    TabSheet3: TTabSheet;
    ScrollBox3: TScrollBox;
    PaintBox3: TPaintBox;
    GroupBox3: TGroupBox;
    TrackBar1: TTrackBar;
    GroupBox4: TGroupBox;
    TrackBar2: TTrackBar;
    MainMenu1: TMainMenu;
    Fichier1: TMenuItem;
    Exporterlachanedefiltres1: TMenuItem;
    Importerunechanedefiltres1: TMenuItem;
    Lancerlachane1: TMenuItem;
    OpenDialog1: TOpenDialog;
    SaveDialog1: TSaveDialog;
    procedure FormCreate(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure ToolButton2Click(Sender: TObject);
    procedure ToolButton4Click(Sender: TObject);
    procedure ToolButton5Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
    procedure PaintBox2Paint(Sender: TObject);
    procedure PaintBox3Paint(Sender: TObject);
    procedure Lancerlachane1Click(Sender: TObject);
    procedure TrackBar2Change(Sender: TObject);
    procedure Exporterlachanedefiltres1Click(Sender: TObject);
    procedure Importerunechanedefiltres1Click(Sender: TObject);
  private
    FSrcBitmap,FDstBitmap,FPreviewBitmap:TBitmap;
  public
    procedure RegisterFilter(FilterClass:TFilterFrameClass);
    procedure FilterMenuItemClick(Sender:TObject);

    procedure UpdateGUI;
    procedure UpdatePreview;

    function GetZoomRect:TRect;

    procedure ResetProcess;
    procedure Process;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  SpeedButton1.Align:=alRight;
  Edit1.Align:=alRight;
  FSrcBitmap:=TBitmap.Create;
  FDstBitmap:=TBitmap.Create;
  FPreviewBitmap:=TBitmap.Create;
  RegisterFilter(TMedianBoxFilterFrame);
  RegisterFilter(TChannelExtractFrame);
  RegisterFilter(TRGB2HSVFrame);
  RegisterFilter(THSV2RGBFrame);
  RegisterFilter(TRGB2LuminanceFrame);
  RegisterFilter(TBinarizeFrame);
  RegisterFilter(TGradientNormFrame);
  RegisterFilter(TTresholdFrame);
end;

procedure TMainForm.SpeedButton1Click(Sender: TObject);
var
  p:TBitmap;
begin
  if OpenPictureDialog1.Execute then begin
    Edit1.Text:=OpenPictureDialog1.FileName;
    p:=LoadBitmapFromFile(OpenPictureDialog1.FileName);
    FSrcBitmap.Destroy;
    FSrcBitmap:=p;
    UpdatePreview;
    ResetProcess;
    Lancerlachane1.Enabled:=(FSrcBitmap.Width>0) and (FSrcBitmap.Height>0);
  end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FSrcBitmap.Destroy;
  FDstBitmap.Destroy;
  FPreviewBitmap.Destroy;
end;

procedure TMainForm.RegisterFilter(FilterClass: TFilterFrameClass);
var
  m:TMenuItem;
begin
  m:=TMenuItem.Create(Self);
  m.Caption:=FilterClass.Caption;
  m.Tag:=Integer(FilterClass);
  m.OnClick:=FilterMenuItemClick;
  PopupMenu1.Items.Add(m);
  RegisterClass(FilterClass);
end;

procedure TMainForm.UpdateGUI;
begin
  if ListBox1.ItemIndex>-1 then begin
    while GroupBox2.ControlCount>0 do
      GroupBox2.Controls[0].Parent:=nil;
    with TFilterFrame(ListBox1.Items.Objects[ListBox1.ItemIndex]) do begin
      Align:=alClient;
      Parent:=GroupBox2;
    end;
    ToolButton2.Enabled:=True;
  end else
    ToolButton2.Enabled:=False;
  ToolButton4.Enabled:=ListBox1.ItemIndex>0;
  ToolButton5.Enabled:=(ListBox1.ItemIndex>-1) and (ListBox1.ItemIndex<ListBox1.Count-1);
  UpdatePreview;
end;

procedure TMainForm.FilterMenuItemClick(Sender: TObject);
var
  f:TFrame;
begin
  with TFilterFrameClass(TMenuItem(Sender).Tag) do begin
    f:=Create(ListBox1);
    f.Name:='';
    ListBox1.ItemIndex:=ListBox1.Items.AddObject(Caption,f);
  end;
  UpdateGUI;
end;

procedure TMainForm.ListBox1Click(Sender: TObject);
begin
  UpdateGUI;
end;

procedure TMainForm.ToolButton2Click(Sender: TObject);
var
  a:Integer;
begin
  a:=ListBox1.ItemIndex;
  ListBox1.Items.Objects[a].Destroy;
  ListBox1.Items.Delete(a);
  if a>=ListBox1.Items.Count then
    Dec(a);
  ListBox1.ItemIndex:=a;
  UpdateGUI;
end;

procedure TMainForm.ToolButton4Click(Sender: TObject);
begin
  ListBox1.Items.Exchange(ListBox1.ItemIndex,ListBox1.ItemIndex-1);
  UpdateGUI;
end;

procedure TMainForm.ToolButton5Click(Sender: TObject);
begin
  ListBox1.Items.Exchange(ListBox1.ItemIndex,ListBox1.ItemIndex+1);
  UpdateGUI;
end;

procedure TMainForm.FormShow(Sender: TObject);
begin
  TrackBar1.SetTick(0);
  Lancerlachane1.ImageIndex:=4;
end;

procedure TMainForm.TrackBar1Change(Sender: TObject);
begin
  UpdatePreview;
end;

procedure TMainForm.UpdatePreview;
var
  r:TRect;
begin
  r:=GetZoomRect;
  PaintBox1.BoundsRect:=r;
  PaintBox1.Invalidate;
  PaintBox2.BoundsRect:=r;
  PaintBox2.Invalidate;
  PaintBox3.BoundsRect:=r;
  PaintBox3.Invalidate;
end;

function TMainForm.GetZoomRect: TRect;
const
  T:array[-5..5] of Single=(0.1,0.2,0.3,0.5,0.75,1,1.5,2,3,5,10);
begin
  Result.TopLeft:=Point(0,0);
  Result.Right:=Round(FSrcBitmap.Width*T[TrackBar1.Position]);
  Result.Bottom:=Round(FSrcBitmap.Height*T[TrackBar1.Position]);
end;

procedure TMainForm.PaintBox1Paint(Sender: TObject);
begin
  PaintBox1.Canvas.StretchDraw(GetZoomRect,FSrcBitmap);
end;

procedure TMainForm.PaintBox2Paint(Sender: TObject);
begin
  PaintBox2.Canvas.StretchDraw(GetZoomRect,FDstBitmap);
end;

procedure TMainForm.PaintBox3Paint(Sender: TObject);
var
  BF:TBlendFunction;
begin
  FPreviewBitmap.Width:=FSrcBitmap.Width;
  FPreviewBitmap.Height:=FSrcBitmap.Height;
  FPreviewBitmap.Canvas.Draw(0,0,FSrcBitmap);
  with BF do begin
    BlendOp:=AC_SRC_OVER;
    BlendFlags:=0;
    SourceConstantAlpha:=TrackBar2.Position;
    AlphaFormat:=0;
  end;
  with FDstBitmap do
    Windows.AlphaBlend(FPreviewBitmap.Canvas.Handle,0,0,FPreviewBitmap.Width,FPreviewBitmap.Height,Canvas.Handle,0,0,Width,Height,BF);
  PaintBox3.Canvas.StretchDraw(GetZoomRect,FPreviewBitmap);
end;

procedure TMainForm.Process;
var
  p:TBitmapData;
  a:Integer;
begin
  p:=TBitmapData.CreateAsRGB(FSrcBitmap);
  try
    for a:=0 to ListBox1.Items.Count-1 do
      with TFilterFrame(ListBox1.Items.Objects[a]) do
        try
          Filter(p);
        except
          on e:Exception do begin
            Messagebox(0,PChar('Le filtre "'+Caption+'" n°'+IntToStr(a)+' a provoqué une erreur: '#13+e.Message),PChar('Exception '+e.ClassName),MB_ICONERROR);
            raise;
          end;
        else
          Messagebox(0,PChar('Le filtre "'+Caption+'" n°'+IntToStr(a)+' a provoqué une erreur non spécifiée'),PChar('Exception inconnue'),MB_ICONERROR);
          raise;
        end;
    p.WriteToBitmap(FDstBitmap);
  finally
    p.Destroy;
    UpdatePreview;
  end;
end;

procedure TMainForm.ResetProcess;
begin
  FDstBitmap.Width:=0;
  FDstBitmap.Height:=0;
  UpdatePreview;
end;

procedure TMainForm.Lancerlachane1Click(Sender: TObject);
begin
  Process;
end;

procedure TMainForm.TrackBar2Change(Sender: TObject);
begin
  PaintBox3Paint(nil);
end;

procedure TMainForm.Exporterlachanedefiltres1Click(Sender: TObject);
var
  f:TFileStream;
  a,b:Integer;
  s:string;
begin
  if SaveDialog1.Execute then begin
    f:=TFileStream.Create(SaveDialog1.FileName,fmOpenWrite or fmCreate);
    try
      f.Seek(0,soFromBeginning);
      for a:=0 to ListBox1.Items.Count-1 do begin
        s:=ListBox1.Items.Objects[a].ClassName;
        b:=Length(s);
        f.Write(b,SizeOf(b));
        f.Write(s[1],b);
        f.WriteComponent(ListBox1.Items.Objects[a] as TComponent);
      end;
    finally
      f.Destroy;
    end;
  end;
end;

procedure TMainForm.Importerunechanedefiltres1Click(Sender: TObject);
var
  f:TFileStream;
  g:TFilterFrame;
  a:Integer;
  s:string;
begin
  if OpenDialog1.Execute then begin
    f:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
    try
      for a:=0 to ListBox1.Items.Count-1 do
        ListBox1.Items.Objects[a].Destroy;
      ListBox1.Items.Clear;
      ListBox1.ItemIndex:=-1;
      f.Seek(0,soFromBeginning);
      while f.Position<f.Size do begin
        f.Read(a,SizeOf(a));
        SetLength(s,a);
        f.Read(s[1],a);
        g:=TFilterFrameClass(GetClass(s)).Create(nil);
        g.DestroyComponents;
        g.Name:='';
        f.ReadComponent(g);
        ListBox1.Items.AddObject(g.Caption,g);
      end;
    finally
      f.Destroy;
      UpdateGUI;
    end;
  end;
end;

end.

Conclusion

Remarque finale: les frames qui sont dans les fichiers se terminant par ...FrameUnit.pas sont des descendants de TFilterFrame. Pour cette raison, lorsqu'on les ouvre dans Delphi certaines propriétés qui ne devraient pas être là sont rajouttées dans le fichier .dfm correspondant par Delphi, car l'IDE ne connait pas la classe TFilterFrame. Ces 3 propriétés incorrectes sont les suivantes:

  OldCreateOrder = True
  PixelsPerInch = 96
  TextHeight = 13

Si vous ouvrez l'un de ces fichiers avec Delphi, il faudra éditer manuellement le fichier *.dfm correspondant (par exemple en faisant Alt+F12) et supprimer ces 3 propriétés, enregistrer et fermer le fichier avant de compiler. Sinon, lors de la création du filtre correspondant, il se produira une erreur "Error while reading property OldCreateOrder: property does not exist". Il existe un moyen de contourner le problème, mais il implique l'installation d'un package, et je ne voulais pas alourdir le code.
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Commentaires et avis

signaler à un administrateur
Commentaire de CptPingu le 28/08/2006 00:05:12

Bonne source, avec des applications tres utiles.

Pour extraire une "sous-image" d'une image, je peux te conseiller le filtre de Gabor (je l'ai utilisé pour mon projet de 2eme année sur la reconnaissance digitale). Il te faut recupérer la frequence et la direction de l'image, puis les mettre en parametres du filtre.

Je n'ai pas le temps de te convertir les sources en Delphi, mais je peux t'envoyer les sources en C si ca t'interesse.

signaler à un administrateur
Commentaire de Forman le 28/08/2006 00:16:30

Oui ça m'intéresse.
Tu peux me les envoyer ici: feuvrier "at" clipper.ens.fr
Merci

signaler à un administrateur
Commentaire de f0xi le 28/08/2006 00:54:48 administrateur CS

Hey ça a l'air vachement interressant tout ça ...

par contre j'ai vus une erreur dans le fichier TresholdFrameUnit

procedure TTresholdFrame.TrackBar1Change(Sender: TObject);
begin
  Panel1.Caption:=Format('Seuil minimum: %f',[TrackBar1.Position]);
end;

c'est %d pour integer et pas %f ! tu n'as pas d'erreur qui se produit ?
sinon il faudrait ecrire :
Format('Seuil minimum: %f',[TrackBar1.Position/1]);

ce qui boufferais un peu plus de cycles a cause de la division.

aprés dans mainform :

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FSrcBitmap.Free;
  FDstBitmap.Free;
  FPreviewBitmap.Free;
end;

on ne doit pas appeler Destroy a moins d'etre sur du pourquoi on appel pas Free.
J'explique pourquoi, Free verifie que l'objet est instancié puis appel destroy ... destroy ne verifie pas si l'objet est instancié et donc peu provoquer des erreurs quand objet = nil.

ensuite dans :

procedure TMainForm.Exporterlachanedefiltres1Click(Sender: TObject);
var
  f : TFileStream;
  a : Integer;
  s : ShortString;
begin
  if SaveDialog1.Execute then begin
    f := TFileStream.Create(SaveDialog1.FileName,fmOpenWrite or fmCreate);
    try
      // pas besoin de seeker aprés creation.
      //f.Seek(0,soFromBeginning);

      for a:=0 to ListBox1.Items.Count-1 do begin
        S := ListBox1.Items.Objects[a].ClassName;
        // Mieux vaut utilise WriteBuffer que Write
        // en utilisant le type ShortString on evite de faire quelque manipulation en plus
        // shortstring est une chaine AZT et son premier byte determine la taille de la chaine
        // il nous faut donc ajouter 1 a la longeur pour ecrire l'octet de taille et la chaine.
        f.WriteBuffer(S,Length(S)+1);
        f.WriteComponent(ListBox1.Items.Objects[a] as TComponent);
      end;
    finally
      f.Free;
    end;
  end;
end;

on modifie la lecture en consequence :

procedure TMainForm.Importerunechanedefiltres1Click(Sender: TObject);
var
  f : TFileStream;
  g : TFilterFrame;
  a : Integer;
  s : shortstring;
begin
  if OpenDialog1.Execute then begin
    f:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
    try
      for a:=0 to ListBox1.Items.Count-1 do
        ListBox1.Items.Objects[a].Destroy;
      ListBox1.Items.Clear;
      ListBox1.ItemIndex:=-1;
      // pas besoin de seeker.
      // f.Seek(0,soFromBeginning);
      while f.Position < f.Size do begin
        f.ReadBuffer(A,SizeOf(byte));
        f.Position := f.Position - 1;
        f.ReadBuffer(S,A+1);

        // ici la methode me semble suspecte ... car tu ne libere pas G
        // cela pourrait entrainer des surcharge de memoire voir des erreurs d'allocation
        // il faudrait donc utiliser un parent ou un tableau dynamique pour les objets
        // et surtout ne pas oublier de les liberer a la fin du programme ou avant chaque modification.
        // exemple : SetLength(Filters, Length(Filters)+1);
        // Filters[high(Filters)] := TFilterFrameClasse....create(nil);
        // avec Filters : array of TFilterFrameClass;
        // la liberation se ferait dans une boucle : for i := 0 to high(Filters) do Filters[i].Free;
        g := TFilterFrameClass(GetClass(S)).Create(nil);
        g.DestroyComponents;
        g.Name:='';
        f.ReadComponent(g);
        ListBox1.Items.AddObject(g.Caption,g);
      end;
    finally
      f.Free;
      UpdateGUI;
    end;
  end;
end;


dans imgutils il y'a aussi un truc a ameliorer :

function LoadBitmapFromFile(const FileName : string; BMP :TBitmap) : boolean;
// c'est l'utilisateur qui se charge de la creation de BMP! c'est mieux ainsi et offre plus de souplesse.
var
  p : TPicture;
begin
  result := false;
  if (not FileExists(FileName)) or (not Assigned(BMP)) then exit;

  p := TPicture.Create;
  try
    p.LoadFromFile(FileName);
    // on prefereras la methode qui provoque le moins d'erreur.
    BMP.Width  := p.Width;
    BMP.Height := p.Height;
    BMP.Canvas.Draw(0,0,p.Graphic);
    result     := true;
  finally
    p.Free;
  end;
end;


un oublis de else :

function TruncByte(x:Single):Byte;
begin
  if x < 0 then
     x := 0
  else
  if x > 255 then
     x := 255;

  Result:=Round(x);
end;


attention a GetMem et FreeMem ... mieux vaut utilisee New et Dispose :
new(p);
dispose(p);
cela evite d'avoir a gerer et calculer la taille memoire a allouer, surtout quand il s'agit de type tableau statique.

bon sinon j'ai pas ete plus loin ...
tout est correct dans l'ensemble, du bon boulot quoi, il est dommage de ne pas avoir de commentaire, surrement une refact a venir ?

bon taff.

signaler à un administrateur
Commentaire de Forman le 28/08/2006 13:24:43

Merci fOxi, je corrigerai.
Par contre je ne vois pas où j'ai utilisé un GetMem avec une variable de taille statique?

Bon je posterai une autre version prochainement, avec des commentaires.

signaler à un administrateur
Commentaire de DRJEROME le 28/08/2006 14:13:20

Salut,
pouah un mélanome.... ça me rappelle mes consults de dépistage

sinon, félicitation...il travaille le Forman !

signaler à un administrateur
Commentaire de Caribensila le 29/08/2006 12:14:11

Salut Forman,
Bin, quand tu t'y mets, tu ne fais pas les choses à moitié! ;)
Toujours aussi impressionnant ton travail!

"Je suis en train de travailler sur une méthode plus précise, mais ça risque de prendre des temps de calcul prohibitifs"...
Une idée, comme ça...
Pourquoi ne pas laisser à l'utilisateur le soin de sélectionner à la souris une zone de détourage qui seule sera analysée? En diminuant le nombre de pixels à traiter, on diminuera le temps de calcul d'autant, non?
Sinon,j'attends ta version commentée. Ca me sera bien utile...
Bravo et merci!

signaler à un administrateur
Commentaire de elguevel le 29/08/2006 12:52:24

Forman le spécialiste du graphisme ... T'aurai pas travaillé chez Adobe toi ?

En tout cas bravo pour tes projets qui sont assez collossaux pour des réponses de Forum ! :-)

signaler à un administrateur
Commentaire de Forman le 29/08/2006 13:45:46

Caribensila: l'idée, c'était justement que l'utilisateur n'ait qu'à prendre la photo, la donner au soft, celui ci extrait la "zone d'intérêt", stocke la nouvelle photo dans une base de données et déclenche une alarme en cas de changement de texture par exemple. Laisser un humain découper la zone, c'est prendre le risque qu'il ne le fasse pas toujours pareil... et donc d'avoir qqchose de peu fiable. Mais j'ai bon espoir de faire qqchose qui fonctionne quasiment à tous les coups pour l'extraction de la zone d'intérêt: ma méthode de binarisation devrait fonctionner quasiment à chaque fois sur le canal de la teinte (après transformation RGB->HSV). Il faut juste que je trouve un moyen de prendre en compte le caractère cyclique de cette composante et ça devrait fonctionner. Après, je pense qu'il faudrait utiliser la méthode que CptPingu m'a envoyée en C pour comparer les variations de texture.

Elguevel: lol non j'ai juste fait un DEA de traitement d'images il y a 3 ans. C'est celui de l'ENS Cachan, il est très intéressant, et contient aussi une partie traitement du signal/réseaux de neurones.

signaler à un administrateur
Commentaire de Caribensila le 29/08/2006 14:39:04

Je te suis, Forman.
Un soft utilisable par des non-spécialites sera une bonne chose pour multiplier les dépistages.
C'est DRJEROME qui sera content de pouvoir faire ça "at home"!  ;)

signaler à un administrateur
Commentaire de blatour le 05/09/2006 12:37:34

Bonjour tout le monde,

Moi je vis en Afrique à Dakar. Ton algo d'analyse peut t'il être adapté aussi à des peaux noires ?

Ici ça serait vraiment trés utile car il y a beaucoup de ce cancer.
Il serait certainement intéressant de créer une zone de développement pour créer un logiciel OpenSource à l'attention du corps médical. Ici en Afrique, les médecins n'ont pas la même capacité d'investissement qu'en Europe.

Qu'en pensez-vous ?

signaler à un administrateur
Commentaire de blatour le 05/09/2006 12:38:08

J'ai oublié de dire bravo pour ce travail.

lol

signaler à un administrateur
Commentaire de Caribensila le 05/09/2006 13:43:54

Salut Blatour,

"un logiciel OpenSource à l'attention du corps médical"
C'est un peu ce qui se passe, non?

Pour les peaux noires, je pense que le problème est et restera douloureux. Perso, j'avais pensé à des photos prises sous éclairage UV ou autre (ou même ultrasons comme les échographies). Il faudrait tester. En tout cas, il serait utile de disposer de telles photos pour faire des tests... Malgré mes recherches, je n'en ai pas trouvé sur Internet. Si tu peux nous aider...

signaler à un administrateur
Commentaire de blatour le 05/09/2006 14:00:13

Bonjour Caribensila,

Déjà quand je parle d'un logiciel OpenSource c'est de proposer une solution complète pour cette fonctionnalité aux médecins (mise en place d'une base de données local, interface graphique d'utilisation, fichier d'aide, etc...). D'autant plus que si le logiciel fonctionne bien, on peut trouver des subventions pour développer le projet.

Pour récupérer ce type de photo ben je ne sais pas si les médecins sont équipés de tout ça ici. Moi ma femme est sénégalaise alors je vous en ferais bien des photos mais j'ai pas le système d'éclairage adéquat (UV ou autre).

Il faudrait que je me renseigne auprès des dermatologues locaux pour voir ce qu'ils en pensent.

Mais bon déjà attendons que Forman nous propose sa version finale d'analyse de l'image.

signaler à un administrateur
Commentaire de Forman le 05/09/2006 16:35:29

Salut Blatour

En ce moment je suis assez pris par la rentrée et pas mal d'autres projets en cours, donc je ne pense pas avoir le temps de beaucoup avancer sur ce projet d'ici à fin septembre au moins. En attendant, si tu as la possibilité de prendre en photo (avec un appareil numérique standard en couleurs) un grain de beauté sur une peau noire, ça m'intéresse. La coloration spécifique aux grains de beauté (du moins pour les photos que j'ai vu jusqu'ici) change surtout la teinte de la peau, plutôt que le contraste. Donc il n'est pas exclu que cette donnée (la teinte donc, qu'on peut extraire d'une photo couleur) soit suffisante pour analyser des grains de beauté peu contrastés (comme sur une peau noire par exemple).

Pour me joindre: feuvrier "at" clipper.ens.fr

signaler à un administrateur
Commentaire de vienbv le 01/03/2008 17:47:34

Merci pour votre code, cela m'aide beaucoup.

signaler à un administrateur
Commentaire de nethacker le 27/07/2008 23:19:52

Comment binariser une image en rendant tout ce qui est dans une couleur 0 et tout ce qui ne l'est pas 1 ? pour avoir enfin une image blanc et noir !

signaler à un administrateur
Commentaire de CptPingu le 27/07/2008 23:51:17

Cette question aurait plus sa place sur le forum qu'ici.
Toutefois, pour réaliser cela, tu dois convertir ton image en noir et blanc
pixel.rouge = (pixel.rouge + pixel.vert + pixel.bleu) / 3
pixel.vert = (pixel.rouge + pixel.vert + pixel.bleu) / 3
pixel.bleu = (pixel.rouge + pixel.vert + pixel.bleu) /3

Puis la binariser:
pixel.rouge = si pixel.rouge > 127 alors blanc(0XFF) sinon noir (0X00)
pixel.vert = pixel.rouge
pixel.bleu = pixel.rouge

signaler à un administrateur
Commentaire de Forman le 28/07/2008 00:04:48

Ben il y a la fonction Binarize dans ImgUtils.pas qui fait ça aussi, c'est même le principal intérêt de ce code...

Tout dépend dans quel contexte tu veux faire ça. Si tu veux une méthode simple (et rapide!) celle de CptPingu est très bien. Peut-être d'ailleurs faudrait-il plutôt faire son test avec la couleur verte, qui en général est mieux liée à l'intensité lumineuse.

Ma méthode est plus complexe, et sert à déterminer un seuil (la valeur 127 mentionnée par CptPingu) qui soit adapté à la statistique de l'image, par exemple pour extraire un objet sur un fond uni. Ca prend pas mal de temps. Une fois le seuil calculé, c'est exactement le même algo en remplaçant 127 par la valeur qu'on a trouvée.

signaler à un administrateur
Commentaire de Forman le 28/07/2008 00:12:32

Pour faire la méthode de CptPingu avec mon source, tu peux utiliser la chaine de filtres suivante:
-RGB -> Luminance
-Seuillage (avec une valeur paramétrable)

Autre solution (mais beaucoup plus lente!):
-RGB -> Luminance
-Binarization (avec distance euclidienne et norme L infini)

signaler à un administrateur
Commentaire de Caribensila le 28/07/2008 09:04:57

@NetHacker
En résumé :
1ère étape: Convertir ton image en niveaux de gris.
2étape    : Déterminer un seuil de gris sous lequel les pixels deviendront blancs, et au dessus duquel les pixels deviendront noirs.

@Tous
Mais il y a une autre façon de transformer une image en niveaux de gris, autre que celle proposée par CPTPINGU. C'est :

Gris = R*0.2125 + V*0.7154 + B*0.0721

Cette méthode est plus proche de la sensibilité réelle de notre oeuil aux couleurs.

La question que je me pose, perso, c'est de savoir laquelle de ces deux méthodes est la mieux adaptée à un traitement tel que celui que nous propose Forman ici.
Et, question subsidiaire : Est-ce que les gris des anciennes épreuves dites  "en noir et blanc" des photos argentiques tiennent compte de notre sensibilité aux couleurs, ou est-ce qu'ils ne sont qu'un simple effet photochimique sans correctif?

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

à l'aide, recherche d'algo de traitement d'images [ par czone29 ] Bonjour,je suis &#224; la recherche d'algorithmes de traitement d'images comme le plus proche voisin, l'interpolation bilin&#233;aire et la convolutio Extraction du texte à partir des images [ par Adminsma ] Salut tout le monde, Je veux extraire une partie spécifique (Texte) d'une image à l'aide de Delphi. Mais malheureusement j'ai pas trouvé le composant Extraction sous chaine dans une variable [ par franklin007 ] Bonjour,Etant débutant en delphi, je suis confronté à un  problème.Je cherche un moyen d'extraire une sous chaine d'une chaine, et de placer la sous c Cryptographie des images numerique [ par rt15 ] [quote=alida1986]bonjour;je suis une etudiente de 5ieme année ingenieur  je fais mon projet fin d''etude concernnant la cryptographie des images numer Sauvegarde d'images avec WebBrowser [ par duaru157 ] Bonjour a tous,Je viens tout juste de découvrir WebBrowser.Je voudrai savoir comment en ayant une page internet chargée sur le WebBrowser, découvrir l traitement de texte [ par tof62bis ] Bonjour a tous, J'ai besoin d'un coup de main Voila je recupere dans un memo 40 ligne d'une discutions dans un chat et jaimerai savoir comment recuper Inclures des images dans le projet (executable) [ par NivekR ] Bonsoir à tous et à toutes,Voilà mon souci, j'ai crée une petite application nécessitant des images chargées aléatoirement.Pour le moment, il faut que Drawgrid insertion images (Boucles imbriquées) [ par djzeg ] Bonjour a tous Je suis actuellement en train de creer un logiciel utilisant une draw grid  pour  inserer des images le probleme est dans l'insertion d Problème affectation dynamique ImageList à un ComboBoxEx.Images [ par informatixo ] Bonsoir le forum,J'ai un problème avec le composant ComboBoxEx et plus particulièrement avec sa propriété Images.J'ai créé une procédure qui permet de ImageList sous Delphi 7 [ par Bacterius ] Bonjour, j'ai un problème avec le composant TImageList sous Delphi 7. En effet, je n'arrive pas à ajouter des images (que ce soient des images 256 cou


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Comparez les prix Nouvelle version


LG KP501

Entre 9€ et 159€


Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,437 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.