[shotwell: 1/2] Wip/faces



commit 56acbed6e8d65d2d3debd86b8fd79a809e10a4f1
Author: NMA <narendra_m_a yahoo com>
Date:   Tue Feb 19 14:43:00 2019 +0000

    Wip/faces

 .gitignore                                         |   84 ++
 facedetect/meson.build                             |   10 -
 facedetect/shotwell-facedetect.cpp                 |  134 ---
 flatpak/org.gnome.Shotwell.Faces.json              |   97 ++
 flatpak/org.gnome.Shotwell.json                    |    3 +-
 meson.build                                        |    4 +-
 meson_options.txt                                  |    1 +
 src/AppDirs.vala                                   |    8 +-
 src/AppWindow.vala                                 |    2 +-
 src/Commands.vala                                  |   42 +-
 src/Photo.vala                                     |    2 +-
 src/Resources.vala                                 |   12 +
 src/db/DatabaseTable.vala                          |    2 +-
 src/db/Db.vala                                     |   26 +
 src/db/FaceLocationTable.vala                      |   76 +-
 src/db/FaceTable.vala                              |   48 +-
 src/faces/Face.vala                                |   29 +-
 src/faces/FaceDetect.vala                          |   93 ++
 src/faces/FaceLocation.vala                        |   46 +-
 src/faces/FacePage.vala                            |   14 +
 src/faces/FaceShape.vala                           |   69 +-
 src/faces/FacesTool.vala                           |  194 ++--
 src/meson.build                                    |    9 +
 .../facedetect-haarcascade.xml                     |    0
 .../shotwell-facedetect/facedetect-opencv.cpp      |  175 +++
 subprojects/shotwell-facedetect/meson.build        |   51 +
 .../org.gnome.Shotwell.Faces1.desktop.in           |    7 +
 .../org.gnome.Shotwell.Faces1.service.in           |    3 +
 .../org.gnome.ShotwellFaces1.xml                   |   51 +
 .../shotwell-facedetect/shotwell-facedetect.cpp    |  116 ++
 .../shotwell-facedetect/shotwell-facedetect.hpp    |   36 +
 vapi/opencv.vapi                                   | 1116 ++++++++++++++++++++
 32 files changed, 2261 insertions(+), 299 deletions(-)
---
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..fcea052f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,84 @@
+
+# Hide sublime text stuff
+*.sublime-project
+*.sublime-workspace
+
+# Hide some OS X stuff
+.DS_Store
+.AppleDouble
+.LSOverride
+Icon
+
+# Thumbnails
+._*
+
+.idea
+
+# pytest
+.cache
+
+# GITHUB Proposed Python stuff:
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+.eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Logs
+*.log
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+
+.python-version
+
+# emacs auto backups
+*~
+*#
+*.orig
+
+# venv stuff
+pyvenv.cfg
+pip-selfcheck.json
+venv
+.venv
+
+# vimmy stuff
+*.swp
+*.swo
+
+ctags.tmp
+
+# vagrant stuff
+virtualization/vagrant/setup_done
+virtualization/vagrant/.vagrant
+virtualization/vagrant/config
+
+# Visual Studio Code
+.vscode
diff --git a/flatpak/org.gnome.Shotwell.Faces.json b/flatpak/org.gnome.Shotwell.Faces.json
new file mode 100644
index 00000000..e01327c3
--- /dev/null
+++ b/flatpak/org.gnome.Shotwell.Faces.json
@@ -0,0 +1,97 @@
+{
+    "app-id" : "org.gnome.Shotwell.Faces1",
+    "runtime" : "org.gnome.Platform",
+    "runtime-version" : "3.30",
+    "sdk" : "org.gnome.Sdk",
+    "tags" : [
+        "nightly"
+    ],
+    "desktop-file-name-prefix" : "(Nightly) ",
+    "finish-args" : [
+        "--env=DCONF_USER_CONFIG_DIR=.config/dconf",
+        "--filesystem=~/.config/dconf:ro",
+        "--filesystem=xdg-download",
+        "--filesystem=xdg-pictures",
+        "--share=ipc",
+        "--talk-name=org.gtk.vfs",
+        "--talk-name=org.gtk.vfs.*"
+    ],
+    "cleanup" : [
+        "/include",
+        "/lib/pkconfig",
+        "/share/pkgconfig",
+        "/share/gtk-doc",
+        "/share/man",
+        "/share/vala",
+        "/lib/girepository",
+        "*.la",
+        "*.a"
+    ],
+    "modules" : [
+        {
+            "name" : "opencv",
+            "buildsystem" : "cmake",
+            "builddir" : true,
+            "cleanup" : [
+                "/share/OpenCV/*.cmake",
+                "/share/OpenCV/*.supp"
+            ],
+            "config-opts" : [
+                "-DBUILD_TESTS=OFF",
+                "-DBUILD_EXAMPLES=OFF",
+                "-DBUILD_PERF_TESTS=OFF",
+                "-DWITH_FFMPEG=OFF",
+                "-DWITH_GTK=OFF",
+                "-DWITH_GSTREAMER=OFF",
+                "-DWITH_JASPER=OFF",
+                "-DWITH_OPENEXR=OFF",
+                "-DWITH_GDAL=OFF",
+                "-DWITH_GDCM=OFF",
+                "-DBUILD_opencv_apps=OFF",
+                "-DCMAKE_INSTALL_LIBDIR=lib",
+                "-DBUILD_LIST=imgproc,imgcodecs,objdetect,dnn"
+            ],
+            "sources" : [
+                {
+                    "type" : "git",
+                    "tag" : "3.4.1",
+                    "commit" : "6ffc48769ac60d53c4bd1913eac15117c9b1c9f7",
+                    "url" : "https://github.com/opencv/opencv";
+                }
+            ]
+        },
+        {
+            "name" : "shotwell-facedetect",
+            "buildsystem" : "meson",
+            "subdir" : "subprojects/shotwell-facedetect",
+            "sources" : [
+                {
+                    "type" : "git",
+                    "path": "/home/jgeorg/Source/GNOME/shotwell",
+                    "branch" : "enhanced-faces"
+                },
+                {
+                    "type" : "extra-data",
+                    "filename" : "openface.nn4.small2.v1.t7",
+                    "url" : "https://storage.cmusatyalab.org/openface-models/nn4.small2.v1.t7";,
+                    "sha256" : "9b72d54aeb24a64a8135dca8e792f7cc675c99a884a6940350a6cedcf7b7ba08",
+                    "size" : 31510785
+                },
+                {
+                    "type" : "extra-data",
+                    "filename" : "res10_300x300_ssd_iter_140000_fp16.caffemodel",
+                    "url" : 
"https://raw.githubusercontent.com/opencv/opencv_3rdparty/19512576c112aa2c7b6328cb0e8d589a4a90a26d/res10_300x300_ssd_iter_140000_fp16.caffemodel";,
+                    "sha256" : "510ffd2471bd81e3fcc88a5beb4eae4fb445ccf8333ebc54e7302b83f4158a76",
+                    "size" : 5351047
+                },
+                {
+                    "type" : "extra-data",
+                    "filename" : "deploy.prototxt",
+                    "url" : 
"https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt";,
+                    "sha256" : "f62621cac923d6f37bd669298c428bb7ee72233b5f8c3389bb893e35ebbcf795",
+                    "size" : 28092
+                }
+            ]
+        }
+    ]
+}
diff --git a/flatpak/org.gnome.Shotwell.json b/flatpak/org.gnome.Shotwell.json
index c32ec38f..34f3f952 100644
--- a/flatpak/org.gnome.Shotwell.json
+++ b/flatpak/org.gnome.Shotwell.json
@@ -221,7 +221,8 @@
             "config-opts" : [
                 "-Dudev=false",
                 "-Dinstall-apport-hook=false",
-                "-Dface-detection=true"
+                "-Dface-detection=true",
+                "-Dface-detection-helper=false"
             ],
             "sources" : [
                 {
diff --git a/meson.build b/meson.build
index b589be74..795cd918 100644
--- a/meson.build
+++ b/meson.build
@@ -88,7 +88,9 @@ endif
 
 if get_option('face-detection')
   add_global_arguments(['--define=ENABLE_FACES'], language : 'vala')
-  subdir('facedetect')
+  if get_option('face-detection-helper')
+      subproject('shotwell-facedetect')
+  endif
 endif
 
 json_glib = dependency('json-glib-1.0')
diff --git a/meson_options.txt b/meson_options.txt
index 3c81a5cd..f9b17a75 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -7,3 +7,4 @@ option('dupe-detection', type: 'boolean', value : 'true', description: 'Disable
 option('udev', type: 'boolean', value : 'true', description: 'Enable or disable udev support')
 option('install-apport-hook', type : 'boolean', value : 'true', description: 'Enable Ubuntu apport hook')
 option('face-detection', type:'boolean', value:false)
+option('face-detection-helper', type : 'boolean', value : 'true', description : 'If face-detection is 
enabled, build the external helper tool')
diff --git a/src/AppDirs.vala b/src/AppDirs.vala
index 74b045f1..66b1c938 100644
--- a/src/AppDirs.vala
+++ b/src/AppDirs.vala
@@ -210,7 +210,7 @@ class AppDirs {
         
         return tmp_dir;
     }
-    
+
     public static File get_data_subdir(string name, string? subname = null) {
         File subdir = get_data_dir().get_child(name);
         if (subname != null)
@@ -338,7 +338,7 @@ class AppDirs {
         }
         return f;
     }
-    
+
     public static File get_haarcascade_file() {
         File f = 
File.new_for_path(AppDirs.get_exec_dir().get_parent().get_parent().get_child("facedetect").get_child("facedetect-haarcascade.xml").get_path());
         if (f.query_exists()) {//testing meson builddir
@@ -346,6 +346,10 @@ class AppDirs {
         }
         return get_resources_dir().get_child("facedetect-haarcascade.xml");
     }
+
+    public static File get_openface_dnn_dir() {
+        return get_data_subdir("data"); //get_child("openface.nn4.small2.v1.t7");
+    }
 #endif
 
 }
diff --git a/src/AppWindow.vala b/src/AppWindow.vala
index 0374e2ee..ad3adf74 100644
--- a/src/AppWindow.vala
+++ b/src/AppWindow.vala
@@ -566,7 +566,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 6924f826..014f11dd 100644
--- a/src/Commands.vala
+++ b/src/Commands.vala
@@ -2542,7 +2542,8 @@ public class RemoveFacesFromPhotosCommand : SimpleProxyableCommand {
         
         face.attach_many(map_source_geometry.keys);
         foreach (Gee.Map.Entry<MediaSource, string> entry in map_source_geometry.entries)
-            FaceLocation.create(face.get_face_id(), ((Photo) entry.key).get_photo_id(), entry.value);
+            FaceLocation.create(face.get_face_id(), ((Photo) entry.key).get_photo_id(),
+                                                    { entry.value, null });
     }
     
     private void on_source_destroyed(DataSource source) {
@@ -2573,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);
@@ -2608,7 +2629,8 @@ public class DeleteFaceCommand : SimpleProxyableCommand {
                 Face face = (Face) source;
                 
                 face.attach(photo);
-                FaceLocation.create(face.get_face_id(), entry.key, entry.value);
+                FaceLocation.create(face.get_face_id(), entry.key,
+                                                        { entry.value, null });
             }
         }
     }
@@ -2618,10 +2640,10 @@ public class ModifyFacesCommand : SingleDataSourceCommand {
     private MediaSource media;
     private Gee.ArrayList<SourceProxy> to_add = new Gee.ArrayList<SourceProxy>();
     private Gee.ArrayList<SourceProxy> to_remove = new Gee.ArrayList<SourceProxy>();
-    private Gee.Map<SourceProxy, string> to_update = new Gee.HashMap<SourceProxy, string>();
-    private Gee.Map<SourceProxy, string> geometries = new Gee.HashMap<SourceProxy, string>();
+    private Gee.Map<SourceProxy, FaceLocationData?> to_update = new Gee.HashMap<SourceProxy, 
FaceLocationData?>();
+    private Gee.Map<SourceProxy, FaceLocationData?> geometries = new Gee.HashMap<SourceProxy, 
FaceLocationData?>();
     
-    public ModifyFacesCommand(MediaSource media, Gee.Map<Face, string> new_face_list) {
+    public ModifyFacesCommand(MediaSource media, Gee.Map<Face, FaceLocationData?> new_face_list) {
         base (media, Resources.MODIFY_FACES_LABEL, "");
         
         this.media = media;
@@ -2640,13 +2662,13 @@ public class ModifyFacesCommand : SingleDataSourceCommand {
                         FaceLocation.get_face_location(face.get_face_id(), ((Photo) media).get_photo_id());
                     assert(face_location != null);
                     
-                    geometries.set(proxy, face_location.get_serialized_geometry());
+                    geometries.set(proxy, face_location.get_face_data());
                 }
             }
         }
         
         // Add any face that's in the new list but not the original
-        foreach (Gee.Map.Entry<Face, string> entry in new_face_list.entries) {
+        foreach (Gee.Map.Entry<Face, FaceLocationData?> entry in new_face_list.entries) {
             if (original_faces == null || !original_faces.contains(entry.key)) {
                 SourceProxy proxy = entry.key.get_proxy();
                 
@@ -2662,13 +2684,13 @@ public class ModifyFacesCommand : SingleDataSourceCommand {
                 assert(face_location != null);
                 
                 string old_geometry = face_location.get_serialized_geometry();
-                if (old_geometry != entry.value) {
+                if (old_geometry != entry.value.geometry) {
                     SourceProxy proxy = entry.key.get_proxy();
                     
                     to_update.set(proxy, entry.value);
                     proxy.broken.connect(on_proxy_broken);
                     
-                    geometries.set(proxy, old_geometry);
+                    geometries.set(proxy, face_location.get_face_data());
                 }
             }
         }
@@ -2695,7 +2717,7 @@ public class ModifyFacesCommand : SingleDataSourceCommand {
         foreach (SourceProxy proxy in to_remove)
             ((Face) proxy.get_source()).detach(media);
         
-        foreach (Gee.Map.Entry<SourceProxy, string> entry in to_update.entries) {
+        foreach (Gee.Map.Entry<SourceProxy, FaceLocationData?> entry in to_update.entries) {
             Face face = (Face) entry.key.get_source();
             FaceLocation.create(face.get_face_id(), ((Photo) media).get_photo_id(), entry.value);
         }
diff --git a/src/Photo.vala b/src/Photo.vala
index ae0f489c..a858ae31 100644
--- a/src/Photo.vala
+++ b/src/Photo.vala
@@ -5222,7 +5222,7 @@ public class LibraryPhoto : Photo, Flaggable, Monitorable {
                 if (location != null) {
                     face.attach(dupe);
                     FaceLocation.create(face.get_face_id(), dupe.get_photo_id(), 
-                        location.get_serialized_geometry());
+                        location.get_face_data());
                 }
              }
         }
diff --git a/src/Resources.vala b/src/Resources.vala
index 62b9a9d8..4c8deff6 100644
--- a/src/Resources.vala
+++ b/src/Resources.vala
@@ -411,6 +411,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/DatabaseTable.vala b/src/db/DatabaseTable.vala
index 5ec5be12..293d7546 100644
--- a/src/db/DatabaseTable.vala
+++ b/src/db/DatabaseTable.vala
@@ -21,7 +21,7 @@ public abstract class DatabaseTable {
      * tables are created on demand and tables and columns are easily ignored when already present.
      * However, the change should be noted in upgrade_database() as a comment.
      ***/
-    public const int SCHEMA_VERSION = 20;
+    public const int SCHEMA_VERSION = 21;
     
     protected static Sqlite.Database db;
     
diff --git a/src/db/Db.vala b/src/db/Db.vala
index 3eca8cee..ac24f113 100644
--- a/src/db/Db.vala
+++ b/src/db/Db.vala
@@ -349,6 +349,32 @@ private VerifyResult upgrade_database(int input_version) {
     //
     
     version = 20;
+
+#if ENABLE_FACES
+    //
+    // Version 21:
+    // * Added face pixels column to FaceLocationTable
+    // * Added face vector column to FaceTable
+    //
+    
+    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("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;
+    }
+    
+    version = 21;
+#endif
     
     //
     // Finalize the upgrade process
diff --git a/src/db/FaceLocationTable.vala b/src/db/FaceLocationTable.vala
index 14fef4c7..991786ad 100644
--- a/src/db/FaceLocationTable.vala
+++ b/src/db/FaceLocationTable.vala
@@ -29,6 +29,7 @@ public class FaceLocationRow {
     public FaceID face_id;
     public PhotoID photo_id;
     public string geometry;
+    public string vec;
 }
 
 public class FaceLocationTable : DatabaseTable {
@@ -44,7 +45,9 @@ public class FaceLocationTable : DatabaseTable {
             + "id INTEGER NOT NULL PRIMARY KEY, "
             + "face_id INTEGER NOT NULL, "
             + "photo_id INTEGER NOT NULL, "
-            + "geometry TEXT"
+            + "geometry TEXT, "
+            + "vec TEXT, "
+            + "guess INTEGER DEFAULT 0"
             + ")", -1, out stmt);
         assert(res == Sqlite.OK);
         
@@ -60,10 +63,10 @@ public class FaceLocationTable : DatabaseTable {
         return instance;
     }
  
-    public FaceLocationRow add(FaceID face_id, PhotoID photo_id, string geometry) 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) VALUES (?, ?, ?)",
+            "INSERT INTO FaceLocationTable (face_id, photo_id, geometry, vec) VALUES (?, ?, ?, ?)",
              -1, out stmt);
         assert(res == Sqlite.OK);
         
@@ -73,6 +76,9 @@ public class FaceLocationTable : DatabaseTable {
         assert(res == Sqlite.OK);
         res = stmt.bind_text(3, geometry);
         assert(res == Sqlite.OK);
+       if (vec == null) vec = "";
+        res = stmt.bind_text(4, vec);
+        assert(res == Sqlite.OK);
         
         res = stmt.step();
         if (res != Sqlite.DONE)
@@ -83,6 +89,7 @@ public class FaceLocationTable : DatabaseTable {
         row.face_id = face_id;
         row.photo_id = photo_id;
         row.geometry = geometry;
+        row.vec = vec;
         
         return row;
     }
@@ -90,7 +97,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);
         
@@ -109,6 +116,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);
         }
@@ -197,6 +205,66 @@ public class FaceLocationTable : DatabaseTable {
         if (res != Sqlite.DONE)
             throw_error("FaceLocationTable.update_face_location_serialized_geometry", res);
     }
+
+    public void update_face_location_face_data(FaceLocation face_location)
+        throws DatabaseError {
+        Sqlite.Statement 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_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);
+        
+        res = stmt.step();
+        if (res != Sqlite.DONE)
+            throw_error("FaceLocationTable.update_face_location_serialized_geometry", res);
+    }
+
+    public Gee.List<FaceLocationRow?> get_face_ref_vecs(Gee.List<FaceRow?> face_rows)
+        throws DatabaseError {
+        Sqlite.Statement stmt;
+
+        string[] where_in = {};
+        foreach (var r in face_rows) {
+            if (r != null) where_in += "?";
+        }
+        int res = db.prepare_v2(
+            "SELECT id, face_id, photo_id, geometry, vec FROM FaceLocationTable WHERE photo_id IN (%s)"
+                    .printf(string.joinv(",", where_in)),
+            -1, out stmt);
+        assert(res == Sqlite.OK);
+        int c = 1;
+        foreach (var r in face_rows) {
+            if (r != null) { 
+                res = stmt.bind_int64(c, r.ref.id);
+                assert(res == Sqlite.OK);
+            }
+            c++;
+        }
+        
+        Gee.List<FaceLocationRow?> rows = new Gee.ArrayList<FaceLocationRow?>();
+        for (;;) {
+            res = stmt.step();
+            if (res == Sqlite.DONE)
+                break;
+            else if (res != Sqlite.ROW)
+                throw_error("FaceLocationTable.get_face_ref_vecs", res);
+            
+            FaceLocationRow row = new FaceLocationRow();
+            row.face_location_id = FaceLocationID(stmt.column_int64(0));
+            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);
+        }
+        return rows;
+    }
 }
 
 #endif
diff --git a/src/db/FaceTable.vala b/src/db/FaceTable.vala
index a6e0bad6..be53515f 100644
--- a/src/db/FaceTable.vala
+++ b/src/db/FaceTable.vala
@@ -27,6 +27,8 @@ public class FaceRow {
     public FaceID face_id;
     public string name;
     public time_t time_created;
+    public PhotoID ref;
+    public string vec;
 }
 
 public class FaceTable : DatabaseTable {
@@ -41,7 +43,8 @@ public class FaceTable : DatabaseTable {
             + "("
             + "id INTEGER NOT NULL PRIMARY KEY, "
             + "name TEXT NOT NULL, "
-            + "time_created TIMESTAMP"
+            + "time_created TIMESTAMP, "
+            + "ref INTEGER DEFAULT -1"
             + ")", -1, out stmt);
         assert(res == Sqlite.OK);
         
@@ -165,5 +168,48 @@ 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);
+    }
+
+    public Gee.List<FaceRow?> get_ref_rows() throws DatabaseError {
+        Sqlite.Statement stmt;
+        int res = db.prepare_v2("SELECT id, name, time_created, ref FROM FaceTable WHERE ref != -1", -1,
+            out stmt);
+        assert(res == Sqlite.OK);
+        
+        Gee.List<FaceRow?> rows = new Gee.ArrayList<FaceRow?>();
+        
+        for (;;) {
+            res = stmt.step();
+            if (res == Sqlite.DONE)
+                break;
+            else if (res != Sqlite.ROW)
+                throw_error("FaceTable.get_all_rows", res);
+            
+            // res == Sqlite.ROW
+            FaceRow row = new FaceRow();
+            row.face_id = FaceID(stmt.column_int64(0));
+            row.name = stmt.column_text(1);
+            row.time_created = (time_t) stmt.column_int64(2);
+            row.ref = PhotoID(stmt.column_int64(3));
+            
+            rows.add(row);
+        }
+        
+        return rows;
+    }
 }
 #endif
