[shotwell/wip/phako/enhanced-faces: 38/136] Restructuring face-to-vec process to happen with face-detection



commit 97da43c65e79b22d568b02d397dd05eacd6cf94f
Author: NarendraMA <narendra_m_a yahoo com>
Date:   Sun Jul 22 19:27:10 2018 +0530

    Restructuring face-to-vec process to happen with face-detection

 facedetect/facedetect-opencv.cpp        | 37 +++++++++++++++++---------
 facedetect/org.gnome.ShotwellFaces1.xml |  3 ++-
 facedetect/shotwell-facedetect.cpp      | 22 +++++++++++-----
 facedetect/shotwell-facedetect.hpp      |  4 ++-
 src/AppDirs.vala                        |  6 +++++
 src/AppWindow.vala                      |  2 +-
 src/Commands.vala                       | 20 ++++++++++++++
 src/Resources.vala                      | 12 +++++++++
 src/db/Db.vala                          | 17 +++++++-----
 src/db/FaceLocationTable.vala           | 46 +++++++--------------------------
 src/db/FaceTable.vala                   | 15 +++++++++++
 src/faces/Face.vala                     | 10 +++++++
 src/faces/FaceDetect.vala               |  5 ++--
 src/faces/FaceLocation.vala             | 10 ++++---
 src/faces/FacePage.vala                 | 14 ++++++++++
 src/faces/FacesTool.vala                | 13 ++++------
 16 files changed, 159 insertions(+), 77 deletions(-)
---
diff --git a/facedetect/facedetect-opencv.cpp b/facedetect/facedetect-opencv.cpp
index 9bf151c0..203c487a 100644
--- a/facedetect/facedetect-opencv.cpp
+++ b/facedetect/facedetect-opencv.cpp
@@ -1,7 +1,7 @@
 #include "shotwell-facedetect.hpp"
 
 // Detect faces in a photo
