1 /*
2 * GStreamer
3 * Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
4 * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
5 * Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
6 * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * Alternatively, the contents of this file may be used under the
27 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28 * which case the following provisions apply instead of the ones
29 * mentioned above:
30 *
31 * This library is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU Library General Public
33 * License as published by the Free Software Foundation; either
34 * version 2 of the License, or (at your option) any later version.
35 *
36 * This library is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 * Library General Public License for more details.
40 *
41 * You should have received a copy of the GNU Library General Public
42 * License along with this library; if not, write to the
43 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
44 * Boston, MA 02110-1301, USA.
45 */
46
47 /**
48 * SECTION:element-faceblur
49 *
50 * Blurs faces in images and videos.
51 *
52 * <refsect2>
53 * <title>Example launch line</title>
54 * |[
55 * gst-launch-1.0 autovideosrc ! videoconvert ! faceblur ! videoconvert ! autovideosink
56 * ]|
57 * </refsect2>
58 */
59
60 #ifdef HAVE_CONFIG_H
61 # include <config.h>
62 #endif
63
64 #include <vector>
65
66 #include "gstfaceblur.h"
67 #include <opencv2/imgproc.hpp>
68
69 GST_DEBUG_CATEGORY_STATIC (gst_face_blur_debug);
70 #define GST_CAT_DEFAULT gst_face_blur_debug
71
72 #define DEFAULT_PROFILE OPENCV_PREFIX G_DIR_SEPARATOR_S "share" \
73 G_DIR_SEPARATOR_S OPENCV_PATH_NAME G_DIR_SEPARATOR_S "haarcascades" \
74 G_DIR_SEPARATOR_S "haarcascade_frontalface_default.xml"
75 #define DEFAULT_SCALE_FACTOR 1.25
76 #if (CV_MAJOR_VERSION >= 4)
77 #define DEFAULT_FLAGS CASCADE_DO_CANNY_PRUNING
78 #else
79 #define DEFAULT_FLAGS CV_HAAR_DO_CANNY_PRUNING
80 #endif
81 #define DEFAULT_MIN_NEIGHBORS 3
82 #define DEFAULT_MIN_SIZE_WIDTH 30
83 #define DEFAULT_MIN_SIZE_HEIGHT 30
84
85 using namespace cv;
86 using namespace std;
87 enum
88 {
89 PROP_0,
90 PROP_PROFILE,
91 PROP_SCALE_FACTOR,
92 PROP_MIN_NEIGHBORS,
93 PROP_FLAGS,
94 PROP_MIN_SIZE_WIDTH,
95 PROP_MIN_SIZE_HEIGHT
96 };
97
98 /**
99 * GstOpencvFaceDetectFlags:
100 * @GST_CAMERABIN_FLAG_SOURCE_RESIZE: enable video crop and scale
101 * after capture
102 *
103 * Flags parameter to OpenCV's cvHaarDetectObjects function.
104 */
105 typedef enum
106 {
107 GST_OPENCV_FACE_BLUR_HAAR_DO_CANNY_PRUNING = (1 << 0)
108 } GstOpencvFaceBlurFlags;
109
110 #define GST_TYPE_OPENCV_FACE_BLUR_FLAGS (gst_opencv_face_blur_flags_get_type())
111
112 static void
register_gst_opencv_face_blur_flags(GType * id)113 register_gst_opencv_face_blur_flags (GType * id)
114 {
115 static const GFlagsValue values[] = {
116 {(guint) GST_OPENCV_FACE_BLUR_HAAR_DO_CANNY_PRUNING,
117 "Do Canny edge detection to discard some regions", "do-canny-pruning"},
118 {0, NULL, NULL}
119 };
120 *id = g_flags_register_static ("GstOpencvFaceBlurFlags", values);
121 }
122
123 static GType
gst_opencv_face_blur_flags_get_type(void)124 gst_opencv_face_blur_flags_get_type (void)
125 {
126 static GType id;
127 static GOnce once = G_ONCE_INIT;
128
129 g_once (&once, (GThreadFunc) register_gst_opencv_face_blur_flags, &id);
130 return id;
131 }
132
133 /* the capabilities of the inputs and outputs.
134 */
135 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
136 GST_PAD_SINK,
137 GST_PAD_ALWAYS,
138 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
139 );
140
141 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
142 GST_PAD_SRC,
143 GST_PAD_ALWAYS,
144 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
145 );
146
147 G_DEFINE_TYPE (GstFaceBlur, gst_face_blur, GST_TYPE_OPENCV_VIDEO_FILTER);
148
149 static void gst_face_blur_set_property (GObject * object, guint prop_id,
150 const GValue * value, GParamSpec * pspec);
151 static void gst_face_blur_get_property (GObject * object, guint prop_id,
152 GValue * value, GParamSpec * pspec);
153
154
155 static gboolean gst_face_blur_set_caps (GstOpencvVideoFilter * transform,
156 gint in_width, gint in_height, int in_cv_type,
157 gint out_width, gint out_height, int out_cv_type);
158 static GstFlowReturn gst_face_blur_transform_ip (GstOpencvVideoFilter *
159 transform, GstBuffer * buffer, Mat img);
160
161 static CascadeClassifier *gst_face_blur_load_profile (GstFaceBlur *
162 filter, gchar * profile);
163
164 /* Clean up */
165 static void
gst_face_blur_finalize(GObject * obj)166 gst_face_blur_finalize (GObject * obj)
167 {
168 GstFaceBlur *filter = GST_FACE_BLUR (obj);
169
170 filter->cvGray.release ();
171
172 if (filter->cvCascade)
173 delete filter->cvCascade;
174
175 g_free (filter->profile);
176
177 G_OBJECT_CLASS (gst_face_blur_parent_class)->finalize (obj);
178 }
179
180
181 /* initialize the faceblur's class */
182 static void
gst_face_blur_class_init(GstFaceBlurClass * klass)183 gst_face_blur_class_init (GstFaceBlurClass * klass)
184 {
185 GObjectClass *gobject_class;
186 GstOpencvVideoFilterClass *gstopencvbasefilter_class;
187
188 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
189 gobject_class = (GObjectClass *) klass;
190 gstopencvbasefilter_class = (GstOpencvVideoFilterClass *) klass;
191
192 gstopencvbasefilter_class->cv_trans_ip_func = gst_face_blur_transform_ip;
193 gstopencvbasefilter_class->cv_set_caps = gst_face_blur_set_caps;
194
195 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_face_blur_finalize);
196 gobject_class->set_property = gst_face_blur_set_property;
197 gobject_class->get_property = gst_face_blur_get_property;
198
199 g_object_class_install_property (gobject_class, PROP_PROFILE,
200 g_param_spec_string ("profile", "Profile",
201 "Location of Haar cascade file to use for face blurion",
202 DEFAULT_PROFILE,
203 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
204 g_object_class_install_property (gobject_class, PROP_FLAGS,
205 g_param_spec_flags ("flags", "Flags", "Flags to cvHaarDetectObjects",
206 GST_TYPE_OPENCV_FACE_BLUR_FLAGS, DEFAULT_FLAGS,
207 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
208 g_object_class_install_property (gobject_class, PROP_SCALE_FACTOR,
209 g_param_spec_double ("scale-factor", "Scale factor",
210 "Factor by which the windows is scaled after each scan", 1.1, 10.0,
211 DEFAULT_SCALE_FACTOR,
212 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
213 g_object_class_install_property (gobject_class, PROP_MIN_NEIGHBORS,
214 g_param_spec_int ("min-neighbors", "Mininum neighbors",
215 "Minimum number (minus 1) of neighbor rectangles that makes up "
216 "an object", 0, G_MAXINT, DEFAULT_MIN_NEIGHBORS,
217 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
218 g_object_class_install_property (gobject_class, PROP_MIN_SIZE_WIDTH,
219 g_param_spec_int ("min-size-width", "Minimum size width",
220 "Minimum window width size", 0, G_MAXINT, DEFAULT_MIN_SIZE_WIDTH,
221 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
222 g_object_class_install_property (gobject_class, PROP_MIN_SIZE_HEIGHT,
223 g_param_spec_int ("min-size-height", "Minimum size height",
224 "Minimum window height size", 0, G_MAXINT, DEFAULT_MIN_SIZE_HEIGHT,
225 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
226
227 gst_element_class_set_static_metadata (element_class,
228 "faceblur",
229 "Filter/Effect/Video",
230 "Blurs faces in images and videos",
231 "Michael Sheldon <mike@mikeasoft.com>,Robert Jobbagy <jobbagy.robert@gmail.com>");
232
233 gst_element_class_add_static_pad_template (element_class, &src_factory);
234 gst_element_class_add_static_pad_template (element_class, &sink_factory);
235 }
236
237 /* initialize the new element
238 * instantiate pads and add them to element
239 * set pad calback functions
240 * initialize instance structure
241 */
242 static void
gst_face_blur_init(GstFaceBlur * filter)243 gst_face_blur_init (GstFaceBlur * filter)
244 {
245 filter->profile = g_strdup (DEFAULT_PROFILE);
246 filter->cvCascade = gst_face_blur_load_profile (filter, filter->profile);
247 filter->sent_profile_load_failed_msg = FALSE;
248 filter->scale_factor = DEFAULT_SCALE_FACTOR;
249 filter->min_neighbors = DEFAULT_MIN_NEIGHBORS;
250 filter->flags = DEFAULT_FLAGS;
251 filter->min_size_width = DEFAULT_MIN_SIZE_WIDTH;
252 filter->min_size_height = DEFAULT_MIN_SIZE_HEIGHT;
253
254 gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
255 TRUE);
256 }
257
258 static void
gst_face_blur_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)259 gst_face_blur_set_property (GObject * object, guint prop_id,
260 const GValue * value, GParamSpec * pspec)
261 {
262 GstFaceBlur *filter = GST_FACE_BLUR (object);
263
264 switch (prop_id) {
265 case PROP_PROFILE:
266 g_free (filter->profile);
267 if (filter->cvCascade)
268 delete filter->cvCascade;
269 filter->profile = g_value_dup_string (value);
270 filter->cvCascade = gst_face_blur_load_profile (filter, filter->profile);
271 filter->sent_profile_load_failed_msg = FALSE;
272 break;
273 case PROP_SCALE_FACTOR:
274 filter->scale_factor = g_value_get_double (value);
275 break;
276 case PROP_MIN_NEIGHBORS:
277 filter->min_neighbors = g_value_get_int (value);
278 break;
279 case PROP_MIN_SIZE_WIDTH:
280 filter->min_size_width = g_value_get_int (value);
281 break;
282 case PROP_MIN_SIZE_HEIGHT:
283 filter->min_size_height = g_value_get_int (value);
284 break;
285 case PROP_FLAGS:
286 filter->flags = g_value_get_flags (value);
287 break;
288 default:
289 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290 break;
291 }
292 }
293
294 static void
gst_face_blur_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)295 gst_face_blur_get_property (GObject * object, guint prop_id,
296 GValue * value, GParamSpec * pspec)
297 {
298 GstFaceBlur *filter = GST_FACE_BLUR (object);
299
300 switch (prop_id) {
301 case PROP_PROFILE:
302 g_value_set_string (value, filter->profile);
303 break;
304 case PROP_SCALE_FACTOR:
305 g_value_set_double (value, filter->scale_factor);
306 break;
307 case PROP_MIN_NEIGHBORS:
308 g_value_set_int (value, filter->min_neighbors);
309 break;
310 case PROP_MIN_SIZE_WIDTH:
311 g_value_set_int (value, filter->min_size_width);
312 break;
313 case PROP_MIN_SIZE_HEIGHT:
314 g_value_set_int (value, filter->min_size_height);
315 break;
316 case PROP_FLAGS:
317 g_value_set_flags (value, filter->flags);
318 break;
319 default:
320 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
321 break;
322 }
323 }
324
325 static gboolean
gst_face_blur_set_caps(GstOpencvVideoFilter * transform,gint in_width,gint in_height,int in_cv_type,gint out_width,gint out_height,int out_cv_type)326 gst_face_blur_set_caps (GstOpencvVideoFilter * transform,
327 gint in_width, gint in_height, int in_cv_type,
328 gint out_width, gint out_height, int out_cv_type)
329 {
330 GstFaceBlur *filter = GST_FACE_BLUR (transform);
331
332 filter->cvGray.create (Size (in_width, in_height), CV_8UC1);
333
334 return TRUE;
335 }
336
337 static GstFlowReturn
gst_face_blur_transform_ip(GstOpencvVideoFilter * transform,GstBuffer * buffer,Mat img)338 gst_face_blur_transform_ip (GstOpencvVideoFilter * transform,
339 GstBuffer * buffer, Mat img)
340 {
341 GstFaceBlur *filter = GST_FACE_BLUR (transform);
342 vector < Rect > faces;
343 unsigned int i;
344
345 if (!filter->cvCascade) {
346 if (filter->profile != NULL
347 && filter->sent_profile_load_failed_msg == FALSE) {
348 GST_ELEMENT_WARNING (filter, RESOURCE, NOT_FOUND,
349 ("Profile %s is missing.", filter->profile),
350 ("missing faceblur profile file %s", filter->profile));
351 filter->sent_profile_load_failed_msg = TRUE;
352 }
353 return GST_FLOW_OK;
354 }
355
356 cvtColor (img, filter->cvGray, COLOR_RGB2GRAY);
357
358 filter->cvCascade->detectMultiScale (filter->cvGray, faces,
359 filter->scale_factor, filter->min_neighbors, filter->flags,
360 Size (filter->min_size_width, filter->min_size_height), Size (0, 0));
361
362 if (!faces.empty ()) {
363
364 for (i = 0; i < faces.size (); ++i) {
365 Rect *r = &faces[i];
366 Mat roi (img, Rect (r->x, r->y, r->width, r->height));
367 blur (roi, roi, Size (11, 11));
368 GaussianBlur (roi, roi, Size (11, 11), 0, 0);
369 }
370 }
371
372 return GST_FLOW_OK;
373 }
374
375 static CascadeClassifier *
gst_face_blur_load_profile(GstFaceBlur * filter,gchar * profile)376 gst_face_blur_load_profile (GstFaceBlur * filter, gchar * profile)
377 {
378 CascadeClassifier *cascade;
379
380 cascade = new CascadeClassifier (profile);
381 if (cascade->empty ()) {
382 GST_ERROR_OBJECT (filter, "Invalid profile file: %s", profile);
383 delete cascade;
384 return NULL;
385 }
386 return cascade;
387 }
388
389
390 /* entry point to initialize the plug-in
391 * initialize the plug-in itself
392 * register the element factories and other features
393 */
394 gboolean
gst_face_blur_plugin_init(GstPlugin * plugin)395 gst_face_blur_plugin_init (GstPlugin * plugin)
396 {
397 /* debug category for filtering log messages */
398 GST_DEBUG_CATEGORY_INIT (gst_face_blur_debug, "faceblur",
399 0, "Blurs faces in images and videos");
400
401 return gst_element_register (plugin, "faceblur", GST_RANK_NONE,
402 GST_TYPE_FACE_BLUR);
403 }
404