Updated patch for Detecting Duplicates in F-Spot



Hi guys!

I have reworked the patch for detecting duplicates in importing and also
with the photos that are already imported. It is for the actual CVS. I
have it working in my F-Spot subversion repository.

Larry, could you take a look in order to add it to the official F-Spot
code?

I have left in the code some traces in order you can see the extra time
it is needed in importing in order to create the MD5 signatures for the
images (around 10%). If the patch is ok, I will clean all the messages
before applying it.

Just as a reminder, you can see the feature working in the video:

http://acs.barrapunto.org/~acs/FSpotDuplicates.html

Cheers

-- Alvaro
Index: src/ImportCommand.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/ImportCommand.cs,v
retrieving revision 1.39
diff -u -b -B -p -r1.39 ImportCommand.cs
--- src/ImportCommand.cs	22 Aug 2005 10:47:34 -0000	1.39
+++ src/ImportCommand.cs	3 Sep 2005 09:55:34 -0000
@@ -80,6 +80,9 @@ public class ImportCommand : FSpot.Glade
                         if (this.Icon == null)			if (this.Icon == null && this.IsCamera)
 				this.Icon = PixbufUtils.LoadThemeIcon ("gnome-dev-media-cf", 32);
 
+				if (this.Icon == null)
+				this.Icon = PixbufUtils.LoadThemeIcon (vol.Icon, 32);
+
 			try {
 				if (this.Icon == null)
 					this.Icon = new Gdk.Pixbuf (vol.Icon);
@@ -378,6 +381,9 @@ public class ImportCommand : FSpot.Glade
 	bool copy;
 
 	int total;
+    private int          duplicates;
+    private long         total_md5;
+    private long         total_import;
 	PhotoStore store;
 
 	FSpot.Delay step;
@@ -390,6 +396,8 @@ public class ImportCommand : FSpot.Glade
 	{
 		main_window = mw;
 		step = new FSpot.Delay (10, new GLib.IdleHandler (Step));
+        total_md5 = 0;
+        total_import = 0;
 	}
 
 	private void HandleDialogResponse (object obj, ResponseArgs args)
@@ -401,12 +409,16 @@ public class ImportCommand : FSpot.Glade
 		}
 	}
 
-	private void UpdateProgressBar (int count, int total)
+	private void UpdateProgressBar (int count, int total, int duplicates)
 	{
 		if (progress_bar == null)
 			return;
-
+        if (duplicates > 0) {
+            progress_bar.Text = String.Format ("Importing {0} of {1}, {2} duplicate/s", 
+                                               count-duplicates, total-duplicates, duplicates);
+        } else {
 		progress_bar.Text = String.Format ("Importing {0} of {1}", count, total);
+        }
 		progress_bar.Fraction = (double) count / System.Math.Max (total, 1);
 	}
 
@@ -425,12 +437,19 @@ public class ImportCommand : FSpot.Glade
 		
 	}
 
+    private bool import_duplicates = false;
+    private bool first_duplicate   = true;
+
 	private bool Step ()
 	{			
 		Photo photo;
 		Pixbuf thumbnail;
 		int count;
 		bool ongoing = true;
+        TimeSpan timeTakenMD5 = new TimeSpan();;
+
+        Console.WriteLine ("Start importing the photo ...");
+        long startTime = DateTime.Now.Ticks;
 
 		if (importer == null)
 			return false;
@@ -441,13 +460,65 @@ public class ImportCommand : FSpot.Glade
 			Console.WriteLine ("Could not import file");
 		} else {
 			//icon_scrolled.Visible = true;
+            // FIXME: If we are in remote, download the real photo
+            //if (importer is FlickrImportBackend || importer is GalleryImportBackend) {
+            //    Console.WriteLine ("Downloading the real images ...");
+            //}
+            long startTimeMD5 = DateTime.Now.Ticks;
+            photo.MD5Sum = FSpot.DuplicatesFinder.PhotoMD5 (photo);            
+            store.Commit (photo);
+            long endTimeMD5 = DateTime.Now.Ticks;
+            total_md5 += endTimeMD5 - startTimeMD5;
+            timeTakenMD5 = new TimeSpan (endTimeMD5 - startTimeMD5);
+            Console.WriteLine ("Total time for MD5 {0}", timeTakenMD5.ToString());
+
+            if (FSpot.DuplicatesFinder.IsDuplicate (photo) && !import_duplicates) {
+                Console.WriteLine ("Photo: {0} duplicated\n", photo.Name);
+                duplicates++;
+                if (first_duplicate) {
+                    HigMessageDialog md = new HigMessageDialog (this.Dialog,
+                                                                DialogFlags.DestroyWithParent,
+                                                                MessageType.Error,
+                                                                ButtonsType.YesNo,
+                                                                String.Format (Mono.Posix.Catalog.GetString ("Duplicate photo found {0}."), photo.Name),
+                                                                Mono.Posix.Catalog.GetString ("Do you want to import duplicate photos?"));
+                    ResponseType response = (Gtk.ResponseType) md.Run ();
+                    md.Destroy ();
+                    if (response == ResponseType.Yes) {
+                        import_duplicates = true;
+                        Console.WriteLine ("Importing duplicates ...");
+                    } else {
+                        import_duplicates = false;
+                        Console.WriteLine ("Not Importing duplicates ...");
+                    }
+                    first_duplicate = false;
+                }
+                if (import_duplicates) {                    
 			collection.Add (photo);
+                } else {
+                    Console.WriteLine ("Removing duplicate ...");
+                    store.Remove (photo);
+                }
+            } else {                
+                Console.WriteLine ("Importing Photo: {0}", photo.Name);
+                collection.Add (photo);
+            }
 		
 			//grid.AddThumbnail (thumbnail);
-			UpdateProgressBar (count, total);
+            if (import_duplicates) {
+                UpdateProgressBar (count, total, 0);
+            } else {
+                UpdateProgressBar (count, total, duplicates);
+            }
 			thumbnail.Dispose ();
 		}
 
