Monday, February 28, 2011

opencv Haarcascade files in Ubuntu 10.10 (Maverick)

The haarcascade files used by opencv library to detect faces and other features are not being shipped in Ubuntu 10.10 (Maverick). There is an open bug for the issue:

https://bugs.launchpad.net/opencv/+bug/698016

This means Ubuntu 10.10 users won't be able to see the Face Overlay effect in Cheese :(

These are the missing files:

/usr/share/opencv/haarcascades/haarcascade_eye.xml
/usr/share/opencv/haarcascades/haarcascade_eye_tree_eyeglasses.xml
/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml
/usr/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml
/usr/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml
/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml
/usr/share/opencv/haarcascades/haarcascade_fullbody.xml
/usr/share/opencv/haarcascades/haarcascade_lefteye_2splits.xml
/usr/share/opencv/haarcascades/haarcascade_lowerbody.xml
/usr/share/opencv/haarcascades/haarcascade_mcs_eyepair_big.xml
/usr/share/opencv/haarcascades/haarcascade_mcs_eyepair_small.xml
/usr/share/opencv/haarcascades/haarcascade_mcs_lefteye.xml
/usr/share/opencv/haarcascades/haarcascade_mcs_mouth.xml
/usr/share/opencv/haarcascades/haarcascade_mcs_nose.xml
/usr/share/opencv/haarcascades/haarcascade_mcs_righteye.xml
/usr/share/opencv/haarcascades/haarcascade_mcs_upperbody.xml
/usr/share/opencv/haarcascades/haarcascade_profileface.xml
/usr/share/opencv/haarcascades/haarcascade_righteye_2splits.xml
/usr/share/opencv/haarcascades/haarcascade_upperbody.xml

Add yourself to the "people affected by this" list in the bug to make its heat value go up! ;)

Friday, February 18, 2011

Face overlays

I've finished the first version of the GStreamer plugin that encapsulates facedetect and rsvgoverlay plugins.

I created a GstBin derived plugin that handles the bus messages sent by facedetect, then I allow the user to change the image size and position relative to the face itself. So any image can be placed above, below, to the left or right of your face, and it will have the right size when you get near or far away from the camera. Width and height can also be set as fractions of the face size.

I handle the bus messages sent by facedetect element in the container bin itself. At first I thought I would need to catch events in a custom internal element placed between the facedetect and the rsvgoverlay elements. This solution ended up being simpler and better, and we didn't have to add event sending functions to facedetect.


To see it working I downloaded some random images from openclipart and tweaked their sizes and positions so they would fit my face.



So, this took me a little longer than I thought, but I learned a lot about GStreamer plugins. Now I'll ask some people to review my code so it can be uploaded to gst-plugins-bad. Enjoy!

edit: here is the patch https://bugzilla.gnome.org/show_bug.cgi?id=642759

New SVG overlays

Several new designs for frame overlays were kindly submitted and as promised I'll announce the names of the winners:

We have two winners! ;)
And they are: Peter Shinners, for the images in the left column, and Michał Prędotka for the images in the right column!


All images will be included in gnome-video-effects as examples, and (I think) two will be shown in cheese as effects. I like the kiss and polaroid ones myself!

Monday, February 7, 2011

A new GStreamer plugin

Some time ago, I bumped into a dead end when trying to implement the face-tracking overlays feature. The problem started when I tried to set the overlay width and height in run time.

The opencv facedetect gst-plugin sends bus messages with the x, y, width and height of the faces it detects. I captured the messages and set the rsvgoverlay element x and y properties without problems. But rsvgoverlay element lacks width and height properties.

This meant that if you moved your face away or close from the camera, the mask would still look the same size, or if the svg dimensions were huge or tiny, it would look that way instead of resizing to the face size.

