facedetect: detect face features
Also detect mouth, nose and eyes. Drop faces that don't have them. Fixes leaking the cascades. Adds more docs.
This commit is contained in:
parent
a857c90590
commit
fefa1df8b9
@ -3,6 +3,7 @@
|
|||||||
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
@ -48,14 +49,27 @@
|
|||||||
*
|
*
|
||||||
* Performs face detection on videos and images.
|
* Performs face detection on videos and images.
|
||||||
*
|
*
|
||||||
|
* The image is scaled down multiple times using the GstFacedetect::scale-factor
|
||||||
|
* until the size is <= GstFacedetect::min-size-width or
|
||||||
|
* GstFacedetect::min-size-height.
|
||||||
|
*
|
||||||
* <refsect2>
|
* <refsect2>
|
||||||
* <title>Example launch line</title>
|
* <title>Example launch line</title>
|
||||||
* |[
|
* |[
|
||||||
* gst-launch-0.10 autovideosrc ! decodebin2 ! colorspace ! facedetect ! colorspace ! xvimagesink
|
* gst-launch-0.10 autovideosrc ! decodebin2 ! colorspace ! facedetect ! colorspace ! xvimagesink
|
||||||
* ]|
|
* ]| Detect and show faces
|
||||||
|
* |[
|
||||||
|
* gst-launch-0.10 autovideosrc ! video/x-raw-yuv,width=320,height=240 ! colorspace ! facedetect min-size-width=60 min-size-height=60 ! colorspace ! xvimagesink
|
||||||
|
* ]| Detect large faces on a smaller image
|
||||||
|
*
|
||||||
* </refsect2>
|
* </refsect2>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* FIXME: development version of OpenCV has CV_HAAR_FIND_BIGGEST_OBJECT which
|
||||||
|
* we might want to use if available
|
||||||
|
* see https://code.ros.org/svn/opencv/trunk/opencv/modules/objdetect/src/haar.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif
|
#endif
|
||||||
@ -67,7 +81,10 @@
|
|||||||
GST_DEBUG_CATEGORY_STATIC (gst_facedetect_debug);
|
GST_DEBUG_CATEGORY_STATIC (gst_facedetect_debug);
|
||||||
#define GST_CAT_DEFAULT gst_facedetect_debug
|
#define GST_CAT_DEFAULT gst_facedetect_debug
|
||||||
|
|
||||||
#define DEFAULT_PROFILE "/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml"
|
#define DEFAULT_FACE_PROFILE "/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml"
|
||||||
|
#define DEFAULT_NOSE_PROFILE "/usr/share/opencv/haarcascades/haarcascade_mcs_nose.xml"
|
||||||
|
#define DEFAULT_MOUTH_PROFILE "/usr/share/opencv/haarcascades/haarcascade_mcs_mouth.xml"
|
||||||
|
#define DEFAULT_EYES_PROFILE "/usr/share/opencv/haarcascades/haarcascade_mcs_eyepair_small.xml"
|
||||||
#define DEFAULT_SCALE_FACTOR 1.1
|
#define DEFAULT_SCALE_FACTOR 1.1
|
||||||
#define DEFAULT_FLAGS 0
|
#define DEFAULT_FLAGS 0
|
||||||
#define DEFAULT_MIN_NEIGHBORS 3
|
#define DEFAULT_MIN_NEIGHBORS 3
|
||||||
@ -85,7 +102,10 @@ enum
|
|||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_DISPLAY,
|
PROP_DISPLAY,
|
||||||
PROP_PROFILE,
|
PROP_FACE_PROFILE,
|
||||||
|
PROP_NOSE_PROFILE,
|
||||||
|
PROP_MOUTH_PROFILE,
|
||||||
|
PROP_EYES_PROFILE,
|
||||||
PROP_SCALE_FACTOR,
|
PROP_SCALE_FACTOR,
|
||||||
PROP_MIN_NEIGHBORS,
|
PROP_MIN_NEIGHBORS,
|
||||||
PROP_FLAGS,
|
PROP_FLAGS,
|
||||||
@ -155,7 +175,8 @@ static gboolean gst_facedetect_set_caps (GstOpencvVideoFilter * transform,
|
|||||||
static GstFlowReturn gst_facedetect_transform_ip (GstOpencvVideoFilter * base,
|
static GstFlowReturn gst_facedetect_transform_ip (GstOpencvVideoFilter * base,
|
||||||
GstBuffer * buf, IplImage * img);
|
GstBuffer * buf, IplImage * img);
|
||||||
|
|
||||||
static void gst_facedetect_load_profile (GstFacedetect * filter);
|
static CvHaarClassifierCascade *gst_facedetect_load_profile (GstFacedetect *
|
||||||
|
filter, gchar * profile);
|
||||||
|
|
||||||
/* Clean up */
|
/* Clean up */
|
||||||
static void
|
static void
|
||||||
@ -163,14 +184,24 @@ gst_facedetect_finalize (GObject * obj)
|
|||||||
{
|
{
|
||||||
GstFacedetect *filter = GST_FACEDETECT (obj);
|
GstFacedetect *filter = GST_FACEDETECT (obj);
|
||||||
|
|
||||||
if (filter->cvGray) {
|
if (filter->cvGray)
|
||||||
cvReleaseImage (&filter->cvGray);
|
cvReleaseImage (&filter->cvGray);
|
||||||
}
|
if (filter->cvStorage)
|
||||||
if (filter->cvStorage) {
|
|
||||||
cvReleaseMemStorage (&filter->cvStorage);
|
cvReleaseMemStorage (&filter->cvStorage);
|
||||||
}
|
|
||||||
|
|
||||||
g_free (filter->profile);
|
g_free (filter->face_profile);
|
||||||
|
g_free (filter->nose_profile);
|
||||||
|
g_free (filter->mouth_profile);
|
||||||
|
g_free (filter->eyes_profile);
|
||||||
|
|
||||||
|
if (filter->cvFaceDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvFaceDetect);
|
||||||
|
if (filter->cvNoseDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvNoseDetect);
|
||||||
|
if (filter->cvMouthDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvMouthDetect);
|
||||||
|
if (filter->cvEyesDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvEyesDetect);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
G_OBJECT_CLASS (parent_class)->finalize (obj);
|
||||||
}
|
}
|
||||||
@ -215,10 +246,24 @@ gst_facedetect_class_init (GstFacedetectClass * klass)
|
|||||||
g_param_spec_boolean ("display", "Display",
|
g_param_spec_boolean ("display", "Display",
|
||||||
"Sets whether the detected faces should be highlighted in the output",
|
"Sets whether the detected faces should be highlighted in the output",
|
||||||
TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
g_object_class_install_property (gobject_class, PROP_PROFILE,
|
|
||||||
g_param_spec_string ("profile", "Profile",
|
g_object_class_install_property (gobject_class, PROP_FACE_PROFILE,
|
||||||
|
g_param_spec_string ("profile", "Face profile",
|
||||||
"Location of Haar cascade file to use for face detection",
|
"Location of Haar cascade file to use for face detection",
|
||||||
DEFAULT_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
DEFAULT_FACE_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_NOSE_PROFILE,
|
||||||
|
g_param_spec_string ("nose-profile", "Nose profile",
|
||||||
|
"Location of Haar cascade file to use for nose detection",
|
||||||
|
DEFAULT_NOSE_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_MOUTH_PROFILE,
|
||||||
|
g_param_spec_string ("mouth-profile", "Mouth profile",
|
||||||
|
"Location of Haar cascade file to use for mouth detection",
|
||||||
|
DEFAULT_MOUTH_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_EYES_PROFILE,
|
||||||
|
g_param_spec_string ("eyes-profile", "Eyes profile",
|
||||||
|
"Location of Haar cascade file to use for eye-pair detection",
|
||||||
|
DEFAULT_EYES_PROFILE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_FLAGS,
|
g_object_class_install_property (gobject_class, PROP_FLAGS,
|
||||||
g_param_spec_flags ("flags", "Flags", "Flags to cvHaarDetectObjects",
|
g_param_spec_flags ("flags", "Flags", "Flags to cvHaarDetectObjects",
|
||||||
GST_TYPE_OPENCV_FACE_DETECT_FLAGS, DEFAULT_FLAGS,
|
GST_TYPE_OPENCV_FACE_DETECT_FLAGS, DEFAULT_FLAGS,
|
||||||
@ -244,21 +289,29 @@ gst_facedetect_class_init (GstFacedetectClass * klass)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* initialize the new element
|
/* initialize the new element
|
||||||
* instantiate pads and add them to element
|
|
||||||
* set pad calback functions
|
|
||||||
* initialize instance structure
|
* initialize instance structure
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
gst_facedetect_init (GstFacedetect * filter, GstFacedetectClass * gclass)
|
gst_facedetect_init (GstFacedetect * filter, GstFacedetectClass * gclass)
|
||||||
{
|
{
|
||||||
filter->profile = g_strdup (DEFAULT_PROFILE);
|
filter->face_profile = g_strdup (DEFAULT_FACE_PROFILE);
|
||||||
|
filter->nose_profile = g_strdup (DEFAULT_NOSE_PROFILE);
|
||||||
|
filter->mouth_profile = g_strdup (DEFAULT_MOUTH_PROFILE);
|
||||||
|
filter->eyes_profile = g_strdup (DEFAULT_EYES_PROFILE);
|
||||||
filter->display = TRUE;
|
filter->display = TRUE;
|
||||||
filter->scale_factor = DEFAULT_SCALE_FACTOR;
|
filter->scale_factor = DEFAULT_SCALE_FACTOR;
|
||||||
filter->min_neighbors = DEFAULT_MIN_NEIGHBORS;
|
filter->min_neighbors = DEFAULT_MIN_NEIGHBORS;
|
||||||
filter->flags = DEFAULT_FLAGS;
|
filter->flags = DEFAULT_FLAGS;
|
||||||
filter->min_size_width = DEFAULT_MIN_SIZE_WIDTH;
|
filter->min_size_width = DEFAULT_MIN_SIZE_WIDTH;
|
||||||
filter->min_size_height = DEFAULT_MIN_SIZE_HEIGHT;
|
filter->min_size_height = DEFAULT_MIN_SIZE_HEIGHT;
|
||||||
gst_facedetect_load_profile (filter);
|
filter->cvFaceDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->face_profile);
|
||||||
|
filter->cvNoseDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->nose_profile);
|
||||||
|
filter->cvMouthDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->mouth_profile);
|
||||||
|
filter->cvEyesDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->eyes_profile);
|
||||||
|
|
||||||
gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
|
gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
|
||||||
TRUE);
|
TRUE);
|
||||||
@ -271,10 +324,37 @@ gst_facedetect_set_property (GObject * object, guint prop_id,
|
|||||||
GstFacedetect *filter = GST_FACEDETECT (object);
|
GstFacedetect *filter = GST_FACEDETECT (object);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_PROFILE:
|
case PROP_FACE_PROFILE:
|
||||||
g_free (filter->profile);
|
g_free (filter->face_profile);
|
||||||
filter->profile = g_value_dup_string (value);
|
if (filter->cvFaceDetect)
|
||||||
gst_facedetect_load_profile (filter);
|
cvReleaseHaarClassifierCascade (&filter->cvFaceDetect);
|
||||||
|
filter->face_profile = g_value_dup_string (value);
|
||||||
|
filter->cvFaceDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->face_profile);
|
||||||
|
break;
|
||||||
|
case PROP_NOSE_PROFILE:
|
||||||
|
g_free (filter->nose_profile);
|
||||||
|
if (filter->cvNoseDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvNoseDetect);
|
||||||
|
filter->nose_profile = g_value_dup_string (value);
|
||||||
|
filter->cvNoseDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->nose_profile);
|
||||||
|
break;
|
||||||
|
case PROP_MOUTH_PROFILE:
|
||||||
|
g_free (filter->mouth_profile);
|
||||||
|
if (filter->cvMouthDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvMouthDetect);
|
||||||
|
filter->mouth_profile = g_value_dup_string (value);
|
||||||
|
filter->cvMouthDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->mouth_profile);
|
||||||
|
break;
|
||||||
|
case PROP_EYES_PROFILE:
|
||||||
|
g_free (filter->eyes_profile);
|
||||||
|
if (filter->cvEyesDetect)
|
||||||
|
cvReleaseHaarClassifierCascade (&filter->cvEyesDetect);
|
||||||
|
filter->eyes_profile = g_value_dup_string (value);
|
||||||
|
filter->cvEyesDetect =
|
||||||
|
gst_facedetect_load_profile (filter, filter->eyes_profile);
|
||||||
break;
|
break;
|
||||||
case PROP_DISPLAY:
|
case PROP_DISPLAY:
|
||||||
filter->display = g_value_get_boolean (value);
|
filter->display = g_value_get_boolean (value);
|
||||||
@ -307,8 +387,17 @@ gst_facedetect_get_property (GObject * object, guint prop_id,
|
|||||||
GstFacedetect *filter = GST_FACEDETECT (object);
|
GstFacedetect *filter = GST_FACEDETECT (object);
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_PROFILE:
|
case PROP_FACE_PROFILE:
|
||||||
g_value_set_string (value, filter->profile);
|
g_value_set_string (value, filter->face_profile);
|
||||||
|
break;
|
||||||
|
case PROP_NOSE_PROFILE:
|
||||||
|
g_value_set_string (value, filter->nose_profile);
|
||||||
|
break;
|
||||||
|
case PROP_MOUTH_PROFILE:
|
||||||
|
g_value_set_string (value, filter->mouth_profile);
|
||||||
|
break;
|
||||||
|
case PROP_EYES_PROFILE:
|
||||||
|
g_value_set_string (value, filter->eyes_profile);
|
||||||
break;
|
break;
|
||||||
case PROP_DISPLAY:
|
case PROP_DISPLAY:
|
||||||
g_value_set_boolean (value, filter->display);
|
g_value_set_boolean (value, filter->display);
|
||||||
@ -391,17 +480,27 @@ gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
|
|||||||
{
|
{
|
||||||
GstFacedetect *filter = GST_FACEDETECT (base);
|
GstFacedetect *filter = GST_FACEDETECT (base);
|
||||||
|
|
||||||
if (filter->cvCascade) {
|
if (filter->cvFaceDetect) {
|
||||||
GstMessage *msg = NULL;
|
GstMessage *msg = NULL;
|
||||||
GValue facelist = { 0 };
|
GValue facelist = { 0 };
|
||||||
CvSeq *faces;
|
CvSeq *faces;
|
||||||
|
CvSeq *mouth, *nose, *eyes;
|
||||||
gint i;
|
gint i;
|
||||||
|
gboolean do_display = FALSE;
|
||||||
|
|
||||||
|
if (filter->display) {
|
||||||
|
if (gst_buffer_is_writable (buf)) {
|
||||||
|
do_display = TRUE;
|
||||||
|
} else {
|
||||||
|
GST_LOG_OBJECT (filter, "Buffer is not writable, not drawing faces.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cvCvtColor (img, filter->cvGray, CV_RGB2GRAY);
|
cvCvtColor (img, filter->cvGray, CV_RGB2GRAY);
|
||||||
cvClearMemStorage (filter->cvStorage);
|
cvClearMemStorage (filter->cvStorage);
|
||||||
|
|
||||||
faces =
|
faces =
|
||||||
cvHaarDetectObjects (filter->cvGray, filter->cvCascade,
|
cvHaarDetectObjects (filter->cvGray, filter->cvFaceDetect,
|
||||||
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
filter->flags, cvSize (filter->min_size_width, filter->min_size_height)
|
filter->flags, cvSize (filter->min_size_width, filter->min_size_height)
|
||||||
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
@ -417,43 +516,167 @@ gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
|
|||||||
for (i = 0; i < (faces ? faces->total : 0); i++) {
|
for (i = 0; i < (faces ? faces->total : 0); i++) {
|
||||||
CvRect *r = (CvRect *) cvGetSeqElem (faces, i);
|
CvRect *r = (CvRect *) cvGetSeqElem (faces, i);
|
||||||
GValue value = { 0 };
|
GValue value = { 0 };
|
||||||
GstStructure *s = gst_structure_new ("face",
|
GstStructure *s;
|
||||||
|
guint mw = filter->min_size_width / 8;
|
||||||
|
guint mh = filter->min_size_height / 8;
|
||||||
|
guint rnx, rny, rnw, rnh;
|
||||||
|
guint rmx, rmy, rmw, rmh;
|
||||||
|
guint rex, rey, rew, reh;
|
||||||
|
gboolean have_nose, have_mouth, have_eyes;
|
||||||
|
|
||||||
|
/* detect face features */
|
||||||
|
|
||||||
|
rnx = r->x + r->width / 4;
|
||||||
|
rny = r->y + r->height / 4;
|
||||||
|
rnw = r->width / 2;
|
||||||
|
rnh = r->height / 2;
|
||||||
|
cvSetImageROI (filter->cvGray, cvRect (rnx, rny, rnw, rnh));
|
||||||
|
nose =
|
||||||
|
cvHaarDetectObjects (filter->cvGray, filter->cvNoseDetect,
|
||||||
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
|
filter->flags, cvSize (mw, mh)
|
||||||
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
|
, cvSize (mw + 2, mh + 2)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
have_nose = (nose && nose->total);
|
||||||
|
cvResetImageROI (filter->cvGray);
|
||||||
|
|
||||||
|
rmx = r->x;
|
||||||
|
rmy = r->y + r->height / 2;
|
||||||
|
rmw = r->width;
|
||||||
|
rmh = r->height / 2;
|
||||||
|
cvSetImageROI (filter->cvGray, cvRect (rmx, rmy, rmw, rmh));
|
||||||
|
mouth =
|
||||||
|
cvHaarDetectObjects (filter->cvGray, filter->cvMouthDetect,
|
||||||
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
|
filter->flags, cvSize (mw, mh)
|
||||||
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
|
, cvSize (mw + 2, mh + 2)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
have_mouth = (mouth && mouth->total);
|
||||||
|
cvResetImageROI (filter->cvGray);
|
||||||
|
|
||||||
|
rex = r->x;
|
||||||
|
rey = r->y;
|
||||||
|
rew = r->width;
|
||||||
|
reh = r->height / 2;
|
||||||
|
cvSetImageROI (filter->cvGray, cvRect (rex, rey, rew, reh));
|
||||||
|
eyes =
|
||||||
|
cvHaarDetectObjects (filter->cvGray, filter->cvEyesDetect,
|
||||||
|
filter->cvStorage, filter->scale_factor, filter->min_neighbors,
|
||||||
|
filter->flags, cvSize (mw, mh)
|
||||||
|
#if (CV_MAJOR_VERSION >= 2) && (CV_MINOR_VERSION >= 2)
|
||||||
|
, cvSize (mw + 2, mh + 2)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
have_eyes = (eyes && eyes->total);
|
||||||
|
cvResetImageROI (filter->cvGray);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (filter,
|
||||||
|
"%2d/%2d: x,y = %4u,%4u: w.h = %4u,%4u : features(e,n,m) = %d,%d,%d",
|
||||||
|
i, faces->total, r->x, r->y, r->width, r->height,
|
||||||
|
have_eyes, have_nose, have_mouth);
|
||||||
|
|
||||||
|
/* ignore 'face' where we don't fix mount/nose/eyes ? */
|
||||||
|
if (!(have_eyes && have_nose && have_mouth))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
s = gst_structure_new ("face",
|
||||||
"x", G_TYPE_UINT, r->x,
|
"x", G_TYPE_UINT, r->x,
|
||||||
"y", G_TYPE_UINT, r->y,
|
"y", G_TYPE_UINT, r->y,
|
||||||
"width", G_TYPE_UINT, r->width,
|
"width", G_TYPE_UINT, r->width,
|
||||||
"height", G_TYPE_UINT, r->height, NULL);
|
"height", G_TYPE_UINT, r->height, NULL);
|
||||||
|
if (nose && nose->total) {
|
||||||
GST_LOG_OBJECT (filter, "%2d/%2d: x,y = %4u,%4u: w.h = %4u,%4u", i,
|
CvRect *sr = (CvRect *) cvGetSeqElem (nose, 0);
|
||||||
faces->total, r->x, r->y, r->width, r->height);
|
GST_LOG_OBJECT (filter, "nose/%d: x,y = %4u,%4u: w.h = %4u,%4u",
|
||||||
|
nose->total, rnx + sr->x, rny + sr->y, sr->width, sr->height);
|
||||||
|
gst_structure_set (s,
|
||||||
|
"nose->x", G_TYPE_UINT, rnx + sr->x,
|
||||||
|
"nose->y", G_TYPE_UINT, rny + sr->y,
|
||||||
|
"nose->width", G_TYPE_UINT, sr->width,
|
||||||
|
"nose->height", G_TYPE_UINT, sr->height, NULL);
|
||||||
|
}
|
||||||
|
if (mouth && mouth->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (mouth, 0);
|
||||||
|
GST_LOG_OBJECT (filter, "mouth/%d: x,y = %4u,%4u: w.h = %4u,%4u",
|
||||||
|
mouth->total, rmx + sr->x, rmy + sr->y, sr->width, sr->height);
|
||||||
|
gst_structure_set (s,
|
||||||
|
"mouth->x", G_TYPE_UINT, rmx + sr->x,
|
||||||
|
"mouth->y", G_TYPE_UINT, rmy + sr->y,
|
||||||
|
"mouth->width", G_TYPE_UINT, sr->width,
|
||||||
|
"mouth->height", G_TYPE_UINT, sr->height, NULL);
|
||||||
|
}
|
||||||
|
if (eyes && eyes->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (eyes, 0);
|
||||||
|
GST_LOG_OBJECT (filter, "eyes/%d: x,y = %4u,%4u: w.h = %4u,%4u",
|
||||||
|
eyes->total, rex + sr->x, rey + sr->y, sr->width, sr->height);
|
||||||
|
gst_structure_set (s,
|
||||||
|
"eyes->x", G_TYPE_UINT, rex + sr->x,
|
||||||
|
"eyes->y", G_TYPE_UINT, rey + sr->y,
|
||||||
|
"eyes->width", G_TYPE_UINT, sr->width,
|
||||||
|
"eyes->height", G_TYPE_UINT, sr->height, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
g_value_init (&value, GST_TYPE_STRUCTURE);
|
g_value_init (&value, GST_TYPE_STRUCTURE);
|
||||||
gst_value_set_structure (&value, s);
|
gst_value_set_structure (&value, s);
|
||||||
gst_value_list_append_value (&facelist, &value);
|
gst_value_list_append_value (&facelist, &value);
|
||||||
g_value_unset (&value);
|
g_value_unset (&value);
|
||||||
}
|
|
||||||
if (filter->display) {
|
|
||||||
if (gst_buffer_is_writable (buf)) {
|
|
||||||
/* draw colored circles for each face */
|
|
||||||
for (i = 0; i < (faces ? faces->total : 0); i++) {
|
|
||||||
CvRect *r = (CvRect *) cvGetSeqElem (faces, i);
|
|
||||||
CvPoint center;
|
|
||||||
CvSize axes;
|
|
||||||
gdouble w = r->width * 0.5;
|
|
||||||
gdouble h = r->height * 0.6; /* tweak for face form */
|
|
||||||
gint cb = 255 - ((i & 3) << 7);
|
|
||||||
gint cg = 255 - ((i & 12) << 5);
|
|
||||||
gint cr = 255 - ((i & 48) << 3);
|
|
||||||
|
|
||||||
center.x = cvRound ((r->x + w));
|
if (do_display) {
|
||||||
center.y = cvRound ((r->y + h));
|
CvPoint center;
|
||||||
|
CvSize axes;
|
||||||
|
gdouble w, h;
|
||||||
|
gint cb = 255 - ((i & 3) << 7);
|
||||||
|
gint cg = 255 - ((i & 12) << 5);
|
||||||
|
gint cr = 255 - ((i & 48) << 3);
|
||||||
|
|
||||||
|
w = r->width / 2;
|
||||||
|
h = r->height / 2;
|
||||||
|
center.x = cvRound ((r->x + w));
|
||||||
|
center.y = cvRound ((r->y + h));
|
||||||
|
axes.width = w;
|
||||||
|
axes.height = h * 1.25; /* tweak for face form */
|
||||||
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
|
3, 8, 0);
|
||||||
|
|
||||||
|
if (nose && nose->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (nose, 0);
|
||||||
|
|
||||||
|
w = sr->width / 2;
|
||||||
|
h = sr->height / 2;
|
||||||
|
center.x = cvRound ((rnx + sr->x + w));
|
||||||
|
center.y = cvRound ((rny + sr->y + h));
|
||||||
axes.width = w;
|
axes.width = w;
|
||||||
|
axes.height = h * 1.25; /* tweak for nose form */
|
||||||
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
|
1, 8, 0);
|
||||||
|
}
|
||||||
|
if (mouth && mouth->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (mouth, 0);
|
||||||
|
|
||||||
|
w = sr->width / 2;
|
||||||
|
h = sr->height / 2;
|
||||||
|
center.x = cvRound ((rmx + sr->x + w));
|
||||||
|
center.y = cvRound ((rmy + sr->y + h));
|
||||||
|
axes.width = w * 1.5; /* tweak for mouth form */
|
||||||
axes.height = h;
|
axes.height = h;
|
||||||
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
3, 8, 0);
|
1, 8, 0);
|
||||||
|
}
|
||||||
|
if (eyes && eyes->total) {
|
||||||
|
CvRect *sr = (CvRect *) cvGetSeqElem (eyes, 0);
|
||||||
|
|
||||||
|
w = sr->width / 2;
|
||||||
|
h = sr->height / 2;
|
||||||
|
center.x = cvRound ((rex + sr->x + w));
|
||||||
|
center.y = cvRound ((rey + sr->y + h));
|
||||||
|
axes.width = w * 1.5; /* tweak for eyes form */
|
||||||
|
axes.height = h;
|
||||||
|
cvEllipse (img, center, axes, 0.0, 0.0, 360.0, CV_RGB (cr, cg, cb),
|
||||||
|
1, 8, 0);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
GST_LOG_OBJECT (filter, "Buffer is not writable, not drawing "
|
|
||||||
"circles for faces");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,14 +691,16 @@ gst_facedetect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static CvHaarClassifierCascade *
|
||||||
gst_facedetect_load_profile (GstFacedetect * filter)
|
gst_facedetect_load_profile (GstFacedetect * filter, gchar * profile)
|
||||||
{
|
{
|
||||||
filter->cvCascade =
|
CvHaarClassifierCascade *cascade;
|
||||||
(CvHaarClassifierCascade *) cvLoad (filter->profile, 0, 0, 0);
|
|
||||||
if (!filter->cvCascade) {
|
if (!(cascade = (CvHaarClassifierCascade *) cvLoad (profile, 0, 0, 0))) {
|
||||||
GST_WARNING ("Couldn't load Haar classifier cascade: %s.", filter->profile);
|
GST_WARNING_OBJECT (filter, "Couldn't load Haar classifier cascade: %s.",
|
||||||
|
profile);
|
||||||
}
|
}
|
||||||
|
return cascade;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
* Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
||||||
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
* Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
* Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
|
||||||
|
* Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
@ -75,7 +76,10 @@ struct _GstFacedetect
|
|||||||
|
|
||||||
gboolean display;
|
gboolean display;
|
||||||
|
|
||||||
gchar *profile;
|
gchar *face_profile;
|
||||||
|
gchar *nose_profile;
|
||||||
|
gchar *mouth_profile;
|
||||||
|
gchar *eyes_profile;
|
||||||
gdouble scale_factor;
|
gdouble scale_factor;
|
||||||
gint min_neighbors;
|
gint min_neighbors;
|
||||||
gint flags;
|
gint flags;
|
||||||
@ -83,7 +87,10 @@ struct _GstFacedetect
|
|||||||
gint min_size_height;
|
gint min_size_height;
|
||||||
|
|
||||||
IplImage *cvGray;
|
IplImage *cvGray;
|
||||||
CvHaarClassifierCascade *cvCascade;
|
CvHaarClassifierCascade *cvFaceDetect;
|
||||||
|
CvHaarClassifierCascade *cvNoseDetect;
|
||||||
|
CvHaarClassifierCascade *cvMouthDetect;
|
||||||
|
CvHaarClassifierCascade *cvEyesDetect;
|
||||||
CvMemStorage *cvStorage;
|
CvMemStorage *cvStorage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user