diff options
author | Reynaldo H. Verdejo Pinochet <reynaldo@osg.samsung.com> | 2017-11-15 10:30:46 -0800 |
---|---|---|
committer | Reynaldo H. Verdejo Pinochet <reynaldo@osg.samsung.com> | 2017-12-21 16:32:22 -0800 |
commit | 5e8bd0fa3f741aac9923872546b0bf4e25cc6ddf (patch) | |
tree | f31f1499636750444c6d479d2d50d3a36efd5402 | |
parent | 191c8742d3cfde62ef837760fb5ca6d4bc226f1e (diff) |
opencv: facedetect: DO NOT UPSTREAM: Rebased mods to facedetect
-rw-r--r-- | ext/opencv/gstfacedetect.cpp | 188 | ||||
-rw-r--r-- | ext/opencv/gstfacedetect.h | 4 |
2 files changed, 169 insertions, 23 deletions
diff --git a/ext/opencv/gstfacedetect.cpp b/ext/opencv/gstfacedetect.cpp index 503b9e70f..05227faa2 100644 --- a/ext/opencv/gstfacedetect.cpp +++ b/ext/opencv/gstfacedetect.cpp @@ -91,10 +91,12 @@ GST_DEBUG_CATEGORY_STATIC (gst_face_detect_debug); #define HAAR_CASCADES_DIR OPENCV_PREFIX G_DIR_SEPARATOR_S "share" \ G_DIR_SEPARATOR_S OPENCV_PATH_NAME G_DIR_SEPARATOR_S "haarcascades" \ G_DIR_SEPARATOR_S -#define DEFAULT_FACE_PROFILE HAAR_CASCADES_DIR "haarcascade_frontalface_default.xml" -#define DEFAULT_NOSE_PROFILE HAAR_CASCADES_DIR "haarcascade_mcs_nose.xml" -#define DEFAULT_MOUTH_PROFILE HAAR_CASCADES_DIR "haarcascade_mcs_mouth.xml" -#define DEFAULT_EYES_PROFILE HAAR_CASCADES_DIR "haarcascade_mcs_eyepair_small.xml" +#define DEFAULT_FACE_PROFILE "haarcascade_frontalface_default.xml" +#define DEFAULT_NOSE_PROFILE "haarcascade_mcs_nose.xml" +#define DEFAULT_MOUTH_PROFILE "haarcascade_mcs_mouth.xml" +#define DEFAULT_EYES_PROFILE "haarcascade_mcs_eyepair_small.xml" +#define DEFAULT_EAR_L_PROFILE "haarcascade_mcs_leftear.xml" +#define DEFAULT_EAR_R_PROFILE "haarcascade_mcs_rightear.xml" #define DEFAULT_SCALE_FACTOR 1.25 #define DEFAULT_FLAGS CV_HAAR_DO_CANNY_PRUNING #define DEFAULT_MIN_NEIGHBORS 3 @@ -127,6 +129,12 @@ enum PROP_MIN_STDDEV }; +enum +{ + DIRECTION_INCONCLUSIVE = 0, + DIRECTION_RIGHT, + DIRECTION_LEFT +}; /* * GstOpencvFaceDetectFlags: @@ -252,6 +260,8 @@ gst_face_detect_finalize (GObject * obj) g_free (filter->nose_profile); g_free (filter->mouth_profile); g_free (filter->eyes_profile); + g_free (filter->ear_l_profile); + g_free (filter->ear_r_profile); if (filter->cvFaceDetect) delete (filter->cvFaceDetect); @@ -261,6 +271,10 @@ gst_face_detect_finalize (GObject * obj) delete (filter->cvMouthDetect); if (filter->cvEyesDetect) delete (filter->cvEyesDetect); + if (filter->cvEarLDetect) + delete (filter->cvEarLDetect); + if (filter->cvEarRDetect) + delete (filter->cvEarRDetect); G_OBJECT_CLASS (gst_face_detect_parent_class)->finalize (obj); } @@ -291,22 +305,22 @@ gst_face_detect_class_init (GstFaceDetectClass * klass) 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", - DEFAULT_FACE_PROFILE, + HAAR_CASCADES_DIR DEFAULT_FACE_PROFILE, (GParamFlags) (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, + HAAR_CASCADES_DIR DEFAULT_NOSE_PROFILE, (GParamFlags) (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, + HAAR_CASCADES_DIR DEFAULT_MOUTH_PROFILE, (GParamFlags) (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, + HAAR_CASCADES_DIR DEFAULT_EYES_PROFILE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_FLAGS, @@ -363,10 +377,17 @@ gst_face_detect_class_init (GstFaceDetectClass * klass) static void gst_face_detect_init (GstFaceDetect * filter) { - 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); + + const gchar *haar_path; + haar_path = g_getenv ("GST_HAAR_CASCADES_PATH")? + g_getenv ("GST_HAAR_CASCADES_PATH") : HAAR_CASCADES_DIR; + + filter->face_profile = g_strconcat (haar_path, DEFAULT_FACE_PROFILE, NULL); + filter->nose_profile = g_strconcat (haar_path, DEFAULT_NOSE_PROFILE, NULL); + filter->mouth_profile = g_strconcat (haar_path, DEFAULT_MOUTH_PROFILE, NULL); + filter->eyes_profile = g_strconcat (haar_path, DEFAULT_EYES_PROFILE, NULL); + filter->ear_r_profile = g_strconcat (haar_path, DEFAULT_EAR_R_PROFILE, NULL); + filter->ear_l_profile = g_strconcat (haar_path, DEFAULT_EAR_L_PROFILE, NULL); filter->display = TRUE; filter->face_detected = FALSE; filter->scale_factor = DEFAULT_SCALE_FACTOR; @@ -383,6 +404,10 @@ gst_face_detect_init (GstFaceDetect * filter) gst_face_detect_load_profile (filter, filter->mouth_profile); filter->cvEyesDetect = gst_face_detect_load_profile (filter, filter->eyes_profile); + filter->cvEarLDetect = + gst_face_detect_load_profile (filter, filter->ear_l_profile); + filter->cvEarRDetect = + gst_face_detect_load_profile (filter, filter->ear_r_profile); gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter), TRUE); @@ -572,6 +597,37 @@ gst_face_detect_run_detector (GstFaceDetect * filter, } } +/** + * Delta percentaje after wich position is considered off-center + */ +#define LINEAR_DAMPING_FACTOR 5 + +/** + * @gst_face_detect_get_leaning_side + * + * Figures out what side of a reference a certain cuantity falls into + * @pos: Object x coordinate + * @ref_pos: Reference x coordinate + * @side: result. DIRECTION_RIGHT or DIRECTION_LEFT. Untouched if inconclusive + * @displacement: Magnitude of min to max ramp + */ +static inline void +gst_face_detect_get_leaning_side (guint *side, gint reference, gint position, + guint displacement) { + + g_assert (side); + + displacement = displacement * LINEAR_DAMPING_FACTOR / 100; + + if ((guint)ABS (position - reference) <= displacement) + return; + + if (position > reference) + *side = DIRECTION_LEFT; + else if (position < reference) + *side = DIRECTION_RIGHT; +} + /* * Performs the face detection */ @@ -590,6 +646,8 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, vector < Rect > mouth; vector < Rect > nose; vector < Rect > eyes; + vector < Rect > r_ears; + vector < Rect > l_ears; gboolean post_msg = FALSE; Mat mtxOrg (cv::cvarrToMat (img)); @@ -644,8 +702,10 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, guint rnx = 0, rny = 0, rnw, rnh; guint rmx = 0, rmy = 0, rmw, rmh; guint rex = 0, rey = 0, rew, reh; + guint relx = 0, rely = 0, relw, relh; + guint rerx = 0, rery = 0, rerw, rerh; guint rhh = r.height / 2; - gboolean have_nose, have_mouth, have_eyes; + gboolean have_nose, have_mouth, have_eyes, have_l_ears, have_r_ears; /* detect face features */ @@ -685,11 +745,35 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, have_eyes = FALSE; } + if (filter->cvEarLDetect) { + relx = r.x; + rely = r.y; + relw = r.width; + relh = rhh; + gst_face_detect_run_detector (filter, filter->cvEarLDetect, mw, mh, + Rect (relx, rely, relw, relh), l_ears); + have_l_ears = !l_ears.empty (); + } else { + have_l_ears = FALSE; + } + + if (filter->cvEarRDetect) { + rerx = r.x; + rery = r.y; + rerw = r.width; + rerh = rhh; + gst_face_detect_run_detector (filter, filter->cvEarRDetect, mw, mh, + Rect (rerx, rery, rerw, rerh), r_ears); + have_r_ears = !r_ears.empty (); + } else { + have_r_ears = FALSE; + } + GST_LOG_OBJECT (filter, "%2d/%2" G_GSIZE_FORMAT - ": x,y = %4u,%4u: w.h = %4u,%4u : features(e,n,m) = %d,%d,%d", i, - faces.size (), r.x, r.y, r.width, r.height, have_eyes, have_nose, - have_mouth); + ": x,y = %4u,%4u: w.h = %4u,%4u : features(e,n,m) = %d,%d,%d,%d,%d", + i, faces.size (), r.x, r.y, r.width, r.height, have_eyes, have_nose, + have_mouth, have_r_ears, have_l_ears); if (post_msg) { s = gst_structure_new ("face", "x", G_TYPE_UINT, r.x, @@ -702,6 +786,10 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, structure_and_message (mouth, "mouth", rmx, rmy, filter, s); if (have_eyes) structure_and_message (eyes, "eyes", rex, rey, filter, s); + if (have_l_ears) + structure_and_message (l_ears, "left ears", relx, rely, filter, s); + if (have_r_ears) + structure_and_message (r_ears, "right ears", rerx, rery, filter, s); g_value_init (&facedata, GST_TYPE_STRUCTURE); g_value_take_boxed (&facedata, s); @@ -711,20 +799,21 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, } if (filter->display) { - CvPoint center; - Size axes; + CvPoint f_center, center; + Size f_axes, axes; gdouble w, h; + guint aim = DIRECTION_INCONCLUSIVE; + 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 */ - ellipse (mtxOrg, center, axes, 0, 0, 360, Scalar (cr, cg, cb), 3, 8, 0); + f_center.x = cvRound ((r.x + w)); + f_center.y = cvRound ((r.y + h)); + f_axes.width = w; + f_axes.height = h * 1.25; /* tweak for face form */ if (have_nose) { Rect sr = nose[0]; @@ -733,11 +822,17 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, h = sr.height / 2; center.x = cvRound ((rnx + sr.x + w)); center.y = cvRound ((rny + sr.y + h)); + + /* left/right toggle */ + gst_face_detect_get_leaning_side (&aim, f_center.x, center.x, + r.width); + axes.width = w; axes.height = h * 1.25; /* tweak for nose form */ ellipse (mtxOrg, center, axes, 0, 0, 360, Scalar (cr, cg, cb), 1, 8, 0); } + if (have_mouth) { Rect sr = mouth[0]; @@ -745,11 +840,34 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, h = sr.height / 2; center.x = cvRound ((rmx + sr.x + w)); center.y = cvRound ((rmy + sr.y + h)); + + if (!aim) { + gst_face_detect_get_leaning_side (&aim, f_center.x, center.x, + r.width); + } + axes.width = w * 1.5; /* tweak for mouth form */ axes.height = h; ellipse (mtxOrg, center, axes, 0, 0, 360, Scalar (cr, cg, cb), 1, 8, 0); } + + /* Face ellipse */ + switch (aim) { + case DIRECTION_RIGHT: + ellipse (mtxOrg, f_center, f_axes, 0, 0, 360, Scalar (cr, 0, 0), + 3, 8, 0); + break; + case DIRECTION_LEFT: + ellipse (mtxOrg, f_center, f_axes, 0, 0, 360, Scalar (0, 0, cb), + 3, 8, 0); + break; + case DIRECTION_INCONCLUSIVE: + default: + ellipse (mtxOrg, f_center, f_axes, 0, 0, 360, Scalar (cr, cg, cb), + 3, 8, 0); + } + if (have_eyes) { Rect sr = eyes[0]; @@ -762,6 +880,30 @@ gst_face_detect_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf, ellipse (mtxOrg, center, axes, 0, 0, 360, Scalar (cr, cg, cb), 1, 8, 0); } + if (have_r_ears) { + Rect sr = r_ears[0]; + + w = sr.width / 2; + h = sr.height / 2; + center.x = cvRound ((rerx + sr.x + w)); + center.y = cvRound ((rery + sr.y + h)); + axes.width = w * 1.25; /* tweak for ears form */ + axes.height = h; + ellipse (mtxOrg, center, axes, 0, 0, 360, Scalar (cr, cg, cb), 1, 8, + 0); + } + if (have_l_ears) { + Rect sr = l_ears[0]; + + w = sr.width / 2; + h = sr.height / 2; + center.x = cvRound ((relx + sr.x + w)); + center.y = cvRound ((rely + sr.y + h)); + axes.width = w * 1.25; /* tweak for ears form */ + axes.height = h; + ellipse (mtxOrg, center, axes, 0, 0, 360, Scalar (cr, cg, cb), 1, 8, + 0); + } } gst_buffer_add_video_region_of_interest_meta (buf, "face", (guint) r.x, (guint) r.y, (guint) r.width, (guint) r.height); diff --git a/ext/opencv/gstfacedetect.h b/ext/opencv/gstfacedetect.h index 03cf81c2e..81477d8fc 100644 --- a/ext/opencv/gstfacedetect.h +++ b/ext/opencv/gstfacedetect.h @@ -96,6 +96,8 @@ struct _GstFaceDetect gchar *nose_profile; gchar *mouth_profile; gchar *eyes_profile; + gchar *ear_l_profile; + gchar *ear_r_profile; gdouble scale_factor; gint min_neighbors; gint flags; @@ -109,6 +111,8 @@ struct _GstFaceDetect cv::CascadeClassifier *cvNoseDetect; cv::CascadeClassifier *cvMouthDetect; cv::CascadeClassifier *cvEyesDetect; + cv::CascadeClassifier *cvEarLDetect; + cv::CascadeClassifier *cvEarRDetect; }; struct _GstFaceDetectClass |