I contacted rsvgoverlay maintainer Olivier Aubert to see if width and height could be added as properties for that element. He preferred not to and explained to me the reasons. (He pointed me to this bug where coincidentally Luciana had asked for something similar before our internships in GNOME Outreach program started, and she implemented a plugin for png overlays already, I haven't checked it yet).

Well, Olivier suggested I used the SVG data itself to set size and position. So I started implementing this:

case GST_MESSAGE_ELEMENT:
{
  if (strcmp (gst_structure_get_name (message->structure), "facedetect") == 0)
  {
    CheeseCameraPrivate *priv = CHEESE_CAMERA_GET_PRIVATE (camera);
    
    guint x, y, width, height;
    gchar *svg_data;
    const GstStructure *face;
    int face_count;
    
    /* TODO: get coordinates of multiple faces: 
       for (i=0; i < gst_value_list_get_size (gst_structure_get_value (message->structure, "faces")); i++) { ... }
    */
    face_count = gst_value_list_get_size (gst_structure_get_value (message->structure, "faces"));
    /* The last face in the list seems to be the right one, objects mistakenly 
     * detected as faces for a couple of frames seem to be in the list 
     * beginning. TODO: needs confirmation. */
    face = gst_value_get_structure (gst_value_list_get_value (gst_structure_get_value (message->structure, "faces"), face_count-1));
    gst_structure_get_uint (face, "x",      &x);
    gst_structure_get_uint (face, "y",      &y);
    gst_structure_get_uint (face, "width",  &width);
    gst_structure_get_uint (face, "height", &height);
             
    /* TODO: allow different positioning according to type of overlay
    if hat
    if beard
    x = the face right temple, so think a way to allow hats to go above, 
    beards to go midway below, etc.
    */
    
    /* TODO: ViewBox size affects anything? */
    svg_data = g_strdup_printf ("<svg viewBox=\"0 0 800 600\"><image x=\"%u\" y=\"%u\" width=\"%u\" height=\"%u\" xlink:href=\"%s\" /></svg>",
                                x, 
                                y, 
                                width, 
                                height, 
                                "/path/to/foo.svg");
    /* FIXME: Problem is right here ^^^^^^^^^^^^^^ I have no way of knowing the 
     * file path on run time with what we currently have. */
    
    /* The rsvgoverlay element in face recognition + svg overlay effects 
     * must be named "face_overlay" for this to work. Not very nice. 
     * TODO: iterate effect_filter bin elements looking for the rsvgoverlay 
     * element */
    GstElement *overlay_element = gst_bin_get_by_name (GST_BIN (priv->effect_filter), "face_overlay");                    
    g_object_set (overlay_element, "data", svg_data, NULL);
    
    g_object_unref (overlay_element);
    g_free(svg_data);
  }
}
break;


Then I realized: for this to work, I needed the SVG file path and we don't have a way to know it on runtime right now. It is parsed as part of the pipeline description from the .effect files in gnome-video-effects and the entire pipeline description is used to create the rsvgoverlay element. The same applies to all other effects. So to access that path, we would have to start knowing which effect we are handling at a specific time inside of cheese, and we don't want to do that.

Other option would be to parse the .effect files in a different way. That's something that is already being discussed here and it will allow for more functionality than I need for this.

So I started writing my very own GStreamer plugin, to have more control and manage several options and use cases better. Maybe I'll be using the existing rsvgoverlay and facedetect plugins somehow.

Last days I was following the steps in GStreamer Plugin Writer's Guide and climbing the vertical autotools learning curve. Hoo boy.


About Autotools:

Task #1: add a subdirectory named "pixmaps" with .svg images to gnome-video-effects to be installed in /usr/share/gnome-video-effects/pixmaps when running make install.

The files to edit were: gnome-video-effects/configure.ac and gnome-video-effects/Makefile.am

diff --git a/Makefile.am b/Makefile.am
index 8638fc8..b5b52fb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = effects
+SUBDIRS = effects pixmaps
 
 DIST_SUBDIRS = $(SUBDIRS) po
 
diff --git a/configure.ac b/configure.ac
index 499b6f2..9d33ffb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -23,5 +23,6 @@ AC_OUTPUT([
 Makefile
 gnome-video-effects.pc
 effects/Makefile
+pixmaps/Makefile
 po/Makefile.in
 ])

And a new Makefile.am needs to be added in the new pixmaps directory:

pixmapsdir = $(datadir)/gnome-video-effects/pixmaps

pixmaps_DATA = \
  kiss.svg \
  snow.svg \
  birthday-card.svg \
  microphone.svg
    
EXTRA_DIST = \
    $(pixmaps_DATA)

It looks simple now, but finding out how to do this, from which files to edit to what to add there, was a guessing-in-the-dark ordeal.

Task #2: include <cv.h> in my new plugin header file. Run "make" without errors.
Holy tap-dancing christ with crutches. This is so easy to do in Visual Studio. Just add the include path and the library to link against. But what to say about autotools that hasn't been said already? (With this I'm not saying that everything about programming in Windows is better, absolutely on the contrary: programming in linux is paradise compared to that. But creating a new little project to tinker and test things quickly? Yes, that is lacking in linux that I know of).

Files that needed to be edited: configure.ac in top-level directory, and Makefikle.am in src directory.

Here I include the entire Makefile.am file with added parts in blue:


# Note: plugindir is set in configure

##############################################################################
# TODO: change libgstplugin.la to something else, e.g. libmysomething.la     #
##############################################################################
plugin_LTLIBRARIES = libgstfaceoverlay.la

##############################################################################
# TODO: for the next set of variables, name the prefix if you named the .la, #
#  e.g. libmysomething.la => libmysomething_la_SOURCES                       #
#                            libmysomething_la_CFLAGS                        #
#                            libmysomething_la_LIBADD                        #
#                            libmysomething_la_LDFLAGS                       #
##############################################################################

# sources used to compile this plug-in
libgstfaceoverlay_la_SOURCES = gstfaceoverlay.c gstfaceoverlay.h