diff --git a/src/faces/Face.vala b/src/faces/Face.vala
index 9be33c9d..338c98e7 100644
--- a/src/faces/Face.vala
+++ b/src/faces/Face.vala
@@ -346,9 +346,18 @@ public class Face : DataSource, ContainerSource, Proxyable, Indexable {
         // add them all at once to the SourceCollection
         global.add_many(faces);
         global.init_add_many_unlinked(unlinked);
+
+#if ENABLE_FACES       
+        // Start the face detection background process
+        // FaceTool talks to it over DBus
+        start_facedetect_process();
+#endif
     }
     
     public static void terminate() {
+        try {
+            FaceDetect.interface.terminate();
+        } catch(Error e) {}
     }
     
     public static int compare_names(void *a, void *b) {
@@ -366,6 +375,14 @@ public class Face : DataSource, ContainerSource, Proxyable, Indexable {
     public static bool equal_name_strings(void *a, void *b) {
         return String.collated_equals(a, b);
     }
+
+#if ENABLE_FACES       
+    private static void start_facedetect_process() {
+        message("Launching facedetect process: %s", AppDirs.get_facedetect_bin().get_path());
+        // Start the watcher, process started via DBus service
+        FaceDetect.init(AppDirs.get_openface_dnn_dir().get_path());
+    }
+#endif
     
     // Returns a Face for the name, creating a new empty one if it does not already exist.
     // name should have already been prepared by prep_face_name.
@@ -388,7 +405,7 @@ public class Face : DataSource, ContainerSource, Proxyable, Indexable {
         
         return face;
     }
-    
+
     // Utility function to cleanup a face name that comes from user input and prepare it for use
     // in the system and storage in the database.  Returns null if the name is unacceptable.
     public static string? prep_face_name(string name) {
@@ -575,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
new file mode 100644
index 00000000..618d3a54
--- /dev/null
+++ b/src/faces/FaceDetect.vala
@@ -0,0 +1,93 @@
+/**
+ * Face detection and recognition functions
+ * Copyright 2018 Narendra A (narendra_m_a(at)yahoo(dot)com)
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+// DBus interface definition
+public struct FaceRect {
+    public double x;
+    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, bool infer)
+        throws IOError, DBusError;
+    public abstract bool load_net(string netFile)
+        throws IOError, DBusError;
+    public abstract double[] face_to_vec(string inputName)
+        throws IOError, DBusError;
+    public abstract void terminate() throws IOError, DBusError;
+}
+
+// Class to communicate with facedetect process over DBus
+public class FaceDetect {
+    public const string DBUS_NAME = "org.gnome.Shotwell.Faces1";
+    public const string DBUS_PATH = "/org/gnome/shotwell/faces";
+    public static bool connected = false;
+    public static string net_file;
+    public const string ERROR_MESSAGE = "Unable to connect to facedetect service";
+    
+    public static FaceDetectInterface interface;
+
+    public static void create_interface(DBusConnection connection, string bus_name, string owner) {
+        if (bus_name == DBUS_NAME) {
+            message("Dbus name %s available", bus_name);
+        }
+    }
+
+    public static void interface_gone(DBusConnection connection, string bus_name) {
+        message("Dbus name %s gone", bus_name);
+        connected = false;
+    }
+    
+    public static void init(string net_file) {
+        FaceDetect.net_file = net_file;
+        Bus.watch_name(BusType.SESSION, DBUS_NAME, BusNameWatcherFlags.NONE,
+                       create_interface, interface_gone);
+        try {
+            // Service file should automatically run the facedetect binary
+            interface = Bus.get_proxy_sync (BusType.SESSION, DBUS_NAME, DBUS_PATH);
+            interface.load_net(net_file);
+        } catch(IOError e) {
+            AppWindow.error_message(ERROR_MESSAGE);
+        } catch(DBusError e) {
+            AppWindow.error_message(ERROR_MESSAGE);
+        }
+        connected = true;
+    }
+
+    public static double dot_product(double[] vec1, double[] vec2) {
+        if (vec1.length != vec2.length) {
+            return 0;
+        }
+        double ret = 0;
+        for (var i = 0; i < vec1.length; i++) {
+            ret += vec1[i] * vec2[i];
+        }
+        return ret;
+    }
+}
diff --git a/src/faces/FaceLocation.vala b/src/faces/FaceLocation.vala
index cc5c4cf3..48abd2d4 100644
--- a/src/faces/FaceLocation.vala
+++ b/src/faces/FaceLocation.vala
@@ -6,6 +6,12 @@
 
 #if ENABLE_FACES
 
+// Encapsulate geometry and pixels of a Face
+public struct FaceLocationData {
+    public string geometry;
+    public string vec;
+}
+
 public class FaceLocation : Object {
     
     private static Gee.Map<FaceID?, Gee.Map<PhotoID?, FaceLocation>> face_photos_map;
@@ -14,17 +20,17 @@ public class FaceLocation : Object {
     private FaceLocationID face_location_id;
     private FaceID face_id;
     private PhotoID photo_id;
-    private string geometry;
-    
+    private FaceLocationData face_data;
+
     private FaceLocation(FaceLocationID face_location_id, FaceID face_id, PhotoID photo_id,
-    string geometry) {
+            FaceLocationData face_data) {
         this.face_location_id = face_location_id;
         this.face_id = face_id;
         this.photo_id = photo_id;
-        this.geometry = geometry;
+        this.face_data = face_data;
     }
     
-    public static FaceLocation create(FaceID face_id, PhotoID photo_id, string geometry) {
+    public static FaceLocation create(FaceID face_id, PhotoID photo_id, FaceLocationData face_data) {
         FaceLocation face_location = null;
         
         // Test if that FaceLocation already exists (that face in that photo) ...
@@ -35,12 +41,11 @@ public class FaceLocation : Object {
             
             face_location = faces_map.get(face_id);
             
-            if (face_location.get_serialized_geometry() != geometry) {
-                face_location.set_serialized_geometry(geometry);
+            if (face_location.get_serialized_geometry() != face_data.geometry) {
+                face_location.set_face_data(face_data);
                 
                 try {
-                    FaceLocationTable.get_instance().update_face_location_serialized_geometry(
-                        face_location);
+                    FaceLocationTable.get_instance().update_face_location_face_data(face_location);
                 } catch (DatabaseError err) {
                     AppWindow.database_error(err);
                 }
@@ -53,7 +58,7 @@ public class FaceLocation : Object {
         try {
             face_location =
                 FaceLocation.add_from_row(
-                    FaceLocationTable.get_instance().add(face_id, photo_id, geometry));
+                    FaceLocationTable.get_instance().add(face_id, photo_id, face_data.geometry, 
face_data.vec));
         } catch (DatabaseError err) {
             AppWindow.database_error(err);
         }
@@ -86,7 +91,8 @@ public class FaceLocation : Object {
     public static FaceLocation add_from_row(FaceLocationRow row) {
         
         FaceLocation face_location =
-            new FaceLocation(row.face_location_id, row.face_id, row.photo_id, row.geometry);
+            new FaceLocation(row.face_location_id, row.face_id, row.photo_id,
+                { 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>
@@ -198,11 +204,23 @@ public class FaceLocation : Object {
     }
     
     public string get_serialized_geometry() {
-        return geometry;
+        return face_data.geometry;
+    }
+
+    public string get_serialized_vec() {
+        return face_data.vec;
+    }
+
+    public FaceLocationData get_face_data() {
+        return face_data;
+    }
+
+    public PhotoID get_photo_id() {
+        return photo_id;
     }
     
-    private void set_serialized_geometry(string geometry) {
-        this.geometry = geometry;
+    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..31baa6b8 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", "");
         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/FaceShape.vala b/src/faces/FaceShape.vala
index 089c051a..a5e76e86 100644
--- a/src/faces/FaceShape.vala
+++ b/src/faces/FaceShape.vala
@@ -20,14 +20,16 @@ public abstract class FaceShape : Object {
     protected Gdk.CursorType current_cursor_type = Gdk.CursorType.BOTTOM_RIGHT_CORNER;
     protected EditingTools.PhotoCanvas canvas;
     protected string serialized = null;
+    protected double[] face_vec;
     
     private bool editable = true;
     private bool visible = true;
     private bool known = true;
+    private double guess = 0.0;
     
     private weak FacesTool.FaceWidget face_widget = null;
     
-    public FaceShape(EditingTools.PhotoCanvas canvas) {
+    public FaceShape(EditingTools.PhotoCanvas canvas, double[] vec) {
         this.canvas = canvas;
         this.canvas.new_surface.connect(prepare_ctx);
         
@@ -39,6 +41,7 @@ public abstract class FaceShape : Object {
         face_window.show_all();
         face_window.hide();
         
+        this.face_vec = vec;
         this.canvas.set_cursor(current_cursor_type);
     }
     
@@ -90,7 +93,15 @@ public abstract class FaceShape : Object {
     public bool get_known() {
         return known;
     }
+
+    public void set_guess(double guess) {
+        this.guess = guess;
+    }
     
+    public double get_guess() {
+        return guess;
+    }
+
     public void set_widget(FacesTool.FaceWidget face_widget) {
         this.face_widget = face_widget;
     }
@@ -162,7 +173,7 @@ public abstract class FaceShape : Object {
         return true;
     }
     
-    public abstract string serialize();
+    public abstract string serialize(bool geometry_only = false);
     public abstract void update_face_window_position();
     public abstract void prepare_ctx(Cairo.Context ctx, Dimensions dim);
     public abstract void on_resized_pixbuf(Dimensions old_dim, Gdk.Pixbuf scaled);
@@ -172,6 +183,7 @@ public abstract class FaceShape : Object {
     public abstract bool cursor_is_over(int x, int y);
     public abstract bool equals(FaceShape face_shape);
     public abstract double get_distance(int x, int y);
+    public abstract double[] get_face_vec();
     
     protected abstract void paint();
     protected abstract void erase();
@@ -193,8 +205,13 @@ public class FaceRectangle : FaceShape {
     private int last_grab_y = -1;
     
     public FaceRectangle(EditingTools.PhotoCanvas canvas, int x, int y,
-        int half_width = NULL_SIZE, int half_height = NULL_SIZE) {
-        base(canvas);
+        int half_width = NULL_SIZE, int half_height = NULL_SIZE, double[] vec = {}) {
+       double[] int_vec;
+       if (vec.length == 0)
+          int_vec = create_empty_vec();
+       else
+          int_vec = vec;
+        base(canvas, int_vec);
         
         Gdk.Rectangle scaled_pixbuf_pos = canvas.get_scaled_pixbuf_position();
         x -= scaled_pixbuf_pos.x;
@@ -221,6 +238,14 @@ public class FaceRectangle : FaceShape {
         if (!is_editable())
             erase_label();
     }
+
+    public static double[] create_empty_vec() {
+        double[] empty_vec = new double[128];
+        for (int i = 0; i < 128; i++) {
+            empty_vec[i] = 0;
+       }
+       return empty_vec;
+    }
     
     public static new FaceRectangle from_serialized(EditingTools.PhotoCanvas canvas, string[] args)
         throws FaceShapeError {
@@ -228,7 +253,6 @@ public class FaceRectangle : FaceShape {
         
         Photo photo = canvas.get_photo();
         Dimensions raw_dim = photo.get_raw_dimensions();
-        
         int x = (int) (raw_dim.width * double.parse(args[1]));
         int y = (int) (raw_dim.height * double.parse(args[2]));
         int half_width = (int) (raw_dim.width * double.parse(args[3]));
@@ -267,9 +291,21 @@ public class FaceRectangle : FaceShape {
         
         if (half_width < FACE_MIN_SIZE || half_height < FACE_MIN_SIZE)
             throw new FaceShapeError.CANT_CREATE("FaceShape is out of cropped photo area");
-        
+
+        string[] vec_str;
+        if (args.length == 6)
+            vec_str = args[5].split(",");
+        else
+            vec_str = {};
+        double[] vec = new double[128];
+        for (int i = 0; i < 128; i++) {
+            if (vec_str.length > i)
+                vec[i] = double.parse(vec_str[i]);
+            else
+                vec[i] = 0;
+        }
         return new FaceRectangle(canvas, box.left + half_width, box.top + half_height,
-            half_width, half_height);
+            half_width, half_height, vec);
     }
     
     public override void update_face_window_position() {
@@ -370,7 +406,7 @@ public class FaceRectangle : FaceShape {
         ctx.restore();
     }
     
-    public override string serialize() {
+    public override string serialize(bool geometry_only = false) {
         if (serialized != null)
             return serialized;
         
@@ -380,10 +416,15 @@ public class FaceRectangle : FaceShape {
         double half_height;
         
         get_geometry(out x, out y, out half_width, out half_height);
-        
-        serialized = "%s;%s;%s;%s;%s".printf(SHAPE_TYPE, x.to_string(),
+        serialized = "%s;%s;%s;%s;%s;".printf(SHAPE_TYPE, x.to_string(),
             y.to_string(), half_width.to_string(), half_height.to_string());
-        
+        if (!geometry_only) {
+            string face_vec_str = "";
+            foreach (var d in face_vec[0:-2])
+                face_vec_str += d.to_string() + ",";
+            face_vec_str += face_vec[-1].to_string();
+            serialized += face_vec_str;
+        }
         return serialized;
     }
     
@@ -427,9 +468,13 @@ public class FaceRectangle : FaceShape {
         half_width = (width_right_end - width_left_end) / 2;
         half_height = (height_bottom_end - height_top_end) / 2;
     }
+
+    public override double[] get_face_vec() {
+        return face_vec;
+    }
     
     public override bool equals(FaceShape face_shape) {
-        return serialize() == face_shape.serialize();
+        return serialize(true) == face_shape.serialize(true);
     }
     
     public override void prepare_ctx(Cairo.Context ctx, Dimensions dim) {
diff --git a/src/faces/FacesTool.vala b/src/faces/FacesTool.vala
index 050ac85a..a5d6ac00 100644
--- a/src/faces/FacesTool.vala
+++ b/src/faces/FacesTool.vala
@@ -314,117 +314,47 @@ public class FacesTool : EditingTools.EditingTool {
     private class FaceDetectionJob : BackgroundJob {
         private Gee.Queue<string> faces = null;
         private string image_path;
-        private string output;
-        public SpawnError? spawnError;
+        private float scale;
+        public string? spawnError;
 
-        public FaceDetectionJob(FacesToolWindow owner, string image_path,
+        public FaceDetectionJob(FacesToolWindow owner, string image_path, float scale,
             CompletionCallback completion_callback, Cancellable cancellable,
             CancellationCallback cancellation_callback) {
             base(owner, completion_callback, cancellable, cancellation_callback);
 
             this.image_path = image_path;
+            this.scale = scale;
         }
 
         public override void execute() {
+            if (!FaceDetect.connected) {
+                spawnError = "Face detect process not connected!\n";
+                return;
+            }
+            FaceRect[] rects;
             try {
-                string[] argv = {
-                    AppDirs.get_facedetect_bin().get_path(),
-                    "--cascade=" + AppDirs.get_haarcascade_file().get_path(),
-                    "--scale=1.2",
-                    image_path
-                };
-                Process.spawn_sync(null, argv, null, SpawnFlags.STDERR_TO_DEV_NULL, null, out output);
-
-            } catch (SpawnError e) {
-                spawnError = e;
-                critical(e.message);
-
+                rects = FaceDetect.interface.detect_faces(image_path,
+                                                          AppDirs.get_haarcascade_file().get_path(), scale, 
true);
+            } catch(Error e) {
+                spawnError = "DBus error: " + e.message + "!\n";
                 return;
             }
-
             faces = new Gee.PriorityQueue<string>();
-            string[] lines = output.split("\n");
-            foreach (string line in lines) {
-                if (line.length == 0)
-                    continue;
-
-                string[] type_and_serialized = line.split(";");
-                if (type_and_serialized.length != 2) {
-                    critical("Wrong serialized line in face detection program output.");
-                    assert_not_reached();
-                }
-
-                switch (type_and_serialized[0]) {
-                    case "face":
-                        StringBuilder serialized_geometry = new StringBuilder();
-                        serialized_geometry.append(FaceRectangle.SHAPE_TYPE);
-                        serialized_geometry.append(";");
-                        serialized_geometry.append(parse_serialized_geometry(type_and_serialized[1]));
-
-                        faces.add(serialized_geometry.str);
-                        break;
-
-                    case "warning":
-                        warning("%s\n", type_and_serialized[1]);
-                        break;
-
-                    case "error":
-                        critical("%s\n", type_and_serialized[1]);
-                        assert_not_reached();
-
-                    default:
-                        assert_not_reached();
-                }
-            }
-        }
-
-        private string parse_serialized_geometry(string serialized_geometry) {
-            string[] serialized_geometry_pieces = serialized_geometry.split("&");
-            if (serialized_geometry_pieces.length != 4) {
-                critical("Wrong serialized line in face detection program output.");
-                assert_not_reached();
-            }
-
-            double x = 0;
-            double y = 0;
-            double width = 0;
-            double height = 0;
-            foreach (string piece in serialized_geometry_pieces) {
-
-                string[] name_and_value = piece.split("=");
-                if (name_and_value.length != 2) {
-                    critical("Wrong serialized line in face detection program output.");
-                    assert_not_reached();
-                }
-
-                switch (name_and_value[0]) {
-                    case "x":
-                        x = name_and_value[1].to_double();
-                        break;
-
-                    case "y":
-                        y = name_and_value[1].to_double();
-                        break;
-
-                    case "width":
-                        width = name_and_value[1].to_double();
-                        break;
-
-                    case "height":
-                        height = name_and_value[1].to_double();
-                        break;
-
-                    default:
-                        critical("Wrong serialized line in face detection program output.");
-                        assert_not_reached();
+            for (int i = 0; i < rects.length; i++) {
+                double rect_x, rect_y, rect_w, rect_h;
+                string face_vec_str = "";
+                rect_w = rects[i].width / 2;
+                rect_h = rects[i].height / 2;
+                rect_x = rects[i].x + rect_w;
+                rect_y = rects[i].y + rect_h;
+                if (rects[i].vec != null) {
+                    foreach (var d in rects[i].vec) { face_vec_str += d.to_string() + ","; }
                 }
+                string serialized = "%s;%f;%f;%f;%f;%s".printf(FaceRectangle.SHAPE_TYPE,
+                                                                                rect_x, rect_y, rect_w, 
rect_h,
+                                                                                face_vec_str);
+                faces.add(serialized);
             }
-
-            double half_width = width / 2;
-            double half_height = height / 2;
-
-            return "%s;%s;%s;%s".printf((x + half_width).to_string(), (y + half_height).to_string(),
-                half_width.to_string(), half_height.to_string());
         }
 
         public string? get_next() {
@@ -447,6 +377,7 @@ public class FacesTool : EditingTools.EditingTool {
     private Workers workers;
     private FaceShape editing_face_shape = null;
     private FacesToolWindow faces_tool_window = null;
+    private const int FACE_DETECT_MAX_WIDTH = 1200;
 
     private FacesTool() {
         base("FacesTool");
@@ -478,8 +409,10 @@ public class FacesTool : EditingTools.EditingTool {
             foreach (Gee.Map.Entry<FaceID?, FaceLocation> entry in face_locations.entries) {
                 FaceShape new_face_shape;
                 string serialized_geometry = entry.value.get_serialized_geometry();
+                string serialized_vec = entry.value.get_serialized_vec();
+                string face_shape_str = serialized_geometry + ";" + serialized_vec;
                 try {
-                    new_face_shape = FaceShape.from_serialized(canvas, serialized_geometry);
+                    new_face_shape = FaceShape.from_serialized(canvas, face_shape_str);
                 } catch (FaceShapeError e) {
                     if (e is FaceShapeError.CANT_CREATE)
                         continue;
@@ -499,9 +432,12 @@ public class FacesTool : EditingTools.EditingTool {
 
         face_detection_cancellable = new Cancellable();
         workers = new Workers(1, false);
+        Dimensions dimensions = canvas.get_photo().get_dimensions();
+        float scale_factor = (float)dimensions.width / FACE_DETECT_MAX_WIDTH;
         face_detection = new FaceDetectionJob(faces_tool_window,
-            canvas.get_photo().get_file().get_path(), on_faces_detected,
-            face_detection_cancellable, on_detection_cancelled);
+                                              canvas.get_photo().get_file().get_path(), scale_factor,
+                                              on_faces_detected,
+                                              face_detection_cancellable, on_detection_cancelled);
 
         bind_window_handlers();
 
@@ -781,14 +717,21 @@ public class FacesTool : EditingTools.EditingTool {
         if (face_shapes == null)
             return;
 
-        Gee.Map<Face, string> new_faces = new Gee.HashMap<Face, string>();
+        Gee.Map<Face, FaceLocationData?> new_faces = new Gee.HashMap<Face, FaceLocationData?>();
         foreach (FaceShape face_shape in face_shapes.values) {
             if (!face_shape.get_known())
                 continue;
 
             Face new_face = Face.for_name(face_shape.get_name());
-
-            new_faces.set(new_face, face_shape.serialize());
+            string[] face_string = face_shape.serialize().split(";");
+            string face_vec_str, face_geometry;
+            face_geometry = string.joinv(";", face_string[0:5]);
+            face_vec_str = face_string[5];
+            FaceLocationData face_data =
+                {
+                 face_geometry, face_vec_str
+                };
+            new_faces.set(new_face, face_data);
         }
 
         ModifyFacesCommand command = new ModifyFacesCommand(canvas.get_photo(), new_faces);
@@ -905,7 +848,6 @@ public class FacesTool : EditingTools.EditingTool {
     private void detect_faces() {
         faces_tool_window.detection_button.set_sensitive(false);
         faces_tool_window.set_editing_phase(EditingPhase.DETECTING_FACES);
-
         workers.enqueue(face_detection);
     }
 
@@ -942,19 +884,59 @@ public class FacesTool : EditingTools.EditingTool {
                 continue;
 
             c++;
+            // Reference faces to match with
+            Face? guess = get_face_match(face_shape, 0.7);
 
-            face_shape.set_name("Unknown face #%d".printf(c));
-            face_shape.set_known(false);
+            if (guess == null) {
+                face_shape.set_name("Unknown face #%d".printf(c));
+                face_shape.set_known(false);
+            } else {
+                string name_str;
+                name_str = "%s (%0.2f%%)".printf(guess.get_name(), face_shape.get_guess() * 100);
+                face_shape.set_name(name_str);
+                face_shape.set_known(true);
+            }
             add_face(face_shape);
         }
     }
 
+    private Face? get_face_match(FaceShape face_shape, double threshold) {
+        Gee.List<FaceLocationRow?> face_vecs;
+        try {
+            Gee.List<FaceRow?> face_rows = FaceTable.get_instance().get_ref_rows();
+            face_vecs = FaceLocationTable.get_instance().get_face_ref_vecs(face_rows);
+        } catch(DatabaseError err) {
+            warning("Cannot get reference faces from DB");
+            return null;
+        }
+        FaceID? guess_id = null;
+        double max_product = threshold;
+        foreach (var row in face_vecs) {
+            string[] vec_str = row.vec.split(",");
+            double[] vec = {};
+            foreach (var d in vec_str) vec += double.parse(d);
+            double product = FaceDetect.dot_product(face_shape.get_face_vec(), vec[0:128]);
+            if (product > max_product) {
+                max_product = product;
+                guess_id = row.face_id;
+            }
+        }
+
+        Face? face = null;
+        if (guess_id != null) {
+            face = Face.global.fetch(guess_id);
+            face_shape.set_guess(max_product);
+            assert(face != null);
+        }
+        return face;
+    }
+    
     private void on_faces_detected() {
         face_detection_cancellable.reset();
         
         if (face_detection.spawnError != null){
             string spawnErrorMessage = _("Error trying to spawn face detection program:\n");
-            AppWindow.error_message(spawnErrorMessage + face_detection.spawnError.message + "\n");
+            AppWindow.error_message(spawnErrorMessage + face_detection.spawnError + "\n");
             faces_tool_window.set_editing_phase(EditingPhase.DETECTING_FACES_FINISHED);
         } else
             pick_faces_from_autodetected();
diff --git a/src/meson.build b/src/meson.build
index abea7b21..b230ccf1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -17,7 +17,15 @@ processor = executable('shotwell-graphics-processor',
                        dependencies: [gio, gdk, gee],
                        link_with: sw_graphics_processor)
 
+shotwell_deps = [gio, gee, sqlite, gtk, sqlite, posix, gphoto2,
+                 gstreamer_pbu, gio_unix, gudev, gexiv2, gmodule,
+                 libraw, libexif, sw_plugin]
+
+shotwell_libs = [sw_graphics_processor]
+
 face_sources = []
+face_obj = ''
+
 if get_option('face-detection')
   face_sources = (['faces/FacesBranch.vala',
                      'faces/FaceLocation.vala',
@@ -25,6 +33,7 @@ if get_option('face-detection')
                      'faces/FaceShape.vala',
                      'faces/Faces.vala',
                      'faces/Face.vala',
+                     'faces/FaceDetect.vala',
                      'db/FaceLocationTable.vala',
                      'db/FaceTable.vala',
                      'faces/FacesTool.vala'])
diff --git a/facedetect/facedetect-haarcascade.xml 
b/subprojects/shotwell-facedetect/facedetect-haarcascade.xml
similarity index 100%
rename from facedetect/facedetect-haarcascade.xml
rename to subprojects/shotwell-facedetect/facedetect-haarcascade.xml
diff --git a/subprojects/shotwell-facedetect/facedetect-opencv.cpp 
b/subprojects/shotwell-facedetect/facedetect-opencv.cpp
new file mode 100644
index 00000000..1eff969a
--- /dev/null
+++ b/subprojects/shotwell-facedetect/facedetect-opencv.cpp
@@ -0,0 +1,175 @@
+#include "shotwell-facedetect.hpp"
+
+#include <opencv2/imgcodecs.hpp>
+
+#define OPENFACE_RECOG_TORCH_NET "openface.nn4.small2.v1.t7"
+#define RESNET_DETECT_CAFFE_NET "res10_300x300_ssd_iter_140000_fp16.caffemodel"
+
+// Detect faces in a photo
+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;
+       }
+
+       if (inputName.empty()) {
+        std::cout << "error;You must specify the file to process." << std::endl;
+       }
+
+    cv::Mat img = cv::imread(inputName, 1);
+       if (img.empty()) {
+        std::cout << "error;Could not load the file to process. Filename: \"" << inputName << "\"" << 
std::endl;
+       }
+
+    std::vector<cv::Rect> faces;
+    cv::Size smallImgSize;
+    static bool disableDnn;
+
+#ifdef HAS_OPENCV_DNN
+    disableDnn = faceDetectNet.empty();
+#else
+    disableDnn = true;
+#endif
+    if (disableDnn) {
+        // Classical face detection
+        cv::Mat gray;
+        cvtColor(img, gray, CV_BGR2GRAY);
+
+        cv::Mat smallImg(cvRound(img.rows / scale), cvRound(img.cols / scale), CV_8UC1);
+        smallImgSize = smallImg.size();
+
+        cv::resize(gray, smallImg, smallImgSize, 0, 0, cv::INTER_LINEAR);
+        cv::equalizeHist(smallImg, smallImg);
+
+        cascade.detectMultiScale(smallImg, faces, 1.1, 2, CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));
+    } else {
+#ifdef HAS_OPENCV_DNN
+        // DNN based face detection
+        faces = detectFacesMat(img);
+        smallImgSize = img.size(); // Not using the small image here
+#endif
+    }
+
+    std::vector<FaceRect> scaled;
+    for (std::vector<cv::Rect>::const_iterator r = faces.begin(); r != faces.end(); r++) {
+        FaceRect i;
+        i.x = (float) r->x / smallImgSize.width;
+        i.y = (float) r->y / smallImgSize.height;
+        i.width = (float) r->width / smallImgSize.width;
+        i.height = (float) r->height / smallImgSize.height;
+#ifdef HAS_OPENCV_DNN
+        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);
+        }
+#else
+        i.vec.assign(128, 0);
+#endif
+        scaled.push_back(i);
+    }
+
+    return scaled;
+}
+
+// Load network into global var
+bool loadNet(cv::String baseDir) {
+#ifdef HAS_OPENCV_DNN
+    try {
+        faceDetectNet = cv::dnn::readNetFromCaffe(baseDir + "/deploy.prototxt",
+                                                  baseDir + "/" + RESNET_DETECT_CAFFE_NET);
+        faceRecogNet = cv::dnn::readNetFromTorch(baseDir + "/" + OPENFACE_RECOG_TORCH_NET);
+    } catch(cv::Exception &e) {
+        std::cout << "File load failed: " << e.msg << std::endl;
+        return false;
+    }
+    if (faceRecogNet.empty() || faceDetectNet.empty()) {
+        std::cout << "Loading open-face net failed!" << std::endl;
+        return false;
+    } else {
+        return true;
+    }
+#else
+    return false;
+#endif
+}
+
+// Face detector
+// Adapted from OpenCV example:
+// https://github.com/opencv/opencv/blob/master/samples/dnn/js_face_recognition.html
+std::vector<cv::Rect> detectFacesMat(cv::Mat img) {
+    std::vector<cv::Rect> faces;
+#ifdef HAS_OPENCV_DNN
+    cv::Mat blob = cv::dnn::blobFromImage(img, 1.0, cv::Size(128*8, 96*8),
+                                          cv::Scalar(104, 177, 123, 0), false, false);
+    faceDetectNet.setInput(blob);
+    cv::Mat out = faceDetectNet.forward();
+    // out is a 4D matrix [1 x 1 x n x 7]
+    // n - number of results
+    assert(out.dims == 4);
+    int outIdx[4] = { 0, 0, 0, 0 };
+    for (int i = 0, n = out.size[2]; i < n; i++) {
+        outIdx[2] = i; outIdx[3] = 2;
+        float confidence = out.at<float>(outIdx);
+        outIdx[3]++;
+        float left = out.at<float>(outIdx) * img.cols;
+        outIdx[3]++;
+        float top = out.at<float>(outIdx) * img.rows;
+        outIdx[3]++;
+        float right = out.at<float>(outIdx) * img.cols;
+        outIdx[3]++;
+        float bottom = out.at<float>(outIdx) * img.rows;
+        left = std::min(std::max(0.0f, left), (float)img.cols - 1);
+        right = std::min(std::max(0.0f, right), (float)img.cols - 1);
+        bottom = std::min(std::max(0.0f, bottom), (float)img.rows - 1);
+        top = std::min(std::max(0.0f, top), (float)img.rows - 1);
+        if (confidence > 0.98 && left < right && top < bottom) {
+            cv::Rect rect(left, top, right - left, bottom - top);
+            faces.push_back(rect);
+        }
+    }
+#endif // HAS_OPENCV_DNN
+    return faces;
+}
+
+// Face to vector convertor
+// Adapted from OpenCV example:
+// https://github.com/opencv/opencv/blob/master/samples/dnn/js_face_recognition.html
+#ifdef HAS_OPENCV_DNN
+std::vector<double> faceToVecMat(cv::Mat img) {
+    std::vector<double> ret;
+    cv::Mat smallImg(96, 96, CV_8UC1);
+    cv::Size smallImgSize = smallImg.size();
+
+    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);
+    cv::Mat vec = faceRecogNet.forward();
+    // Return vector
+    for (int i = 0; i < vec.rows; ++i)
+        ret.insert(ret.end(), vec.ptr<float>(i), vec.ptr<float>(i) + vec.cols);
+    return ret;
+}
+#endif
+
+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;
+    }
+#ifdef HAS_OPENCV_DNN
+    ret = faceToVecMat(img);
+#else
+    ret.assign(128, 0);
+#endif
+    return ret;
+}
+
diff --git a/subprojects/shotwell-facedetect/meson.build b/subprojects/shotwell-facedetect/meson.build
new file mode 100644
index 00000000..4ece7d15
--- /dev/null
+++ b/subprojects/shotwell-facedetect/meson.build
@@ -0,0 +1,51 @@
+project('shotwell-facedetect', ['c', 'cpp'])
+gnome = import('gnome')
+facedetect_dep = dependency('opencv', version : ['>= 2.3.0'], required : true)
+cpp = meson.get_compiler('cpp')
+has_dnn = cpp.has_header('opencv2/dnn.hpp', dependencies: facedetect_dep)
+if has_dnn
+  dnn_define = declare_dependency(compile_args: '-DHAS_OPENCV_DNN')
+else
+  dnn_define = []
+endif
+
+libexecdir = join_paths(get_option('libexecdir'), 'shotwell')
+
+gio = dependency('gio-2.0', version: '>= 2.40')
+gio_unix = dependency('gio-unix-2.0', required : true)
+gdbus_src = gnome.gdbus_codegen('dbus-interface',
+  sources: 'org.gnome.ShotwellFaces1.xml',
+  interface_prefix : 'org.gnome.')
+
+con = configuration_data()
+con.set('libexecdir', join_paths(get_option('prefix'), libexecdir))
+
+if meson.is_subproject()
+    config_incdir = include_directories('../..')
+else
+    config_incdir = include_directories('.')
+    configure_file(
+        input: 'org.gnome.Shotwell.Faces1.desktop.in',
+        output: 'org.gnome.Shotwell.Faces1.desktop',
+        configuration: con,
+        install: true,
+        install_dir : join_paths(get_option('datadir'), 'applications')
+    )
+endif
+
+executable('shotwell-facedetect',
+           'shotwell-facedetect.cpp', 'facedetect-opencv.cpp', gdbus_src,
+           dependencies : [facedetect_dep, gio, gio_unix, dnn_define],
+           install : true,
+           include_directories: config_incdir,
+           install_dir : libexecdir)
+install_data('facedetect-haarcascade.xml',
+              install_dir : join_paths(get_option('datadir'), 'shotwell'))
+
+configure_file(
+    input : 'org.gnome.Shotwell.Faces1.service.in',
+    output : 'org.gnome.Shotwell.Faces1.service',
+    configuration: con,
+    install: true,
+    install_dir : join_paths(get_option('datadir'), 'dbus-1', 'services')
+    )
diff --git a/subprojects/shotwell-facedetect/org.gnome.Shotwell.Faces1.desktop.in 
b/subprojects/shotwell-facedetect/org.gnome.Shotwell.Faces1.desktop.in
new file mode 100644
index 00000000..d64714e2
--- /dev/null
+++ b/subprojects/shotwell-facedetect/org.gnome.Shotwell.Faces1.desktop.in
@@ -0,0 +1,7 @@
+[Desktop Entry]
+Version=1.0
+Name=Shotwell Face detection
+Comment=Shotwell face detection and recognition backend
+NoDisplay=true
+Type=Application
+Exec=@libexecdir@/shotwell-facedetect
diff --git a/subprojects/shotwell-facedetect/org.gnome.Shotwell.Faces1.service.in 
b/subprojects/shotwell-facedetect/org.gnome.Shotwell.Faces1.service.in
new file mode 100644
index 00000000..127d7445
--- /dev/null
+++ b/subprojects/shotwell-facedetect/org.gnome.Shotwell.Faces1.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Shotwell.Faces1
+Exec=@libexecdir@/shotwell-facedetect
\ No newline at end of file
diff --git a/subprojects/shotwell-facedetect/org.gnome.ShotwellFaces1.xml 
b/subprojects/shotwell-facedetect/org.gnome.ShotwellFaces1.xml
new file mode 100644
index 00000000..6bc84f19
--- /dev/null
+++ b/subprojects/shotwell-facedetect/org.gnome.ShotwellFaces1.xml
@@ -0,0 +1,51 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+  <!--
+      org.gnome.Shotwell.Faces1:
+      @short_description: Face detection/recognition
+  -->
+  <interface name="org.gnome.Shotwell.Faces1">
+    <!--
+        DetectFaces
+        @image: Image file to run face detection on
+        @cascade: Cascade XML file
+        @scale: Scaling to apply on image
+        Returns an array of face bounding boxes (x,y,w,h) in dimensionless units
+    -->
+    <method name="DetectFaces">
+      <arg type="s" name="image" direction="in" />
+      <arg type="s" name="cascade" direction="in" />
+      <arg type="d" name="scale" direction="in" />
+      <arg type="b" name="infer" direction="in" />
+      <arg type="a(ddddad)" name="faces" direction="out" />
+    </method>
+
+    <!--
+        LoadNet
+        @net: Torch t7 net file
+        Returns non-zero on any error
+    -->
+    <method name="LoadNet">
+      <arg type="s" name="net" direction="in" />
+      <arg type="b" name="ret" direction="out" />
+    </method>
+
+    <!--
+        FaceToVec
+        @image: Image of face to convert
+        Returns 128 element vector
+    -->
+    <method name="FaceToVec">
+      <arg type="s" name="image" direction="in" />
+      <arg type="ad" name="vec" direction="out" />
+    </method>
+
+    <!--
+        Terminate
+    -->
+    <method name="Terminate">
+    </method>
+  </interface>
+</node>
diff --git a/subprojects/shotwell-facedetect/shotwell-facedetect.cpp 
b/subprojects/shotwell-facedetect/shotwell-facedetect.cpp
new file mode 100644
index 00000000..7a6aca95
--- /dev/null
+++ b/subprojects/shotwell-facedetect/shotwell-facedetect.cpp
@@ -0,0 +1,116 @@
+/* Copyright 2016 Software Freedom Conservancy Inc.
+ *
+ * Copyright 2011 Valentín Barros Puertas <valentin(at)sanva(dot)net>
+ * Copyright 2018 Ricardo Fantin da Costa <ricardofantin(at)gmail(dot)com>
+ * Copyright 2018 Narendra A <narendra_m_a(at)yahoo(dot)com>
+ * 
+ * This software is licensed under the GNU LGPL (version 2.1 or later).
+ * See the COPYING file in this distribution.
+ */
+
+#include "shotwell-facedetect.hpp"
+#include "dbus-interface.h"
+
+// DBus binding functions
+static gboolean on_handle_detect_faces(ShotwellFaces1 *object,
+                                       GDBusMethodInvocation *invocation,
+                                       const gchar *arg_image,
+                                       const gchar *arg_cascade,
+                                       gdouble arg_scale,
+                                       gboolean arg_infer) {
+    GVariantBuilder *builder;
+    GVariant *faces;
+    std::vector<FaceRect> rects = 
+        detectFaces(arg_image, arg_cascade, arg_scale, arg_infer);
+    // Construct return value
+    builder = g_variant_builder_new(G_VARIANT_TYPE ("a(ddddad)"));
+    for (std::vector<FaceRect>::const_iterator r = rects.begin(); r != rects.end(); r++) {
+        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(ddddad)", builder);
+    g_variant_builder_unref (builder);
+    // Call return
+    shotwell_faces1_complete_detect_faces(object, invocation,
+                                          faces);
+    return TRUE;
+}
+
+static gboolean on_handle_load_net(ShotwellFaces1 *object,
+                                   GDBusMethodInvocation *invocation,
+                                   const gchar *arg_net) {
+    bool ret = loadNet(arg_net);
+    // Call return
+    shotwell_faces1_complete_load_net(object, invocation,
+                                      ret);
+    return TRUE;
+}
+
+static gboolean on_handle_face_to_vec(ShotwellFaces1 *object,
+                                      GDBusMethodInvocation *invocation,
+                                      const gchar *arg_image) {
+    GVariantBuilder *builder;
+    GVariant *ret;
+    std::vector<double> vec = faceToVec(arg_image);
+    builder = g_variant_builder_new(G_VARIANT_TYPE ("ad"));
+    for (std::vector<double>::const_iterator r = vec.begin(); r != vec.end(); r++) {
+        GVariant *v = g_variant_new("d", *r);
+        g_variant_builder_add(builder, "d", v);
+    }
+    ret = g_variant_new("ad", builder);
+    g_variant_builder_unref(builder);
+    shotwell_faces1_complete_face_to_vec(object, invocation,
+                                         ret);
+    return TRUE;
+}
+
+static gboolean on_handle_terminate(ShotwellFaces1 *object,
+                                    GDBusMethodInvocation *invocation,
+                                    gpointer user_data) {
+    g_debug("Exiting...");
+    shotwell_faces1_complete_terminate(object, invocation);
+    g_main_loop_quit(reinterpret_cast<GMainLoop *>(user_data));
+
+    return TRUE;
+}
+
+static void on_name_acquired(GDBusConnection *connection,
+                             const gchar *name, gpointer user_data) {
+    ShotwellFaces1 *interface;
+    GError *error;
+    interface = shotwell_faces1_skeleton_new();
+    g_debug("Got name %s", name);
+    g_signal_connect(interface, "handle-detect-faces", G_CALLBACK (on_handle_detect_faces), NULL);
+    g_signal_connect(interface, "handle-terminate", G_CALLBACK (on_handle_terminate), user_data);
+    g_signal_connect(interface, "handle-load-net", G_CALLBACK (on_handle_load_net), NULL);
+    g_signal_connect(interface, "handle-face-to-vec", G_CALLBACK (on_handle_face_to_vec), NULL);
+    error = NULL;
+    g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(interface), connection, 
"/org/gnome/shotwell/faces", &error);
+}
+
+static void on_name_lost(GDBusConnection *connection,
+                         const gchar *name, gpointer user_data) {
+    if (connection == NULL) {
+        g_debug("Unable to establish connection for name %s", name);
+    } else {
+        g_debug("Connection for name %s disconnected", name);
+    }
+    g_main_loop_quit((GMainLoop *)user_data);
+}
+
+int main(int argc, char **argv) {
+    GMainLoop *loop;
+    loop = g_main_loop_new (NULL, FALSE);
+       g_bus_own_name(G_BUS_TYPE_SESSION, "org.gnome.Shotwell.Faces1", G_BUS_NAME_OWNER_FLAGS_NONE, NULL,
+                   on_name_acquired, on_name_lost, loop, NULL);
+    g_main_loop_run (loop);
+    return 0;
+}
diff --git a/subprojects/shotwell-facedetect/shotwell-facedetect.hpp 
b/subprojects/shotwell-facedetect/shotwell-facedetect.hpp
new file mode 100644
index 00000000..688a1012
--- /dev/null
+++ b/subprojects/shotwell-facedetect/shotwell-facedetect.hpp
@@ -0,0 +1,36 @@
+/* 
+ * Copyright 2018 Narendra A (narendra_m_a(at)yahoo dot com)
+ * 
+ * This software is licensed under the GNU LGPL (version 2.1 or later).
+ * See the COPYING file in this distribution.
+ *
+ * Header file for facedetect/recognition routines
+ */
+
+#include <opencv2/core/core.hpp>
+#include <opencv2/objdetect/objdetect.hpp>
+#include <opencv2/imgproc/imgproc.hpp>
+#ifdef HAS_OPENCV_DNN
+#include <opencv2/dnn.hpp>
+#endif
+
+#include <iostream>
+#include <stdio.h>
+#include <algorithm>
+
+typedef struct {
+    float x, y, width, height;
+    std::vector<double> vec;
+} FaceRect;
+
+// Global variable for DNN to generate vector out of face
+#ifdef HAS_OPENCV_DNN
+static cv::dnn::Net faceRecogNet;
+static cv::dnn::Net faceDetectNet;
+#endif
+
+bool loadNet(cv::String netFile);
+std::vector<FaceRect> detectFaces(cv::String inputName, cv::String cascadeName, double scale, bool infer);
+std::vector<cv::Rect> detectFacesMat(cv::Mat img);
+std::vector<double> faceToVecMat(cv::Mat img);
+std::vector<double> faceToVec(cv::String inputName);
diff --git a/vapi/opencv.vapi b/vapi/opencv.vapi
new file mode 100644
index 00000000..b811f4a5
--- /dev/null
+++ b/vapi/opencv.vapi
@@ -0,0 +1,1116 @@
+/**
+ * OpenCV Vala Bindings
+ * Copyright 2010 Evan Nemerson <evan coeus-group com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* Status:
+ *   Core is mostly done, as is HighGUI. Auxiliary is almost
+ *   completely unbound. I'm not sure when, or if, I will have time to
+ *   finish these.
+ */
+
+[CCode (cheader_filename = "cv.h", cprefix = "Cv", lower_case_cprefix = "cv")]
+namespace OpenCV {
+       [Compact, CCode (cname = "CvArr", has_type_id = false)]
+       public class Array {
+               [CCode (cname = "cvGetCol")]
+               public OpenCV.Matrix get_col (OpenCV.Matrix submat, int col);
+               [CCode (cname = "cvGetCols")]
+               public OpenCV.Matrix get_cols (OpenCV.Matrix submat, int start_col, int end_col);
+               [CCode (cname = "cvGetDiag")]
+               public OpenCV.Matrix get_diagonal (OpenCV.Matrix submat, int diag = 0);
+               [CCode (cname = "cvGetDimSize")]
+               public int get_dimension_size (int index);
+               [CCode (cname = "cvGetDims")]
+               public int get_dimensions (int[]? sizes = null);
+               [CCode (cname = "cvGetElemType")]
+               public OpenCV.Type get_elem_type ();
+               [CCode (cname = "cvGetImage")]
+               public OpenCV.IPL.Image get_image (OpenCV.IPL.Image header);
+               [CCode (cname = "cvGetMat")]
+               public OpenCV.Matrix get_matrix (OpenCV.Matrix header, int[]? coi = null, int allowND = 0);
+               [CCode (cname = "cvGetRow")]
+               public OpenCV.Matrix get_row (OpenCV.Matrix submat, int row);
+               [CCode (cname = "cvGetRows")]
+               public OpenCV.Matrix get_rows (OpenCV.Matrix submat, int start_row, int end_Row, int 
delta_row = 1);
+               [CCode (cname = "cvGetSubRect")]
+               public OpenCV.Matrix get_subrectangle (OpenCV.Matrix submat, OpenCV.Rectangle rect);
+
+               [CCode (cname = "cvAdd")]
+               public void add (OpenCV.Array src2, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvAddS")]
+               public void add_scalar (OpenCV.Scalar value, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvSub")]
+               public void subtract (OpenCV.Array src2, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvSubS")]
+               public void subtract_scalar (OpenCV.Scalar value, OpenCV.Array dst, OpenCV.Array? mask = 
null);
+               [CCode (cname = "cvSubRS")] // what is "R"? "subtract_r_scalar" sucks
+               public void subtract_r_scalar (OpenCV.Scalar value, OpenCV.Array dst, OpenCV.Array? mask = 
null);
+               [CCode (cname = "cvMul")]
+               public void multiply (OpenCV.Array src2, OpenCV.Array dst, double scale = 1.0);
+               [CCode (cname = "cvDiv")]
+               public void divide (OpenCV.Array src2, OpenCV.Array dst, double scale = 1.0);
+               [CCode (cname = "cvScaleAdd")]
+               public void scale_add (OpenCV.Scalar scale, OpenCV.Array src2, OpenCV.Array dst);
+               [CCode (cname = "cvAddWeighted")]
+               public void add_weighted (double alpha, OpenCV.Array src2, double beta, double gamma, 
OpenCV.Array dst);
+               [CCode (cname = "cvDotProduct")]
+               public double dot_product (OpenCV.Array src2);
+               [CCode (cname = "cvAnd")]
+               public void and (OpenCV.Array src2, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvAndS")]
+               public void and_scalar (OpenCV.Scalar value, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvOr")]
+               public void or (OpenCV.Array src2, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvOrS")]
+               public void or_scalar (OpenCV.Scalar value, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvXor")]
+               public void xor (OpenCV.Array src2, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvXorS")]
+               public void xor_scalar (OpenCV.Scalar value, OpenCV.Array dst, OpenCV.Array? mask = null);
+               [CCode (cname = "cvNot")]
+               public void not (OpenCV.Array dst);
+               [CCode (cname = "cvInRange")]
+               public void in_range (OpenCV.Array lower, OpenCV.Array upper, OpenCV.Array dst);
+               [CCode (cname = "cvInRangeS")]
+               public void in_range_scalar (OpenCV.Scalar lower, OpenCV.Scalar upper, OpenCV.Array dst);
+               [CCode (cname = "cvCmp")]
+               public void compare (OpenCV.Array src2, OpenCV.Array dst, OpenCV.ComparisonOperator cmp_op = 
OpenCV.ComparisonOperator.EQUAL);
+               [CCode (cname = "cvCmpS")]
+               public void compare_scalar (double value, OpenCV.Array dst, OpenCV.ComparisonOperator cmp_op 
= OpenCV.ComparisonOperator.EQUAL);
+               [CCode (cname = "cvMin")]
+               public void min (OpenCV.Array src2, OpenCV.Array dst);
+               [CCode (cname = "cvMax")]
+               public void max (OpenCV.Array src2, OpenCV.Array dst);
+               [CCode (cname = "cvMinS")]
+               public void min_scalar (double value, OpenCV.Array dst);
+               [CCode (cname = "cvMaxS")]
+               public void max_scalar (double value, OpenCV.Array dst);
+               [CCode (cname = "cvAbsDiff")]
+               public void abs_diff (OpenCV.Array src2, OpenCV.Array dst);
+               [CCode (cname = "cvAbsDiffS")]
+               public void abs_diff_scalar (OpenCV.Array dst, OpenCV.Scalar value);
+               [CCode (cname = "cvAbs")]
+               public void abs (OpenCV.Array dst);
+
+               [CCode (cname = "cvCartToPolar")]
+               public static void cartesian_to_polar (OpenCV.Array x, OpenCV.Array y, OpenCV.Array 
magnitude, OpenCV.Array? angle = null, int angle_in_degrees = 0);
+               [CCode (cname = "cvPolarToCart")]
+               public static void polar_to_cartesian (OpenCV.Array magnitude, OpenCV.Array angle, 
OpenCV.Array x, OpenCV.Array y, int angle_in_degress = 0);
+               [CCode (cname = "cvPow")]
+               public void pow (OpenCV.Array dst, double power);
+               [CCode (cname = "cvExp")]
+               public void exp (OpenCV.Array dst);
+               [CCode (cname = "cv")]
+               public void log (OpenCV.Array dst);
+               [CCode (cname = "cvCheckArr")]
+               public int check_array (OpenCV.Check flags = 0, double min_val = 0.0, double max_val = 0.0);
+               [CCode (cname = "cvSort")]
+               public void sort (OpenCV.Array? dst = null, OpenCV.Array? idxmat = null, OpenCV.Sort flags = 
OpenCV.Sort.EVERY_ROW | OpenCV.Sort.ASCENDING);
+               [CCode (cname = "cvCrossProduct")]
+               public void cross_product (OpenCV.Array src2, OpenCV.Array dst);
+               [CCode (cname = "cvMatMulAdd")]
+               public void matrix_multiply_add (OpenCV.Array src2, OpenCV.Array? src3, OpenCV.Array dst);
+               [CCode (cname = "cvMatMul")]
+               public void matrix_multiply (OpenCV.Array src2, OpenCV.Array dst);
+               [CCode (cname = "cvGEMM")]
+               public void GEMM (OpenCV.Array src2, double alpha, OpenCV.Array src3, double beta, 
OpenCV.Array dst, OpenCV.GEMMTranspose tABC = 0);
+               [CCode (cname = "cvTransform")]
+               public void transform (OpenCV.Array dst, OpenCV.Matrix transmat, OpenCV.Matrix? shiftvec = 
null);
+               [CCode (cname = "cvPerspectiveTransform")]
+               public void perspective_transform (OpenCV.Array dst, OpenCV.Matrix mat);
+               [CCode (cname = "cvMulTransposed")]
+               public void multiply_transposed (OpenCV.Array dst, int order, OpenCV.Array? delta = null, 
double scale = 1.0);
+               [CCode (cname = "cvTranspose")]
+               public void transpose (OpenCV.Array? dst = null);
+               [CCode (cname = "cvFlip")]
+               public void flip (OpenCV.Array? dst = null, OpenCV.FlipMode flip_mode = 
OpenCV.FlipMode.HORIZONTAL);
+               [CCode (cname = "cvSVD")]
+               public void SVD (OpenCV.Array w, OpenCV.Array? u = null, OpenCV.Array? v = null, 
OpenCV.SVDFlag flags = 0);
+               [CCode (cname = "cvSVBkSb")]
+               public static void SVBkSb (OpenCV.Array W, OpenCV.Array U, OpenCV.Array V, OpenCV.Array B, 
OpenCV.Array X, OpenCV.SVDFlag flags = 0);
+
+               [CCode (cname = "cvLine")]
+               public void line (OpenCV.Point pt1, OpenCV.Point pt2, OpenCV.Scalar color, int thickness = 1);
+               [CCode (cname = "cvRectangle")]
+               public void rectangle (OpenCV.Point pt1, OpenCV.Point pt2, OpenCV.Scalar color, int thickness 
= 1, int line_type = 8, int shift = 0);
+               [CCode (cname = "cvCircle")]
+               public void circle (OpenCV.Point center, int radius, OpenCV.Scalar color, int thickness = 1, 
int line_type = 8, int shift = 0);
+               [CCode (cname = "cvEllipse")]
+               public void ellipse (OpenCV.Point center, OpenCV.Size axes, double angle, double start_angle, 
double end_angle, OpenCV.Scalar color, int thickness = 1);
+               [CCode (cname = "cvEllipseBox")]
+               public void ellipse_box (OpenCV.Box2D box, OpenCV.Scalar color, int thickness = 1, int 
line_type = 8, int shift = 0);
+               [CCode (cname = "cvFillConvexPoly")]
+               public void fill_convex_polygon (OpenCV.Point[] pts, OpenCV.Scalar color, int line_type = 8, 
int shift = 0);
+               [CCode (cname = "cvFillPoly")]
+               public void fill_polygon ([CCode (array_length = false)] OpenCV.Point[][] pts, [CCode 
(array_length = false)] int[] npts, int contours, OpenCV.Scalar color, int line_type = 8, int shift = 0);
+               [CCode (cname = "cvPolyLine")]
+               public void poly_line ([CCode (array_length = false)] OpenCV.Point[][] pts, [CCode 
(array_length = false)] int[] npts, int contours, int is_closed, OpenCV.Scalar color, int thickness = 1, int 
line_type = 8, int shift = 0);
+               [CCode (cname = "cvSegmentImage", cheader_filename = "cvaux.h")]
+               public OpenCV.Sequence segment_image (OpenCV.Array dstarr, double canny_threshhold, double 
ffill_threshhold, OpenCV.Memory.Storage storage);
+               [CCode (cname = "cvConvert")]
+               public void convert (OpenCV.Array dst);
+       }
+
+       [SimpleType, CCode (cname = "CvBox2D", has_type_id = false)]
+       public struct Box2D {
+               public OpenCV.Point2D32f center;
+               public OpenCV.Size2D32f size;
+               public float angle;
+       }
+
+       [Flags, CCode (cname = "int", has_type_id = false, lower_case_cprefix = "CV_CHECK_")]
+       public enum Check {
+               RANGE,
+               QUIET
+       }
+
+       [CCode (cname = "int", has_type_id = false, lower_case_cprefix = "CV_CMP_")]
+       public enum ComparisonOperator {
+               EQ,
+               GT,
+               GE,
+               LT,
+               LE,
+               NE,
+
+               [CCode (cname = "CV_CMP_EQ")]
+               EQUAL,
+               [CCode (cname = "CV_CMP_GT")]
+               GREATER_THAN,
+               [CCode (cname = "CV_CMP_GE")]
+               GRATER_THAN_OR_EQUAL,
+               [CCode (cname = "CV_CMP_LT")]
+               LESS_THAN,
+               [CCode (cname = "CV_CMP_LE")]
+               LESS_THAN_OR_EQUAL,
+               [CCode (cname = "CV_CMP_NE")]
+               NOT_EQUAL
+       }
+
+       [Compact, CCode (cname = "CvEHMM", cheader_filename = "cvaux.h", free_function = "cvRelease2DHMM", 
free_function_address_of = true)]
+       public class EHMM {
+               [CCode (cname = "cvCreate2DHMM")]
+               public EHMM.2D ([CCode (array_length = false)] int[] stateNumber, [CCode (array_length = 
false)] int[] numMix, int obsSize);
+
+               [CCode (cname = "cvUniformImgSegm", instance_pos = -1)]
+               public void uniform_image_segment (OpenCV.EHMM.ImageObservationInfo obs);
+               [CCode (cname = "cvInitMixSegm", instance_pos = -1)]
+               public void init_mix_segment (OpenCV.EHMM.ImageObservationInfo[] obs_info_array);
+               [CCode (cname = "cvEstimateHMMStateParams", instance_pos = -1)]
+               public void estimate_state_params (OpenCV.EHMM.ImageObservationInfo[] obs_info_array);
+               [CCode (cname = "cvEstimateTransProb", instance_pos = -1)]
+               public void estimate_transition_probability (OpenCV.EHMM.ImageObservationInfo[] 
obs_info_array);
+               [CCode (cname = "cvEstimateObsProb", instance_pos = -1)]
+               public void estimate_observation_probability (OpenCV.EHMM.ImageObservationInfo obs_info);
+               [CCode (cname = "cvEViterbi", instance_pos = -1)]
+               public void viterbi (OpenCV.EHMM.ImageObservationInfo obs_info);
+               [CCode (cname = "cvMixSegmL2", instance_pos = -1)]
+               public void mix_segm_l2 (OpenCV.EHMM.ImageObservationInfo[] obs_info_array);
+
+               public int level;
+               [CCode (array_length_cname = "num_states")]
+               public float[] transP;
+               [CCode (array_length = false)]
+               public float[][] obsProb;
+               public OpenCV.EHMM.StateInfo u;
+
+               [Compact, CCode (cname = "CvEHMMState")]
+               public class State {
+                       [CCode (array_length_cname = "num_mix")]
+                       public float[] mu;
+                       [CCode (array_length_cname = "num_mix")]
+                       public float[] inv_var;
+                       [CCode (array_length_cname = "num_mix")]
+                       public float[] log_var_val;
+                       [CCode (array_length_cname = "num_mix")]
+                       public float[] weight;
+               }
+
+               public struct StateInfo {
+                       public OpenCV.EHMM.State state;
+                       public OpenCV.EHMM ehmm;
+               }
+
+               [Compact, CCode (cname = "CvImgObsInfo", free_function = "cvReleaseObsInfo", 
free_function_address_of = true)]
+               public class ImageObservationInfo {
+                       public ImageObservationInfo (OpenCV.Size numObs, int obsSize);
+
+                       // [CCode (cname = "CV_COUNT_OBS", instance_pos = -1)]
+                       // public void count (roi, win, delta);
+               }
+       }
+
+       [CCode (cname = "int", has_type_id = false)]
+       public enum FlipMode {
+               [CCode (cname = "0")]
+               HORIZONTAL,
+               [CCode (cname = "1")]
+               VERTICAL,
+               [CCode (cname = "-1")]
+               BOTH
+       }
+
+       [CCode (cname = "int", has_type_id = false, cprefix = "CV_FONT_")]
+       public enum FontFace {
+               HERSHEY_SIMPLEX,
+               HERSHEY_PLAIN,
+               HERSHEY_DUPLEX,
+               HERSHEY_COMPLEX,
+               HERSHEY_TRIPLEX,
+               HERSHEY_COMPLEX_SMALL,
+               HERSHEY_SCRIPT_SIMPLEX,
+               HERSHEY_SCRIPT_COMPLEX,
+               ITALIC,
+               VECTOR0
+       }
+
+       [Flags, CCode (cname = "int", has_type_id = false)]
+       public enum GEMMTranspose {
+               [CCode (cname = "CV_GEMM_A_T")]
+               A,
+               [CCode (cname = "CV_GEMM_B_T")]
+               B,
+               [CCode (cname = "CV_GEMM_C_T")]
+               C
+       }
+
+       [CCode (cname = "CvLineIterator", has_type_id = false)]
+       public struct LineIterator {
+               [CCode (cname = "cvInitLineIterator")]
+               public LineIterator (OpenCV.Array image, OpenCV.Point pt1, OpenCV.Point pt2, int connectivity 
= 8, int left_to_right = 0);
+
+               public int err;
+               public int plus_delta;
+               public int minus_delta;
+               public int plus_step;
+               public int minus_step;
+       }
+
+       [Flags, CCode (cname = "int", has_type_id = false, lower_case_cprefix = "CV_SORT_")]
+       public enum Sort {
+               EVERY_ROW,
+               EVERY_COLUMN,
+               ASCENDING,
+               DESCENDING
+       }
+
+       [Flags, CCode (cname = "int", has_type_id = false, lower_case_cprefix = "CV_SVD_")]
+       public enum SVDFlag {
+               MODIFY_A,
+               U_T,
+               V_T
+       }
+
+       [CCode (cname = "int", has_type_id = false)]
+       public enum Type {
+               [CCode (cname = "CV_8U")]
+               U8,
+               [CCode (cname = "CV_8S")]
+               S8,
+               [CCode (cname = "CV_16U")]
+               U16,
+               [CCode (cname = "CV_16S")]
+               S16,
+               [CCode (cname = "CV_32S")]
+               S32,
+               [CCode (cname = "CV_32F")]
+               F32,
+               [CCode (cname = "CV_64F")]
+               F64,
+               [CCode (cname = "CV_USRTYPE1")]
+               USR1,
+
+               [CCode (cname = "CV_CV_8UC1")]
+               UC8_1,
+               [CCode (cname = "CV_8UC2")]
+               UC8_2,
+               [CCode (cname = "CV_8UC3")]
+               UC8_3,
+               [CCode (cname = "CV_8UC4")]
+               UC8_4,
+
+               [CCode (cname = "CV_8SC1")]
+               SC8_1,
+               [CCode (cname = "CV_8SC2")]
+               SC8_2,
+               [CCode (cname = "CV_8SC3")]
+               SC8_3,
+               [CCode (cname = "CV_8SC4")]
+               SC8_4,
+
+               [CCode (cname = "CV_16UC1")]
+               UC16_1,
+               [CCode (cname = "CV_16UC2")]
+               UC16_2,
+               [CCode (cname = "CV_16UC3")]
+               UC16_3,
+               [CCode (cname = "CV_16UC4")]
+               UC16_4,
+
+               [CCode (cname = "CV_16SC1")]
+               SC16_1,
+               [CCode (cname = "CV_16SC2")]
+               SC16_2,
+               [CCode (cname = "CV_16SC3")]
+               SC16_3,
+               [CCode (cname = "CV_16SC4")]
+               SC16_4,
+
+               [CCode (cname = "CV_32SC1")]
+               SC32_1,
+               [CCode (cname = "CV_32SC2")]
+               SC32_2,
+               [CCode (cname = "CV_32SC3")]
+               SC32_3,
+               [CCode (cname = "CV_32SC4")]
+               SC32_4,
+
+               [CCode (cname = "CV_32FC1")]
+               FC32_1,
+               [CCode (cname = "CV_32FC2")]
+               FC32_2,
+               [CCode (cname = "CV_32FC3")]
+               FC32_3,
+               [CCode (cname = "CV_32FC4")]
+               FC32_4,
+
+               [CCode (cname = "CV_64FC1")]
+               FC64_1,
+               [CCode (cname = "CV_64FC2")]
+               FC64_2,
+               [CCode (cname = "CV_64FC3")]
+               FC64_3,
+               [CCode (cname = "CV_64FC4")]
+               FC64_4
+       }
+
+       [CCode (cname = "CvFont", has_type_id = false, destroy_function = "", has_copy_function = false)]
+       public struct Font {
+               [CCode (cname = "cvInitFont")]
+               public Font (OpenCV.FontFace font_face, double hscale, double vscale, double shear = 0.0, int 
thickness = 1, int line_type = 8);
+
+               [CCode (cname = "cvGetTextSize", instance_pos = 1.9)]
+               public void get_text_size (string text_string, out OpenCV.Size text_size, out int baseline);
+
+               public OpenCV.FontFace font_face;
+               [CCode (array_length = false, array_null_terminated = true)]
+               public int[] ascii;
+               [CCode (array_length = false, array_null_terminated = true)]
+               public int[] greek;
+               [CCode (array_length = false, array_null_terminated = true)]
+               public int[] cyrillic;
+               public float hscale;
+               public float vscale;
+               public int thickness;
+               public float dx;
+               public int line_type;
+       }
+
+       [Compact, CCode (cname = "CvHaarClassifierCascade")]
+       public class HaarClassifierCascade {
+               [CCode (cname = "cvLoad", type = "void*")]
+               public static unowned HaarClassifierCascade? load (string filename, OpenCV.Memory.Storage 
storage, string? name = null, out string? real_name = null);
+
+               [CCode (cname = "cvHaarDetectObjects", instance_pos = 1.9)]
+               public unowned OpenCV.Sequence<OpenCV.Rectangle?> detect_objects (OpenCV.Array image, 
OpenCV.Memory.Storage storage, double scale_factor = 1.0, int min_neighbors = 3, 
OpenCV.HaarClassifierCascade.Flags flags = 0, OpenCV.Size min_size = OpenCV.Size (0, 0));
+               [CCode (cname = "cvSetImagesForHaarClassifierCascade")]
+               public void set_images (OpenCV.Array sum, OpenCV.Array sqsum, OpenCV.Array tilted_sum, double 
scale);
+               [CCode (cname = "cvRunHaarClassifierCascade")]
+               public int run (OpenCV.Point pt, int start_stage = 0);
+
+               [Flags, CCode (cname = "int", has_type_id = false, cprefix = "CV_HAAR_")]
+               public enum Flags {
+                       DO_CANNY_PRUNING,
+                       SCALE_IMAGE,
+                       FIND_BIGGEST_OBJECT,
+                       DO_ROUGH_SEARCH
+               }
+       }
+
+       [SimpleType, CCode (cname = "CvInput")]
+       public struct Input {
+               public OpenCV.Callback @callback;
+               public void* data;
+       }
+
+       [Compact, CCode (cname = "CvMat", has_type_id = false, free_function = "cvReleaseMat", 
free_function_address_of = true, copy_function = "cvCloneMat")]
+       public class Matrix : OpenCV.Array {
+               [CCode (cname = "cvCreateMat")]
+               public Matrix (int rows, int cols, int type);
+               [CCode (cname = "cvCreateMatHeader")]
+               public Matrix.header (int rows, int cols, int type);
+
+               [CCode (cname = "cvCloneMat")]
+               public Matrix clone ();
+               [CCode (cname = "cvCompleteSymm")]
+               public void complete_symmetric (int LtoR);
+               [CCode (cname = "cvIplDepth")]
+               public static int depth (int type);
+               [CCode (cname = "cvmGet")]
+               public double get (int row, int col);
+               [CCode (cname = "cvmSet")]
+               public void set (int row, int col, double value);
+               [CCode (cname = "cvSolveCubic")]
+               public int solve_cubic (OpenCV.Matrix roots);
+               [CCode (cname = "cvSolvePoly")]
+               public void solve_polynomial (OpenCV.Matrix roots2, int maxiter = 20, int fig = 100);
+
+               public OpenCV.Type type;
+               public int step;
+               public int rows;
+               public int cols;
+               public OpenCV.Matrix.Data data;
+
+               [CCode (cname = "union { uchar* ptr; short* s; int* i; float* fl; double* db; }", has_type_id 
= false)]
+               public struct Data {
+                       public void* ptr;
+                       public short* s;
+                       public int* i;
+                       public float* fl;
+                       public double* db;
+               }
+
+               [Compact, CCode (cname = "cvMatND", has_type_id = false, free_function = "cvReleaseMatND", 
free_function_address_of = true, copy_function = "cvCloneMatND")]
+               public class ND {
+                       [CCode (cname = "cvCreateMatND")]
+                       public ND (int dims, [CCode (array_length = false, array_null_terminated = true)] 
int[] sizes, int type);
+
+                       [CCode (cname = "cvCloneMatND")]
+                       public ND clone ();
+
+                       public OpenCV.Type type;
+                       public int dims;
+                       public OpenCV.Matrix.Data data;
+                       // TODO: s/32/OpenCV.MAX_DIM/ when b.g.o. #624507 is resolved
+                       public OpenCV.Matrix.ND.Dimension dim[32];
+
+                       public struct Dimension {
+                               public int size;
+                               public int step;
+                       }
+               }
+
+               [Compact, CCode (cname = "cvSparseMat", has_type_id = false, free_function = 
"cvReleaseSparseMat", free_function_address_of = true, copy_function = "cvCloneSparseMat")]
+               public class Sparse {
+                       [CCode (cname = "cvCreateSparseMat")]
+                       public Sparse (int dims, [CCode (array_length = false, array_null_terminated = true)] 
int[] sizes, int type);
+
+                       [CCode (cname = "cvCloneSparseMat")]
+                       public Sparse clone ();
+
+                       public OpenCV.Type type;
+                       public int dims;
+                       // public struct CvSet* heap;
+                       // public void** hashtable;
+                       // public int hashsize;
+                       [CCode (cname = "valoffset")]
+                       public int value_offset;
+                       [CCode (cname = "idxoffset")]
+                       public int index_offset;
+                       // TODO: s/32/OpenCV.MAX_DIM/ when b.g.o. #624507 is resolved
+                       public int size[32];
+
+                       [CCode (cname = "CvSparseNode", has_type_id = false)]
+                       public struct Node {
+                               [CCode (cname = "hashval")]
+                               public uint hash_value;
+                               public Node* next;
+                       }
+
+                       [CCode (cname = "CvSparseMatIterator", has_type_id = false)]
+                       public struct Iterator {
+                               [CCode (cname = "mat")]
+                               public OpenCV.Matrix.Sparse matrix;
+                               public Node* node;
+                               [CCode (cname = "curidx")]
+                               public int current_index;
+                       }
+               }
+       }
+
+       [CCode (cname = "CvString")]
+       public class String {
+               [CCode (cname = "cvMemStorageAllocString")]
+               public String (OpenCV.Memory.Storage storage, string str, int len = -1);
+
+               public int len;
+               public string ptr;
+       }
+
+       namespace Math {
+               [CCode (cname = "cvCeil")]
+               public static int ceil (double value);
+               [CCode (cname = "cvCbrt")]
+               public static float cubic_root (float value);
+               [CCode (cname = "cvFastArctan")]
+               public static float fast_arctan (float x, float y);
+               [CCode (cname = "cvFloor")]
+               public static int floor (double value);
+               [CCode (cname = "cvInvSqrt")]
+               public static float inv_sqrt ();
+               [CCode (cname = "cvIsInf")]
+               public static int is_inf (double value);
+               [CCode (cname = "cvIsNaN")]
+               public static int is_nan (double value);
+               [CCode (cname = "cvRound")]
+               public static int round (double value);
+               [CCode (cname = "cvSqrt")]
+               public static float sqrt (double value);
+       }
+
+       namespace Memory {
+               [Compact, CCode (cname = "CvMemBlock")]
+               public struct Block {
+                       public OpenCV.Memory.Block? prev;
+                       public OpenCV.Memory.Block? next;
+               }
+
+               [Compact, CCode (cname = "CvMemStorage", free_function = "cvReleaseMemStorage", 
free_function_address_of = true)]
+               public class Storage {
+                       [CCode (cname = "cvCreateMemStorage")]
+                       public Storage (int block_size = 0);
+                       [CCode (cname = "cvCreateChildMemStorage")]
+                       public Storage.from_parent (OpenCV.Memory.Storage parent);
+
+                       [CCode (cname = "cvClearMemStorage")]
+                       public void clear ();
+                       [CCode (cname = "cvSaveMemStoragePos")]
+                       public void save_position (OpenCV.Memory.Storage.Position pos);
+                       [CCode (cname = "cvRestoreMemStoragePos")]
+                       public void restore_position (OpenCV.Memory.Storage.Position pos);
+                       [CCode (cname = "cvMemStorageAlloc")]
+                       public void* alloc (size_t size);
+                       [CCode (cname = "cvMemStorageAllocString")]
+                       public OpenCV.String alloc_string (string ptr, int len = -1);
+
+                       [CCode (cname = "cvLoad", instance_pos = 1.9)]
+                       public void* load (string filename, string name, out string? real_name = null);
+
+                       public int signature;
+                       public OpenCV.Memory.Block bottom;
+                       public OpenCV.Memory.Block top;
+                       public OpenCV.Memory.Storage parent;
+                       public int block_size;
+                       public int free_space;
+
+                       [CCode (cname = "CvMemStoragePos")]
+                       public struct Position {
+                               public OpenCV.Memory.Block top;
+                               public int free_space;
+                       }
+               }
+
+               [CCode (cname = "cvAlloc")]
+               public static void* alloc ();
+               [CCode (cname = "cvFree")]
+               public static void free (void* ptr);
+       }
+
+       [SimpleType, CCode (cname = "CvPoint", has_type_id = false)]
+       public struct Point {
+               [CCode (cname = "cvPoint")]
+               public Point (int x, int y);
+               [CCode (cname = "cvPointFrom32f")]
+               public Point.from_32f (OpenCV.Point2D32f point);
+
+               [CCode (cname = "cvPointTo32f")]
+               public Point to_32f ();
+
+               public int x;
+               public int y;
+       }
+
+       [CCode (cname = "CvPoint2D32f", has_type_id = false)]
+       public struct Point2D32f {
+               public float x;
+               public float y;
+
+               [CCode (cname = "cvPoint2D32f")]
+               public Point2D32f (float x, float y);
+               [CCode (cname = "cvPointTo32f")]
+               public Point2D32f.from_point (OpenCV.Point point);
+
+               [CCode (cname = "cvPointFrom32f")]
+               public Point to_point (OpenCV.Point2D32f point);
+       }
+
+       [CCode (cname = "CvPoint2D32f", has_type_id = false)]
+       public struct Point2D64f {
+               [CCode (cname = "cvPoint2D64f")]
+               public Point2D64f (double x, double y);
+
+               public double x;
+               public double y;
+       }
+
+       [CCode (cname = "CvPoint3D32f", has_type_id = false)]
+       public struct Point3D32f {
+               [CCode (cname = "cvPoint3D32f")]
+               public Point3D32f (float x, float y, float z);
+
+               public float x;
+               public float y;
+               public float z;
+       }
+
+       [CCode (cname = "CvPoint3D64f")]
+       public struct Point3D64f {
+               [CCode (cname = "cvPoint3D64f")]
+               public Point3D64f (double x, double y, double z);
+
+               public double x;
+               public double y;
+               public double z;
+       }
+
+       [SimpleType, CCode (cname = "CvRect", has_type_id = false, destroy_function = "")]
+       public struct Rectangle {
+               [CCode (cname = "cvRect")]
+               public Rectangle (int x, int y, int width, int height);
+               [CCode (cname = "cvRectToROI")]
+               public OpenCV.IPL.ROI to_roi (int coi);
+
+               public int x;
+               public int y;
+               public int width;
+               public int height;
+       }
+
+       [SimpleType, CCode (cname = "CvScalar", has_type_id = false, destroy_function = "")]
+       public struct Scalar {
+               [CCode (cname = "cvScalar")]
+               public Scalar (double val0, double val1 = 0.0, double val2 = 0.0, double val3 = 0.0);
+               [CCode (cname = "cvScalarAll")]
+               public Scalar.all (double val0123);
+               [CCode (cname = "cvRawDataToScalar")]
+               public Scalar.from_raw_data ([CCode (array_length = false)] uint8[] data, int type);
+               [CCode (cname = "cvColorToScalar")]
+               public Scalar.from_color (double packed_color, int arrtype);
+               [CCode (cname = "CV_RGB")]
+               public Scalar.from_rgb (double red, double green, double blue);
+
+               [CCode (cname = "cvScalarToRawData")]
+               public void to_raw_data ([CCode (array_length = false)] uint8[] data, int type, int 
extend_to_12 = 0);
+
+               public double val[4];
+       }
+
+       [Compact, CCode (cname = "CvSeq", free_function = "")]
+       public class Sequence<T> {
+               [CCode (cname = "cvCreateSeq")]
+               public Sequence (int seq_flags, int header_size, int elem_size, OpenCV.Memory.Storage 
storage);
+
+               [CCode (cname = "cvSetSeqBlockSize")]
+               public void set_block_size (int delta_elems);
+               [CCode (cname = "cvSeqPush")]
+               public unowned T push (T element);
+               [CCode (cname = "cvSeqPushFront")]
+               public unowned T push_front (T element);
+               [CCode (cname = "cvSeqPop")]
+               public void pop (T element = null);
+               [CCode (cname = "cvSeqPopFront")]
+               public void pop_front (T element = null);
+               [CCode (cname = "cvGetSeqElem")]
+               public unowned T? get (int index);
+               [CCode (cname = "cvSeqPushMulti")]
+               public void push_multi (T[] elements, bool in_front = false);
+               [CCode (cname = "cvSeqPopMulti")]
+               public void pop_multi (T[] elements, bool in_front = false);
+               [CCode (cname = "cvSeqInsert")]
+               public void insert (int before_index, T element);
+               [CCode (cname = "cvSeqRemove")]
+               public void remove (int index);
+               [CCode (cname = "cvClearSeq")]
+               public void clear (int index);
+               [CCode (cname = "cvCvtSeqToArray", array_length_pos = 1.9)]
+               public T[]? to_array (OpenCV.Slice slice = OpenCV.Slice.WHOLE_ARRAY);
+
+               public int total;
+       }
+
+       [SimpleType, CCode (cname = "CvSize", has_type_id = false)]
+       public struct Size {
+               public int width;
+               public int height;
+
+               [CCode (cname = "cvSize")]
+               public Size (int width, int height);
+       }
+
+       public struct Size2D32f {
+               [CCode (cname = "cvSize2D32f")]
+               public Size2D32f (double width, double heigh);
+
+               public float width;
+               public float height;
+       }
+
+       [SimpleType, CCode (cname = "CvSlice")]
+       public struct Slice {
+               public int start_index;
+               public int end_index;
+
+               [CCode (cname = "cvSlice")]
+               public Slice (int start, int end);
+               [CCode (cname = "cvSliceLength")]
+               public int length (OpenCV.Sequence seq);
+
+               [CCode (cname = "CV_WHOLE_ARR")]
+               public const OpenCV.Slice WHOLE_ARRAY;
+       }
+
+       [SimpleType, CCode (cname = "CvTermCriteria", has_type_id = false)]
+       public struct TermCriteria {
+               public OpenCV.Type type;
+               public int max_iter;
+               public double epsilon;
+
+               [CCode (cname = "cvTermCriteria")]
+               public TermCriteria (int type, int max_iter, double epsilon);
+       }
+
+       [CCode (cheader_filename = "highgui.h")]
+       namespace Window {
+               [CCode (cname = "cvNamedWindow")]
+               public static int create_named (string window_name, OpenCV.Window.Flags flags = 
OpenCV.Window.Flags.AUTO_SIZE);
+               [CCode (cname = "cvShowImage")]
+               public static void show_image (string window_name, OpenCV.Array arr);
+               [CCode (cname = "cvDestroyWindow")]
+               public static void destroy (string window_name);
+               [CCode (cname = "cvGetWindowProperty")]
+               public static void get_property (string window_name, OpenCV.Window.Property prop_id);
+               [CCode (cname = "cvSetWindowProperty")]
+               public static void set_property (string window_name, OpenCV.Window.Property prop_id, double 
value);
+               [CCode (cname = "cvResizeWindow")]
+               public static void resize (string window_name, int width, int height);
+               [CCode (cname = "cvMoveWindow")]
+               public static void move (string window_name, int x, int y);
+               [CCode (cname = "cvDestroyAllWindows")]
+               public static void destroy_all ();
+               [CCode (cname = "cvGetWindowHandle")]
+               public static void* get_handle (string window_name);
+               [CCode (cname = "cvGetWindowName")]
+               public static unowned string get_name (void* handle);
+               [CCode (cname = "cvSetMouseCallback")]
+               public static void set_mouse_callback (string window_name, OpenCV.MouseCallback on_mouse);
+
+               [Flags, CCode (cname = "int", has_type_id = false, cprefix = "CV_WINDOW_")]
+               public enum Flags {
+                       [CCode (cname = "CV_WINDOW_AUTOSIZE")]
+                       AUTO_SIZE
+               }
+
+               [CCode (cname = "int", has_type_id = false, cprefix = "CV_WND_PROP_")]
+               public enum Property {
+                       FULLSCREEN,
+                       AUTOSIZE
+               }
+       }
+
+       [CCode (cheader_filename = "highgui.h")]
+       namespace Trackbar {
+               [CCode (cname = "CvTrackBarCallback2")]
+               public delegate void Callback (int pos);
+
+               [CCode (cname = "cvCreateTrackbar2")]
+               public int create (string trackbar_name, string window_name, ref int value, int count, 
OpenCV.Trackbar.Callback on_change);
+               [CCode (cname = "cvGetTrackbarPos")]
+               public int get_position (string trackbar_name, string window_name);
+               [CCode (cname = "cvSetTrackbarPos")]
+               public void set_position (string trackbar_name, string window_name, int pos);
+       }
+
+       [CCode (cname = "int", has_type_id = false, cprefix = "CV_EVENT_", cheader_filename = "highgui.h")]
+       public enum EventType {
+               [CCode (cname = "CV_EVENT_MOUSEMOVE")]
+               MOUSE_MOVE,
+               [CCode (cname = "CV_EVENT_LBUTTONDOWN")]
+               LEFT_BUTTON_DOWN,
+               [CCode (cname = "CV_EVENT_RBUTTONDOWN")]
+               RIGHT_BUTTON_DOWN,
+               [CCode (cname = "CV_EVENT_MBUTTONDOWN")]
+               MIDDLE_BUTTON_DOWN,
+               [CCode (cname = "CV_EVENT_LBUTTONUP")]
+               LEFT_BUTTON_UP,
+               [CCode (cname = "CV_EVENT_RBUTTONUP")]
+               RIGHT_BUTTON_UP,
+               [CCode (cname = "CV_EVENT_MBUTTONUP")]
+               MIDDLE_BUTTON_UP,
+               [CCode (cname = "CV_EVENT_LBUTTONDBLCLK")]
+               LEFT_BUTTON_DOUBLE_CLICK,
+               [CCode (cname = "CV_EVENT_RBUTTONDBLCLK")]
+               RIGHT_BUTTON_DOUBLE_CLICK,
+               [CCode (cname = "CV_EVENT_MBUTTONDBLCLK")]
+               MIDDLE_BUTTON_DOUBLE_CLICK,
+
+               MOUSEMOVE,
+               LBUTTONDOWN,
+               RBUTTONDOWN,
+               MBUTTONDOWN,
+               LBUTTONUP,
+               RBUTTONUP,
+               MBUTTONUP,
+               LBUTTONDBLCLK,
+               RBUTTONDBLCLK,
+               MBUTTONDBLCLK,
+       }
+
+       [Flags, CCode (cname = "int", has_type_id = false, cprefix = "CV_EVENT_FLAG_", cheader_filename = 
"highgui.h")]
+       public enum EventFlag {
+               [CCode (cname = "CV_EVENT_FLAG_LBUTTON")]
+               LEFT_BUTTON,
+               [CCode (cname = "CV_EVENT_FLAG_RBUTTON")]
+               RIGHT_BUTTON,
+               [CCode (cname = "CV_EVENT_FLAG_MBUTTON")]
+               MIDDLE_BUTTON,
+               [CCode (cname = "CV_EVENT_FLAG_CTRLKEY")]
+               CONTROL_KEY,
+               [CCode (cname = "CV_EVENT_FLAG_SHIFTKEY")]
+               SHIFT_KEY,
+               [CCode (cname = "CV_EVENT_FLAG_ALTKEY")]
+               ALT_KEY,
+
+               LBUTTON,
+               RBUTTON,
+               MBUTTON,
+               CTRLKEY,
+               SHIFTKEY,
+               ALTKEY
+       }
+
+       [Compact, CCode (cname = "CvCapture", free_function = "cvReleaseCapture", free_function_address_of = 
true, has_type_id = false, cheader_filename = "highgui.h")]
+       public class Capture {
+               [CCode (cname = "cvCreateFileCapture")]
+               public Capture.from_file (string filename);
+               [CCode (cname = "cvCreateCameraCapture")]
+               public Capture.from_camera (int index);
+
+               [CCode (cname = "cvGrabFrame")]
+               public bool grab_frame ();
+               [CCode (cname = "cvRetrieveFrame")]
+               public unowned OpenCV.IPL.Image retrieve_frame (int stream_index = 0);
+               [CCode (cname = "cvQueryFrame")]
+               public unowned OpenCV.IPL.Image query_frame ();
+               [CCode (cname = "cvGetCaptureProperty")]
+               public double get_property (OpenCV.Capture.Property property_id);
+               [CCode (cname = "cvSetCaptureProperty")]
+               public int set_property (OpenCV.Capture.Property property_id, double value);
+               [CCode (cname = "cvGetCaptureDomain")]
+               public OpenCV.Capture.Domain get_domain ();
+
+               [CCode (cname = "int", has_type_id = false, cprefix = "CV_CAP_")]
+               public enum Domain {
+                       ANY,
+                       MIL,
+                       VFW,
+                       V4L,
+                       V4L2,
+                       FIREWARE,
+                       FIREWIRE,
+                       IEEE1394,
+                       DC1394,
+                       CMU1394,
+                       STEREO,
+                       TYZX,
+                       [CCode (cname = "CV_TYZX_LEFT")]
+                       TYZX_LEFT,
+                       [CCode (cname = "CV_TYZX_RIGHT")]
+                       TYZX_RIGHT,
+                       [CCode (cname = "CV_TYZX_COLOR")]
+                       TYZX_COLOR,
+                       [CCode (cname = "CV_TYZX_Z")]
+                       TYZX_Z,
+                       QT,
+                       UNICAP,
+                       DSHOW,
+                       PVAPI
+               }
+
+               [CCode (cname = "int", has_type_id = false, cprefix = "CV_CAP_PROP_")]
+               public enum Property {
+                       POS_MSEC,
+                       POS_FRAMES,
+                       POS_AVI_RATIO,
+                       FRAME_WIDTH,
+                       FRAME_HEIGHT,
+                       FPS,
+                       FOURCC,
+                       FRAME_COUNT,
+                       FORMAT,
+                       MODE,
+                       BRIGHTNESS,
+                       CONTRAST,
+                       SATURATION,
+                       HUE,
+                       GAIN,
+                       EXPOSURE,
+                       CONVERT_RGB,
+                       WHITE_BALANCE,
+                       RECTIFICATION,
+               }
+       }
+
+       [CCode (cname = "CV_MAX_DIM")]
+       public const int MAX_DIM;
+
+       [CCode (cname = "CvCallback")]
+       public delegate void Callback (int index, void* buffer);
+       [CCode (cname = "CvMouseCallback")]
+       public delegate void MouseCallback (OpenCV.EventType ev, int x, int y, OpenCV.EventFlag flags);
+
+       [CCode (cname = "cvWaitKey")]
+       public static void wait_key (int delay = 0);
+
+       [CCode (cheader_filename = "cvaux.h")]
+       namespace Eigen {
+               [CCode (cname = "cvCalcCovarMatrixEx")]
+               public static void calculate_covariation_matrix (int nObjects, void* input, int ioFlags, 
[CCode (array_length_pos = 3.9)] uint8[] buffer, void* user_data, OpenCV.IPL.Image avg, [CCode (array_length 
= false)] float[] covar_matrix);
+               [CCode (cname = "cvCalcEigenObjects")]
+               public static void calculate_objects (int nObjects, void* input, void* output, int ioFlags, 
int ioBufSize, void* userData, OpenCV.TermCriteria calc_limit, OpenCV.IPL.Image avg, [CCode (array_length = 
false)] float[] eigenvals);
+               [CCode (cname = "cvClacDecompCoeff")]
+               public static double calculate_decomposition_coefficient (OpenCV.IPL.Image obj, 
OpenCV.IPL.Image eigObj, OpenCV.IPL.Image avg);
+               [CCode (cname = "cvEigenDecomposite")]
+               public static void eigen_decomposite (OpenCV.IPL.Image obj, int nEigObjs, void* eigInput, int 
ioFlags, void* userData, OpenCV.IPL.Image avg, [CCode (array_length = false)] float[] coeffs);
+               [CCode (cname = "cvEigenProjection")]
+               public static void eigen_projection (void* eigInput, int nEigObjs, int ioFlags, void* 
userData, [CCode (array_length = false)] float[] coeffs, OpenCV.IPL.Image avg, OpenCV.IPL.Image proj);
+       }
+
+       [CCode (cprefix = "Ipl")]
+       namespace IPL {
+               [Compact, CCode (cname = "IplImage", has_type_id = false, free_function = "cvReleaseImage", 
free_function_address_of = true, copy_function = "cvCloneImage")]
+               public class Image : OpenCV.Array {
+                       [CCode (cname = "cvCreateImage")]
+                       public Image (OpenCV.Size size, int depth, int channels);
+                       [CCode (cname = "cvLoadImage", cheader_filename = "highgui.h")]
+                       public Image.load (string filename, OpenCV.IPL.Image.LoadType type = 
OpenCV.IPL.Image.LoadType.COLOR);
+
+                       [CCode (cname = "cvCloneImage")]
+                       public Image clone ();
+                       [CCode (cname = "cvInitImageHeader")]
+                       public void init_header (OpenCV.Size size, int depth, int channels, int origin = 0, 
int align = 4);
+                       [CCode (cname = "cvGetImageCOI")]
+                       public int get_coi ();
+                       [CCode (cname = "cvGetImageROI")]
+                       public OpenCV.Rectangle get_roi ();
+                       [CCode (cname = "cvResetImageROI")]
+                       public int reset_roi ();
+                       [CCode (cname = "cvSetImageROI")]
+                       public void set_roi (OpenCV.Rectangle roi);
+                       [CCode (cname = "cvSetImageCOI")]
+                       public void set_coi (int coi);
+
+                       [CCode (cname = "nSize")]
+                       public int n_size;
+                       [CCode (cname = "ID")]
+                       public int id;
+                       [CCode (cname = "nChannels")]
+                       public int n_channels;
+                       [CCode (cname = "alphaChannel")]
+                       public int alpha_channel;
+                       public int depth;
+                       [CCode (cname = "colorModel")]
+                       public char color_model[4];
+                       [CCode (cname = "channelSeq")]
+                       public char channel_sequence[4];
+                       [CCode (cname = "dataOrder")]
+                       public int data_order;
+                       public int origin;
+                       public int align;
+                       public int width;
+                       public int height;
+                       public OpenCV.IPL.ROI* roi;
+                       [CCode (cname = "maskROI")]
+                       public Image mask_roi;
+                       [CCode (cname = "imageId")]
+                       public void* image_id;
+                       // [CCode (cname = "tileInfo")]
+                       // public TileInfo tile_info;
+                       [CCode (cname = "imageData", array_length_cname = "imageSize")]
+                       public uint8[] image_data;
+                       [CCode (cname = "widthStep")]
+                       public int width_step;
+                       [CCode (cname = "BorderMode")]
+                       public int border_mode[4];
+                       [CCode (cname = "BorderConst")]
+                       public int border_const[4];
+                       // [CCode (cname = "imageDataOrigin")]
+                       // public ? image_data_origin;
+
+                       [CCode (cname = "int", has_type_id = false, cprefix = "CV_LOAD_IMAGE_")]
+                       public enum LoadType {
+                               UNCHANGED,
+                               GRAYSCALE,
+                               COLOR,
+                               [CCode (cname = "CV_LOAD_IMAGE_ANYDEPTH")]
+                               ANY_DEPTH,
+                               [CCode (cname = "CV_LOAD_IMAGE_ANYCOLOR")]
+                               ANY_COLOR
+                       }
+               }
+
+               [SimpleType, CCode (cname = "IplROI", has_type_id = false)]
+               public struct ROI {
+                       public int coi;
+                       [CCode (cname = "xOffset")]
+                       public int x_offset;
+                       [CCode (cname = "yOffset")]
+                       public int y_offset;
+                       public int width;
+                       public int height;
+
+                       [CCode (cname = "cvROIToRect")]
+                       public OpenCV.Rectangle to_rectangle ();
+               }
+
+               [CCode (cname = "IplConvKernel", has_type_id = false)]
+               public struct ConvKernel {
+                       [CCode (cname = "nCols")]
+                       public int n_cols;
+                       [CCode (cname = "nRows")]
+                       public int n_rows;
+                       [CCode (cname = "anchorX")]
+                       public int anchor_x;
+                       [CCode (cname = "anchorY")]
+                       public int anchor_y;
+                       [CCode (array_length = false)]
+                       public int[] values;
+                       [CCode (cname = "nShiftR")]
+                       public int n_shift_r;
+               }
+
+               [CCode (cname = "IplConvKernelFP", has_type_id = false)]
+               public struct ConvKernelFP {
+                       [CCode (cname = "nCols")]
+                       public int nCols;
+                       [CCode (cname = "nRows")]
+                       public int nRows;
+                       [CCode (cname = "anchorX")]
+                       public int anchorX;
+                       [CCode (cname = "anchorY")]
+                       public int anchorY;
+                       [CCode (array_length = false)]
+                       public float[] values;
+               }
+       }
+}


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