[shotwell: 1/2] Wip/faces
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell: 1/2] Wip/faces
- Date: Tue, 19 Feb 2019 14:43:24 +0000 (UTC)
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]