# compiler and linker flags used to compile this plugin, set in configure.ac
libgstfaceoverlay_la_CFLAGS = $(GST_CFLAGS) $(OPENCV_CFLAGS)
libgstfaceoverlay_la_LIBADD = $(GST_LIBS) $(OPENCV_LIBS) 
libgstfaceoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstfaceoverlay_la_LIBTOOLFLAGS = --tag=disable-static

# headers we need but don't want installed
noinst_HEADERS = gstfaceoverlay.h

Such a simple starter Makefile.am with instructions, such a small change to add. But to discover what had to be changed!

And to configure.ac I added:

dnl *** opencv ***
translit(dnm, m, l) AM_CONDITIONAL(USE_OPENCV, true)

  dnl we specify a max. version too because we set CV_NO_BACKWARD_COMPATIBILITY
  dnl and don't want the build to break when a new opencv version comes out.
  dnl Need to adjust this upwards once we know that our code compiles fine with
  dnl a new version and the no-backward-compatibility define. (There doesn't
  dnl seem to be a switch to suppress the warnings the cvcompat.h header
  dnl causes.)
  PKG_CHECK_MODULES(OPENCV, opencv >= 2.0.0 opencv <= 2.1.0 , [
    AC_PROG_CXX
    AC_LANG_CPLUSPLUS
    OLD_CPPFLAGS=$CPPFLAGS
    CPPFLAGS=$OPENCV_CFLAGS
    AC_CHECK_HEADER(highgui.h, HAVE_HIGHGUI="yes", HAVE_HIGHGUI="no")
    AC_CHECK_HEADER(cvaux.h, HAVE_CVAUX="yes", HAVE_CVAUX="no")
    CPPFLAGS=$OLD_CPPFLAGS
    AC_LANG_C
    if test "x$HAVE_HIGHGUI" = "xno"; then
      AC_MSG_RESULT(highgui.h could not be found.)
      HAVE_OPENCV="no"
    elif test "x$HAVE_CVAUX" = "xno"; then
      AC_MSG_RESULT(cvaux.h could not be found.)
      HAVE_OPENCV="no"
    else
      HAVE_OPENCV="yes" 
      AC_SUBST(OPENCV_CFLAGS)
      AC_SUBST(OPENCV_LIBS)  
    fi
  ], [
    HAVE_OPENCV="no"
    AC_MSG_RESULT(no)
  ])

And I don't even know if this last one is really ok because I placed the AC_SUBST macros inside an if and I don't know if that's valid. All this was made copying from already working projects with similar stuff and changing things until it worked, 9879864 "./autogen.sh &&./configure && make && make install" attempts later.



A programmer in fierce battle against GNU Autotools

How is it possible...? That it's so difficult? I cannot believe it. I felt that I needed to change a lightbulb; the documentation explained the history of electricity, the reference was as big as a manual to build your own nuclear reactor, and the examples explained how to light a candle.
Just this minute I found this: http://smalltalk.gnu.org/blog/bonzinip/all-you-should-really-know-about-autoconf-and-automake
I had to google "GNU Autotools hate" to find it.
And there is a lot more I'd like to say about autotools! But I'll let that for a RANT&RAGE type post, if I ever get to write about it ;)

Thursday, February 3, 2011

Cheese decoration design contest

Cheese will soon include effects that show .svg files above the video (and of course in the captured pictures and recordings). The idea is to add three or four effects with cool designs until an easy way for the user to use his/her own images is implemented.


So far, we have these images:




Adapted from http://www.openclipart.org contributions ( all clipart found there is released under http://creativecommons.org/publicdomain/zero/1.0 ).

Think you can do better? Do you want your design to be chosen and seen by all Cheese users, bringing fame, fortune and complete bliss into your life? =P I promise, if you submit a design, it will rain cookies and marmalade in your country for a week.

Ahem! I mean, submit your design and a strict jury of artists and graphical designers will select the best among the thousands of submissions. It's not likely your design will be selected, competition will be fierce. Maybe, If you are good enough, you know. One of the jurors is Andy Warhol himself. Yep, I have his living brain here in my house in a jar, like a futurama head.

Now seriously, send your design if you want, and I hope I can include it in the list of effect previews. I can't promise anything because I don't know how many collaborations I will receive, but it will make it for sure in the examples folder so the users will have them available when drag-and-drop of svg files is implemented. Of course your name will appear in the credits under "artwork by" tab in "About" dialog.


These images won't move and will be resized automatically according to the video resolution, so any .svg dimension is ok as long as the proportions allow them to look good in commonly used video aspect ratios. (4:3, 3:2, 16:9 I guess, depends on the user's capture device).


So bring them on! ;)

Edit:
Deadline is Feb 16 2011, and Feb 18 the selected images will be announced ;)

Submit your images here: https://bugzilla.gnome.org/show_bug.cgi?id=641444