+        long endTime = DateTime.Now.Ticks;
+        total_import += endTime - startTime;
+        TimeSpan timeTaken = new TimeSpan(endTime - startTime);
+        Console.WriteLine ("Total time importing the photo {0} ({1} MD5)\n", 
+                           timeTaken.ToString(), timeTakenMD5.ToString ());        
+
 		if (ongoing && total > 0)
 			return true;
 		else {
@@ -473,8 +544,9 @@ public class ImportCommand : FSpot.Glade
 		this.importer = imp;
 		AllowFinish = false;
 
+        duplicates = 0;
 		total = importer.Prepare ();
-		UpdateProgressBar (0, total);
+		UpdateProgressBar (0, total, 0);
 		
 		collection.Clear ();
 		collection.Capacity = total;
@@ -492,10 +564,20 @@ public class ImportCommand : FSpot.Glade
 			}
 		}
 
+        TimeSpan timeTaken = new TimeSpan(total_import);
+        TimeSpan timeTakenMD5 = new TimeSpan(total_md5);
+        Console.WriteLine ("Total time importing the photos {0} ({1} MD5)\n", 
+                           timeTaken.ToString(), timeTakenMD5.ToString ()); 
+
 		FSpot.ThumbnailGenerator.Default.PopBlock ();
 		
-		if (importer != null)
+		if (importer != null) {
+			try { 
 			importer.Finish ();
+            } catch (System.Exception e) {
+                Console.WriteLine ("Exception finishing import ..." + e);
+            }
+        }
 		
 		importer = null;
 