-std::vector<FaceRect> detectFaces(cv::String inputName, cv::String cascadeName, double scale) {
+std::vector<FaceRect> detectFaces(cv::String inputName, cv::String cascadeName, double scale, bool infer = 
false) {
     cv::CascadeClassifier cascade;
        if (!cascade.load(cascadeName)) {
         std::cout << "error;Could not load classifier cascade. Filename: \"" << cascadeName << "\"" << 
std::endl;
@@ -35,6 +35,14 @@ std::vector<FaceRect> detectFaces(cv::String inputName, cv::String cascadeName,
         i.y = (float) r->y / smallImgSize.height;
         i.width = (float) r->width / smallImgSize.width;
         i.height = (float) r->height / smallImgSize.height;
+        if (infer && !faceRecogNet.empty()) {
+            // Get colour image for vector generation
+            cv::Mat colourImg;
+            cv::resize(img, colourImg, smallImgSize, 0, 0, cv::INTER_LINEAR);
+            i.vec = faceToVecMat(colourImg(*r)); // Run vector conversion on the face
+        } else {
+            i.vec.assign(128, 0);
+        }
         scaled.push_back(i);
     }
 
@@ -61,27 +69,32 @@ bool loadNet(cv::String netFile) {
 // Face to vector convertor
 // Adapted from OpenCV example:
 // https://github.com/opencv/opencv/blob/master/samples/dnn/js_face_recognition.html
-std::vector<double> faceToVec(cv::String inputName) {
+std::vector<double> faceToVecMat(cv::Mat img) {
     std::vector<double> ret;
-    cv::Mat img = imread(inputName, 1);
-       if (img.empty()) {
-        std::cout << "error;Could not load the file to process. Filename: \"" << inputName << "\"" << 
std::endl;
-        return ret;
-       }
-
     cv::Mat smallImg(96, 96, CV_8UC1);
     cv::Size smallImgSize = smallImg.size();
-    cv::resize(img, smallImg, smallImgSize, 0, 0, cv::INTER_LINEAR);
 
+    cv::resize(img, smallImg, smallImgSize, 0, 0, cv::INTER_LINEAR);
     // Generate 128 element face vector using DNN
     cv::Mat blob = cv::dnn::blobFromImage(smallImg, 1.0 / 255, smallImgSize,
                                           cv::Scalar(), true, false);
     faceRecogNet.setInput(blob);
-    std::cout << "Starting recognition on " << inputName << " blob height " <<
-        blob.size().height << " width " << blob.size().width << std::endl;
     cv::Mat vec = faceRecogNet.forward();
-    std::cout << "Recognition done!" << std::endl;
     // Return vector
     ret.assign((double*)vec.datastart, (double*)vec.dataend);
+    std::cout << "Recognition done! " << ret.back() << std::endl;
+    return ret;
+}
+
+std::vector<double> faceToVec(cv::String inputName) {
+    std::vector<double> ret;
+    cv::Mat img = imread(inputName, 1);
+       if (img.empty()) {
+        std::cout << "error;Could not load the file to process. Filename: \"" << inputName << "\"" << 
std::endl;
+        ret.assign(128, 0);
+        return ret;
+       }
+    ret = faceToVecMat(img);
     return ret;
 }
+
diff --git a/facedetect/org.gnome.ShotwellFaces1.xml b/facedetect/org.gnome.ShotwellFaces1.xml
index 26bb321b..6bc84f19 100644
--- a/facedetect/org.gnome.ShotwellFaces1.xml
+++ b/facedetect/org.gnome.ShotwellFaces1.xml
@@ -18,7 +18,8 @@
       <arg type="s" name="image" direction="in" />
       <arg type="s" name="cascade" direction="in" />
       <arg type="d" name="scale" direction="in" />
-      <arg type="a(dddd)" name="faces" direction="out" />
+      <arg type="b" name="infer" direction="in" />
+      <arg type="a(ddddad)" name="faces" direction="out" />
     </method>
 
     <!--
diff --git a/facedetect/shotwell-facedetect.cpp b/facedetect/shotwell-facedetect.cpp
index 8061a844..d6d46d36 100644
--- a/facedetect/shotwell-facedetect.cpp
+++ b/facedetect/shotwell-facedetect.cpp
@@ -16,19 +16,27 @@ static gboolean on_handle_detect_faces(ShotwellFaces1 *object,
                                        GDBusMethodInvocation *invocation,
                                        const gchar *arg_image,
                                        const gchar *arg_cascade,
-                                       gdouble arg_scale) {
+                                       gdouble arg_scale,
+                                       gboolean arg_infer) {
     GVariantBuilder *builder;
     GVariant *faces;
     std::vector<FaceRect> rects = 
-        detectFaces(arg_image, arg_cascade, arg_scale);
+        detectFaces(arg_image, arg_cascade, arg_scale, arg_infer);
     // Construct return value
-    builder = g_variant_builder_new(G_VARIANT_TYPE ("a(dddd)"));
+    builder = g_variant_builder_new(G_VARIANT_TYPE ("a(ddddad)"));
     for (std::vector<FaceRect>::const_iterator r = rects.begin(); r != rects.end(); r++) {
-        GVariant *rect = g_variant_new("(dddd)", r->x, r->y, r->width, r->height);
-        g_variant_builder_add(builder, "(dddd)", rect);
-        g_debug("Returning %f,%f", r->x, r->y);
+        GVariantBuilder *arr_builder = g_variant_builder_new(G_VARIANT_TYPE ("ad"));
+        for (std::vector<double>::const_iterator v = r->vec.begin(); v != r->vec.end(); v++) {
+            GVariant *d = g_variant_new("d", *v);
+            g_variant_builder_add(arr_builder, "d", d);
+        }
+        GVariant *vec = g_variant_new("ad", arr_builder);
+        g_variant_builder_unref(arr_builder);
+        GVariant *rect = g_variant_new("(dddd@ad)", r->x, r->y, r->width, r->height, vec);
+        g_variant_builder_add(builder, "@(ddddad)", rect);
+        g_debug("Returning %f,%f-%f", r->x, r->y, r->vec.back());
     }
-    faces = g_variant_new("a(dddd)", builder);
+    faces = g_variant_new("a(ddddad)", builder);
     g_variant_builder_unref (builder);
     // Call return
     shotwell_faces1_complete_detect_faces(object, invocation,
diff --git a/facedetect/shotwell-facedetect.hpp b/facedetect/shotwell-facedetect.hpp
index 0fdff2b7..d8413952 100644
--- a/facedetect/shotwell-facedetect.hpp
+++ b/facedetect/shotwell-facedetect.hpp
@@ -18,11 +18,13 @@
 
 typedef struct {
     float x, y, width, height;
+    std::vector<double> vec;
 } FaceRect;
 
 // Global variable for DNN to generate vector out of face
 static cv::dnn::Net faceRecogNet;
 
 bool loadNet(cv::String netFile);
-std::vector<FaceRect> detectFaces(cv::String inputName, cv::String cascadeName, double scale);
+std::vector<FaceRect> detectFaces(cv::String inputName, cv::String cascadeName, double scale, bool infer);
+std::vector<double> faceToVecMat(cv::Mat img);
 std::vector<double> faceToVec(cv::String inputName);
diff --git a/src/AppDirs.vala b/src/AppDirs.vala
index 331a3f30..e2a5065a 100644
--- a/src/AppDirs.vala
+++ b/src/AppDirs.vala
@@ -210,6 +210,12 @@ class AppDirs {
         
         return tmp_dir;
     }
+
+    public static string get_temp_filename() {
+        string tmp_dir = get_temp_dir().get_path();
+        assert(tmp_dir != null);
+        return Path.build_filename(tmp_dir, "face_XXXXXX.png");
+    }
     
     public static File get_data_subdir(string name, string? subname = null) {
         File subdir = get_data_dir().get_child(name);
diff --git a/src/AppWindow.vala b/src/AppWindow.vala
index b746ebbd..fd831569 100644
--- a/src/AppWindow.vala
+++ b/src/AppWindow.vala
@@ -578,7 +578,7 @@ public abstract class AppWindow : PageWindow {
         panic(_("A fatal error occurred when accessing Shotwell’s library. Shotwell cannot 
continue.\n\n%s").printf(
             err.message));
     }
-    
+
     public static void panic(string msg) {
         critical(msg);
         error_message(msg);
diff --git a/src/Commands.vala b/src/Commands.vala
index a0d3766c..014f11dd 100644
--- a/src/Commands.vala
+++ b/src/Commands.vala
@@ -2574,6 +2574,26 @@ public class RenameFaceCommand : SimpleProxyableCommand {
     }
 }
 
+public class SetFaceRefCommand : SimpleProxyableCommand {
+    private FaceLocation face_loc;
+    
+    public SetFaceRefCommand(Face face, MediaSource source) {
+        base (face, Resources.set_face_from_photo_label(face.get_name()), face.get_name());
+        Gee.Map<FaceID?, FaceLocation>? face_loc_map = FaceLocation.get_locations_by_photo((Photo)source);
+        face_loc = face_loc_map.get(face.get_face_id());
+    }
+    
+    protected override void execute_on_source(DataSource source) {
+        if (!((Face) source).set_reference(face_loc))
+            AppWindow.error_message(Resources.set_face_from_photo_error());
+    }
+
+    protected override void undo_on_source(DataSource source) {
+        //if (!((Face) source).rename(old_name))
+        //    AppWindow.error_message(Resources.rename_face_exists_message(old_name));
+    }
+}
+
 public class DeleteFaceCommand : SimpleProxyableCommand {
     private Gee.Map<PhotoID?, string> photo_geometry_map = new Gee.HashMap<PhotoID?, string>
         ((Gee.HashDataFunc)FaceLocation.photo_id_hash, (Gee.EqualDataFunc)FaceLocation.photo_ids_equal);
diff --git a/src/Resources.vala b/src/Resources.vala
index ff364132..8332f4c5 100644
--- a/src/Resources.vala
+++ b/src/Resources.vala
@@ -417,6 +417,18 @@ along with Shotwell; if not, write to the Free Software Foundation, Inc.,
         return ngettext ("Remove Face “%s” From Photo",
                          "Remove Face “%s” From Photos", count).printf(name);
     }
+
+    public string set_face_from_photo_menu(string name) {
+        return _("_Train Face “%s” From Photo").printf(name);
+    }
+    
+    public string set_face_from_photo_label(string name) {
+        return _("_Train Face “%s” From Photo").printf(name);
+    }
+
+    public static string set_face_from_photo_error() {
+        return "Unable to set face as reference";
+    }
     
     public string rename_face_menu(string name) {
         return _("Re_name Face “%s”…").printf(name);
diff --git a/src/db/Db.vala b/src/db/Db.vala
index 6facbbc3..ac24f113 100644
--- a/src/db/Db.vala
+++ b/src/db/Db.vala
@@ -357,14 +357,19 @@ private VerifyResult upgrade_database(int input_version) {
     // * Added face vector column to FaceTable
     //
     
-    if (!DatabaseTable.has_column("FaceLocationTable", "pix")) {
-        message("upgrade_database: adding pix column to FaceLocationTable");
-        if (!DatabaseTable.add_column("FaceLocationTable", "pix", "BLOB"))
+    if (!DatabaseTable.has_column("FaceLocationTable", "vec")) {
+        message("upgrade_database: adding vec column to FaceLocationTable");
+        if (!DatabaseTable.add_column("FaceLocationTable", "vec", "TEXT"))
             return VerifyResult.UPGRADE_ERROR;
     }
-    if (!DatabaseTable.has_column("FaceTable", "vec")) {
-        message("upgrade_database: adding vec column to FaceTable");
-        if (!DatabaseTable.add_column("FaceTable", "vec", "TEXT"))
+    if (!DatabaseTable.has_column("FaceLocationTable", "guess")) {
+        message("upgrade_database: adding guess column to FaceLocationTable");
+        if (!DatabaseTable.add_column("FaceLocationTable", "guess", "INTEGER DEFAULT 0"))
+            return VerifyResult.UPGRADE_ERROR;
+    }
+    if (!DatabaseTable.has_column("FaceTable", "ref")) {
+        message("upgrade_database: adding ref column to FaceTable");
+        if (!DatabaseTable.add_column("FaceTable", "ref", "INTEGER DEFAULT -1"))
             return VerifyResult.UPGRADE_ERROR;
     }
     
diff --git a/src/db/FaceLocationTable.vala b/src/db/FaceLocationTable.vala
index f8132615..219928d9 100644
--- a/src/db/FaceLocationTable.vala
+++ b/src/db/FaceLocationTable.vala
@@ -29,7 +29,7 @@ public class FaceLocationRow {
     public FaceID face_id;
     public PhotoID photo_id;
     public string geometry;
-    public uint8[] pix;
+    public string vec;
 }
 
 public class FaceLocationTable : DatabaseTable {
@@ -46,7 +46,7 @@ public class FaceLocationTable : DatabaseTable {
             + "face_id INTEGER NOT NULL, "
             + "photo_id INTEGER NOT NULL, "
             + "geometry TEXT, "
-            + "pix BLOB"
+            + "vec TEXT"
             + ")", -1, out stmt);
         assert(res == Sqlite.OK);
         
@@ -62,10 +62,10 @@ public class FaceLocationTable : DatabaseTable {
         return instance;
     }
  
-    public FaceLocationRow add(FaceID face_id, PhotoID photo_id, string geometry, uint8[]? pix = null) 
throws DatabaseError {
+    public FaceLocationRow add(FaceID face_id, PhotoID photo_id, string geometry, string? vec = null) throws 
DatabaseError {
         Sqlite.Statement stmt;
         int res = db.prepare_v2(
-            "INSERT INTO FaceLocationTable (face_id, photo_id, geometry, pix) VALUES (?, ?, ?, ?)",
+            "INSERT INTO FaceLocationTable (face_id, photo_id, geometry, vec) VALUES (?, ?, ?, ?)",
              -1, out stmt);
         assert(res == Sqlite.OK);
         
@@ -75,12 +75,7 @@ public class FaceLocationTable : DatabaseTable {
         assert(res == Sqlite.OK);
         res = stmt.bind_text(3, geometry);
         assert(res == Sqlite.OK);
-        message("face table pix len %d", pix.length);
-        if (pix == null) {
-            res = stmt.bind_blob(4, null, 0);
-        } else {
-            res = stmt.bind_blob(4, pix, pix.length);
-        }
+        res = stmt.bind_text(4, vec);
         assert(res == Sqlite.OK);
         
         res = stmt.step();
@@ -92,7 +87,7 @@ public class FaceLocationTable : DatabaseTable {
         row.face_id = face_id;
         row.photo_id = photo_id;
         row.geometry = geometry;
-        row.pix = pix;
+        row.vec = vec;
         
         return row;
     }
@@ -100,7 +95,7 @@ public class FaceLocationTable : DatabaseTable {
     public Gee.List<FaceLocationRow?> get_all_rows() throws DatabaseError {
         Sqlite.Statement stmt;
         int res = db.prepare_v2(
-            "SELECT id, face_id, photo_id, geometry FROM FaceLocationTable",
+            "SELECT id, face_id, photo_id, geometry, vec FROM FaceLocationTable",
             -1, out stmt);
         assert(res == Sqlite.OK);
         
@@ -119,6 +114,7 @@ public class FaceLocationTable : DatabaseTable {
             row.face_id = FaceID(stmt.column_int64(1));
             row.photo_id = PhotoID(stmt.column_int64(2));
             row.geometry = stmt.column_text(3);
+            row.vec = stmt.column_text(4);
             
             rows.add(row);
         }
@@ -175,28 +171,6 @@ public class FaceLocationTable : DatabaseTable {
         return stmt.column_text(0);
     }
     
-    public uint8[]? get_face_source_pix(Face face, MediaSource source)
-        throws DatabaseError {
-        Sqlite.Statement stmt;
-        int res = db.prepare_v2(
-            "SELECT pix FROM FaceLocationTable WHERE face_id=? AND photo_id=?",
-            -1, out stmt);
-        assert(res == Sqlite.OK);
-        
-        res = stmt.bind_int64(1, face.get_instance_id());
-        assert(res == Sqlite.OK);
-        res = stmt.bind_int64(2, ((Photo) source).get_instance_id());
-        assert(res == Sqlite.OK);
-        
-        res = stmt.step();
-        if (res == Sqlite.DONE)
-            return null;
-        else if (res != Sqlite.ROW)
-            throw_error("FaceLocationTable.get_face_source_pix", res);
-        
-        return (uint8[])stmt.column_blob(0);
-    }
-    
     public void remove_face_from_source(FaceID face_id, PhotoID photo_id) throws DatabaseError {
         Sqlite.Statement stmt;
         int res = db.prepare_v2(
@@ -233,13 +207,13 @@ public class FaceLocationTable : DatabaseTable {
     public void update_face_location_face_data(FaceLocation face_location)
         throws DatabaseError {
         Sqlite.Statement stmt;
-        int res = db.prepare_v2("UPDATE FaceLocationTable SET geometry=?, pix=? WHERE id=?", -1, out stmt);
+        int res = db.prepare_v2("UPDATE FaceLocationTable SET geometry=?, vec=? WHERE id=?", -1, out stmt);
         assert(res == Sqlite.OK);
 
         FaceLocationData face_data = face_location.get_face_data();
         res = stmt.bind_text(1, face_data.geometry);
         assert(res == Sqlite.OK);
-        res = stmt.bind_blob(2, face_data.pixbuf, face_data.pixbuf.length);
+        res = stmt.bind_text(2, face_data.vec);
         assert(res == Sqlite.OK);
         res = stmt.bind_int64(3, face_location.get_face_location_id().id);
         assert(res == Sqlite.OK);
diff --git a/src/db/FaceTable.vala b/src/db/FaceTable.vala
index a6e0bad6..c8e934cd 100644
--- a/src/db/FaceTable.vala
+++ b/src/db/FaceTable.vala
@@ -165,5 +165,20 @@ public class FaceTable : DatabaseTable {
     public void rename(FaceID face_id, string new_name) throws DatabaseError {
         update_text_by_id_2(face_id.id, "name", new_name);
     }
+
+    public void set_reference(FaceID face_id, PhotoID photo_id)
+        throws DatabaseError {
+        Sqlite.Statement stmt;
+        int res = db.prepare_v2("UPDATE FaceTable SET ref=? WHERE id=?", -1, out stmt);
+        assert(res == Sqlite.OK);
+        res = stmt.bind_int64(1, photo_id.id);
+        assert(res == Sqlite.OK);
+        res = stmt.bind_int64(2, face_id.id);
+        assert(res == Sqlite.OK);
+        
+        res = stmt.step();
+        if (res != Sqlite.DONE)
+            throw_error("FaceTable.set_reference", res);
+    }
 }
 #endif
diff --git a/src/faces/Face.vala b/src/faces/Face.vala
index 66968c58..1c180b25 100644
--- a/src/faces/Face.vala
+++ b/src/faces/Face.vala
@@ -592,6 +592,16 @@ public class Face : DataSource, ContainerSource, Proxyable, Indexable {
         
         return true;
     }
+
+    public bool set_reference(FaceLocation face_loc) {
+        try {
+            FaceTable.get_instance().set_reference(row.face_id, face_loc.get_photo_id());
+        } catch (DatabaseError err) {
+            AppWindow.database_error(err);
+            return false;
+        }
+        return true;
+    }
     
     public bool contains(MediaSource source) {
         return media_views.has_view_for_source(source);
diff --git a/src/faces/FaceDetect.vala b/src/faces/FaceDetect.vala
index 25086069..7a027b48 100644
--- a/src/faces/FaceDetect.vala
+++ b/src/faces/FaceDetect.vala
@@ -29,15 +29,16 @@ public struct FaceRect {
     public double y;
     public double width;
     public double height;
+    public double[] vec;
 }
 
 [DBus (name = "org.gnome.Shotwell.Faces1")]
 public interface FaceDetectInterface : Object {
-    public abstract FaceRect[] detect_faces(string inputName, string cascadeName, double scale)
+    public abstract FaceRect[] detect_faces(string inputName, string cascadeName, double scale, bool infer)
         throws IOError, DBusError;
     public abstract bool load_net(string netFile)
         throws IOError, DBusError;
-    public abstract bool face_to_vec(string inputName)
+    public abstract double[] face_to_vec(string inputName)
         throws IOError, DBusError;
     public abstract void terminate() throws IOError, DBusError;
 }
diff --git a/src/faces/FaceLocation.vala b/src/faces/FaceLocation.vala
index fce8e99e..c7e2c6c2 100644
--- a/src/faces/FaceLocation.vala
+++ b/src/faces/FaceLocation.vala
@@ -9,7 +9,7 @@
 // Encapsulate geometry and pixels of a Face
 public struct FaceLocationData {
     public string geometry;
-    public uint8[] pixbuf;
+    public string vec;
 }
 
 public class FaceLocation : Object {
@@ -58,7 +58,7 @@ public class FaceLocation : Object {
         try {
             face_location =
                 FaceLocation.add_from_row(
-                    FaceLocationTable.get_instance().add(face_id, photo_id, face_data.geometry, 
face_data.pixbuf));
+                    FaceLocationTable.get_instance().add(face_id, photo_id, face_data.geometry, 
face_data.vec));
         } catch (DatabaseError err) {
             AppWindow.database_error(err);
         }
@@ -92,7 +92,7 @@ public class FaceLocation : Object {
         
         FaceLocation face_location =
             new FaceLocation(row.face_location_id, row.face_id, row.photo_id,
-                { row.geometry, row.pix });
+                { row.geometry, row.vec });
         
         Gee.Map<PhotoID?, FaceLocation> photos_map = face_photos_map.get(row.face_id);
         if (photos_map == null) {photos_map = new Gee.HashMap<PhotoID?, FaceLocation>
@@ -214,6 +214,10 @@ public class FaceLocation : Object {
     public FaceLocationData get_face_data() {
         return face_data;
     }
+
+    public PhotoID get_photo_id() {
+        return photo_id;
+    }
     
     private void set_face_data(FaceLocationData face_data) {
         this.face_data = face_data;
diff --git a/src/faces/FacePage.vala b/src/faces/FacePage.vala
index 41d1cef1..d9b60640 100644
--- a/src/faces/FacePage.vala
+++ b/src/faces/FacePage.vala
@@ -46,6 +46,7 @@ public class FacePage : CollectionPage {
         { "DeleteFace", on_delete_face },
         { "RenameFace", on_rename_face },
         { "RemoveFaceFromPhotos", on_remove_face_from_photos },
+        { "SetFaceRefFromPhoto", on_set_face_ref },
         { "DeleteFaceSidebar", on_delete_face },
         { "RenameFaceSidebar", on_rename_face }
     };
@@ -76,6 +77,7 @@ public class FacePage : CollectionPage {
        
         menuFaces.add_menu_item(Resources.remove_face_from_photos_menu(this.face.get_name(), 
get_view().get_count()), "RemoveFaceFromPhotos", "<Primary>r");
         menuFaces.add_menu_item(Resources.rename_face_menu(this.face.get_name()), "RenameFace", 
"<Primary>e");
+        menuFaces.add_menu_item(Resources.set_face_from_photo_menu(this.face.get_name()), 
"SetFaceRefFromPhoto", "<Primary>r");
         menuFaces.add_menu_item(Resources.delete_face_menu(this.face.get_name()), "DeleteFace", 
"<Primary>t");
         
         return menuFaces;
@@ -104,6 +106,11 @@ public class FacePage : CollectionPage {
             null,
             selected_count > 0);
         
+        set_action_details("SetFaceRefFromPhoto",
+            Resources.set_face_from_photo_menu(face.get_name()),
+            null,
+            selected_count == 1);
+            
         base.update_actions(selected_count, count);
     }
     
@@ -122,6 +129,13 @@ public class FacePage : CollectionPage {
                 (Gee.Collection<MediaSource>) get_view().get_selected_sources()));
         }
     }
+
+    private void on_set_face_ref() {
+        if (get_view().get_selected_count() == 1) {
+            get_command_manager().execute(new SetFaceRefCommand(face,
+                            (MediaSource) get_view().get_selected_at(0).get_source()));
+        }
+    }
 }
 
 #endif
diff --git a/src/faces/FacesTool.vala b/src/faces/FacesTool.vala
index 2c5faf6a..65c532ab 100644
--- a/src/faces/FacesTool.vala
+++ b/src/faces/FacesTool.vala
@@ -334,7 +334,7 @@ public class FacesTool : EditingTools.EditingTool {
             FaceRect[] rects;
             try {
                 rects = FaceDetect.interface.detect_faces(image_path,
-                                                          AppDirs.get_haarcascade_file().get_path(), scale);
+                                                          AppDirs.get_haarcascade_file().get_path(), scale, 
true);
             } catch(Error e) {
                 spawnError = "DBus error: " + e.message + "!\n";
                 return;
@@ -763,15 +763,12 @@ public class FacesTool : EditingTools.EditingTool {
                 continue;
 
             Face new_face = Face.for_name(face_shape.get_name());
-            uint8[] face_pix;
-            try {
-                face_shape.get_pixbuf().save_to_buffer(out face_pix, "png");
-                message("face pix length %d", face_pix.length);
-            } catch (Error e) {
-                AppWindow.error_message("Cannot get pixbuf for image\n");
+            string face_vec_str = "";
+            if (face_vec != null) {
+                foreach (var d in face_vec) { face_vec_str += d.to_string() + ","; }
             }
             FaceLocationData face_data = {
-                face_shape.serialize(), face_pix
+                face_shape.serialize(), face_vec_str
             };
             new_faces.set(new_face, face_data);
         }


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