2
* Copyright (C) 2006 Mark Nauwelaerts <mnauw@users.sourceforge.net>
4
* mplayer hqdn3d filter
5
* Copyright (C) 2003 Daniel Moreno <comac@comac.darktech.org>
7
* mplayer/transcode denoise3d filter
8
* Copyright (C) 2003 Daniel Moreno <comac@comac.darktech.org>
9
* Copyright (C) 2003 Erik Slagter <erik@oldconomy.org> (GPL) 2003
10
* transcode optimizations
12
* This program is free software; you can redistribute it and/or modify
13
* it under the terms of the GNU General Public License as published by
14
* the Free Software Foundation; either version 2 of the License, or
15
* (at your option) any later version.
17
* This program is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
22
* You should have received a copy of the GNU General Public License
23
* along with this program; if not, write to the Free Software
24
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1307 USA
28
* SECTION:element-hqdn3d
32
* This filter aims to reduce image noise producing smooth images and making
33
* still images really still (This should enhance compressibility).
36
* Depending on <link linkend="GstHqdn3d--high-quality">hiqh-quality</link> setting,
37
* it runs either mplayer's hqdn3d filter algorithm or the denoise3d algorithm,
38
* which is cruder and simpler, but also faster.
41
* Leaving or setting any of the other parameters to 0 will make the filter
42
* use the indicated internal defaults. Setting some parameters will
43
* also have this value 'trickle' into the other parameters as follows:
46
* luma_spatial --> luma_temp, chroma_spatial, chroma_temp
49
* chroma_spatial --> chroma_temp
52
* luma_temp --> chroma_temp
55
* In addition, setting a components (both) parameters to a negative value will not
56
* have the filter applied to this component.
58
* <title>History</title>
62
* Mplayer denoise3d and hqdn3d filter [Daniel Moreno]
65
* Also used in transcode; denoise3d filter somewhat optimized [Erik Slagter]
68
* Also available in avidemux (MPlayer hqdn3d, MPlayer denoise3d)
81
#include "plugin-mencoder.h"
86
#define GST_TYPE_HQDN3D \
87
(gst_hqdn3d_get_type())
88
#define GST_HQDN3D(obj) \
89
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HQDN3D,GstHqdn3d))
90
#define GST_HQDN3D_CLASS(klass) \
91
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HQDN3D,GstHqdn3dClass))
92
#define GST_IS_HQDN3D(obj) \
93
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HQDN3D))
94
#define GST_IS_HQDN3D_CLASS(klass) \
95
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HQDN3D))
97
typedef struct _GstHqdn3d GstHqdn3d;
98
typedef struct _GstHqdn3dClass GstHqdn3dClass;
100
/* number of coefficients for each 'dimension' */
101
#define COEFS_SIZE 512*16
103
/* function that will do the actual denoising */
104
typedef void (*GstDenoiseFunction) (guint8 * frame, guint * lineant,
105
gushort ** frameprevptr,
107
gint * horizontal, gint * vertical, gint * temporal);
112
GstVideoFilter videofilter;
116
/* user denoise parameters */
117
gdouble set_luma_spatial, set_luma_temp, set_chroma_spatial, set_chroma_temp;
118
/* internal denoise parameters */
119
gdouble luma_spatial, luma_temp, chroma_spatial, chroma_temp;
122
/* coefs used in denoising */
123
gint (*coefs)[COEFS_SIZE];
124
/* stores the previous line temporarily (within a plane) */
126
/* store the previous *plane*, so several required per frame */
128
/* performs actual denoising depending on quality setting */
129
GstDenoiseFunction denoise_func;
132
struct _GstHqdn3dClass
134
GstVideoFilterClass parent_class;
137
GST_DEBUG_CATEGORY_STATIC (hqdn3d_debug);
138
#define GST_CAT_DEFAULT hqdn3d_debug
140
/* signals and args */
158
#define DEFAULT_LUMA_SPATIAL 4.0
159
#define DEFAULT_LUMA_TEMP 6.0
160
#define DEFAULT_CHROMA_SPATIAL 3.0
161
#define DEFAULT_CHROMA_TEMP \
162
(DEFAULT_LUMA_TEMP * DEFAULT_CHROMA_SPATIAL / DEFAULT_LUMA_SPATIAL)
163
#define HQDN3D_MAX_PARAM 255
164
#define DEFAULT_HIGH_QUALITY TRUE
166
/* use these names rather than hardcoded indexes for the coefficient array below */
177
static GstElementDetails hqdn3d_details =
178
GST_ELEMENT_DETAILS ("Hqdn3d",
179
"Filter/Effect/Video",
180
"High Quality 3D Denoiser",
181
"Mark Nauwelaerts <mnauw@users.sourceforge.net>,\n"
182
"Daniel Moreno, Erik Slagter");
184
static GstStaticPadTemplate gst_hqdn3d_src_template =
185
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SRC_NAME,
188
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12 }"))
191
static GstStaticPadTemplate gst_hqdn3d_sink_template =
192
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SINK_NAME,
195
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12 }"))
198
static gboolean gst_hqdn3d_hook_caps (GstHqdn3d * btrans, GstCaps * incaps,
200
static GstFlowReturn gst_hqdn3d_transform_ip (GstBaseTransform * btrans,
202
static gboolean gst_hqdn3d_start (GstBaseTransform * btrans);
203
static gboolean gst_hqdn3d_stop (GstBaseTransform * btrans);
205
static void gst_hqdn3d_set_property (GObject * object, guint prop_id,
206
const GValue * value, GParamSpec * pspec);
207
static void gst_hqdn3d_get_property (GObject * object, guint prop_id,
208
GValue * value, GParamSpec * pspec);
210
static void gst_hqdn3d_finalize (GObject * object);
212
GST_BOILERPLATE (GstHqdn3d, gst_hqdn3d, GstVideoFilter, GST_TYPE_VIDEO_FILTER);
214
GST_VIDEO_FILTER_SET_CAPS_BOILERPLATE_FULL (GstHqdn3d, gst_hqdn3d,
215
gst_hqdn3d_hook_caps);
217
GST_VIDEO_FILTER_GET_UNIT_SIZE_BOILERPLATE (gst_hqdn3d);
220
gst_hqdn3d_base_init (gpointer g_class)
222
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
224
gst_element_class_set_details (element_class, &hqdn3d_details);
226
gst_element_class_add_pad_template (element_class,
227
gst_static_pad_template_get (&gst_hqdn3d_sink_template));
228
gst_element_class_add_pad_template (element_class,
229
gst_static_pad_template_get (&gst_hqdn3d_src_template));
233
gst_hqdn3d_class_init (GstHqdn3dClass * g_class)
235
GObjectClass *gobject_class;
236
GstBaseTransformClass *trans_class;
238
gobject_class = G_OBJECT_CLASS (g_class);
239
trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
241
GST_DEBUG_CATEGORY_INIT (hqdn3d_debug, "hqdn3d", 0, "hqdn3d");
243
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_hqdn3d_finalize);
245
gobject_class->set_property = gst_hqdn3d_set_property;
246
gobject_class->get_property = gst_hqdn3d_get_property;
248
g_object_class_install_property (gobject_class, PROP_LUMA_SPATIAL,
249
g_param_spec_double ("luma-spatial", "Luma Spatial",
250
"Spatial Luma Strength (0: use default)",
251
-1, HQDN3D_MAX_PARAM, DEFAULT_LUMA_SPATIAL,
252
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
254
g_object_class_install_property (gobject_class, PROP_CHROMA_SPATIAL,
255
g_param_spec_double ("chroma-spatial", "Chroma Spatial",
256
"Spatial Chroma Strength (0: use default)",
257
-1, HQDN3D_MAX_PARAM, DEFAULT_CHROMA_SPATIAL,
258
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
260
g_object_class_install_property (gobject_class, PROP_LUMA_TEMP,
261
g_param_spec_double ("luma-temp", "Luma Temporal",
262
"Temporal Luma Strength (0: use default)",
263
-1, HQDN3D_MAX_PARAM, DEFAULT_LUMA_TEMP,
264
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
266
g_object_class_install_property (gobject_class, PROP_CHROMA_TEMP,
267
g_param_spec_double ("chroma-temp", "Chroma Temporal",
268
"Temporal Chroma Strength (0: use default)",
269
-1, HQDN3D_MAX_PARAM, DEFAULT_CHROMA_TEMP,
270
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
272
g_object_class_install_property (gobject_class, PROP_HIGH_QUALITY,
273
g_param_spec_boolean ("high-quality", "High Quality",
274
"High Quality Denoising (hqdn3d versus denoise3d)",
275
DEFAULT_HIGH_QUALITY, G_PARAM_READWRITE));
277
trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_hqdn3d_set_caps);
278
trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_hqdn3d_get_unit_size);
279
trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_hqdn3d_transform_ip);
280
trans_class->start = GST_DEBUG_FUNCPTR (gst_hqdn3d_start);
281
trans_class->stop = GST_DEBUG_FUNCPTR (gst_hqdn3d_stop);
285
gst_hqdn3d_init (GstHqdn3d * filter, GstHqdn3dClass * g_class)
288
filter->frame[0] = filter->frame[1] = filter->frame[2] = NULL;
290
/* does not depend on any stream specific property,
291
* so can be allocated here */
292
filter->coefs = g_malloc (sizeof(*(filter->coefs)) * COEFS_LAST_INDEX);
294
filter->quality = DEFAULT_HIGH_QUALITY;
298
gst_hqdn3d_finalize (GObject * object)
300
GstHqdn3d *filter = GST_HQDN3D (object);
302
g_free (filter->coefs);
304
G_OBJECT_CLASS (parent_class)->finalize (object);
308
gst_hqdn3d_free (GstHqdn3d * filter)
310
g_free (filter->line);
312
/* hm, just too small to do loop */
313
g_free (filter->frame[0]);
314
g_free (filter->frame[1]);
315
g_free (filter->frame[2]);
316
filter->frame[0] = filter->frame[1] = filter->frame[2] = NULL;
320
gst_hqdn3d_alloc (GstHqdn3d * filter)
322
filter->line = g_malloc (filter->width * sizeof (guint));
323
/* frame will be allocated at the actual transformation time when needed */
327
gst_hqdn3d_hook_caps (GstHqdn3d *filter, GstCaps * incaps,
330
gst_hqdn3d_free (filter);
331
gst_hqdn3d_alloc (filter);
337
gst_hqdn3d_precalc_coefs (gint * coefs, double Dist25)
340
double Gamma, Simil, C;
342
Gamma = log(0.25) / log(1.0 - Dist25 / 255.0 - 0.00001);
344
for (i = -256 * 16; i < 256 * 16; i++) {
345
Simil = 1.0 - (i > 0 ? i : -i) / (16 * 255.0);
346
C = pow(Simil, Gamma) * 65536.0 * (double) i / 16.0;
347
coefs[16 * 256 + i] = (C < 0) ? (C - 0.5) : (C + 0.5);
352
LowPassMul(guint PrevMul, guint CurrMul, gint * Coef)
354
// int dMul= (PrevMul&0xFFFFFF)-(CurrMul&0xFFFFFF);
355
gint dMul = PrevMul - CurrMul;
356
gint d = ( (dMul + 0x10007FF) >> 12);
357
return CurrMul + Coef[d];
361
gst_hqdn3d_denoise (guint8 * Frame, guint * LineAnt, gushort ** FrameAntPtr,
363
gint * Horizontal, gint * Vertical, gint * Temporal)
369
gushort* FrameAnt = (*FrameAntPtr);
372
(*FrameAntPtr) = FrameAnt = g_malloc (W * H * sizeof (gushort));
373
for (Y = 0; Y < H; Y++){
374
gushort *dst = &FrameAnt[Y * W];
375
guint8 *src = Frame + Y * W;
376
for (X = 0; X < W; X++)
377
dst[X] = src[X] << 8;
381
/* First pixel has no left nor top neighbour. Only previous frame */
382
LineAnt[0] = PixelAnt = Frame[0] << 16;
383
PixelDst = LowPassMul (FrameAnt[0] << 8, PixelAnt, Temporal);
384
FrameAnt[0] = ((PixelDst + 0x1000007F) >> 8);
385
Frame[0] = ((PixelDst + 0x10007FFF) >> 16);
387
/* First line has no top neighbour. Only left one for each pixel and
390
for (X = 1; X < W; X++){
391
LineAnt[X] = PixelAnt = LowPassMul (PixelAnt, Frame[X] << 16, Horizontal);
392
PixelDst = LowPassMul (FrameAnt[X] << 8, PixelAnt, Temporal);
393
FrameAnt[X] = ((PixelDst + 0x1000007F) >> 8);
394
Frame[X] = ((PixelDst + 0x10007FFF) >> 16);
397
for (Y = 1; Y < H; Y++){
399
gushort *LinePrev = &FrameAnt[Y*W];
401
/* First pixel on each line doesn't have previous pixel */
402
PixelAnt = Frame[sLineOffs] << 16;
403
LineAnt[0] = LowPassMul(LineAnt[0], PixelAnt, Vertical);
404
PixelDst = LowPassMul(LinePrev[0]<<8, LineAnt[0], Temporal);
405
LinePrev[0] = ((PixelDst + 0x1000007F) >> 8);
406
Frame[sLineOffs] = ((PixelDst + 0x10007FFF) >> 16);
408
for (X = 1; X < W; X++){
410
/* The rest are normal */
411
PixelAnt = LowPassMul (PixelAnt, Frame[sLineOffs+X] << 16, Horizontal);
412
LineAnt[X] = LowPassMul (LineAnt[X], PixelAnt, Vertical);
413
PixelDst = LowPassMul (LinePrev[X] << 8, LineAnt[X], Temporal);
414
LinePrev[X] = ((PixelDst + 0x1000007F) >> 8);
415
Frame[sLineOffs+X] = ((PixelDst + 0x10007FFF) >> 16);
420
/* denoise3d variant */
423
gst_denoise3d_precalc_coefs (gint * ct, double dist25)
426
double gamma, simil, c;
428
gamma = log(0.25) / log(1.0 - dist25 / 255.0);
430
for(i = -256; i <= 255; i++) {
431
simil = 1.0 - (double) ABS (i) / 255.0;
432
c = pow (simil, gamma) * (double) i;
433
ct[256 + i] = (int) ((c < 0) ? (c - 0.5) : (c + 0.5));
437
#define LowPass(prev, curr, coef) (curr + coef[(gint) prev - (gint) curr])
440
gst_denoise3d_denoise (guint8 * frame, guint * lineant, gushort ** frameprevptr,
442
gint * horizontal, gint * vertical, gint * temporal)
447
guint8 *frameprev = (guint8 *) (*frameprevptr);
450
*frameprevptr = g_memdup (frame, w * h);
451
frameprev = (guint8 *) *frameprevptr;
453
lineantptr = (guint8 *) lineant;
459
/* First pixel has no left nor top neighbour, only previous frame */
460
*lineantptr = pixelant = *frame;
461
*frame = *frameprev = LowPass(*frameprev, *lineantptr, temporal);
466
/* First line has no top neighbour, only left one for each pixel and last frame */
467
for(x = 1; x < w; x++) {
468
pixelant = LowPass(pixelant, *frame, horizontal);
469
*lineantptr = pixelant;
470
*frame = *frameprev = LowPass(*frameprev, *lineantptr, temporal);
476
for (y = 1; y < h; y++) {
477
lineantptr = (guint8 *) lineant;
479
/* First pixel on each line doesn't have previous pixel */
481
*lineantptr = LowPass(*lineantptr, pixelant, vertical);
482
*frame = *frameprev = LowPass(*frameprev, *lineantptr, temporal);
487
for (x = 1; x < w; x++) {
488
/* The rest is normal */
489
pixelant = LowPass(pixelant, *frame, horizontal);
490
*lineantptr = LowPass(*lineantptr, pixelant, vertical);
491
*frame = *frameprev = LowPass(*frameprev, *lineantptr, temporal);
501
gst_hqdn3d_transform_ip (GstBaseTransform * btrans, GstBuffer * in)
505
GstFlowReturn ret = GST_FLOW_OK;
507
gst_object_sync_values (G_OBJECT (btrans), GST_BUFFER_TIMESTAMP (in));
509
filter = GST_HQDN3D (btrans);
511
src = (guint8 *) GST_BUFFER_DATA (in);
512
dest = (guint8 *) GST_BUFFER_DATA (in);
514
gint width = filter->width;
515
gint height = filter->height;
517
/* first run it on the luma plane */
518
if (G_LIKELY (filter->luma_spatial > 0 && filter->luma_temp > 0))
519
filter->denoise_func (src, filter->line, &(filter->frame[0]),
520
GST_VIDEO_I420_Y_ROWSTRIDE (width), height,
521
filter->coefs[COEFS_LS_INDEX], filter->coefs[COEFS_LS_INDEX],
522
filter->coefs[COEFS_LT_INDEX]);
523
/* then on the chroma planes */
524
if (G_LIKELY (filter->chroma_spatial > 0 && filter->chroma_temp > 0)) {
525
filter->denoise_func (src + GST_VIDEO_I420_U_OFFSET (width, height),
526
filter->line, &(filter->frame[1]),
527
GST_VIDEO_I420_U_ROWSTRIDE(width), height / 2,
528
filter->coefs[COEFS_CS_INDEX], filter->coefs[COEFS_CS_INDEX],
529
filter->coefs[COEFS_CT_INDEX]);
530
filter->denoise_func (src + GST_VIDEO_I420_V_OFFSET (width, height),
531
filter->line, &(filter->frame[2]),
532
GST_VIDEO_I420_V_ROWSTRIDE(width), height / 2,
533
filter->coefs[COEFS_CS_INDEX], filter->coefs[COEFS_CS_INDEX],
534
filter->coefs[COEFS_CT_INDEX]);
541
gst_hqdn3d_set_params (GstHqdn3d * filter)
543
/* recalculate only the needed params */
545
filter->luma_spatial = filter->set_luma_spatial;
546
if (!filter->luma_spatial)
547
filter->luma_spatial = DEFAULT_LUMA_SPATIAL;
549
filter->chroma_spatial = filter->set_chroma_spatial;
550
if (!filter->chroma_spatial)
551
filter->chroma_spatial = DEFAULT_CHROMA_SPATIAL * filter->luma_spatial /
552
DEFAULT_LUMA_SPATIAL;
554
filter->luma_temp = filter->set_luma_temp;
555
if (!filter->luma_temp)
556
filter->luma_temp = DEFAULT_LUMA_TEMP * filter->luma_spatial /
557
DEFAULT_LUMA_SPATIAL;
559
filter->chroma_temp = filter->set_chroma_temp;
560
if (!filter->chroma_temp)
561
filter->chroma_temp = filter->luma_temp * filter->chroma_spatial /
562
filter->luma_spatial;
566
static void gst_hqdn3d_update_props (GstHqdn3d * filter)
568
void (*calc_coefs) (gint *, double);
570
/* are we hqdn3d or denoise3d */
571
if (filter->quality) {
572
calc_coefs = gst_hqdn3d_precalc_coefs;
574
calc_coefs = gst_denoise3d_precalc_coefs;
577
/* and pre-calculated based on the configured properties */
578
gst_hqdn3d_set_params (filter);
579
calc_coefs (filter->coefs[COEFS_LS_INDEX], filter->luma_spatial);
580
calc_coefs (filter->coefs[COEFS_CS_INDEX], filter->chroma_spatial);
581
calc_coefs (filter->coefs[COEFS_LT_INDEX], filter->luma_temp);
582
calc_coefs (filter->coefs[COEFS_CT_INDEX], filter->chroma_temp);
586
gst_hqdn3d_start (GstBaseTransform * btrans)
588
GstHqdn3d *filter = GST_HQDN3D (btrans);
590
/* let's not change this mid-flight */
591
if (filter->quality) {
592
filter->denoise_func = gst_hqdn3d_denoise;
594
filter->denoise_func = gst_denoise3d_denoise;
597
gst_hqdn3d_update_props (filter);
599
GST_DEBUG_OBJECT (filter,
600
"starting with luma=%f, luma-temp=%f, chroma=%f, chroma-temp=%f",
601
filter->luma_spatial, filter->luma_temp,
602
filter->chroma_spatial, filter->chroma_temp);
608
gst_hqdn3d_stop (GstBaseTransform * btrans)
610
GstHqdn3d *filter = GST_HQDN3D (btrans);
612
gst_hqdn3d_free (filter);
618
gst_hqdn3d_set_property (GObject * object, guint prop_id,
619
const GValue * value, GParamSpec * pspec)
622
gboolean changed = FALSE;
625
g_return_if_fail (GST_IS_HQDN3D (object));
626
src = GST_HQDN3D (object);
629
case PROP_LUMA_SPATIAL:
630
tmp = g_value_get_double (value);
631
if (tmp != src->set_luma_spatial) {
632
src->set_luma_spatial = tmp;
636
case PROP_CHROMA_SPATIAL:
637
tmp = g_value_get_double (value);
638
if (tmp != src->set_chroma_spatial) {
639
src->set_chroma_spatial = tmp;
644
tmp = g_value_get_double (value);
645
if (tmp != src->set_luma_temp) {
646
src->set_luma_temp = tmp;
650
case PROP_CHROMA_TEMP:
651
tmp = g_value_get_double (value);
652
if (tmp != src->set_chroma_temp) {
653
src->set_chroma_temp = tmp;
657
case PROP_HIGH_QUALITY:
658
src->quality = g_value_get_boolean (value);
661
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
666
gst_hqdn3d_update_props (src);
670
gst_hqdn3d_get_property (GObject * object, guint prop_id, GValue * value,
675
g_return_if_fail (GST_IS_HQDN3D (object));
676
src = GST_HQDN3D (object);
679
case PROP_LUMA_SPATIAL:
680
g_value_set_double (value, src->luma_spatial);
682
case PROP_CHROMA_SPATIAL:
683
g_value_set_double (value, src->chroma_spatial);
686
g_value_set_double (value, src->luma_temp);
688
case PROP_CHROMA_TEMP:
689
g_value_set_double (value, src->chroma_temp);
691
case PROP_HIGH_QUALITY:
692
g_value_set_boolean (value, src->quality);
695
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);