Accueil > > > RECHERCHE / SUPPRESSION D'IMAGES EN DOUBLE (BASÉE SUR UNE COMPARAISON "INTELLIGENTE")
RECHERCHE / SUPPRESSION D'IMAGES EN DOUBLE (BASÉE SUR UNE COMPARAISON "INTELLIGENTE")
Information sur la source
Description
Ce programme recherche dans un répertoire (en ses sous-répertoires) les images qui sont en double, et permet d'en supprimer une selon plusieurs critères (la plus grosse, la plus petite, la plus ancienne...) La comparaison se fait sur la transformée en faisceaux d'ondelettes, une méthode beaucoup plus performante qu'une comparaison pixel par pixel. L'algorithme de comparaison détectera les images qui ont changé de résolution, de contraste ou qui sont plus nettes ou plus floues. Des valeurs "normalles" de similarité maximale sont comprises entre 80% et 100%. Il faut d'abord cliquer sur le boutton "Search" et ensuite sur le boutton "Compare", une boite de dialogue demande quelle image supprimer à chaque fois (boutton "Skip" pour ne rien faire) et donne la possibilité d'utiliser le même critère à chaque fois (pour ne pas avoir à cliquer 200 fois sur le même boutton). Notez bien que je ne suis pas responsable de l'utilisation que vous ferez du programme, si vous perdez images importantes ce ne sera pas de ma faute! De toute façon le programme permet d'envoyer les fichiers à la corbeille plutôt que de les supprimer, donc il est possible d'annuler la suppression. Voilà, je crois que j'ai tout dit, merci de me tenir au courant des éventuels bugs. Note: si vous voulez que le programme puisse lire plus de fichiers d'images (pour l'instant, seuls JPEG, BMP, WMF et ICO) sont pris en compte, il faudra simplement rajoutter dans les uses de MainFormUnit.pas une unité qui permet la lecture des formats additionnels (si celle-ci est bien programmée et intégrée à Delphi ça devrait fonctionner). En particulier ça devrait fonctionner avec l'unité GifImage téléchargeable je ne sais plus où (voir Google).
Source
- unit MainFormUnit;
-
- interface
-
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, ShlObj, Buttons, StdCtrls, ExtCtrls, ToolWin, ComCtrls, ExtDlgs,
- ImgList, Menus, StrUtils, SearchProgressFormUnit, CompareProgressFormUnit,
- DecisionFormUnit, ShellAPI, JPEG;
-
- type
- TMainForm = class(TForm)
- GroupBox1: TGroupBox;
- Edit1: TEdit;
- SpeedButton1: TSpeedButton;
- CheckBox1: TCheckBox;
- BitBtn1: TBitBtn;
- GroupBox2: TGroupBox;
- ListView1: TListView;
- ImageList1: TImageList;
- ImageList2: TImageList;
- ToolBar1: TToolBar;
- ToolButton1: TToolButton;
- PopupMenu1: TPopupMenu;
- Details1: TMenuItem;
- List1: TMenuItem;
- Details2: TMenuItem;
- Smallicons1: TMenuItem;
- ImageList3: TImageList;
- ToolButton2: TToolButton;
- PopupMenu2: TPopupMenu;
- Byname1: TMenuItem;
- Bydimensions1: TMenuItem;
- Byfilesize1: TMenuItem;
- Bydate1: TMenuItem;
- N1: TMenuItem;
- Ascendant1: TMenuItem;
- Descendant1: TMenuItem;
- BitBtn2: TBitBtn;
- Panel1: TPanel;
- TrackBar1: TTrackBar;
- ComboBox1: TComboBox;
- ToolButton3: TToolButton;
- Panel2: TPanel;
- ImageList4: TImageList;
- ToolButton4: TToolButton;
- Panel3: TPanel;
- ComboBox2: TComboBox;
- procedure FormCreate(Sender: TObject);
- procedure BitBtn1Click(Sender: TObject);
- procedure SpeedButton1Click(Sender: TObject);
- procedure Smallicons1Click(Sender: TObject);
- procedure ListView1Compare(Sender: TObject; Item1, Item2: TListItem;
- Data: Integer; var Compare: Integer);
- procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn);
- procedure BitBtn2Click(Sender: TObject);
- procedure TrackBar1Change(Sender: TObject);
- procedure ComboBox1DrawItem(Control: TWinControl; Index: Integer;
- Rect: TRect; State: TOwnerDrawState);
- procedure Descendant1Click(Sender: TObject);
- procedure Bydate1Click(Sender: TObject);
- procedure ComboBox2Change(Sender: TObject);
- private
- FAscendantOrder:Boolean; // Ordre de classement croissant/décroissant
- FColumnSortID:Integer; // Critère de classement
- public
- CurrentFolder:string; // Variables utilisées par les fenêtres de progression
- FileCount,FolderCount:Integer;
- CompareProgress:Single;
-
- procedure ClearData; // Pour nettoyer le ListView (la propriété Data des TListItem contient un pointeur vers une matrice de coefficients)
- end;
-
- TTriVertex=packed record
- x:Longint;
- y:Longint;
- Red:Word;
- Green:Word;
- Blue:Word;
- Alpha:Word;
- end;
-
- TSingleArray=array[0..$FFFFFF] of Single;
- PSingleArray=^TSingleArray;
-
- TSearchThread=class(TThread) // Thread de recherche de fichiers images
- private
- FSearchData:TSearchRec; // Variables utilisées dans la méthode synchronisée
- FPicture:TPicture;
- FBitmap1,FBitmap2:TBitmap;
- FRect1,FRect2:TRect;
- FPath,FSubPath:string;
- FSize:Integer;
- FBuffer:PSingleArray;
- protected
- procedure Execute;override;
- procedure Finished; // Méthode appelée à la fin du thread pour enlever la fenêtre de progression
- procedure AddFile; // Méthode synchronisée avec le thread principal pour manipuler le ListView1
- end;
-
- TCompareThread=class(TThread) // Thread de comparaison des fichiers images trouvés
- private
- FFile1,FFile2:string; // Variables utilisées dans la méthode synchronisée
- FAction:TImageAction;
- FSimilarity:Single;
- protected
- procedure Execute;override;
- procedure Finished; // Méthode appelée à la fin du thread pour enlever la fenêtre de progression
- procedure FileAction; // Méthode synchronisée avec le thread principal pour manipuler le ListView1 et lancer le dialogue de suppression
- end;
-
- var
- MainForm: TMainForm;
-
- implementation
-
- {$R *.dfm}
-
- function CallBack(Wnd:HWND;uMsg:UINT;wParam,lpData:LPARAM):Integer;stdcall; // Fonction de "callback" appelée à l'initialisation de la
- begin // boîte de sélection de répertoire pour définir le répertoire
- if uMsg=BFFM_INITIALIZED then // par défaut
- SendMessage(Wnd,BFFM_SETSELECTION,1,lpData);
- Result:=0;
- end;
-
- function SelectFolder(var Folder:string;const AllowCreateNew:Boolean=False;const Title:string=''):Boolean; // Fonction pour demander à l'utilisateur de
- const // choisir un répertoire
- BIF_NEWDIALOGSTYLE=$0040; // Folder: le répertoire par défaut
- var // qui contiendra le répertoire
- BI:TBrowseInfo; // choisi en cas de confirmation
- p:PItemIDList; // AllowCreateNew: autorise ou non la possibilité
- begin // de créer un nouveau dossier
- ZeroMemory(@BI,SizeOf(BI)); // Title: titre de la boîte de dialogue
- with BI do begin // Résultat: True si l'utilisation a cliqué
- hwndOwner:=Application.Handle; // sur OK, False sinon
- pszDisplayName:=PChar(Folder);
- lpszTitle:=PChar(Title);
- ulFlags:=BIF_RETURNONLYFSDIRS;
- if AllowCreateNew then
- ulFlags:=ulFlags or BIF_NEWDIALOGSTYLE;
- if Folder<>'' then begin
- lParam:=Integer(PChar(Folder));
- lpfn:=CallBack;
- end;
- end;
- p:=SHBrowseForFolder(BI);
- if Assigned(p) then begin
- SetLength(Folder,MAX_PATH);
- Result:=SHGetPathFromIDList(p,PChar(Folder));
- GlobalFreePtr(p);
- SetLength(Folder,Length(PChar(Folder)));
- end else
- Result:=False;
- end;
-
- { TSearchThread }
-
- procedure TSearchThread.AddFile; // Procédure synchronisée avec le thread principal pour ajoutter un item dans le TListView
-
- function BuildWavelet:PSingleArray; // Fonction qui construit la transformée en ondelettes de Haar de l'image
- var
- a,b,c,d,e:Integer;
- s:PByteArray;
- begin
- GetMem(Result,FBitmap2.Width*FBitmap2.Height*3*SizeOf(Single));
- for b:=0 to FBitmap2.Height-1 do begin
- s:=FBitmap2.ScanLine[b];
- for a:=0 to 3*FBitmap2.Width-1 do
- Result[FSize*b*3+a]:=s[a];
- end;
- c:=FSize;
- while c>1 do begin // Transformée en paquets d'ondelettes...
- e:=c div 2;
- for d:=0 to 2 do begin // On fait successivement la transformée des 3 canaux RGB
- for a:=0 to FSize-1 do begin // Transformée horizontale
- for b:=0 to c-1 do
- FBuffer[b]:=Result[(FSize*a+b)*3+d];
- for b:=0 to e-1 do begin
- Result[(FSize*a+b)*3+d]:=0.5*(FBuffer[2*b]+FBuffer[2*b+1]);
- Result[(FSize*a+b+e)*3+d]:=0.5*(FBuffer[2*b+1]-FBuffer[2*b]);
- end;
- end;
- for a:=0 to FSize-1 do begin // Transformée verticale
- for b:=0 to c-1 do
- FBuffer[b]:=Result[(FSize*b+a)*3+d];
- for b:=0 to e-1 do begin
- Result[(FSize*b+a)*3+d]:=0.5*(FBuffer[2*b]+FBuffer[2*b+1]);
- Result[(FSize*(b+e)+a)*3+d]:=0.5*(FBuffer[2*b+1]-FBuffer[2*b]);
- end;
- end;
- end;
- c:=c div 2; // Passage à l'échelle suivante
- end;
- end;
-
- begin
- MainForm.CurrentFolder:=FSubPath; // Variables utilisées par la fenêtre de progression...
- FPicture.LoadFromFile(FPath+'\'+FSearchData.Name);
- FBitmap1.Canvas.StretchDraw(FRect1,FPicture.Graphic);
- FBitmap2.Canvas.StretchDraw(FRect2,FPicture.Graphic);
- with MainForm,ListView1.Items.Add do begin
- Data:=BuildWavelet;
- Caption:=FSubPath+FSearchData.Name;
- SubItems.Add(Format('%d x %d',[FPicture.Width,FPicture.Height]));
- {$WARNINGS OFF}
- SubItems.Add(IntToStr(Int64(FSearchData.FindData.nFileSizeHigh) shl 32+FSearchData.FindData.nFileSizeLow));
- {$WARNINGS ON}
- SubItems.Add(DateTimeToStr(FileDateToDateTime(FileAge(FPath+'\'+FSearchData.Name))));
- ImageIndex:=ImageList1.Add(FBitmap1,nil);
- ImageList2.Add(FBitmap2,nil);
- end;
- Inc(MainForm.FileCount);
- end;
-
- procedure TSearchThread.Execute;
- var
- l:TStringList;
-
- procedure ListFolder(Path:string;SubPath:string); // Parcours récursif (en fonction de CheckBox1.Checked) d'un répertoire
- var
- f:TSearchRec;
- begin
- Inc(MainForm.FolderCount);
- if FindFirst(Path+'\*',faAnyFile or faDirectory,f)=0 then
- try
- repeat
- if Terminated then
- Break;
- if f.Attr and faDirectory=faDirectory then begin
- if MainForm.CheckBox1.Checked and (f.Name<>'.') and (f.Name<>'..') then
- ListFolder(Path+'\'+f.Name,SubPath+f.Name+'\');
- end else
- if l.IndexOf(ExtractFileExt(f.Name))>-1 then begin // Si l'extension est une extension graphique connue (en l'occurence *.JPEG, *.BMP, *.ICO, *.WMF)
- try
- FSearchData:=f;
- FSubPath:=SubPath;
- FPath:=Path;
- Synchronize(AddFile); // Synchronisation de la méthode d'ajout
- except
- ;
- end;
- end;
- until FindNext(f)<>0;
- finally
- FindClose(f);
- end;
- end;
-
- begin
- l:=TStringList.Create;
- l.Text:=AnsiReplaceText(AnsiReplaceStr(GraphicFileMask(TGraphic),';',#13),'*',''); // Extraction de la liste des extensions graphiques connues
- l.CaseSensitive:=False;
- FPicture:=TPicture.Create;
- FBitmap1:=TBitmap.Create;
- FBitmap1.Width:=MainForm.ImageList1.Width;
- FBitmap1.Height:=MainForm.ImageList1.Height;
- FRect1:=Rect(0,0,FBitmap1.Width,FBitmap1.Height);
- FBitmap2:=TBitmap.Create;
- FBitmap2.Width:=MainForm.ImageList2.Width;
- FBitmap2.Height:=MainForm.ImageList2.Height;
- FBitmap2.PixelFormat:=pf24bit; // Format RGB24
- SetStretchBltMode(FBitmap2.Canvas.Handle,HALFTONE); // Meilleur redimensionnement
- FRect2:=Rect(0,0,FBitmap2.Width,FBitmap2.Height);
- FSize:=FBitmap2.Width;
- GetMem(FBuffer,FSize*SizeOf(Single)); // Buffer de travail...
- MainForm.ListView1.Items.BeginUpdate; // Pour éviter des réaffichages multiples
- try
- ListFolder(MainForm.Edit1.Text,'');
- finally
- MainForm.ListView1.Items.EndUpdate;
- FreeMem(FBuffer);
- FBitmap2.Destroy;
- FBitmap1.Destroy;
- FPicture.Destroy;
- l.Destroy;
- if not Terminated then
- Synchronize(Finished); // On cache la fenêtre de progression si elle est encore visible
- end;
- end;
-
- procedure TSearchThread.Finished; // On cache la fenêtre de progression si elle est encore visible
- begin
- if SearchProgressForm.Visible then
- SearchProgressForm.ModalResult:=mrOk;
- end;
-
- { TCompareThread }
-
- procedure TCompareThread.Execute;
- var
- a,b,c,n:Integer;
- t:PSingleArray;
- r,m:Single;
- const
- u:array[0..1,0..1] of Single=((0.25,1),(1,1));
-
- function Dist(p,q:PSingleArray):Single; // Distance de 2 transformées d'ondelettes
- var
- a,b,c:Integer;
- begin
- Result:=0;
- for a:=0 to n-1 do begin
- if Result>m then // Si les 2 images sont déjà assez différentes, pas besoin de faire le calcul jusqu'au bout.
- Exit;
- for b:=0 to n-1 do
- for c:=0 to 2 do
- Result:=Result+t[n*a+b]*Abs(p[(n*a+b)*3+c]-q[(n*a+b)*3+c]); // Ajout des différences pondérées des 2 transformées en ondelettes
- end;
- end;
-
- begin
- n:=MainForm.ImageList2.Width;
- GetMem(t,n*n*SizeOf(Single)); // Matrice de pondération multi-échelle
- c:=1;
- r:=1/16;
- t[0]:=1;
- while c<n do begin // remplissage de la matrice de pondérations
- for a:=0 to c-1 do
- for b:=0 to c-1 do begin
- t[n*(a+c)+b]:=r*u[1,0];
- t[n*a+b+c]:=r*u[0,1];
- t[n*(a+c)+b+c]:=r*u[1,1];
- end;
- c:=2*c;
- r:=r*u[0,0];
- end;
- m:=100-MainForm.TrackBar1.Position/10;
- DecisionForm.Canceled:=False;
- DecisionForm.CheckBox1.Checked:=False;
- try
- a:=0;
- with MainForm do
- while a<ListView1.Items.Count do begin
- MainForm.CompareProgress:=a/(ListView1.Items.Count+1);
- for b:=ListView1.Items.Count-1 downto a+1 do begin
- FSimilarity:=100-Dist(ListView1.Items[a].Data,ListView1.Items[b].Data);
- if (FSimilarity>=TrackBar1.Position/10) then begin
- FFile1:=Edit1.Text+'\'+ListView1.Items[a].Caption;
- FFile2:=Edit1.Text+'\'+ListView1.Items[b].Caption;
- Synchronize(FileAction); // Synchronisation de l'affichage du dialogue de suppression
- case FAction of // Mise à jour du ListView1 en fonction de l'action choisie par l'utilisateur
- iaDelete1:begin
- FreeMem(ListView1.Items[a].Data);
- ListView1.Items.Delete(a);
- Dec(a);
- Break;
- end;
- iaDelete2:begin
- FreeMem(ListView1.Items[b].Data);
- ListView1.Items.Delete(b);
- end;
- end;
- end;
- if DecisionForm.Canceled or Terminated then // Action annulée...
- Break;
- end;
- if DecisionForm.Canceled or Terminated then // Action annulée...
- Break;
- Inc(a);
- end;
- finally
- FreeMem(t);
- end;
- if not Terminated then
- Synchronize(Finished); // On ferme la fenêtre de progression si elle est encore visible...
- end;
-
- procedure TCompareThread.FileAction;
-
- procedure DoDeleteFile(FileName:string); // Suppression d'un fichier
- var
- FOS:TSHFileOpStruct;
- begin
- if MainForm.ComboBox1.ItemIndex=1 then begin
- if not DeleteFile(FileName) then // Suppression irréversible
- RaiseLastOSError;
- end else begin
- ZeroMemory(@FOS,SizeOf(FOS)); // Envoi à la corbeille
- with FOS do begin
- wFunc:=FO_DELETE;
- pFrom:=PChar(FileName+#0#0);
- fFlags:=FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT;
- end;
- if ShFileOperation(FOS)<>0 then
- RaiseLastOSError;
- end;
- end;
-
- begin
- FAction:=DecisionForm.Execute(FFile1,FFile2,FSimilarity); // On demande son avis à l'utilisateur...
- case FAction of // Et on fait ce qu'il a décidé...
- iaDelete1:DoDeleteFile(FFile1);
- iaDelete2:DoDeleteFile(FFile2);
- end;
- end;
-
- procedure TCompareThread.Finished;
- begin
- if CompareProgressForm.Visible then
- CompareProgressForm.ModalResult:=mrOk; // On ferme la fenêtre de progression si elle est encore visible...
- end;
-
- { TMainForm }
-
- procedure TMainForm.FormCreate(Sender: TObject);
- begin
- Edit1.Text:=GetCurrentDir; // On se met dans le répertoire de travail
- end;
-
- procedure TMainForm.BitBtn1Click(Sender: TObject);
- const
- T:array[0..3] of Integer=(16,32,64,128); // Qualités de la transformée en ondelettes: Low, Average, Good, Very good
- begin
- ClearData; // On efface les données précédantes
- ImageList2.Width:=T[ComboBox2.ItemIndex]; // La taille de ImageList2 va définir la taille de la transformée en ondelettes (en fonction de la qualité désirée)
- ImageList2.Height:=T[ComboBox2.ItemIndex];
- CurrentFolder:=''; // Initialisations diverses...
- FileCount:=0;
- FolderCount:=0;
- with TSearchThread.Create(False) do begin // Lancement du thread de recherche
- if SearchProgressForm.ShowModal=mrCancel then begin // Affichage de la fenêtre de progression: si l'utilisateur appuie dur "cancel"...
- Terminate; // ...alors on arrête le thread
- WaitFor;
- end;
- Destroy;
- end;
- GroupBox2.Caption:=Format('%d graphic files',[ListView1.Items.Count]); // Nombre de fichiers trouvés
- BitBtn2.Enabled:=True; // On peut passer à l'étape suivante
- end;
-
- procedure TMainForm.SpeedButton1Click(Sender: TObject);
- var
- s:string;
- begin
- s:=Edit1.Text;
- if SelectFolder(s) then begin // Changement du répertoire de recherche
- Edit1.Text:=s;
- ClearData;
- end;
- end;
-
- procedure TMainForm.Smallicons1Click(Sender: TObject);
- var
- a:Integer;
- begin // En fonction du menu coché, on adapte l'affichage du ListView
- for a:=0 to PopupMenu1.Items.Count-1 do
- if PopupMenu1.Items[a].Checked then
- ListView1.ViewStyle:=TViewStyle(a);
- end;
-
- procedure TMainForm.ListView1Compare(Sender: TObject; Item1,
- Item2: TListItem; Data: Integer; var Compare: Integer);
-
- function BoolSgn(x:Boolean):Integer;
- begin
- if x then
- Result:=1
- else
- Result:=-1;
- if FAscendantOrder then
- Result:=-Result;
- end;
-
- function StrToDim(s:string):Integer;
- var
- a:Integer;
- begin
- a:=Pos(' x ',s);
- Result:=StrToInt(Copy(s,1,a-1))*StrToInt(Copy(s,a+3,Length(s)));
- end;
-
- begin
- case FColumnSortID of // Différentes méthodes de comparaison en fonction de la colonne cliquée
- 0:Compare:=BoolSgn(Item1.Caption>Item2.Caption);
- 1:Compare:=BoolSgn(StrToDim(Item1.SubItems[0])>StrToDim(Item2.SubItems[0]));
- 2:Compare:=BoolSgn(StrToInt(Item1.SubItems[1])>StrToInt(Item2.SubItems[1]));
- 3:Compare:=BoolSgn(StrToDateTime(Item1.SubItems[2])>StrToDateTime(Item2.SubItems[2]));
- end;
- end;
-
- procedure TMainForm.ListView1ColumnClick(Sender: TObject;
- Column: TListColumn);
- begin
- if Column.Index=FColumnSortID then begin // Si on a cliqué 2 fois sur la même colonne
- FAscendantOrder:=not FAscendantOrder; // alors on inverse l'ordre de classement
- Ascendant1.Checked:=not FAscendantOrder; // Mise à jour du menu coché
- Descendant1.Checked:=FAscendantOrder;
- end;
- FColumnSortID:=Column.Index;
- PopupMenu2.Items[FColumnSortID].Checked:=True; // Mise à jour du menu coché
- ListView1.CustomSort(nil,0); // on classe les items
- end;
-
- procedure TMainForm.ClearData; // libération de la mémoire occupée par les transformées en ondelette et on vide le ListView1
- var
- a:Integer;
- begin
- BitBtn2.Enabled:=False;
- ListView1.Items.BeginUpdate;
- try
- for a:=0 to ListView1.Items.Count-1 do
- FreeMem(ListView1.Items[a].Data);
- ListView1.Clear;
- finally
- ListView1.Items.EndUpdate;
- end;
- ImageList1.Clear; // on efface les listes d'images
- ImageList2.Clear;
- ListView1.Repaint;
- end;
-
- procedure TMainForm.BitBtn2Click(Sender: TObject);
- begin
- with TCompareThread.Create(False) do begin // Lancement du thread de comparaison
- if CompareProgressForm.ShowModal=mrCancel then begin // Si l'utilisateur annule l'opération
- Terminate; // on arrête le thread
- WaitFor;
- end;
- Destroy;
- end;
- GroupBox2.Caption:=Format('%d graphic files',[ListView1.Items.Count]); // Nombre de fichiers restants
- end;
-
- procedure TMainForm.TrackBar1Change(Sender: TObject);
- begin
- Panel1.Caption:=Format('Max similarity value: %f %%',[TrackBar1.Position/10]); // Mise à jour de l'interface
- end;
-
- procedure TMainForm.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
- Rect: TRect; State: TOwnerDrawState);
- begin
- with ComboBox1,ComboBox1.Canvas do begin // Affichage des icônes de la ComboBox
- if odSelected in State then
- Brush.Color:=clHighlight;
- Dec(Rect.Right,1);
- FillRect(Rect);
- ImageList4.Draw(Canvas,Rect.Left+1,Rect.Top,Index);
- TextOut(Rect.Left+21,Rect.Top+2,Items[Index]);
- end;
- end;
-
- procedure TMainForm.Descendant1Click(Sender: TObject);
- begin
- FAscendantOrder:=Ascendant1=Sender; // Changement de l'ordre d'affichage
- ListView1ColumnClick(nil,ListView1.Columns[FColumnSortID]);
- end;
-
- procedure TMainForm.Bydate1Click(Sender: TObject);
- begin
- FColumnSortID:=PopupMenu2.Items.IndexOf(TMenuItem(Sender)); // Changement du critère d'affichage
- ListView1ColumnClick(nil,ListView1.Columns[FColumnSortID]); // On range de nouveau les items
- end;
-
- procedure TMainForm.ComboBox2Change(Sender: TObject);
- begin
- ClearData; // On doit recalculer les transformées en ondelettes donc on efface tout
- end;
-
- end.
unit MainFormUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ShlObj, Buttons, StdCtrls, ExtCtrls, ToolWin, ComCtrls, ExtDlgs,
ImgList, Menus, StrUtils, SearchProgressFormUnit, CompareProgressFormUnit,
DecisionFormUnit, ShellAPI, JPEG;
type
TMainForm = class(TForm)
GroupBox1: TGroupBox;
Edit1: TEdit;
SpeedButton1: TSpeedButton;
CheckBox1: TCheckBox;
BitBtn1: TBitBtn;
GroupBox2: TGroupBox;
ListView1: TListView;
ImageList1: TImageList;
ImageList2: TImageList;
ToolBar1: TToolBar;
ToolButton1: TToolButton;
PopupMenu1: TPopupMenu;
Details1: TMenuItem;
List1: TMenuItem;
Details2: TMenuItem;
Smallicons1: TMenuItem;
ImageList3: TImageList;
ToolButton2: TToolButton;
PopupMenu2: TPopupMenu;
Byname1: TMenuItem;
Bydimensions1: TMenuItem;
Byfilesize1: TMenuItem;
Bydate1: TMenuItem;
N1: TMenuItem;
Ascendant1: TMenuItem;
Descendant1: TMenuItem;
BitBtn2: TBitBtn;
Panel1: TPanel;
TrackBar1: TTrackBar;
ComboBox1: TComboBox;
ToolButton3: TToolButton;
Panel2: TPanel;
ImageList4: TImageList;
ToolButton4: TToolButton;
Panel3: TPanel;
ComboBox2: TComboBox;
procedure FormCreate(Sender: TObject);
procedure BitBtn1Click(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure Smallicons1Click(Sender: TObject);
procedure ListView1Compare(Sender: TObject; Item1, Item2: TListItem;
Data: Integer; var Compare: Integer);
procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn);
procedure BitBtn2Click(Sender: TObject);
procedure TrackBar1Change(Sender: TObject);
procedure ComboBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
procedure Descendant1Click(Sender: TObject);
procedure Bydate1Click(Sender: TObject);
procedure ComboBox2Change(Sender: TObject);
private
FAscendantOrder:Boolean; // Ordre de classement croissant/décroissant
FColumnSortID:Integer; // Critère de classement
public
CurrentFolder:string; // Variables utilisées par les fenêtres de progression
FileCount,FolderCount:Integer;
CompareProgress:Single;
procedure ClearData; // Pour nettoyer le ListView (la propriété Data des TListItem contient un pointeur vers une matrice de coefficients)
end;
TTriVertex=packed record
x:Longint;
y:Longint;
Red:Word;
Green:Word;
Blue:Word;
Alpha:Word;
end;
TSingleArray=array[0..$FFFFFF] of Single;
PSingleArray=^TSingleArray;
TSearchThread=class(TThread) // Thread de recherche de fichiers images
private
FSearchData:TSearchRec; // Variables utilisées dans la méthode synchronisée
FPicture:TPicture;
FBitmap1,FBitmap2:TBitmap;
FRect1,FRect2:TRect;
FPath,FSubPath:string;
FSize:Integer;
FBuffer:PSingleArray;
protected
procedure Execute;override;
procedure Finished; // Méthode appelée à la fin du thread pour enlever la fenêtre de progression
procedure AddFile; // Méthode synchronisée avec le thread principal pour manipuler le ListView1
end;
TCompareThread=class(TThread) // Thread de comparaison des fichiers images trouvés
private
FFile1,FFile2:string; // Variables utilisées dans la méthode synchronisée
FAction:TImageAction;
FSimilarity:Single;
protected
procedure Execute;override;
procedure Finished; // Méthode appelée à la fin du thread pour enlever la fenêtre de progression
procedure FileAction; // Méthode synchronisée avec le thread principal pour manipuler le ListView1 et lancer le dialogue de suppression
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
function CallBack(Wnd:HWND;uMsg:UINT;wParam,lpData:LPARAM):Integer;stdcall; // Fonction de "callback" appelée à l'initialisation de la
begin // boîte de sélection de répertoire pour définir le répertoire
if uMsg=BFFM_INITIALIZED then // par défaut
SendMessage(Wnd,BFFM_SETSELECTION,1,lpData);
Result:=0;
end;
function SelectFolder(var Folder:string;const AllowCreateNew:Boolean=False;const Title:string=''):Boolean; // Fonction pour demander à l'utilisateur de
const // choisir un répertoire
BIF_NEWDIALOGSTYLE=$0040; // Folder: le répertoire par défaut
var // qui contiendra le répertoire
BI:TBrowseInfo; // choisi en cas de confirmation
p:PItemIDList; // AllowCreateNew: autorise ou non la possibilité
begin // de créer un nouveau dossier
ZeroMemory(@BI,SizeOf(BI)); // Title: titre de la boîte de dialogue
with BI do begin // Résultat: True si l'utilisation a cliqué
hwndOwner:=Application.Handle; // sur OK, False sinon
pszDisplayName:=PChar(Folder);
lpszTitle:=PChar(Title);
ulFlags:=BIF_RETURNONLYFSDIRS;
if AllowCreateNew then
ulFlags:=ulFlags or BIF_NEWDIALOGSTYLE;
if Folder<>'' then begin
lParam:=Integer(PChar(Folder));
lpfn:=CallBack;
end;
end;
p:=SHBrowseForFolder(BI);
if Assigned(p) then begin
SetLength(Folder,MAX_PATH);
Result:=SHGetPathFromIDList(p,PChar(Folder));
GlobalFreePtr(p);
SetLength(Folder,Length(PChar(Folder)));
end else
Result:=False;
end;
{ TSearchThread }
procedure TSearchThread.AddFile; // Procédure synchronisée avec le thread principal pour ajoutter un item dans le TListView
function BuildWavelet:PSingleArray; // Fonction qui construit la transformée en ondelettes de Haar de l'image
var
a,b,c,d,e:Integer;
s:PByteArray;
begin
GetMem(Result,FBitmap2.Width*FBitmap2.Height*3*SizeOf(Single));
for b:=0 to FBitmap2.Height-1 do begin
s:=FBitmap2.ScanLine[b];
for a:=0 to 3*FBitmap2.Width-1 do
Result[FSize*b*3+a]:=s[a];
end;
c:=FSize;
while c>1 do begin // Transformée en paquets d'ondelettes...
e:=c div 2;
for d:=0 to 2 do begin // On fait successivement la transformée des 3 canaux RGB
for a:=0 to FSize-1 do begin // Transformée horizontale
for b:=0 to c-1 do
FBuffer[b]:=Result[(FSize*a+b)*3+d];
for b:=0 to e-1 do begin
Result[(FSize*a+b)*3+d]:=0.5*(FBuffer[2*b]+FBuffer[2*b+1]);
Result[(FSize*a+b+e)*3+d]:=0.5*(FBuffer[2*b+1]-FBuffer[2*b]);
end;
end;
for a:=0 to FSize-1 do begin // Transformée verticale
for b:=0 to c-1 do
FBuffer[b]:=Result[(FSize*b+a)*3+d];
for b:=0 to e-1 do begin
Result[(FSize*b+a)*3+d]:=0.5*(FBuffer[2*b]+FBuffer[2*b+1]);
Result[(FSize*(b+e)+a)*3+d]:=0.5*(FBuffer[2*b+1]-FBuffer[2*b]);
end;
end;
end;
c:=c div 2; // Passage à l'échelle suivante
end;
end;
begin
MainForm.CurrentFolder:=FSubPath; // Variables utilisées par la fenêtre de progression...
FPicture.LoadFromFile(FPath+'\'+FSearchData.Name);
FBitmap1.Canvas.StretchDraw(FRect1,FPicture.Graphic);
FBitmap2.Canvas.StretchDraw(FRect2,FPicture.Graphic);
with MainForm,ListView1.Items.Add do begin
Data:=BuildWavelet;
Caption:=FSubPath+FSearchData.Name;
SubItems.Add(Format('%d x %d',[FPicture.Width,FPicture.Height]));
{$WARNINGS OFF}
SubItems.Add(IntToStr(Int64(FSearchData.FindData.nFileSizeHigh) shl 32+FSearchData.FindData.nFileSizeLow));
{$WARNINGS ON}
SubItems.Add(DateTimeToStr(FileDateToDateTime(FileAge(FPath+'\'+FSearchData.Name))));
ImageIndex:=ImageList1.Add(FBitmap1,nil);
ImageList2.Add(FBitmap2,nil);
end;
Inc(MainForm.FileCount);
end;
procedure TSearchThread.Execute;
var
l:TStringList;
procedure ListFolder(Path:string;SubPath:string); // Parcours récursif (en fonction de CheckBox1.Checked) d'un répertoire
var
f:TSearchRec;
begin
Inc(MainForm.FolderCount);
if FindFirst(Path+'\*',faAnyFile or faDirectory,f)=0 then
try
repeat
if Terminated then
Break;
if f.Attr and faDirectory=faDirectory then begin
if MainForm.CheckBox1.Checked and (f.Name<>'.') and (f.Name<>'..') then
ListFolder(Path+'\'+f.Name,SubPath+f.Name+'\');
end else
if l.IndexOf(ExtractFileExt(f.Name))>-1 then begin // Si l'extension est une extension graphique connue (en l'occurence *.JPEG, *.BMP, *.ICO, *.WMF)
try
FSearchData:=f;
FSubPath:=SubPath;
FPath:=Path;
Synchronize(AddFile); // Synchronisation de la méthode d'ajout
except
;
end;
end;
until FindNext(f)<>0;
finally
FindClose(f);
end;
end;
begin
l:=TStringList.Create;
l.Text:=AnsiReplaceText(AnsiReplaceStr(GraphicFileMask(TGraphic),';',#13),'*',''); // Extraction de la liste des extensions graphiques connues
l.CaseSensitive:=False;
FPicture:=TPicture.Create;
FBitmap1:=TBitmap.Create;
FBitmap1.Width:=MainForm.ImageList1.Width;
FBitmap1.Height:=MainForm.ImageList1.Height;
FRect1:=Rect(0,0,FBitmap1.Width,FBitmap1.Height);
FBitmap2:=TBitmap.Create;
FBitmap2.Width:=MainForm.ImageList2.Width;
FBitmap2.Height:=MainForm.ImageList2.Height;
FBitmap2.PixelFormat:=pf24bit; // Format RGB24
SetStretchBltMode(FBitmap2.Canvas.Handle,HALFTONE); // Meilleur redimensionnement
FRect2:=Rect(0,0,FBitmap2.Width,FBitmap2.Height);
FSize:=FBitmap2.Width;
GetMem(FBuffer,FSize*SizeOf(Single)); // Buffer de travail...
MainForm.ListView1.Items.BeginUpdate; // Pour éviter des réaffichages multiples
try
ListFolder(MainForm.Edit1.Text,'');
finally
MainForm.ListView1.Items.EndUpdate;
FreeMem(FBuffer);
FBitmap2.Destroy;
FBitmap1.Destroy;
FPicture.Destroy;
l.Destroy;
if not Terminated then
Synchronize(Finished); // On cache la fenêtre de progression si elle est encore visible
end;
end;
procedure TSearchThread.Finished; // On cache la fenêtre de progression si elle est encore visible
begin
if SearchProgressForm.Visible then
SearchProgressForm.ModalResult:=mrOk;
end;
{ TCompareThread }
procedure TCompareThread.Execute;
var
a,b,c,n:Integer;
t:PSingleArray;
r,m:Single;
const
u:array[0..1,0..1] of Single=((0.25,1),(1,1));
function Dist(p,q:PSingleArray):Single; // Distance de 2 transformées d'ondelettes
var
a,b,c:Integer;
begin
Result:=0;
for a:=0 to n-1 do begin
if Result>m then // Si les 2 images sont déjà assez différentes, pas besoin de faire le calcul jusqu'au bout.
Exit;
for b:=0 to n-1 do
for c:=0 to 2 do
Result:=Result+t[n*a+b]*Abs(p[(n*a+b)*3+c]-q[(n*a+b)*3+c]); // Ajout des différences pondérées des 2 transformées en ondelettes
end;
end;
begin
n:=MainForm.ImageList2.Width;
GetMem(t,n*n*SizeOf(Single)); // Matrice de pondération multi-échelle
c:=1;
r:=1/16;
t[0]:=1;
while c<n do begin // remplissage de la matrice de pondérations
for a:=0 to c-1 do
for b:=0 to c-1 do begin
t[n*(a+c)+b]:=r*u[1,0];
t[n*a+b+c]:=r*u[0,1];
t[n*(a+c)+b+c]:=r*u[1,1];
end;
c:=2*c;
r:=r*u[0,0];
end;
m:=100-MainForm.TrackBar1.Position/10;
DecisionForm.Canceled:=False;
DecisionForm.CheckBox1.Checked:=False;
try
a:=0;
with MainForm do
while a<ListView1.Items.Count do begin
MainForm.CompareProgress:=a/(ListView1.Items.Count+1);
for b:=ListView1.Items.Count-1 downto a+1 do begin
FSimilarity:=100-Dist(ListView1.Items[a].Data,ListView1.Items[b].Data);
if (FSimilarity>=TrackBar1.Position/10) then begin
FFile1:=Edit1.Text+'\'+ListView1.Items[a].Caption;
FFile2:=Edit1.Text+'\'+ListView1.Items[b].Caption;
Synchronize(FileAction); // Synchronisation de l'affichage du dialogue de suppression
case FAction of // Mise à jour du ListView1 en fonction de l'action choisie par l'utilisateur
iaDelete1:begin
FreeMem(ListView1.Items[a].Data);
ListView1.Items.Delete(a);
Dec(a);
Break;
end;
iaDelete2:begin
FreeMem(ListView1.Items[b].Data);
ListView1.Items.Delete(b);
end;
end;
end;
if DecisionForm.Canceled or Terminated then // Action annulée...
Break;
end;
if DecisionForm.Canceled or Terminated then // Action annulée...
Break;
Inc(a);
end;
finally
FreeMem(t);
end;
if not Terminated then
Synchronize(Finished); // On ferme la fenêtre de progression si elle est encore visible...
end;
procedure TCompareThread.FileAction;
procedure DoDeleteFile(FileName:string); // Suppression d'un fichier
var
FOS:TSHFileOpStruct;
begin
if MainForm.ComboBox1.ItemIndex=1 then begin
if not DeleteFile(FileName) then // Suppression irréversible
RaiseLastOSError;
end else begin
ZeroMemory(@FOS,SizeOf(FOS)); // Envoi à la corbeille
with FOS do begin
wFunc:=FO_DELETE;
pFrom:=PChar(FileName+#0#0);
fFlags:=FOF_ALLOWUNDO or FOF_NOCONFIRMATION or FOF_SILENT;
end;
if ShFileOperation(FOS)<>0 then
RaiseLastOSError;
end;
end;
begin
FAction:=DecisionForm.Execute(FFile1,FFile2,FSimilarity); // On demande son avis à l'utilisateur...
case FAction of // Et on fait ce qu'il a décidé...
iaDelete1:DoDeleteFile(FFile1);
iaDelete2:DoDeleteFile(FFile2);
end;
end;
procedure TCompareThread.Finished;
begin
if CompareProgressForm.Visible then
CompareProgressForm.ModalResult:=mrOk; // On ferme la fenêtre de progression si elle est encore visible...
end;
{ TMainForm }
procedure TMainForm.FormCreate(Sender: TObject);
begin
Edit1.Text:=GetCurrentDir; // On se met dans le répertoire de travail
end;
procedure TMainForm.BitBtn1Click(Sender: TObject);
const
T:array[0..3] of Integer=(16,32,64,128); // Qualités de la transformée en ondelettes: Low, Average, Good, Very good
begin
ClearData; // On efface les données précédantes
ImageList2.Width:=T[ComboBox2.ItemIndex]; // La taille de ImageList2 va définir la taille de la transformée en ondelettes (en fonction de la qualité désirée)
ImageList2.Height:=T[ComboBox2.ItemIndex];
CurrentFolder:=''; // Initialisations diverses...
FileCount:=0;
FolderCount:=0;
with TSearchThread.Create(False) do begin // Lancement du thread de recherche
if SearchProgressForm.ShowModal=mrCancel then begin // Affichage de la fenêtre de progression: si l'utilisateur appuie dur "cancel"...
Terminate; // ...alors on arrête le thread
WaitFor;
end;
Destroy;
end;
GroupBox2.Caption:=Format('%d graphic files',[ListView1.Items.Count]); // Nombre de fichiers trouvés
BitBtn2.Enabled:=True; // On peut passer à l'étape suivante
end;
procedure TMainForm.SpeedButton1Click(Sender: TObject);
var
s:string;
begin
s:=Edit1.Text;
if SelectFolder(s) then begin // Changement du répertoire de recherche
Edit1.Text:=s;
ClearData;
end;
end;
procedure TMainForm.Smallicons1Click(Sender: TObject);
var
a:Integer;
begin // En fonction du menu coché, on adapte l'affichage du ListView
for a:=0 to PopupMenu1.Items.Count-1 do
if PopupMenu1.Items[a].Checked then
ListView1.ViewStyle:=TViewStyle(a);
end;
procedure TMainForm.ListView1Compare(Sender: TObject; Item1,
Item2: TListItem; Data: Integer; var Compare: Integer);
function BoolSgn(x:Boolean):Integer;
begin
if x then
Result:=1
else
Result:=-1;
if FAscendantOrder then
Result:=-Result;
end;
function StrToDim(s:string):Integer;
var
a:Integer;
begin
a:=Pos(' x ',s);
Result:=StrToInt(Copy(s,1,a-1))*StrToInt(Copy(s,a+3,Length(s)));
end;
begin
case FColumnSortID of // Différentes méthodes de comparaison en fonction de la colonne cliquée
0:Compare:=BoolSgn(Item1.Caption>Item2.Caption);
1:Compare:=BoolSgn(StrToDim(Item1.SubItems[0])>StrToDim(Item2.SubItems[0]));
2:Compare:=BoolSgn(StrToInt(Item1.SubItems[1])>StrToInt(Item2.SubItems[1]));
3:Compare:=BoolSgn(StrToDateTime(Item1.SubItems[2])>StrToDateTime(Item2.SubItems[2]));
end;
end;
procedure TMainForm.ListView1ColumnClick(Sender: TObject;
Column: TListColumn);
begin
if Column.Index=FColumnSortID then begin // Si on a cliqué 2 fois sur la même colonne
FAscendantOrder:=not FAscendantOrder; // alors on inverse l'ordre de classement
Ascendant1.Checked:=not FAscendantOrder; // Mise à jour du menu coché
Descendant1.Checked:=FAscendantOrder;
end;
FColumnSortID:=Column.Index;
PopupMenu2.Items[FColumnSortID].Checked:=True; // Mise à jour du menu coché
ListView1.CustomSort(nil,0); // on classe les items
end;
procedure TMainForm.ClearData; // libération de la mémoire occupée par les transformées en ondelette et on vide le ListView1
var
a:Integer;
begin
BitBtn2.Enabled:=False;
ListView1.Items.BeginUpdate;
try
for a:=0 to ListView1.Items.Count-1 do
FreeMem(ListView1.Items[a].Data);
ListView1.Clear;
finally
ListView1.Items.EndUpdate;
end;
ImageList1.Clear; // on efface les listes d'images
ImageList2.Clear;
ListView1.Repaint;
end;
procedure TMainForm.BitBtn2Click(Sender: TObject);
begin
with TCompareThread.Create(False) do begin // Lancement du thread de comparaison
if CompareProgressForm.ShowModal=mrCancel then begin // Si l'utilisateur annule l'opération
Terminate; // on arrête le thread
WaitFor;
end;
Destroy;
end;
GroupBox2.Caption:=Format('%d graphic files',[ListView1.Items.Count]); // Nombre de fichiers restants
end;
procedure TMainForm.TrackBar1Change(Sender: TObject);
begin
Panel1.Caption:=Format('Max similarity value: %f %%',[TrackBar1.Position/10]); // Mise à jour de l'interface
end;
procedure TMainForm.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
begin
with ComboBox1,ComboBox1.Canvas do begin // Affichage des icônes de la ComboBox
if odSelected in State then
Brush.Color:=clHighlight;
Dec(Rect.Right,1);
FillRect(Rect);
ImageList4.Draw(Canvas,Rect.Left+1,Rect.Top,Index);
TextOut(Rect.Left+21,Rect.Top+2,Items[Index]);
end;
end;
procedure TMainForm.Descendant1Click(Sender: TObject);
begin
FAscendantOrder:=Ascendant1=Sender; // Changement de l'ordre d'affichage
ListView1ColumnClick(nil,ListView1.Columns[FColumnSortID]);
end;
procedure TMainForm.Bydate1Click(Sender: TObject);
begin
FColumnSortID:=PopupMenu2.Items.IndexOf(TMenuItem(Sender)); // Changement du critère d'affichage
ListView1ColumnClick(nil,ListView1.Columns[FColumnSortID]); // On range de nouveau les items
end;
procedure TMainForm.ComboBox2Change(Sender: TObject);
begin
ClearData; // On doit recalculer les transformées en ondelettes donc on efface tout
end;
end.
Sources du même auteur
Sources de la même categorie
Commentaires et avis
Discussions en rapport avec ce code source dans le forum
comment accelerer la recherche de la semilarite entre deux images dans une grande base de données imades [ par kamicaz2002 ]
voila je cherche accelerer le temps de recherche d'une image dans une BDD d'images...j'ai utilisé la methode de la correlation pour faire la comparais
comparaison de deux images bitmap [ par kamicaz2002 ]
aidez moi sur la comparaison de deux images bmp si elle sont simelaire ou bien non. notre images sources et l'autre sont deux images de main sur un f
Effets images [ par Matt 261 ]
Bonjour à tous !Je suis en train de finir mon écran de veille visible ICI et je voulais y ajouter des effets sur les images par exempl
affichage d'images [ par guigui265 ]
bonjour, j'ai un formulaire ou j'affiche une image que j'ouvre avec un openpicturedialog. je voudrais ensuite faire comme le fais l'afficheur de windo
Fichier d'image [ par benStNarsRepresent ]
Je voudrais savoir quel format de fichier peut stocker plusieurs images, dans le même fichier, et aussi comment mettre ces images dans le fichier. Les
OPengl et images bmp [ par yvessimon ]
Avec OPEngl est il possible de lier une image bmp aux faces d'un cubeyvessimon
Magnipuler des images [ par Descom_q ]
Salut tout le monde:)! Je suis en train de faire une application qui redimentionne les image dans un fichier powerpoint. J'arrive à magnipuler la tail
Delphi: Base de données [ par jdudoret ]
Bonjour,J'ai créer une table sous Excel 2000 avec des données texte et images.Pas de problèmes pour afficher du texte dans des DBText .. par les tabl
Peut-on améliorer l'affichage des images dans les menus ? [ par Squallou ]
Salut à tous ! Je sais pas si c'est la bonne section mais je ne me voyais pas mettre dans dans 'Graphisme'... :/ Ma question est assez simple : j'ai
IMAGES AVEC UN statictext [ par yomane51100 ]
Bonjour je neasit pa si une perssone peut medez . ges installer une scrollbox sur mon logiciel est dans c ete scrollbox ges mis statictext avec les n
|
Derniers Blogs
[DIVERS] SUIVRE VOS SéRIES PRéFéRéS SUR LA TOILE[DIVERS] SUIVRE VOS SéRIES PRéFéRéS SUR LA TOILE par orion
Comme de nombreux geek, je suis un grand amateur de série TV et je rate régulièrement des épisodes de mes séries préférés. Une solution s'offre à vous avec ce merveilleux site : Tv Gorge - www.tvgorge.com Moteur de recherche à l'appui, vous pouvez ...
Cliquez pour lire la suite de l'article par orion TECHDAYS PARIS 2010 : LA BI DANS SHAREPOINT 2010TECHDAYS PARIS 2010 : LA BI DANS SHAREPOINT 2010 par ROMELARD Fabrice
Animé par: Vincent Bellet et Baptiste Giraudier La BI dans SharePoint 2010, Les nouveaux services d'application dans SP2010 et SQL Server Reporting services 2008 R2. La BI dans SharePoint est généralisée pour tous afin de permettre à tous les coll...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice TECHDAYS PARIS 2010 : PLAN DE MIGRATION VERS SHAREPOINT 2010TECHDAYS PARIS 2010 : PLAN DE MIGRATION VERS SHAREPOINT 2010 par ROMELARD Fabrice
Animé par: Arnault Nouvel et Antoine Dongois Le processus à prendre : Apprendre (découvrir la plateforme) Préparer (documenter l'historique et choisir la méthode de MAJ) Test (Test de MAJ) Implémenter (Effectuer la MAJ) Valid...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice TECHDAYS PARIS 2010 : LA PLEINIèRE DU SECOND JOURTECHDAYS PARIS 2010 : LA PLEINIèRE DU SECOND JOUR par ROMELARD Fabrice
Après un retour sur l'histoire des TechDays de Paris et le fait que ce soit le plus gros event MS au monde (du fait de sa gratuité), le président de MS France (Eric Boustoullier) a fait une présentation de la vision Microsoft pour les années à venir...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice
Logiciels
DB-MAIN (9.1.0)DB-MAIN (9.1.0)DB-MAIN is a data-modeling and data-architecture tool. It is designed to help developers and anal... Cliquez pour télécharger DB-MAIN Xilisoft DPG Convertisseur (5.1.37.0120)XILISOFT DPG CONVERTISSEUR (5.1.37.0120)Xilisoft DPG Convertisseur offre aux fans de Nintendo DS une bonne solution leur permettant de dé... Cliquez pour télécharger Xilisoft DPG Convertisseur GraphicsGale (2.01.01)GRAPHICSGALE (2.01.01)GraphicsGale est un logiciel de PixelArt avec de nombreuse fonctionnalités permettant de réalisé ... Cliquez pour télécharger GraphicsGale Architecte 3D (Platinum 2010)ARCHITECTE 3D (PLATINUM 2010)Architecte 3D Platinium vous permet de concevoir facilement les plans votre future maison, de l'é... Cliquez pour télécharger Architecte 3D TeamViewer 5 (TeamViewer 5)TEAMVIEWER 5 (TEAMVIEWER 5)Dépanner un ami,expliquer une manipulation devient un jeu d'enfant.
Prise en main d'un autre ord... Cliquez pour télécharger TeamViewer 5
|