@@ -786,7 +868,7 @@ public class ImportCommand : FSpot.Glade
 	{
 		Db db = new Db (db_path, true);
 
-		ImportCommand command = new ImportCommand ();
+		ImportCommand command = new ImportCommand (main_window);
 
 		command.ImportFromPath (db.Photos, directory_path, true);
 
Index: src/MainWindow.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/MainWindow.cs,v
retrieving revision 1.212
diff -u -b -B -p -r1.212 MainWindow.cs
--- src/MainWindow.cs	26 Aug 2005 21:31:04 -0000	1.212
+++ src/MainWindow.cs	3 Sep 2005 09:55:35 -0000
@@ -13,10 +13,11 @@ using System.Text.RegularExpressions;
 using LibGPhoto2;
 
 public class MainWindow {
-        public static MainWindow Toplevel;
+    public static MainWindow Toplevel = null;
 
 	Db db;
 
+	
 	TagSelectionWidget tag_selection_widget;
 	[Glade.Widget] Gtk.Window main_window;
 	[Glade.Widget] Gtk.VBox left_vbox;
@@ -68,6 +69,8 @@ public class MainWindow {
 	[Glade.Widget] MenuItem remove_tag;
 	[Glade.Widget] MenuItem find_tag;
 	
+    [Glade.Widget] MenuItem find_duplicates;
+	
 	[Glade.Widget] Scale zoom_scale;
 
 	[Glade.Widget] VPaned info_vpaned;
@@ -1660,6 +1663,28 @@ public class MainWindow {
 	void HandleClearDateRange (object sender, EventArgs args) {
 		query.Range = null;
 	}
+
+	void HandleFindDuplicates (object sender, EventArgs args) {
+        find_duplicates.Sensitive = false;
+        FSpot.DuplicatesFinder finder = new FSpot.DuplicatesFinder (db, query);
+        finder.CreateDuplicateTag ();
+        tag_selection_widget.Update ();
+        if (!PhotoSelectionActive()) {
+            icon_view.SelectAllCells ();
+        }
+        System.Console.WriteLine ("Looking for duplicates ...");
+        finder.SearchFinished += new FSpot.DuplicatesFinderEnd (HandleEndDuplicates);
+        finder.startFind (SelectedIds());
+    }
+
+    void HandleEndDuplicates (Boolean success) {
+        if (success) {
+            tag_selection_widget.Select (db.Tags.Duplicate);
+            UpdateQuery ();
+            SetViewMode (ModeType.IconView);
+        }
+        find_duplicates.Sensitive = true;
+    }
 
 	// Version Id updates.
 
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/f-spot/src/Makefile.am,v
retrieving revision 1.38
diff -u -b -B -p -r1.38 Makefile.am
--- src/Makefile.am	26 Aug 2005 21:28:48 -0000	1.38
+++ src/Makefile.am	3 Sep 2005 09:55:35 -0000
@@ -15,6 +15,7 @@ F_SPOT_CSDISTFILES =				\
 	$(srcdir)/Delay.cs			\
 	$(srcdir)/DirectoryAdaptor.cs		\
 	$(srcdir)/DirectoryCollection.cs	\
+	$(srcdir)/DuplicatesFinder.cs	\
 	$(srcdir)/Exif.cs			\
 	$(srcdir)/ExifUtils.cs			\
 	$(srcdir)/FlickrExport.cs		\
Index: src/PhotoStore.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/PhotoStore.cs,v
retrieving revision 1.71
diff -u -b -B -p -r1.71 PhotoStore.cs
--- src/PhotoStore.cs	1 Sep 2005 10:01:52 -0000	1.71
+++ src/PhotoStore.cs	3 Sep 2005 09:55:35 -0000
@@ -28,6 +28,7 @@ public class Photo : DbItem, IComparable
 		return Compare (this, photo);
 	}
 	
+    // FIXME: With md5sum field this could be easy
 	public static int Compare (Photo photo1, Photo photo2)
 	{
 		int result = photo1.Id.CompareTo (photo2.Id);
@@ -137,6 +138,16 @@ public class Photo : DbItem, IComparable
 		}
 	}
 
+    private string md5sum;
+    public string MD5Sum {
+		get {
+			return md5sum;
+		}
+		set {
+			md5sum = value;
+		}
+	}
+
 	// Version management
 	public const int OriginalVersionId = 1;
 	private uint highest_version_id;
@@ -393,6 +404,7 @@ public class Photo : DbItem, IComparable
 		this.name = name;
 
 		description = "";
+        md5sum      = "";
 
 		// Note that the original version is never stored in the photo_versions table in the
 		// database.
@@ -493,7 +505,8 @@ public class PhotoStore : DbStore {
 			"       directory_path     STRING NOT NULL,		   " +
 			"       name               STRING NOT NULL,		   " +
 			"       description        TEXT NOT NULL,	           " +
-			"       default_version_id INTEGER NOT NULL		   " +
+			"       default_version_id INTEGER NOT NULL,		     " +
+            "       md5sum             STRING NOT NULL		         " +
 			")";
 
 		command.ExecuteNonQuery ();
@@ -538,8 +551,8 @@ public class PhotoStore : DbStore {
 		command.Connection = Connection;
 
 		command.CommandText = String.Format ("INSERT INTO photos (time, " +
-						     "directory_path, name, description, default_version_id) " +
-						     "       VALUES ({0}, '{1}', '{2}', '{3}', {4})                                       ",
+						     "directory_path, name, description, default_version_id, md5sum) " +
+						     "       VALUES ({0}, '{1}', '{2}', '{3}', {4}, '')                                       ",
 						     unix_time,
 						     SqlString (System.IO.Path.GetDirectoryName (path)),
 						     SqlString (System.IO.Path.GetFileName (path)),
@@ -728,7 +741,8 @@ public class PhotoStore : DbStore {
 						     "       directory_path,                       " +
 						     "       name,                                 " +
 						     "       description,                          " +
-						     "       default_version_id                    " +
+						     "       default_version_id,                   " +
+                             "       md5sum                                " +
 						     "     FROM photos                             " +
 						     "     WHERE id = {0}                          ",
 						     id);
@@ -742,6 +756,7 @@ public class PhotoStore : DbStore {
 
 			photo.Description = reader[3].ToString ();
 			photo.DefaultVersionId = Convert.ToUInt32 (reader[4]);
+            photo.MD5Sum = reader[5].ToString ();
 			AddToCache (photo);
 		}
 
@@ -895,11 +910,13 @@ public class PhotoStore : DbStore {
 
 		SqliteCommand command = new SqliteCommand ();
 		command.Connection = Connection;
-		command.CommandText = String.Format ("UPDATE photos SET description = '{0}',     " +
-						     "                  default_version_id = {1} " +
-						     "              WHERE id = {2}",
+		command.CommandText = String.Format ("UPDATE photos SET description = '{0}'," +
+						     "                default_version_id = {1}," +
+                             "                md5sum = '{2}'"+                
+						     "                WHERE id = {3}",
 						     SqlString (photo.Description),
 						     photo.DefaultVersionId,
+                             SqlString (photo.MD5Sum),  
 						     photo.Id);
 		command.ExecuteNonQuery ();
 		command.Dispose ();
@@ -1004,6 +1021,7 @@ public class PhotoStore : DbStore {
 				
 				photo.Description = reader[4].ToString ();
 				photo.DefaultVersionId = Convert.ToUInt32 (reader[5]);		 
+                photo.MD5Sum = reader[6].ToString ();
 				
 				version_list.Add (photo);
 			}
@@ -1043,7 +1061,8 @@ public class PhotoStore : DbStore {
 						     "       photos.directory_path,              " +
 						     "       photos.name,                        " +
 						     "       photos.description,                 " +
-						     "       photos.default_version_id           " +
+						     "       photos.default_version_id,          " +
+                             "       photos.md5sum                       " +
 						     "     FROM photos                           " +
 						     "     WHERE directory_path = \"{0}\"", dir.FullName);
 
@@ -1084,7 +1103,8 @@ public class PhotoStore : DbStore {
 				      "       photos.directory_path,              " +
 				      "       photos.name,                        " +
 				      "       photos.description,                 " +
-				      "       photos.default_version_id           " +
+				      "       photos.default_version_id,          " +
+                      "       photos.md5sum                       " +
 				      "     FROM photos                      ");
 		
 		if (range != null) {
Index: src/TagStore.cs
===================================================================
RCS file: /cvs/gnome/f-spot/src/TagStore.cs,v
retrieving revision 1.17
diff -u -b -B -p -r1.17 TagStore.cs
--- src/TagStore.cs	10 Mar 2005 02:48:53 -0000	1.17
+++ src/TagStore.cs	3 Sep 2005 09:55:36 -0000
@@ -209,6 +209,16 @@ public class TagStore : DbStore {
 		}
 	}
 
+    private Tag duplicate;
+	public Tag Duplicate {
+		get {
+			return duplicate;
+		}
+        set {
+            duplicate = value;
+        }
+	}
+
 	// In this store we keep all the items (i.e. the tags) in memory at all times.  This is
 	// mostly to simplify handling of the parent relationship between tags, but it also makes it
 	// a little bit faster.  We achieve this by passing "true" as the cache_is_immortal to our
@@ -242,6 +252,9 @@ public class TagStore : DbStore {
 			
 			if (tag.Name == "Hidden")
 				hidden = tag;
+
+            if (tag.Name == "Duplicate")
+                duplicate = tag;            
 		}
 
 		reader.Close ();
@@ -401,6 +414,11 @@ public class TagStore : DbStore {
 	
 	public override void Remove (DbItem item)
 	{
+        // FIXME: Hack!
+        if (string.Compare (((Tag) item).Name, "Duplicate") == 0) {
+            duplicate = null;
+        }
+        
 		RemoveFromCache (item);
 		
 		((Tag)item).Category = null;
Index: src/f-spot.glade
===================================================================
RCS file: /cvs/gnome/f-spot/src/f-spot.glade,v
retrieving revision 1.106
diff -u -b -B -p -r1.106 f-spot.glade
--- src/f-spot.glade	17 Aug 2005 19:35:02 -0000	1.106
+++ src/f-spot.glade	3 Sep 2005 09:55:38 -0000
@@ -7129,6 +7129,15 @@ Photo Details</property>
 		  </child>
 
 		  <child>
+		    <widget class="GtkMenuItem" id="find_duplicates">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_Find Duplicates</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="HandleFindDuplicates" last_modification_time="Tue, 10 Aug 2004 07:08:24 GMT"/>
+		    </widget>
+		  </child>
+
+		  <child>
 		    <widget class="GtkSeparatorMenuItem" id="separator15">
 		      <property name="visible">True</property>
 		    </widget>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]