2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include <gst/video/video.h>
29 #define GST_TYPE_ALPHA \
30 (gst_alpha_get_type())
31 #define GST_ALPHA(obj) \
32 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ALPHA,GstAlpha))
33 #define GST_ALPHA_CLASS(klass) \
34 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ALPHA,GstAlphaClass))
35 #define GST_IS_ALPHA(obj) \
36 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ALPHA))
37 #define GST_IS_ALPHA_CLASS(obj) \
38 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ALPHA))
40 typedef struct _GstAlpha GstAlpha;
41 typedef struct _GstAlphaClass GstAlphaClass;
52 #define ROUND_UP_2(x) (((x) + 1) & ~1)
53 #define ROUND_UP_4(x) (((x) + 3) & ~3)
54 #define ROUND_UP_8(x) (((x) + 7) & ~7)
65 gint in_width, in_height;
66 gint out_width, out_height;
75 GstAlphaMethod method;
80 gfloat y; /* chroma color */
83 gfloat accept_angle_cos;
84 gfloat accept_angle_sin;
85 guint8 accept_angle_tg;
86 guint8 accept_angle_ctg;
93 GstElementClass parent_class;
96 /* elementfactory information */
97 static GstElementDetails gst_alpha_details =
98 GST_ELEMENT_DETAILS ("alpha filter",
99 "Filter/Effect/Video",
100 "Adds an alpha channel to video",
101 "Wim Taymans <wim@fluendo.com>");
104 /* Alpha signals and args */
111 #define DEFAULT_METHOD ALPHA_METHOD_SET
112 #define DEFAULT_ALPHA 1.0
113 #define DEFAULT_TARGET_R 0
114 #define DEFAULT_TARGET_G 255
115 #define DEFAULT_TARGET_B 0
116 #define DEFAULT_ANGLE 20.0
117 #define DEFAULT_NOISE_LEVEL 2.0
132 static GstStaticPadTemplate gst_alpha_src_template =
133 GST_STATIC_PAD_TEMPLATE ("src",
136 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV"))
139 static GstStaticPadTemplate gst_alpha_sink_template =
140 GST_STATIC_PAD_TEMPLATE ("sink",
143 GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("AYUV")
144 ";" GST_VIDEO_CAPS_YUV ("I420")
149 static void gst_alpha_base_init (gpointer g_class);
150 static void gst_alpha_class_init (GstAlphaClass * klass);
151 static void gst_alpha_init (GstAlpha * alpha);
152 static void gst_alpha_init_params (GstAlpha * alpha);
154 static void gst_alpha_set_property (GObject * object, guint prop_id,
155 const GValue * value, GParamSpec * pspec);
156 static void gst_alpha_get_property (GObject * object, guint prop_id,
157 GValue * value, GParamSpec * pspec);
159 static gboolean gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps);
160 static GstFlowReturn gst_alpha_chain (GstPad * pad, GstBuffer * buffer);
162 static GstStateChangeReturn gst_alpha_change_state (GstElement * element,
163 GstStateChange transition);
166 static GstElementClass *parent_class = NULL;
168 #define GST_TYPE_ALPHA_METHOD (gst_alpha_method_get_type())
170 gst_alpha_method_get_type (void)
172 static GType alpha_method_type = 0;
173 static GEnumValue alpha_method[] = {
174 {ALPHA_METHOD_SET, "0", "Set/adjust alpha channel"},
175 {ALPHA_METHOD_GREEN, "1", "Chroma Key green"},
176 {ALPHA_METHOD_BLUE, "2", "Chroma Key blue"},
177 {ALPHA_METHOD_CUSTOM, "3", "Chroma Key on target_r/g/b"},
181 if (!alpha_method_type) {
182 alpha_method_type = g_enum_register_static ("GstAlphaMethod", alpha_method);
184 return alpha_method_type;
187 /* static guint gst_alpha_signals[LAST_SIGNAL] = { 0 }; */
190 gst_alpha_get_type (void)
192 static GType alpha_type = 0;
195 static const GTypeInfo alpha_info = {
196 sizeof (GstAlphaClass),
199 (GClassInitFunc) gst_alpha_class_init,
204 (GInstanceInitFunc) gst_alpha_init,
208 g_type_register_static (GST_TYPE_ELEMENT, "GstAlpha", &alpha_info, 0);
214 gst_alpha_base_init (gpointer g_class)
216 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
218 gst_element_class_set_details (element_class, &gst_alpha_details);
220 gst_element_class_add_pad_template (element_class,
221 gst_static_pad_template_get (&gst_alpha_sink_template));
222 gst_element_class_add_pad_template (element_class,
223 gst_static_pad_template_get (&gst_alpha_src_template));
226 gst_alpha_class_init (GstAlphaClass * klass)
228 GObjectClass *gobject_class;
229 GstElementClass *gstelement_class;
231 gobject_class = (GObjectClass *) klass;
232 gstelement_class = (GstElementClass *) klass;
234 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
236 gobject_class->set_property = gst_alpha_set_property;
237 gobject_class->get_property = gst_alpha_get_property;
239 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_METHOD,
240 g_param_spec_enum ("method", "Method",
241 "How the alpha channels should be created", GST_TYPE_ALPHA_METHOD,
242 DEFAULT_METHOD, (GParamFlags) G_PARAM_READWRITE));
243 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ALPHA,
244 g_param_spec_double ("alpha", "Alpha", "The value for the alpha channel",
245 0.0, 1.0, DEFAULT_ALPHA, (GParamFlags) G_PARAM_READWRITE));
246 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_R,
247 g_param_spec_uint ("target_r", "Target Red", "The Red target", 0,
248 255, DEFAULT_TARGET_R, (GParamFlags) G_PARAM_READWRITE));
249 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_G,
250 g_param_spec_uint ("target_g", "Target Green", "The Green target", 0,
251 255, DEFAULT_TARGET_G, (GParamFlags) G_PARAM_READWRITE));
252 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TARGET_B,
253 g_param_spec_uint ("target_b", "Target Blue", "The Blue target",
254 0, 255, DEFAULT_TARGET_B, (GParamFlags) G_PARAM_READWRITE));
255 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
256 g_param_spec_float ("angle", "Angle", "Size of the colorcube to change",
257 0.0, 90.0, DEFAULT_ANGLE, (GParamFlags) G_PARAM_READWRITE));
258 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NOISE_LEVEL,
259 g_param_spec_float ("noise_level", "Noise Level", "Size of noise radius",
260 0.0, 64.0, DEFAULT_NOISE_LEVEL, (GParamFlags) G_PARAM_READWRITE));
262 gstelement_class->change_state = gst_alpha_change_state;
266 gst_alpha_init (GstAlpha * alpha)
268 /* create the sink and src pads */
270 gst_pad_new_from_template (gst_static_pad_template_get
271 (&gst_alpha_sink_template), "sink");
272 gst_element_add_pad (GST_ELEMENT (alpha), alpha->sinkpad);
273 gst_pad_set_chain_function (alpha->sinkpad, gst_alpha_chain);
274 gst_pad_set_setcaps_function (alpha->sinkpad, gst_alpha_sink_setcaps);
277 gst_pad_new_from_template (gst_static_pad_template_get
278 (&gst_alpha_src_template), "src");
279 gst_element_add_pad (GST_ELEMENT (alpha), alpha->srcpad);
281 alpha->alpha = DEFAULT_ALPHA;
282 alpha->method = DEFAULT_METHOD;
283 alpha->target_r = DEFAULT_TARGET_R;
284 alpha->target_g = DEFAULT_TARGET_G;
285 alpha->target_b = DEFAULT_TARGET_B;
286 alpha->angle = DEFAULT_ANGLE;
287 alpha->noise_level = DEFAULT_NOISE_LEVEL;
290 /* do we need this function? */
292 gst_alpha_set_property (GObject * object, guint prop_id,
293 const GValue * value, GParamSpec * pspec)
297 g_return_if_fail (GST_IS_ALPHA (object));
299 alpha = GST_ALPHA (object);
303 alpha->method = g_value_get_enum (value);
304 switch (alpha->method) {
305 case ALPHA_METHOD_GREEN:
307 alpha->target_g = 255;
310 case ALPHA_METHOD_BLUE:
313 alpha->target_b = 255;
318 gst_alpha_init_params (alpha);
321 alpha->alpha = g_value_get_double (value);
324 alpha->target_r = g_value_get_uint (value);
325 gst_alpha_init_params (alpha);
328 alpha->target_g = g_value_get_uint (value);
329 gst_alpha_init_params (alpha);
332 alpha->target_b = g_value_get_uint (value);
333 gst_alpha_init_params (alpha);
336 alpha->angle = g_value_get_float (value);
337 gst_alpha_init_params (alpha);
339 case ARG_NOISE_LEVEL:
340 alpha->noise_level = g_value_get_float (value);
341 gst_alpha_init_params (alpha);
344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349 gst_alpha_get_property (GObject * object, guint prop_id, GValue * value,
354 g_return_if_fail (GST_IS_ALPHA (object));
356 alpha = GST_ALPHA (object);
360 g_value_set_enum (value, alpha->method);
363 g_value_set_double (value, alpha->alpha);
366 g_value_set_uint (value, alpha->target_r);
369 g_value_set_uint (value, alpha->target_g);
372 g_value_set_uint (value, alpha->target_b);
375 g_value_set_float (value, alpha->angle);
377 case ARG_NOISE_LEVEL:
378 g_value_set_float (value, alpha->noise_level);
381 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
387 gst_alpha_sink_setcaps (GstPad * pad, GstCaps * caps)
390 GstStructure *structure;
394 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
395 structure = gst_caps_get_structure (caps, 0);
397 if (gst_structure_get_fourcc (structure, "format", &fourcc)) {
399 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
402 case GST_MAKE_FOURCC ('A', 'Y', 'U', 'V'):
412 ret = gst_structure_get_int (structure, "width", &alpha->in_width);
413 ret &= gst_structure_get_int (structure, "height", &alpha->in_height);
419 gst_alpha_set_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
422 gint b_alpha = (gint) (alpha * 255);
428 width = ROUND_UP_2 (width);
429 height = ROUND_UP_2 (height);
431 stride = ROUND_UP_4 (width);
432 size = stride * height;
434 wrap = stride - width;
436 for (i = 0; i < height; i++) {
437 for (j = 0; j < width; j++) {
438 *dest++ = (*src++ * b_alpha) >> 8;
449 gst_alpha_set_i420 (guint8 * src, guint8 * dest, gint width, gint height,
452 gint b_alpha = (gint) (alpha * 255);
458 gint stride, stride2;
461 width = ROUND_UP_2 (width);
462 height = ROUND_UP_2 (height);
464 stride = ROUND_UP_4 (width);
465 size = stride * height;
466 stride2 = ROUND_UP_8 (width) / 2;
467 size2 = stride2 * height / 2;
469 wrap = stride - 2 * (width / 2);
470 wrap2 = stride2 - width / 2;
476 for (i = 0; i < height; i++) {
477 for (j = 0; j < width / 2; j++) {
499 gst_alpha_chroma_key_ayuv (guint8 * src, guint8 * dest, gint width, gint height,
506 gint x, z, u, v, y, a;
513 width = ROUND_UP_2 (width);
514 height = ROUND_UP_2 (height);
516 stride = ROUND_UP_4 (width);
517 size = stride * height;
522 wrap = stride - width;
524 for (i = 0; i < height; i++) {
525 for (j = 0; j < width; j++) {
526 a = *src1++ * (alpha->alpha);
531 /* Convert foreground to XZ coords where X direction is defined by
533 tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
534 x = CLAMP (tmp, -128, 127);
535 tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
536 z = CLAMP (tmp, -128, 127);
538 /* WARNING: accept angle should never be set greater than "somewhat less
539 than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
540 80 degrees should be enough if foreground is reasonable. If this seems
541 to be a problem, go to alternative ways of checking point position
542 (scalar product or line equations). This angle should not be too small
543 either to avoid infinite ctg (used to suppress foreground without use of
546 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
547 tmp = MIN (tmp, 127);
550 /* keep foreground Kfg = 0 */
553 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
555 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
556 tmp = CLAMP (tmp, -128, 127);
561 tmp1 = MAX (tmp1, 0);
562 b_alpha = (((unsigned char) (tmp1) *
563 (unsigned short) (alpha->one_over_kc)) / 2);
564 b_alpha = 255 - CLAMP (b_alpha, 0, 255);
565 b_alpha = (a * b_alpha) >> 8;
567 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
568 tmp1 = MIN (tmp, 255);
573 /* Convert suppressed foreground back to CbCr */
574 tmp = ((char) (x1) * (short) (alpha->cb) -
575 (char) (y1) * (short) (alpha->cr)) >> 7;
576 u = CLAMP (tmp, -128, 127);
578 tmp = ((char) (x1) * (short) (alpha->cr) +
579 (char) (y1) * (short) (alpha->cb)) >> 7;
580 v = CLAMP (tmp, -128, 127);
582 /* Deal with noise. For now, a circle around the key color with
583 radius of noise_level treated as exact key color. Introduces
586 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
587 tmp = MIN (tmp, 0xffff);
589 if (tmp < alpha->noise_level * alpha->noise_level) {
607 /* based on http://www.cs.utah.edu/~michael/chroma/
610 gst_alpha_chroma_key_i420 (guint8 * src, guint8 * dest, gint width, gint height,
614 guint8 *srcY1, *srcY2, *srcU, *srcV;
615 guint8 *dest1, *dest2;
617 gint x, z, u, v, y11, y12, y21, y22, a;
619 gint stride, stride2;
620 gint wrap, wrap2, wrap3;
624 width = ROUND_UP_2 (width);
625 height = ROUND_UP_2 (height);
627 stride = ROUND_UP_4 (width);
628 size = stride * height;
629 stride2 = ROUND_UP_8 (width) / 2;
630 size2 = stride2 * height / 2;
633 srcY2 = src + stride;
638 dest2 = dest + width * 4;
640 wrap = 2 * stride - 2 * (width / 2);
641 wrap2 = stride2 - width / 2;
642 wrap3 = 8 * width - 8 * (width / 2);
644 a = 255 * alpha->alpha;
646 for (i = 0; i < height / 2; i++) {
647 for (j = 0; j < width / 2; j++) {
655 /* Convert foreground to XZ coords where X direction is defined by
657 tmp = ((short) u * alpha->cb + (short) v * alpha->cr) >> 7;
658 x = CLAMP (tmp, -128, 127);
659 tmp = ((short) v * alpha->cb - (short) u * alpha->cr) >> 7;
660 z = CLAMP (tmp, -128, 127);
662 /* WARNING: accept angle should never be set greater than "somewhat less
663 than 90 degrees" to avoid dealing with negative/infinite tg. In reality,
664 80 degrees should be enough if foreground is reasonable. If this seems
665 to be a problem, go to alternative ways of checking point position
666 (scalar product or line equations). This angle should not be too small
667 either to avoid infinite ctg (used to suppress foreground without use of
670 tmp = ((short) (x) * alpha->accept_angle_tg) >> 4;
671 tmp = MIN (tmp, 127);
674 /* keep foreground Kfg = 0 */
677 /* Compute Kfg (implicitly) and Kbg, suppress foreground in XZ coord
679 tmp = ((short) (z) * alpha->accept_angle_ctg) >> 4;
680 tmp = CLAMP (tmp, -128, 127);
685 tmp1 = MAX (tmp1, 0);
686 b_alpha = (((unsigned char) (tmp1) *
687 (unsigned short) (alpha->one_over_kc)) / 2);
688 b_alpha = 255 - CLAMP (b_alpha, 0, 255);
689 b_alpha = (a * b_alpha) >> 8;
691 tmp = ((unsigned short) (tmp1) * alpha->kfgy_scale) >> 4;
692 tmp1 = MIN (tmp, 255);
703 /* Convert suppressed foreground back to CbCr */
704 tmp = ((char) (x1) * (short) (alpha->cb) -
705 (char) (y1) * (short) (alpha->cr)) >> 7;
706 u = CLAMP (tmp, -128, 127);
708 tmp = ((char) (x1) * (short) (alpha->cr) +
709 (char) (y1) * (short) (alpha->cb)) >> 7;
710 v = CLAMP (tmp, -128, 127);
712 /* Deal with noise. For now, a circle around the key color with
713 radius of noise_level treated as exact key color. Introduces
716 tmp = z * (short) (z) + (x - alpha->kg) * (short) (x - alpha->kg);
717 tmp = MIN (tmp, 0xffff);
719 if (tmp < alpha->noise_level * alpha->noise_level) {
720 /* Uncomment this if you want total suppression within the noise circle */
755 gst_alpha_init_params (GstAlpha * alpha)
762 0.257 * alpha->target_r + 0.504 * alpha->target_g +
763 0.098 * alpha->target_b;
765 -0.148 * alpha->target_r - 0.291 * alpha->target_g +
766 0.439 * alpha->target_b;
768 0.439 * alpha->target_r - 0.368 * alpha->target_g -
769 0.071 * alpha->target_b;
770 kgl = sqrt (tmp1 * tmp1 + tmp2 * tmp2);
771 alpha->cb = 127 * (tmp1 / kgl);
772 alpha->cr = 127 * (tmp2 / kgl);
774 alpha->accept_angle_cos = cos (M_PI * alpha->angle / 180);
775 alpha->accept_angle_sin = sin (M_PI * alpha->angle / 180);
776 tmp = 15 * tan (M_PI * alpha->angle / 180);
777 tmp = MIN (tmp, 255);
778 alpha->accept_angle_tg = tmp;
779 tmp = 15 / tan (M_PI * alpha->angle / 180);
780 tmp = MIN (tmp, 255);
781 alpha->accept_angle_ctg = tmp;
783 alpha->one_over_kc = 255 * 2 * tmp - 255;
784 tmp = 15 * (float) (alpha->y) / kgl;
785 tmp = MIN (tmp, 255);
786 alpha->kfgy_scale = tmp;
787 alpha->kg = MIN (kgl, 127);
791 gst_alpha_chain (GstPad * pad, GstBuffer * buffer)
795 gint new_width, new_height;
798 alpha = GST_ALPHA (GST_PAD_PARENT (pad));
800 new_width = alpha->in_width;
801 new_height = alpha->in_height;
803 if (new_width != alpha->out_width ||
804 new_height != alpha->out_height || !GST_PAD_CAPS (alpha->srcpad)) {
807 newcaps = gst_caps_copy (gst_pad_get_negotiated_caps (alpha->sinkpad));
808 gst_caps_set_simple (newcaps,
809 "format", GST_TYPE_FOURCC, GST_STR_FOURCC ("AYUV"),
810 "width", G_TYPE_INT, new_width, "height", G_TYPE_INT, new_height, NULL);
812 gst_pad_set_caps (alpha->srcpad, newcaps);
814 alpha->out_width = new_width;
815 alpha->out_height = new_height;
819 gst_buffer_new_and_alloc (ROUND_UP_2 (new_width) *
820 ROUND_UP_2 (new_height) * 4);
821 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (alpha->srcpad));
822 GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
823 GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buffer);
825 switch (alpha->method) {
826 case ALPHA_METHOD_SET:
828 gst_alpha_set_ayuv (GST_BUFFER_DATA (buffer),
829 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
831 gst_alpha_set_i420 (GST_BUFFER_DATA (buffer),
832 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha->alpha);
835 case ALPHA_METHOD_GREEN:
836 case ALPHA_METHOD_BLUE:
837 case ALPHA_METHOD_CUSTOM:
839 gst_alpha_chroma_key_ayuv (GST_BUFFER_DATA (buffer),
840 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
842 gst_alpha_chroma_key_i420 (GST_BUFFER_DATA (buffer),
843 GST_BUFFER_DATA (outbuf), new_width, new_height, alpha);
850 gst_buffer_unref (buffer);
852 ret = gst_pad_push (alpha->srcpad, outbuf);
857 static GstStateChangeReturn
858 gst_alpha_change_state (GstElement * element, GstStateChange transition)
862 alpha = GST_ALPHA (element);
864 switch (transition) {
865 case GST_STATE_CHANGE_NULL_TO_READY:
867 case GST_STATE_CHANGE_READY_TO_PAUSED:
868 gst_alpha_init_params (alpha);
870 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
872 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
874 case GST_STATE_CHANGE_PAUSED_TO_READY:
876 case GST_STATE_CHANGE_READY_TO_NULL:
880 parent_class->change_state (element, transition);
882 return GST_STATE_CHANGE_SUCCESS;
886 plugin_init (GstPlugin * plugin)
888 return gst_element_register (plugin, "alpha", GST_RANK_NONE, GST_TYPE_ALPHA);
891 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
894 "adds an alpha channel to video",
895 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)